Simple RPM calculation from Encoder Data?

Hello,

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!

Thanks again for your help!
Scott

The encoder I'm using has an index pulse

interrupts are typically used. but if there is a single output that pules once every revolution, why not just measure it?

the above process is completely missed

Post ALL the code that has the error.

Any delay, like printing, can lead to a failed "==" comparison.

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

Thanks again!
Scott

There is a index pulse, but it's "stuck" at the input of the LS7366R chip.

What does this mean? How have you programmed the index mode?

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.

 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");