OSAL之按键驱动
来源:互联网 发布:骚扰电话 软件 编辑:程序博客网 时间:2024/05/21 11:32
本博文根据协议栈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之按键驱动
- linux驱动之按键
- linux驱动之按键驱动
- OSAL的按键设计分析
- android驱动之虚拟按键
- linux驱动之按键中断
- Android驱动之虚拟按键
- 输入子系统之按键驱动
- 驱动之路-platform按键驱动
- S3C2440驱动之按键驱动(一)
- linux下驱动之按键驱动总结
- Linux驱动学习之:按键中断驱动
- 字符设备驱动之中断按键驱动
- 输入设备驱动之按键设备驱动
- 输入设备驱动之按键设备驱动
- ZiGbee CC2530 OSAL系统 按键事件讲解
- OSAL之任务管理
- OSAL 之功耗管理
- Java-选择排序
- C++文件操作详解
- 记一次死锁问题的排查和解决
- myeclipse去掉maven项目红叉的方法
- Oracle数据排查or业务sql优化常用SQL
- OSAL之按键驱动
- 读《大规模敏捷开发实践》
- Fildder手机抓包
- 黑马程序员——Java基础---GUI(图形用户界面)
- 曾经在linux中用交叉编译工具编了一个可执行文件在android系统的板子上跑,无法执行
- mysql数据类型
- QVGA/HVGA/WVGA/FWVGA分辨率屏含义及大小
- openwrt 目录结构概述
- mvc的应用