一個學妹寫的按鍵檢測函數把我秀翻了!

2022-01-07 嵌入式Linux

摘要:今年實驗室來了三個學妹,其中一個學妹以前是物聯網專業的,進了實驗室老師二話沒說:先把STM32單片機過一遍,有啥問題就找小師弟。還好單片機小師弟會玩一點點,玩的也不好,所以一起學習吧!

上來第一個例程就是使用按鍵點亮一個LED燈,好傢夥。點燈小師弟比較在行,畢竟32、FPGA、Linux的小燈都被小師弟點了一遍。哈哈哈!所以今天還是來說一說按鍵檢測吧!

一、如何進行按鍵檢測

檢測按鍵有中斷方式和GPIO查詢方式兩種。推薦大家用GPIO查詢方式。

1.從裸機的角度分析

中斷方式:中斷方式可以快速地檢測到按鍵按下,並執行相應的按鍵程序,但實際情況是由於按鍵的機械抖動特性,在程序進入中斷後必須進行濾波處理才能判定是否有效的按鍵事件。如果每個按鍵都是獨立的接一個 IO 引腳,需要我們給每個 IO 都設置一個中斷,程序中過多的中斷會影響系統的穩定性。中斷方式跨平臺移植困難。

查詢方式:查詢方式有一個最大的缺點就是需要程序定期的去執行查詢,耗費一定的系統資源。實際上耗費不了多大的系統資源,因為這種查詢方式也只是查詢按鍵是否按下,按鍵事件的執行還是在主程序裡面實現。

2.從OS的角度分析中斷方式:在 OS 中要儘可能少用中斷方式,因為在RTOS中過多的使用中斷會影響系統的穩定性和可預見性。只有比較重要的事件處理需要用中斷的方式。查詢方式:對於用戶按鍵推薦使用這種查詢方式來實現,現在的OS基本都帶有CPU利用率的功能,這個按鍵FIFO佔用的還是很小的,基本都在1%以下。二、最簡單的按鍵檢測程序

先給他說了一種經典的按鍵檢測代碼,相信大多數人使用按鍵函數都見過它,很簡單就不過多介紹了!

#define KEY0_PRES 1  //KEY0  
#define KEY1_PRES 2  //KEY1 
#define WKUP_PRES 3  //WK_UP 

u8 KEY_Scan(u8 mode)
{  
 static u8 key_up=1;//按鍵按鬆開標誌
 if(mode)key_up=1;  //支持連按    
 if(key_up&&(KEY0==0||KEY1==0||WK_UP==1))
 {
  delay_ms(10);//去抖動 
  key_up=0;
  if(KEY0==0)return KEY0_PRES;
  else if(KEY1==0)return KEY1_PRES;
  else if(WK_UP==1)return WKUP_PRES; 
 }else if(KEY0==1&&KEY1==1&&WK_UP==0)key_up=1;       
 return 0;// 無按鍵按下
}

int main(void)

 u8 t=0;   
 delay_init();       //延時函數初始化   
 LED_Init();       //初始化與LED連接的硬體接口
 KEY_Init();           //初始化與按鍵連接的硬體接口
 LED=0;     //點亮LED
 while(1)
 {
  t=KEY_Scan(0);  //得到鍵值
  switch(t)
  {     
   case KEY0_PRES:  //如果KEY0按下
     LED=!LED;
    break;
   default:
    delay_ms(10); 
  } 
 }
}

如果你在工作中使用這種代碼,有可能會被同事笑話。當然我這裡並不是說這種代碼不好,不管黑貓白貓,能抓住老鼠就是好貓。只要能滿足項目需求實現對應的功能就是好代碼。但是如果你使用下面這種個人感覺可能會更好。

其實也並沒有什麼神秘感,就是使用了FIFO機制。參考的就是安富萊的按鍵例程,不過原始碼相對比較複雜,對於初學者並不友好,所以小小的修改了一下,僅供參考!

