Hello, I am currently trying to use these optical encoders for a feedback loop to control some DC motors. I am just trying to move each motor a certain amount in a specific order Ex. rotate 1st motor 100 steps, 2nd motor 200 steps, and 3rd motor 300 steps. The way I am doing this is by using 3 For Loops that will power each motor accordingly until the amount of steps has been reached by each encoder attached to their respective motors. So in my example, the first for loop will rotate the MotorA until the optical encoder attached has rotated 100 steps. The 2nd for loop will rotate the MotorB until it's optical encoder has rotated 200 steps etc... This is why I believe I should be able to use regular input pins rather than interrupt pins as I will have scheduled times to check for rotation of each encoder within the code (I will only be checking the 1st encoder rotating in the 1st for loop, the 2nd encoder in the 2nd loop, and the 3rd encoder in the 3rd). The problem I am encountering is I do not know how to properly program the loops to check for the pulses in the optical encoder.
I know this code works to check pulses in a standard rotary encoder:
#define CLK 3
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
pinMode(CLK,INPUT);
Serial.begin(9600);
lastStateCLK = digitalRead(CLK);
}
void loop() {
currentStateCLK = digitalRead(CLK);
//not checking for direction, just incrementing
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
counter++;
Serial.println(counter);
}
lastStateCLK = currentStateCLK;
delay(4); //debouncing
}
And I know this code works to sense rotation in an optical encoder using interrupt pins:
int temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
void setup() {
Serial.begin (9600);
pinMode(2, INPUT_PULLUP); // internal pullup input pin 2
pinMode(3, INPUT_PULLUP); // internal pullup input pin 3
//Setting up interrupt
attachInterrupt(0, ai0, RISING);
attachInterrupt(1, ai1, RISING);
}
void loop() {
Serial.println(digitalRead(3));
if( counter != temp ){
Serial.println (counter);
temp = counter;
}
}
void ai0() {
// ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
// Check pin 3 to determine the direction
if(digitalRead(3)==LOW) {
counter++;
}else{
counter--;
}
}
void ai1() {
// ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
// Check with pin 2 to determine the direction
if(digitalRead(2)==LOW) {
counter--;
}else{
counter++;
}
}
So my question is how can I sense optical encoders without using interrupt pins?
One of the motors is 12 rpm, the other 2 are 80 rpm
All 3 encoders have 600 pulses per revolution
The motors will be changing directions, although this isn't a concern because I will control which direction they are rotating in the for loops and subtract or add the step counts for each encoder accordingly
I tried the modified code without the delay, higher baud rate, and lastStateCLK in the conditional and the counter did not increment when the encoder was rotated. Are you sure this code is correct? If so my encoders may be damaged, if you were just spitballing and haven't tested this yourself please tell me so I know that I didn't accidentally break my encoders
Output
AB two -phase quadrature output rectangular pulse, the circuit output is NPN open collector output type ,the output of this type can and with additonal added pull-up resistor(not included) is directly connected MCU or PLC, such as 51 single or Mitsubishi PLC (PLC input mode should be connected to 0V switch function ).
...you need pullup resistors on the signals. Try:
pinMode(CLK,INPUT_PULLUP);
I've used other optical encoders, but at first I was assuming you were getting some signal out of them. I was re-ordering you code to speed it up a bit.
For quick feedback on manual reading encoders, I often use a line like:
Serial.print(".");
or:
Serial.print(digitalRead(Bpin)==HIGH? "+": "-");
I'd also often choose just one edge of the CLK pin to signal on
#define CLK 3
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
pinMode(CLK,INPUT_PULLUP);
Serial.begin(115200);
lastStateCLK = digitalRead(CLK);
}
void loop() {
currentStateCLK = digitalRead(CLK);
//not checking for direction, just incrementing
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
if(currentStateCLK == HIGH){ // rising edge
counter++;
Serial.print('.');
}
lastStateCLK = currentStateCLK;
}
// delay(4); //debouncing
}
With 600PPR, and and triggering on both the rising and falling edges, you'd get 1200 pulses/rev. At 80RPM, that's 80*1200/60=1600 count/sec, or 625us/pulse. That should leave plenty of time for checking state changes, as long as the rest of your code isn't blocking too much with for loops and delay(4)s.
So this is the current code that I am using to test:
#define CLK 6 // this is the pin that the white wire (phase A) of the encoder is connected to
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
//Rotates the motor connecting to the encoder
pinMode(38,OUTPUT);
pinMode(37,OUTPUT);
pinMode(36,OUTPUT);
digitalWrite(38,HIGH);
digitalWrite(36,LOW);
digitalWrite(37,HIGH);
pinMode(CLK,INPUT_PULLUP);
Serial.begin(115200);
lastStateCLK = digitalRead(CLK);
}
void loop() {
currentStateCLK = digitalRead(CLK);
//not checking for direction, just incrementing
if (currentStateCLK != lastStateCLK && currentStateCLK == 1){
counter++;
lastStateCLK = currentStateCLK;
Serial.println(counter);
}
}
After modifying the CLK pinMode to INPUT_PULLUP the serial monitor only printed, "⸮" right when the motor initially started to move, I guess that is progress from nothing being printed but it is still not able to count the steps rotated.
Could it be how I am powering my encoders? Currently, three of them have their VCCs and Gnds wired in parallel and are all connected to a 6V 5Ah battery that they are sharing with 7 MG 996R Servo motors. I know they are voltage-driven and should not draw more current than they need but I wonder if there are limits to that rule and if 5Ah may have been too much of an current supply?
Oops. I missed seeing the && currentStateCLK == 1 in your if condition. That would interface with setting the last condition within the loop, and is wrong. Sorry about that. Try this change to your code:
#define CLK 6 // this is the pin that the white wire (phase A) of the encoder is connected to
int counter = 0;
int currentStateCLK;
int lastStateCLK;
void setup() {
//Rotates the motor connecting to the encoder
pinMode(38,OUTPUT);
pinMode(37,OUTPUT);
pinMode(36,OUTPUT);
digitalWrite(38,HIGH);
digitalWrite(36,LOW);
digitalWrite(37,HIGH);
pinMode(CLK,INPUT_PULLUP);
Serial.begin(115200);
lastStateCLK = digitalRead(CLK);
}
void loop() {
currentStateCLK = digitalRead(CLK);
//not checking for direction, just incrementing
if (currentStateCLK != lastStateCLK ){
if(currentStateCLK == HIGH){
counter++;
Serial.println(counter);
}
lastStateCLK = currentStateCLK;
}
}
I'd think you'd get better output than garbage. Does your terminal program work with 115200 baud? You really need a faster baud to report count at every pulse--9600 baud can print about 960 chars per second, and with 3 digits and a "\r\n" eturn per rising edge, printing the count would take 5ms, almost long enough to miss counts at 12RPM.
Does this work?
void setup() {
Serial.begin(115200);
for(int ii =0 ; ii<100 ; ++ii){ // several repetitions to fill the Serial buffer
Serial.print("Hello world!");
}
}
void loop() {
}
Sorry for my late replies, I finally conceded and just decided to use interrupt pins (I am going to use an Arduino Mega) I appreciate all of your help, especially DaveX, and thank you for replying to my posts