AVR Timer-based One Shot Explained

Last time, we made one-shot pulses using the AVR’s built in hardware timer module. Today we are going to dive deep into the datasheets to see how this technique is able to coax the normally free-running timer into generating a single pulse. Along the way, we will learn about the low level rules that govern the operation of the timer, and use a trick or two to get around those rules. Read on!…

A Simple Timer

The AVR Timer hardware has lots of modes. For this technique we will be using Fast PWM mode 7…

Capture

…which follows these fundamental waveform generation rules (excerpted from the data sheet)…

  1. The counter counts from BOTTOM to TOP then restarts from BOTTOM.
  2. The output is set when the counter equals MATCH.
  3. The output is cleared at BOTTOM.

That’s it. Seems straight forward, but with such simple rules how are we going to be able to find a loophole to drive our pulse train though?

The Way Things Are Meant To Be

In normal operation, these simple rules generate a nice repeating waveform like this…

path4227

Moving the MATCH closer to TOP gives us less “on” time in our repeating square wave, while moving MATCH closer to BOTTOM give us more “on” time.

The Out Of Bounds Match

What happens if we mix things up and set MATCH to be higher than TOP? It sounds like crazy talk, but these are all just numbers inside a chip and we can set the numbers to whatever the hell we want. The chip will continue to follow its rules no matter what numbers we shove in there.

With MATCH higher than TOP, the counter will follow its rule and reset back to BOTTOM every time it hits TOP, but it will never hit MATCH. It will also still follow its rule and clear the output each time it resets back to bottom, but the output is already clear so it just stays clear. Because it never makes it up to MATCH, the output will never get set. The output will stay low forever…

path4227

Upsetting The Pattern

Well, that is not very useful. But while this futile Timer-to-nowhere is impotently counting away, what if we throw a monkey wrench in and directly set the counter to a value that is higher than TOP but lower than MATCH? The counter will continue following the rules and dutifully keep counting up from where ever we set it. It will eventually hit MATCH, and when it does it will set the output just like it is supposed to according to the rules…

path4227

(The purple dot shows the moment we manually set the counter to a value between TOP and MATCH)

We are half way there! We have figured out how to at least make the beginning of a pulse, now we just have to come up with a good way to end it.

Counter To Nowhere?

It looks like the counter is just going to keep counting up and up forever and never hit MATCH or TOP or anything else except infinity. Luckily, the counter is just a register and registers are just composed of a bunch of bits – 8 bits in this case. The largest binary number you can hold in 8 bits is 11111111 (that’s 255 in decimal). The data sheet calls this number MAX, and if you increment MAX by 1, you end up at 00000000 (that’s zero). It is called overflow or rollover. Normally overflowing a register is bad, but in this case it is very nice. It takes us shoots-and-ladders-style right back down to BOTTOM, which is a good place for us to be. Remember that according to the rules, the output is cleared whenever we hit BOTTOM, so we get this…

path4227
(Again, the purple dot shows the moment we manually set the counter to a value between TOP and MATCH)

Look at that! We have a one shot pulse! Sweet!

Total Control

If you look carefully at moment where we hit MATCH and set the output, you’ll notice that by moving MATCH closer MAX, we also make the width of the pulse shorter…

path4227

Moving MATCH away from MAX similarly makes our pulse wider. So we can precisely control the width of our pulse by carefully picking the value we give MATCH.

The Big Picture

Our pulse generation strategy just boils down to…

  1. Set TOP lower than MATCH. Now the Timer is free-running, but constantly outputting a 0.
  2. Set MATCH to any value we want. Picking a MATCH that is close to MAX makes a shorter pulse, farther away makes a longer one. As long as we set MATCH higher than TOP, then the counter will never reach it on its own so we can modify MATCH any time we want and it will not effect the output.
  3. When are ready to actually fire the pulse, we jump in and manually assign a value to the counter that is greater than TOP and less than MATCH.

Once we assign our value to the counter, the trigger has been pulled and everything proceeds on autopilot and with perfect lock-step timing….

  1. The counter will dutifully count up until it hits MATCH, which will set the output- making the rising edge of our pulse.
  2. It will continue to count up until it hits MAX, at which time it will roll over back to zero (which is BOTTOM).
  3. Hitting BOTTOM will clear the output- making the falling edge of our complete pulse!
  4. After it rolls back to BOTTOM, it will get stuck back into an infinite and futile BOTTOM to TOP loop. The output will patiently stay low until we call upon our one-shot again.

Note that to trigger additional one-shots that have the same width as the last one, we need only assign a value between TOP and MATCH to the counter. This can be a very fast and simple operation – as short as a single cycle.

All or None, But Never In-between

One really important thing to notice here is that we can interrupt the above steps any place we want, and in no case will we cause an incorrect pulse to be generated. Try it. Nothing is ever output until the instant when we manually set the counter in step #3 and once we do we do set that counter, then the pulse is out of the gate and will keep running until it is done. Once it is done, it will stay done until we do something else. There is no moment where we started triggering a pulse and haven’t finished yet. It is at every moment either not started, started, or finished (which is really not started for the next pulse)- the pulse trigger is atomic. This is really, really cool and what makes this technique magical.

Technicalities

That is really all there is to it, but there are a few complications to consider…

  1. The lower we set TOP, the more room we have to move MATCH away from MAX and make wider pulses. If we move TOP all the way down to be equal to BOTTOM, then the counter will be stuck at zero all the time- it reaches TOP on every tick and gets reset back to BOTTOM on every tick. Since we can set MATCH to any value higher than TOP, this gives us almost a full scale (254 possible steps) in which to move our MATCH around in.
  2. The closer we set the counter to MATCH when we initiate the pulse firing sequence, the less time we will need to wait before the counter hits MATCH and actually starts outputting our pulse. It seems like if we started the counter at MATCH, then the pulse would start instantly. Alas, this doesn’t work because the Timer has a special rule that tells it to ignore any match that happens on the tick after we change MATCH1. This is not much of a problem, we can just set the counter to 1 less than MATCH and the output will get set on the next tick. We can be patient and wait the extra tick, especially since it is a very brief wait (62ns at 16MHz) and always the same. We do loose one more step, making our pulse width range now limited to 0-253 ticks.
  3. Any time we write a new value to MATCH, our change is buffered and does not actually take effect until the next time we reach BOTTOM2. This could be very inconvenient, except for the lucky fact that by setting TOP to BOTTOM (in complication #1) we actually make it so that we hit BOTTOM (and reload the buffered value into MATCH) on every tick while we are idle and waiting to start the next pulse. That was easy!
  4. Notice that the only way that the counter could be higher than MATCH is if we manually set it there, which means that if the counter is not 0 then we must have started a pulse. Since the counter gets set back to 0 when the pulse is finished, we can easily and atomically test to see if there is a pulse in progress simply by checking to see if the counter is greater than 0.

Code Key

Here are the actual register names for all these values we have been talking about…

Value Register name
counter TCNT2
TOP OCR2A
MATCH OCR2B

You should now be able to read the code and it should make perfect sense.

Take Home Message

While 20 years from now you might not care about the intricacies of the internal operations of the 8-bit timer modules on Atmel AVR processors, hopefully this journey has shown you that…

  1. No matter how complicated the high level description of some piece of computing machinery is, if you look deep enough you will find that its operation is governed by a finite list of simple rules, and…
  2. The device does not understand those rules, it blindly follows them and applies them to the inputs even if you give it inputs that might not make sense in the context of the high level description of what those rules are supposed to do, and…
  3. If you know and understand the rules, it is often possible to craft otherwise inappropriate inputs to cause the device to perform some useful operation that is was not designed for.

  1. From the datasheet: “All CPU write operations to the TCNT2 Register will block any compare match that occurs in the next timer clock cycle, even when the timer is stopped.” 
  2. From the datasheet: “The OCR2x Register is double buffered when using any of the Pulse Width Modulation (PWM) modes. The double buffering synchronizes the update of the OCR2x Compare Register to either top or bottom of the counting sequence.” 

16 comments

  1. ubi de feo (@ubidefeo)

    hi Josh
    I wish this existed when I had to figure out precise timing for my metronome :D
    fantastic resource, really.
    I’m gonna see if I can implement some of this knowledge in the future.
    thank you for sharing.
    :)

  2. Adrian Velcich

    Hi Josh, did you ever take a good look at using the 16 bit timer? I have transposed your code, but I’m struggling a bit to get it to work properly. If you have any thoughts, I’d appreciate them.

    Thanks!

    • bigjosh2

      I looked over the registers and did not see any obvious blocking problems, but I have not tried to actually write the code. One thing to keep in mind is that the access to the 16-bit timer registers is atomic on AVR though a temp register, so this makes things easier.

  3. dntruong

    You are able to set a LOW-HIGH pulse of any width with this trick.

    How would you apply this to ws2812 timings?

    … I see manually launching each bit as a pulse encoding the low of the previous pulse:
    ___- zero
    ___– one
    have the ws2812 code do a sleep loop for the whole cycle-1, clear interrupts, and check if the counter is not cleared, else abort transmission, then wait for end of cycle, set next bit, set interrupts?

    That way a long interrupt just glitches a refresh but does not corrupt the display?

    I have not played with avr timers yet, but I wonder if we can reverse the logic to set a HIGH-LOW pulse. Either reverse the pin level, or raise the BOTTOM value to non zero.

    This has the benefit of telling us on a read if we are in a range [1..2] when we can force the next bit out. You can set the LOW duration to max of ws2812 and with a cmp decide if the interrupt corrupted your display (past 2 counter == BOTTOM), yet allow you to preempt deterministically the current cycle to push the next bit…

    -__1_______2
    —_1________2

    • bigjosh2

      Yes! This is exactly why I started on this path. It turns out that it works, but it is only of limited usefulness. If the interrupt comes in the middle of a pixel, then that pixel will get reset with incomplete color info and change to a corrupted color. To avoid this, you need to disable interrupts for the full 24 bits of each pixel.

      1. Disable interrupts
      2. Send a full 24 bit pixel
      3. Check to see if there is a pending interrupt. Maybe test the Interrupt Flag bits, or go ahead and enable and disable ints and check to see how long it took
      4. If an INT is pending, then you about sending the rest of the string, enable interrupts, let the interrupt run, then start the string over from scratch.

      Note that the longer your string, and the more frequent your interrupts then the more chance that a string update will get aborted and so the more stale the far end of the string will be.

      • dntruong

        I think it’s worth trying and putting into a lib as an alternative. It also allows 4mhz drivers to work reliably. can the bottom value be controlled to simplify pulse control?
        This would give a good reason to fix the time interrupt code.

  4. Chris Hahn

    Hi Josh:
    This is an excellent article and I am interested in using it for a project I am working on, however, when I loaded this into my ATMEGA2560 board I was unable to get it to run. Is there an issue running this on the larger processors? Thanks for your help.

    • bigjosh2

      Should be no problem – there are plenty of timers (5!) available on that chip and they all seem to have the required functionality. Are you sure you are looking for the output on the pin corresponding to the timer you are using?

      • Chris Hahn

        Thanks for the quick reply Josh. I loaded the program exactly as written into my ATMEGA2560 board. It compiled correctly and gave no errors, however, I am not seeing the expected pulse signal on the specified pin, or any pin for that matter. Did I miss something?

  5. Chris Hahn

    Ok, I think I’ve got it figured out. I’ve transposed your original code to use a 16-bit timer (timer-3), and also to run on an ATMEGA 2560 platform. For those interested, here is the code:

    #include
    #include

    #define OSP_SET_WIDTH(cycles) (OCR3B = 0xffff-(cycles-1))

    // Setup the one-shot pulse generator and initialize with a pulse width that is (cycles) clock counts long

    void osp_setup(uint16_t cycles) {

    TCCR3B = 0; // Halt counter by setting clock select bits to 0 (No clock source).
    // This keeps anyhting from happeneing while we get set up

    TCNT3 = 0x0000; // Start counting at bottom.
    OCR3A = 0; // Set TOP to 0. This effectively keeps us from counting becuase the counter just keeps reseting back to 0.
    // We break out of this by manually setting the TCNT higher than 0, in which case it will count all the way up to MAX
    // and then overflow back to 0 and get locked up again.
    OSP_SET_WIDTH(cycles); // This also makes new OCR values get loaded from the buffer on every clock cycle.

    TCCR3A = (1<<COM3B0) | (1<<COM3B1) | (1<<WGM30) | (1<<WGM31); // OC3B=Set on Match, clear on BOTTOM. Mode 15 Fast PWM.
    TCCR3B = (1<<WGM32) | (1<<WGM33) | (1<<CS30); // Start counting now. Mode 15 Fast PWM.

    DDRE = (1 <0)

    // Fire a one-shot pusle with the specififed width.
    // Order of operations in calculating m must avoid overflow of the unint8_t.
    // TCNT2 starts one count lower than the match value becuase the chip will block any compare on the cycle after setting a TCNT.

    #define OSP_SET_AND_FIRE(cycles) {uint16_t m=0xffff-(cycles-1); OCR3B=m; TCNT3 = m-1;}

    void setup()
    {
    osp_setup();

    }

    void loop()
    {
    // Step though 0-19 cycle long pulses for demo purposes

    for (uint16_t o = 0; o < 20; o++) {

    OSP_SET_AND_FIRE(o);

    while (OSP_INPROGRESS()); // This just shows how you would wait if necessary – not nessisary in this application.

    delay(1000); // Wait a sec to let the audience clap

    }

    }

  6. Nevell Greenough, N2GX

    After a struggle I’ve gotten your code modded by Chris Hahn to work on Timer 1B for long pulses when using the prescaler. Turns out that there must be at least one clock pulse output from the prescaler between programming the OCR1B register and the TCNT1 register. I’ve added a
    OSP_SET_AND_FIRE_LONG(cycles) macro, a bunch of prescaler values and associated time wasters using delayMicroseconds(). Please let me know how I can attach the sample code. Thanks, Nevell

  7. Nevell, , N2GX

    Here’s working code and program examples for Timer 1A and Timer 1B. It works on both ‘328P boards like the Uno and 2560 boards with one small comment change. The prescaler is supported to create pulses up to 4+ seconds length.

    I used Mode14 rather than 15 to free up OCR1A for its compare function. I also needed to waste some time between loading OCR1A and jamming TCNT1. The prescaler must output at least one pulse in order for the registers to get loaded properly.

    To use this variant, pick your prescaler value and un-comment the appropriate line around 49; then choose your board type- Uno -328P vs. Mga2560-type and un-comment the appropriate line around 55; finally pick and un-comment the appropriate “wait” time-waster for your chosen prescaler around line 85.

    In use, either call OSP_SET_AND_FIRE_LONG(cycles)
    or the use the 3-instruction sequence…


    OSP_SET_WIDTH(prescaler_cycles);
    wait; // Macro defined above to match chosen prescaler value
    OSP_FIRE();

    TimerShot code for Timer1A; good for pulses up to 4 seconds- I called it TimerShot1A.ino

    TimerShot code for Timer1B- I called it TimerShot1B.ino

    Many thanks, Josh for your insight into the AVR timers and thanks for letting me make a small contribution to your effort.

    • Anthony Peate

      Thank you this was an excellent bit of C code. Just what I needed to fix the gitter in my Arduino nano servo tester code. I was using the servo.h library but it didn’t generate an accurate Servo On pules time. The Oscilloscope showed the On pulse was randomly jumping by up to 20uS.

      This method of firing of the precision on pulse is is spot on. I also timed the servo off width using this bit of code below that intercepts the 1mS timer0 interupt:-

      //in the seup function:
      OCR0A = 0xAF;
      TIMSK0 |= _BV(OCIE0A);

      //1mS Timer 0 Interrupt
      SIGNAL(TIMER0_COMPA_vect)
      {
      //1mS Timer 0 Interrupt
      if( Servo1.OffCount>=ServoOffCycles )
      {
      //when servo off time >=1ms x ServoOffCycles fire the OnPulse on D9
      OSP_SET_WIDTH( Servo1.OnTime );
      TCNT1 = OCR1A - 1; //Fire Precision Servo On Pulse for Servo1.OnTime uS
      Servo1.OffCount=0;
      }
      Servo1.OffCount++;
      RE1.Check();
      }

      //I modified the OSP_SET_WITH to multiply the Servo.OnTime x 16 as it is in units of 1uS

      #define OSP_SET_WIDTH(ServoPW) (OCR1A = 0xffff-((ServoPW<<4)-1))

  8. Daniele

    Josh Hello and congratulations for your work.
    I used your instructions to drive a servomotor and I found them very useful.
    I need to have to also drive a vibration motor (DC 1,5-6V) simultaneously to the servomotor.
    If I try to use the analogWrite function to drive the vibration motor everything hangs.
    Is there a way to manage 2 output on 2 different pin to drive both the servomotor that the vibration motor.
    I thank you and I wish you good work

    • bigjosh2

      You should be able to drive the vibration motor from a PWM pin (“analog out” in Arduino speak) as long as the pin uses a different than the OneShot code (my example code uses Timer2). Take a look at PJRC’s Timer1 library. This should let you set up PWM out on Arduino pin 9 or 10 and use the Timer1.pwm(pin, duty) function to change the duty cycle (the “analog” out). I’d use an LED (with a current limiting resistor) instead of the motor until you get the kinks worked out just to keep things simpler. Keep in mind that (1) if your motor uses more than ~20mA then you will probably need a transistor rather than just connecting it directly to the pin, and (2) you probably also want a flywheel diode across the leads of the motor to prevent inductive kick.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s