在前面分享了使用系統滴答定時器實現了多個軟體定時器,在按鍵FIFO中也需要使用這個定時器。在系統的開始我們會啟動一個10ms的軟體定時器。在這個10ms的軟體定時器中不斷的進行按鍵掃描,與其他的任務互不影響。

三、為什麼要了解FIFO

要回答什麼是FIFO,先要回答為什麼要使用FIFO。只有搞清楚使用FIFO的好處,你才會有意無意的使用FIFO。學習FIFO機制和狀態機機制一樣,都是在裸機編程中非常重要的編程思想。編程思想很重要。初級coder總是在關注代碼具體是怎麼寫,高級coder關注的是程序的框架邏輯,而不是某個細節。只要你框架邏輯通了,則一通百通。

四、什麼是FIFO

FIFO是先入先出的意思,即誰先進入隊列,誰先出去。比如我們需要串口列印數據,當使用緩存將該數據保存的時候,在輸出數據時必然是先進入的數據先出去,那麼該如何實現這種機制呢?首先就是建立一個緩存空間,這裡假設為10個字節空間進行說明。

從這張圖就知道如果要使用FIFO,就要定義一個結構體,而這個結構體至少應該有三個成員。數組buf、讀指針read、寫指針write

typedef struct
{
 uint8_t Buf[10];  /* 緩衝區 */
 uint8_t Read;   /* 緩衝區讀指針*/
 uint8_t Write;   /* 緩衝區寫指針 */
}KEY_FIFO_T;

緩存一開始沒有數據,並且用一個變量write指示下一個寫入緩存的索引地址,這裡下一個存放的位置就是0,用另一個變量read 指示下一個讀出緩存的索引地址,並且下一個讀出數據的索引地址也是0。目前隊列中是沒有數據的,也就是不能讀出數據,隊列為空的判斷條件在這裡就是兩個索引值相同。

現在開始存放數據:

在這裡可以看到隊列中加入了9個數據,並且每加入一個數據後隊尾索引加 1,隊頭不變,這就是數據加入隊列的過程。但是緩存空間只有10個,如何判斷隊列已滿呢?如果只是先一次性加數據到隊列中,然後再讀出數據,那這裡的判斷條件顯然是隊尾索引為9。

好了這就是FIFO的基本原理,下面來看一下按鍵FIFO是怎麼操作的

我們這裡以5個字節的FIFO空間進行說明。Write變量表示寫位置,Read 變量表示讀位置。初始狀態時,Read = Write = 0。

我們依次按下按鍵 K1,K2,那麼FIFO中的數據變為:

如果 Write!= Read,則我們認為有新的按鍵事件。我們通過函數KEY_FIFO_Get()讀取一個按鍵值進行處理後,Read 變量變為 1。Write 變量不變。

繼續通過函數KEY_FIFO_Get()讀取 3 個按鍵值進行處理後,Read 變量變為 4。此時Read = Write= 4。兩個變量已經相等,表示已經沒有新的按鍵事件需要處理。

有一點要特別的注意,如果 FIFO 空間寫滿了,Write 會被重新賦值為 0,也就是重新從第一個字節空間填數據進去,如果這個地址空間的數據還沒有被及時讀取出來,那麼會被後來的數據覆蓋掉,這點要引起大家的注意。我們的驅動程序開闢了 10 個字節的 FIFO 緩衝區,對於一般的應用足夠了。

五、按鍵FIFO的優點可靠地記錄每一個按鍵事件,避免遺漏按鍵事件。特別是需要實現按鍵的按下、長按、自動連發、彈起等事件時。讀取按鍵的函數可以設計為非阻塞的,不需要等待按鍵抖動濾波處理完畢。按鍵 FIFO 程序在嘀嗒定時器中定期的執行檢測,不需要在主程序中一直做檢測,這樣可以有效地降低系統資源消耗。六、按鍵 FIFO 的實現1.定義結構體

在我們的key.h文件中定義一個結構體類型為KEY_FIFO_T的結構體。就是前面說的那個結構體。這只是類型聲明,並沒有分配變量空間。

