Skip to content

Commit f221d1f

Browse files
committed
Fixed PWM frequency glitch
1 parent 156884b commit f221d1f

File tree

2 files changed

+88
-94
lines changed

2 files changed

+88
-94
lines changed

CHANGELOG

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ SAMD CORE
33
* Fixed Serial.flush() blocking before any writes. Thanks @hangmoh
44
* Added snprintf_P to avr/pgmspace.h stub. Thanks @jantje
55
* Another small fix to String iterators. Thanks @Ivan-Perez @Chris--A
6+
* Fixed glitch in PWM generation that may happen when calling analogWrite()
67

78
SAMD CORE 1.6.6 2016.05.19
89

cores/arduino/wiring_analog.c

+87-94
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ uint32_t analogRead(uint32_t pin)
189189
// to digital output.
190190
void analogWrite(uint32_t pin, uint32_t value)
191191
{
192-
uint32_t attr = g_APinDescription[pin].ulPinAttribute;
192+
PinDescription pinDesc = g_APinDescription[pin];
193+
uint32_t attr = pinDesc.ulPinAttribute;
193194

194195
if ((attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG)
195196
{
@@ -211,104 +212,96 @@ void analogWrite(uint32_t pin, uint32_t value)
211212

212213
if ((attr & PIN_ATTR_PWM) == PIN_ATTR_PWM)
213214
{
214-
if (attr & PIN_ATTR_TIMER) {
215-
#if !(ARDUINO_SAMD_VARIANT_COMPLIANCE >= 10603)
216-
// Compatibility for cores based on SAMD core <=1.6.2
217-
if (g_APinDescription[pin].ulPinType == PIO_TIMER_ALT) {
215+
value = mapResolution(value, _writeResolution, 8);
216+
217+
uint32_t tcNum = GetTCNumber(pinDesc.ulPWMChannel);
218+
uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel);
219+
static bool tcEnabled[TCC_INST_NUM+TC_INST_NUM];
220+
221+
if (!tcEnabled[tcNum]) {
222+
tcEnabled[tcNum] = true;
223+
224+
if (attr & PIN_ATTR_TIMER) {
225+
#if !(ARDUINO_SAMD_VARIANT_COMPLIANCE >= 10603)
226+
// Compatibility for cores based on SAMD core <=1.6.2
227+
if (pinDesc.ulPinType == PIO_TIMER_ALT) {
228+
pinPeripheral(pin, PIO_TIMER_ALT);
229+
} else
230+
#endif
231+
{
232+
pinPeripheral(pin, PIO_TIMER);
233+
}
234+
} else {
235+
// We suppose that attr has PIN_ATTR_TIMER_ALT bit set...
218236
pinPeripheral(pin, PIO_TIMER_ALT);
219-
} else
220-
#endif
221-
{
222-
pinPeripheral(pin, PIO_TIMER);
223237
}
224-
} else {
225-
// We suppose that attr has PIN_ATTR_TIMER_ALT bit set...
226-
pinPeripheral(pin, PIO_TIMER_ALT);
227-
}
228238

229-
Tc* TCx = NULL;
230-
Tcc* TCCx = NULL;
231-
uint8_t Channelx = GetTCChannelNumber(g_APinDescription[pin].ulPWMChannel);
232-
if (GetTCNumber(g_APinDescription[pin].ulPWMChannel) >= TCC_INST_NUM) {
233-
TCx = (Tc*) GetTC(g_APinDescription[pin].ulPWMChannel);
239+
uint16_t GCLK_CLKCTRL_IDs[] = {
240+
GCLK_CLKCTRL_ID(GCM_TCC0_TCC1), // TCC0
241+
GCLK_CLKCTRL_ID(GCM_TCC0_TCC1), // TCC1
242+
GCLK_CLKCTRL_ID(GCM_TCC2_TC3), // TCC2
243+
GCLK_CLKCTRL_ID(GCM_TCC2_TC3), // TC3
244+
GCLK_CLKCTRL_ID(GCM_TC4_TC5), // TC4
245+
GCLK_CLKCTRL_ID(GCM_TC4_TC5), // TC5
246+
GCLK_CLKCTRL_ID(GCM_TC6_TC7), // TC6
247+
GCLK_CLKCTRL_ID(GCM_TC6_TC7), // TC7
248+
};
249+
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_IDs[tcNum]);
250+
while (GCLK->STATUS.bit.SYNCBUSY == 1);
251+
252+
// Set PORT
253+
if (tcNum >= TCC_INST_NUM) {
254+
// -- Configure TC
255+
Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel);
256+
// Disable TCx
257+
TCx->COUNT8.CTRLA.bit.ENABLE = 0;
258+
syncTC_8(TCx);
259+
// Set Timer counter Mode to 8 bits, normal PWM
260+
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8 | TC_CTRLA_WAVEGEN_NPWM;
261+
syncTC_8(TCx);
262+
// Set the initial value
263+
TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value;
264+
syncTC_8(TCx);
265+
// Set PER to maximum counter value (resolution : 0xFF)
266+
TCx->COUNT8.PER.reg = 0xFF;
267+
syncTC_8(TCx);
268+
// Enable TCx
269+
TCx->COUNT8.CTRLA.bit.ENABLE = 1;
270+
syncTC_8(TCx);
271+
} else {
272+
// -- Configure TCC
273+
Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel);
274+
// Disable TCCx
275+
TCCx->CTRLA.bit.ENABLE = 0;
276+
syncTCC(TCCx);
277+
// Set TCx as normal PWM
278+
TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
279+
syncTCC(TCCx);
280+
// Set the initial value
281+
TCCx->CC[tcChannel].reg = (uint32_t) value;
282+
syncTCC(TCCx);
283+
// Set PER to maximum counter value (resolution : 0xFF)
284+
TCCx->PER.reg = 0xFF;
285+
syncTCC(TCCx);
286+
// Enable TCCx
287+
TCCx->CTRLA.bit.ENABLE = 1;
288+
syncTCC(TCCx);
289+
}
234290
} else {
235-
TCCx = (Tcc*) GetTC(g_APinDescription[pin].ulPWMChannel);
236-
}
237-
238-
// Enable clocks according to TCCx instance to use
239-
switch (GetTCNumber(g_APinDescription[pin].ulPWMChannel))
240-
{
241-
case 0: // TCC0
242-
case 1: // TCC1
243-
// Enable GCLK for TCC0 and TCC1 (timer counter input clock)
244-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC0_TCC1));
245-
break;
246-
247-
case 2: // TCC2
248-
case 3: // TC3
249-
// Enable GCLK for TCC2 and TC3 (timer counter input clock)
250-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TCC2_TC3));
251-
break;
252-
253-
case 4: // TC4
254-
case 5: // TC5
255-
// Enable GCLK for TC4 and TC5 (timer counter input clock)
256-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
257-
break;
258-
259-
case 6: // TC6 (not available on Zero)
260-
case 7: // TC7 (not available on Zero)
261-
// Enable GCLK for TC6 and TC7 (timer counter input clock)
262-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC6_TC7));
263-
break;
264-
}
265-
266-
while (GCLK->STATUS.bit.SYNCBUSY == 1);
267-
268-
value = mapResolution(value, _writeResolution, 8);
269-
270-
// Set PORT
271-
if (TCx)
272-
{
273-
// -- Configure TC
274-
275-
// Disable TCx
276-
TCx->COUNT8.CTRLA.reg &= ~TC_CTRLA_ENABLE;
277-
syncTC_8(TCx);
278-
// Set Timer counter Mode to 8 bits
279-
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_MODE_COUNT8;
280-
// Set TCx as normal PWM
281-
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_WAVEGEN_NPWM;
282-
// Set TCx in waveform mode Normal PWM
283-
TCx->COUNT8.CC[Channelx].reg = (uint8_t) value;
284-
syncTC_8(TCx);
285-
// Set PER to maximum counter value (resolution : 0xFF)
286-
TCx->COUNT8.PER.reg = 0xFF;
287-
syncTC_8(TCx);
288-
// Enable TCx
289-
TCx->COUNT8.CTRLA.reg |= TC_CTRLA_ENABLE;
290-
syncTC_8(TCx);
291-
}
292-
else
293-
{
294-
// -- Configure TCC
295-
// Disable TCCx
296-
TCCx->CTRLA.reg &= ~TCC_CTRLA_ENABLE;
297-
syncTCC(TCCx);
298-
// Set TCx as normal PWM
299-
TCCx->WAVE.reg |= TCC_WAVE_WAVEGEN_NPWM;
300-
syncTCC(TCCx);
301-
// Set TCx in waveform mode Normal PWM
302-
TCCx->CC[Channelx].reg = (uint32_t)value;
303-
syncTCC(TCCx);
304-
// Set PER to maximum counter value (resolution : 0xFF)
305-
TCCx->PER.reg = 0xFF;
306-
syncTCC(TCCx);
307-
// Enable TCCx
308-
TCCx->CTRLA.reg |= TCC_CTRLA_ENABLE;
309-
syncTCC(TCCx);
291+
if (tcNum >= TCC_INST_NUM) {
292+
Tc* TCx = (Tc*) GetTC(pinDesc.ulPWMChannel);
293+
TCx->COUNT8.CC[tcChannel].reg = (uint8_t) value;
294+
syncTC_8(TCx);
295+
} else {
296+
Tcc* TCCx = (Tcc*) GetTC(pinDesc.ulPWMChannel);
297+
TCCx->CTRLBSET.bit.LUPD = 1;
298+
syncTCC(TCCx);
299+
TCCx->CCB[tcChannel].reg = (uint32_t) value;
300+
syncTCC(TCCx);
301+
TCCx->CTRLBCLR.bit.LUPD = 1;
302+
syncTCC(TCCx);
303+
}
310304
}
311-
312305
return;
313306
}
314307

0 commit comments

Comments
 (0)