多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
* 导师视频讲解:[**去听课**](https://www.bilibili.com/video/BV1k34y1D7Vz/) >[success] **技术支持说明:** >**1**.一般以自主学习为主 > **2**.可到官方问答社区中提问:[**去提问**](https://bbs.csdn.net/forums/zigbee) > **3**.工程师**会尽快**解答社区问题,但他们是一线开发,【**难以保证**】解答时效,解答辛苦,感谢理解! <br/> 上节课讲解HAL 按键的API的使用,本节课将深入分析一下其中原理。**如果读者暂时不需要学习其中的原理,可以跳过本节课**。 ### HAL的按键框架的主要代码在hal_key.c文件中,打开该文件可以找到以下这几个重要的函数: ### ``` HalKeyInit HalKeyConfig HalKeyPoll HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR ) ``` <br/> ## **初始化函数HalKeyInit** ``` 1.void HalKeyInit( void )     2.{     3.  /* Initialize previous key to 0 */     4.  halKeySavedKeys = 0;     5.     6.  HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT);  // I/O为普通GPIO   7.     8.#if ! defined ENABLE_LED4_DISABLE_S1     9.  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);  // I/O配置为输入   10.#endif     11.       12.  HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT);   13.  HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT);    14.     15.  /* Initialize callback function */     16.  pHalKeyProcessFunction  = NULL;     17.     18.  /* Start with key is not configured */     19.  HalKeyConfigured = FALSE;     20.} ``` 初始化函数主要是用于配置GPIO为输入功能,HAL\_KEY\_SW\_6\_SEL等定义其实是按键GPIO配置相关的寄存器,这些定义可以在hal\_key.c中找到,如果我们需要添加其他按键,也遵循这个方法即可! 配置HAL\_KEY\_6相关寄存器的定义如下: ![](https://img.kancloud.cn/1c/38/1c3884768710099f204a728ffa2b66a5_738x594.png =600x) ### 在初始化函数HalKeyInit中有个地方需要说明,这个函数中有一段代码(用了快配置HAL\_KEY\_SW\_6这个按键的引脚为输入功能): ``` 1.#if ! defined ENABLE_LED4_DISABLE_S1     2.  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);  // I/O配置为输入   3.#endif ``` ZStack的本意是S1按键引脚P0\_1可以用来驱动LED,就是和按键复用了,是否用作LED取决于宏ENABLE\_LED4\_DISABLE\_S1是否定义,但是协议栈没有开源的那部分代码中定义了这个宏,也算是ZStack的一个Bug,所以这段代码我们需做修改,把预编译去掉(注释掉),否则按键引脚不会被配置为输入: ``` 1.// #if ! defined ENABLE_LED4_DISABLE_S1     2.  HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT);  // I/O配置为输入   3.// #endif ``` ## **HalKeyConfig** 这个函数主要用来配置和中断相关的内容: ``` 1.void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback) 2.{   3.  Hal_KeyIntEnable = interruptEnable;  // 标志:是否使能中断 4.   5.  pHalKeyProcessFunction = cback;  // 回调函数,按键按下时调用 6.   7.  /* Determine if interrupt is enable or not */   8.  if (Hal_KeyIntEnable)  // 如果使能了中断,必须配置中断相关内容 9.  {   10.    PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */   11.    /* For falling edge, the bit must be set. */   12.  #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)   13.    PICTL |= HAL_KEY_SW_6_EDGEBIT;   14.  #endif   15. 16.    /* Interrupt configuration:  17.     * - Enable interrupt generation at the port  18.     * - Enable CPU interrupt  19.     * - Clear any pending interrupt  20.     */   21.    HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT;   22.    HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT;   23.    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);   24. 25. .........  // 屏蔽无关紧要的代码 26. 27.    if (HalKeyConfigured == TRUE)   28.    {   29.    osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); // 不需要轮询 30.    }   31.  }   32.  else   // 如果没有使用中断,必须把中断相关内容关闭掉 33.  {   34.    HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT);  35.    HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);  36.    osal_set_event(Hal_TaskID, HAL_KEY_EVENT);  // 按键轮询事件 37.  }   38. 39.  HalKeyConfigured = TRUE;   40.} ``` <br/> ## **HalKeyPoll** ``` 1.void HalKeyPoll (void)     2.{     3.  uint8 keys = 0;     4.  if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT))    5.  {     6.    keys = halGetJoyKeyInput();     7.  }     8.  /*   9.   *   .........  10.   */     11.  if (!Hal_KeyIntEnable)     12.  {     13.    if (keys == halKeySavedKeys)     14.    {     15.      return;     16.    }     17.    halKeySavedKeys = keys;     18.  }     19.  else     20.  {     21.    /* Key interrupt handled here */     22.  }     23.  if (HAL_PUSH_BUTTON1())  // 检测HAL_KEY_SW_6是否按下,添加其他 24.  {                        // 按键时,同样也需要在这里做检测!   25.    keys |= HAL_KEY_SW_6;     26.  }     27.       28.  if (pHalKeyProcessFunction     29.#ifdef HAL_LEGACY_KEYS     30.    && keys   31.#endif     32.    )     33.  {     34.    (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);     35.  }     36.} ``` 这个函数是用来查询按键是否按键,如果是中断方式,检测到中断时去抖然后进入这个函数;如果不是中断方式那么采用周期性轮询查看按键是否按下。 ### 函数HAL\_ISR\_FUNCTION( halKeyPort0Isr, P0INT\_VECTOR ): 这是个中断处理函数,中断向量是P0INT\_VECTOR也就是P0口的中断,按键连接的是P0\_1,按下产生中断就会来到这个函数: ``` 1.HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )     2.{     3.  HAL_ENTER_ISR();    4.  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)  // P0_1中断   5.  {     6.    halProcessKeyInterrupt();  // 调用这个函数来处理中断   7.                                  // 最终会调用 HalKeyPoll   8.  }     9.  /*   10.    Clear the CPU interrupt flag for Port_0   11.    PxIFG has to be cleared before PxIF   12.  */     13.  HAL_KEY_SW_6_PXIFG = 0;      // 清除中断标志   14.  HAL_KEY_CPU_PORT_0_IF = 0;     15.  CLEAR_SLEEP_MODE();     16.  HAL_EXIT_ISR();     17.} ``` 最终中断会在函数halProcessKeyInterrupt()中被处理: ``` 1.void halProcessKeyInterrupt (void)   2.{   3.  bool valid=FALSE;   4.  // 清除HAL_KEY_SW_6引脚对应的中断标志位 5.  if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) 6.  {   7.    HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); 8.    valid = TRUE;   9.  } 10.   11.  if (valid)   12.  {   // 启动一个事件,25ms后到期(去抖),事件会在hal_drivers.c 13. // 中被处理(Hal_ProcessEvent),最终会调用HalKeyPoll osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT,  HAL_KEY_DEBOUNCE_VALUE);   14.  }   15.} ``` 问题:上面讲解的函数最终会在哪里被调用呢? ### 我们提供一种方法可以用来在IAR中查看所有与关键字相关的内容,进而可以找到函数被调用的地方: ![](https://img.kancloud.cn/ca/de/caded0f93e01a9b381e348292fa232b7_768x792.png =600x) ### 这个搜索功能是全局搜索,也就是说会IAR搜索工程中的所有文件,然后匹配我们要搜索的关键字,再将结果显示出来;比如我们想搜索与关键字HalKeyInit相关的所有内容,可以在弹出的对话框中输入这个关键字,然后点击”Find”即可,然后我们根据搜索出来的结果,可以通过鼠标双击对应的结果来查看相关内容: ![](https://img.kancloud.cn/30/3c/303c0830ae3252688d305902511c66b9_732x618.png =600x) <br/> <br/> ## **项目定制** * 如需项目定制开发,可扫码添加项目经理好友(注明“**项目定制**”) * 定制范围:**NB-IoT**、**CATn(4G)**、**WiFi**、**ZigBee**、**BLE Mesh**以及**STM32**、**嵌入式Linux**等IoT技术方案 * 善学坊官网:[www.sxf-iot.com](https://www.sxf-iot.com/) ![](https://img.kancloud.cn/ca/73/ca739f92cab220a3059378642e3bd502_430x430.png =200x) * 非项目定制**勿扰**,此处**非**技术支持