欧美极品高清xxxxhd,国产日产欧美最新,无码AV国产东京热AV无码,国产精品人与动性XXX,国产传媒亚洲综合一区二区,四库影院永久国产精品,毛片免费免费高清视频,福利所导航夜趣136

標(biāo)題: 嵌入式設(shè)備中按鍵的硬件防抖, 軟件防抖和按鍵消息處理 [打印本頁(yè)]

作者: michaelchain    時(shí)間: 2021-12-8 13:38
標(biāo)題: 嵌入式設(shè)備中按鍵的硬件防抖, 軟件防抖和按鍵消息處理
按鍵

按鈕就是一種配備了彈性裝置的雙狀態(tài)開關(guān): 連通和斷開. 由于彈性部件的作用, 大部分時(shí)間按鈕是斷開的. 從電路角度看, 按鈕扮演的角色就是開路和短路. 按鈕在嵌入式設(shè)備中是常見組件, 通常情況下, 一個(gè)按鈕需要有一個(gè)弱上拉或下拉電阻, 對(duì)于STM32而言, GPIO口已經(jīng)自帶了弱上拉電阻, 可以在程序中設(shè)置是否使用, STC系列的MCU, 要看具體型號(hào)和具體的IO口, 例如經(jīng)典的stc89c51/stc89c52, P0口就是漏極開路的雙向IO口, 使用時(shí)當(dāng)電流流出需外接上拉電阻.

將按鈕連接到MCU通常有兩種方式, 一種是低電平有效, 另一種是高電平有效, 在低電平有效的電路中, 當(dāng)按鈕按下時(shí), 將在引腳上讀取到邏輯0, 按鈕釋放后讀取的是1; 在高電平有效電路中則正好相反.

上拉/下拉電阻阻值選取

如果電阻太小, 電流過大可能會(huì)損壞元件, 一般這個(gè)阻值在幾K到幾十K歐. 阻值的大小受GPIO的邏輯轉(zhuǎn)換時(shí)間限制, 對(duì)于STM32, IO口電容為5pF, 上拉電阻可以為10K歐.

按鍵抖動(dòng)效應(yīng) The bounce effect

按鈕在按下和釋放時(shí)都有可能產(chǎn)生抖動(dòng)效應(yīng), 會(huì)導(dǎo)致過程中產(chǎn)生多次短路與開路之間的切換, 對(duì)于這個(gè)問題, 需要從硬件和軟件方面來(lái)解決:
* 硬件上, 低通濾除抖動(dòng)
* 軟件上, 增加第一次檢測(cè)到動(dòng)作后的 dead time

硬件處理

硬件防抖動(dòng)(debouncing)是需要優(yōu)先考慮的方法, 比軟件方式更穩(wěn)定和高效. 可以通過在GPIO口和按鍵之間添加一個(gè)低通濾波電路實(shí)現(xiàn).

實(shí)現(xiàn)低通濾波最簡(jiǎn)單的電路就是 RC濾波. 其阻值和容值怎么計(jì)算呢? 取決于抖動(dòng)的容忍頻率. 可以使用以下計(jì)算式

$f_{LP} = \frac{1}{2 \pi RC} = 0.1 \cdot f_{bounce}$

低通頻率不能太低, 否則會(huì)濾除正常的操作, 在正常情況下, 一個(gè)人不太可能以100赫茲的頻率去按按鍵, 所以
* 將低通頻率設(shè)為10KHz, 對(duì)應(yīng)的就是160歐的電阻和100nF的電容, 或1K歐電阻和16nF電容
* 將低通頻率設(shè)為1KHz, 對(duì)應(yīng)的就是1K歐電阻和160nF電容
* 將低通頻率設(shè)為100Hz, 對(duì)應(yīng)的就是10K歐電阻和160nF電容

下面的電路中, 使用了10KR電阻和100nF(104)電容作為硬件防抖處理



軟件處理

軟件處理分兩種情況, 如果僅僅需要檢測(cè)短按, 是比較簡(jiǎn)單的, 聲明一個(gè)volatile static a變量用于表示按鍵狀態(tài), 聲明一個(gè)static uint8_t b變量用于計(jì)數(shù), 每個(gè)循環(huán)的檢測(cè)中, 低電平(假定按下為低電平)b加1, 當(dāng)b值計(jì)數(shù)到達(dá)一個(gè)閾值時(shí)表示按鈕按下, 將a置位, 當(dāng)循環(huán)中檢測(cè)到高電平時(shí)將a和b都清零.

如果需要檢測(cè)短按和長(zhǎng)按, 就需要三個(gè)變量, 除了上面的a和b以外, 再增加一個(gè)循環(huán)計(jì)數(shù)c. 檢測(cè)的每個(gè)循環(huán)中, 先按檢測(cè)短按的方式, 做短按判斷, 另外再通過第三個(gè)變量記錄短按的次數(shù), 當(dāng)達(dá)到預(yù)設(shè)的長(zhǎng)按判斷的次數(shù)閾值時(shí), 判斷為長(zhǎng)按. 要注意的是
1. 短按的置位要由按鈕釋放觸發(fā)
1. 長(zhǎng)按的置位由按鈕按下觸發(fā)
1. 長(zhǎng)按釋放時(shí), 要避免判斷為短按

