概述
本博文根据协议栈1.3.2,尊重原创,注明出处,欢迎转载
学习按键驱动的主要有两大块:
第一:按键引脚设置;
第二:按键事件的触发检测与轮询,以及按键消息的发送
先说明第一大块,按键引脚设置, 超出cc2540片子从机的按键两个按键,按键是共地。所以它的触发方式是下降沿触发按键中断,同时程序对按键按下这个动作检测是中断方式检测,一旦有按键按下,触发一个按键轮询函数执行,同时把对按键的检测方式换成轮询模式。所以对按键引脚设置的内容也就出来了。主要一下几个方面:
1、设置按键映射到的物理引脚GPIO特性
2、对引脚的输入输出模式设置,
3、最重要的是对按键引脚中断寄存器的设置
//触发按键轮询函数执行先延时15毫秒,达到去抖动的效果
#define HAL_KEY_DEBOUNCE_VALUE 15 //15ms的去抖动,
/* CPU port interrupt */ //这里对寄存器特定位这样宏定义是有好处的,好好体会
#define HAL_KEY_CPU_PORT_0_IF P0IF //P0口中断标志位
#define HAL_KEY_CPU_PORT_2_IF P2IF //P2口中断标志位 ,0无中断,1有中断发生
#if defined ( CC2540_MINIDK ) || (iTA) //按键引脚定义封装
/* SW_1 is at P0.0闪烁功能 */
#define HAL_KEY_SW_1_PORT P0 //按键映射到物理引脚P0引脚,但是没有P0.0这样定义,这是一个处理方法
#define HAL_KEY_SW_1_BIT BV(0) //这个宏提供了对P0.0的精确控制,对P0.0置1这两个宏组合可以发现P0.0按键按下,是我们按键轮询的依据
#define HAL_KEY_SW_1_SEL P0SEL //P0SEL 引脚功能选择 0 GPIO ,1 外设
#define HAL_KEY_SW_1_DIR P0DIR //设置引脚输入 0 /输出 1
/* SW_2 is at P0.1 模拟来电*/
#define HAL_KEY_SW_2_PORT P0
#define HAL_KEY_SW_2_BIT BV(1)
#define HAL_KEY_SW_2_SEL P0SEL
#define HAL_KEY_SW_2_DIR P0DIR
/* SW_3 is at P0.2 模式转换*/
#define HAL_KEY_SW_3_PORT P0
#define HAL_KEY_SW_3_BIT BV(2)
#define HAL_KEY_SW_3_SEL P0SEL
#define HAL_KEY_SW_3_DIR P0DIR
//P0.0 按键中断设置
#define HAL_KEY_SW_1_IEN IEN1 /* CPU interrupt mask register P0口中断总开关,对P0.0~P0.7所有端口控制*/
#define HAL_KEY_SW_1_ICTL P0IEN /* Port Interrupt Control register端口P0.0~P0.7中断开关,每个端口的分别控制 */
//下面这两个宏是对上面两个宏进行设置的宏
#define HAL_KEY_SW_1_ICTLBIT BV(0) /* P0IEN - P0.0 enable/disable bit */
#define HAL_KEY_SW_1_IENBIT BV(5) /* Mask bit for all of Port_0 */
//当P0.0~P0.7发生中断时P0IFG相应位置位
#define HAL_KEY_SW_1_PXIFG P0IFG /* Interrupt flag at source 当P0.0到P0.7有中断发生时,相应位置位*/
//P0.1 按键中断设置
#define HAL_KEY_SW_2_IEN IEN1 /* CPU interrupt mask register */
#define HAL_KEY_SW_2_ICTL P0IEN /* Port Interrupt Control register */
#define HAL_KEY_SW_2_ICTLBIT BV(1) /* P0IEN - P0.1 enable/disable bit */
#define HAL_KEY_SW_2_IENBIT BV(5) /* Mask bit for all of Port_0 */
#define HAL_KEY_SW_2_PXIFG P0IFG /* Interrupt flag at source */
//P0.2 按键中断设置
#define HAL_KEY_SW_3_IEN IEN1 /* P0口中断开关 */
#define HAL_KEY_SW_3_ICTL P0IEN /* P0.0~P0.7中断开关,相应位置一开中断 */
#define HAL_KEY_SW_3_ICTLBIT BV(2) /* 开P0.2中断位掩码 */
#define HAL_KEY_SW_3_IENBIT BV(5) /*开P0口中断位掩码*/
#define HAL_KEY_SW_3_PXIFG P0IFG //中断标志位
#define HAL_KEY_SW_1_EDGEBIT BV(0) /*按键中断触发方式选择,这里是下降沿触发 因为共地接法*/
这上面主要对一些按键引脚,定时器的一些位,以及定时器进行了宏定义;下面将用这些宏对按键引脚端口属性进行设置。
在halkeyinit()函数中就做了一件事情,把按键引脚设置成GPIO模式同时是输入模式。那么就完成了第一大块1、2两件事情
HAL_KEY_SW_1_SEL &= ~(HAL_KEY_SW_1_BIT); /* Set pin function to GPIO */
HAL_KEY_SW_1_DIR &= ~(HAL_KEY_SW_1_BIT); /* Set pin direction to Input */
HAL_KEY_SW_2_SEL &= ~(HAL_KEY_SW_2_BIT); /* Set pin function to GPIO */
HAL_KEY_SW_2_DIR &= ~(HAL_KEY_SW_2_BIT); /* Set pin direction to Input*/
//增加P0.2的模式选择按键,GPIO,输入设置
HAL_KEY_SW_3_SEL &= ~(HAL_KEY_SW_3_BIT); /* Set pin function to GPIO */
HAL_KEY_SW_3_DIR &= ~(HAL_KEY_SW_3_BIT); /* Set pin direction to input*/
//同时对按键回调函数初始化为空,以及标记现在按键还没设置完
/* Initialize callback function */
pHalKeyProcessFunction = NULL;
//这个函数主要是用来发送按键消息,同时有按键按下将对按键的检测方式改为轮询
//下面可以发现现在回调函数是OnBoard_KeyCallback
/* Start with key is not configured */
HalKeyConfigured = FALSE;
下面这个函数完成了第一大块的第三件事情
HalKeyConfig函数,这个函数在对按键设置里面有非常重要的作用。分为两大块。就是这个函数完成在中断方式和轮询方式的转换,可以看到在OnBoard_KeyCallback函数里面就对这个函数进行了调用。
1这部分代码设置按键进入中断模式
/* Enable/Disable Interrupt or */
// Hal_KeyIntEnable是中断方式和轮询方式的标志位
Hal_KeyIntEnable = interruptEnable;
/* Register the callback fucntion */
//按键回调函数OnBoard_KeyCallback,在按键halkeypoll函数调用
pHalKeyProcessFunction = cback;
/* Determine if interrupt is enable or not */
if (Hal_KeyIntEnable) //如果是开按键中断,进行中断设置
{
#if defined ( CC2540_MINIDK ) || (iTA)
/* Rising/Falling edge configuratinn *///设置P0口中断为下降沿触发
/* Set the edge bit to set falling edge to give interrupt */
PICTL |= HAL_KEY_SW_1_EDGEBIT; //设置最后位即P0端口为下降沿给中断
/* enable interrupt generation at port *///启动P0.0~P0.7中断P0IEN
HAL_KEY_SW_1_ICTL |= HAL_KEY_SW_1_ICTLBIT;
/* enable CPU interrupt *///启动P0总中断IEN1
HAL_KEY_SW_1_IEN |= HAL_KEY_SW_1_IENBIT;
/* Clear any pending interrupt */ //清除任何中断标志位
HAL_KEY_SW_1_PXIFG &= ~(HAL_KEY_SW_1_BIT);
HAL_KEY_SW_2_ICTL |= HAL_KEY_SW_2_ICTLBIT; /* enable interrupt generation at port */
HAL_KEY_SW_2_IEN |= HAL_KEY_SW_2_IENBIT; /* enable CPU interrupt */
HAL_KEY_SW_2_PXIFG &= ~(HAL_KEY_SW_2_BIT); /* Clear any pending interrupt */
HAL_KEY_SW_3_ICTL |= HAL_KEY_SW_3_ICTLBIT; // 启动p0.2中断
HAL_KEY_SW_3_IEN |= HAL_KEY_SW_3_IENBIT; //启动P0口中断
HAL_KEY_SW_3_PXIFG &=~(HAL_KEY_SW_3_BIT); //清零P0.2的中断标志位
这上面就完成了对所有按键中断寄存器,以及中断寄存器位的设置;然后
/* Key now is configured */
HalKeyConfigured = TRUE;
说明按键设置完成了。上面是按键初始化在这个函数执行的代码,当然这个函数不止这些代码,其他代码是在运行时候执行的,用于中断方式和轮询方式的转换用。
2这部分代码是设置引脚进入轮询模式的代码
else /* Interrupts NOT enabled */ //按键中断没开
{
#if defined ( CC2540_MINIDK ) || ( iTA )
HAL_KEY_SW_1_ICTL &= ~(HAL_KEY_SW_1_ICTLBIT); /* don't generate interrupt */
HAL_KEY_SW_1_IEN &= ~(HAL_KEY_SW_1_IENBIT); /* Clear interrupt enable bit */
HAL_KEY_SW_2_ICTL &= ~(HAL_KEY_SW_2_ICTLBIT); /* don't generate interrupt */
HAL_KEY_SW_2_IEN &= ~(HAL_KEY_SW_2_IENBIT); /* Clear interrupt enable bit */
HAL_KEY_SW_3_ICTL &= ~( HAL_KEY_SW_3_ICTLBIT ); //不产生中断
HAL_KEY_SW_3_IEN &= ~( HAL_KEY_SW_3_IENBIT ); //清除P0口中断使能
osal_set_event(Hal_TaskID, HAL_KEY_EVENT);
这里可以看到在中断标志位为0的情况下执行上面这部分代码,是在有按键的按下的情况下执行,其实就是对相应IO口中断功能给关了,同时设置一个HAL_KEY_EVENT事件为轮询做准备, 跟踪代码可发现就是调用了按键轮询函数HalKeyPoll,然后设置了一个定时器周期触发HAL_KEY_EVENT事件然后去轮询按键。
//下面这部分代码是向按键按下然后释放了,它的if条件说明了在对按键初始化配置的时候是不会执行的。那么再次调用HalKeyConfig函数把1部分代码给执行了引脚重新回到中断方式,在执行下面这个代码关闭轮询定时器以可以进入睡眠模式,
/* 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 */
}
第二大块:是按键事件的触发检测,轮询,按键消息发送
假设有一个按键刚按下:处理流程如下
第一级触发检测:CC2540里面使用中断去扑捉按键按下动作
HAL_ISR_FUNCTION这个函数是P1端口的中断服务程序
#if defined ( CC2540_MINIDK ) || (iTA) //这里的宏是选择硬件平台
if ((HAL_KEY_SW_1_PXIFG & HAL_KEY_SW_1_BIT) || (HAL_KEY_SW_2_PXIFG & HAL_KEY_SW_2_BIT) || (HAL_KEY_SW_3_PXIFG & HAL_KEY_SW_3_BIT) )//这里是分别对S1 S2 S3按键按下情况检测
//如果有按键按下,调用下面按键中断处理函数
#else
if (HAL_KEY_SW_6_PXIFG & HAL_KEY_SW_6_BIT)
#endif
{
halProcessKeyInterrupt();
}
然后清除P0.0 P0.1按键 的中断状态标志位,全清或者对每一位单独清零都行,中断状态标志位只是为了进入中断服务程序,所以必须清零。然后用其他函数再去轮询按键获得按键的当前的真实状态。
#if defined ( CC2540_MINIDK ) || ( iTA )
HAL_KEY_SW_1_PXIFG = 0;
HAL_KEY_SW_2_PXIFG = 0;
#else
HAL_KEY_SW_6_PXIFG = 0;
#endif
HAL_KEY_CPU_PORT_0_IF = 0; //清除P0口的中断标志位
这里需要注意的是P0端口每个引脚的中断标志位清零要在P0端口清零之前进行。
在进入halProcessKeyInterrupe()函数对按键进行进一步的盘查
void halProcessKeyInterrupt (void)
{
bool valid=FALSE;
#if defined ( CC2540_MINIDK ) || ( iTA )
//这里任何一个if分支为真都能是valid为真,然后触发定时器,在15ms后去执行
HAL_KEY_EVENT事件
if( HAL_KEY_SW_1_PXIFG & HAL_KEY_SW_1_BIT) /* Interrupt Flag has been set by SW1 */
{
HAL_KEY_SW_1_PXIFG = ~(HAL_KEY_SW_1_BIT); /* Clear Interrupt Flag */
valid = TRUE;
}
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 (HAL_KEY_SW_3_PXIFG & HAL_KEY_SW_3_BIT)
{
HAL_KEY_SW_3_PXIFG &= ~(HAL_KEY_SW_3_BIT);
valid =TRUE;
}
#else
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;
}
#endif
if (valid) //valid为真说明按下了按键,触发 HAL_KEY_EVENT事件为了轮询按键
{
osal_start_timerEx (Hal_TaskID, HAL_KEY_EVENT, HAL_KEY_DEBOUNCE_VALUE);
}
}
//友情提醒,上面所有代码都在HAL_KEY.C文件中,可以说上面所有的工作都是为了按键检测能自动扑捉的前提工作。可以说到此中断这层对按键触发的检测是完成了。
………………..通过这个定时器,我们在往上走到了hal_drivers.c文件的事件处理函数,到了硬件抽象层啦。定位到HAL_KEY_EVENT事件,然后调用了HalKeyPoll()这个函数,它就是去获得那个按键按下了,然后记录按键号
这些是在hal_key.h文件定义的按键号
#define HAL_KEY_SW_1 0x01
#define HAL_KEY_SW_2 0x02
#define HAL_KEY_SW_5 0x04
#define HAL_KEY_SW_4 0x08
#define HAL_KEY_SW_3 0x10
HalKeyPoll()函数代码解析
这一段代码是轮询代码的精髓,定位每个按键,同时将所有按键按下的按键号记录到keys变量中,keys有八位那么可以记录八个按键的信息
if ( !(HAL_KEY_SW_1_PORT & HAL_KEY_SW_1_BIT) ) /* Key is active low */
{
keys |= HAL_KEY_SW_1;
}
if ( !(HAL_KEY_SW_2_PORT & HAL_KEY_SW_2_BIT) ) /* Key is active low */
{
keys |= HAL_KEY_SW_2;
}
if (!(HAL_KEY_SW_3_PORT & HAL_KEY_SW_3_BIT)) //P0.2 模式选择
{
keys |= HAL_KEY_SW_3;
}
下一步:中断是否开启来判定是否是中断方式或者轮询方式,Hal_KeyIntEnable =FALSE 轮询方式,Hal_KeyIntEnable =TRUE,说明在中断方式。因为这里是按键刚按下说明还处于中断模式下,那么执行红色代码
特别提醒:Hal_KeyIntEnable变量是中断方式或者轮询方式的标志位,0轮询方式,1中断方式。
if (!Hal_KeyIntEnable)
{
if (keys == halKeySavedKeys)
{
/* Exit - since no keys have changed */
return;
}
else
{
notify = 1;
}
}
else
{
/* Key interrupt handled here */
if (keys)
{
notify = 1;
}
}
/* Store the current keys for comparation next time保存当前按键号,为了下次比较只用 */
halKeySavedKeys = keys;
——————————–华丽丽分割线————————————–
以上就完成了对按键事件触发的检测,下面就是发送按键消息,同时呢把按键检测工作模式转换为轮询模式。
轮询函数最后这里憋了一个大招,回调代码。回调函数pHalKeyProcessFunction = OnBoard_KeyCallback,
这个函数在Onboard.c文件里面。也可以看到这里传递的参数是按键号keys,以及HAL_KEY_STATE_NORMAL宏,这个宏具体的作用是没有的。
if (notify && (pHalKeyProcessFunction))
{
(pHalKeyProcessFunction) (keys, HAL_KEY_STATE_NORMAL);
}
那我们就跳到OnBoard_KeyCallback函数里面,总结分析这个函数功能,就做了两件事,第一件事:发送按键消息,第二件事:对按键工作模式转换为轮询模式进行设置。
第一件事:发送按键消息
if ( OnBoard_SendKeys( keys, shift ) != SUCCESS )
第二件事:转换为轮询方式,这是很重要的的一部分
//当前如果有任何按键按下而且中断仍然开启,那么关闭中断以启动轮询方式。
if( keys != 0 )
{
if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_ENABLE )
{
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_DISABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
//再次进入这个函数就是执行上面那2段代码的。关闭中断,同时设置HAL_KEY_EVENT事件
}
}
//当前如果没有按键按下而且中断关闭,那么就要开启中断以按键进入中断工作方式
else
{
if( OnboardKeyIntEnable == HAL_KEY_INTERRUPT_DISABLE )
{
OnboardKeyIntEnable = HAL_KEY_INTERRUPT_ENABLE;
HalKeyConfig( OnboardKeyIntEnable, OnBoard_KeyCallback);
//再次进入这个函数就是执行上面1段代码,开启中断,同时关闭HAL_KEY_EVENT事件的软件定时器
}
}
因为按键按下了那么执行的是if分支,然后进入轮询模式,每100ms调用轮询函数HalKeyPoll()去轮询按键状态,然后这个时候halkeypoll是执行
if (!Hal_KeyIntEnable)
{
if (keys == halKeySavedKeys)
{
/* Exit - since no keys have changed */
return;
}
else
{
notify = 1;
}
这段代码,轮询的功能是检查按键跟上次轮询获得按键状态是否一致,若一致那么退出函数执行,也就不会再次调用OnBoard_KeyCallback函数。如果不一致,比如新按下一个按键,那么重新封装按键消息发送到上层应用。让上层应用获取当前按键按下的最新信息。
突然某个时间,按键释放了,但是程序还是处于轮询工作模式,那么相比按下按键状态keys变量是有变化的,所以在释放按键的时候也是会产生一个按键消息的。相当于上层按键处理函数会获得两个按键消息,一个是按键按下的时候,包含了按键号,另外一个是按键释放的时候,按键号keys = 0。然后keys = 0说明按键释放了。设置中断模式,按键重新进入中断工作模式,并把定时器给关了。
但是这里有个小问题是:按键释放了,然后重新设置进入中断模式,那么在轮询模式下的引脚的中断标志位是会置位的,那么会不会错误发送一个按键按下的消息给上层呢。虽然我们把P0口,P0.0和P0.1的中断使能给关了不会中断响应。但是我们没有对相应的中断标志位清零的。所以再次转入中断模式下呢会触发中断服务函数HAL_ISR_FUNCTION( halKeyPort0Isr, P0INT_VECTOR )执行并调用halProcessKeyInterrupt 函数去查看中断标志是否有置位,当然有的,然后设置了HAL_KEY_EVENT事件,触发halkeypoll函数轮询按键,还好因为keys =0 所以不会发生任何事情。执行一次而已。其实我们以前写中断服务程序都是一个函数写到黑,那么这些问题就不能避免了,但是这样用函数调用的方式很好的避免了这样的问题。不愧是意外之喜。
2015/06/03 23:17
最后
以上就是成就指甲油为你收集整理的OSAL之按键驱动本博文根据协议栈1.3.2,尊重原创,注明出处,欢迎转载学习按键驱动的主要有两大块:的全部内容,希望文章能够帮你解决OSAL之按键驱动本博文根据协议栈1.3.2,尊重原创,注明出处,欢迎转载学习按键驱动的主要有两大块:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复