RPM measurements with LV-168

Previously, I posted a simple project to measure RPM using a home-made optical shaft encoder and sensor with the original Orangutan. See

This has been very useful for analyzing the behavior of surplus motors, so I updated the code to work with the LV-168 and the supplied LCD routines. It should work with the SV-168 as well. I began with the example LV168Demo4, which has the necessary LCD routines and just replaced the main routine. This project happens to use printf for formatting the results, which is not necessary but is nevertheless instructive. Also, I still count transitions on the black&white code disk within a wait loop in the main program. This could easily be changed to use pin-change interrupts and the result would probably be more accurate. Otherwise, operation is identical to the version described in the previous post, and uses the same probe. I hope this is useful!

Jim

The LV-168 main routine code follows:

/*****************************************************************************
 * Orangutan LV-168 rpm meter: (derived from Pololu LV168Demo4)              *
 *****************************************************************************/


#include <avr/io.h>
#include <avr/interrupt.h>   // we need this if we want to use interrupts
#include "device.h"         // pinout definitions for the Orangutan LV-168
#include <util/delay.h>      // F_CPU is defined in "device.h" above
#include <stdio.h>
#include <stdlib.h>

#include "lcd.h"         // provides routines for using the LCD


// ***** GLOBAL VARIABLES *****

volatile unsigned long ticks;

int lcd_putchar(char c, FILE *stream) {
/*
 * Send character c to the LCD for use with printf
 */
   lcd_data(c);
   return 0;
}

FILE lcd_str = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE);

// delay for time_ms milliseconds by looping
void delay_ms( unsigned int time_ms )
{
   unsigned int i;

   for ( i = 0; i < time_ms; i++ )
      _delay_ms( 1 );      // _delay_ms() comes from <util/delay.h>
                     //  and can only delay for a max of around 13 ms
                     //  when the IO clock is 20 MHz.
}

/*
 * Timer0 overflow interrupt handler. Counts system clock/64 -- provides global ticks at 2.5 kHz
 */

ISR(TIMER0_OVF_vect)
{
   static unsigned int pcount = 2500;   //2500 ticks per second
   TCNT0=256-125;
   ticks++;       
   if (pcount-- == 0) {     //decrement tick counter and update runtime seconds
      pcount=2500;
      }
}

void timer_init(void) {

// set up Timer0 to provide 0.4 ms ticks

   TCCR0B  = (3<<CS00);  //clock/64
   TIMSK0 |= (1<<TOIE0); // enable int on overflow
   TCNT0=256-125;
}


int main()
{
   long int now,then;
   int t,period,rpm;

   lcd_init();               // this function must be called before any other LCD command
   stdout = &lcd_str;         // associate lcd stream with stdout

   lcd_string("RPM 0.2");   // display the string on the LCD
   lcd_gotoxy(0, 1);      // goto the start of LCD line 2
   lcd_string(" LV168 ");
   delay_ms(1500);         // loop-delay for 1.5 seconds

   lcd_clear();
   timer_init();
   ticks=0;

   sei();                  //enable timer interrupts

   //first pass synchronizes edges

   while( (PINC&(1<<PC5)) ==0);   //seeing white, wait till black
   while( (PINC&(1<<PC5)) !=0);   //seeing black, wait till white

   while(1){
   then=ticks;                  //now time one full revolution
   while( (PINC&(1<<PC5)) ==0);   //seeing white, wait till black
   while( (PINC&(1<<PC5)) !=0);   //seeing black, wait till white
   now=ticks;
   period=now-then;

   rpm=1500000L/(long) period;      //convert to rpm*10 (1 rev/second = 2500 ticks)
   t=rpm%10;                   //one decimal point (avoid loading floating point routines)
   rpm=rpm/10;                  //integer value
   now=period*10;
   period=now/25;                  //period in ms
   lcd_clear();
   printf("rpm %d.%d",rpm,t);      //print rpm string
   lcd_gotoxy(0,1);   
   printf("ms %d",period);      //print period in milliseconds

   } //end while(1)

//end of main
}