下面是一段實(shí)際應(yīng)用中的代碼, 會(huì)在一個(gè)間隔10ms的定時(shí)器中調(diào)用, 其中
* KEY1 為按鍵對(duì)應(yīng)的IO口, 例如P01
* debounce[0] 為按鍵1對(duì)應(yīng)的防抖延時(shí)計(jì)數(shù)器
* k1_pressed 當(dāng)判斷按鍵1為按下時(shí)置位, 全局使用
* switchcount[0] 按鍵1對(duì)應(yīng)的長(zhǎng)按鍵計(jì)數(shù)器, SW_CNTMAX為判斷閾值
* k1_long_pressed 當(dāng)判斷按鍵1為長(zhǎng)按時(shí)置位, 全局使用
* event 按鍵事件, 全局使用

  1. void read_key1(void)

  2. {

  3.     //未按下時(shí), KEY1處于高電平, 因此debounce為0xFF

  4.     debounce[0] = (debounce[0] << 1) | KEY1;

  5.     if (debounce[0] == 0x00) { // 8次檢測(cè)都為0, 按下置位

  6.         k1_pressed = 1;

  7.         if (!k1_long_pressed) { // 如果長(zhǎng)按未置位, 計(jì)數(shù)加1

  8.             switchcount[0]++;

  9.         }

  10.     } else { // 按鍵已松開或未按下

  11.         if (k1_pressed) {

  12.             if (!k1_long_pressed) {

  13.                 // 如果短按已置位, 但是長(zhǎng)按未置位, 按短按發(fā)出系統(tǒng)消息

  14.                 event = EV_K1_SHORT;

  15.             }

  16.             // 清理狀態(tài)和計(jì)數(shù)器

  17.             k1_pressed = 0;

  18.             k1_long_pressed = 0;

  19.             switchcount[0] = 0;

  20.         }

  21.     }

  22.     if (switchcount[0] > SW_CNTMAX) {

  23.         // 如果長(zhǎng)按計(jì)數(shù)器已經(jīng)達(dá)到閾值, 長(zhǎng)按置位(避免松開時(shí)發(fā)出短按消息), 發(fā)出長(zhǎng)按系統(tǒng)消息

  24.         k1_long_pressed = 1;

  25.         switchcount[0] = 0;

  26.         event = EV_K1_LONG;

  27.     }

  28. }
復(fù)制代碼

按鍵消息處理

按鍵的系統(tǒng)消息是通過狀態(tài)機(jī)模型進(jìn)行處理的, 在每個(gè)按鍵處理循環(huán)中,
1. 清除全局消息
1. 根據(jù)當(dāng)前的按鍵狀態(tài), 判斷長(zhǎng)按和短按對(duì)應(yīng)的下一個(gè)狀態(tài)
1. 下一個(gè)循環(huán), 會(huì)跳到對(duì)應(yīng)的按鍵狀態(tài), 再去判斷下一個(gè)狀態(tài)
1. 根據(jù)按鍵狀態(tài)決定當(dāng)前的顯示模式

  1. void main(void)

  2. {

  3.     //...

  4.     while (true)

  5.     {

  6.         while (!loop_gate); // wait for open every 100ms

  7.         loop_gate = 0; // close gate



  8.         ev = event;

  9.         event = EV_NONE;

  10.         switch (kmode)

  11.         {

  12.             case K_DISP_SEC:

  13.                 dmode = D_DISP_SEC;

  14.                 if (ev == EV_K2_SHORT) {

  15.                     kmode = K_DISP_ALARM;

  16.                     m_timeout = TIMEOUT_SHORT;

  17.                 }

  18.                 break;

  19.             //...

  20.             case K_NORMAL:

  21.             default:

  22.                 dmode = D_NORMAL;

  23.                

  24.                 if (ev == EV_K1_SHORT) {

  25.                     kmode = K_DISP_DATE;

  26.                     m_timeout = TIMEOUT_SHORT;

  27.                 } else if (ev == EV_ALARM) {

  28.                     kmode = K_BUZZ_ALARM;

  29.                     m_timeout = TIMEOUT_LONG;

  30.                 }

  31.                 else if (ev == EV_K1_LONG)

  32.                     kmode = K_SET_MINUTE;

  33.                 else if (ev == EV_K2_SHORT)

  34.                     kmode = K_DISP_SEC;

  35.                 else if (ev == EV_K2_LONG)

  36.                     kmode = K_SET_ALARM_MINUTE;

  37.         }

  38. //...

  39.     }

  40. }
復(fù)制代碼

參考
* 按鍵防抖的硬件和軟件處理:可以去百度這篇文章:Dealing with push-buttons using an STM32
* 例子中的完整代碼 https://github.com/IOsetting/HML ... 2c887/alarm_clock.c


作者: lzzasd    時(shí)間: 2021-12-10 08:31
好貼    主要是10KR電阻和100nF(104)電容
作者: adad2    時(shí)間: 2021-12-10 08:39
好資料,51黑有你更精彩!!!!




歡迎光臨 (http://www.raoushi.com/bbs/) Powered by Discuz! X3.1