概述
首先要区分,按键的处理和PROFILES中的按键服务是完全两个维度,两码事。simplekeys.c是一种profile,类似simpleGATTprofile.c。
本文关注协议栈中按键是怎么从底层单片机一路向上,送到应用层处理的。
1、顺着代码逻辑从头看起,涉及到按键的最早是在main函数中先调用了HalDriverInit函数,void HalDriverInit (void)调用了HalKeyInit();设置IO口功能和方向,然后将全局变量HalKeyConfigured = FALSE;
2、main函数的osal_init_system中调用了SimpleBLEPeripheral.c 的 SimpleBLEPeripheral_Init ,其中有
RegisterForKeys( simpleBLEPeripheral_TaskID );//其中仅仅做一件事,参数赋值给全局变量:registeredKeysTaskID = task_id;
3、接着还是main函数中一系列初始化的后半部分有:InitBoard( OB_READY ); 其中就干了两件事:先把全局变量OnboardKeyIntEnable赋值1;
4、然后InitBoard中调用HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
HalKeyConfig做了什么事呢?
赋值给一个全局变量: pHalKeyProcessFunction = cback; 也就是pHalKeyProcessFunction =OnBoard_KeyCallback后面会用到这个全局变量。
开启中断、设置IO口中断方式。
然后在if (Hal_KeyIntEnable)前提下:
if (HalKeyConfigured == TRUE)//这里第一次进来,所以不走这个。
{
osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); /* Cancel polling if active */
}
最后 HalKeyConfigured = TRUE;
到这里就可以等单片机的中断信号了。
IAR中常用的中断函数定义方式为:
#pragma vector=T1_VECTOR //(h文件中定义的中断向量)
__interrupt void 中断服务程序名任意(void)
{
//中断处理程序
}
2540协议栈类似,不过是加了宏定义绕了个弯:
HAL_ISR_FUNCTION( halKeyPort2Isr, P2INT_VECTOR )
{
HAL_ENTER_ISR();
if (HAL_KEY_JOY_MOVE_PXIFG & HAL_KEY_JOY_MOVE_BIT)
{
halProcessKeyInterrupt();
}
CLEAR_SLEEP_MODE();
HAL_EXIT_ISR();
return;
}
void halProcessKeyInterrupt (void)
{
bool valid=FALSE;
if (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT) /* Interrupt Flag has been set by SW2 */
{
HAL_KEY_SW_2_PXIFG = ~(HAL_KEY_SW_2_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
if (valid)
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);//开启定时器,消抖
}
}
也就是给 tasksArr[]事件处理表中第二个任务,hal层,设置个事件,对应的处理函数就是Hal_ProcessEvent,osal操作系统检查到这里的时候就会直接调用这个函数了。
Hal_ProcessEvent( uint8 task_id, uint16 events )中有:
if (events & HAL_KEY_EVENT)
{
HalKeyPoll();//按键扫描!!!
}
if (!Hal_KeyIntEnable) //* 如果没开中断,就定时扫描按键是否有键值。
{
osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100);
}
5、HalKeyPoll()做了什么呢?
会通过 if (!(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT)) 判断确定是哪个按键,然后触发回调函数 (pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);也就是调用我们之前设置好的OnBoard_KeyCallback函数了,其中keys就是代表哪个按键。
void HalKeyPoll (void)
{
uint8 keys = 0; uint8 notify = 0;
if (!(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT)) /* Key is active low */
{
keys |= HAL_KEY_SW_2;
}
/* 查询方式下的按键消抖判断,主线不用。难点,配合下面理解。 */
if (!Hal_KeyIntEnable)
{if (keys == halKeySavedKeys){ return;} else{notify = 1; }}// keys=0也通知,此时正是查询过程中呢。keys=0说明按键松开了。
else
{//中断方式下,在中断服务函数中的定时器已经消抖,所以此时如果有键值,就代表有按键。
/* Key interrupt handled here */
if (keys)
{
notify = 1;
}
}
halKeySavedKeys = keys;//保存按键值,给下次消抖用
if (notify && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
}
6、 OnBoard_KeyCallback中调用了OnBoard_SendKeys。这个是主线,请继续看7.
OnBoard_SendKey之后还调用了下面这些???这是难点。难以理解,然而并没什么用,不影响我们按照平常的方式使用按键。
/* If any key is currently pressed down and interrupt
is still enabled, disable interrupt and switch to polling */
if( keys != 0 )
{
if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE )
{
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
/* 其中有 osal_set_event(Hal_TaskID, HAL_KEY_EVENT);又来了一遍,又设置了HAL层的事件,又到了Hal_ProcessEvent中,又是以下这 一段:
if (events & HAL_KEY_EVENT)
{ HalKeyPoll();//这次是在if (!Hal_KeyIntEnable)条件下进入的。此时按键值和上次中断进入的时候一样则return,不一样则调用回调通知上层。作用:能让我们知道按键什么时候恢复了。中断那一步时按键按下通知应用层一个keys了。这次按键值变了,说明可能是松开了,把keys=0又通知一次应用层。
if (!Hal_KeyIntEnable)
{ osal_start_timerEx( Hal_TaskID, HAL_KEY_EVENT, 100); }如果HalKeyPoll中return了,那就接着来,一直监督什么时候按键松开。
*/
}//续上正文。
}
else//跟踪函数总结为:如果这时候按键没有了,keys==0了。就重新开中断,osal_stop_timerEx(Hal_TaskID, HAL_KEY_EVENT); 停那个100的延时事件。它是指查询到键值回零后、由查询方式转为中断方式时,停止定时器。
{
if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )
{
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
}
}
小结这个难点:
这段代码是实现中断方式和扫描方式互相切换。作用是检测键值是否松开:
1)当“硬件上的按键不按为高电平,按下为低电平”时:下降沿触发时我们可以知道何时
按键松开。
2)当“硬件上的按键不按为低电平,按下为高电平”时:上升沿触发时我们可以知道何时
按键松开。
假设举例:
中断方式检测按键按下、扫描方式检测按键松开。(按键松开会通知用户)
①按键 S1 按下
触发上升沿中断,发送键值“ 0x20”给应用层。
由中断触发方式切换成扫描方式。
②按键 S1 松开
扫描到引脚变化,则发送键值“ 0x00”给应用层。不过我们基本不会用、管这个。
由扫描方式切换回中断触发方式。这属于非主线内容了。
6、 OnBoard_SendKeys中有 osal_msg_send( registeredKeysTaskID, (uint8 *)msgPtr );
通过 osal_msg_send 传递的消息, 都会到选中的任务下的默认事件下(此时也就是simpleBLEPeripheral_TaskID 任务的 SYS_EVENT_MSG 事件)。
发送消息给应用层,最终我们熟悉的应用层事件-》消息处理函数中就可以进行我们的处理了。registeredKeysTaskID在register的时候就设置好了我们的应用层任务。
说到底,按键中断的获取和标志位的复位是下层完成的,按键后产生什么样的效果则完全是应用层来进行处理。
应用层就是:
static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
switch ( pMsg->event )
{
#if defined( CC2540_MINIDK )
case KEY_CHANGE:
simpleBLEPeripheral_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
break;
.....
simpleBLEPeripheral_HandleKeys函数中进行我们想要的操作,比如按键后改变是否允许广播的设置。
最后
以上就是不安野狼为你收集整理的CC2541 OSAL中按键处理过程全解析的全部内容,希望文章能够帮你解决CC2541 OSAL中按键处理过程全解析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复