## Settings for the Arduino Timers

The SAM3X8e processor has three timer counter modules and each of these has three channels giving nine timer counter channels in all. These channels can be independently programmed to carry out a wide range of different functions. For these experiments I will only be using timer channels to generate triggers at regular intervals for the ADC and the DACs. These notes will concentrate on the programming necessary to do this.

In the examples given so far one timer channel is used to trigger the ADC and DAC conversions. Once the ADC conversion is complete an interrupt is generated and the ADC interrupt handler reads the value and, if available, also writes an item of data to the DAC. While satisfactory for the initial tests in general it will be better to use different timer channels for triggering the ADC and the DACs as this will allow multi-rate processing to be used.

Both the ADC and the DACs need their triggers to be generated by timer counter module 0 (TC0). This module has three channels available and fields in the ADC and DAC mode registers specify which of these channels will be used as a trigger. I will use channel 0 to trigger the ADC and channel 1 to trigger the DACs. Each channel in the timer counter module has its own set of registers. There are some common registers, but these will not be used here.

I will now describe how I setup the channel registers; noting that after a system reset all of the registers are zero. Some registers are read-only (RO), some are write-only (WO) and others are read-write (RW).

Control Register (WO, see section 36.7.1). Only three bits of this register are used. CLKEN, CLKDIS and SWTRG. CLKEN, enables the clock if CLKDIS is not equal to 1. CLKDIS disables the clock. SWTRG resets the counter and the clock is started.

Mode Register (RW, see section 36.7.3). The WAVE bit in this register determines how the remaining bits are used. I will be using the WAVE mode, selected when the WAVE bit is set to 1. There are a number of different options in this mode, but I will just be using it to generate a series of regular pulses (on TIOA) and this is done by setting the WAVSEL field to 10 (see section 36.11.2 and figure 36-9). In this case the counter is incremented and when it reaches the value set in the Register C (RC) the counter is reset and we start again. The TIOA output is set when the counter reaches the RC value and cleared when the value set in a second register, Register A, (RA) is reached. Register A, is set to one half of the RC value, therefore generating a trigger with an equal mark space ratio. The clock frequency used to drive the counter is selected by the TCCLKS field, we will use an internal clock and the relevant options are:

$\displaystyle \begin{array}{rcl} \rm{TIMER\_CLOCK1}& = & \rm{MCK}/2 \\ \rm{TIMER\_CLOCK2}& = & \rm{MCK}/8 \\ \rm{TIMER\_CLOCK3}& = & \rm{MCK}/32 \\ \rm{TIMER\_CLOCK4}& = & \rm{MCK}/128 \\ \end{array}$

${\rm{MCK}=84}$MHz and so the corresponding clock frequencies are: 42,000, 10,500, 2,625 and 656.25 kHz. To get, for example, a sampling rate of 44.1kHz, we would need counts of: 952.38, 238.09, 59.52 and 14.88. Counts are integers, so taking the nearest integer values we would have effective sample rates of: 44.1176, 44.1176, 43.75 and 43.75 kHz. In this case we would take ${\rm{RC}=952}$ using the larger value for RC and we can then take ${\rm{RA}=476}$.

Other fields of interest in the mode register are:

• CLKI – when this bit is 0 the counter is clocked on the rising edge, when it is 1 the counter is clocked on the falling edge. I will use the risng edge, so set a value of 0.
• BURST – this field controls whether the clock is gated by an external signal. I do not want to use this facility and so this field’s value will be zero.
• CPCSTOP – this bit determines whether the clock is stopped when the counter reaches RC. I will not use this option, so the field’s value will be zero.
• CPCDIS – this bit determines whether the clock is disabled when the counter reaches RC. I will not use this option, so the value will be zero.
• EEVTEDG, EEVT and EEVETRG – these control the handling of external events and will not be used here. These fields will all be set to 0. Setting EEVT to 0 also sets TIOB as an input rather than an output, this is not a problem as the TIOB output is not required.
• ACPA – this field controls what happens to TIOA when the counter reaches RA. This field is used to clear TIOA ready for the next trigger.
• ACPC – this field controls what happens to TIOA when the counter reaches RC. This field is set to 1 and this has the effect of setting TIOA to 1 when the counter reaches the value set in RC.

The remaining fields control the effect of external events and control output to TIOB, these are not used and all fields set to zero

Stepper Motor Mode Register (RW, see section 36.7.4). This register is not used here.

Counter Value Register (RO, see section 36.7.5). This register contains the counter value. I will not be using this value directly.

Register A (RW, when WAVE=1, see section 36.7.6) and Register C(RW, when WAVE=1, see section 36.7.8). These are set as outlined above.

Register B (RW, when WAVE=1, see section 36.7.7). I will not be using this register.

Status Register (RO, see section 36.7.9). This register contains status information, but I will not be using it here.

Interrupt Enable Register (WO, see section 36.7.10), Interrupt Disable Register (WO, see section 36.7.11) and Interrupt Mask Register (RO, see section 36.7.12). These register control the generation of timer interrupts. I will not be using interrupts at the moment.

Other registers, which are common to all of the channels in a timer module, are not used here.

The revised code used to setup the TC0 timers is

double setup_TC0Timer(
uint32_t channelNumber,
uint32_t freq
)
{
if (channelNumber<0 || channelNumber>2)
return -1;
uint32_t rcCount = VARIANT_MCK / (2 * freq);
double actualFreq = double(VARIANT_MCK) / (2 * rcCount);

TcChannel * t = TC0->TC_CHANNEL+channelNumber;	// pointer to TC0 registers
// for the given channel
t->TC_CCR = TC_CCR_CLKDIS;  // disable internal clocking while setup regs
t->TC_IDR = 0xFF;			// disable interrupts
t->TC_SR;                   // read int status reg to clear pending
t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1 |   // use TCLK1 (prescale by 2)
TC_CMR_WAVE |			// waveform mode
TC_CMR_WAVSEL_UP_RC |	// count-up PWM using RC as threshold
TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET;
// set and clear flags for RA and RC compares
t->TC_RC = rcCount;	// counter resets on RC, so sets period in terms of master clock frequency/2
t->TC_RA = rcCount / 2;	// roughly square wave

return actualFreq;
}

void TC0ClockDisable(
uint32_t channelNumber
)
{
if (channelNumber<0 || channelNumber>2)
return;

TcChannel * t = TC0->TC_CHANNEL + channelNumber; // pointer to TC0 registers
// for the given channel
t->TC_CCR = TC_CCR_CLKDIS;  // disable internal clocking
}

void TC0ClockEnable(
uint32_t channelNumber
)
{
if (channelNumber < 0 || channelNumber>2)
return;

TcChannel * t = TC0->TC_CHANNEL + channelNumber;  // pointer to TC0 registers
// for the given channel
t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;  // re-enable local clocking and
// switch to hardware trigger source.
}

double timer_setup(uint32_t freq) // Frequency in Hz
{
pmc_enable_periph_clk (ID_TC0) ; // clock the TC0 channel 0

double actualFreq = setupTC0Timer(0, freq);

return actualFreq;
}