I'm using a LS7366R encoder interface module from Robogaia which is working great for me to count encoder pulses reliably. The encoder I'm using has an index pulse which is also attached to the LS chip, and I have the chip setup to reset the count to zero every full rotation.
I'd like to count the RPM of the encoder. I've seen some examples that count the ticks between each individual encoder tick. It seems to me I should be able to count the time it takes for the encoder output to go from 0 to 0 again. Is this possible / easy / crazy? I'm trying to avoid running a wire directly from the encoder to another pin on the Arduino and running interrupts and a bunch of other code. It seems there should be a more elegant way!
Any help is appreciated. The links for the breakout board and the datasheet for the chip are in the first two lines of commented code below (which doesn't include any rpm code, just what I have currently to make the encoder work)
If it matters, the output in the serial window counts cleanly from 0 to 1440 before returning back to 0 again directly after.
Thank you!
//https://www.robogaia.com/3-axis-encoder-conter-arduino-shield.html
//https://www.robogaia.com/uploads/6/8/0/9/6809982/ls7366r.pdf Datasheet for LS7366R
// the sensor communicates using SPI, so include the library:
#include <SPI.h>
int chipSelectPin2 = 9;
//*****************************************************
void setup()
//*****************************************************
{
Serial.begin(9600);
pinMode(chipSelectPin2, OUTPUT);
digitalWrite(chipSelectPin2, HIGH);
LS7366_Init();
delay(100);
}
//*****************************************************
long getEncoderValue(int encoder)
//*****************************************************
{
unsigned int count1Value, count2Value, count3Value, count4Value;
long result;
selectEncoder(encoder);
SPI.transfer(0x60); // Request count
count1Value = SPI.transfer(0x00); // Read highest order byte
count2Value = SPI.transfer(0x00);
count3Value = SPI.transfer(0x00);
count4Value = SPI.transfer(0x00); // Read lowest order byte
deselectEncoder(encoder);
result = ((long)count1Value << 24) + ((long)count2Value << 16) + ((long)count3Value << 8) + (long)count4Value;
return result;
}//end func
//*************************************************
void selectEncoder(int encoder)
//*************************************************
{
switch (encoder)
{
case 2:
digitalWrite(chipSelectPin2, LOW);
break;
}//end switch
}//end func
//*************************************************
void deselectEncoder(int encoder)
//*************************************************
{
switch (encoder)
{
case 2:
digitalWrite(chipSelectPin2, HIGH);
break;
}//end switch
}//end func
// LS7366 Initialization and configuration
//*************************************************
void LS7366_Init(void)
//*************************************************
{
// SPI initialization
SPI.begin();
//SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI at 1Mhz (on 16Mhz clock)
delay(10);
digitalWrite(chipSelectPin2, LOW);
SPI.transfer(0x88);
SPI.transfer(0x23); // if I send 0x02 instead, x2 quadrature mode is enabled and gives me half the number of ticks per rev
digitalWrite(chipSelectPin2, HIGH);
}//end func
//*****************************************************
void loop()
//*****************************************************
{
long encoder2Value;
encoder2Value = getEncoderValue(2);
Serial.print(" Encoder = ");
Serial.print(encoder2Value);
Serial.print("\r\n");
}//end loop
at the start of each minute, capture the current encoder position
during the minute add 1440 for each complete rotation and
at the end of a minute, add the current encoder position, subtract the initial position and divide by 1440
or measure the time it takes the encoder to change one position (micro()) and divide that time into 60 sec
Thanks for your input. I’ve just added a simple test of:
If encoder2value == 0
newTime = millis()
serialPrintln newTime.
As I rotate the encoder past zero, the above process is completely missed leaving newTime at 0 unless I turn the encoder extremely slow, at which newTime will update to the current millis. If I look for a Encoder reading between 0 and 200 I can turn the encoder somewhat faster but that defeats the purpose.
Looks like I’ll need to go the interrupt on a pin route, unless anyone knows of a better way!
Right, printing takes time... perhaps my "test" was completely flawed. Regardless, below is the code as is right now. I've noticed that increasing serial print from 9600 to 115200 works somewhat better, but the delay of the printing may be causing trouble. I'll go through and turn it into RPM and try printing that.
There is a index pulse, but it's "stuck" at the input of the LS7366R chip. I can run a wire to an interrupt pin if need be, I'm just trying to not do that for some reason.
//Robogaia.com
//https://www.robogaia.com/3-axis-encoder-conter-arduino-shield.html
//https://www.robogaia.com/uploads/6/8/0/9/6809982/ls7366r.pdf Datasheet for LS7366R
// the sensor communicates using SPI, so include the library:
#include <SPI.h>
volatile float time = 0;
volatile float time_last = 0;
volatile int rpm = 0;
int chipSelectPin2 = 9;
//*****************************************************
void setup()
//*****************************************************
{
Serial.begin(115200);
pinMode(chipSelectPin2, OUTPUT);
digitalWrite(chipSelectPin2, HIGH);
LS7366_Init();
//delay(100);
}
//*****************************************************
long getEncoderValue(int encoder)
//*****************************************************
{
unsigned int count1Value, count2Value, count3Value, count4Value;
long result;
selectEncoder(encoder);
SPI.transfer(0x60); // Request count
count1Value = SPI.transfer(0x00); // Read highest order byte
count2Value = SPI.transfer(0x00);
count3Value = SPI.transfer(0x00);
count4Value = SPI.transfer(0x00); // Read lowest order byte
deselectEncoder(encoder);
result = ((long)count1Value << 24) + ((long)count2Value << 16) + ((long)count3Value << 8) + (long)count4Value;
return result;
}//end func
//*************************************************
void selectEncoder(int encoder)
//*************************************************
{
switch (encoder)
{
case 2:
digitalWrite(chipSelectPin2, LOW);
break;
}//end switch
}//end func
//*************************************************
void deselectEncoder(int encoder)
//*************************************************
{
switch (encoder)
{
case 2:
digitalWrite(chipSelectPin2, HIGH);
break;
}//end switch
}//end func
// LS7366 Initialization and configuration
//*************************************************
void LS7366_Init(void)
//*************************************************
{
// SPI initialization
SPI.begin();
//SPI.setClockDivider(SPI_CLOCK_DIV16); // SPI at 1Mhz (on 16Mhz clock)
//delay(10);
digitalWrite(chipSelectPin2, LOW);
SPI.transfer(0x88);
SPI.transfer(0x23); // if I send 0x02 instead, x2 quadrature mode is enabled and gives me half the number of ticks per rev
digitalWrite(chipSelectPin2, HIGH);
}//end func
/*void encoder_z_interrupt()
{
time = (micros() - time_last);
time_last = micros();
}
*/
//*****************************************************
void loop()
//*****************************************************
{
long encoder2Value;
encoder2Value = getEncoderValue(2);
Serial.print(" Encoder = ");
Serial.print(encoder2Value);
Serial.print(" Millis : ");
Serial.print(millis());
Serial.print(" Time_Last : ");
Serial.print(time_last);
Serial.print("\r\n");
if (encoder2Value == 0)
{
time = (millis() - time_last);
time_last = millis();
}
}//end loop
This is a problem. Always use unsigned long for variables associated with millis() and micros()
volatile float time = 0;
volatile float time_last = 0;
If you want to do calculations during the time between encoder pulses, or identify a particular encoder value, you simply cannot afford to do ANY of this pointless printing.