Intro

I will present here a simple approach to handle timers in micro-controllers: a function to program the registers that control the timer frequency. This is not finished work, but rather what could be the be basis for a more complex work.

The presented solution works on an architecture common to many manufacturers and brands: timers in micro-controllers are implemented by counters associated to compare registers, where the counter clock is generated from some internal clock that passes through a hardware divider (usually called “prescaler”; see figure below, from Microchip’s PIC32MX5XX/6XX/7XX datasheet). This is what happens in micro-controllers by Microchip and Atmel, among others. In these cases, timer programming requires computing the necessary values for the compare and prescaler registers. The code presented in this example performs this task automatically; the only action required to the programmer is to define the internal clock frequency and the desired End-Of-Count (EOC) event frequency.

image

The code

The module timer_hw is composed of two files: timer_hw.c and timer_hw.h. timer_hw.h contains the definition of a macro for the crystal frequency and the set_timer() function prototype. The case presented below corresponds to the code for a ATMEGA128 microcontroller and the AVRGCC8 compiler. Other microcontrollers and other programming chains will require adaptation.

int8_t set_timer(uint32_t frequency);

The set_timer() function makes the necessary register programming so that the timer generates an EOC event at the required frequency. The required values for the prescalers and the compare register value are computed automatically, given the value of frequency and the defined crystal frequency.

Internally, a constant array holds the prescaler divider values:

/* Possible values for counter prescaler */
const uint16_t prescalers[] = {0,1,8,32,64,128,256,1024};

The code then computes the overall divider value, from the internal clock frequency to the EOC event frequency. This value corresponds to the product of the prescaler value and the compare register value. This is done by a search on the prescaler values, until a feasible value is found. Note that we are with 8-bit registers, capable of holding a maximum value of 255.

/* find a prescaler value */
/* try to find a suitable value, starting from the */
/* first non-zero prescaler value */
while(prescalers[i]< divider/255)
{
  i++;

  if(i>=NUMBER_OF_PRESCALERS)
  {
    /* There is not a large enough prescaler value */
    /* Stop the counter */
    TCCR0 &= 0xF8;

    /* Signal error and leave */
    return -1;
  }
}
/* i holds the index to the selected prescaler value and
* it will be the value to write to CS02:0 in TCCR0 */

The required value for the compare register is easily found now (in the example, the compare register is OCR0)

/* Program Output Control Register */
OCR0 = divider / prescalers[i];

Example code

The program TimeMgt.c contains a simple example of a demo program using timer_hw.

Conclusion

The time_hw module provides a simple example of how to compute automatically the required values for programming the event generation frequency in microcontrollers. Although the presented example is targeted at Atmel’s ATMega128A, it is easily ported to other microcontrollers that share the same timer architecture.

Resources

timer_hw.c timer_hw.h TimeMgt.c

(To be continued…)