<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 牛人業(yè)話(huà) > 【單片機到嵌入式之路】序列之4:你的按鍵還活著(zhù)么?

【單片機到嵌入式之路】序列之4:你的按鍵還活著(zhù)么?

作者: 時(shí)間:2015-05-24 來(lái)源:網(wǎng)絡(luò ) 收藏

  硬件平臺單片機、ARM等等

本文引用地址:http://dyxdggzs.com/article/274621.htm

  編譯環(huán)境MDK5.0

  硬件工具配合MCU

  主要文件無(wú)

  作 者@量子CPU(747764222)

  本節我們將學(xué)習讓你惱火的按鍵,主要是從下面4個(gè)方面進(jìn)行講解:

  1.按鍵觸發(fā)方式迫在眉睫

  2.按鍵掃描

  3.中斷掃描

  4.按鍵狀態(tài)機制

  一、按鍵觸發(fā)方迫在眉睫

  如果你在公司上班,抑或你在學(xué)校,當你做的項目任務(wù)進(jìn)度比較多的時(shí)候,你還在使用按鍵掃描,也許老板會(huì )說(shuō)你SB,老師說(shuō)你無(wú)能。這就是現實(shí),單片機和ST序列的目前還是單核的,怎么可能一直在做掃描,你大才小用了吧。你把精華浪費在做無(wú)聊的事情上面,現在你評價(jià)一下你自己吧,我不多說(shuō)。O(∩_∩)O哈哈~

  親,你的按鍵還活著(zhù)么?你的按鍵會(huì )苦惱你么?如果你還在考慮按鍵用掃描方式,那可能有一天要被人罵了,如果想要面子,又讓你的按鍵有活力。Follow me !!!

  你不會(huì )按鍵的三種方式,你媽知道么?(*^__^*) 嘻嘻……

  下面以單個(gè)獨立按鍵來(lái)講解,矩陣按鍵可以思想是一樣的。

  

 

  二、按鍵掃描方式

  按鍵掃描的方式,這應該是大家比較熟悉的,因為大部分單片機入門(mén)的時(shí)候,碰到按鍵的時(shí)候,都是通過(guò)掃描方式來(lái)實(shí)現。

  按鍵掃描的原理:CPU需要不停的工作,來(lái)判斷IO口是否被拉低或者置高,效率比較低。按鍵掃描主要是處理消抖。

  STM32為例講解

  /***************************************

  * 函數描述:按鍵初始化函數

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:初始化按鍵

  * 修改記錄:

  ****************************************/

  void KEY_Init(void)

  {

  GPIO_InitTypeDef GPIO_InitStruct;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //按鍵PA0

  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //輸入模式

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;

  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉輸入

  GPIO_Init(GPIOA, &GPIO_InitStruct);

  }

  /***************************************

  * 函數描述:按鍵掃描函數

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:掃描按鍵

  * 修改記錄:

  ****************************************/

  uint8_t KEY_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_Pin)

  {

  if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == 0 ) //檢測是否有按鍵按下

  {

  Delay(10000); //延時(shí)消抖

  if(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == 0 )

  {

  while(GPIO_ReadInputDataBit(GPIOx,GPIO_Pin) == 0); //等待按鍵釋放 */

  return 0 ;

  }

  else

  return 1;

  }

  else

  eturn 1;

  }

  /***************************************

  * 函數描述:部分主函數

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:

  * 修改記錄:

  ****************************************/

  while(1)

  {

  if( KEY_Scan (GPIOA,GPIO_Pin_0) ==0)//判定按鍵是否按下

  {

  GPIO_WriteBit(GPIOC, GPIO_Pin_9,

  (BitAction)((1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9))));//反轉led2燈

  }

  }

  這就是按鍵掃描,至于缺點(diǎn)不言而喻。這不就是苦逼的CPU的么?O(∩_∩)O哈哈~http://bbs.ickey.cn/group-topic-id-13062.html

  三、中斷掃描

  在你對單片機比較熟悉的時(shí)候,在你考慮多任務(wù)的時(shí)候,你應該被按鍵掃描煩死了,這時(shí)候,你絞盡腦汁,終于想到了—————————— 中斷掃描。

  中斷掃描的原理:中斷控制效率很高,一旦系統IO口出現上升或者下降沿電平就會(huì )觸發(fā)執行中斷內的程序。這樣MCU就無(wú)需一直在掃描,這樣你就可以干其他的時(shí)候,而且不會(huì )覺(jué)得按鍵不靈活。

  同樣以STM32為例講解:

  說(shuō)明:STM32用IO口外部中斷的一般步驟:

  1.初始化IO口為輸入;

  2.開(kāi)啟IO口時(shí)鐘,設置 IO 口與中斷線(xiàn)的映射關(guān)系;

  3.初始化線(xiàn)上中斷,設置觸發(fā)條件等;

  4.配置中斷分組(NVIC),并使能中斷;

  5.編寫(xiě)中斷服務(wù)函數。

  /***************************************

  * 函數描述:按鍵初始化函數

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:按鍵初始化

  * 修改記錄:

  ****************************************/

  void KEY_Init(void)

  {

  GPIO_InitTypeDef GPIO_InitStruct;

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //按鍵PA0

  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //輸入模式

  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_2;

  GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉輸入

  GPIO_Init(GPIOA, &GPIO_InitStruct);

  }

  /***************************************

  * 函數描述:外部中斷初始化

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:

  * 修改記錄:

  ****************************************/

  void EXTI_KEY_Init(void)

  {

  EXTI_InitTypeDef EXTI_InitStruct;

  NVIC_InitTypeDef NVIC_InitStruct;

  KEY_Init();

  RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); //使能系統時(shí)鐘配置

  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

  //連接EXTI0給GPIOA0

  SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

  //配置GPIO與中斷線(xiàn)的映射關(guān)系

  EXTI_InitStruct.EXTI_Line = EXTI_Line0; //中斷線(xiàn)標號0

  EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //外部中斷模式

  EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿中斷

  EXTI_InitStruct.EXTI_LineCmd = ENABLE; //中斷線(xiàn)使能

  EXTI_Init(&EXTI_InitStruct);

  NVIC_InitStruct.NVIC_IRQChannel = EXTI0_1_IRQn;

  NVIC_InitStruct.NVIC_IRQChannelPriority = 0x00;

  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

  NVIC_Init(&NVIC_InitStruct);

  }

  /***************************************

  * 函數描述:外部中斷0服務(wù)程序

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:

  * 修改記錄:

  ****************************************/

  void EXTI0_1_IRQHandler(void)

  {

  if(EXTI_GetITStatus(EXTI_Line0) != RESET)

  //判斷線(xiàn)0上的中斷是否發(fā)生,可以理解為標志位

  {

  /* Toggle LED1and LED2 */

  GPIO_WriteBit(GPIOC, GPIO_Pin_8,

  (BitAction)((1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_8))));

  GPIO_WriteBit(GPIOC, GPIO_Pin_9,

  (BitAction)((1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_9))));

  /* Clear the EXTI line 0 pending bit */

  EXTI_ClearITPendingBit(EXTI_Line0);//清除LINE0上的中斷標志位

  }

  }

  /***************************************

  * 函數描述:主函數

  * 輸入參數:No

  * 返 回 值:No

  * 說(shuō) 明:

  * 修改記錄:

  ****************************************/

  int main(void)

  {

  SystemInit(); //系統初始化

  LED_Init(); //LED燈初始化

  GPIO_ResetBits(GPIOC,GPIO_Pin_8);

  KEY_Init(); //按鍵初始化

  EXTI_KEY_Init(); //外部中斷初始化

  while(1)

  {

  }

  }

  按鍵中斷的有點(diǎn)是不是不言而喻!!!這不就拯救了苦逼的CPU么?O(∩_∩)O哈哈~

  http://bbs.ickey.cn/group-topic-id-13062.html

  四、按鍵狀態(tài)機制

  網(wǎng)上看見(jiàn)一片講狀態(tài)機制很經(jīng)典的文章。借用并轉之!!!

  首先按鍵程序進(jìn)入初始狀態(tài)S1,在這個(gè)狀態(tài)下,檢測按鍵是否按下,如果有按下,則進(jìn)入按鍵消抖狀態(tài)2,在下一次執行按鍵程序時(shí)候,直接由按鍵消抖狀態(tài)進(jìn)入按鍵按下?tīng)顟B(tài)3,在此狀態(tài)下檢測按鍵是否按下,如果沒(méi)有按鍵按下,則返回初始狀態(tài)S1,如果有則可以返回鍵值,同時(shí)進(jìn)入長(cháng)按狀態(tài)S4,在長(cháng)按狀態(tài)下每次進(jìn)入按鍵程序時(shí)候對按鍵時(shí)間計數,當計數值超過(guò)設定閾值時(shí)候,則表明長(cháng)按事件發(fā)生,同時(shí)進(jìn)入按鍵連_發(fā)狀態(tài)S5。如果按鍵鍵值為空鍵,則返回按鍵釋放狀態(tài)S6,否則繼續停留在本狀態(tài)。在按鍵連_發(fā)狀態(tài)下,如果按鍵鍵值為空鍵則返回按鍵釋放狀態(tài)S6,如果按鍵時(shí)間計數超過(guò)連_發(fā)閾值,則返回連_發(fā)按鍵值,清零時(shí)間計數后繼續停留在本狀態(tài)。

  看了這么多,也許你已經(jīng)有一個(gè)模糊的概念了,下面讓我們趁熱打鐵,一起來(lái)動(dòng)手編寫(xiě)按鍵驅動(dòng)程序吧。

  下面是我使用的硬件的連接圖。

  

 

  硬件連接很簡(jiǎn)單,四個(gè)獨立按鍵分別接在P3^0------P3^3四個(gè)I/O上面。

  因為51單片機I/O口內部結構的限制,在讀取外部引腳狀態(tài)的時(shí)候,需要向端口寫(xiě)1.在51單片機復位后,不需要進(jìn)行此操作也可以進(jìn)行讀取外部引腳的操作。因此,在按鍵的端口沒(méi)有復用的情況下,可以省略此步驟。而對于其它一些真正雙向I/O口的單片機來(lái)說(shuō),將引腳設置成輸入狀態(tài),是必不可少的一個(gè)步驟。

  下面的程序代碼初始化引腳為輸入。

  void KeyInit(void)

  {

  io_key_1 = 1 ;

  io_key_2 = 1 ;

  io_key_3 = 1 ;

  io_key_4 = 1 ;

  }

  根據按鍵硬件連接定義按鍵鍵值

  #define KEY_VALUE_1 0x0e

  #define KEY_VALUE_2 0x0d

  #define KEY_VALUE_3 0x0b

  #define KEY_VALUE_4 0x07

  #define KEY_NULL 0x0f

  下面我們來(lái)編寫(xiě)按鍵的硬件驅動(dòng)程序。

  根據第一章所描述的按鍵檢測原理,我們可以很容易的得出如下的代碼:

  static uint8 KeyScan(void)

  {

  if(io_key_1 == 0)return KEY_VALUE_1 ;

  if(io_key_2 == 0)return KEY_VALUE_2 ;

  if(io_key_3 == 0)return KEY_VALUE_3 ;

  if(io_key_4 == 0)return KEY_VALUE_4 ;

  return KEY_NULL ;

  }

  其中io_key_1等是我們按鍵端口的定義,如下所示:

  sbit io_key_1 = P3^0 ;

  sbit io_key_2 = P3^1 ;

  sbit io_key_3 = P3^2 ;

  sbit io_key_4 = P3^3 ;

  KeyScan()作為底層按鍵的驅動(dòng)程序,為上層按鍵掃描提供一個(gè)接口,這樣我們編寫(xiě)的上層按鍵掃描函數可以幾乎不用修改就可以拿到我們的其它程序中去使用,使得程序復用性大大提高。同時(shí),通過(guò)有意識的將與底層硬件連接緊密的程序和與硬件無(wú)關(guān)的代碼分開(kāi)寫(xiě),使得程序結構層次清晰,可移植性也更好。對于單片機類(lèi)的程序而言,能夠做到函數級別的代碼重用已經(jīng)足夠了。

  在編寫(xiě)我們的上層按鍵掃描函數之前,需要先完成一些宏定義。

  //定義長(cháng)按鍵的TICK數,以及連_發(fā)間隔的TICK數

  #define KEY_LONG_PERIOD 100

  #define KEY_CONTINUE_PERIOD 25

  //定義按鍵返回值狀態(tài)(按下,長(cháng)按,連_發(fā),釋放)

  #define KEY_DOWN 0x80

  #define KEY_LONG 0x40

  #define KEY_CONTINUE 0x20

  #define KEY_UP 0x10

  //定義按鍵狀態(tài)

  #define KEY_STATE_INIT 0

  #define KEY_STATE_WOBBLE 1

  #define KEY_STATE_PRESS 2

  #define KEY_STATE_LONG 3

  #define KEY_STATE_CONTINUE 4

  #define KEY_STATE_RELEASE 5

  接著(zhù)我們開(kāi)始編寫(xiě)完整的上層按鍵掃描函數,按鍵的短按,長(cháng)按,連按,釋放等等狀態(tài)的判斷均是在此函數中完成。對照狀態(tài)流程轉移圖,然后再看下面的函數代碼,可以更容易的去理解函數的執行流程。完整的函數代碼如下:

  void GetKey(uint8 *pKeyValue)

  {

  static uint8 s_u8KeyState = KEY_STATE_INIT ;

  static uint8 s_u8KeyTimeCount = 0 ;

  static uint8 s_u8LastKey = KEY_NULL ; //保存按鍵釋放時(shí)候的鍵值

  uint8 KeyTemp = KEY_NULL ;

  KeyTemp = KeyScan() ; //獲取鍵值

  switch(s_u8KeyState)

  {

  case KEY_STATE_INIT :

  {

  if(KEY_NULL != (KeyTemp))

  {

  s_u8KeyState = KEY_STATE_WOBBLE ;

  %3

linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)

51單片機相關(guān)文章:51單片機教程


單片機相關(guān)文章:單片機教程


單片機相關(guān)文章:單片機視頻教程


單片機相關(guān)文章:單片機工作原理




關(guān)鍵詞: 嵌入式

評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>