2014年4月8日

CC2530开发板基础实验(5)—— JoyStick

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有没有被按下。

没有评论:

发表评论