通过节点 1 的按键 S1 中断配置,检测按键的按下情况。整个过程在协议栈 Z-STACK 的 SampleApp.eww 上完成。
协议栈已经自带了按键的驱动和使用函数。所以将按键改到任 意 IO 口也不是问题了。
首先我们需要做的是花精力将官方自带的按键 IO 改到我的板子的 IO口上。 官方电路的按键 S1 连接的是 P0.1 引脚,我的按键 S1连接的是 P0.1 口,按键 S2 连接的是 P2.0 口。
首先就是要了解协议栈中按键的检测与按键事件的传递过程;
按键流程分析 :
在上一节工程的基础上我们来进行分析修改,(上一节的工程链接为:https://pan.baidu.com/s/10eRDKbndrYMTkm-sgpGd_w)
打开 SampleApp.eww 工程,在 ZMain.c 的 main 主函数中跟按键相关的有:
HalDriverInit();
InitBoard( OB_READY );
进入 HalDriverInit()中的 HalKeyInit()
void HalKeyInit( void ) { /* Initialize previous key to 0 */ halKeySavedKeys = 0; HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */ HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */ HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */ HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */ /* Initialize callback function */ pHalKeyProcessFunction = NULL; /* Start with key is not configured */ HalKeyConfigured = FALSE; }
这里主要是初始化按键相关的引脚, HAL_KEY_SW_6 就是对应 P01,而HAL_KEY_JOY 就是 TI 子上的 J-STICK 摇杆,
我们的板子上大部分都没有,直接注释或删掉。
接着分析 InitBoard( OB_READY )
void InitBoard( uint8 level ) { if ( level == OB_COLD ) { // IAR does not zero-out this byte below the XSTACK. *(uint8 *)0x0 = 0; // Interrupts off osal_int_disable( INTS_ALL ); // Check for Brown-Out reset ChkReset(); } else // !OB_COLD { /* Initialize Key stuff */ HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback); } }
对按键的具体配置(重点): 配置按键的检测方式和按键的回调函数
HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE,OnBoard_KeyCallback);
进入到此函数中,先配置非中断检测的,先看代码:
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback) { /* Enable/Disable Interrupt or */ Hal_KeyIntEnable = interruptEnable; /* Register the callback fucntion */ pHalKeyProcessFunction = cback; /* Determine if interrupt is enable or not */ if (Hal_KeyIntEnable) { /* Rising/Falling edge configuratinn */ PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE) PICTL |= HAL_KEY_SW_6_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT; HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT; HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Rising/Falling edge configuratinn */ HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE) HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT; HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT; HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Do this only after the hal_key is configured - to work with sleep stuff */ if (HalKeyConfigured == TRUE) { osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */ } } else /* Interrupts NOT enabled */ { HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don\'t generate interrupt */ HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */ osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */ } /* Key now is configured */ HalKeyConfigured = TRUE; }
从上面的配置可以知道按键检测有两种方式,一种为中断,一种为定时检测 定时检 测 的 话 在 配置 时就 会 直 接 启 动 HAL_KEY_EVENT 事件 , 找到此事件的处理函数。
hal_drivers.c 中的 Hal_ProcessEvent()函数:
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events ) { uint8 *msgPtr; (void)task_id; // Intentionally unreferenced parameter if ( events & SYS_EVENT_MSG ) { msgPtr = osal_msg_receive(Hal_TaskID); while (msgPtr) { /* Do something here - for now, just deallocate the msg and move on */ /* De-allocate */ osal_msg_deallocate( msgPtr ); /* Next */ msgPtr = osal_msg_receive( Hal_TaskID ); } return events ^ SYS_EVENT_MSG; } if ( events & HAL_LED_BLINK_EVENT ) { #if (defined (BLINK_LEDS)) && (HAL_LED == TRUE) HalLedUpdate(); #endif /* BLINK_LEDS && HAL_LED */ return events ^ HAL_LED_BLINK_EVENT; } if (events & HAL_KEY_EVENT) { #if (defined HAL_KEY) && (HAL_KEY == TRUE) /* Check for keys */ HalKeyPoll(); /* if interrupt disabled, do next polling */ if (!Hal_KeyIntEnable) { osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); } #endif // HAL_KEY return events ^ HAL_KEY_EVENT; } #ifdef POWER_SAVING if ( events & HAL_SLEEP_TIMER_EVENT ) { halRestoreSleepLevel(); return events ^ HAL_SLEEP_TIMER_EVENT; } #endif #ifdef CC2591_COMPRESSION_WORKAROUND if ( events & PERIOD_RSSI_RESET_EVT ) { macRxResetRssi(); return (events ^ PERIOD_RSSI_RESET_EVT); } #endif /* Nothing interested, discard the message */ return 0; }
接下来就是 HalKeyPoll()了,
void HalKeyPoll (void) { uint8 keys = 0; /* if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) // Key is active HIGH { keys = halGetJoyKeyInput(); } */ if (!HAL_PUSH_BUTTON2())//S0 { keys |= HAL_KEY_SW_1; } if (!HAL_PUSH_BUTTON1())//S1 { keys |= HAL_KEY_SW_6; } if (!Hal_KeyIntEnable) { if (keys == halKeySavedKeys) { /* Exit - since no keys have changed */ return; } /* Store the current keys for comparation next time */ halKeySavedKeys = keys; } else { /* Key interrupt handled here */ } /* Invoke Callback if new keys were depressed */ if (keys && (pHalKeyProcessFunction)) { (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL); } }
当按键按下时就传递给上面注册过的回调函数 OnBoard_KeyCallback,传 递 进 去 的 就 是 相 应 键 值 keys ; 找 到 之 前 注 册 的 回 调 函 数OnBoard_KeyCallback(在 OnBoard.c 中)
void OnBoard_KeyCallback ( uint8 keys, uint8 state ) { uint8 shift; (void)state; shift = (keys & HAL_KEY_SW_6) ? true : false; if ( OnBoard_SendKeys( keys, shift ) != ZSuccess ) { // Process SW1 here if ( keys & HAL_KEY_SW_1 ) // Switch 1 { } // Process SW2 here if ( keys & HAL_KEY_SW_2 ) // Switch 2 { } // Process SW3 here if ( keys & HAL_KEY_SW_3 ) // Switch 3 { } // Process SW4 here if ( keys & HAL_KEY_SW_4 ) // Switch 4 { } // Process SW5 here if ( keys & HAL_KEY_SW_5 ) // Switch 5 { } // Process SW6 here if ( keys & HAL_KEY_SW_6 ) // Switch 6 { } } }
这里就是通过 OnBoard_SendKeys( keys, shift )在发送系统信息给用户的应用任务。
uint8 OnBoard_SendKeys( uint8 keys, uint8 state ) { keyChange_t *msgPtr; if ( registeredKeysTaskID != NO_TASK_ID ) { // Send the address to the task msgPtr = (keyChange_t *)osal_msg_allocate( sizeof(keyChange_t) ); if ( msgPtr ) { msgPtr->hdr.event = KEY_CHANGE; msgPtr->state = state; msgPtr->keys = keys; osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr ); } return ( ZSuccess ); } else return ( ZFailure ); }
到这里我们就不知道 msg 发送到哪里了? registeredKeysTaskID 任务?这个就 是 我 们 用 户 根 据 自 己 的 需 要 选 择 按 键 的 传 递 的 任 务 号 , 可 通 过RegisterForKeys( xxx_TaskID )注册,此工程中在 SampleApp_Init()中已经调用此函数注册到 SampleApp_TaskID 中了,就是说按键信息会传递到此任务中。
接下来就是终点站了:
void SampleApp_HandleKeys( uint8 shift, uint8 keys ) { (void)shift; // Intentionally unreferenced parameter if ( keys & HAL_KEY_SW_1 ) { HalUARTWrite(0,"KEY1\n",4); /* This key sends the Flash Command is sent to Group 1. * This device will not receive the Flash Command from this * device (even if it belongs to group 1). */ SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION ); } if ( keys & HAL_KEY_SW_2 ) { HalUARTWrite(0,"KEY2\n",4); /* The Flashr Command is sent to Group 1. * This key toggles this device in and out of group 1. * If this device doesn\'t belong to group 1, this application * will not receive the Flash command sent to group 1. */ aps_Group_t *grp; grp = aps_FindGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); if ( grp ) { // Remove from the group aps_RemoveGroup( SAMPLEAPP_ENDPOINT, SAMPLEAPP_FLASH_GROUP ); } else { // Add to the flash group aps_AddGroup( SAMPLEAPP_ENDPOINT, &SampleApp_Group ); } } }
根据具体的键值做相应的处理,这里利用串口打印提示按键按下。
轮训按键链接https://pan.baidu.com/s/1zNySZRpc83p9g0-h71jhzg
第二中按键检测方式:中断检测
在 HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE, OnBoard_KeyCallback);中 设置成中断检测方式,这样会更节省系统资源,所以一般都是使用中断方式来检测按键
当按键按下时进入P0口中断服务函数
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback) { /* Enable/Disable Interrupt or */ Hal_KeyIntEnable = interruptEnable; /* Register the callback fucntion */ pHalKeyProcessFunction = cback; /* Determine if interrupt is enable or not */ if (Hal_KeyIntEnable) { /* Rising/Falling edge configuratinn */ PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE) PICTL |= HAL_KEY_SW_6_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_SW_6_ICTL |= HAL_KEY_SW_6_ICTLBIT; HAL_KEY_SW_6_IEN |= HAL_KEY_SW_6_IENBIT; HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Rising/Falling edge configuratinn */ HAL_KEY_JOY_MOVE_ICTL &= ~(HAL_KEY_JOY_MOVE_EDGEBIT); /* Clear the edge bit */ /* For falling edge, the bit must be set. */ #if (HAL_KEY_JOY_MOVE_EDGE == HAL_KEY_FALLING_EDGE) HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_EDGEBIT; #endif /* Interrupt configuration: * - Enable interrupt generation at the port * - Enable CPU interrupt * - Clear any pending interrupt */ HAL_KEY_JOY_MOVE_ICTL |= HAL_KEY_JOY_MOVE_ICTLBIT; HAL_KEY_JOY_MOVE_IEN |= HAL_KEY_JOY_MOVE_IENBIT; HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Do this only after the hal_key is configured - to work with sleep stuff */ if (HalKeyConfigured == TRUE) { osal_stop_timerEx( Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */ } } else /* Interrupts NOT enabled */ { HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); /* don\'t generate interrupt */ HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT); /* Clear interrupt enable bit */ osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE); /* Kick off polling */ } /* Key now is configured */ HalKeyConfigured = TRUE; }
并调用 halProcessKeyInterrupt()函数处理按键中断事件:
void halProcessKeyInterrupt (void) { bool valid=FALSE; if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT) /* Interrupt Flag has been set */ { HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT); /* Clear Interrupt Flag */ valid = TRUE; } if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT) /* Interrupt Flag has been set */ { HAL_KEY_JOY_MOVE_PXIFG = ~(HAL_KEY_JOY_MOVE_BIT); /* Clear Interrupt Flag */ valid = TRUE; } if (valid) { osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE); } }
这里又跑去启动 HAL_KEY_EVENT 事件了,然后就把按键发送个系统上层任务,后面 就跟轮询方式一样了
中断方式链接:https://pan.baidu.com/s/1oKAOByy6PdIaiiKieIoXMA
下面是修改IO口和中断标志的位置
修改 hal_key.C 文件.
1、 修改 SW_6 所在 IO 口
2、
/* SW_6 is at P0.4 */ #define HAL_KEY_SW_6_PORT P0 #define HAL_KEY_SW_6_BIT BV(4) //BV(1) 改到 P0.4 #define HAL_KEY_SW_6_SEL P0SEL #define HAL_KEY_SW_6_DIR P0DIR
3、 边缘触发方式
/* edge interrupt */ #define HAL_KEY_SW_6_EDGEBIT BV(0) #define HAL_KEY_SW_6_EDGE HAL_KEY_RISING_EDGE//HAL_KEY_FALLING_EDGE 改成上升缘触发
4、 中断一些相关标志位
/* SW_6 interrupts */ #define HAL_KEY_SW_6_IEN IEN1 /* CPU interrupt mask register */ #define HAL_KEY_SW_6_IENBIT BV(5) /* Mask bit for all of Port_0 */ #define HAL_KEY_SW_6_ICTL P0IEN /* Port Interrupt Control register */ #define HAL_KEY_SW_6_ICTLBIT BV(4) //BV(1) /* P0IEN – P0.1 enable/disable bit 改到 P0.4*/ #define HAL_KEY_SW_6_PXIFG P0IFG /* Interrupt flag at source */
不需要用到 TI 的摇杆 J-STICK, 所以把代码注释掉。
void HalKeyPoll (void) { uint8 keys = 0; if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */ { //keys = halGetJoyKeyInput(); }
修改 hal_board_cfg.h 文件。
1、 修改 SW_6 所在 IO 口 /* S1 */ #define PUSH1_BV BV(4) //BV(1) #define PUSH1_SBIT P0_4 //P0_1
修改 OnBoard.C 文件。在 ZMain.C 目录树下,
2、 使能中断 HalKeyConfig(HAL_KEY_INTERRUPT_ENABLE,OnBoard_KeyCallback);
下面是协议栈是检测到按键按下时候是如何处理的, 16 位必须只占 1 位,所以只能 16个任务。
回到 SampleApp.c 文件,找到按键时间处理 KEY_CHANGE 事件的函数:
当按键按下时,就会进入上面事件,我们加入串口提示:
// Received when a key is pressed case KEY_CHANGE://按键事件 HalUARTWrite(0,"KEY ",4); SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys ); break;
进入 SampleApp_HandleKeys()函数,加入我们的按键处理函数。这里是SW_6,也即是我们刚定义好的开发板上的 S1。
if ( keys & HAL_KEY_SW_6 ) { HalUARTWrite(0,"KEY1\n",4); /* This key sends the Flash Command is sent to Group 1. * This device will not receive the Flash Command from this * device (even if it belongs to group 1). */ SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION ); }
这个实验花了一个下午才弄明白,不过这一个下午的改错,对这个操作系统工作流程的理解确实比原来理解的深了一点,不过要暂时先放下ZigBee了,继续着手STM32,开始学STM32的时候忘记写一写博客了,等把这个工训大赛结束之后一起写吧!