[OK210開發(fā)板體驗]功能篇(2)Linux字符驅(qū)動之Key按鍵驅(qū)動
【OK210開發(fā)板體驗】的第一篇:開箱驗板
【OK210開發(fā)板體驗】的第二篇:板載資源
【OK210開發(fā)板體驗】的第三篇:開發(fā)環(huán)境(軟件安裝,開發(fā)環(huán)境,燒寫系統(tǒng))
【OK210開發(fā)板體驗】的第四篇:編程入門(NFS登錄,驅(qū)動入門)
前一篇介紹了功能篇的字符驅(qū)動之LED燈,今天是功能篇的第二篇:字符驅(qū)動之Key按鍵的控制,本節(jié)主要分3部分:硬件分析,軟件基礎(chǔ),驅(qū)動編程。
一、硬件分析
在【OK210開發(fā)板體驗】的第二篇:板載資源中,簡單分析了Key按鍵的功能和作用。其實對Key的操作,和對LED的操作是一相對的,都是對GPIO的最基本操作,也是入門操作。Key是對GPIO的輸入進(jìn)行操作,LED是對GPIO的輸出進(jìn)行操作。
首先從OK210的底板原理圖中可知,OK210的5個按鍵,通過核心板接到了S5PV210的XEINT3-7引腳上,
接著從用戶手冊的第103頁中得知,這些引腳沒有復(fù)用功能,默認(rèn)為GPI,即通用端口輸入,其實在用戶手冊的92而,有這么一句話:,想必大家都知道其什么意思了。
GPH0,1,2,3: 32 in/out port - Key pad, External Wake-up (up-to 32-bit). (GPH* groupsare in Alive region)
二、軟件基礎(chǔ)
1 字符設(shè)備驅(qū)動
字符驅(qū)動模塊,可以簡單的總結(jié)為如下圖所示:包括對設(shè)備文件的創(chuàng)建,字符驅(qū)動的注冊以及文件操作的編寫等。
其實個人理解,對字符驅(qū)動的編寫,就是對struct file_operations 結(jié)構(gòu)體的填充,該結(jié)構(gòu)體定義在linux/fs.h頭文件中,在2.6.35.7內(nèi)核中,定義為如下形式:
1. struct file_operations {
2. struct module *owner;
3. loff_t (*llseek) (struct file *, loff_t, int);
4. ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
5. ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
6. ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
7. ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
8. int (*readdir) (struct file *, void *, filldir_t);
9. unsigned int (*poll) (struct file *, struct poll_table_struct *);
10. int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
11. long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
12. long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
13. int (*mmap) (struct file *, struct vm_area_struct *);
14. int (*open) (struct inode *, struct file *);
15. int (*flush) (struct file *, fl_owner_t id);
16. int (*release) (struct inode *, struct file *);
17. int (*fsync) (struct file *, int datasync);
18. int (*aio_fsync) (struct kiocb *, int datasync);
19. int (*fasync) (int, struct file *, int);
20. int (*lock) (struct file *, int, struct file_lock *);
21. ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
22. unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
23. int (*check_flags)(int);
24. int (*flock) (struct file *, int, struct file_lock *);
25. ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
26. ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
27. int (*setlease)(struct file *, long, struct file_lock **);
28. };
復(fù)制代碼
本節(jié)將會用到open,read,poll,fasync,release這5個函數(shù),下面將會一一介紹。
2驅(qū)動中斷
驅(qū)動程序中進(jìn)行中斷處理涉及到的最基本的內(nèi)核API,主要用于申請和釋放中斷,在linux/sched.h里聲明,其原型為:
int request_irq(unsigned int irq,
void (*handler)(int irq, void *dev_id, struct pt_regs *regs
),
unsigned long irqflags,
const char * devname,
void *dev_id);
irq是要申請的硬件中斷號;handler是向系統(tǒng)登記的中斷處理函數(shù)。這是一個回調(diào)函數(shù),中斷發(fā)生時,系統(tǒng)調(diào)用這個函數(shù),傳入的參數(shù)包括硬件中斷號,device id,寄存器值。dev_id就是下面的request_irq時傳遞給系統(tǒng)的參數(shù)dev_id。irqflags是中斷處理的一些屬性。比較重要的有SA_INTERRUPT,標(biāo)明中斷處理程序是快速處理程序(設(shè)置SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)。快速處理程序被調(diào)用時屏蔽所有中斷,慢速處理程序不屏蔽。還有一個SA_SHIRQ屬性,設(shè)置了以后運行多個設(shè)備共享中斷。dev_id在中斷共享時會用到。一般設(shè)置為這個設(shè)備的 device結(jié)構(gòu)本身或者NULL。中斷處理程序可以用dev_id找到相應(yīng)的控制這個中斷的設(shè)備,或者用irq2dev_map找到中斷對應(yīng)的設(shè)備。
void free_irq(unsigned int irq,void *dev_id);
三、驅(qū)動編程
(一)、本驅(qū)動程序分別實現(xiàn)了對按鍵的簡單讀(read)操作,支持輪詢機制(poll)和支持異步機制(fasync)。
1簡單讀(read)操作
簡單的讀操作的工作原理是:當(dāng)應(yīng)用程序讀取鍵值時,會調(diào)用按鍵驅(qū)動程序的read函數(shù),而實現(xiàn)的read函數(shù)檢測完讀取長度后沒有直接讀取鍵值而是等待按鍵消息,如果沒有按鍵,程序會進(jìn)入休眠狀態(tài),這樣可以節(jié)省大量的CPU,而當(dāng)按下按鍵時硬件會產(chǎn)生中斷,程序自動進(jìn)入中斷處理函數(shù),在中斷處理函數(shù)中,驅(qū)動程序讀取鍵值存入全局變量并激活read函數(shù)中等待的消息,應(yīng)用程序被迅速喚醒并通過read函數(shù)讀取鍵值,如此,完成了獲取鍵值的工作。測試程序見TestButtons1()。
2支持輪詢機制(poll)
上面的實現(xiàn)的按鍵驅(qū)動程序有個弊端,如果不按鍵,應(yīng)用程序?qū)肋h(yuǎn)阻塞在這里,幸運的是,linux內(nèi)核提供了poll機制,可以設(shè)置超時等待時間,如果在這個時間內(nèi)讀取到鍵值則正常返回,反之則超時退出。測試程序見TestButtons2()。
3支持異步機制(fasync)
很多情況下,程序在等待按鍵期間需要處理其它任務(wù)而不是在這里空等,這時,就需要采用異步模式了。所謂異步模式,實際上是采用消息機制,即當(dāng)驅(qū)動程序檢測到按鍵后發(fā)送消息給應(yīng)用程序,應(yīng)用程序接收到消息后再去讀取鍵值。與前面的兩種模式相比,最大的不同在于異步方式是驅(qū)動告訴應(yīng)用程序來讀而不是應(yīng)用程序主動去讀。測試程序見TestButtons3()。
下面是按鍵采用異步機制的測試結(jié)果:
(二)程序代碼
1驅(qū)動程序:
1.
2. #include <linux/types.h>
3. #include <linux/module.h>
4. #include <linux/cdev.h>
5. #include <linux/fs.h>
6. #include <linux/device.h>
7. #include <linux/gpio.h>
8. #include <linux/irq.h>
9. #include <linux/interrupt.h>
10. #include <linux/sched.h>
11. #include <linux/wait.h>
12. #include <linux/uaccess.h>
13. #include <linux/poll.h>
14.
15.
16. static dev_t devno;
17. static struct cdev cdev;
18. static struct class* my_buttons_class;
19. static struct device* my_buttons_device;
20.
21. static wait_queue_head_t my_button_waitq;
22. static struct fasync_struct *my_button_fasync;
23. static volatile int pressed = 0;
24. static unsigned char key_val;
25.
26. struct key_desc{
27. unsigned int pin;
28. unsigned char value;
29. char* name;
30. };
31.
32. static struct key_desc my_key_descs[] = {
33. [0] = {
34. .pin = S5PV210_GPH0(3),
35. .value = 0x01,
36. .name ="Key1",
37. },
38.
39. [1] = {
40. .pin = S5PV210_GPH0(4),
41. .value = 0x02,
42. .name ="Key2",
43. },
44.
45. [2] = {
46. .pin = S5PV210_GPH0(5),
47. .value = 0x03,
48. .name ="Key3",
49. },
50.
51. [3] = {
52. .pin = S5PV210_GPH0(6),
53. .value = 0x04,
54. .name ="Key4",
55. },
56.
57. [4] = {
58. .pin = S5PV210_GPH0(7),
59. .value = 0x05,
60. .name ="Key5",
61. },
62.
63. };
64.
65. #define BUTTONS_NUM ARRAY_SIZE(my_key_descs)
66.
67. static irqreturn_t my_buttons_irq(int irq, void *dev_id){
68. volatile struct key_desc *key = (volatile struct key_desc *)dev_id;
69.
70. if(gpio_get_value(key->pin)){
71. key_val = key->value|0x80;
72. }
73. else{
74. key_val = key->value;
75. }
76.
77. pressed = 1;
78. wake_up_interruptible(&my_button_waitq);
79.
80. kill_fasync(&my_button_fasync, SIGIO, POLL_IN); //用kill_fasync函數(shù)告訴應(yīng)用程序,有數(shù)據(jù)可讀了
81. return IRQ_RETVAL(IRQ_HANDLED);
82. }
83.
84. static int my_buttons_open(struct inode *inode, struct file *file){
85. int ret;
86.
87. ret = request_irq( IRQ_EINT3, my_buttons_irq, IRQ_TYPE_EDGE_BOTH, "key1", &my_key_descs[0]);
88. if(ret)
89. return ret;
90. ret = request_irq( IRQ_EINT4, my_buttons_irq, IRQ_TYPE_EDGE_BOTH, "key2", &my_key_descs[1]);
91. if(ret)
92. return ret;
93. ret = request_irq( IRQ_EINT5, my_buttons_irq, IRQ_TYPE_EDGE_BOTH, "key3", &my_key_descs[2]);
94. if(ret)
95. return ret;
96. ret = request_irq( IRQ_EINT6, my_buttons_irq, IRQ_TYPE_EDGE_BOTH, "key4", &my_key_descs[3]);
97. if(ret)
98. return ret;
99. ret = request_irq( IRQ_EINT7, my_buttons_irq, IRQ_TYPE_EDGE_BOTH, "key5", &my_key_descs[4]);
100. if(ret)
101. return ret;
102.
103. return 0;
相關(guān)產(chǎn)品 >
-
OKMX6UL-C開發(fā)板
飛凌嵌入式專注imx6系列imx6ul開發(fā)板、飛思卡爾imx6ul核心板等ARM嵌入式核心控制系統(tǒng)研發(fā)、設(shè)計和生產(chǎn),i.mx6UL系列產(chǎn)品現(xiàn)已暢銷全國,作為恩智浦imx6ul,imx6ul開發(fā)板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。
了解詳情 -
OKMX6ULL-C開發(fā)板
40*29mm,雙網(wǎng)雙CAN,8路串口| i.MX6ULL開發(fā)板是基于NXP i.MX6ULL設(shè)計開發(fā)的的一款Linux開發(fā)板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對板連接器,適應(yīng)場景豐富。 了解詳情