前邊已經學了兩種點燈,本質依然還是通過配置寄存器;在學習STM32的時候除了學習配置一下寄存器,基本都是使用庫來開發,那么在i.MX6ULL還使用寄存器開發明顯是不太適合,那么i.MX6ULL有更方便的開發呢,這篇就來學習一下使用 pinctrl 和 gpio 子系統來完成 LED 燈驅動。
|修改設備樹文件
添加 pinctrl 節點
開發板上的 LED 燈使用了 GPIO1_IO04這個 PIN,打開 imx6ull-14x14-evk.dts,在 iomuxc 節點的 imx6ul-evk 子節點下創建一個名為“pinctrl_led”的子節點,節點內容如下所示:
/* 添加的 */ pinctrl_led: ledgrp { fsl,pins = < MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x10B0 /* LED0 */ >; };MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 表示將該io復用為GPIO。
0x10b0 表示對PAD寄存器的配置值,具體含義為如下:
/*寄存器SW_PAD_SNVS_TAMPER3設置IO屬性 *bit 16:0 HYS關閉 *bit [15:14]: 00 默認下拉 *bit [13]: 0 kepper功能 *bit [12]: 1 pull/keeper使能 *bit [11]: 0 關閉開路輸出 *bit [7:6]: 10 速度100Mhz *bit [5:3]: 110 R0/6驅動能力 *bit [0]: 0 低轉換率 */
圖示:
添加 LED 設備節點
在根節點“/”下創建 LED 燈節點,節點名為“gpioled”,節點內容如下:
gpioled { #address-cells = <1>; #size-cells = <1>; compatible = "atkalpha-gpioled"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio1 4 GPIO_ACTIVE_LOW>; status="okay"; };
pinctrl-0 屬性設置 LED 燈所使用的 PIN 對應的 pinctrl 節點。
led-gpio 屬性指定了 LED 燈所使用的 GPIO,在這里就是 GPIO1 的 IO04,低電平有效。稍后編寫驅動程序的時候會獲取 led-gpio 屬性的內容來得到 GPIO 編號,因為 gpio 子系統的 API 操作函數需要 GPIO 編號。
圖示:
檢查 PIN 是否被其他外設使用
這一點非常重要!!!
很多初次接觸設備樹的驅動開發人員很容易因為這個小問題栽了大跟頭!因為所使用的設備樹基本都是在半導體廠商提供的設備樹文件基礎上修改而來的,而半導體廠商提供的設備樹是根據自己官方開發板編寫的,很多 PIN 的配置和實際所使用的開發板不一樣。
比如 A 這個引腳在官方開發板接的是 I2C 的 SDA,而實際所使用的硬件可能將 A 這個引腳接到了其他的外設,比如 LED 燈上,接不同的外設,A 這個引腳的配置就不同。一個引腳一次只能實現一個功能,如果 A 引腳在設備樹中配置為了 I2C 的 SDA 信號,那么 A 引腳就不能再配置為 GPIO,否則的話驅動程序在申請 GPIO 的時候就會失敗。檢查 PIN 有沒有被其他外設使用包括兩個方面:
①、檢查 pinctrl 設置。
②、如果這個 PIN 配置為 GPIO 的話,檢查這個 GPIO 有沒有被別的外設使用。
因為本章實驗將 GPIO1_IO04這個 PIN 配置為了 GPIO,所以還需要查找一下有沒有其他的外設使用了 GPIO1_IO04,在 可能使用到的設備樹中搜索“gpio1 4”,看看是否被其他外設使用到:
編譯設備樹和復制文件
編譯沒有問題:
復制文件:
|編譯驅動程序
復制一份新字符驅動,對應改下名稱:
簡單提前了解:使用pinctrl 和 gpio 子系統來完成 LED 燈驅動最明顯的變化就是不同操作寄存器,也就不用對物理地址映射成虛擬地址。完整的代碼如下:
#include#include #include #include #include #include #include #include #include #include #include #include #include /* 添加頭文件 */ #include #include #include #include #define CHRDEVBASE_CNT 1 /* 設備號個數 */ #define CHRDEVBASE_NAME "chrdevbase" /* 名字 */ #define LEDOFF 0 /* 關燈 */ #define LEDON 1 /* 開燈 */ /* chrdevbase 設備結構體 */ struct newchr_dev{ dev_t devid; /* 設備號 */ struct cdev cdev; /* cdev */ struct class *class; /* 類 */ struct device *device; /* 設備 */ int major; /* 主設備號 */ int minor; /* 次設備號 */ struct device_node *nd; /* 設備節點 */ int led_gpio; /* led 所使用的 GPIO 編號 */ }; struct newchr_dev chrdevbase;/* 自定義字符設備 */ /* * @description : LED 硬件初始化 * @param : 無 * @return : 無 */ static int led_hal_init(void) { int ret = 0; /* 設置 LED 所使用的 GPIO */ /* 1、獲取設備節點:gpioled */ chrdevbase.nd = of_find_node_by_path("/gpioled"); if(chrdevbase.nd == NULL) { printk("chrdevbase node cant not found! "); return -EINVAL; } else { printk("chrdevbase node has been found! "); } /* 2、 獲取設備樹中的 gpio 屬性,得到 LED 所使用的 LED 編號 */ chrdevbase.led_gpio = of_get_named_gpio(chrdevbase.nd, "led-gpio", 0); if(chrdevbase.led_gpio < 0) { printk("can't get led-gpio"); return -EINVAL; } printk("led-gpio num = %d ", chrdevbase.led_gpio); /* 3、設置 GPIO1_IO03 為輸出,并且輸出高電平,默認關閉 LED 燈 */ ret = gpio_direction_output(chrdevbase.led_gpio, 1); if(ret < 0) { printk("can't set gpio! "); } return 0; } /* * @description : 打開設備 * @param - inode : 傳遞給驅動的inode * @param - filp : 設備文件,file結構體有個叫做private_data的成員變量 * 一般在open的時候將private_data指向設備結構體。 * @return : 0 成功;其他 失敗 */ static int chrdevbase_open(struct inode *inode, struct file *filp) { printk("[BSP]chrdevbase open! "); filp->private_data = &chrdevbase; /* 設置私有數據 */ return 0; } /* * @description : 從設備讀取數據 * @param - filp : 要打開的設備文件(文件描述符) * @param - buf : 返回給用戶空間的數據緩沖區 * @param - cnt : 要讀取的數據長度 * @param - offt : 相對于文件首地址的偏移 * @return : 讀取的字節數,如果為負值,表示讀取失敗 */ static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { printk("chrdevbase read! "); return 0; } /* * @description : 向設備寫數據 * @param - filp : 設備文件,表示打開的文件描述符 * @param - buf : 要寫給設備寫入的數據 * @param - cnt : 要寫入的數據長度 * @param - offt : 相對于文件首地址的偏移 * @return : 寫入的字節數,如果為負值,表示寫入失敗 */ static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; char writebuf[1]; struct newchr_dev *dev = filp->private_data; /* 接收用戶空間傳遞給內核的數據并且打印出來 */ retvalue = copy_from_user(writebuf, buf, cnt); printk("[BSP]kernel recevdata data:%d! ",writebuf[0]); if(writebuf[0] == LEDON) { gpio_set_value(dev->led_gpio, 0); /* 打開 LED 燈 */ } else if(writebuf[0] == LEDOFF) { gpio_set_value(dev->led_gpio, 1); /* 關閉 LED 燈 */ } // printk("chrdevbase write! "); return 0; } /* * @description : 關閉/釋放設備 * @param - filp : 要關閉的設備文件(文件描述符) * @return : 0 成功;其他 失敗 */ static int chrdevbase_release(struct inode *inode, struct file *filp) { printk("[BSP]release! "); return 0; } /* * 設備操作函數結構體 */ static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, }; /* * @description : 驅動入口函數 * @param : 無 * @return : 0 成功;其他 失敗 */ static int __init chrdevbase_init(void) { /* 初始化硬件 */ led_hal_init(); /* 注冊字符設備驅動 */ /* 1、創建設備號 */ if (chrdevbase.major) { /* 定義了設備號 */ chrdevbase.devid = MKDEV(chrdevbase.major, 0); register_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT, CHRDEVBASE_NAME); } else { /* 沒有定義設備號 */ alloc_chrdev_region(&chrdevbase.devid, 0, CHRDEVBASE_CNT,CHRDEVBASE_NAME); /* 申請設備號 */ chrdevbase.major = MAJOR(chrdevbase.devid); /* 獲取主設備號 */ chrdevbase.minor = MINOR(chrdevbase.devid); /* 獲取次設備號 */ } printk("newcheled major=%d,minor=%d ",chrdevbase.major,chrdevbase.minor); /* 2、初始化 cdev */ chrdevbase.cdev.owner = THIS_MODULE; cdev_init(&chrdevbase.cdev, &chrdevbase_fops); /* 3、添加一個 cdev */ cdev_add(&chrdevbase.cdev, chrdevbase.devid, CHRDEVBASE_CNT); /* 4、創建類 */ chrdevbase.class = class_create(THIS_MODULE, CHRDEVBASE_NAME); if (IS_ERR(chrdevbase.class)) { return PTR_ERR(chrdevbase.class); } /* 5、創建設備 */ chrdevbase.device = device_create(chrdevbase.class, NULL,chrdevbase.devid, NULL, CHRDEVBASE_NAME); if (IS_ERR(chrdevbase.device)) { return PTR_ERR(chrdevbase.device); } return 0; } /* * @description : 驅動出口函數 * @param : 無 * @return : 無 */ static void __exit chrdevbase_exit(void) { /* 注銷字符設備 */ cdev_del(&chrdevbase.cdev);/* 刪除 cdev */ unregister_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT);/* 注銷設備號 */ device_destroy(chrdevbase.class, chrdevbase.devid);/* 銷毀設備 */ class_destroy(chrdevbase.class);/* 銷毀類 */ printk("[BSP]chrdevbase exit! "); } /* * 將上面兩個函數指定為驅動的入口和出口函數 */ module_init(chrdevbase_init); module_exit(chrdevbase_exit); /* * LICENSE和作者信息 */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");
編譯驅動:
復制驅動到對應位置:
| 觀察效果
1、觀察設備樹節點
2、加載驅動
depmod //第一次加載驅動的時候需要運行此命令 modprobe gpioled.ko //加載驅動
3、操作GPIO
使用pinctrl 和 gpio 子系統可以很方便對GPIO進行操作,可以不去查寄存器的地址也能實現,提高了程序員對底層驅動開發的效率。
審核編輯:湯梓紅
-
led
+關注
關注
240文章
23134瀏覽量
658408 -
寄存器
+關注
關注
31文章
5317瀏覽量
120002 -
STM32
+關注
關注
2266文章
10871瀏覽量
354789 -
子系統
+關注
關注
0文章
109瀏覽量
12383 -
GPIO
+關注
關注
16文章
1196瀏覽量
51914
原文標題:i.MX6ULL|pinctrl 和 gpio 子系統點燈
文章出處:【微信號:玩轉單片機,微信公眾號:玩轉單片機】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論