14.linux按鍵驅動程序(一)


            按鍵驅動程序

  本文學習主要包含按鍵硬件的實現、中斷分層管理、按鍵定時器去抖、阻塞性驅動程序設計。這里面需要使用到混雜設備驅動中斷處理程序的內容。

一、創建按鍵混雜設備驅動模型 

 1 int key_open(struct inode *node,struct file *filp)
 2 {
 3     return 0;    
 4 }
 5 struct file_operations key_fops = 
 6 {
 7     .open = key_open,    
 8 };
 9 struct miscdevice key_miscdev = {
10     .minor = 200,     //次設備號
11     .name = "6410key",   //設備名
12     .fops = &key_fops,    
13 };

二、按鍵硬件的實現

  首先是按鍵的初始化,按鍵的初始化可以選擇在open函數,和模塊的初始化函數當中完成硬件的初始化。下面我們是選擇在模塊的初始化函數進行按鍵的初始化。按鍵的初始化,主要涉及對GPIO的引腳的功能進行相應的設置。我用的是ok6410A開發板上面有六個按鍵,核心板原理圖如下:

  

2.1硬件初始化

  

 s3c6410芯片手冊對GPNCON的IO功能定義如下因此對按鍵硬件的初始化如下:

 1 #define GPNCON 0x7f008830
 2 #define GPNDAT 0x7f008834
 3 
 4 void key_hw_init(void)   //硬件初始化
 5 {
 6     unsigned int *gpio_config;
 7     unsigned short data;    
 8     gpio_config = ioremap(GPNCON,4);  //動態映射虛擬地址
 9     data = readw(gpio_config);
10     data &= ~0xfff;
11     data |= 0xaaa;
12     writew(data,gpio_config);
13     gpio_dat = ioremap(GPNDAT,4);  //將數據寄存器地址轉化為虛擬地址
14 }

 2.2按鍵中斷程序(暫時注冊第一個按鍵)

來源參照:中斷處理程序 

 1 #include <linux/module.h>
 2 #include <linux/init.h>
 3 #include <linux/miscdevice.h>
 4 #include <linux/interrupt.h>
 5 #include <linux/io.h>
 6 #include <linux/fs.h>
 7 
 8 #define GPNCON 0x7f008830
 9 #define GPNDAT 0x7f008834
10 
11 MODULE_LICENSE("GPL");
12  
13 irqreturn_t key_init(int irp,void *dev_id)
14 {
15     //1.檢測是否發生中斷    
16     //2.清除已經發生的按鍵中斷
17     
18     //3.打印按鍵值
19     printk(KERN_EMERG"key down!\n");
20     return 0;
21 }
22 
23 void key_hw_init(void)   //硬件初始化
24 {
25     unsigned int *gpio_config;
26     unsigned short data;    
27     gpio_config = ioremap(GPNCON,4);
28     data = readw(gpio_config);
29     data &= ~0xfff;
30     data |= 0xaaa;
31     writew(data,gpio_config);
32     gpio_dat = ioremap(GPNDAT,4);  //將數據寄存器地址轉化為虛擬地址
33 }
34 
35 int key_open(struct inode *node,struct file *filp)
36 {    
37     return 0;
38 }
39 struct file_operations key_fops = 
40 {
41     .open = key_open,
42 };
43 
44 struct miscdevice key_miscdev = 
45 {
46     .minor = 200,
47     .name = "key",
48     .fops = &key_fops,
49 };
50 
51 static int button_init(void)
52 {
53     misc_register(&key_miscdev);   //注冊混雜設備    
54     //按鍵硬件初始化
55     key_hw_init();
56     request_irq(IRQ_EINT(0),key_init,IRQF_TRIGGER_FALLING,"key",0);  //注冊中斷處理程序
57     return 0;
58 }
59 
60 static void button_exit(void)
61 {
62     misc_deregister(&key_miscdev);   //注銷混雜設備
63     
64     //注銷中斷處理程序
65     free_irq(IRQ_EINT(0),0);
66 }
67 
68 module_init(button_init);
69 module_exit(button_exit);

  這里需要注意上面代碼特殊標記的內容:IRQ_EINT(0)中斷號、IRQF_TRIGGER_FALLING標志的來源

標志來源:

  按鍵中斷的處理,對於按鍵而言,可以在按下的時候產生中斷,也可以在彈起的時候產生中斷。需要通過一個標志來指定:IRQF_TRIGGER_FALLING,這個是從高電平到低電平產生中斷。下表是其他產生中斷的方式(內核代碼中搜索IRQF_TRIGGER_FALLING):

  

