本文,我們還是在前面的按鍵驅動程序的基礎上,引入定時器,來消除抖動。
一、內核定時器詳解
1.timer_list結構體
在/include/linux/timer.h中。
struct timer_list { struct list_head entry; //定時器鏈表,用于存放軟定時器,該鏈表根據定時器expirex字段的值將它們分組存放。 unsigned long expires; //定時器的到期時間,到達expires時間后,定時器將調用其成員函數function,其中將data字段作為function的參數。該字段表示的時間是以時間節拍為單位。例如如果你想定時一秒,則expires=jiffies+HZ*1。 void (*function)(unsigned long); //定時器到時處理函數 unsigned long data; //function 的參數 struct tvec_t_base_s *base; #ifdef CONFIG_TIMER_STATS void *start_site; char start_comm[16]; int start_pid; #endif }; |
時鐘中斷的發生頻率設定為HZ,HZ是一個與體系結構無關的常數,在文件
2.初始化定時器
①init_timer
void init_timer(struct timer_list *timer) { debug_timer_init(timer); __init_timer(timer); }
static void __init_timer(struct timer_list *timer) { timer->entry.next = NULL; timer->base = __raw_get_cpu_var(tvec_bases); #ifdef CONFIG_TIMER_STATS timer->start_site = NULL; timer->start_pid = -1; memset(timer->start_comm, 0, TASK_COMM_LEN); #endif } |
init_timer()函數被定義在kernel/timer.c中,實際上是將timer的entry的next指針置為NULL,為base字段賦值。
②. timer=TIMER_INITIALIZER(function,expires,data);
采用這種初始化方式,必須首先先寫好定時器處理函數function. TIMER_INITIALIZER宏的定義如下:
#define TIMER_INITIALIZER(_function, _expires, _data) { .entry = { .prev = TIMER_ENTRY_STATIC }, .function = (_function), .expires = (_expires), .data = (_data), .base = &boot_tvec_bases, } |
其中boot_tcec_bases是在kernel/timer中定義的一個全局的tvec_t_base_s類型的變量。
③.DEFINE_TIMER(timer,function,expires,data);
定義并初始化定時器timer,功能和前面差不多
#define DEFINE_TIMER(_name, _function, _expires, _data) struct timer_list _name = TIMER_INITIALIZER(_function, _expires, _data) |
④. setup_timer(&timer);
等同于前面,不過對base字段的賦值是調用了init_timer()函數。setup_timer()原型為:
static inline void setup_timer(struct timer_list * timer,void (*function)(unsigned long),unsigned long data) { timer->function = function; timer->data = data; init_timer(timer); } |
3.注冊定時器
在定義并初始化了定時器之后,就要調用add_timer()函數來將該定時器注冊到內核中,這樣定時器才會工作。在注冊之后,定時器就開始計時,在到達時間expires時,執行回調函數function(->data)。add_timer()函數的原型為:
static inline void add_timer(struct timer_list *timer) { BUG_ON(timer_pending(timer)); __mod_timer(timer, timer->expires); } |
4.刪除定時器
int del_timer(struct timer_list *timer);
從內核中刪除已經注冊的定時器timer。如果該定時器是活動的,則返回1,否則返回0。
int del_timer(struct timer_list *timer) { tvec_base_t *base; unsigned long flags; int ret = 0; timer_stats_timer_clear_start_info(timer); if (timer_pending(timer)) { base = lock_timer_base(timer, &flags); if (timer_pending(timer)) { detach_timer(timer, 1); ret = 1; } spin_unlock_irqrestore(&base->lock, flags); } return ret; } |
5.修改定時器時間
如果所給的要修改的時間等于定時器原來的時間并且定時器現在正處于活動狀態,則不修改,返回1,否則修改定時器時間,返回0。mod_timer()是一個非有效的更新處于活動狀態的定時器的時間的方法,如果定時器處于非活動狀態,則會激活定時器。在功能上,mod_timer()等價于:
del_timer(timer);
timer->expires=expires;
add_timer(timer);
int mod_timer(struct timer_list *timer, unsigned long expires) { BUG_ON(!timer->function); timer_stats_timer_set_start_info(timer); if (timer->expires == expires && timer_pending(timer)) return 1; return __mod_timer(timer, expires); } |
6.使用方法
1.創建定時器:struct timer_list my_timer;
2.初始化定時器:init_timer(&my_timer);
3.根據需要,設置定時器了:
my_timer.expires = jiffies + delay;
my_timer.data = 0;
my_timer.function = my_function;
4.激活定時器:add_timer(&my_timer);
7.實例解析
程序是在前面程序的基礎上修改的,(博文地址:http://www.cnblogs.com/lihaiyan/p/4295961.html)
為了結構更簡單,前面的程序使用了六個GPIO引腳,可能會相對復雜,為了更加堅定明了,此處簡化為一個GPIO引腳了,思路和前面差不多,這里就是使用GPIO2_10這個引腳
① 定義定時器
②在打開函數中初始化定時器,并且申請GPIO2_10中斷
③在中斷中修改定時器,并且激活定時器,則沒發生一次中斷則激活一次定時器達到消除抖動的目的
此處的中斷函數只有一個任務,就是激活定時器,然后將其他的功能均轉移至定時器調用的函數中。
④可以發現,在定時器中,將前面中斷中的功能全部實現了,一旦按鍵按下,接下來20ms內的抖動都無效,因為修改定時器無法修改已經激活的定時器,所以那20ms內高低電平的抖動都將無效,達到了消除抖動的功能
⑤編譯測試。結果如下圖所示:
附上驅動程序代碼:
1 /******************************
2 linux key_query
3 *****************************/
4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 #include 18 #include 19 20 #include 'mx257_gpio.h' 21 #include 'mx25_pins.h' 22 #include 'iomux.h' 23 24 #define Driver_NAME 'key_interrupt' 25 #define DEVICE_NAME 'key_interrupt' 26 27 #define GPIO2_21 MX25_PIN_CLKO 28 #define GPIO3_15 MX25_PIN_EXT_ARMCLK 29 #define GPIO2_10 MX25_PIN_A24 30 #define GPIO2_11 MX25_PIN_A25 31 #define GPIO2_8 MX25_PIN_A22 32 #define GPIO2_9 MX25_PIN_A23 33 #define GPIO2_6 MX25_PIN_A20 34 #define GPIO2_7 MX25_PIN_A21 35 //command 36 #define key_input 0 37 #define version 1 38 //定義各個按鍵按下的鍵值 39 struct pin_desc{ 40 unsigned int pin; 41 unsigned int key_val; 42 }; 43 //當按鍵按下時,鍵值分別為 以下值 44 struct pin_desc pins_desc[8] = { 45 {GPIO2_6, 0x01}, 46 {GPIO2_7, 0x02}, 47 {GPIO2_8, 0x03}, 48 {GPIO2_9, 0x04}, 49 {GPIO2_10, 0x05}, 50 {GPIO2_11, 0x06}, 51 {GPIO2_21, 0x07}, 52 {GPIO3_15, 0x08}, 53 }; 54 struct pin_desc * pindesc; 55 static int flag=0; 56 //定義一個全局變量,用于保存按下的鍵值 57 static unsigned int key_val; 58 59 struct timer_list my_timer; 60 unsigned int cnt_timer = 0; 61 #define TIMER_DELAY_20MS HZ/50 62 63 //interrupt head 64 static DECLARE_WAIT_QUEUE_HEAD(key_interrupt_wait); 65 static volatile unsigned char ev_press; 66 67 static int major=0; 68 69 //auto to create device node 70 static struct class *drv_class = NULL; 71 static struct class_device *drv_class_dev = NULL; 72 73 void timer_function(unsigned long data){ 74 int index = data; 75 cnt_timer ++; 76 printk('<0>enter timer_%d__%d',index,cnt_timer); 77 if(flag){ 78 //獲取按鍵鍵值 79 gpio_direction_input(IOMUX_TO_GPIO(GPIO2_10)); 80 key_val = gpio_get_value(IOMUX_TO_GPIO(pindesc->pin)); 81 if(!key_val){ 82 key_val = pindesc->key_val; 83 printk('<0>get key 0x%x',key_val); 84 printk('<0>KEY_DOWNn'); 85 flag = 0; 86 ev_press = 1; 87 wake_up_interruptible(&key_interrupt_wait); 88 } 89 } 90 } 91 /* 中斷程序key_irq */ 92 static irqreturn_t key_irq(int irq, void *dev_id) 93 { 94 pindesc = (struct pin_desc *)dev_id; 95 //發生了中斷 96 printk('<0>2_10function interrupt key_irq!nn'); 97 mod_timer(&my_timer, jiffies+HZ/50); 98 flag = 1; 99 100 return IRQ_RETVAL(IRQ_HANDLED); 101 } 102 103 104 /* 應用程序對設備文件/dev/key_query執行open(...)時, 105 * 就會調用key_open函數*/ 106 static int key_open(struct inode *inode, struct file *file) 107 { 108 printk('<0>function open 2_10! nn'); 109 110 init_timer(&my_timer); 111 my_timer.function = timer_function; 112 my_timer.expires = jiffies+HZ/50; 113 add_timer(&my_timer); //定時器應該在此時被啟動 114 115 request_irq(IOMUX_TO_IRQ(GPIO2_10), key_irq, IRQF_TRIGGER_FALLING, 'key_GPIO2_10', &pins_desc[4]); 116 return 0; 117 } 118 119 120 121 static int key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) 122 { 123 int ret; 124 if(filp->f_flags & O_NONBLOCK){ 125 if(!ev_press) 126 return -EAGAIN; 127 }else{ 128 //如果按鍵沒有按下,沒有中斷,休眠 129 wait_event_interruptible(key_interrupt_wait,ev_press); 130 } 131 ret = copy_to_user(buff,&key_val,sizeof(key_val)); 132 if(ret){ 133 ; 134 } 135 ev_press = 0; 136 return sizeof(key_val); 137 } 138 139 static ssize_t key_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos) 140 { 141 printk('<0>function write!nn'); 142 143 return 1; 144 } 145 146 static int key_release(struct inode *inode, struct file *filp)
上一篇:IMX257 linux設備驅動之Cdev結構
下一篇:【改進信號量】IMX257實現GPIO-IRQ中斷按鍵獲取鍵值驅動程序
推薦閱讀最新更新時間:2025-06-07 23:41







設計資源 培訓 開發板 精華推薦
- 肖特激光玻璃助力實現核聚變領域又一個歷史性里程碑
- 英飛凌SEMPER? NOR閃存系列獲得ASIL-D功能安全認證
- 如何在開關模式電源中運用氮化鎵技術
- LGVL配合FreeType為可變字體設置字重-ESP32篇
- 使用樹莓派進行 ESP32 Jtag 調試
- ESP32怎么在SPIFFS里面存儲html,css,js文件,以及網頁和arduino的通訊
- ESP32 freeRTOS使用測試
- API調用小記(Touchdesigner和ESP32)
- 芯科科技Tech Talks技術培訓重磅回歸: 賦能物聯網創新,共筑智能互聯未來
- 關于ESP32/8266使用async-mqtt-client庫的一些基本介紹