typedef struct
{
 uint8_t Buf[10];  /* 緩衝區 */
 uint8_t Read;   /* 緩衝區讀指針*/
 uint8_t Write;   /* 緩衝區寫指針 */
}KEY_FIFO_T;

接著在key.c 中定義 s_tKey 結構變量, 此時編譯器會分配一組變量空間。

static KEY_FIFO_T s_tKey;/* 按鍵FIFO變量,結構體 */

好了按鍵FIFO的結構體數據類型就定義完了,很簡單吧!

2.將鍵值寫入FIFO

既然結構體都定義好了,接著就是往這個FIFO的數組中寫入數據,也就是按鍵的鍵值,用來模擬按鍵的動作了。

/*
**********************************************************
* 函 數 名: KEY_FIFO_Put
* 功能說明: 將1個鍵值壓入按鍵FIFO緩衝區。可用於模擬一個按鍵。
* 形    參:  _KeyCode : 按鍵代碼
* 返 回 值: 無
**********************************************************
*/
void KEY_FIFO_Put(uint8_t _KeyCode)
{
 s_tKey.Buf[s_tKey.Write] = _KeyCode;

 if (++s_tKey.Write  >= KEY_FIFO_SIZE)
 {
  s_tKey.Write = 0;
 }
}

函數的主要功能就是將按鍵代碼_KeyCode寫入到FIFO中,而這個FIFO就是我們定義結構體的這個數組成員,每寫一次,就是每調用一次KEY_FIFO_Put()函數,寫指針write就++一次,也就是向後移動一個空間,如果FIFO空間寫滿了,也就是s_tKey.Write >= KEY_FIFO_SIZE,Write會被重新賦值為 0。

3.從FIFO讀出鍵值

有寫入鍵值當然就有讀出鍵值。

/*
***********************************************************
* 函 數 名: KEY_FIFO_Get
* 功能說明: 從按鍵FIFO緩衝區讀取一個鍵值。
* 形    參: 無
* 返 回 值: 按鍵代碼
************************************************************
*/
uint8_t KEY_FIFO_Get(void)
{
 uint8_t ret;

 if (s_tKey.Read == s_tKey.Write)
 {
  return KEY_NONE;
 }
 else
 {
  ret = s_tKey.Buf[s_tKey.Read];

  if (++s_tKey.Read >= KEY_FIFO_SIZE)
  {
   s_tKey.Read = 0;
  }
  return ret;
 }
}

如果寫指針和讀出的指針相等,那麼返回值就為0,表示按鍵緩衝區為空,所有的按鍵時間已經處理完畢。如果不相等就說明FIFO的緩衝區不為空,將Buf中的數讀出給ret變量。同樣,如果FIFO空間讀完了,沒有緩存了,也就是s_tKey.Read >= KEY_FIFO_SIZE,Read也會被重新賦值為 0。按鍵的鍵值定義在key.h 文件,下面是具體內容:

typedef enum
{
 KEY_NONE = 0,   /* 0 表示按鍵事件 */

 KEY_1_DOWN,    /* 1鍵按下 */
 KEY_1_UP,    /* 1鍵彈起 */
 KEY_1_LONG,    /* 1鍵長按 */

 KEY_2_DOWN,    /* 2鍵按下 */
 KEY_2_UP,    /* 2鍵彈起 */
 KEY_2_LONG,    /* 2鍵長按 */

 KEY_3_DOWN,    /* 3鍵按下 */
 KEY_3_UP,    /* 3鍵彈起 */
 KEY_3_LONG,    /* 3鍵長按 */
}KEY_ENUM;

必須按次序定義每個鍵的按下、彈起和長按事件,即每個按鍵對象佔用 3 個數值。推薦使用枚舉enum, 不用#define的原因是便於新增鍵值,方便調整順序。使用{ } 將一組相關的定義封裝起來便於理解。編譯器也可幫我們避免鍵值重複。

