-
Notifications
You must be signed in to change notification settings - Fork 217
Description
While experimenting with ATSAMD21G18A board I've noticed that TC and TCC period is not what I'd expect, even though the frequency I wanted to obtain is lower than expected, even though the theoretical period is an integer number of cycles.
I believe the problem is here:
atsamd/hal/src/peripherals/pwm/d11.rs
Lines 60 to 82 in 2d890f0
| pub fn set_period(&mut self, period: Hertz) | |
| { | |
| let params = TimerParams::new(period, self.clock_freq); | |
| let count = self.tc.count16(); | |
| count.ctrla().modify(|_, w| w.enable().clear_bit()); | |
| while count.status().read().syncbusy().bit_is_set() {} | |
| count.ctrla().modify(|_, w| { | |
| match params.divider { | |
| 1 => w.prescaler().div1(), | |
| 2 => w.prescaler().div2(), | |
| 4 => w.prescaler().div4(), | |
| 8 => w.prescaler().div8(), | |
| 16 => w.prescaler().div16(), | |
| 64 => w.prescaler().div64(), | |
| 256 => w.prescaler().div256(), | |
| 1024 => w.prescaler().div1024(), | |
| _ => unreachable!(), | |
| } | |
| }); | |
| count.ctrla().modify(|_, w| w.enable().set_bit()); | |
| while count.status().read().syncbusy().bit_is_set() {} | |
| count.cc(0).write(|w| unsafe { w.cc().bits(params.cycles as u16) }); | |
| } |
TimerParams::new()called in line 62 correctly calculates the number of ticks by dividing the frequencies- code in line 81 applies the calculated period:
count.cc(0).write(|w| unsafe { w.cc().bits(params.cycles as u16) });
However, looking at the "SAM D21 Family Data Sheet" section 30.6.1, definition of TOP:
The counter reaches TOP when it becomes equal to the highest value in
the count sequence. The TOP value can be the same as Period (PER)
or the Compare Channel 0 (CC0) register value depending on the
waveform generator mode in Waveform Output Operations.
My understanding is that the timer counts from 0 to CC0 value (inclusive), so in fact it goes through n + 1 distinct values. To generate 1/n frequency, the value loaded to CC0 should be equal to n - 1, and not n.
Changing the line 81 (and similar code in line 49) to
count.cc(0).write(|w| unsafe { w.cc().bits(params.cycles as u16 - 1) });solves the problem for me (verified with a frequency counter).
The code for TCC updating PER register suffers from the same issue.