I am trying to read from an encoder using interrupts.
For some reason it is only working in one direction, not the other direction. It never seems to negative decrement and for the life of me I cannot figure it out.
Would anyone mind taking a look at my code and seeing if you see something obvious?
The VNH5019MC is just a motor controller that causes it to move.
The way the MC works is a positive value between 1 and 400 goes in one direction, and -400 to -1 goes the opposite direction.
Either positive or negative values are working as my motor is going in the right direction, but my encoder counter is only going positive.
As an FYI I am resetting at 0 because this application will only have a 360 degree rotation limit when I get the code working properly.
Any help would be much appreciated.
#include "VNH5019MC.h"
VNH5019MC md;
boolean AisHigh,BisHigh;
#define encoderPinA 2
#define encoderPinB 3
short CPR=32; //counts per revolution base
short GR=131; //Gear Ratio
volatile int encoderCount = 0; //Will count my encoder ticks
short DD=2; //debounce delay
boolean rotating; //debouncing boolean
void setup() {
// digitalWrite(2, HIGH); // turn on pullup resistor
//digitalWrite(3, HIGH); // turn on pullup resistor
AisHigh=false; //set boolean for AisHigh = false as starting point
BisHigh=false; // "" "" for BisHigh
attachInterrupt(0,riseA,RISING);
attachInterrupt(0,fallA,FALLING);
attachInterrupt(1,riseB,RISING);
attachInterrupt(1,fallB,FALLING);
Serial.begin(9600);
md.init();
}
void loop() {
rotating=true; //reset debounce boolean
goToPos(500,-50); //make motor move CCW at neg 50
Serial.println(encoderCount);
}
void goToPos(int encPos,int speed) {
md.setM1Speed(speed);
}
void riseA() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
AisHigh=true; //found rise set true
iterate();
/*
if(BisHigh) {
encoderCount+=1;
}
else {
encoderCount-=1;
}
*/
rotating=false;
//Serial.print("RiseA ");
//Serial.println(AisHigh);
}
void fallA() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
AisHigh=false;
iterate();
/*
if(BisHigh) {
encoderCount-=1;
}
else {
encoderCount+=1;
}
*/
rotating=false;
//Serial.print("FallA ");
//Serial.println(AisHigh);
}
void riseB() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
BisHigh=true;
iterate();
/*
if(AisHigh) {
encoderCount+=1;
}
else {
encoderCount-=1;
}
*/
rotating=false;
//Serial.print("RiseB ");
//Serial.println(BisHigh);
}
void fallB() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
BisHigh=false;
iterate();
/*
if(AisHigh) {
encoderCount-=1;
}
else {
encoderCount+=1;
}
*/
rotating=false;
// Serial.print("FallB ");
//Serial.println(BisHigh);
}
void iterate() {
if(AisHigh == BisHigh) {
encoderCount+=1;
}
else {
encoderCount-=1;
}
if(encoderCount>=CPR*GR || encoderCount<=CPR*GR*-1) {
//implies we have gone one full rotation
//Reset the encoderCount
encoderCount=0;
}
// Serial.println("I");
}
I did not study your code - is your direction detector signed variable?
I am using Gray code and direction detection works ( as it should) when encoder A are B are attached to Int0 /int1.
No. Only one defined trigger action allowed per attachment declaration.
(I knew that, it was just a hint to the OP to check for themself)
Vaclav:
I did not study your code
We have enough problems just getting people to post code, so when they do, I think it is only courteous (there's that word again) to read it, don't you?
PaulS:
If you know which direction the motor is rotating, because you told it to rotate that way, do you really need to get direction from the encoder?
Some links to the motor controller and encoder, if it's a separate device, would be good.
Paul, the reason I want to know direction is I am making an application with a home built linear actuator that I am hoping will NOT be backdriveable without very high forces applied. So, my hope is that I can turn off my motors at times, and if a force is very high it will actually back drive my motors, rather than destroy my motors. In this case I want to be able to note exact positioning and the only way I can see doing that would be by reading the encoder pos either positive or negative. Hopefully I am explaining this properly, and perhaps I am overcomplicating this and do not need both directions, but I cannot see how at this point.
Regarding the MC and encoder, I am using this Pololu MC:
And this motor/encoder combo also from Pololu:
I have re-written the code as follows, and I am now getting directional position, but I am quite sure that there is a problem somewhere. This encoder is supposed to be 64 CPR, and with a 131:1 gear box, I should be seeing about 8384 counts per output shaft rotation, and I am seeing about 1000 or so.
#include "VNH5019MC.h"
VNH5019MC md;
boolean AisHigh,BisHigh;
#define encoderPinA 2
#define encoderPinB 3
short CPR=64; //counts per revolution base
short GR=131; //Gear Ratio
volatile int encoderCount = 0;
short DD=2; //debounce delay
boolean rotating; //debouncing boolean
void setup() {
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
digitalWrite(encoderPinA, HIGH); // turn on pullup resistor
digitalWrite(encoderPinA, HIGH); // turn on pullup resistor
AisHigh=false; //set AisHigh bool to false to set it to low as a start
BisHigh=false; //"" BisHigh ""
attachInterrupt(0,changeA,CHANGE);
attachInterrupt(1,changeB,CHANGE);
Serial.begin(9600);
md.init();
}
void loop() {
rotating=true; // reset debunker boolean
goToPos(500,50); //set speed and direction
Serial.println(encoderCount);
}
void goToPos(int encPos,int speed) {
md.setM1Speed(speed);
}
void changeA() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
//first toggle the AisHigh boolean
if(digitalRead(encoderPinA)==LOW) AisHigh=false;
else AisHigh=true;
iterate();
rotating=false;
}
void changeB() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
//first toggle the BisHigh boolean
if(digitalRead(encoderPinB)==LOW) BisHigh=false;
else BisHigh=true;
iterate();
rotating=false;
}
void iterate() {
if(AisHigh == BisHigh) {
encoderCount+=1;
}
else {
encoderCount-=1;
}
if(encoderCount>=CPR*GR || encoderCount<=CPR*GR*-1) {
//implies we have gone one full rotation
//Reset the encoderCount
encoderCount=0;
}
// Serial.println("I");
}
So, changeA() and changeB() are interrupt service routines.
void changeA() {
// debounce
if ( rotating ) delay (DD); // wait a little until the bouncing is done
You can't use delay() in an ISR. ISRs are supposed to be fast. delay() is the exact opposite of fast.
void iterate()
{
// Serial.println("I");
}
I know that Serial.print() is commented out, but Serial.print() relies on interrupts, which are disabled during an ISR. You should never call Serial.print() from an ISR. Not even for debugging purposes.
Interestingly enough I just added encoders to my project.
The counts per revolution are very high. They will generate an interrupt around once every 120 microseconds. You do not want to be, and should not need to debounce the signals. If your counts seem low it's because you are not servicing the interrupts fast enough.
It uses a timer interrupt to sample the encoder output but in your case you would use the pin interrupts. The code is also setup to service two encoders, one for each of the bot's two motors.
According to the C++ standard, variables in the global namespace, starting with an underscore, are reserved.
17.4.3.2.1 Global names
Certain sets of names and function signatures are always reserved to the implementation:
Each name that contains a double underscore (_ _) or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.