Skip to content
Rob Stave edited this page Feb 18, 2018 · 5 revisions

Timers and Clocks

I am not an expert on this stuff, so what you are going to find here are mostly notes and observations. :)

For the majority of these sketches, we are using the ATTINY with its internal clock running at 8Mhz. This is of course configurable, but considering that the ATTINY85 has only 8 pins (and two are reserved for +/gnd) we do not have a whole lot of options for configuring the clock. Really we have that reset pin and an external crystal would burn two more pins. We can always go slower, but who wants that?

The clock drives the CPU as well as the timers which are basically counters that are hanging off of the bus. You can configure these timers to trigger interrupts or drive PWM circuitry. You can even set up the timers to run off a PLL that is running at 8x the clock speed.

Many of your Intro to Arduino type sketches use things like delay() and millis(). Under the hood, they are using the timers. If you are doing something like flashing a light or beeping, you can get away a delay in the loop. Keep in mind that you can not do any other processing while you are doing the delay. In addition, any other code running in your loop will run outside of the delay. You might be able to do something like produce a 440hz tone, but if you try and read an analog value at the same time, you will need to cut your delay a bit.

Using Delays in Loop

A horrible example

A pronounced version might be a loop that reads a value and then uses that value to set a delay between 1 second and 10 seconds. Take this snippet.

void loop() {
    int val1 = analogRead(SLIDER1);  
    int sliderValue = map(val1, 0, 1020, 1000, 10000);
    digitalWrite(LED1, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(sliderValue);              // wait 
    val1 = analogRead(SLIDER1);  
    sliderValue = map(val1, 0, 1020, 1000, 10000);
    digitalWrite(LED1, LOW);    // turn the LED off by making the voltage LOW
    delay(sliderValue);              // wait 
 }

If you have the slider set for 1 second, you see the light flash 1 time per second. If you slide the slider to max, it will take one second to see the response, but then 10 seconds to see the next response. It has to wait 10 seconds just to get the next reading. It will feel REALLY sluggish.

A slightly better example

see https://github.com/robstave/arduino-analog-read-experiments/blob/master/Blink_2_lights_loop.ino

Rather than doing a long delay of 10 seconds, why not break it up into 100 delays of 100ms? You check in every 100ms and read the analog pin to get a counter value. If it decreases, then you can break the loop and set a new value. You end up with a much more responsive feel to what you are controlling. You do not even need to read the analog pin every time. It can be staggered as well.

Using Timers directly

Ok, so the above strategy has you checking values much faster, however, if you want a solid piece of code that outputs something like a 440 Hz without changing much, you are going to need to use the timers directly.

The strategy is to set up the timer to call an interrupt every x clock cycles. You do your processing in the interrupt handler. This runs at a much higher priority than the loop code, so it will be timely and consistent. (Assuming you keep your handler short and sweet).

Long interrupt handlers

You can end up with interrupt handlers that are too long. In these cases, we are dealing in the audio realm, so sometimes you can catch the bug with your ears. Otherwise, your best bet is to get out your calculator, and determine what you expect to see. Then use a frequency counter (many voltmeters have them) or your scope to verify that you are hitting that frequency.

  • Keep your interrupt handlers small.
  • if using variables, use the smallest you can. Byte vs int and so on.
  • Can you shift by 2 rather than divide by 4?
  • Avoid AnalogRead and DigitalWrite. Manipulate the Ports directly if you can.
  • Offset PWM to the Timers if you have room.
  • Do your math and use a scope or freq counter to verify.

Timer and Millis

The Delay api in the library actually does use one of the timers. (timer0) The easy way to figure it out is to just check for it. This will fail in the compile, but tell you what you need to know.

#if TIMER_TO_USE_FOR_MILLIS == 0
  #error TIMER_TO_USE_FOR_MILLIS is 0
#endif
#if TIMER_TO_USE_FOR_MILLIS == 1
  #error TIMER_TO_USE_FOR_MILLIS is 1
#endif

Timer prescaler values

These are the values that we use to configure the timer. If you are doing something like checking a temperature every hour, there is no need to have your clock going all that fast. You can also scale your timers down as well with the following prescalers. You will probably find much better references on the details around the web. Ultimately though, these are the tables you will keep on coming back too.

Timer 0

Clock 0 Select Bits

CS02 CS01 CS00 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clk I/O /(No prescaling)
0 1 0 clk I/O /8 (From prescaler)
0 1 1 clk I/O /64 (From prescaler)
1 0 0 clk I/O /256 (From prescaler)
1 0 1 clk I/O /1024 (From prescaler)
1 1 0 External clock source on T0 pin. Clock on falling edge.
1 1 1 External clock source on T0 pin. Clock on rising edge.

Timer 1

Clock 1 select bits

CS13 CS12 CS11 CS10 Asynchronous Clocking Mode Synchronous Clocking Mode
0 0 0 0 T/C1 stopped T/C1 stopped
0 0 0 1 PCK CK
0 0 1 0 PCK/2 CK/2
0 0 1 1 PCK/4 CK/4
0 1 0 0 PCK/8 CK/8
0 1 0 1 PCK/16 CK/16
0 1 1 0 PCK/32 CK/32
0 1 1 1 PCK/64 CK/64
1 0 0 0 PCK/128 CK/128
1 0 0 1 PCK/256 CK/256
1 0 1 0 PCK/512 CK/512
1 0 1 1 PCK/1024 CK/1024
1 1 0 0 PCK/2048 CK/2048
1 1 0 1 PCK/4096 CK/4096
1 1 1 0 PCK/8192 CK/8192
1 1 1 1 PCK/16384 CK/16384

Clone this wiki locally