中斷號:

  就是request_irq函數的第一個參數。我們在內核代碼中搜索irqs.h,找對應的板子的:

  

  從上面的代碼看到,IRQ_EINT0_3的中斷號是32.系統留出了S3C_IRQ_OFFSET=32個中斷號,這是給軟中斷的。所以中斷號就是等於硬件編號加上偏移量。可以查看內核代碼的entry-macro.S

 三、中斷分層管理

  

3.1中斷嵌套

  所謂的中斷嵌套就是,當一種中斷正在執行的時候,又產生了另外中斷。可以是同類型的,也可以是不同類型的。

    慢速中斷:是指在進行中斷處理的時候,中斷的總開關是不關閉的。允許其他類型中斷產生。

  快速中斷:當中斷產生的時候,控制位的IF為被置1,別的中斷被禁止發生。這樣就會產生我們不想看到的情況:中斷丟失。

3.2中斷分層

  

  上半部:當中斷發生時,它進行相應地硬件讀寫,並“登記”該中斷。通常由中斷處理程序充當上半部。

  下半部:在系統空閑的時候對上半部“登記”的中斷進行后續處理。

3.3工作隊列

  工作隊列是一種將任務推后執行的形式,他把推后的任務交由一個內核線程去執行。這樣下半部會在進程上下文執行,它允許重新調度甚至睡眠。 每個被推后的任務叫做“工作”,由這些工作組成的隊列稱為工作隊列

  下圖為3內核的處理器隊列處理圖:

  

  

3.3.1工作隊列描述

  Linux內核使用struct  work_struct來描述一個工作隊列:

1 struct workqueue_struct{
2     struct cpu_workqueue_struct *cpu_wq;
3     struct list_head list;
4     const char *name; /*workqueue name*/
5     int singlethread;
6     int freezeable; /* Freeze threads during suspend */
7     int rt;
8 };

3.3.2工作隊列項

  Linux內核使用struct work_struct來描述一個工作項:

1 struct work_struct{
2     atomic_long_t data;
3     struct list_headentry;
4     work_func_t func;
5 };
6 typedef void (*work_func_t)(struct work_struct *work);

3.3.3工作隊列使用

step1. 創建工作隊列
  create_workqueue
step2. 創建工作
  INIT_WORK
step3. 提交工作
  queue_work

  在大多數情況下, 驅動並不需要自己建立工作隊列,只需定義工作, 然后將工作提交到內核已經定義好的工作隊列keventd_wq中。

1. 提交工作到默認隊列
  schedule_work

按照工作隊列修改按鍵程序:

 1 #include <linux/module.h>
 2 #include <linux/init.h>
 3 #include <linux/miscdevice.h>
 4 #include <linux/interrupt.h>
 5 #include <linux/io.h>
 6 #include <linux/fs.h>
 7 #include <linux/slab.h>
 8 
 9 #define GPNCON 0x7f008830
10 MODULE_LICENSE("GPL");
11 struct work_struct *work1;
12 struct timer_list key_timer;
13 
14 void work1_func(struct work_struct *work)
15 {
16     printk(KERN_EMERG"key down!\n");
17 }
18  
19 irqreturn_t key_int(int irp,void *dev_id)
20 {    
21     //3.提交下半部
22     schedule_work(work1);
23     return 0;
24 }
25 
26 void key_hw_init(void)   //硬件初始化
27 {
28     unsigned int *gpio_config;
29     unsigned short data;
30     
31     gpio_config = ioremap(GPNCON,4);
32     data = readw(gpio_config);
33     data &= ~0xfff;
34     data |= 0xaaa;
35     writew(data,gpio_config);
36     gpio_dat = ioremap(GPNDAT,4);  //將數據寄存器地址轉化為虛擬地址
37 }
38 
39 int key_open(struct inode *node,struct file *filp)
40 {    
41     return 0;
42 }
43 struct file_operations key_fops = 
44 {
45     .open = key_open,
46 };
47 
48 struct miscdevice key_miscdev = 
49 {
50     .minor = 200,
51     .name = "key",
52     .fops = &key_fops,
53 };
54 
55 static int button_init(void)
56 {
57     misc_register(&key_miscdev);   //注冊混雜設備
58     
59     //按鍵硬件初始化
60     key_hw_init();
61     request_irq(IRQ_EINT(0),key_int,IRQF_TRIGGER_FALLING,"key",0);  //注冊中斷處理程序
62     //創建工作1
63     work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
64     INIT_WORK(work1,work1_func);
65     return 0;
66 }
67 
68 static void button_exit(void)
69 {
70     misc_deregister(&key_miscdev);   //注銷混雜設備
71     //注銷中斷處理程序
72     free_irq(IRQ_EINT(0),0);
73 }
74 
75 module_init(button_init);
76 module_exit(button_exit);

接下來的內容在下一個文章里包括:定時器去抖和阻塞性驅動程序設計


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM