Optical sensor bounce issue

Status
Not open for further replies.

turtle9er

Well-known member
Hi Everyone,

I have been working on a project and getting some odd behaviours when using an optical slotted switch (http://www.digikey.ca/product-detail/en/tt-electronics-optek-technology/OPB625/365-1090-ND/498727)
It is set up on a indoor bicycle that is reading a custom chainring that has 72 teeth, and is used to measure both the angle of the pedal, but also the angular velocity. I have always had it set up to read when a tooth blocks the sensor, or when there is a falling edge in the signal. It worked during all my breadboard testing, however as soon as I started to use a teensy 3.2 (5v tolerant digital) vs a 3.1 (used a logic level converter 5-3.3v), I am getting a reading of 2us.
At first the only thing I could think of was to put a capacitor on the digital output signal, which worked (also as soon as I hooked up the OScope the device works, unhook it and get wrong reading). Even though it worked I was still in the dark as to why I was having this issue. So I hooked up the scope again and got this image Optical sensor bounce.png which shows the digital signal bounces up to a level that must activate the interrupt (This image is with cap in place, without it bounces to 1.2V). This is the code in my interrupt routine which I thought would eliminate any bad readings by setting a limit of 3000us, however does not seem to be the case. See code below. Even though it working, I still don't know exactly why and that is driving me crazy, plus I like things to be perfect. Thanks for your time and any experts out there that might have an idea, I would love to hear it. Forgot to mention the optical switch has a 10k pull up resistor built in, so in theory when I add a capacitor it creates an RC filter (30pf is the lowest capacitor that works, but 0.1uf also works, but creates a very slow rise time in the signal)
Code:
//June 2016
//Track Cycling SRM Platinum collection program
//Updated from BMX sketch, adjust buffersize to get all samples during slower start
//OSH Board Ver 1
//No Direction pin used

#define hzPin 11 //PortC
#define degPin 0 //PortB
#define cadencePin 2 //PortD
#define dirPin 4 //PortA, not used, but will still set as input
#define DEGREE_FLAG 1
#define Cadence_FLAG 2

#define calLED 7
#define calLED2 15
#define LIGHTS true //If you want a signal light at specific angle
//TDC=360 deg
uint16_t degOn = 360;//start of indicator light
uint16_t degOff = 5;//stop of indicator light

// rising edge indicates 360 or tdc, used in labview post processing
#define PulsePin 22 //
#define PULSEOUT true //if need a position pulse, set to true
uint16_t PulseLow = 180;
uint16_t PulseHigh = 360;

volatile uint8_t bUpdateFlagsShared; //hold the updated flag
// shared variables are updated by the ISR and read by loop.
//Evan June Pilot testing max 629 samples at 9259hz
//Will be bike dependent if slope is larger or smaller
const int BUFFERSIZE=1000; //Adjust to meet needs of Track Cycling, adjust labview accordingly
volatile int DegreeTimeShared;
volatile int CadenceTimeShared;
volatile uint16_t HZSamplesShared = 0;
volatile int HZInShared[BUFFERSIZE];
volatile int IndexShared;
volatile int PositionShared;
volatile int PositionDegShared;
volatile unsigned long DegreeStart;

unsigned long HZStart;
unsigned long HZPeriod;
unsigned long HZ;
unsigned long CadenceStart;
float Offset = 460.00; //Van SRM 474, Victoria 533: typical offset value
float sum = 0.00;
uint8_t offsetcount = 0;

uint16_t i=0;
uint16_t count=0;
boolean calibrate = false;
int SampleSize;
uint8_t collectionCount = 2;

void setup(void) 
{ 
  Serial.begin(115200); 
  pinMode(hzPin, INPUT); //pin 11
  pinMode(degPin, INPUT); //pin 0
  pinMode(cadencePin, INPUT); //pin 2
  pinMode(dirPin, INPUT); //pin 4, just to protect teensy, not used
  pinMode(calLED, OUTPUT); //Calibrate LED
  pinMode(calLED2, OUTPUT);
  pinMode(PulsePin, OUTPUT);
  
  NVIC_SET_PRIORITY(IRQ_PORTC,32); //Pin 11 is on Port C 0=Highest
  //if using 3.3V, then RISING works better, if 5V switch to FALLING
  //however, both seem to work for 3.3V
  attachInterrupt(hzPin, counterhz, RISING);//schmitt trigger inverts signal
  
  //collect 2000 samples to determine offset  
  collectionCount = 2000 / BUFFERSIZE;
  while (calibrate == true) {
    digitalWrite(calLED, HIGH); 
    digitalWrite(calLED2, HIGH);
  }
  //delay(5000);
  digitalWrite(calLED, LOW); //turn off calibrate light
  digitalWrite(calLED2, LOW);
  SampleSize = BUFFERSIZE*collectionCount;
  SampleSize = SampleSize-10;
  Offset = sum/SampleSize; //Calculate average of samples collected
  //Serial.println(SampleSize);
  //Serial.println(Offset);

  //did not work with encoder without Oscope connected to ground, try with capacitors
  //changed degPin to RISING since falling did not work, however dbl counted position
 //need to try another method with capacitors, maybe less than 100nf
 //creating RC time constant with 10k pullup, try 100pf (0.0108ms delay)
 //added 30pf and fixed the timing issue
 //sensor seems to bounce when falling edge, so might have to use rising with capacitor
 //30pf reduces bounce to 940mv which won't trigger interupt
 attachInterrupt(degPin, counterDeg, FALLING); //Start at leading edge of Tooth
 attachInterrupt(cadencePin, doIndex, RISING);//Starts at index opening

}

void loop(void) 
{
  static int DegreeTime;
  static int HZIn[BUFFERSIZE];
  static int Index; 
  static int Position;
  static int PositionDeg;
  static uint8_t bUpdateFlags;
  static uint16_t HZSamples = 0;

  if (Serial.available() > 0) {

    int inByte = Serial.read();
    switch (inByte) {
    case 'a':    
      IndexShared = 0; //if serial is a, reset index
      Serial.flush();
      break;
     case 'c':   //Calibrate SRM Offset 
      calibrate = true;
      offsetcount=0;
      sum = 0;
      while (calibrate == true){
        digitalWriteFast(calLED,HIGH);
        digitalWriteFast(calLED2,HIGH);
      }
      Offset = sum/SampleSize; 
      digitalWriteFast(calLED,LOW);
      digitalWriteFast(calLED2,LOW); 
      //Serial.println(sum);
      //Serial.println(Offset);
      Serial.flush();
      break;
    }
  }


  if(bUpdateFlagsShared)
  {
    noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables
    // take a local copy of which channels were updated in case we need to use this in the rest of loop
    bUpdateFlags = bUpdateFlagsShared;

    if(bUpdateFlags & DEGREE_FLAG)
    {
      DegreeTime = DegreeTimeShared;
      HZSamples = HZSamplesShared;
      Index = IndexShared;
      Position = PositionShared;
      PositionDeg = PositionDegShared;
      for (int k=0; k<HZSamples; k++)
      {
        HZIn [k] = HZInShared [k]; //copy HZ array
      }
    }

    bUpdateFlagsShared = 0;
    HZSamplesShared=0; 
    interrupts(); //turn back on

  }

  if(bUpdateFlags & DEGREE_FLAG ){
    Serial.print ("O");
    Serial.print (Offset);
    Serial.print ("I");
    Serial.print (Index);
    Serial.print ("D");
    Serial.print (DegreeTime);
    Serial.print ("C");
    Serial.print (CadenceTimeShared); 
    Serial.print ("P");
    Serial.print (Position);
    Serial.print ("Q");
    Serial.print (PositionDeg);
    Serial.print ("H");
    for ( i=0; i<HZSamples; i++) { //print HZ array
      Serial.print(HZIn[i]); 
      if (i<HZSamples-1){
        Serial.print(",");
      }
    }
    Serial.println();
    Serial.send_now();
    bUpdateFlags = 0;
  }
  
  //If set to true, light will flash at positions set above
  if (LIGHTS) {
    if (PositionDeg == degOn) {
      digitalWriteFast(calLED, HIGH); //Flash LED to use in Video Sync?
      digitalWriteFast(calLED2, HIGH);
    }
    if (PositionDeg == degOff || PositionDeg > 360) { //if above 360 turn off
      digitalWriteFast(calLED, LOW); //
      digitalWriteFast(calLED2, LOW);
    }
  }

    //Send digital pulse out when needed to send sync to other device
  if (PULSEOUT) {
    if (PositionDeg == PulseHigh)digitalWriteFast(PulsePin, HIGH); //attach to ADC to sync EMG when Right TDC
    if (PositionDeg == PulseLow || PositionDeg > 360) digitalWriteFast(PulsePin, LOW); //reset if above 360
  }
  
}


//Triggers on RISING (index open = HIGH)
//Adjust gap so rising just after 360 trigger (falling edge)
void doIndex() { //Cadence
  if (PositionShared >= 71){ //to prevent false reset on bad index signal, OMIT?
    PositionShared=-1; //Reset position to -1 since want position 0-71 
    PositionDegShared=0; //Reset degree position want 5-360deg
    CadenceTimeShared=millis()-CadenceStart;
    CadenceStart=millis(); 
    IndexShared++;
  }
}

//Trigger on Falling, or leading edge of tooth
void counterDeg()// Degree Time
{ 
  DegreeTimeShared=micros()-DegreeStart;
  if (DegreeTimeShared >= 3000){ //to prevent false firing, 3000 = 278rpm
    DegreeStart=micros(); 
    PositionShared++; 
    PositionDegShared+=5;
    bUpdateFlagsShared |= DEGREE_FLAG;
 }
}

FASTRUN void counterhz() //Frequency
{ 
  HZPeriod = micros() - HZStart;
  if (HZPeriod >= 75){ //285us=3500hz, 334=3000hz, 75=13333hz
    HZStart=micros();
    HZ=1000000 / HZPeriod; //convert period to frequency
    if ( HZSamplesShared < BUFFERSIZE && HZ >300 && HZ < 10000) { //fill array with all HZ per chaing ring tooth
      //Vic SRM offset ~530hz
      //Van SRM offset ~400hz
      //Vic Test rig ~390
      HZInShared[HZSamplesShared++] = HZ;
      HZ=10000; //changed from 8500
    }
  }
  
  //Below is only called during collect offset
  if(HZSamplesShared == BUFFERSIZE && calibrate == true){ //Changed single & to &&, so make sure it works
    for (int b=2; b<BUFFERSIZE; b++)//ignore first couple samples, for some reason get spikes
    {
      sum += HZInShared [b]; //Sum samples
    }   
    offsetcount++;
    HZSamplesShared=0;
      }
  if (offsetcount == collectionCount) calibrate = false; //stop calibration, size dependent on BUFFERSIZE

}
 
Hi, I'ts odd to me that the switch bounces at all as it has a schmitt trigger. So the noise on the photo diode must be quite high. How solid is your ground connection and can you put a cap of at least 100nF across the power supply pins of the switch? Maybe a more stable supply is enough so the schmitt can do its work.
 
Thanks, will try that tomorrow to see what happens.

Edit: So tried it, no different. Even with 30pf on the digital output it still gave false readings, but if I put 100nf it works.
The thing I don't get is why the void counterDeg is allowing small numbers, usually 2, but sometimes 1 or 3. I use that sensor to trigger the serial print information. I try to read it with the scope, but with no capacitors and only the scope, the signal always works. Something is fishy on that falling signal and even causing the software filter to fail.
 
Last edited:
So even though I fixed the issue with the capacitor, it was driving me crazy the code was not working as I thought it should. Well I figured it out. If there is no capacitor the interrupt triggers again, which I thought the software would filter out. But I guess since I calculate the degreetimeshared outside the if statement, it was being recalculated on the second bounce. Was an easy fix, I just copied the DegreeTimeShared to another variable within the if statement. I know not perfect, plus the capacitor fixes the issue. Was driving me crazy, so thought I would update in case anyone comes across this thread with a similar issue.
Code:
//Trigger on Falling, or leading edge of tooth
void counterDeg()// Degree Time
{   
  DegreeTimeShared = micros()-DegreeStart;  
  if (DegreeTimeShared >= 1000){ //to prevent false firing, 3000 = 278rpm
    DegreeStart=micros();
    //Check to make sure still ok with normal unit
    DT=DegreeTimeShared;//fixed incorrect DegreeTime when no capacitor in place    
    PositionShared++; 
    PositionDegShared+=5;
    bUpdateFlagsShared |= DEGREE_FLAG;    
 }
}
 
Status
Not open for further replies.
Back
Top