Possible to reset timer on ATMega328P other than with CTC Match?

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.
}

I may have found the answer to my own question. The web page http://www.avrbeginners.net/architecture/timers/timers.html mentions the proper procedure for reading and writing 16-bit values with an 8-bit microcontroller. The example is for direct access to the 16-bit counter register, the very one I'm using.

Looking in the ATmega328P data sheet, I found Section 15.11.4 that refers to direct access of TCNT1H and TCNT1L, the two 8-bit registers that comprise the 16-bit timer1. It would seem all I need to do is zero both of these whenever a pin transition happens and I will be able to correct any drift that might creep in.

I'm going to try it with my trivial flashing LED example and see if I can use a button to trigger a pin interrupt and reset the counter.

As a follow-up to answering my own question, I am posting the method I used to detect pin changes for anyone interested.

In the setup() loop, I added this to manipulate the pin change interrupt registers and enable detection on pin D7:

Serial.begin(9600);
pinMode(BUTTON, INPUT_PULLUP);

// Set up Pin Change Interrupt Control Registers to detect incoming transitions.
PCICR = B00000100;   // Enable only PCINT2 (PCINT23..16 or Arduino pins D0..D7).
PCMSK2 = B10000000;  // Enable only INT23 (Arduino D7 pin).

Serial.begin() was added only because I am writing out the current count value for debugging. Otherwise, it's not needed.

I have the BUTTON defined as pin 7 and a variable declared to hold a copy of the timer's count:

#define BUTTON 7

volatile unsigned int count;

And finally, another interrupt servicing routine:

// Interrupt handler for pin change is an alias for timer match.
ISR(PCINT2_vect)
{
  count = TCNT1L;
  count += TCNT1H * 256;
  Serial.print("Timer1 Count: ");
  Serial.println(count, HEX);
}

This stores a copy of the timer count in the variable I declared earlier on and then prints it to the serial debugger.

Now all I have to do is ground pin 7 by pressing a button and the current timer value is sent to the serial debugger. Actually, a whole lot of counts are sent, because of switch bounce.

It looks like this:
Timer1 Count: B09
Timer1 Count: FD2
Timer1 Count: FDD
Timer1 Count: 132D
Timer1 Count: 1338

All I have to do is change my new interrupt servicing routine to sync up the timer counter instead of sending it to serial debugging and I can accomplish my original task:

// Interrupt handler for pin change is an alias for timer match.
ISR(PCINT2_vect)
{
  TCNT1 = OCR1A >> 1;
}

It also occurred to me that I need to set the timer to 1/2 the max value to get it to read the incoming pin in the middle of the bit being transmitted. That's were the bit-shifting comes in. Shifting right once is the same as divide by two.

I've already posted a tutorial on the project hub for first part of my quest to get the timer interrupts functioning and I'm working on a part two for the pin change.

It's here, if you're interested: https://create.arduino.cc/projecthub/dhorton668

Well done for figuring all that out, but to be honest, that all sounds a bit like too much trouble. Easier to use an Arduino with an available hardware UART for such project. One with an atmega32u4 or samd21 chip for example.

Also, you should be able to set the timer for 104.125us, which is close enough that it shouldn’t be a problem.
(Even 104us shouldn’t be a problem, if you properly restart during the stop bit like you should. A standard uart can handle almost 5% error, usually allocated as 2% on each side.)

@PaulRB, I've got a couple of unflashed ATmega328s in my desk drawer, so that's why I'm going with the bit-banged uart instead of investing in something else. Plus, it's pretty much a quest at this point.

@westfw, I agree, it should be fine at 104-ish uSec. The sketch I've got running with the two LED pulses has been going all day and it's still sync'd. Next step is to time it for 9600 baud, do some level shifting and send a message to the raspberry pi.

Just a thought: is it possible to change the value of OCR1A on-the-fly? If so, you could have the timer generate an interrupt every 104 microseconds, and every now-and-then one (or more) with a higher value in OCR1A (which gives a slightly longer period). I think it's possible to get an average period of 104.166667 microseconds that way.

@Erik_Baas, I'm pretty sure that would work. I think I'm going to go with resetting the counter, though. It's occurred to me that since I need to read in the center of the incoming bit, I should reset my counter to half the max value whenever a pin transition interrupt occurs. Then, when it reaches maximum count and the timer1 interrupt fires, the incoming signal should be right in the middle and at its most stable.

I did a proof of concept flashing an LED via interrupt and syncing it to a second LED flashed with the old delay() method. I wanted to see if they would stay sync'd long term and they did. I wrote it up in a two part tutorial if anyone is interested. Part one is here: https://create.arduino.cc/projecthub/dhorton668/pardon-me-for-interrupting-acbd1a

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.