我的开发板上只有一个按键S2和一个5向Joystick。Joystick涉及到ADC,暂时不去弄了。对于按键的处理可以采用两种方式:轮询和中断。轮询是一种低效的方式,CPU不断的访问每一个可能产生输入信号的管脚,如果有信号输入,那就做出相应的处理,否则访问下一个输入管脚。而中断方式则类似于C#里的事件绑定、Java里的观察者模式,当输入管脚、时钟等产生一个中断信号,CPU会中止当前正在处理的事务,转去执行中断源的程序。待处理完中断再继续之前中止的事务。这里我用的是“类似”,即它并不是一种多线程模式,如果在执行中断源的响应程序的过程中,又有新的中断,那么后产生中断会等待当前中断源的响应程序执行完毕才会执行。这个等待队列也是有限的,似乎只能容纳一个等待的中断源。
首先讲一下轮询模式。
轮询模式下,需要将按键的管脚设置为普通I/O口,输入模式。我的按键在P0.5,所以把P0SEL和P0DIR的第5位都置0:
#define S2 P0_5 P0SEL &= ~0x20; P0DIR &= ~0x20;
然后在main函数里:
void main(void){ initLed(); // 初始化所以的LED灯,并全部处于熄灭状态,此处略去具体实现 P0SEL &= ~0x20; P0DIR &= ~0x20; while(1){ if (scanS2()){ // 按键按下则改变LED状态 flashLed(); // 闪烁所有的LED灯,此处略去具体实现 } } }
scanS2()这个函数是用来检测按键S2是否被按下并被释放,即在被释放后才会执行flashLed()。
scanS2()的实现如下:
uchar scanS2(void){ if (S2 == 0){ //0为按下,1为未按下 delay(10); // 延时去抖 if (S2 == 0){ while(!S2); //松手检测 return 1; //有按键按下 } } return 0; //无按键按下 }
轮询模式到这里就结束了,代码很少。下面是中断模式
中断模式需要查阅CC2530的数据手册,如图,需要将PICTL对应位上置0或1(端口触发中断模式,上升沿、下降沿,由图可知,两种方式都可以触发,但CC2430的图CC2530不同,只能采取一种方式触发)、P0IEN(P0口各位中断使能)的对应位置1;P0IE(P0口中断使能)、以及EA(总中断使能)设为1。
然后是实现中断处理函数:
#pragma vector = P0INT_VECTOR __interrupt void P0_5_KeyPress(void) { if((P0IFG & 0x20) == 0x20){ //P0IFG的第5位为1,即P0_5触发了中断 flashLed(500); // 响应完毕,最后清除标志位 P0IF = 0; //清P0的中断未决标志(0为无中断未决) P0IFG &= ~0x20; //清P0_5的中断标志 } }
其中前两行的格式固定,必须前后相接,唯一可以改动的就是第二行的函数名称,我这里是P0_5_KeyPress,这个名字是可以改的。从这个名字可以看出,一旦S2被按下,就会发出中断信号,即使按键还没有被释放。而第四行里,判断是否为S2产上的中断用了if((P0IFG & 0x20) == 0x20,这样可以准确地判断是否是由P0_5产生的中断。而我看的教程里,直接用P0IFG > 0 来判断,这种做法是存在误判的可能的。
当执行完中断处理的程序后,需要清理掉相关的标志位,注释中已经写明了,同样只动P0IFG的第5位,别的不要碰。
使用中断方式,main()函数里也很简练:
void main(void){ initLed(); P0IEN |= 0x20; // P0.5 设置为中断方式 1:中断使能 PICTL |= 0x20; // 下降沿触发 P0IE = 1; // 允许P0口中断; EA = 1; // 打开总中断 while(1); }
至此,按键的两种模式就讲完了。实际上,可以将这些LED、按键的初始化操作封装到几个函数中,之后在开发中只要引入头文件就可以直接调用了,十分方便。虽然这一工作已经有人完成,并且可以很容易地在网上找到这些代码,但对于新手来说,这样的“重新发明轮子”的工作还是值得去做一下的。
没有评论:
发表评论