@@ -41,71 +41,79 @@ static void syncTCC(Tcc* TCCx) {
41
41
}
42
42
#endif
43
43
44
- extern uint32_t toneMaxFrequency;
45
-
46
44
#if defined(__SAMD51__)
47
- #define PER_COUNTER 0xFF
45
+ #define MAX_PERIOD 0xFF
48
46
#else
49
- #define PER_COUNTER 0xFFFF
47
+ #define MAX_PERIOD 0xFFFF
50
48
#endif
51
49
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)
53
54
{
54
55
// if it's a rest, set to 1Hz (below audio range)
55
56
frequency = (frequency > 0 ? frequency : 1 );
56
57
//
57
58
// 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;
60
60
61
- ccValue = toneMaxFrequency / frequency - 1 ;
62
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1 ;
61
+ period = F_CPU / frequency - 1 ;
62
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV1_Val ;
63
63
64
64
uint8_t i = 0 ;
65
65
66
- while (ccValue > PER_COUNTER )
66
+ while (period > MAX_PERIOD )
67
67
{
68
- ccValue = toneMaxFrequency / frequency / (2 << i) - 1 ;
69
- i++;
70
68
if (i == 4 || i == 6 || i == 8 ) // DIV32 DIV128 and DIV512 are not available
71
69
i++;
70
+ period = F_CPU / frequency / (2 << i) - 1 ;
71
+ i++;
72
72
}
73
73
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
+
74
82
switch (i - 1 )
75
83
{
76
84
case 0 :
77
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2 ;
85
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV2_Val ;
78
86
break ;
79
87
80
88
case 1 :
81
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4 ;
89
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV4_Val ;
82
90
break ;
83
91
84
92
case 2 :
85
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8 ;
93
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV8_Val ;
86
94
break ;
87
95
88
96
case 3 :
89
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16 ;
97
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV16_Val ;
90
98
break ;
91
99
92
100
case 5 :
93
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64 ;
101
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV64_Val ;
94
102
break ;
95
103
96
104
case 7 :
97
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256 ;
105
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV256_Val ;
98
106
break ;
99
107
100
108
case 9 :
101
- prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024 ;
109
+ prescalerConfigVal = TC_CTRLA_PRESCALER_DIV1024_Val ;
102
110
break ;
103
111
104
112
default :
105
113
break ;
106
114
}
107
115
108
- return prescalerConfigBits ;
116
+ return prescalerConfigVal ;
109
117
}
110
118
111
119
void 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)
116
124
#if defined(__SAMD51__)
117
125
if (attr & (PIN_ATTR_PWM_E | PIN_ATTR_PWM_F | PIN_ATTR_PWM_G))
118
126
{
119
- duty = mapResolution (duty, 10 , 8 ) ;
120
- uint32_t prescalerConfigBits ;
127
+ unsigned long prescalerConfigVal ;
128
+ uint32_t period ;
121
129
122
- prescalerConfigBits = calcPrescaler (frequency);
130
+ prescalerConfigVal = calcPrescaler (frequency, period);
131
+ duty = map (duty, 0 , (1 <<PWM_API_RESOLUTION), 0 , period);
123
132
124
133
uint32_t tcNum = GetTCNumber (pinDesc.ulPWMChannel );
125
134
uint8_t tcChannel = GetTCChannelNumber (pinDesc.ulPWMChannel );
@@ -149,7 +158,7 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
149
158
while (TCx->COUNT8 .SYNCBUSY .bit .ENABLE )
150
159
;
151
160
// 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) ;
153
162
TCx->COUNT8 .WAVE .reg = TC_WAVE_WAVEGEN_NPWM;
154
163
155
164
while (TCx->COUNT8 .SYNCBUSY .bit .CC0 )
@@ -158,8 +167,8 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
158
167
TCx->COUNT8 .CC [tcChannel].reg = (uint8_t )duty;
159
168
while (TCx->COUNT8 .SYNCBUSY .bit .CC0 )
160
169
;
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 ;
163
172
while (TCx->COUNT8 .SYNCBUSY .bit .PER )
164
173
;
165
174
// Enable TCx
@@ -181,7 +190,7 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
181
190
while (TCCx->SYNCBUSY .bit .ENABLE )
182
191
;
183
192
// Set prescaler
184
- TCCx->CTRLA .reg = prescalerConfigBits | TCC_CTRLA_PRESCSYNC_GCLK;
193
+ TCCx->CTRLA .reg = TC_CTRLA_PRESCALER (prescalerConfigVal) | TCC_CTRLA_PRESCSYNC_GCLK;
185
194
186
195
// Set TCx as normal PWM
187
196
TCCx->WAVE .reg = TCC_WAVE_WAVEGEN_NPWM;
@@ -194,8 +203,8 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
194
203
TCCx->CC [tcChannel].reg = (uint32_t )duty;
195
204
while (TCCx->SYNCBUSY .bit .CC0 || TCCx->SYNCBUSY .bit .CC1 )
196
205
;
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 ;
199
208
while (TCCx->SYNCBUSY .bit .PER )
200
209
;
201
210
// Enable TCCx
@@ -210,10 +219,10 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
210
219
211
220
if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM)
212
221
{
213
- duty = mapResolution (duty, 10 , 16 ) ;
214
- uint32_t prescalerConfigBits ;
222
+ uint32_t prescalerConfigVal ;
223
+ uint32_t period ;
215
224
216
- prescalerConfigBits = calcPrescaler (frequency);
225
+ prescalerConfigVal = calcPrescaler (frequency, period );
217
226
218
227
uint32_t tcNum = GetTCNumber (pinDesc.ulPWMChannel );
219
228
uint8_t tcChannel = GetTCChannelNumber (pinDesc.ulPWMChannel );
@@ -259,13 +268,14 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
259
268
// Set PORT
260
269
if (tcNum >= TCC_INST_NUM)
261
270
{
271
+ duty = mapResolution (duty, 10 , 16 );
262
272
// -- Configure TC
263
273
Tc *TCx = (Tc *)GetTC (pinDesc.ulPWMChannel );
264
274
// Disable TCx
265
275
TCx->COUNT16 .CTRLA .bit .ENABLE = 0 ;
266
276
syncTC_16 (TCx);
267
277
// 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) ;
269
279
syncTC_16 (TCx);
270
280
// Set the initial value
271
281
TCx->COUNT16 .CC [tcChannel].reg = (uint32_t )duty;
@@ -276,19 +286,23 @@ void pwm(uint32_t outputPin, uint32_t frequency, uint32_t duty)
276
286
}
277
287
else
278
288
{
289
+ duty = map (duty, 0 , (1 <<PWM_API_RESOLUTION), 0 , period);
279
290
// -- Configure TCC
280
291
Tcc *TCCx = (Tcc *)GetTC (pinDesc.ulPWMChannel );
281
292
// Disable TCCx
282
293
TCCx->CTRLA .bit .ENABLE = 0 ;
283
294
syncTCC (TCCx);
295
+ // Set prescaler
296
+ TCCx->CTRLA .bit .PRESCALER = prescalerConfigVal;
297
+ syncTCC (TCCx);
284
298
// Set TCCx as normal PWM
285
299
TCCx->WAVE .reg |= TCC_WAVE_WAVEGEN_NPWM;
286
300
syncTCC (TCCx);
287
301
// Set the initial value
288
302
TCCx->CC [tcChannel].reg = (uint32_t )duty;
289
303
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 ;
292
306
syncTCC (TCCx);
293
307
// Enable TCCx
294
308
TCCx->CTRLA .bit .ENABLE = 1 ;
0 commit comments