For the Royal Enfield diesel motorcycle project, I wanted a 120 kmh speedometer.
I found a KUS 120 kmh speedometer that needs a square wave signal with a frequency proportional to the displayed speed value.
The speedometer needs a 500 Hz signal at 30 kmh and at 60 kmh a 1000 Hz signal is and so on. I placed a Hall-effect sensor along with a small magnet at the motorcycle front wheel to measure wheel rotation speed.
The challenge is to add a piece of electronics to convert the front wheel speed signal to the signal required by the speedometer.
For this purpose a microcontroller is needed to do the calculations for the signal conversion. I used the Attiny85 with an external 8 MHz crystal to do the job and ended up with this circuit:
The Hall-effect sensor is supplied with 5 V DC at the SV1 connector, and Hall-effect signal is provided at SV1 connector pin 2. I used theAtmel Studio software development program to compile the AVR GCC C code and to flash the Attiny85 microcontroller. The flash device is the AVR-ISP MKII. The speedometer conversion/interface PCB looks like this:
I did a conversion table to cope with the lack of a 16 bit counter in the Attiny85, it helps to understand the C code In the table ;). Note: Rulleon=Rulleomkreds ~ Wheel Circumference.
The C code is listed here:
/* Speedometer pulse converter program using an ATTINY25.
Converts a pulse per
wheelrotation to a KUS speedometer signal that provides 1000 Hz
at 60 km/h.
3 Attiny25 interrupts are in use:
INT0 ISR starts measuring the next wheel rotation period
Timer0 ISR counts the wheel period using 0.25 msec increments
Timer1 ISR toggles the output pin at the required speedometer
freq.
Conversion table and calculations are provided in an spread
sheet !
Erik Henneberg – 2013.11.23
2014.04.27 – Increased wheel sensorpulses by 4 % ( 1 + 26 / 25 )
to achieve higher Timer1 counter/prescale values to lower the
output signal frequency to the speedometer by 4 %
2014.04.30 – adjusted wheel circumference to 2040 mm
*/ #include <avr/io.h>
#include <avr/interrupt.h>
#define WheelCircumference 2040 // wheel circumference in mm
#define MaxSpeed 120 // max. speed is 120 kmh
#define MinSpeed 4 // min. speed is 5 kmh
// define wheel period limits in 0.25 msec resolution, 4 KHz ticks
#define MinPeriod 244 // WheelCircumference *36 / 10 / MaxSpeed
/ 0,25 msec
#define MaxPeriod 7344 // WheelCircumference *36 / 10 /
MinSpeed / 0,25 msec
#define TACHO PORTB0 // tacho signal output attiny pin 5
#define INT0_PIN PORTB2 // wheel signal attiny pin 7
static unsigned int WheelTimer; // 4 KHz tick counter
int main(void)
{
WheelTimer = 0;
DDRB |= _BV(TACHO); // set the TACHO pin as an output
DDRB &= _BV(INT0_PIN); // set the INT0_PIN pin as an input
PORTB |= _BV(INT0_PIN); // pull up resistor on INT0_PIN input
GIMSK |= _BV(INT0); // Enable external interrupt INT0
MCUCR |= (1<<ISC01) | (1<<ISC00); // INT0 on rising edge
// timer0 controls the 4 khz interrupt to measure the wheel sensor
period
GTCCR |= _BV(TSM) | _BV(PSR0); // stop and clear
TCCR0A |= _BV(WGM01); // enable ctc
TCCR0B |= _BV(CS01); // clk/8
TIMSK |= _BV(OCIE0A); // enable output compare a interrupt
TCNT0 = 0;
OCR0A = 31; // 4 khz timer0 interrupt
GTCCR &= ~_BV(TSM);
TCCR1 = 0; // Stop Timer1, no output signal
sei(); // enable global interrupts
while(1) { }
} ISR(INT0_vect) {
// handles the external INT0 (
wheel sensor
) interrupt
unsigned int temp;
if ((WheelTimer < MaxPeriod) && (WheelTimer > MinPeriod))
{ TCCR1 =
0; // Stop Timer1
TCCR1 |= _BV(CTC1); // clear timer1 when it matches the
value in OCR1C
TIMSK |= _BV(OCIE1A); // enable interrupt when OCR1A
matches the timer value
if (WheelTimer < 985) { // 985 = 1024 / 1.04
temp = WheelTimer * 26; //.. to keep calculation within 1024
temp = temp / 25; // adds 4 % to wheel pulse timer count
temp = temp/4; // OCR = ( WheelPeriodTemp * 1.04 ) / 4
OCR1A = temp; // set the match value for interrupt
OCR1C = temp; // and the same match value to clear the
timer
TCCR1 |= _BV(CS10) | _BV(CS11); // set prescaler to divide by 4
and restart timer 1
} else {
// (
WheelTimer <
MaxPeriod and WheelTimer >
985)
temp = WheelTimer / 4; // divide by 4 ..
temp = temp * 26; // .. to keep calculation within an unsigned
int
temp = temp / 25; // adds 4 % to wheel pulse timer count
temp = temp/8; // OCR = ( (WheelPeriodTemp/4)*104% ) / 8
OCR1A = temp; // set the match value for interrupt
OCR1C = temp; // and the same match value to clear the
timer
TCCR1 |= _BV(CS11) | _BV(CS12); // set prescaler to divide by 32
and restart timer 1
}}
WheelTimer = 0; // reset WheelTimer
}
// 4 kHz timer0 interrupt running continously. Provides ticks to
measure wheel rotation time.
ISR(TIMER0_COMPA_vect) {
if (WheelTimer >= MaxPeriod) { // if Wheelrotation low or zero
TCCR1 = 0; // Stop Timer1
WheelTimer = MaxPeriod;
} else WheelTimer++;
}
// Outputs the signal to the speedometer. Toggles output pin.
ISR(TIMER1_COMPA_vect) { // handles the Timer1 Compare Match
A interrupt
PORTB ^= _BV(TACHO); // just toggle the TACHO output signal
}