====== Counters/Timers ====== Counters, which in some sense can be called timers, are one of the most important sub-functions of a microcontroller. These enable to precisely time processes, generate signals and count events. A counter converts the number of input cycles to a binary value using an array of triggers. The maximum number of counted cycles depends on the length of this array, and this is marked by the length of the binary code. AVR has 8- and 16-bit counters. If a timer has reached its maximum value (255 in 8-bit and 65535 in 16-bit counters), the next cycle will generate an overflow and the counter resets back to 0. A counter's clock signal can come from the clock signal of the microcontroller, and in this case it is possible to decrease its value using a prescaler. Some AVRs have an internal independent clock signal generator, which can be modified to run faster using a frequency multiplier. Counters also differ in application cases and work modes. ===== Counter's Default Mode ===== In the default mode, a counter does nothing more than continually count sequential numbers. Its value can, of course, be read and changed from the program at any time. The only additional function in the default mode is to cause an interrupt on counter overflow. The default mode is typically used to execute a section of the program at certain intervals. Task: Make an 8 MHz ATmega128 fire an interrupt every 10 ms (frequency 100 Hz). For this task, the 8-bit counter 0 is suitable. #include ISR(TIMER0_OVF_vect) { // Give the counter such a value // that the next overflow occurs in 10 ms. // Formula: 256 - 8 MHz / 1024 / 100 Hz = 177,785 = ~178 TCNT0 = 178; } int main() { // To make the first overflow interrupt fire in 10 ms as well, // the counter needs to be initialized here. TCNT0 = 178; // Prescaler value 1024 TCCR0 = 0x07; // Allow overflow interrupts TIMSK |= (1 << TOIE0); // Allow interrupts globally sei(); // Endless loop while (1) continue; } The counter in this example will not generate the interrupt in exactly 10 ms, though, in order to do that it would require giving the counter a decimal value, and this is not possible. To achieve a precise interval between the interrupts, both the prescaler value and the initial value of the counter have to be chosen so that their division results in an exact number. This is not always possible, especially with 8-bit counters as their value range is quite small. To achieve a more precise or larger interval, a 16-bit counter can be used. ==== External Clock Counter ==== It is also possible to use an external clock source as a counter's clock signal. AVR has a pin called Tn for this purpose, n marking the number of the counter. External clock signal and the polarity can be selected using the prescaler register. ==== Timing Events ==== Since the counters allow timing operations, more complex AVR microcontrollers have an option to time specific events on a hardware level. This part of the counter is called an input capture unit. There is a choice between two events: the logical change in the value of a special input pin or in the value of the analog comparator result. If the selected event occurs, the counter's value is written to a special register, from where it can be read at any time. If the event is longer than the overflow time of the counter, the program has to count the overflows as well and take them into account when calculating the final result. Task: Measure the frequency of an external 122 Hz - 100 kHz logical square signal using an 8 MHz ATmega128. The measurement has to be at 1 Hz precision. The program uses a 16-bit counter with 1 input capture unit. #include unsigned long frequency; // Interrupt for the event ISR(TIMER1_CAPT_vect) { // Counter to 0 TCNT1 = 0; // The result is valid only if the counter // has not overflowed yet if (!(TIFR & (1 << TOV1))) { // Calculating the frequency from the period frequency = (unsigned long)8000000 / (unsigned long)ICR1; } else { // Frequency is less than 122 Hz frequency = 0; // Set the counter's overflow flag to 0 TIFR &= ~(1 << TOV1); } } int main() { // Register a rising front, prescaler value 1 TCCR1B = (1 << ICES1) | (1 << CS10); // Allow event interrupts TIMSK = (1 << TICIE1); // Allow interrupts globally sei(); // Endless loop while (1) continue; } The program fires an interrupt each time a rising front occurs in the external signal. During the interrupt, the counter is checked for overflows - this can happen if the frequency of the signal is below 122 Hz (8 MHz / 216) and in this case the value of the counter doesn't reflect a real period anymore. The frequency is calculated using 32-bit numbers to get the inverse of the period. The first thing is to set the counter to 0, because the timer works on the same clock signal as the processor and each instruction execution occurring after the external event shortens the measured period corrupting the result. The maximum measured frequency is limited by the time spent in the interrupt program. Catching events and registering the time it took for them to occur can also be resolved at the software level. It is possible to use external or other interrupts and read the value of the counter during these events. The hardware-level event catching is meant to run independently from the main program and time relatively short (or frequent) events. ===== Signal Generating ===== More complex counters can generate a signal, in addition to timing the length of one. For this purpose the counter has an output compare unit and a compare match output unit. The output compare unit has registers with the same bit-width as the counter and the values of these registers are compared to the value of the counter while it is running. An interrupt can be generated and special pins' values can be changed each time the counter's value is equal to the value in the compare unit register. At this moment a pin can either be set high, low or inversed. The signal is generated by changes in the value of the output pin. In some signal generating modes, the counter's maximum value can be altered. The counter's physical size will remain the same, but a comparison register is used to reset the counter at a specific count. The previous examples could also be solved by using this method, but the function is rather for changing the period of the signal. In addition to this, a counter can be configured to a mode where it works with both incrementing and decrementing. The counters and the signal generating modes using them are one of the most complex peripheral modules in an AVR. Writing about all of them here is beyond the scope of this text, and typically there is no need to know all aspects in order to use them. The following describes one of the most common PWM signals in robotics. The rest can be read from the AVR documentation. ==== Pulse Width Modulation ==== Pulse width modulation (PWM) is a type of signal, where the frequency and period (typically) are both constant, but the length of the half-periods changes. PWM signals are used for controlling electromechanical, optical and other devices. For example, the servo motors known from modeling use a PWM signal of 50 Hz and have a high half-period of 1 to 2 ms. Task: Using an 8MHz ATmega128, generate two speed regulating servo motor signals. Use pin PB5 (OC1A) to generate a pulse width of 1 ms and pin PB6 (OC1B) to generate pulse width of 2 ms. #include int main() { // Set pins as outputs DDRB |= (1 << PIN5) | (1 << PIN6); // Set outputs A and B low for comparison, // "Fast PWM" mode, prescaler value 8 TCCR1A = (1 << COM1A1) | (1 << COM1B1) | (1 << WGM11); TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // Maximum value of the counter. Formula: // TOP = 8 MHz / 8 / 50 Hz ICR1 = 20000; // Half-period of the first motor is 1 ms, and second 2 ms OCR1A = 1000; OCR1B = 2000; // Endless loop while (1) continue; }