Reputation: 11
I'm trying to create some PWM waves using a TC timer in Arduino Due. My problem is that I cannot generate accurate frequencies using this kind of timers.
Here's a simple code for what I'm trying to do :
static void setupTimer(uint32_t freq_desired)
{
uint32_t ul_div;
uint32_t ul_tcclks;
uint32_t ul_sysclk = sysclk_get_cpu_hz();
uint32_t counts;
// Configure PMC
pmc_enable_periph_clk(ID_TC);
// Configure TC for the given frequency and trigger on RC compare.
tc_find_mck_divisor(
(uint32_t)freq_desired, // The desired frequency as a uint32.
ul_sysclk, // Master clock freq in Hz.
&ul_div, // Pointer to register where divisor will be stored.
&ul_tcclks, // Pointer to reg where clock selection number is stored.
ul_sysclk); // Board clock freq in Hz.
tc_init(TC0, CHANNEL, ul_tcclks | TC_CMR_CPCTRG);
// Find the best estimate of counts, then write it to TC register C.
counts = (ul_sysclk/ul_div)/freq_desired;
tc_write_rc(TC0, 0, counts);
// Enable interrupts for this TC, and start the TC.
tc_enable_interrupt(TC0, CHANNEL0, TC_IER_CPCS); // Enable interrupt.
tc_start(TC0,CHANNEL0); // Start the TC.
NVIC_DisableIRQ(TC_IRQn);
NVIC_ClearPendingIRQ(TC_IRQn);
NVIC_SetPriority(TC_IRQn,configMAX_PRIORITIES);
NVIC_EnableIRQ(TC_IRQn);
}
Then on the Timer's Handle all what I do is triggering an output pin with :
ioport_toggle_pin_level(OUT_PIN);
The problem is that when I try for instance to generate a 1000Hz wave (giving 2000Hz for the timer of course), it works fine. But when I try, like 3427Hz, then it generate only 3420Hz or something like that.
Do you have please any idea how to fix that ? I tried to add round() for calculating the 'counts' variable value, it helped a bit, but still not extremely accurate.
Thanks in advance.
Upvotes: 0
Views: 1244
Reputation: 142
TC0 is 24 bit. all timer period registers on the due are 24 bit. if using pwm the counter period registers are 16 bit
Upvotes: 0
Reputation: 1
I'm guessing that TC0 is an 8-bit timer? You won't get good precision at "unusual" intervals with only 256 choices.
Try using a 16-bit timer. I've seen several "Due Timer"s on GitHub if you need them.
From my calculations:
Get an 8-bit divisor with a prescale by 256:
84MHz / 3427 / 256 = 95.75 divisor, round to 96
which generates
84MHz / 256 / 96 = 3,417.96 Hz
But with 16 bits, no prescale:
84MHz / 3427 = 24,511.23 divisor, truncate to 24511
which generates
84MHz / 24511 = 3,427.03 Hz
Since the calculated divisor of 24511 fits inside a 16-bit value, you've gotten a lot closer to your chosen rate.
And, of course, if TC0 is in fact a 16-bit timer, then there's something else wrong! ;-) Good luck!
Upvotes: 0