======================= Enabling the Atmel AT91EB55 Timer Counter Channels and Using Them in Waveform Mode ======================= -------------------- Written by: Josh Ibach-MacKeen Michael Ivey Group 9 Winter 2010 -------------------- Introduction ------------ This tutorial will explain how to setup a Timer Counter (TC) channel in waveform mode on the AT91EB55 evaluation board and how to configure the TC to generate interrupts. The AT91EB55 board has 6 identical 16 bit TCs (TC0 to TC5). These are documented in pages 148 to 172 of the M55800 reference manual. Each timer has two output channels (labeled A and B). The timer counters can be used in either CAPTURE mode (where they are used as input channels) or WAVE mode (where TC is used as a PWM waveform generator.) This tutorial covers only WAVE mode of operation and is not intended to be a comprehensive reference regarding the Timer Channels. It is meant only to elucidate basic waveform operation with and without interrupts. Note that the header/documentation files for the TC API are available in "periph/timer_counter" tc.h contains a list of the #defines that can be used to manipulate/set bits in the TC control registers lib_tc.c contains an annotated description of the API functions Timer Counter Basics -------------------- Each of the TCs has a 16 bit counter, a selectable clock source and three internal registers (RA, RB, and RC.) These registers can be read and set at will by using the at91_tc_read() and at91_tc_write() commands. (There is also a fourth register, "CV" meaning "counter value" which contains the current value of the TC's 16b counter register. This register is not writable via at91_tc_write.) In waveform mode, the three registers are used in compare operations (against the value of the TC) to control the TC channel outputs. For example, setting RA=266 and using the TC_ACPA_TOGGLE_OUTPUT flag will cause the output of the TC on channel A to toggle when CV==266. A "Trigger" event on the timer channel causes the counter to reset to 0 and begin counting. It is possible to trigger a TC through software (using at91_tc_trig_cmd()) as well as through an external hardware connection. Note that it is necessary to trigger a TC after initializing it in order to start it counting. A note on clock sources: It is possible to choose a variety of different clock sources which cause the TC to increment. Most of these are derived from the system clock speed (MCK/2, MCK/4, etc.) but it is possible to use an external clock as well. Note that the external clock period must be greater than the system clock period in order for the XCLK to be validated properly. (I.e. the external clock frequency cannot be greater than the system clock frequency.) Demonstration 1: Generating a Pulse Train ----------------------------------------- This demonstration uses a single timer counter channel to generate a uniform pulse train with a frequency of 60 kHz. Consider the following code snippet: /******************************************/ #include "parts/m55800/eb55.h" int main(void) { u_int tcreg[4]; u_int dummy; at91_tc_open(&TC1_DESC,TC_WAVE|TC_ACPC_TOGGLE_OUTPUT|TC_CLKS_MCK2|TC_CPCTRG,TRUE,FALSE); tcreg[RA]=0; tcreg[RB]=0; tcreg[RC]=266; at91_tc_write(&TC1_DESC,tcreg); at91_tc_trig_cmd(&TC1_DESC,TC_TRIG_CHANNEL); //Start the timer for(;;) { dummy++; //NOOP dummy--; } } /******************************************/ The flags in the tc_open() function are as follows: TC_WAVE : Waveform mode (vs. TC_CAPTURE) TC_CLKS_MCK2 : The TC will use the system clock divided by 2 TC_ACPC_TOGGLE_OUTPUT : When TC==RC, the output on Channel A will toggle TC_CPCTRG : When TC==RC, trigger (reset) the TC The final two arguments (TRUE and FALSE) enable output on Channel A and disable output on Channel B respectively. The at91_tc_write() command writes the values for RA, RB and RC (as specified in the variable tcreg) to the TC. at91_tc_trig_cmd() triggers the timer and starts it running. This command can be used to reset the timer channel at will. To verify the functionality, attach an oscilloscope probe to Pin 7 of the JPTimer header. The output should be a 60 kHz pulse train with P2P voltage of [0,3.3]. Demonstration 2: Generating an Interrupt on Register Compare ------------------------------------------------------------ This demonstration builds on the code of Demonstration 1 by causing an interrupt to be generated by the timer channel. This code requires the addition of an assembler file, included with this tutorial as myASM.arm. Consult the M55800 reference manual, pp. 90, 170 for details on the interrupt numbers and interrupt registers used in this example. Consider the following code snippet: /******************************************/ #include "parts/m55800/eb55.h" u_int myCounter; void intHandler(void) { myCounter++; } int main(void) { u_int tcreg[4]; u_int dummy; myCounter=0; EnableInterrupt(); at91_tc_open(&TC1_DESC,TC_WAVE|TC_ACPC_TOGGLE_OUTPUT|TC_CLKS_MCK1024|TC_CPCTRG,TRUE,FALSE); (&TC1_DESC)->tc_base->TC_IER|=(1<<4); //Generate an interrupt on RC_Compare at91_irq_open(7,6,AIC_SRCTYPE_INT_LEVEL_SENSITIVE,&irqasmhandler); //Enable the TC1 interrupt (Interrupt #7) //with priority 6 tcreg[RA]=0; tcreg[RB]=0; tcreg[RC]=31250; at91_tc_write(&TC1_DESC,tcreg); at91_tc_trig_cmd(&TC1_DESC,TC_TRIG_CHANNEL); //Start the timer for(;;) { printf("myCounter=%d\n",myCounter); } } /******************************************/ This code will generate an interrupt which calles the intHandler function every second. (MCK=32000000 Hz; MCK/1024 = 31250 Hz, and an interrupt is generated every 31250 ticks of the MCK/1024.) Note that the EnableInterrupt() function (defined in myASM.arm) is used to enable interrupts if they have been disabled (for example, by the Slingshot USB/JTAG debug probe.)