4.按鍵檢測程序

上面說了如何將按鍵的鍵值存入和讀出FIFO,但是既然是按鍵操作,就肯定涉及到按鍵消抖處理,還有按鍵的狀態是按下還是彈起,是長按還是短按。所以為了以示區分,我們用還需要給每一個按鍵設置很多參數,就需要再定義一個結構體KEY_T,讓每個按鍵對應1個全局的結構體變量。

typedef struct
{
 /* 下面是一個函數指針,指向判斷按鍵手否按下的函數 */
 uint8_t (*IsKeyDownFunc)(void); /* 按鍵按下的判斷函數,1表示按下 */

 uint8_t  Count;   /* 濾波器計數器 */
 uint16_t LongCount;  /* 長按計數器 */
 uint16_t LongTime;  /* 按鍵按下持續時間, 0表示不檢測長按 */
 uint8_t  State;   /* 按鍵當前狀態(按下還是彈起) */
 uint8_t  RepeatSpeed; /* 連續按鍵周期 */
 uint8_t  RepeatCount; /* 連續按鍵計數器 */
}KEY_T;

在key.c 中定義s_tBtn結構體數組變量。

static KEY_T   s_tBtn[3] = {0};

每個按鍵對象都分配一個結構體變量,這些結構體變量以數組的形式存在將便於我們簡化程序代碼行數。因為我的硬體有3個按鍵,所以這裡的數組元素為3。使用函數指針IsKeyDownFunc可以將每個按鍵的檢測以及組合鍵的檢測代碼進行統一管理。

因為函數指針必須先賦值,才能被作為函數執行。因此在定時掃描按鍵之前,必須先執行一段初始化函數來設置每個按鍵的函數指針和參數。這個函數是void KEY_Init(void)。

void KEY_Init(void)
{
 KEY_FIFO_Init();  /* 初始化按鍵變量 */
 KEY_GPIO_Config();  /* 初始化按鍵硬體 */
}

下面是KEY_FIFO_Init函數的定義:

static void KEY_FIFO_Init(void)
{
 uint8_t i;

 /* 對按鍵FIFO讀寫指針清零 */
 s_tKey.Read = 0;
 s_tKey.Write = 0;

 /* 給每個按鍵結構體成員變量賦一組預設值 */
 for (i = 0; i < HARD_KEY_NUM; i++)
 {
  s_tBtn[i].LongTime = 100;/* 長按時間 0 表示不檢測長按鍵事件 */
  s_tBtn[i].Count = 5/ 2; /* 計數器設置為濾波時間的一半 */
  s_tBtn[i].State = 0;/* 按鍵預設狀態,0為未按下 */
  s_tBtn[i].RepeatSpeed = 0;/* 按鍵連發的速度,0表示不支持連發 */
  s_tBtn[i].RepeatCount = 0;/* 連發計數器 */
 }
 /* 判斷按鍵按下的函數 */
 s_tBtn[0].IsKeyDownFunc = IsKey1Down;
 s_tBtn[1].IsKeyDownFunc = IsKey2Down;
 s_tBtn[2].IsKeyDownFunc = IsKey3Down;
}

我們知道按鍵會有機械抖動,你以為按鍵按下就是低電平,其實在按下的一瞬間會存在機械抖動,如果不做延時處理,可能會出錯,一般如果按鍵檢測到按下後再延時50ms檢測一次,如果還是檢測低電平,才能說明按鍵真正的被按下了。反之按鍵彈起時也是一樣的。所以我們程序設置按鍵濾波時間50ms, 因為代碼每10ms掃描一次按鍵,所以按鍵的單位我們可以理解為10ms,濾波的次數就為5次。這樣只有連續檢測到50ms狀態不變才認為有效,包括彈起和按下兩種事件,即使按鍵電路不做硬體濾波(沒有電容濾波),該濾波機制也可以保證可靠地檢測到按鍵事件。

判斷按鍵是否按下,用一個HAL_GPIO_ReadPin就可以搞定。

