[OK210開發(fā)板體驗(yàn)]功能篇(2)Linux字符驅(qū)動(dòng)之Key按鍵驅(qū)動(dòng)

原創(chuàng) 2015-12-23 09:00:00 [OK210開發(fā)板體驗(yàn)]功能篇(2)Linux字符驅(qū)動(dòng)之Key按鍵驅(qū)動(dòng)
前面進(jìn)行了OK210試用體驗(yàn)的入門篇介紹,算是初步入門,分別包含:

   【OK210開發(fā)板體驗(yàn)】的第一篇:開箱驗(yàn)板
   【OK210開發(fā)板體驗(yàn)】的第二篇:板載資源
   【OK210開發(fā)板體驗(yàn)】的第三篇:開發(fā)環(huán)境(軟件安裝,開發(fā)環(huán)境,燒寫系統(tǒng))
   【OK210開發(fā)板體驗(yàn)】的第四篇:編程入門(NFS登錄,驅(qū)動(dòng)入門)
前一篇介紹了功能篇的字符驅(qū)動(dòng)之LED燈,今天是功能篇的第二篇:字符驅(qū)動(dòng)之Key按鍵的控制,本節(jié)主要分3部分:硬件分析,軟件基礎(chǔ),驅(qū)動(dòng)編程。
一、硬件分析
【OK210開發(fā)板體驗(yàn)】的第二篇:板載資源中,簡(jiǎn)單分析了Key按鍵的功能和作用。其實(shí)對(duì)Key的操作,和對(duì)LED的操作是一相對(duì)的,都是對(duì)GPIO的最基本操作,也是入門操作。Key是對(duì)GPIO的輸入進(jìn)行操作,LED是對(duì)GPIO的輸出進(jìn)行操作。
首先從OK210的底板原理圖中可知,OK210的5個(gè)按鍵,通過核心板接到了S5PV210的XEINT3-7引腳上,



接著從用戶手冊(cè)的第103頁(yè)中得知,這些引腳沒有復(fù)用功能,默認(rèn)為GPI,即通用端口輸入,其實(shí)在用戶手冊(cè)的92而,有這么一句話:,想必大家都知道其什么意思了。
GPH0,1,2,3: 32 in/out port - Key pad, External Wake-up (up-to 32-bit). (GPH* groups
are in Alive region)


就是說,要對(duì)5個(gè)Key的操作,就是對(duì)XEINT3-7這5個(gè)引腳的控制。
二、軟件基礎(chǔ)
1 字符設(shè)備驅(qū)動(dòng)
字符驅(qū)動(dòng)模塊,可以簡(jiǎn)單的總結(jié)為如下圖所示:包括對(duì)設(shè)備文件的創(chuàng)建,字符驅(qū)動(dòng)的注冊(cè)以及文件操作的編寫等。



其實(shí)個(gè)人理解,對(duì)字符驅(qū)動(dòng)的編寫,就是對(duì)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é)將會(huì)用到openread,pollfasync,release5個(gè)函數(shù),下面將會(huì)一一介紹。
2驅(qū)動(dòng)中斷
驅(qū)動(dòng)程序中進(jìn)行中斷處理涉及到的最基本的內(nèi)核API,主要用于申請(qǐng)和釋放中斷,在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是要申請(qǐng)的硬件中斷號(hào);handler是向系統(tǒng)登記的中斷處理函數(shù)。這是一個(gè)回調(diào)函數(shù),中斷發(fā)生時(shí),系統(tǒng)調(diào)用這個(gè)函數(shù),傳入的參數(shù)包括硬件中斷號(hào),device id,寄存器值。dev_id就是下面的request_irq時(shí)傳遞給系統(tǒng)的參數(shù)dev_id。irqflags是中斷處理的一些屬性。比較重要的有SA_INTERRUPT,標(biāo)明中斷處理程序是快速處理程序(設(shè)置SA_INTERRUPT)還是慢速處理程序(不設(shè)置SA_INTERRUPT)。快速處理程序被調(diào)用時(shí)屏蔽所有中斷,慢速處理程序不屏蔽。還有一個(gè)SA_SHIRQ屬性,設(shè)置了以后運(yùn)行多個(gè)設(shè)備共享中斷。dev_id在中斷共享時(shí)會(huì)用到。一般設(shè)置為這個(gè)設(shè)備的 device結(jié)構(gòu)本身或者NULL。中斷處理程序可以用dev_id找到相應(yīng)的控制這個(gè)中斷的設(shè)備,或者用irq2dev_map找到中斷對(duì)應(yīng)的設(shè)備。
void free_irq(unsigned int irq,void *dev_id); 

