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有没有被按下。
没有评论:
发表评论