static uint8_t IsKey1Down(void) 
{
 if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == GPIO_PIN_RESET) 
  return 1;
 else 
  return 0;
}
static uint8_t IsKey2Down(void) 
{
 if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == GPIO_PIN_RESET) 
  return 1;
 else 
  return 0;
}

static uint8_t IsKey3Down(void) 
{
 if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2) == GPIO_PIN_RESET) 
  return 1;
 else 
  return 0;
}

下面是KEY_GPIO_Config函數的定義,這個函數就是配置具體的按鍵GPIO的,就不需要過多的解釋了。

static void KEY_GPIO_Config(void)

 GPIO_InitTypeDef GPIO_InitStructure;
 
 /* 第1步:打開GPIO時鐘 */
 __HAL_RCC_GPIOE_CLK_ENABLE();
 
 /* 第2步:配置所有的按鍵GPIO為浮動輸入模式(實際上CPU復位後就是輸入狀態) */
 GPIO_InitStructure.Mode = GPIO_MODE_INPUT;      /* 設置輸入 */
 GPIO_InitStructure.Pull = GPIO_NOPULL;                 /* 上下拉電阻不使能 */
 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  /* GPIO速度等級 */
 
 GPIO_InitStructure.Pin = GPIO_PIN_4;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 GPIO_InitStructure.Pin = GPIO_PIN_3;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
 
 GPIO_InitStructure.Pin = GPIO_PIN_2;
 HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

5.按鍵掃描

按鍵掃描函數KEY_Scan()每隔 10ms 被執行一次。RunPer10ms函數在 systick中斷服務程序中執行。

void RunPer10ms(void)
{
 KEY_Scan();
}

void KEY_Scan(void)
{
 uint8_t i;
 for (i = 0; i < HARD_KEY_NUM; i++)
 {
  KEY_Detect(i);
 }
}
/*

每隔10ms所有的按鍵GPIO均會被掃描檢測一次。KEY_Detect函數實現如下:

static void KEY_Detect(uint8_t i)
{
 KEY_T *pBtn;

 pBtn = &s_tBtn[i];
 if (pBtn->IsKeyDownFunc())
 {//這個裡面執行的是按鍵按下的處理
  if (pBtn->Count < KEY_FILTER_TIME)
  {//按鍵濾波前給 Count 設置一個初值
   pBtn->Count = KEY_FILTER_TIME;
  }
  else if(pBtn->Count < 2 * KEY_FILTER_TIME)
  {//實現 KEY_FILTER_TIME 時間長度的延遲
   pBtn->Count++;
  }
  else
  {
   if (pBtn->State == 0)
   {
    pBtn->State = 1;

    /* 發送按鈕按下的消息 */
    KEY_FIFO_Put((uint8_t)(3 * i + 1));
   }

   if (pBtn->LongTime > 0)
   {
    if (pBtn->LongCount < pBtn->LongTime)
    {
     /* 發送按鈕持續按下的消息 */
     if (++pBtn->LongCount == pBtn->LongTime)
     {
      /* 鍵值放入按鍵FIFO */
      KEY_FIFO_Put((uint8_t)(3 * i + 3));
     }
    }
    else
    {
     if (pBtn->RepeatSpeed > 0)
     {
      if (++pBtn->RepeatCount >= pBtn->RepeatSpeed)
      {
       pBtn->RepeatCount = 0;
       /* 長按鍵後,每隔10ms發送1個按鍵 */
       KEY_FIFO_Put((uint8_t)(3 * i + 1));
      }
     }
    }
   }
  }
 }
 else
 {//這個裡面執行的是按鍵鬆手的處理或者按鍵沒有按下的處理
  if(pBtn->Count > KEY_FILTER_TIME)
  {
   pBtn->Count = KEY_FILTER_TIME;
  }
  else if(pBtn->Count != 0)
  {
   pBtn->Count--;
  }
  else
  {
   if (pBtn->State == 1)
   {
    pBtn->State = 0;

    /* 發送按鈕彈起的消息 */
    KEY_FIFO_Put((uint8_t)(3 * i + 2));
   }
  }
  pBtn->LongCount = 0;
  pBtn->RepeatCount = 0;
 }
}

