概述
我做操作的芯片是TiCC2530。就不考虑Zstack协议栈而言,如果要用到按键,无非是中断或者查询两种方式。
查询方式是通过查询按键对应I/O口的状态来判断按键的状态,从而进行相应的处理。
外部中断的方式是使能按键对应得I/O口的中断,当I/O口的状态改变时触发中断,从而在相应的中断处理函数中进行处理。
万变不离其宗,Zigbee协议栈的按键操作本质上也是对于基本查询方式和终端方式的封装。对于查询方式我们关键要找到它是如何配置引脚的功能/输入输出/上拉下拉,以及是如何查询引脚状态的,对于中断方式关键是配置相应引脚的中断和相应的中断处理函数。
首先是ZMain.c文件中的两个函数涉及到了按键的配置,分别是InitBoard( )和 HalDriverInit()其中InitBoard()调用了两次
// Initialize board I/O
InitBoard( OB_COLD );
右键GO TO,其中参数OB_COLD是一个宏#define OB_COLD 0
void InitBoard( uint8 level )
{
if ( level == OB_COLD )
{
// Interrupts off
osal_int_disable( INTS_ALL );
// Turn all LEDs off
HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
// Check for Brown-Out reset
ChkReset();
}
else // !OB_COLD
{
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
}
第一个if是成立的,所以关闭所有的中断并关闭LED。我们回到main()函数
// Initialze HAL drivers
HalDriverInit();
右键GO TO,找到了按键的初始化函数HalKeyInit()
/* KEY */
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyInit();
#endif
右键GO TO ,这里主要是配置了P0SEL、P0DIR和P1SEL、P1DIR为通用和输入。
void HalKeyInit( void )
{
/* Initialize previous key to 0 */
halKeySavedKeys = 0; //默认当前键值为0
HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); /* Set pin function to GPIO */ //P0.1设置为通用
HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); /* Set pin direction to Input */ //P0.1设置为输入
HAL_KEY_JOY_MOVE_SEL &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin function to GPIO */ //P2.0设置为通用
HAL_KEY_JOY_MOVE_DIR &= ~(HAL_KEY_JOY_MOVE_BIT); /* Set pin direction to Input */ //P2.0设置为输入
/* Initialize callback function */
pHalKeyProcessFunction = NULL; //默认无回调函数
/* Start with key is not configured */
HalKeyConfigured = FALSE;
}
GO TO HAL_KEY_SW_6_SEL发现就是P0SEL寄存器,到这里终于揭开了Zigbee包装的一角,其中寄存器的配置是也是以宏的方式给出的
#define HAL_KEY_SW_6_PORT P0
#define HAL_KEY_SW_6_BIT BV(1)
#define HAL_KEY_SW_6_SEL P0SEL
#define HAL_KEY_SW_6_DIR P0DIR
#ifndef BV
#define BV(n) (1 << (n))
#endif
/* Joy stick move at P2.0 */
#define HAL_KEY_JOY_MOVE_PORT P2
#define HAL_KEY_JOY_MOVE_BIT BV(0)
#define HAL_KEY_JOY_MOVE_SEL P2SEL
#define HAL_KEY_JOY_MOVE_DIR P2DIR
特别的,1<<(n)就是将1左移n位,整数型立即数默认类型是int型,占有4个字节,所以1<<(1)等价于 0x 0000 0000 0000 0010。
/* Initialize callback function */
pHalKeyProcessFunction = NULL;
回调函数为空,说明中断方式的中断处理函数不是在这里进行设置的。我们回到main()函数,发现再次调用了InitBoard(),这次的参数为2 ,#define OB_READY 2
// Final board initialization
InitBoard( OB_READY );
右键GO TO,由于参数为2,这次进入了第一次调用InitBoard()时没有进入的另一个if分支
void InitBoard( uint8 level )
{
if ( level == OB_COLD )
{
// Interrupts off
osal_int_disable( INTS_ALL );
// Turn all LEDs off
HalLedSet( HAL_LED_ALL, HAL_LED_MODE_OFF );
// Check for Brown-Out reset
ChkReset();
}
else // !OB_COLD
{
/* Initialize Key stuff */
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
}
右键GO TO 按键配置函数HalKeyConfig(),发现这里配置了引脚的中断
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_KeyIntEnable = interruptEnable;而interruptEnable的值等于宏HAL_KEY_INTERRUPT_DISABLE,默认是不采用外部中断方式,那也就是查询方式,可以通过选择不同的宏来控制采用哪种方式。
/* Interrupt option - Enable or disable */
#define HAL_KEY_INTERRUPT_DISABLE 0x00
#define HAL_KEY_INTERRUPT_ENABLE 0x01
配置了回调函数,也就是原来的中断处理函数
/* Register the callback fucntion */
pHalKeyProcessFunction = cback;
如果Hal_KeyIntEnable为HAL_KEY_INTERRUPT_ENABLE的话就进行引脚的中断使能配置
if (Hal_KeyIntEnable)
否则的话就清除中断配置,并产生一个HAL_KEY_EVENT事件
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 */
}
有事件产生就有相应的事件处理函数,如果对Zigbee事件机制不太理解的可以参考点击打开链接。
这个事件在hal_drivers.c文件里的Hal_ProcessEvent函数被处理
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;
}
如果Hal_KeyIntEnable == 0 即采用轮询方式就周期性设置这一事件,从而达成了一个循环调用的效果,轮询函数为HalKeyPoll(),可以看到轮询的事件大约是100ms。HalKeyPoll()函数会检查按键的状态以确定是否要按键状态的改变。
void HalKeyPoll (void)
{
uint8 keys = 0;
// if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */ //如果P2.0是高位
// {
// keys = halGetJoyKeyInput();
// }
/* If interrupts are not enabled, previous key status and current key status
* are compared to find out if a key has changed status.
*/
if (!HAL_PUSH_BUTTON1()) //HAL_PUSH_BUTTON1() = !!(P0_1)
{
keys |= HAL_KEY_SW_6;
}
if (!HAL_PUSH_BUTTON2()) //HAL_PUSH_BUTTON2() = !!(P2_0)
{
keys |= HAL_KEY_SW_1;
}
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);
}
}
// if ((HAL_KEY_JOY_MOVE_PORT & HAL_KEY_JOY_MOVE_BIT)) /* Key is active HIGH */ //如果P2.0是高位
// {
// keys = halGetJoyKeyInput();
// }
/* If interrupts are not enabled, previous key status and current key status
* are compared to find out if a key has changed status.
*/
if (!HAL_PUSH_BUTTON1()) //HAL_PUSH_BUTTON1() = !!(P0_1)
{
keys |= HAL_KEY_SW_6;
}
if (!HAL_PUSH_BUTTON2()) //HAL_PUSH_BUTTON2() = !!(P2_0)
{
keys |= HAL_KEY_SW_1;
}
hal_key.c中HalKeyPoll()中的这一段是我私自更改的地方,由于我用的开发板的P0_1和P2_0是上拉状态,所以未按下时引脚是高电平,而协议栈默认是以高电平作为按键按下的状态,所以我的板子初始化之后就会自动识别为有按键按下,因此我改成了低电平时才认为有按键按下。同时,由于我的开发板无操纵杆,所以注销了操作杆的状态识别。
这里的按键是以位区分的,这样做的好处是可以将多个按键的触发事件按位存储到keys中,并通过位运算识别和增删。
/* Switches (keys) */
#define HAL_KEY_SW_1 0x01 // Joystick up
#define HAL_KEY_SW_2 0x02 // Joystick right
#define HAL_KEY_SW_5 0x04 // Joystick center
#define HAL_KEY_SW_4 0x08 // Joystick left
#define HAL_KEY_SW_3 0x10 // Joystick down
#define HAL_KEY_SW_6 0x20 // Button S1 if available
#define HAL_KEY_SW_7 0x40 // Button S2 if available
在最后,如果有按键被按下且回调函数不为空时即调用回调函数。由于在HalKeyConfig()中已经注册了回调函数,所以这个回调函数是不为空的,所以就变成了只要有按键按下就调用回调函数。
/* Invoke Callback if new keys were depressed */
if (keys && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
回调函数在当前文件中
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
uint8 shift;
(void)state;
/* Get shift key status */
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 ),右键GO TO这个函数
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 );
}
在这个函数中将按键触发状态key打包发送到应用层,并触发应用层事件处理函数
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
afIncomingMSGPacket_t *MSGpkt;
(void)task_id;
if ( events & SYS_EVENT_MSG )
{
MSGpkt = (afIncomingMSGPacket_t *)osal_msg_receive( SampleApp_TaskID );
while ( MSGpkt )
{
switch ( MSGpkt->hdr.event )
{
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
然后就可以在应用层调用相应的事件处理函数来处理这个按键事件,而到底是哪个按键被按下,可以通过'&'操作来检查keys的位状态,哪一位为1就表示那一位对应的按键发生了改变。
void SampleApp_HandleKeys( uint8 shift, uint8 keys )
{
(void)shift;
if ( keys & HAL_KEY_SW_6 )
{
/* 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 );
HalLedSet( HAL_LED_1 , HAL_LED_MODE_TOGGLE ) ;
zgWriteStartupOptions(ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE);
SystemReset();
}
if ( keys & HAL_KEY_SW_1 )
{
/* 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.
*/
}
}
水平有限,如有差错,请不吝赐教!
最后
以上就是简单面包为你收集整理的Zigbee 按键机制的全部内容,希望文章能够帮你解决Zigbee 按键机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复