-
Notifications
You must be signed in to change notification settings - Fork 9
ATTINY85 Timers
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.
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.
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.
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).
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.
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
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.
| 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. |
| 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 |