這個函數還是比較難以理解的,主要是結構體的操作。所以好好學習結構體,不要見了結構體就跑。

分析:首先讀取相應按鍵的結構體地址賦值給結構體指針變量pBtn ,因為程序裡面每個按鍵都有自己的結構體,只有通過這個方式才能對具體的按鍵進行操作。(在前面我們使用軟體定時器時也使用了這中操作,在滴答定時器的中斷服務函數中)。

static KEY_T s_tBtn[3];//程序裡面每個按鍵都有自己的結構體,有三個按鍵
KEY_T *pBtn;//定義一個結構體指針變量pBtn
pBtn = &s_tBtn[i];//將按鍵的結構體地址賦值給結構體指針變量pBtn

然後接著就是給按鍵濾波前給Count設置一個初值,前面說按鍵初始化的時候已經設置了Count =5/2。然後判斷是否按下的標誌位,如果按鍵按下了,這裡就將其設置為 1,如果沒有按下這個變量的值就會一直是 0。這裡可能不理解是就是按鍵按下發送的鍵值是3 * i + 1。按鍵彈起發送的鍵值是3 * i + 2,按鍵長按發送的鍵值是3 * i + 3。也就是說按鍵按下發送的鍵值是1和4和7。按鍵彈起發送的鍵值是2和5和8,按鍵長按發送的鍵值是3和6和9。看下面這個枚舉enum你就明白了。

typedef enum
{
 KEY_NONE = 0,   /* 0 表示按鍵事件 */

 KEY_1_DOWN,    /* 1鍵按下 */
 KEY_1_UP,    /* 1鍵彈起 */
 KEY_1_LONG,    /* 1鍵長按 */

 KEY_2_DOWN,    /* 2鍵按下 */
 KEY_2_UP,    /* 2鍵彈起 */
 KEY_2_LONG,    /* 2鍵長按 */

 KEY_3_DOWN,    /* 3鍵按下 */
 KEY_3_UP,    /* 3鍵彈起 */
 KEY_3_LONG,    /* 3鍵長按 */


}KEY_ENUM;

7.試驗演示
int main(void)
{
 uint8_t KeyCode;/* 按鍵代碼 */
 KEY_Init();
 while (1)
 {  
  /* 按鍵濾波和檢測由後臺systick中斷服務程序實現,我們只需要調用KEY_FIFO_Get讀取鍵值即可。 */
  KeyCode = KEY_FIFO_Get(); /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
  if (KeyCode != KEY_NONE)
  {
   switch (KeyCode)
   {
    case KEY_DOWN_K1:   /* K1鍵按下 */
     printf("K1鍵按下\r\n");
     break;
    case KEY_UP_K1:    /* K1鍵彈起 */
     printf("K1鍵彈起\r\n");
     break;
    case KEY_DOWN_K2:   /* K2鍵按下 */
     printf("K2鍵按下\r\n");
     break;
    case KEY_UP_K2:    /* K2鍵彈起 */
     printf("K2鍵彈起\r\n");
     break;
    case KEY_DOWN_K3:   /* K3鍵按下 */
     printf("K3鍵按下\r\n");
     break;
    case KEY_UP_K3:    /* K3鍵彈起 */
     printf("K3鍵彈起\r\n");
     break;        
    default:
     /* 其它的鍵值不處理 */
     break;
   }
  
  }
 }
}

不知道學妹看懂沒,沒看懂就多看幾遍。代碼例程已上傳至Gitee。

https://gitee.com/zhiguoxin/Wechat-Data.git

