Tuesday, September 27, 2011

Feeling a bit touchy

I've spent the last few days playing with mouse capacitive touch pads.  I have successfully setup a touch sensor that is operating as my first mouse button.  The operation is dependent on measuring the time it takes to charge the internal pull-up resistor and a small external capacitor (470pF).  There is also a 100K resistor to ground.  If someone is touching the sensor pad, it takes longer to charge.  That time differential is used to register capacitive touch events.  I did notice that this only works on the PWM equipped ports on the Teensy 2.0 board.

Here is a copy of the subroutine that reads the time it takes to charge:

int getButtonStat(int b){
  digitalWrite(b,HIGH);
  int i;
  for(i=0; i<16; i++){
    if(digitalRead(b) == HIGH){
      break;
      }
    }
  digitalWrite(b,LOW);
  delay(1);  //needed to fully discharge the circuit
  return(i);
  }

This was inspired by the work done here, which uses the same proceedure essentially.

This part of the project is driven by the need to make this trackball silent.  My wife and I really don't like the constant clicking for three hours while I'm raiding on my druid!  My computer sits in the living room, so the constant clicking is teadious for everyone.

3 comments:

  1. John-

    I've done a lot of capacitive touch detection and, while I can't share any code, I can give a few hints. Please ignore the rest if I'm telling you stuff that you already know but don't agree with.

    1) The difference between your routine and the one you linked to is that you're using the digitalRead functions where they are using the direct PORTB/PINB commands. If I remember correctly, the digitalRead command is much slower than direct port access so you end up with a loop that can only count to 16 per sample. If you can get that read to happen quicker then you can get better resolution on each sample.

    2) Disable interrupts while reading. This will make the values much more stable.

    3) You should not need the external cap and the pullup can generally be increased to something in the 510k range. This makes the reading based to a greater degree on the touch influence and less on the constant values.

    -Robert

    ReplyDelete
  2. Thanks for the great suggestions Robert. I have indeed abandoned the digitalRead function in favor of the analogRead version.

    I'm using the pull-ups that are built into the microprocessor and not so much external resistors of my choosing. Perhaps looking at doing these external to the built-in pull-ups would yield satisfying results as well.

    Currently, the code is working extremely well with the built-ins and just the one external cap per pin. I've cut the code down below to highlight what it working with the button detecting. It is far from what I originally started from, but solid detecting. Not to say there isn't room for improvement though :-)

    I like the idea of disabling the interrupts while doing a read of the buttons to ensure that we are not disturbed and get a false reading while charging the cap.

    -

    // Declare variables & set initial values
    const int buttonAvgSamples = 8;
    const int resButton = 0;
    int resbutton = 0;
    int oldBstatus[5];
    int buttonstatus[5];
    int bread[5];
    long bsense[5];
    int bsum[5];
    int bptr[5];
    int buttons[5];
    int bCapAvg[5][buttonAvgSamples];

    void setup() {
    pinMode(resSelectButton, INPUT);
    pinMode(motiondetect,INPUT_PULLUP);

    pinMode(button1, INPUT);
    pinMode(button2, INPUT);
    pinMode(button3, INPUT);

    }



    void setCapSense(void){
    digitalWrite(button1,HIGH);
    digitalWrite(button2,HIGH);
    digitalWrite(button3,HIGH);
    digitalWrite(resSelectButton,HIGH);
    delay(100);
    Serial.println("*** Setting button sensor baselines");
    int ictr=0;
    for(ictr=1;ictr<=4;ictr++){
    delay(10);
    // bsense[ictr] = getButtonStat(ictr);
    bsense[ictr] = 1023; //manually set these, calibration not working. caps staying full 'till touched
    delay(10);
    }
    Serial.println(bsense[1]);
    Serial.println(bsense[2]);
    Serial.println(bsense[3]);
    Serial.println(bsense[4]);
    Serial.println("++++++++++++");
    //delay(3000);
    }

    int getButtonStat(int b){
    int i=0;
    int riseRead=0;
    int riseAvg = 0;
    digitalWrite(b,LOW);
    delay(1);
    digitalWrite(b,HIGH);
    for(i=0; i<3; i++){
    riseRead += analogRead(b);
    }
    riseAvg = riseRead/3;
    digitalWrite(b,LOW);
    //Serial.println("^^^");
    //Serial.println(b);
    //Serial.println(riseRead);
    //Serial.println(riseAvg);
    //delay(1500);
    return(riseAvg);
    }



    void chkButton(int b){
    int butsum=0;
    int avgPtr=0;
    if (bread[b] <= 0) {
    if (bptr[b]>buttonAvgSamples){
    bptr[b]=0;
    }
    if ((getButtonStat(buttons[b])+15) < bsense[b]){
    bCapAvg[b][bptr[b]] = 100;
    }
    else{
    bCapAvg[b][bptr[b]] = -20;
    }
    for(avgPtr=0;avgPtr 0){
    buttonstatus[b] = 1;
    }
    else{
    buttonstatus[b] = 0;
    }
    int calcDelay=0;
    for(calcDelay=1;calcDelay<4;calcDelay++){
    bread[b] += (20 * buttonstatus[calcDelay]);
    }
    }
    }


    void loop() {
    buttons[1]=button1;
    buttons[2]=button2;
    buttons[3]=button3;
    buttons[4]=resSelectButton;
    setCapSense();

    while(1){
    chkButton(4);
    if(buttonstatus[4]){
    resbutton = 1;
    }
    if ( !buttonstatus[4] && resbutton == 1) {
    resbutton = 0;
    changeResolution();
    }

    chkButton(1);
    chkButton(2);
    chkButton(3);


    if( (buttonstatus[1]!=oldBstatus[1] || buttonstatus[2]!=oldBstatus[2] || buttonstatus[3]!=oldBstatus[3]) ){
    Mouse.set_buttons(buttonstatus[1], buttonstatus[2], buttonstatus[3]);
    oldBstatus[1] = buttonstatus[1];
    oldBstatus[2] = buttonstatus[2];
    oldBstatus[3] = buttonstatus[3];
    //Serial.println("---");
    //Serial.println(buttonstatus[1]);
    //Serial.println(buttonstatus[2]);
    //Serial.println(buttonstatus[3]);
    }
    bread[1]--;
    bread[2]--;
    bread[3]--;
    bread[4]--;
    //delay(800);
    }
    }

    ReplyDelete
  3. Apparently, blogger's editor is not too friendly to posting code. I see lots of missing little bits here and there. Hopefully it is complete enough to get the idea.

    ReplyDelete