The Tachometer code is relatively simple. We'll explain each section in detail. But if you just want to get up and running, you can skip down to "The Complete Code" and continue from there.
Libraries and Variables:
We’ll start by including the necessary libraries, and defining some global variables. Note that some of these are defined as ‘volatile’. These variables are accessed both in the main loop and in the interrupt handler. The ‘volatile’ modifier instructs the compiler that these variables can change at any time and it should not attempt to optimize the code it generates for them
#include <Wire.h> #include "Adafruit_LEDBackpack.h" // External interrpt pin for sensor #define interruptPin 3 Adafruit_7segment matrix = Adafruit_7segment(); long lastUpdate = 0; // for timing display updates volatile long accumulator = 0; // sum of last 8 revolution times volatile unsigned long startTime = 0; // start of revolution in microseconds volatile unsigned int revCount = 0; // number of revolutions since last display update
Setup():
In the setup code, we initialize the display, attach the interrupt handler and set the input mode of the pin we are using for the sensor.
The pinMode() function sets the mode to Input, but also enables the internal pullup resistor. This assures that the sensor reading will be a logic HIGH until it detects a reflection from the target on the drive spindle.
The call to attachInterrupt() specifies the interrupt pin we are using, the address of the interrupt handler and whether we want to trigger on the rising or falling edge of the signal. Since we are just counting one pulse per revolution, we don't really care if it is triggered on the rising edge or falling edge.
//------------------------------------- // Setup - runs once at startup //------------------------------------- void setup() { // Initialize the serial port and display Serial.begin(115200); matrix.begin(0x70); // Enable the pullup resistor and attach the interrupt pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), tach_interrupt, FALLING); }
The Interrupt Handler:
The interrupt handler is invoked once per revolution as triggered by the sensor. It calculates the number of microseconds since the last interrupt and adds that to an ‘accumulator’. But before adding, we subtract 1/8 of the accumulator value. So when the speed stabilizes, the accumulator will converge on the sum of the times for the last 8 revolutions.
//------------------------------------- // Interrupt Handler // IR reflective sensor - target passed // Calculate revolution time //------------------------------------- void tach_interrupt() { // calculate the microseconds since the last interrupt long usNow = micros(); long elapsed = usNow - startTime; startTime = usNow; // reset the clock // Accumulate the last 8 interrupt intervals accumulator -= (accumulator >> 3); accumulator += elapsed; revCount++; }
The Loop:
Once per second, the loop code calculates the rpm based on the accumulator value and updates the display.
//------------------------------------- // Main Loop - runs repeatedly. // Calculate RPM and Update LCD Display //------------------------------------- void loop() { if (millis() - lastUpdate > 1000) // update every second { unsigned int rpm = 0; // divide number of microseconds in a minute, by the average interval. if (revCount > 0) { rpm = 60000000 / (accumulator>>3); } Serial.println(rpm); matrix.println(rpm); matrix.writeDisplay(); lastUpdate = millis(); revCount = 0; } }
//------------------------------------- // Pro Trinket Tachometer //------------------------------------- #include <Wire.h> #include "Adafruit_LEDBackpack.h" // External interrpt pin for sensor #define interruptPin 3 Adafruit_7segment matrix = Adafruit_7segment(); long lastUpdate = 0; // for timing display updates volatile long accumulator = 0; // sum of last 8 revolution times volatile unsigned long startTime = 0; // start of revolution in microseconds volatile unsigned int revCount = 0; // number of revolutions since last display update //------------------------------------- // Setup - runs once at startup //------------------------------------- void setup() { // Initialize the serial port and display Serial.begin(115200); matrix.begin(0x70); // Enable the pullup resistor and attach the interrupt pinMode(interruptPin, INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(interruptPin), tach_interrupt, FALLING); } //------------------------------------- // Main Loop - runs repeatedly. // Calculate RPM and Update LCD Display //------------------------------------- void loop() { if (millis() - lastUpdate > 1000) // update every second { unsigned int rpm = 0; // divide number of microseconds in a minute, by the average interval. if (revCount > 0) { rpm = 60000000 / (accumulator>>3); } Serial.println(rpm); matrix.println(rpm); matrix.writeDisplay(); lastUpdate = millis(); revCount = 0; } } //------------------------------------- // Interrupt Handler // IR reflective sensor - target passed // Calculate revolution time //------------------------------------- void tach_interrupt() { // calculate the microseconds since the last interrupt long usNow = micros(); long elapsed = usNow - startTime; startTime = usNow; // reset the clock // Accumulate the last 8 interrupt intervals accumulator -= (accumulator >> 3); accumulator += elapsed; revCount++; }
Text editor powered by tinymce.