之前Zstack协议栈的工作原理已经讲过了,这次来一发应用举例的帮助大家理解,实验内容很简单,就是按键控制LED小灯闪烁,用的是CC2530的电路板,如果是裸机控制,过程相当简单,只要查询是否有按键按下,然后执行小灯闪烁的操作就可以了,或者用中断也可以。但是如果放到了协议栈当中,过程会复杂些,也就是之前所说的工作原理。
在添加自己的函数或者命令行之前,我们要做的最最重要的事情就是宏定义,比如按键的端口,小灯的端口,用的板子的型号等等。诸如此类的宏定义在嵌入式开发中是家常便饭思密达,因为本人使用的板子是宅基地购买的,所以宏定义大多都是HoneyGeek_V2之类的。在修改代码之前,先要有个大概的思路,那些文件里的代码是要做修改的,比如这次是用到按键和小灯,那么hal_key.c和hal_key.h,hal_board_cfg.h等一些和按键、小灯有关的.c和.h文件都要看一下。我们先看hal_board_cfg.h文件,在LED Configuration下面看到#define HAL_NUM_LEDS这句定义,很明显要改,我用的HoneyGeek_V2的板子只有一个LED所以在后面添加|| defined (HoneyGeek_V2)。
#if defined (FS2530)|| defined (HAL_BOARD_CC2530EB_REV17) && !defined (HAL_PA_LNA) && !defined (HAL_PA_LNA_CC2590)
#define HAL_NUM_LEDS 3
#elif defined (HAL_BOARD_CC2530EB_REV13) || defined (HAL_PA_LNA) || defined (HAL_PA_LNA_CC2590)|| defined (HoneyGeek_V2)
#define HAL_NUM_LEDS 1
#else
#error Unknown Board Indentifier
#endif
再往下看,看到对LED1的定义,也就是LED1的PIN和PORT的一些设置,我们可以这样改:
#ifdef HoneyGeek_V2
/* 1 - Green */
#define LED1_BV BV(3)
#define LED1_SBIT P0_3
#define LED1_DDR P0DIR
#define LED1_POLARITY ACTIVE_HIGH
#elif defined (HAL_BOARD_CC2530EB_REV17)
#define LED1_BV BV(0)
#define LED1_SBIT P1_0
#define LED1_DDR P1DIR
#define LED1_POLARITY ACTIVE_HIGH
#endif
再往下,看到一块Push Button Configuration的部分,显然是按键配置:
#ifdef HoneyGeek_V2
#define PUSH1_BV BV(2)
#define PUSH1_SBIT P1_2
#else
/* S1 */
#define PUSH1_BV BV(1)
#define PUSH1_SBIT P0_1
#endif
再往下看,找到Board Initialization我们也要修改:
#elif defined (HoneyGeek_V2)
#define HAL_BOARD_INIT()
{
......
HAL_TURN_OFF_LED1();
LED1_DDR |= LED1_BV;
/* configure tristates */
P1INP |= PUSH1_BV;
}
这部分代码就是内部时钟的初始化,后面两行是小灯和按键的设置,要改成相应的端口数值。接下来看hal_key.c文件,对按键再配置一下:
#ifdef HoneyGeek_V2
/* SW_6 is at P1.2 */
#define HAL_KEY_SW_6_PORT P1
#define HAL_KEY_SW_6_BIT BV(2)
#define HAL_KEY_SW_6_SEL P1SEL
#define HAL_KEY_SW_6_DIR P1DIR
本例子中其他部分无需修改宏定义了。那么,完成前期的宏定义之后,就可以修改后面的命令行,来实现自己要的功能了。在osal_system_start(0函数里是没有任何与按键相关的信息的,这说明在之前的函数里就已经有相关的命令了。首先,看到HAL_BOARD_INIT()函数,GOTO一下发现是Board Initialization,上面已经说过了。然后,InitBoard( OB_COLD )函数点进去看发现HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)这个配置函数,明显这个函数是按键配置函数,输入参数一个是知否中断的宏定义值,还有一个是按键回调函数,那么这个回调函数又能实现什么功能呢?来看一下这个函数:
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
{
}
}
}
关键是看if ( OnBoard_SendKeys( keys, !shift ) != ZSuccess )这句话,因为他又嵌套了一个函数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 );
}
这个函数内部关键就是osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr ),这个函数就不展开了,先看registeredKeysTaskID,这个是注册的按键任务ID号,是多少呢?在应用层已经定义好了,RegisterForKeys( PuppyApp_TaskID )就是这个函数啦!把应用层的任务ID号给了按键的任务ID号,osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr )中有个osal_set_event( destination_task, SYS_EVENT_MSG ),就是设置应用层的任务事件,然后轮询的过程中就会通知应用层有事件要处理了,应用层就调用PuppyApp_ProcessEvent( uint8 task_id, UINT16 events )函数,然后因为是KEY_CHANGE事件,所以执行PuppyApp_HandleKeys()函数,在这个函数里会判断是哪个按键触发的,我的实验用的是按键6,所以:
if ( keys & HAL_KEY_SW_6 )
{
PuppyApp_SendTheMessage();
HalLedBlink(HAL_LED_1,10,50,800);
}
这个命令就是让小灯闪烁10次。说到这里,大家有没有疑问呢?我按键的时候系统怎么知道我按了?嘿嘿,在这个函数HalKeyConfig(HAL_KEY_INTERRUPT_DISABLE, OnBoard_KeyCallback)中有个osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_POLLING_VALUE)函数,就是再过一段时间后触发HAL_KEY_EVENT事件,这次的事件是给硬件层的,所以我们找到硬件层处理函数Hal_ProcessEvent( uint8 task_id, uint16 events ):
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;
}
这里执行了HalKeyPoll()然后又是一次osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100),也就是说每隔100毫秒系统都会接收到一个HAL_KEY_EVENT要处理,反复的来检查是否有按键被按下,如果有按键被按下,就会去执行回调函数,然后按照上面的顺序来点亮小灯。整个过程就是这样啦!可能说的不太清楚,不过大家推敲一下肯定OK的啦!