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 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)
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 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
}