一、為什么要消抖?
二、如何消抖?
三、Linux內(nèi)核定時器
四、源碼
五、測試
本文將在Linux驅(qū)動入門(五)阻塞方式實現(xiàn)按鍵驅(qū)動的基礎(chǔ)上,使用定時器消除按鍵抖動
一、為什么要消抖?
對于沒有做硬件消抖的按鍵,在按下按鍵到電平穩(wěn)定期間,會有一段抖動,此時電平會在高電平和低電平不斷地跳動
如果我們的按鍵中斷采用下降沿觸發(fā),從圖中可以看出有多段下降沿,那么想必按下一次按鍵,中斷會被觸發(fā)多次,那么我們button_read函數(shù)也會被喚醒多次,這就導(dǎo)致一次按鍵,應(yīng)用層多次返回,這并不是我們想要的效果
二、如何消抖?
軟件消抖的原理非常簡單,只要通過延遲(一般為10ms),等待按鍵穩(wěn)定后,再來檢測按鍵是否被按下
在Linux驅(qū)動中,如果使用在原理打轉(zhuǎn)的方式來實現(xiàn)延時的話,這是非常不可取的,在中斷處理中更是大忌
所以要通過定時器來實現(xiàn)延遲,那具體怎么做呢?
在上面介紹中,我們了解在按鍵按下的時候會觸發(fā)多次按鍵中斷,那么我們可以在按鍵中斷設(shè)置定時器,在定時器的處理函數(shù)中去讀取按鍵狀態(tài)
具體的處理方法是,在每一個下降沿觸發(fā)的中斷處理函數(shù)中,將定時器設(shè)置為10ms后超時,這樣子,將在最后一個下降沿10ms后,定時器超時,再檢測按鍵狀態(tài),達(dá)到消抖的目的,如圖所示
三、Linux內(nèi)核定時器
定時器對象
struct timer_list timer;
初始化定時器
void init_timer(struct timer_list * timer);
/* 設(shè)置定時器的處理函數(shù)和傳遞參數(shù) */
timer.function = &xxx_do_timer;
timer.data = (unsigned long)dev;
內(nèi)核提供一個宏定義完成上述功能
setup_timer(timer, fn, data)
注冊定時器
void add_timer(struct timer_list *timer)
刪除定時器
int del_timer(struct timer_list *timer)
修改定時器超時時間
int mod_timer(struct timer_list *timer, unsigned long expires)
內(nèi)核中的jiffies表示時鐘節(jié)拍數(shù),宏定義HZ表示時鐘節(jié)拍的頻率
Linux內(nèi)核的HZ為100,表示1s時鐘跳動100下,所以時鐘周期的10ms
如果要延遲10ms,可以這么做
mod_timer(timer, jiffies + HZ/100); #include static dev_t dev_id; /* 定義并初始化一個等待隊列頭 */ static struct timer_list timer; static void timer_fun(unsigned long data) /* 讀取按鍵值 */ /* 喚醒等待隊列 */ static irqreturn_t button_irq(int irq, void *data) return IRQ_HANDLED; static int button_open(struct inode *inode, struct file *file) return 0; static ssize_t button_read(struct file *file, char __user *data, size_t size, loff_t *loff) /* 睡眠等待 */ return sizeof(val); static int button_release(struct inode *inode, struct file *file) return 0; static struct file_operations button_fops = { static __init int button_init(void) /* 分配字符設(shè)備 */ /* 設(shè)置字符設(shè)備 */ /* 注冊字符設(shè)備 */ /* 創(chuàng)建設(shè)備節(jié)點 */ gpio_request(S5PV210_GPH0(2), 'button'); setup_timer(&timer, timer_fun, 0); return 0; static __exit void button_exit(void) /* 注銷字符設(shè)備 */ /* 注銷注冊的設(shè)備號 */ gpio_free(S5PV210_GPH0(2)); del_timer(&timer); module_init(button_init); MODULE_LICENSE('GPL'); #include #define BUTTON_DEV '/dev/button' int main(int argc, char* argv[]) while(1) if(val == 0) return 0; 運行效果,只有按鍵按下時,read函數(shù)才會返回,且按鍵按下一次,read只返回一次
四、源碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
static struct cdev *button_dev;
static struct class *button_class;
static DECLARE_WAIT_QUEUE_HEAD(button_wq_head);
static int button_conditon;
static int button_val;
{
/* 判斷等待隊列中是否有等待元素 */
if(!waitqueue_active(&button_wq_head))
return;
button_val = gpio_get_value(S5PV210_GPH0(2));
if(button_val) //按鍵未被按下
return;
button_conditon = 1;
wake_up_interruptible(&button_wq_head);
}
{
mod_timer(&timer, jiffies+HZ/100);
}
{
int ret;
ret = request_irq(IRQ_EINT2, button_irq, IRQF_TRIGGER_FALLING, 'button_irq', NULL);
}
{
int ret;
int val;
button_conditon = 0;
wait_event_interruptible(button_wq_head, button_conditon);
button_conditon = 0;
val = button_val;
ret = copy_to_user(data, &val, sizeof(val));
}
{
free_irq(IRQ_EINT2, NULL);
}
.owner = THIS_MODULE,
.open = button_open,
.read = button_read,
.release = button_release,
};
{
/* 申請設(shè)備號 */
alloc_chrdev_region(&dev_id, 1, 1, 'button');
button_dev = cdev_alloc();
cdev_init(button_dev, &button_fops);
cdev_add(button_dev, dev_id, 1);
button_class = class_create(THIS_MODULE, 'button'); //創(chuàng)建類
device_create(button_class, NULL, dev_id, NULL, 'button'); //創(chuàng)建設(shè)備節(jié)點
add_timer(&timer);
}
{
/* 注銷設(shè)備節(jié)點 */
device_destroy(button_class, dev_id);
class_destroy(button_class);
cdev_del(button_dev);
kfree(button_dev);
unregister_chrdev_region(dev_id, 1);
}
module_exit(button_exit);
五、測試
測試應(yīng)用程序
#include
#include
#include
#include
{
int val;
int fd = open(BUTTON_DEV, O_RDONLY);
if(fd < 0)
{
printf('failed to open %sn', BUTTON_DEV);
return -1;
}
{
read(fd, &val, sizeof(val));
printf('read returnn');
{
printf('button pressn');
}
}
close(fd);
}
加載驅(qū)動模塊,執(zhí)行測試應(yīng)用程序
上一篇:Linux驅(qū)動入門(六)poll機(jī)制實現(xiàn)按鍵驅(qū)動
下一篇:Linux內(nèi)核靜態(tài)映射表的建立過程
推薦閱讀最新更新時間:2025-05-27 12:42






