@@ -41,71 +41,79 @@ static void syncTCC(Tcc* TCCx) {
4141}
4242#endif
4343
44- extern uint32_t toneMaxFrequency;
45-
4644#if defined(__SAMD51__)
47- #define PER_COUNTER 0xFF
45+ #define MAX_PERIOD 0xFF
4846#else
49- #define PER_COUNTER 0xFFFF
47+ #define MAX_PERIOD 0xFFFF
5048#endif
5149
52- static inline uint32_t calcPrescaler (uint32_t frequency)
50+ // API uses 10 bit resolution
51+ #define PWM_API_RESOLUTION 10
52+
53+ static inline unsigned long calcPrescaler (uint32_t frequency, uint32_t &period)
5354{
5455 // if it's a rest, set to 1Hz (below audio range)
5556 frequency = (frequency > 0 ? frequency : 1 );
5657 //
5758 // Calculate best prescaler divider and comparator value for a 16 bit TC peripheral
58- uint32_t prescalerConfigBits;
59- uint32_t ccValue;
59+ unsigned long prescalerConfigVal;
6060
61- ccValue = toneMaxFrequency / frequency - 1 ;
62- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1 ;
61+ period = F_CPU / frequency - 1 ;
62+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV1_Val ;
6363
6464 uint8_t i = 0 ;
6565
66- while (ccValue > PER_COUNTER )
66+ while (period > MAX_PERIOD )
6767 {
68- ccValue = toneMaxFrequency / frequency / (2 << i) - 1 ;
69- i++;
7068 if (i == 4 || i == 6 || i == 8 ) // DIV32 DIV128 and DIV512 are not available
7169 i++;
70+ period = F_CPU / frequency / (2 << i) - 1 ;
71+ i++;
7272 }
7373
74+ #if defined(__SAMD51__)
75+ period = MAX_PERIOD;
76+ #else
77+ // Ensure that our period does not erode the API resolution
78+ if (period < (1 <<PWM_API_RESOLUTION))
79+ period = (1 <<PWM_API_RESOLUTION) - 1 ;
80+ #endif
81+
7482 switch (i - 1 )
7583 {
7684 case 0 :
77- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2 ;
85+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV2_Val ;
7886 break ;
7987
8088 case 1 :
81- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4 ;
89+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV4_Val ;
8290 break ;
8391
8492 case 2 :
85- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8 ;
93+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV8_Val ;
8694 break ;
8795
8896 case 3 :
89- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16 ;
97+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV16_Val ;
9098 break ;
9199
92100 case 5 :
93- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64 ;
101+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV64_Val ;
94102 break ;
95103
96104 case 7 :
97- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256 ;
105+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV256_Val ;
98106 break ;
99107
100108 case 9 :
101- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024 ;
109+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV1024_Val ;
102110 break ;
103111
104112 default :
105113 break ;
106114 }
107115
108- return prescalerConfigBits ;
116+ return prescalerConfigVal ;
109117}
110118
111119void pwm (uint32_t outputPin, uint32_t frequency, uint32_t duty)
@@ -116,10 +124,11 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
116124#if defined(__SAMD51__)
117125 if (attr & (PIN_ATTR_PWM_E | PIN_ATTR_PWM_F | PIN_ATTR_PWM_G))
118126 {
119- duty = mapResolution (duty, 10 , 8 ) ;
120- uint32_t prescalerConfigBits ;
127+ unsigned long prescalerConfigVal ;
128+ uint32_t period ;
121129
122- prescalerConfigBits = calcPrescaler (frequency);
130+ prescalerConfigVal = calcPrescaler (frequency, period);
131+ duty = map (duty, 0 , (1 <<PWM_API_RESOLUTION), 0 , period);
123132
124133 uint32_t tcNum = GetTCNumber (pinDesc.ulPWMChannel );
125134 uint8_t tcChannel = GetTCChannelNumber (pinDesc.ulPWMChannel );
@@ -149,7 +158,7 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
149158 while (TCx->COUNT8 .SYNCBUSY .bit .ENABLE )
150159 ;
151160 // Set Timer counter Mode to 8 bits, normal PWM,
152- TCx->COUNT8 .CTRLA .reg = TC_CTRLA_MODE_COUNT8 | prescalerConfigBits ;
161+ TCx->COUNT8 .CTRLA .reg = TC_CTRLA_MODE_COUNT8 | TC_CTRLA_PRESCALER (prescalerConfigVal) ;
153162 TCx->COUNT8 .WAVE .reg = TC_WAVE_WAVEGEN_NPWM;
154163
155164 while (TCx->COUNT8 .SYNCBUSY .bit .CC0 )
@@ -158,8 +167,8 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
158167 TCx->COUNT8 .CC [tcChannel].reg = (uint8_t )duty;
159168 while (TCx->COUNT8 .SYNCBUSY .bit .CC0 )
160169 ;
161- // Set PER to maximum counter value (resolution : 0xFF)
162- TCx->COUNT8 .PER .reg = 0xFF ;
170+ // Set PER to calculated period
171+ TCx->COUNT8 .PER .reg = period ;
163172 while (TCx->COUNT8 .SYNCBUSY .bit .PER )
164173 ;
165174 // Enable TCx
@@ -181,7 +190,7 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
181190 while (TCCx->SYNCBUSY .bit .ENABLE )
182191 ;
183192 // Set prescaler
184- TCCx->CTRLA .reg = prescalerConfigBits | TCC_CTRLA_PRESCSYNC_GCLK;
193+ TCCx->CTRLA .reg = TC_CTRLA_PRESCALER (prescalerConfigVal) | TCC_CTRLA_PRESCSYNC_GCLK;
185194
186195 // Set TCx as normal PWM
187196 TCCx->WAVE .reg = TCC_WAVE_WAVEGEN_NPWM;
@@ -194,8 +203,8 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
194203 TCCx->CC [tcChannel].reg = (uint32_t )duty;
195204 while (TCCx->SYNCBUSY .bit .CC0 || TCCx->SYNCBUSY .bit .CC1 )
196205 ;
197- // Set PER to maximum counter value (resolution : 0xFF)
198- TCCx->PER .reg = 0xFF ;
206+ // Set PER to calculated period
207+ TCCx->PER .reg = period ;
199208 while (TCCx->SYNCBUSY .bit .PER )
200209 ;
201210 // Enable TCCx
@@ -210,10 +219,10 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
210219
211220 if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM)
212221 {
213- duty = mapResolution (duty, 10 , 16 ) ;
214- uint32_t prescalerConfigBits ;
222+ uint32_t prescalerConfigVal ;
223+ uint32_t period ;
215224
216- prescalerConfigBits = calcPrescaler (frequency);
225+ prescalerConfigVal = calcPrescaler (frequency, period );
217226
218227 uint32_t tcNum = GetTCNumber (pinDesc.ulPWMChannel );
219228 uint8_t tcChannel = GetTCChannelNumber (pinDesc.ulPWMChannel );
@@ -259,13 +268,14 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
259268 // Set PORT
260269 if (tcNum >= TCC_INST_NUM)
261270 {
271+ duty = mapResolution (duty, 10 , 16 );
262272 // -- Configure TC
263273 Tc *TCx = (Tc *)GetTC (pinDesc.ulPWMChannel );
264274 // Disable TCx
265275 TCx->COUNT16 .CTRLA .bit .ENABLE = 0 ;
266276 syncTC_16 (TCx);
267277 // Set Timer counter Mode to 16 bits, normal PWM
268- TCx->COUNT16 .CTRLA .reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_NPWM | prescalerConfigBits ;
278+ TCx->COUNT16 .CTRLA .reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_WAVEGEN_NPWM | TC_CTRLA_PRESCALER (prescalerConfigVal) ;
269279 syncTC_16 (TCx);
270280 // Set the initial value
271281 TCx->COUNT16 .CC [tcChannel].reg = (uint32_t )duty;
@@ -276,19 +286,23 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
276286 }
277287 else
278288 {
289+ duty = map (duty, 0 , (1 <<PWM_API_RESOLUTION), 0 , period);
279290 // -- Configure TCC
280291 Tcc *TCCx = (Tcc *)GetTC (pinDesc.ulPWMChannel );
281292 // Disable TCCx
282293 TCCx->CTRLA .bit .ENABLE = 0 ;
283294 syncTCC (TCCx);
295+ // Set prescaler
296+ TCCx->CTRLA .bit .PRESCALER = prescalerConfigVal;
297+ syncTCC (TCCx);
284298 // Set TCCx as normal PWM
285299 TCCx->WAVE .reg |= TCC_WAVE_WAVEGEN_NPWM;
286300 syncTCC (TCCx);
287301 // Set the initial value
288302 TCCx->CC [tcChannel].reg = (uint32_t )duty;
289303 syncTCC (TCCx);
290- // Set PER to maximum counter value (resolution : 0xFFFF)
291- TCCx->PER .reg = 0xFFFF ;
304+ // Set PER to calculated period
305+ TCCx->PER .reg = period ;
292306 syncTCC (TCCx);
293307 // Enable TCCx
294308 TCCx->CTRLA .bit .ENABLE = 1 ;
0 commit comments