I've recently gotten into interrupt programming on the ATMega328P. I have a sketch where I set up a timer1 compare that counts up to a particular value and then resets to zero. This creates a nice periodic interrupt. No problems so far.
My end goal is to create an interrupt routine that checks an input pin every 104.166667 microseconds. (This is 9600baud if you're curious.) I can easily program an interrupt every 104 microseconds, but with the fractional part, the framing will eventually drift a full bit or worse.
But, I can fix it... almost... I know there will always be a transition between the high state of the stop bit and the low state of the start bit of the incoming data. (Possibly more with data bits transitioning, but the stop/start transition is assured.) If I can set a second interrupt to fire on this transition, it could respond by resetting the counter back to zero, correcting any drift that might have occurred.
My problem is this: I can't figure out how to reset the counter other than configuring it to do so on a count match.
Is there a way to asynchronously reset the timer1 count to zero?
Is there a way to trick the timer into resetting itself by updating its configuration somehow?
I couldn't find these answers in the ATmega328 data sheet and internet results are slim
Also, I am aware of the SoftwareSerial library, but I'm hoping to use a different approach that allows full-duplex communication. (SoftwareSerial can't transmit and receive at the same time.) And, it's sort of a quest at this point.
Any assistance on solving the asynchronous timer reset problem would be greatly appreciated.
Here's a trivial proof of concept sketch I wrote to blink an LED using a time1 interrupt. Obviously not at 9600 baud, but it's a foundation:
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
// Disable interrupts while things are being set up.
cli();
// Set up Timer Control Registers. See ATmega328P data sheet section 15.11 (Remember, bits are numbered from zero.)
TCCR1A = B00000000; // All bits zero for "normal" port operation.
TCCR1B = B00001100; // Bit 3 is Clear Timer on Compare match (CTC) and bit 2 specifies a divide by 256 prescaler.
TIMSK1 = B00000010; // Bit 1 to raise an interrupt on timer Output Compare Register A (OCR1A) match.
OCR1A = 31250; // Counter compare match value. 16MHz / prescaler * delay time (in seconds.)
// Reenable the interrupts
sei();
}
void loop() {
}
ISR(TIMER1_COMPA_vect)
{
// Access pin 13 directly performing an XOR on Port B (digital pins 8 - 13).
// Same as "digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN))"
PORTB ^= B00100000; // Toggle bit 5, which maps to pin13.
}