// Elster AS300P Electricity Meter LED simulator
// according to the manual, the 880nm LED flashes for 5ms
// Our 5V Arduino Pro Mini runs at 16MHz
// this makes our undivided timers tick at 62.5ns
// we can divide this by 1024 using a prescaler - this makes a tick 64us
// we want a 5ms flash -> 5000us -> 78.125 ticks, we'll go with 78 :-)
// we use the overflow interupt, which will trigger at count = 65535 and a counter
// compare match interrupt which will trigger at 65535 - 78 = 65457 (0xffb1)
// the counter will overflow every 64us * 65536 = 4.2s
// so we'll get a 5ms flash every 4.2s, which is good for testing
// - - - - - - - - - - - - Not to scale - - - - - - - - - - - -
//
//
// 0xffff-------> | | | |
// /| /| /| /|
// / | / | / | / |
// / | / | / | / |
// 0xffb1---> /| | /| | /| | /| |
// / | | / | | / | | / | |
// / | | / | | / | | / | | /
// / | | / | | / | | / | | /
// / | | / | | / | | / | | /
// / | | / | | / | | / | | /
// / | |/ | |/ | |/ | |/
// 0-> --------------------------------------------------> Time
// | ^ ^ ^ ^
// These are the counter overflow interrupts; for
// | the 16-bit timer 1 this overflows at 0xffff
// For a 16MHz clock and maximum 1024 prescaler
// | this happens every ~4.2s. We turn off the LED
//
// ^ ^ ^ ^
// These are the compare A interrupts, we calculate
// a value such that it will trigger 5ms before the
// overflow (0xffb1 c.f. 0xffff). We turn on the LED
// interrupt service routine for compare mode, value A
ISR(TIMER1_COMPA_vect)
{
// Turn on the LED on pin 9 (aka PB1)
// this is quicker than digitalWrite
PORTB |= _BV(PB1);
}
// interrupt service routine for overflow (counter > 65535)
ISR(TIMER1_OVF_vect)
{
// Turn off the LED on pin 9 (aka PB1)
// this is quicker than digitalWrite
PORTB &= ~_BV(PB1);
}
void setup()
{
// LED connected to D9 (aka PB1)
pinMode(9, OUTPUT);
// turn LED off
PORTB &= ~_BV(PB1);
noInterrupts(); // disable all interrupts
// clear the Time/Counter control registers (A and B) for Timer 1
TCCR1A = 0;
TCCR1B = 0;
// set the clock pre-scaler for Timer 1 to 1024 (slowest)
// this sets bits CS12 and CS10 (see TCCR1B Timer/Counter1 Control Register in ATmega328P manual)
TCCR1B |= (1 << CS12)|(1 << CS10);
// set the Timer/Counter Interrupt Mask Register
// (see Timer/Counter1 Interrupt Mask Register in ATmega328P manual)
TIMSK1 |= (1 << TOIE1); // Set Timer/Counter1, Overflow Interrupt Enable
TIMSK1 |= (1 << OCIE1A); // Set Timer/Counter1, Output Compare A Match Interrupt Enable
// set the Compare A value to be 65457 (hex ffb1)
OCR1AH = 0xff;
OCR1AL = 0xb1;
interrupts(); // enable all interrupts
}
void loop() {
// nothing to see here!
}
|