相關焦點

  • 一個ADC實現多個按鍵檢測
    按鍵作為常用的輸入系統,如何準確並高效的獲取按鍵值,是一個經常要面對的問題,常用的按鍵檢測方式有如下幾種方式:1.獨立按鍵每個按鍵的檢測佔用單片機的一個GPIO引腳,原理圖如下圖所示:圖片來源公眾號自製核心板原理圖我們以BTN1按鍵為例,當按鍵沒有按下的時候,網絡標號KEY1處的電壓被10K的上拉電阻拉至3.3V,PB14(KEY1)引腳設為輸入引腳後,程序中讀取該引腳的值將為1,當按鍵按下之後,網絡標號KEY1處接地,讀取該輸入引腳的值將為0,進而通過此電路實現的獨立按鍵
  • 【單片機】按鍵檢測電路設計-2
    >按鍵作為一個輸入模塊,在單片機開發板中必不可少,本文來討論按鍵的設計方案。方案3-6,看似就是按鍵數量的增加,電路的重複,實際上針對不同的按鍵數量,他們的編程可能存在不同,比如最常見的獨立按鍵編程,只檢測一個按鍵,那如何編程2個獨立按鍵的程序呢?複製,再寫個一樣的函數?這樣做的效率其實很低。
  • 單片機基礎教程【陸】——按鍵檢測
    > 前面我們講解的流水燈實際上就是應用了I/O口的O 即Output/輸出功能這節我們講解I 即Input/輸入功能輸入功能的一個典型應用就是按鍵檢測
  • 【按鍵精靈】做一個閱讀賺錢的腳本
    大家好,我是公眾號3分鐘學堂的郭立員~做按鍵精靈安卓版教程有好幾年了,有時候我在想按鍵腳本有啥用?
  • 學寫按鍵精靈腳本(一)遊戲改了又改 我的掛又白寫了
    書接上文《學寫按鍵精靈腳本(一)換遊戲 寫掛 循環著》巨人的戰國破壞神要公開測試了,看看巨人這兩個字,再看看這遊戲的名字,就知道這遊戲好不到哪去
  • 顛覆你認知的按鍵檢測方法----1
    大家好呀,好久不見呀😍,前段時間項目太多,終於得空來寫點項目乾貨。     因為疫情導致的影響,晶片價格一路高漲,就拿Stm32103的晶片來說,原價6塊多的,現在一片就要60多。(🤫之前我給焊廢了幾片,口袋又癟了,😭)。噥,就下面這一坨東西。     這麼貴的東西,還難焊,用它幹什麼。換!換國產晶片,腳位少。
  • ADC實現多個按鍵檢測有木有?(附軟體電路)
    獲取按鍵值的方式按鍵作為常用的輸入系統,如何準確並高效的獲取按鍵值,是一個經常要面對的問題,常用的按鍵檢測方式有如下幾種方式:1.獨立按鍵每個按鍵的檢測佔用單片機的一個GPIO引腳,原理圖如下圖所示:圖片來源本公眾號自製核心板原理圖我們以BTN1按鍵為例,當按鍵沒有按下的時候,網絡標號KEY1處的電壓被10K的上拉電阻拉至3.3V,PB14(KEY1)引腳設為輸入引腳後,程序中讀取該引腳的值將為1,當按鍵按下之後,網絡標號
  • 教大家使用NRF24L01做一個無線遙控開關,遠程控制很方便!
    上圖中的這個小東西,它相當於一個小開發板,用它來控制設置NRF24L01模塊,別人已經寫有很多庫了,我直接調用就可以,就不用去底層設置寄存器,不用什麼都親歷親為,可以省下很多時間,這對於初學者來說很容易上手,可以快速開發,這個就是用
  • 單片機按鍵消抖程序彙編
    抖動時間是由按鍵的機械特性決定的,一般都會在 10ms以內,為了確保程序對按鍵的一次閉合或者一次斷開只響應一次,必須進行按鍵的消抖處理。當檢測到按鍵狀態變化時,不是立即去響應動作,而是先等待閉合或斷開穩定後再進行處理。按鍵消抖可分為硬體消抖和軟體消抖。硬體消抖就是在按鍵上並聯一個電容,如圖 8-11 所示,利用電容的充放電特性來對抖動過程中產生的電壓毛刺進行平滑處理,從而實現消抖。
  • 219個 opencv 常用函數匯總,一個比一個強大!
    3、cvShowImage:在一個已創建好的窗口中顯示圖像;4、cvWaitKey:使程序暫停,等待用戶觸發一個按鍵操作;5、cvReleaseImage:釋放圖像文件所分配的內存;6、cvDestroyWindow:銷毀顯示圖像文件的窗口;7、cvCreateFileCapture:通過參數設置確定要讀入的AVI文件;
  • Arduino第三課:按鍵掃描
    控制系統現在我們已經踏入控制領域的大門,今天我們做的按鍵控制led已經算是嚴格意義上的一個控制系統。⑴輸入:按鍵;⑵分析:ATmega328p晶片(一隻AVR單片機);⑶輸出:4隻led燈。        2.
  • 按鍵精靈和油猴
    按鍵精靈是對系統腳本語言的封裝,讓很多不懂編程的人,通過閱讀漢字就能開發一些輔助腳本。除了遊戲,還能做自動化辦公。簡而言之,腳本能替代人力的滑鼠和鍵盤操作。按鍵精靈官方為了顯得高大上,引進了一個國外概念,RPA(Robotic Process Automation),機器人流程自動化。
  • 為DS1845 DS1855數字電位器構建按鍵接口
    大部分時間段,GP0作為輸入引腳,按下MID按鍵時將GP0拉低。然而,如果按下其它開關的任何一個或兩個同時按下,PIC?輸出低電平,點亮LED。這種情況下,LED將在MID按下時被點亮,其它按鍵按下時,LED由PIC驅動。R1、R2、R3可選,使用PIC12F509時並不需要這些電阻。圖2給出了估DS1845/DS1855時的特定連接方式。
  • Android TV機頂盒監聽遙控器全局按鍵
    以上面HOME鍵處理為例,在最後的handleShortPressOnHome()函數中繼而調用launchHomeFromHotkey()可以知道最終會發送一個廣播ACTION_CLOSE_SYSTEM_DIALOGS,所以很多APP也通過監聽此廣播action,再判斷reason是homekey則可以確定是用戶按下了HOME鍵。
  • 按鍵精靈安卓版遊戲腳本教學:開發套路
    如何做到腳本通用我見過很多上來就用默認解析度開發的,或者解析度正確,但是DPi不正確,最後導致反工。
  • Python,我只寫墜吊的
    在此我強烈建議大家都去讀一下google編寫的python規範。同時,我們可以使用一些好用的小工具輔助我們寫出更加符合習慣的Python代碼,如flake8等小插件。結合以上這些參考資料和工具,我們這篇專題總結就不會過多去講語法相關的格式化。
  • Linux字符設備驅動模型之按鍵
    這裡以按鍵驅動為例。當按鍵按下是,產生中斷,可以通過判斷GPH0DAT寄存器的位[0]的狀態來判斷是不是當前已經產生了中斷。在這裡對中斷的處理就是列印出響應的標識printk("key1 down! ");。6、關於程序中的struct file_operations在註冊字符設備的時候,也註冊了一個struct file_operations,即為:
  • 【Python】我寫的第一個程序
    上圖中,Print()就是一個自帶的函數,功能就是列印,把括號裡的東西列印出來,是最基本也是最常見的函數,input()也是一個函數,功能是獲取用戶鍵盤輸入內容。上圖中的print、input、len()、str()、int(),都是自帶的函數。len()用來獲取括號裡內容的長度,str()用來把括號裡的內容,無論它是什麼數據類型,都轉化成字符串。
  • 一個用 Arduino 實現的完整項目
    來源:http://www.oschina.net/translate/a-complete-project-with-arduino?utm_source=tuicool  英文原文:A complete project with Arduino 參與翻譯 (3人) : realZ, leoxu, Ley介紹我有一個上小學的女兒.