Zstack的按键机制
这一节我们看按键机制,后续会陆续看UART机制和绑定机制,通过这三大机制深入学习Zstack消息的传递机制,掌握Zstack开发流程。
1、 了解TI 的EB板按键
TI的EB板按键资源有一个独立按键和一个摇杆。独立按键连接到P0_1,摇杆四个方向串联电阻连接到P0_6,摇杆的确认按钮(中心按键)连接到P0_5,类似与独立按键。
TI 的EB板按键部分原理图:
A) 独立按键原理图:
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-27971.png
图1:EB板独立按键原理图
由原理图可以看出按键为低电平有效。
B) 摇杆按键原理图
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-18030.png
图1:EB板摇杆按键原理图(这个图想横着占一页)
由原理图可以看出摇杆的确认按钮(中心按键)是高电平有效。
2、 轮询与中断
Zstack中提供了两种方式采集按键数据:轮询方式和中断方式。
轮询方式:
中断方式:
3、 按键的初始化
A)、相关宏定义
在key.c文件中定义了关于按键的宏。注意按键初始化配置属于底层驱动的配置,在涉及到底层时会遇到大量的CC2430寄存器,要养成查看datasheet的好习惯。
①、按键6(SW6)对应原理图的独立按键S1,按键6(SW6)相关的宏定义:
#define HAL_KEY_BIT0 0x01
#define HAL_KEY_BIT1 0x02
#define HAL_KEY_BIT2 0x04
#define HAL_KEY_BIT3 0x08
#define HAL_KEY_BIT4 0x10
#define HAL_KEY_BIT5 0x20
#define HAL_KEY_BIT6 0x40
#define HAL_KEY_BIT7 0x80
#if defined (HAL_BOARD_CC2430EB) || defined (HAL_BOARD_CC2430BB)
#define HAL_KEY_SW_6_ENABLE
#define HAL_KEY_SW_6_PORT P0
#define HAL_KEY_SW_6_BIT HAL_KEY_BIT1
#define HAL_KEY_SW_6_SEL P0SEL
#define HAL_KEY_SW_6_DIR P0DIR
#define HAL_KEY_SW_6_IEN IEN1
#define HAL_KEY_SW_6_IENBIT HAL_KEY_BIT5
#define HAL_KEY_SW_6_EDGE HAL_KEY_RISING_EDGE
#define HAL_KEY_SW_6_EDGEBIT HAL_KEY_BIT0
#define HAL_KEY_SW_6_ICTL PICTL
#define HAL_KEY_SW_6_ICTLBIT HAL_KEY_BIT3
#define HAL_KEY_SW_6_PXIFG P0IFG
#define HAL_KEY_P0INT_LOW_USED HAL_KEY_SW_6_BIT
#endif
②、按键5(SW5)对应原理图的摇杆的确认按钮(中心按键),摇杆相关宏定义:
#if defined (HAL_BOARD_CC2430EB)
#define HAL_KEY_JOYSTICK_ENABLE
#define HAL_KEY_JOY_CHN HAL_ADC_CHANNEL_6
#define HAL_KEY_SW_5_ENABLE
#define HAL_KEY_SW_5_PORT P0
#define HAL_KEY_SW_5_BIT HAL_KEY_BIT5
#define HAL_KEY_SW_5_SEL P0SEL
#define HAL_KEY_SW_5_DIR P0DIR
#define HAL_KEY_SW_5_INP P0INP
#define HAL_KEY_SW_5_IEN IEN1
#define HAL_KEY_SW_5_IENBIT HAL_KEY_BIT5
#define HAL_KEY_SW_5_EDGE HAL_KEY_RISING_EDGE
#define HAL_KEY_SW_5_EDGEBIT HAL_KEY_BIT2
#define HAL_KEY_SW_5_ICTL PICTL
#define HAL_KEY_SW_5_ICTLBIT HAL_KEY_BIT4
#define HAL_KEY_SW_5_PXIFG P0IFG
#define HAL_KEY_POINT_HIGH_USED HAL_KEY_SW_5_BIT
#endif
B)、代码分析:
按键的初始化属于硬件的初始化,在Zstack中硬件驱动在HalDriverInit()集中处理。在主函数Main中调用了函数HalDriverInit()进行硬件驱动的初始化,该函数根据编译选项对硬件逐个进行了初始化。HalDriverInit()代码如下:
程序代码:
void HalDriverInit (void)
{
/* 定时器 */
HalTimerInit();
/* ADC */
#if (defined HAL_ADC) && (HAL_ADC == TRUE)
HalAdcInit();
#endif
……
/* LED 灯*/
#if (defined HAL_LED) && (HAL_LED == TRUE)
HalLedInit();
#endif
/* 串口 */
#if (defined HAL_UART) && (HAL_UART == TRUE)
HalUARTInit();
#endif
/* 按键 */
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyInit();
#endif
}
由上述代码可以看出除定时器以外其它初始化都是根据条件进行初始化的,这里满足按键初始化条件。注:硬件驱动初始化没有完全列举,可以参见具体协议栈。
程序代码:
/*
Set to TRUE enable KEY usage, FALSE disable it
Notes: On 2430EB/DB analog joystick is used to simulate
keys. Keys won't work unless HAL_ADC is also set
to TRUE
*/
#ifndef HAL_KEY
#define HAL_KEY TRUE
#endif
上述代码摘自hal_board_cfg.h文件,从上述代码可以看出:TI的Zstack协议栈默认是使用独立按键的。
使用摇杆的时候还要确保HAL_ADC为真,即Zstack协议栈使用AD采集。关于HAL_ADC代码如下:
程序代码:
/* Set to TRUE enable ADC usage, FALSE disable it */
#ifndef HAL_ADC
#define HAL_ADC TRUE
#endif
上述代码同样摘自hal_board_cfg.h文件,可以看出Zstack协议栈默认使用AD转换器。由上述#define HAL_KEY TRUE和#define HAL_ADC TRUE可以知道在TI的Zstack协议栈默认情况既可以使用普通的独立按键也可以使用模拟的摇杆。
void HalKeyInit( void )
{
#if (HAL_KEY == TRUE)
halKeySavedKeys = 0;
#if defined (HAL_KEY_SW_6_ENABLE)
HAL_KEY_SW_6_SEL &= ~(HAL_KEY_SW_6_BIT); //设定为通用I/O口
HAL_KEY_SW_6_DIR &= ~(HAL_KEY_SW_6_BIT); //设置为输入模式
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
HAL_KEY_SW_5_SEL &= ~(HAL_KEY_SW_5_BIT); //设定为通用I/O口
HAL_KEY_SW_5_DIR &= ~(HAL_KEY_SW_5_BIT); //设置为输入模式
HAL_KEY_SW_5_INP |= HAL_KEY_SW_5_BIT; //设置为三态模式
#endif
pHalKeyProcessFunction = NULL;
HalKeyConfigured = FALSE;
#endif /* HAL_KEY */
}
按键驱动初始化函数HalKeyInit()说明:
⑴、配置了三个全局变量。全局变量halKeySavedKeys是用来保存按键值的,初始化时将其初始化为0;pHalKeyProcessFunction为指向按键处理函数的指针,当有按键按下时调用按键处理函数对按键进行处理,初始化时将其初始化为NULL,在按键的配置函数中对其进行配置;全局变量HalKeyConfigured用来标示按键是否被配置,初始化时没有配置按键,所以此时该变量被初始化为FALSE。
⑵、配置了两个按键的I/O口。函数中用条件语句判定是否使能了SW5和SW6,由前面的宏定义可以看出,如果使用的是TI的EB板,则SW5和SW6都将被使能。有上述代码可以看出按键驱动初始化函数HalKeyInit()将与SW5和SW6相连接的I/O设定通用I/O口,并都设置为输入模式。而且将SW5的输入模式设定为三态。
4、按键的配置
按键的初始化只是简单的对按键进行了初始化与I/O的配置,如果要使用Zstack的按键仍需要进一步对按键进行配置,下面我们着重理清按键的配置。Zstack对按键处理提供了两种方法:轮询法和中断法。在配置按键时也是分为两种情况与之对应。我们这里对两种配置模式逐一说明。Zstack默认使用轮询的方式处理,我们先以轮询入手。
A)、配置轮询方式的按键
TI的Zstack为什么会默认使用轮询方式对按键进行处理?其实最重要的一点是因为TI的EB和ED板都有摇杆。因为摇杆的四个方向是以电压的大小区分的,是不能产生中断的。如果使用了中断方式对按键进行处理那么摇杆的四个方向键就是摆设了,所以TI的Zstack采取轮询的方式处理按键。为什么要使用摇杆?其实原因也很简单,为了节省I/O口资源,CC2430只有21个通用I/O口,如果使用一个摇杆就可以就可以节省使用三个独立I/O口。
代码分析:
按键的配置函数在板载初始化函数InitBoard()中被调用,函数InitBoard()负责板载的初始化与配置。HalKeyConfig()决定了将按键的处理方式为轮询方式或者是中断方式。
程序代码:
#define HAL_KEY_INTERRUPT_DISABLE 0x00
#define HAL_KEY_INTERRUPT_ENABLE 0x01
void InitBoard( byte level )
{
……
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;//使用轮询方式
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
……
}
板载初始化函数InitBoard()说明:
由代码 HAL_KEY_INTERRUPT_DISABLE得知处理方式被设置为轮询方式。如果我们要设置为中断方式,只需要给变量OnboardKeyIntEnable赋值HAL_KEY_INTERRUPT_ENABLE即可。另一个参数OnBoard_KeyCallback是按键处理的回调函数的函数名,如果有按键按下时将会调用这个回调函数进行处理。该按键回调函数会在按键处理详细说明。
程序代码:
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
#if (HAL_KEY == TRUE)
Hal_KeyIntEnable = interruptEnable;//保存参数决定处理方式是轮询或是中断
pHalKeyProcessFunction = cback; //保存参数按键处理的回调函数
if (Hal_KeyIntEnable)//如果为真则配置为中断方式
{
……
}
else //否则配置为轮询方式
{
……
#if defined (HAL_KEY_SW_6_ENABLE)
HAL_KEY_SW_6_ICTL &= ~(HAL_KEY_SW_6_ICTLBIT); //清除中断 HAL_KEY_SW_6_IEN &= ~(HAL_KEY_SW_6_IENBIT);
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
HAL_KEY_SW_5_ICTL &= ~(HAL_KEY_SW_5_ICTLBIT); HAL_KEY_SW_5_IEN &= ~(HAL_KEY_SW_5_IENBIT);
#endif
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, L_KEY_POLLING_VALUE);
//定时触发事件HAL_KEY_EVENT,溢出时间为HAL_KEY_POLLING_VALUE。
}
HalKeyConfigured = TRUE;
#endif /* HAL_KEY */
}
按键配置函数HalKeyConfig ()说明:
⑴、配置三个全局变量。Hal_KeyIntEnable保存了按键处理方式,全局变量pHalKeyProcessFunction保存了按键处理的回调函数,在按键处理工程中将被调用。第三个变量仍是HalKeyConfigured用来标示按键是否配置,当按键配置完成将其设定为TRUE。
⑵、配置两个按键。由于上述代码使用的是轮询方式对按键事件进行配置,就不允许按键产生中断。在配置按键SW5和SW6时将其对应的中断使能禁止。具体配置代码需参见按键相关宏定义和CC2340的datasheet进行理解。
⑶、事件HAL_KEY_EVENT,在将按键配置为轮询方式后,Zstack会定时触发事件HAL_KEY_EVENT检测是否有按键按下。定时器的溢出时长为HAL_KEY_POLLING_ VALUE。
事件的处理我们后续讲解,这里完成了轮询方式的配置,接下来我们看如何将按键配置为中断方式。
B)、配置中断方式的按键
为了让大家能更加深入了解Zstack中机制和数据流的传递。虽然TI的Zstack默认是以轮询方式处理按键,但其中断方式的代码都给我们准备好了,只需我们稍微改动一点即可。下面一起看如何将按键配置为中断方式。
程序代码:
代码分析:
按键的配置函数在板载初始化函数InitBoard()中被调用,函数InitBoard()负责板载的初始化与配置。在函数InitBoard()调用按键配置函数HalKeyConfig()根据参数值对按键进行配置,决定了将按键的处理方式为轮询方式或者是中断方式。
程序代码:
#define HAL_KEY_INTERRUPT_DISABLE 0x00
#define HAL_KEY_INTERRUPT_ENABLE 0x01
void InitBoard( byte level )
{
……
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;//使用中断方式
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
……
}
程序代码:
void HalKeyConfig (bool interruptEnable, halKeyCBack_t cback)
{
#if (HAL_KEY == TRUE)
Hal_KeyIntEnable = interruptEnable;//保存参数决定处理方式是轮询或是中断
pHalKeyProcessFunction = cback; //保存参数按键处理的回调函数
if (Hal_KeyIntEnable)
{
……
#if defined (HAL_KEY_SW_5_ENABLE)
PICTL &= ~(HAL_KEY_SW_5_EDGEBIT); //设定上升沿或者下降沿触发
#if (HAL_KEY_SW_5_EDGE == HAL_KEY_FALLING_EDGE)
PICTL |= HAL_KEY_SW_5_EDGEBIT;
#endif
HAL_KEY_SW_5_ICTL |= HAL_KEY_SW_5_ICTLBIT; //设定中断使能
HAL_KEY_SW_5_IEN |= HAL_KEY_SW_5_IENBIT;
HAL_KEY_SW_5_PXIFG = ~(HAL_KEY_SW_5_BIT); //清除中断标志
#endif
#if defined (HAL_KEY_SW_6_ENABLE)
PICTL &= ~(HAL_KEY_SW_6_EDGEBIT); //设定上升沿或者下降沿触发
#if (HAL_KEY_SW_6_EDGE == HAL_KEY_FALLING_EDGE)
PICTL |= HAL_KEY_SW_6_EDGEBIT;
#endif
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); /清除中断标志
#endif
……
}
else
……
HalKeyConfigured = TRUE;
#endif /* HAL_KEY */
}
按键配置函数HalKeyConfig ()说明:
⑴、配置三个全局变量。
⑵、配置两个按键。我们这里是要将按键配置为中断方式。需要将按键配置为上升沿或是下降沿触发,同时需要将按键的对应I/O口配置为允许中断,即中断使能。在配置触发沿时首先默认配置为上升沿,然后检测按键相关宏定义决定是否需要配置为下降沿。在配置完中断使能后清除中断标志位允许按键中断。具体配置代码需参见按键相关宏定义和CC2340的datasheet进行理解。
⑶、将按键配置为中断方式,在程序中没有触发类似HAL_KEY_EVENT的事件,而是交由中断函数进行处理,当有按键按下时中断函数就会捕获中断,从而调用按键的处理函数进一步进行相关处理。关于中断方式的按键处理将在后面的进行梳理。
5、按键的处理
A)、轮询方式处理
轮询方式是TI的Zstack对按键默认的处理方式,Zstack会每隔100ms对按键检测一次,如果有按键按下就执行相关的处理。Zstack是以每隔100ms触发一次HAL_KEY_EVENT事件实现的轮询的,定时器溢出时间HAL_KEY_POLLING_VALUE即为轮询的的间隔。
Zstack每次轮询按键都会与保存的按键值(halKeySavedKeys)进行对比,保存按键值在按键初始化函数被初始化为0。如果没有按下即与先前保存的按键值相等,Zstack不进行处理。如果有按键按下,那么按键值与保存的按键值(halKeySavedKeys)不同,将会调用按键处理回调处理函数进行处理,并将此时得到的按键值保存以便下次比较。
在轮询方式配置完成后,Zstack便触发了事件HAL_KEY_EVENT,其任务ID为Hal_TaskID,则对应的处理函数即为HAL层的事件处理函数Hal_ProcessEvent()。触发了HAL层的HAL_KEY_EVENT标志着开始了按键的轮询。详细代码如下:
程序代码:
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
……
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyPoll();
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
……
}
HAL_KEY_EVENT事件处理说明:
⑴、在处理HAL_KEY_EVENT事件时调用了函数HalKeyPoll(),函数HalKeyPoll()负责检测是否有按键按下,如果有按键按下会触发相应的回调函数。
⑵、在调用函数HalKeyPoll()检测完按键过后,用if条件判断语句检测按键是否是轮询方式处理,这里我们是以轮询方式处理按键,所以满足if条件判断语句的条件,即执行函数osal_start_timerEx()定时再次触发事件HAL_KEY_EVENT,定时长度为100ms,由此可以看出如此的循环的触发事件HAL_KEY_EVENT即完成了对按键的定时轮询。
处理HAL_KEY_EVENT事件时调用了函数HalKeyPoll(),HalKeyPoll()函数进一步来去检测是否有按键按下,其详细代码如下:
程序代码:
#define HAL_KEY_STATE_NORMAL 0x00
#define HAL_KEY_STATE_SHIFT 0x01
void HalKeyPoll (void)
{
uint8 keys = 0; ////初始键值为0
#if defined (HAL_KEY_SW_6_ENABLE)
if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) //SW6低电平有效
{
keys |= HAL_KEY_SW_6;
}
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
if (HAL_KEY_SW_5_PORT & HAL_KEY_SW_5_BIT) //SW5高电平有效
{
keys |= HAL_KEY_SW_5;
}
#endif
if (!Hal_KeyIntEnable) //轮询方式
{
if (keys == halKeySavedKeys)
{
return;
}
halKeySavedKeys = keys; //保存按键值下次比较
}
if (keys && (pHalKeyProcessFunction))
{
//调用按键回调函数对按键进一步处理
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
}
HalKeyPoll()函数说明:
⑴、HalKeyPoll()函数对所有的按键进行检测,这里我们只是列举了SW5和SW6两个按键,摇杆的四个方向是利用AD采集的数值不同决定的,具体可以参见协议栈代码,这里不一一列举了。
⑵、按键值的采集。首先函数定义了一个uint8的局部变量keys用来存储按键的值,并将其值初始化为0。通过if条件语句判定是否有SW5或者SW6被按下。注意程序中的代码在检测SW5时是检测对应位是否为高电平,而检测SW6时检测对应位是否为低电平。这里的高低电平与我们最初分析原理图时一致。如果有按键按下则将其对应的数值赋给局部变量keys。
⑶、轮询处理。如果是轮询方式首先要对读取的按键进行判别,如果读取的按键值为上次的按键值直接返回不进行处理。如果读取的按键值和上次的按键值不同,则将读取的按键值保存到全局变量halKeySavedKeys以便下一次比较。并调用函数进行处理。
⑷、回调函数处理按键。当有按键按下后则keys值不为0,并且在按键配置函数HalKeyConfig ()的时候我们为按键配置了回调函数OnBoard_KeyCallback ()。所以 if (keys && (pHalKeyProcessFunction))中的两个判断条件都为真,即可以用回调函数对按键进行处理。
当有按键按下,Zstack的底层获取了按键的按键值会触发按键的回调函数OnBoard_ KeyCallback ()进一步处理,将按键信息传到上层(应用层)。按键回调函数代码如下:
程序代码:
void OnBoard_KeyCallback ( uint8 keys, uint8 state )
{
……
//调用OnBoard_SendKeys将信息发送到上层
if ( OnBoard_SendKeys( keys, shift ) != ZSuccess )
{
……
}
}
OnBoard_KeyCallback ()函数说明:
OnBoard_KeyCallback ()函数中调用了函数OnBoard_SendKeys()进一步处理,但是Zstack并没有给出OnBoard_SendKeys()处理失败的代码,用户可以根据实际需要自行添加。
在函数OnBoard_SendKeys()将会将按键的值和按键的状态进行“打包”发送到注册过按键的那一层。具体代码如下:
程序代码:
byte OnBoard_SendKeys( byte keys, byte state )
{
keyChange_t *msgPtr;
if ( registeredKeysTaskID != NO_TASK_ID )//这里必须要被注册
{
//分配内存空间
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 );
}
OnBoard_SendKeys ()函数说明
⑴、按键的注册。if ( registeredKeysTaskID != NO_TASK_ID )用来判断按键是否被注册。在Zstack中,如果要使用按键必须要注册。类似于端点endpoint的注册。但按键的注册只能注册给一个层。这里我们以SampleApp实例进行说明。
在SampleApp实例的应用层初始化代码中调用了按键注册函数RegisterForKeys ()进行按键注册,其传递的任务ID为SampleApp_TaskID。按键注册函数代码如下:
程序代码:
byte RegisterForKeys( byte task_id )
{
// 仅允许注册一次
if ( registeredKeysTaskID == NO_TASK_ID ) //判断条件,确定没有被注册
{
registeredKeysTaskID = task_id;//将传来的ID赋给了registeredKeysTaskID
return ( true );
}
else
return ( false );
}
按键注册函数仅允许注册一次,即只能有一个层注册按键。在按键注册时首先检测了全局变量registeredKeysTaskID(初始化为NO_TASK_ID)是否等于NO_TASK_ID,如果等于则证明按键没有被注册,可以被注册。按键的注册实际上就是将函数传递来的任务ID赋给
全局变量registeredKeysTaskID的过程。
⑵、数据的发送。在确定按键已经被注册的前提下,Zstack对按键信息进行打包处理,封装到信息包msgPtr中,将将要触发的事件KEY_CHANGE,按键的状态state和按键的键值keys一并封装。然后调用osal_msg_send()将按键信息发送到注册按键的对应层。
这里我们以SampleApp工程为例,Zstack最终触发了SampleApp应用层的处理函数中的KEY_CHANGE事件。代码如下:
程序代码:
uint16 SampleApp_ProcessEvent( uint8 task_id, uint16 events )
{
……
case KEY_CHANGE:
SampleApp_HandleKeys( ((keyChange_t *)MSGpkt)->state, ((keyChange_t *)MSGpkt)->keys );
break;
……
}
SampleApp_ProcessEvent()在处理HAL_KEY_EVENT事件时调用了应用层的按键处理函数SampleApp_HandleKeys()。按键处理函数SampleApp_HandleKeys()对按键进一步处理,其代码如下:
程序代码:
void SampleApp_HandleKeys( uint8 shift, uint8 keys )
{
if ( keys & HAL_KEY_SW_1 )//如果按键值keys&HAL_KEY_SW_1 为1,执行以下代码
{
SampleApp_SendFlashMessage( SAMPLEAPP_FLASH_DURATION );
}
if ( keys & HAL_KEY_SW_2 )//如果按键值keys&HAL_KEY_SW_2 为1,执行以下代码
{
……
}
}
在按键处理函数SampleApp_HandleKeys()中根据按键值的不同调用了不同的函数,按键完成了其使命。以上分析就是以轮询方式处理按键。
B)、中断方式处理
在按键配置函数HalKeyConfig ()将按键配置为中断方式后,使能了按键相对应的I/O口。这里以P0口为例进行梳理。P0口中断函数代码如下:
程序代码:
HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )
{
halProcessKeyInterrupt();
P0IF = 0;
……
}
P0口外部中断函数说明
在该中断函数调用了按键中断处理函数halProcessKeyInterrupt()对中断进行处理,且将P0口中断标志位清零。中断处理函数halProcessKeyInterrupt()代码如下:
程序代码:
void halProcessKeyInterrupt (void)
{
bool valid=FALSE;
#if defined (HAL_KEY_SW_6_ENABLE)
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
{
HAL_KEY_SW_6_PXIFG = ~(HAL_KEY_SW_6_BIT);
valid = TRUE;
}
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
if (HAL_KEY_SW_5_PXIFG & HAL_KEY_SW_5_BIT)
{
HAL_KEY_SW_5_PXIFG = ~(HAL_KEY_SW_5_BIT);
valid = TRUE;
}
#endif
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
}
}
按键中断处理halProcessKeyInterrupt ()说明:
⑴、局部变量valid说明。函数中的局部变量valid标示了是否有按键按下,如果有按键按下则定时触发HAL_KEY_EVENT事件。这里说明一点,P0口外部中断不只是我们这里的两个按键。
⑵、按键的检测。在该函数中通过检测按键对应位的中断标志位是否为1,判断按键按下。CC2340的每一个I/O都可以产生中断。如果有按键按下则要将对应位的中断标志位置为0,并将局部变量valid设置为TRUE,从而触发AL_KEY_EVENT事件对按键处理。
⑶、HAL_KEY_EVENT事件。如果有按键按下则会定时触发HAL_KEY_EVENT事件,定时长度为HAL_KEY_DEBOUNCE_VALUE(25ms)。这里说明一下,在按键中断处理函数halProcessKeyInterrupt ()并没有读取按键的值,而是定时触发了HAL_KEY_EVENT事件,在处理HAL_KEY_EVENT事件时读取。定时时长HAL_KEY_DEBOUNCE_VALUE(25ms)是为了按键消抖。
按键中断处理halProcessKeyInterrupt ()所触发的HAL_KEY_EVENT事件在HAL层事件处理函数Hal_ProcessEvent()被处理,具体代码如下:
程序代码:
uint16 Hal_ProcessEvent( uint8 task_id, uint16 events )
{
……
if (events & HAL_KEY_EVENT)
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
HalKeyPoll();
if (!Hal_KeyIntEnable)
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
#endif // HAL_KEY
return events ^ HAL_KEY_EVENT;
}
……
}
HAL_KEY_EVENT事件处理说明:
⑴、在处理HAL_KEY_EVENT事件时调用了函数HalKeyPoll(),函数HalKeyPoll()负责检测是否有按键按下,如果有按键按下会触发相应的回调函数。
⑵、在调用函数HalKeyPoll()检测完按键过后,用if条件判断语句检测按键是否是轮询方式处理,这里我们是以中断方式处理按键,所以不满足if条件判断语句的条件。
处理HAL_KEY_EVENT事件时调用了函数HalKeyPoll(),HalKeyPoll()函数进一步来去检测是否有按键按下,其详细代码如下:
程序代码:
#define HAL_KEY_STATE_NORMAL 0x00
#define HAL_KEY_STATE_SHIFT 0x01
void HalKeyPoll (void)
{
uint8 keys = 0; ////初始键值为0
#if defined (HAL_KEY_SW_6_ENABLE)
if (!(HAL_KEY_SW_6_PORT & HAL_KEY_SW_6_BIT)) //SW6低电平有效
{
keys |= HAL_KEY_SW_6;
}
#endif
#if defined (HAL_KEY_SW_5_ENABLE)
if (HAL_KEY_SW_5_PORT & HAL_KEY_SW_5_BIT) //SW5高电平有效
{
keys |= HAL_KEY_SW_5;
}
#endif
if (!Hal_KeyIntEnable) //轮询方式
{
if (keys == halKeySavedKeys)
{
return;
}
halKeySavedKeys = keys; //保存按键值下次比较
}
if (keys && (pHalKeyProcessFunction))
{
//调用按键回调函数对按键进一步处理
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
}
HalKeyPoll()函数说明:
⑴、HalKeyPoll()函数对所有的按键进行检测,这里我们只是列举了SW5和SW6两个按键,摇杆的四个方向是利用AD采集的数值不同决定的,具体可以参见协议栈代码,这里不一一列举了。
⑵、按键值的采集。首先函数定义了一个uint8的局部变量keys用来存储按键的值,并将其值初始化为0。通过if条件语句判定是否有SW5或者SW6被按下。注意程序中的代码在检测SW5时是检测对应位是否为高电平,而检测SW6时检测对应位是否为低电平。这里的高低电平与我们最初分析原理图时一致。如果有按键按下则将其对应的数值赋给局部变量keys。
⑶、回调函数处理按键。当有按键按下后则keys值不为0,并且在按键配置函数HalKeyConfig ()的时候我们为按键配置了回调函数OnBoard_KeyCallback ()。所以 if (keys && (pHalKeyProcessFunction))中的两个判断条件都为真,即可以用回调函数对按键进行处理。
后续代码与轮询方式一致,这里不再赘述。具体参见轮询方式的梳理内容。
按键处理流程图:
file:///C:\Users\WANGJI~1\AppData\Local\Temp\ksohtml\wps_clip_image-10884.png
本文来自论坛,点击查看完整帖子内容。