CC2530上有一个5向(上\下\左\右\中心)摇杆(Joystick)。而5个方向的输入并不是靠5个独立的IO口,而是用ADC采集P0_6口的电压值来判断的。这样做的好处是可以节省4个IO口。对摇杆的方向采集同样有轮询和中断两种方式,在ZigBee协议栈中使用的是轮询方式。我在试过两种方式后发现采用中断的效果并不好,想必这也是TI在协议栈中使用轮询的原因。这个问题先不展开,先来看看如何采集摇杆的方向。
首先,需要查阅开发板原理图,轮询或是产生中断的IO口是P2_0,即轮询P2_0口或配置P2_0的中断,当P2_0的状态发生改变时,就意味着摇杆被按下,应该采集电压值。再查阅原理图,可以发现,应该采集的是P0_6口的电压值。由于数据手册中明确提到,只有P0的8个口是支持ADC的,所以容易判断应该采集P0_6还是P2_0。而不论5个方向中哪个键被按下,都将改变P2_0的状态(按下则P2_0置1或P2_0产生中断)。
在进行电压采集时,需要注意,应使用ADC_REF_VOL_AVDD5作为参考电压。实验证明,使用内部电压作为参考是无法得到正确的电压值的。下面是使用轮询方式的代码:
int adcRead(uchar refvol, uchar resolution, uchar channel){
int reading;
ADCCON3 = refvol|resolution|channel;
ADCCFG = 1; // Enable ADC
while(!ADC_EOC);
ADCCFG = 0; // Disable ADC
reading = (int)ADCL;
reading |= (int)(ADCH << 8);
reading >>= (8 - 2 * resolution);
reading = reading < 0 ? 0 : reading;
return reading;
}
uchar getJoystickInput(){
int val = 0;
uchar key = 0;
val = adcRead(ADC_REF_VOL_AVDD5,ADC_RES_8BIT, ADC_CHANNEL_AIN6);
if (val >= 2 && val <= 38){
key |= JOYSTICK_UP;
}
else if (val >= 74 && val <= 88){
key |= JOYSTICK_RIGHT;
}
else if (val >= 60 && val <= 73){
key |= JOYSTICK_LEFT;
}
else if (val >= 39 && val <= 59){
key |= JOYSTICK_DOWN;
}
else if (val >= 89 && val <= 100){
key |= JOYSTICK_CENTER;
}
return key;
}
void initJoystick(void){
P2SEL &= ~0x01;
P2DIR &= ~0x01;
P0SEL &= ~0x40;
P0DIR |= 0x40;
}
uchar pollJoystick(void){
if (P2_0 == 0){ //0为按下,1为未按下
delay(10);
if (P2_0 == 0){
while(!P2_0); //松手检测
return 1; //有按键按下
}
}
return 0; //无按键按下
}
void main(void){
initLed();
setOSC(X_OSC,OSC_SPEED_32M);
initUART0(59,10);
initJoystick();
while(1){
if(pollJoystick()){
uchar key = getJoystickInput();
if(key == getJoystickInput()){
if(key == JOYSTICK_UP){
LED1 =!LED1;
}
else if(key == JOYSTICK_LEFT){
LED2 = !LED2;
}
else if(key == JOYSTICK_DOWN){
LED3 = !LED3;
}
else if(key == JOYSTICK_RIGHT){
LED4 = !LED4;
}
else if (key == JOYSTICK_CENTER){
toggleLed();
}
}
}
}
}
这里选择了晶振、初始化了串口是因为在中断模式里我用串口发送了采集到的电压值,以更好地观察现象。中断模式的代码如下:
void initJoystick(void){
// Push is P2_0, interrupt
EA = 1;
IEN2 |= 0x02; //for P2_0
P2IEN |= 0x01; //for P2_0
}
#pragma vector = P2INT_VECTOR
__interrupt void P2_0_Down(void){
if(P2IFG & 0x01){
// LED1 = !LED1;
uchar key = getJoystickInput();
delay(25);
if(key == getJoystickInput()){
if(key == JOYSTICK_UP){
LED1 =!LED1;
}
else if(key == JOYSTICK_LEFT){
LED2 = !LED2;
}
else if(key == JOYSTICK_DOWN){
LED3 = !LED3;
}
else if(key == JOYSTICK_RIGHT){
LED4 = !LED4;
}
else if (key == JOYSTICK_CENTER){
toggleLed();
}
sprintf(strVal,"%d", key);
UART0SendLine(strVal);
}
}
P2IFG &= ~0x01;
P2IF = 0;
}
下面来讲讲为什么使用中断的效果不好。先说现象,从上面的代码可以看出,按下不同的键会亮起不同的LED,CENTER被按下则是4个LED的切换。在使用轮询时,由于轮询有延时去抖,可以准确地采集电压,LED会正确地亮起。但使用中断时,却会出现按键后无反应、错误的LED亮起,显然这是电压采集出现的问题。于是可以看到我在中断里面学协议栈里面的轮询摇杆的代码,先采集一次、延时、再采集一次。如果两次采集结果一致,才判断具体方向。经过这个流程处理后,效果有所提升,但还是不及轮询。当我在当P2_0没有中断触发时,手动采集P0_6的电压后发现,这个值跟CENTER被按下时的值是一样的。我怀疑就是因为这个值相同,导致难以判断到底CENTER有没有被按下。
没有评论:
发表评论