三、驅(qū)動(dòng)編程
(一)、本驅(qū)動(dòng)程序分別實(shí)現(xiàn)了對(duì)按鍵的簡(jiǎn)單讀(read)操作,支持輪詢機(jī)制(poll)和支持異步機(jī)制(fasync)。
1簡(jiǎn)單讀(read)操作
簡(jiǎn)單的讀操作的工作原理是:當(dāng)應(yīng)用程序讀取鍵值時(shí),會(huì)調(diào)用按鍵驅(qū)動(dòng)程序的read函數(shù),而實(shí)現(xiàn)的read函數(shù)檢測(cè)完讀取長(zhǎng)度后沒有直接讀取鍵值而是等待按鍵消息,如果沒有按鍵,程序會(huì)進(jìn)入休眠狀態(tài),這樣可以節(jié)省大量的CPU,而當(dāng)按下按鍵時(shí)硬件會(huì)產(chǎn)生中斷,程序自動(dòng)進(jìn)入中斷處理函數(shù),在中斷處理函數(shù)中,驅(qū)動(dòng)程序讀取鍵值存入全局變量并激活read函數(shù)中等待的消息,應(yīng)用程序被迅速喚醒并通過read函數(shù)讀取鍵值,如此,完成了獲取鍵值的工作。測(cè)試程序見TestButtons1()。
2支持輪詢機(jī)制(poll)
上面的實(shí)現(xiàn)的按鍵驅(qū)動(dòng)程序有個(gè)弊端,如果不按鍵,應(yīng)用程序?qū)?huì)永遠(yuǎn)阻塞在這里,幸運(yùn)的是,linux內(nèi)核提供了poll機(jī)制,可以設(shè)置超時(shí)等待時(shí)間,如果在這個(gè)時(shí)間內(nèi)讀取到鍵值則正常返回,反之則超時(shí)退出。測(cè)試程序見TestButtons2()。
3支持異步機(jī)制(fasync)
很多情況下,程序在等待按鍵期間需要處理其它任務(wù)而不是在這里空等,這時(shí),就需要采用異步模式了。所謂異步模式,實(shí)際上是采用消息機(jī)制,即當(dāng)驅(qū)動(dòng)程序檢測(cè)到按鍵后發(fā)送消息給應(yīng)用程序,應(yīng)用程序接收到消息后再去讀取鍵值。與前面的兩種模式相比,最大的不同在于異步方式是驅(qū)動(dòng)告訴應(yīng)用程序來讀而不是應(yīng)用程序主動(dòng)去讀。測(cè)試程序見TestButtons3()。
下面是按鍵采用異步機(jī)制的測(cè)試結(jié)果:


(二)程序代碼
1驅(qū)動(dòng)程序:

 

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è)計(jì)和生產(chǎn),i.mx6UL系列產(chǎn)品現(xiàn)已暢銷全國(guó),作為恩智浦imx6ul,imx6ul開發(fā)板,i.mx6提供者,飛凌嵌入式提供基于iMX6 iMX6UL解決方案定制。

    了解詳情
    OKMX6UL-C開發(fā)板
  • OKMX6ULL-C開發(fā)板

    40*29mm,雙網(wǎng)雙CAN,8路串口| i.MX6ULL開發(fā)板是基于NXP i.MX6ULL設(shè)計(jì)開發(fā)的的一款Linux開發(fā)板 ,主頻800MHz,體積小,其核心板僅40*29mm,采用板對(duì)板連接器,適應(yīng)場(chǎng)景豐富。 了解詳情
    OKMX6ULL-C開發(fā)板

推薦閱讀 換一批 換一批