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