手把手带你写一个中断输入设备驱动
今天群里有人問(wèn),要開始驅(qū)動(dòng)開發(fā)的話從什么開始比較好。
我說(shuō),應(yīng)該開始去摸索觸摸屏驅(qū)動(dòng),現(xiàn)在我想了下,觸摸屏驅(qū)動(dòng)可能會(huì)難了些,但是從一個(gè)GPIO開始,我覺(jué)得一定是一件很容易的事情。
所以這篇文章就來(lái)了。
大家好,這是我的朋友逸珺。
首先說(shuō)聲抱歉,最近迷上釣魚了,有時(shí)候晚上出去夜釣大板鯽了,停更了一段時(shí)間。來(lái)幾張魚獲圖片:
技術(shù)還是不太到家,遇到幾次大鯉魚都給溜了,心有不甘,所以最近花了比較多的時(shí)間。言歸正傳,今天來(lái)分享一下以前寫一個(gè)中斷輸入設(shè)備驅(qū)動(dòng)案例,希望對(duì)有需要的朋友能有所幫助。
背景介紹
在一個(gè)項(xiàng)目中,有這樣一個(gè)需求:
主控芯片采用ZYNQ,需要采集外部一個(gè)脈沖編碼輸入信號(hào),這個(gè)信號(hào)是一個(gè)脈沖波形,脈沖數(shù)量代表測(cè)量結(jié)果。比如這有可能是一個(gè)電機(jī)的霍爾信號(hào)輸出,代表電機(jī)的轉(zhuǎn)速,也有可能是一個(gè)光柵編碼器的脈沖輸出,是什么并不重要。
這個(gè)電路本身,利用光耦實(shí)現(xiàn)了輸入測(cè)設(shè)備信號(hào)與采集端的電氣隔離。由于PS端該Bank的電平為3.3V,所以光耦的另一側(cè)也是3.3V。
ZYNQ的PS端運(yùn)行Linux程序,所以在這個(gè)場(chǎng)景下,要從應(yīng)用程序的角度將外部輸入信號(hào)用起來(lái),就需要實(shí)現(xiàn)這樣一個(gè)設(shè)備驅(qū)動(dòng)程序:
創(chuàng)建設(shè)備
在ZYNQ下,使用petalinux工具鏈,當(dāng)然本文中對(duì)于寫這個(gè)驅(qū)動(dòng)程序本身?yè)Q成其他的處理器從代碼的角度是類似的。
1.先運(yùn)行一下工具鏈環(huán)境變量腳本:
source?/opt/pkg/petalinux/settings.sh當(dāng)然也可以不用手動(dòng)這樣運(yùn)行,設(shè)置成linux開發(fā)主機(jī)開機(jī)自動(dòng)運(yùn)行,這里就不贅述怎么設(shè)置了,網(wǎng)上很多介紹。
2.創(chuàng)建設(shè)備
petalinux-create?-t?modules?--name?di-drv這樣在現(xiàn)有的工程下,就自動(dòng)創(chuàng)建設(shè)備文件:
./project-spec/meta-user/recipes-modules/di-drv/files/di-drv.c修改設(shè)備樹
./project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi?
中添加
/include/?"system-conf.dtsi" /?{???amba?{pinctrl_di_default:?di-default?{???mux?{???groups?=?"gpio0_0_grp";???function?=?"gpio0";???};???conf?{???pins?=?"MIO0";???io-standard?=?<1>;???bias-high-impedance;???slew-rate?=?<0>;???};???};???????????};di?{compatible?=?"di-drv";pinctrl-names?=?"default";pinctrl-0?=?<&pinctrl_di_default>;di-gpios?=?<&gpio0?0?0>;???};?????? };本文中,假定使用的IO引腳為PS_MIO0。
驅(qū)動(dòng)代碼
修改上面生成的代碼di-drv.c
#include?<linux/module.h>?? #include?<linux/kernel.h> #include?<linux/init.h>?? #include?<linux/ide.h>?? #include?<linux/types.h>?? #include?<linux/errno.h> #include?<linux/cdev.h> #include?<linux/of.h> #include?<linux/of_address.h> #include?<linux/of_gpio.h> #include?<linux/device.h> #include?<linux/delay.h> #include?<linux/init.h> #include?<linux/gpio.h> #include?<linux/semaphore.h> #include?<linux/timer.h> #include?<linux/of_irq.h> #include?<linux/irq.h> #include?<asm/uaccess.h> #include?<asm/mach/map.h> #include?<asm/io.h>/*?設(shè)備節(jié)點(diǎn)名稱?*/?? #define?DEVICE_NAME???????"di-drv" /*?設(shè)備號(hào)個(gè)數(shù)?*/?? #define?DEVID_COUNT???????1 /*?驅(qū)動(dòng)個(gè)數(shù)?*/?? #define?DRIVE_COUNT???????1 /*?主設(shè)備號(hào)?*/ #define?MAJOR_U /*?次設(shè)備號(hào)?*/ #define?MINOR_U???????????0struct?di_dev?{/*?字符設(shè)備框架?*/dev_t???????? devid;?//設(shè)備號(hào)struct?cdev?????cdev;??//字符設(shè)備struct?class????*class;??//類struct?device??? *device;?//設(shè)備struct?device_node?*nd;???//設(shè)備樹的設(shè)備節(jié)點(diǎn)spinlock_t??????lock;?//自旋鎖變量int??????????di_gpio;?//DI?gpio號(hào)__u32?????????di_pulses;//DI?counter????unsigned?int?????di_irq;?//DI?中斷號(hào) };static?struct?di_dev?di_char?=?{.cdev?=?{.owner?=?THIS_MODULE,}, };/*?中斷服務(wù)函數(shù)?*/ static?irqreturn_t?di_handler(int?irq,?void?*dev) {di_char.di_pulses++;return?IRQ_RETVAL(IRQ_HANDLED); }/*?open函數(shù)實(shí)現(xiàn),?對(duì)應(yīng)到Linux系統(tǒng)調(diào)用函數(shù)的open函數(shù)?*/?? static?int?di_drv_open(struct?inode?*inode_p,?struct?file?*file_p)?? {??printk("di_drv?module?opened\n");??file_p->private_data?=?&di_char;?return?0;?? }??/*?read函數(shù)實(shí)現(xiàn),?對(duì)應(yīng)到Linux系統(tǒng)調(diào)用函數(shù)的read操作?*/?? static?ssize_t?di_drv_read(struct?file?*file_p,?char?__user?*buf,?size_t?len,?loff_t?*loff_t_p)?? {??unsigned?long?flags;int?ret;union?e_int_conv{__u8??buf[8];__u32?di_raw;};??/*?獲取鎖?*/spin_lock_irqsave(&di_char.lock,?flags);union?e_int_conv?di;di.di_raw.di?=?di_char.di_pulses;ret??=?copy_to_user(buf,?di.buf,?8);/*?釋放鎖?*/spin_unlock_irqrestore(&di_char.lock,?flags);return?ret???ret?:?4; }??/*?release函數(shù)實(shí)現(xiàn),?對(duì)應(yīng)到Linux系統(tǒng)調(diào)用函數(shù)的close函數(shù)?*/?? static?int?di_drv_release(struct?inode?*inode_p,?struct?file?*file_p)?? {??printk("di_drv?module?release\n");return?0;?? }??/*?file_operations結(jié)構(gòu)體聲明?*/?? static?struct?file_operations?di_fops?=?{??.owner???=?THIS_MODULE,??.open???=?di_drv_open,??.read?? =?di_drv_read,?????.release?=?di_drv_release,??? };??/*?模塊加載時(shí)會(huì)調(diào)用的函數(shù)?*/?? static?int?__init?di_drv_init(void)?? {u32?ret?=?0;/*?初始化自旋鎖?*/spin_lock_init(&di_char.lock);/**?gpio框架?**/???/*?獲取設(shè)備節(jié)點(diǎn)?*/di_char.nd?=?of_find_node_by_path("/di");if(di_char.nd?==?NULL){printk("di?node?not?foundr\r\n");return?-EINVAL;}/*?獲取節(jié)點(diǎn)中g(shù)pio標(biāo)號(hào)?*/di_char.di_gpio?=?of_get_named_gpio(di_char.nd,?"di-gpios",?0);if(di_char.di_gpio?<?0){printk("Failed?to?get?di-gpios?from?device?tree\r\n");return?-EINVAL;}printk("di-gpio?num?=?%d\r\n",?di_char.di_gpio);/*?申請(qǐng)gpio標(biāo)號(hào)對(duì)應(yīng)的引腳?*/ret?=?gpio_request(di_char.di_gpio,?"di-drv");if(ret?!=?0){printk("Failed?to?request?di_gpio\r\n");return?-EINVAL;}/*?把這個(gè)io設(shè)置為輸入?*/ret?=?gpio_direction_input(di_char.di_gpio);if(ret?<?0){printk("Failed?to?set?di_gpio?as?input\r\n");return?-EINVAL;}/*?獲取中斷號(hào)?*/di_char.di_irq?=?gpio_to_irq(di_char.di_gpio);printk("di_irq?number?is?%d?\r\n",?di_char.di_irq);/*?申請(qǐng)中斷?*/ret?=?request_irq(di_char.di_irq,di_handler,IRQF_TRIGGER_FALLING?|?IRQF_TRIGGER_RISING,"di-drv",?NULL);if(ret?<?0){printk("di_irq?%d?request?failed\r\n",?di_char.di_irq);return?-EFAULT;}????/*?注冊(cè)設(shè)備號(hào)?*/alloc_chrdev_region(&di_char.devid,?MINOR_U,?DEVID_COUNT,?DEVICE_NAME);/*?初始化字符設(shè)備結(jié)構(gòu)體?*/cdev_init(&di_char.cdev,?&di_fops);/*?注冊(cè)字符設(shè)備?*/cdev_add(&di_char.cdev,?di_char.devid,?DRIVE_COUNT);/*?創(chuàng)建類?*/di_char.class?=?class_create(THIS_MODULE,?DEVICE_NAME);if(IS_ERR(di_char.class))?{return?PTR_ERR(di_char.class);}/*?創(chuàng)建設(shè)備節(jié)點(diǎn)?*/di_char.device?=?device_create( di_char.class,?NULL,?di_char.devid,?NULL,?DEVICE_NAME );if(IS_ERR(di_char.device))?{return?PTR_ERR(di_char.device);}di_char.di_pulses?=?0;????return?0;?? }/*?卸載模塊?*/?? static?void?__exit?di_drv_exit(void)?? {??/*?釋放gpio?*/gpio_free(di_char.di_gpio);/*?釋放中斷?*/free_irq(di_char.di_irq,?NULL);/*?注銷字符設(shè)備?*/cdev_del(&di_char.cdev);/*?注銷設(shè)備號(hào)?*/unregister_chrdev_region(di_char.devid,?DEVID_COUNT);/*?刪除設(shè)備節(jié)點(diǎn)?*/device_destroy(di_char.class,?di_char.devid);/*?刪除類?*/class_destroy(di_char.class);printk("DI?dev?exit?ok\n");?? }??/*?標(biāo)記加載、卸載函數(shù)?*/?? module_init(di_drv_init);?? module_exit(di_drv_exit);??/*?驅(qū)動(dòng)描述信息?*/?? MODULE_AUTHOR("Embinn");?? MODULE_ALIAS("DI?input");?? MODULE_DESCRIPTION("DIGITAL?INPUT?driver");?? MODULE_VERSION("v1.0");?? MODULE_LICENSE("GPL");這是一個(gè)字符驅(qū)動(dòng)的實(shí)現(xiàn),在真實(shí)項(xiàng)目中,大部分驅(qū)動(dòng)基本已經(jīng)被芯片廠商給實(shí)現(xiàn)了,但是一些特殊項(xiàng)目的自定義需求,往往就需要去實(shí)現(xiàn)自己的驅(qū)動(dòng)。
編譯部署
運(yùn)行以下命令:
petalinux-config?-c?rootfs進(jìn)入modules,使能剛剛創(chuàng)建的模塊,退出保存。
運(yùn)行下面的命令進(jìn)行編譯:
petalinux-build最終在工程目錄下,搜索di-drv.ko,就得到這個(gè)驅(qū)動(dòng)的內(nèi)核模塊文件了,拷貝到目標(biāo)板的某個(gè)文件夾下,運(yùn)行下面的命令裝載就完成了:
insmod?di-drv.ko這樣在/dev下就會(huì)發(fā)現(xiàn)新增一個(gè)di-drv設(shè)備。
當(dāng)然也可以直接將該驅(qū)動(dòng)放進(jìn)內(nèi)核里,這就需要在內(nèi)核代碼樹里,添加文件了,這個(gè)思路之前有分享過(guò)。
總結(jié)一下
字符設(shè)備是做驅(qū)動(dòng)開發(fā)比較容易掌握的驅(qū)動(dòng)類型,也是大多數(shù)項(xiàng)目中,需要自己動(dòng)手寫的最多的驅(qū)動(dòng)類型。所以還是應(yīng)該掌握它。才能實(shí)現(xiàn)不同的項(xiàng)目需求。至于用戶空間怎么訪問(wèn)這個(gè)設(shè)備,這里就不贅述了,一個(gè)文件打開操作,再來(lái)一個(gè)讀取操作就完事了。
推薦閱讀:
專輯|Linux文章匯總
專輯|程序人生
專輯|C語(yǔ)言
我的知識(shí)小密圈
關(guān)注公眾號(hào),后臺(tái)回復(fù)「1024」獲取學(xué)習(xí)資料網(wǎng)盤鏈接。
歡迎點(diǎn)贊,關(guān)注,轉(zhuǎn)發(fā),在看,您的每一次鼓勵(lì),我都將銘記于心~
總結(jié)
以上是生活随笔為你收集整理的手把手带你写一个中断输入设备驱动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 海康威视摄像头激活失败的几个原因和方法
- 下一篇: 嵌入式真的没前途?