I am attempting to build an Tachometer for a lathe. I have found a sketch to read the RPM and another to display the number on a 4 digit 7 segment display. Both are from Nick's website. Using the sketch labeled "Another frequency counter" in a thread titled "Timers and counters" I can get a stable RPM reading. Using the sketch from "4-digit display made from minimal parts" which would be a great solution to the actual build, I get a no flicker display. I just don't have the brains to combine these to sketches. I have combined a simpler display sketch with Nick's counter but the way it displays the segments, the more on the dimmer it gets.
I just don't have the brains to combine these to sketches.
Or even post them?
Display
#include <SPI.h>
const byte LATCH = 10; // pin for latch (slave select)
const byte MAX_DIGITS = 4;
const byte TIME_BETWEEN_DIGITS = 8; // blanking period between digits in loop iterations
// which bit to turn on to make a particular pin of the LED go high
const unsigned int DIG1 = 0x0200; // Q1 of second IC
const unsigned int DIG2 = 0x1000; // Q4 of second IC
const unsigned int DIG3 = 0x2000; // Q5 of second IC
const unsigned int DIG4 = 0x0002; // Q1 of first IC
const unsigned int SEGA = 0x0400; // Q2 of second IC
const unsigned int SEGB = 0x4000; // Q6 of second IC
const unsigned int SEGC = 0x0008; // Q3 of first IC
const unsigned int SEGD = 0x0020; // Q5 of first IC
const unsigned int SEGE = 0x0040; // Q6 of first IC
const unsigned int SEGF = 0x0800; // Q3 of second IC
const unsigned int SEGG = 0x0004; // Q2 of first IC
const unsigned int SEGDP = 0x0010; // Q4 of first IC
// which segments to light for each digit
const unsigned int digitPatterns [15] = {
SEGA | SEGB | SEGC | SEGD | SEGE | SEGF, // 0
SEGB | SEGC, // 1
SEGA | SEGB | SEGD | SEGE | SEGG, // 2
SEGA | SEGB | SEGC | SEGD | SEGG, // 3
SEGB | SEGC | SEGF | SEGG, // 4
SEGA | SEGC | SEGD | SEGF | SEGG, // 5
SEGA | SEGC | SEGD | SEGE | SEGF | SEGG, // 6
SEGA | SEGB | SEGC, // 7
SEGA | SEGB | SEGC | SEGD | SEGE | SEGF | SEGG, // 8
SEGA | SEGB | SEGC | SEGD | SEGF | SEGG, // 9
0, // space
SEGG, // hyphen
SEGA | SEGB | SEGC| SEGE | SEGF | SEGG, // A
SEGA | SEGE | SEGF | SEGG, // F
SEGD | SEGE | SEGF, // L
};
// how long to leave the digit on for
const byte digitOnTimes [15] = { 3, 1, 3, 3, 2, 3, 3, 2, 3, 3, 0, 0, 3, 3, 2 };
// index into digits
const unsigned int digits [4] = { DIG1, DIG2, DIG3, DIG4 };
// 0 to 9 are themselves in the patterns array
// there are the other entries:
#define PAT_SPACE 10
#define PAT_HYPHEN 11
#define PAT_A 12
#define PAT_F 13
#define PAT_L 14
// what to display (0 to 15, plus 0x10 to light the decimal point)
// see above (eg. 1 displays 1)
volatile byte ledOutput [MAX_DIGITS];
// global variables used by the ISR
unsigned int countdown;
byte digit;
//******************************************************************
// Timer2 Interrupt Service is invoked by hardware Timer 2 every 1ms = 1000 Hz
// 16Mhz / 128 / 125 = 1000 Hz
ISR (TIMER2_COMPA_vect)
{
// keep doing what we were doing before?
if (countdown-- > 0)
return;
if (digit >= MAX_DIGITS)
{
digit = 0;
countdown = TIME_BETWEEN_DIGITS;
// blanking period to allow output chip and LEDs to cool down
digitalWrite (LATCH, LOW);
SPI.transfer (0);
SPI.transfer (0);
digitalWrite (LATCH, HIGH);
return;
} // end of done all digits
// make all digits high
unsigned int output = DIG1 | DIG2 | DIG3 | DIG4;
// bring low the wanted digit (this will sink)
output &= ~digits [digit];
// turn on wanted segments (this will source)
output |= digitPatterns [ledOutput [digit] & 0xF];
// add decimal place if required
if (ledOutput [digit] & 0x10)
output |= SEGDP;
// send to shift registers
digitalWrite (LATCH, LOW);
SPI.transfer (highByte (output));
SPI.transfer (lowByte (output));
digitalWrite (LATCH, HIGH);
// adjust time to allow for different numbers of segments
countdown = digitOnTimes [ledOutput [digit] & 0xF] ;
// onto next digit next time
digit++;
} // end of TIMER2_COMPA_vect
void setup ()
{
SPI.begin ();
// set up Timer 2
// Timer 2 - gives us our 1 mS counting interval
// 16 MHz clock (62.5 nS per tick) - prescaled by 128
// counter increments every 8 uS.
// So we count 125 of them, giving exactly 1000 uS (1 mS)
TCCR2A = 0; // stop timer 2
TCCR2B = 0;
TCCR2A = _BV (WGM21) ; // CTC mode
OCR2A = 124; // count up to 125 (zero relative!!!!)
TIMSK2 = _BV (OCIE2A); // enable Timer2 Interrupt (ie. every 1 mS)
TCNT2 = 0; // reset counter
TCCR2B = _BV (CS20) | _BV (CS22) ; // start Timer with a prescaler of 128
} // end of setup
void loop ()
{
unsigned long now = millis () / 100;
noInterrupts ();
for (int i = 0; i < 4; i++)
{
ledOutput [3 - i] = now % 10;
now /= 10;
}
// turn leading zeroes into spaces
for (int i = 0; i < 4; i++)
{
if (ledOutput [i] == 0)
ledOutput [i] = PAT_SPACE;
else
break;
}
// decimal point before 10ths of a second
ledOutput [2] |= 0x10;
interrupts ();
delay (10);
} // end of loop
Frequency counter
// Frequency timer
// Author: Nick Gammon
// Date: 10th February 2012
// Input: Pin D2
volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
// here on rising edge
void isr ()
{
unsigned int counter = TCNT1; // quickly save it
// wait until we noticed last one
if (triggered)
return;
if (first)
{
startTime = (overflowCount << 16) + counter;
first = false;
return;
}
finishTime = (overflowCount << 16) + counter;
triggered = true;
detachInterrupt(0);
} // end of isr
// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect)
{
overflowCount++;
} // end of TIMER1_OVF_vect
void prepareForInterrupts ()
{
// get ready for next time
EIFR = bit (INTF0); // clear flag for interrupt 0
first = true;
triggered = false; // re-arm for next time
attachInterrupt(0, isr, RISING);
} // end of prepareForInterrupts
void setup ()
{
Serial.begin(115200);
Serial.println("Frequency Counter");
// reset Timer 1
TCCR1A = 0;
TCCR1B = 0;
// Timer 1 - interrupt on overflow
TIMSK1 = bit (TOIE1); // enable Timer1 Interrupt
// zero it
TCNT1 = 0;
overflowCount = 0;
// start Timer 1
TCCR1B = bit (CS10); // no prescaling
// set up for interrupts
prepareForInterrupts ();
} // end of setup
void loop ()
{
if (!triggered)
return;
unsigned long elapsedTime = finishTime - startTime;
float freq = F_CPU / float (elapsedTime); // each tick is 62.5 ns at 16 MHz
Serial.print ("Took: ");
Serial.print (elapsedTime);
Serial.print (" counts. ");
Serial.print ("Frequency: ");
Serial.print (freq);
Serial.println (" Hz. ");
// so we can read it
delay (500);
prepareForInterrupts ();
} // end of loop
What do you want to see on the display?
The display code turns off interrupts while it displays data. That is going to cause you to miss interrupts from the RPM sensor. Probably not a good thing.
I took the "freq" variable (cycles per second) and multiplied it by 60 to get cycles per minute(RPM). That is what I want to display but the programs I have found that I can understand enough to change, all flicker and have uneven segment brightness due to the way they multiplex the segments. I currently have an ATTINY85 to send a PMW signal to a treadmill motor controller and another to run a "Start/Stop" system and will put the 4 digit 7 segment display in between the switches and rheostat.