設(shè)計資源 培訓(xùn) 開發(fā)板 精華推薦
- Microchip 升級數(shù)字信號控制器(DSC)產(chǎn)品線 推出PWM 分辨率和 ADC 速度業(yè)界領(lǐng)先的新器件
- 意法半導(dǎo)體STM32MP23x:突破成本限制的工業(yè)AI應(yīng)用核心
- 意法半導(dǎo)體推出用于匹配遠(yuǎn)距離無線微控制器STM32WL33的集成的匹配濾波芯片
- ESP32開發(fā)板連接TFT顯示屏ST7789跳坑記
- 如何讓ESP32支持analogWrite函數(shù)
- LGVL配合FreeType為可變字體設(shè)置字重-ESP32篇
- 使用樹莓派進(jìn)行 ESP32 Jtag 調(diào)試
- ESP32怎么在SPIFFS里面存儲html,css,js文件,以及網(wǎng)頁和arduino的通訊
- ESP32 freeRTOS使用測試
- CP2400-DK,基于CP2400的LCD控制器開發(fā)套件
- 使用 NXP Semiconductors 的 MCF52259CAG80 的參考設(shè)計
- LT6658AIMSE-2.5 汽車基準(zhǔn)和電源電壓應(yīng)用的典型應(yīng)用電路
- 使用 Analog Devices 的 LTC2908IDDB-C1 的參考設(shè)計
- LM5576 大功率寬電壓DCDC,輸入最高75V 3A
- #第六屆立創(chuàng)電賽#智能家居控制器_小樂同學(xué)
- LTC2183 演示板,16 位 80Msps 雙路 ADC,DDR LVDS 輸出,5-140MHz
- 使用 RP40-4824DFR DC/DC 轉(zhuǎn)換器并根據(jù) EN55022 A 類(單輸出)進(jìn)行 EMC 濾波的典型應(yīng)用
- 7W、91V AC 到 DC 單路輸出電源,用于適配器 AC 到 DC 電源
- 使用 Analog Devices 的 LTC1596-1AISW 的參考設(shè)計
- 納祥科技2W 24位數(shù)字功放NX4920,可用于AI語音播報、WIFI播放器
- 常用解調(diào)器的定義和工作原理
- 從性能與網(wǎng)絡(luò)傳輸出發(fā),講講鐵威馬MAX系列為什么一騎絕塵
- 惠普選中Hailo下一代人工智能加速器,革新零售業(yè)與酒店業(yè)運營模式
- 跨國商務(wù)溝通困局破解之道:時空壺 W4Pro 全場景應(yīng)用解析
- 從矢量降噪到雙向同傳,時空壺 W4Pro 如何重構(gòu) AI 同傳技術(shù)標(biāo)準(zhǔn)?
- LoRa+NB-IoT雙模融合,地下車庫信號盲區(qū)電梯場景等的冗余通信
- LoRa與UWB的“定位之戰(zhàn)”,成本敏感場景高精度需求的場景切割
- 時空壺X1再升級:引領(lǐng)AI同傳新時代,革新演講翻譯體驗
- ARXML 規(guī)則下 ECU 總線通訊與 ADTF 測試方案
- DeepNude現(xiàn)在火了,看看一鍵脫衣AI原理的秘密
- 技術(shù)文章—積分電路原理:放大器與電容的變身
- 技術(shù)文章—射頻技術(shù)能否有效防范信用卡欺詐?
- 有關(guān)AI處理器的名詞解釋一覽
- 臺積電的秘密武器將決定未來芯片走勢
- 兆易創(chuàng)新:公司MCU產(chǎn)品價格主要受供需關(guān)系的影響
- 集微指數(shù)上漲0.99% 大富科技已向華為等供貨5G陶瓷濾波器
- 2021年我國將新建超過60萬個基站:5G概念股掀漲停潮
- 大富科技:已經(jīng)向華為、愛立信等批量供貨5G陶瓷濾波器
- 中科創(chuàng)星:專注于硬科技創(chuàng)業(yè)投資和孵化