Skip to content

Commit 23dbf11

Browse files
agdlmattairtech
authored andcommitted
Tone fix for arduino#59 and optimizations
This fixes issue arduino#59 and optimize the way the prescaler is found. Furthermore non needed instructions are not repeated.
1 parent 304b76e commit 23dbf11

File tree

1 file changed

+64
-67
lines changed

1 file changed

+64
-67
lines changed

Diff for: cores/arduino/Tone.cpp

+64-67
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ volatile uint32_t *portClearRegister;
2929
volatile uint32_t portBitMask;
3030
volatile int64_t toggleCount;
3131
volatile bool toneIsActive = false;
32+
volatile bool firstTimeRunning = false;
3233

3334
/* TC5 does not exist on the D11. Using TC2 instead. It will conflict with the 2 TC2 PWM pins */
3435
#if defined(__SAMD11D14AM__) || defined(__SAMD11C14A__) || defined(__SAMD11D14AS__)
@@ -41,6 +42,7 @@ volatile bool toneIsActive = false;
4142

4243
#define TONE_TC_TOP 0xFFFF
4344
#define TONE_TC_CHANNEL 0
45+
4446
void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler")));
4547

4648
static inline void resetTC (Tc* TCx)
@@ -62,6 +64,25 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
6264

6365
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
6466
{
67+
// Configure interrupt request
68+
NVIC_DisableIRQ(TONE_TC_IRQn);
69+
NVIC_ClearPendingIRQ(TONE_TC_IRQn);
70+
71+
if(!firstTimeRunning)
72+
{
73+
firstTimeRunning = true;
74+
75+
NVIC_SetPriority(TONE_TC_IRQn, 0);
76+
77+
// Enable GCLK for timer used
78+
#if defined(__SAMD11D14AM__) || defined(__SAMD11C14A__) || defined(__SAMD11D14AS__)
79+
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC1_TC2));
80+
#else
81+
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
82+
#endif
83+
while (GCLK->STATUS.bit.SYNCBUSY);
84+
}
85+
6586
if (toneIsActive && (outputPin != lastOutputPin))
6687
noTone(lastOutputPin);
6788

@@ -74,94 +95,70 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
7495

7596
ccValue = toneMaxFrequency / frequency - 1;
7697
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
77-
78-
if (ccValue > TONE_TC_TOP)
98+
99+
uint8_t i = 0;
100+
101+
while(ccValue > TONE_TC_TOP)
102+
{
103+
ccValue = toneMaxFrequency / frequency / (2<<i) - 1;
104+
i++;
105+
if(i == 4 || i == 6 || i == 8) //DIV32 DIV128 and DIV512 are not available
106+
i++;
107+
}
108+
109+
switch(i-1)
79110
{
80-
ccValue = toneMaxFrequency / frequency / 2 - 1;
81-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2;
82-
83-
if (ccValue > TONE_TC_TOP)
84-
{
85-
ccValue = toneMaxFrequency / frequency / 4 - 1;
86-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4;
87-
88-
if (ccValue > TONE_TC_TOP)
89-
{
90-
ccValue = toneMaxFrequency / frequency / 8 - 1;
91-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8;
92-
93-
if (ccValue > TONE_TC_TOP)
94-
{
95-
ccValue = toneMaxFrequency / frequency / 16 - 1;
96-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16;
97-
98-
if (ccValue > TONE_TC_TOP)
99-
{
100-
ccValue = toneMaxFrequency / frequency / 64 - 1;
101-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64;
102-
103-
if (ccValue > TONE_TC_TOP)
104-
{
105-
ccValue = toneMaxFrequency / frequency / 256 - 1;
106-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256;
107-
108-
if (ccValue > TONE_TC_TOP)
109-
{
110-
ccValue = toneMaxFrequency / frequency / 1024 - 1;
111-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024;
112-
}
113-
}
114-
}
115-
}
116-
}
117-
}
111+
case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break;
112+
113+
case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break;
114+
115+
case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break;
116+
117+
case 3: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break;
118+
119+
case 5: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break;
120+
121+
case 7: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break;
122+
123+
case 9: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break;
124+
125+
default: break;
118126
}
119127

120128
toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1);
121129

122-
// Enable GCLK for timer used
123-
#if defined(__SAMD11D14AM__) || defined(__SAMD11C14A__) || defined(__SAMD11D14AS__)
124-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC1_TC2));
125-
#else
126-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
127-
#endif
128-
129-
while (GCLK->STATUS.bit.SYNCBUSY);
130-
131130
resetTC(TONE_TC);
132131

133-
// Set Timer counter Mode to 16 bits
134-
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
135-
136-
// Set TONE_TC mode as match frequency
137-
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
138-
139-
TONE_TC->COUNT16.CTRLA.reg |= prescalerConfigBits;
132+
uint16_t tmpReg = 0;
133+
tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
134+
tmpReg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency
135+
tmpReg |= prescalerConfigBits;
136+
TONE_TC->COUNT16.CTRLA.reg |= tmpReg;
137+
WAIT_TC16_REGS_SYNC(TONE_TC)
140138

141139
TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
142140
WAIT_TC16_REGS_SYNC(TONE_TC)
143141

144-
// Configure interrupt request
145-
NVIC_DisableIRQ(TONE_TC_IRQn);
146-
NVIC_ClearPendingIRQ(TONE_TC_IRQn);
147-
NVIC_SetPriority(TONE_TC_IRQn, 0);
148-
NVIC_EnableIRQ(TONE_TC_IRQn);
149-
150142
portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg);
151143
portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg);
152144
portBitMask = (1ul << g_APinDescription[outputPin].ulPin);
153145

154146
// Enable the TONE_TC interrupt request
155147
TONE_TC->COUNT16.INTENSET.bit.MC0 = 1;
156-
157-
lastOutputPin = outputPin;
158-
digitalWrite(outputPin, LOW);
159-
pinMode(outputPin, OUTPUT);
160-
toneIsActive = true;
148+
149+
if (outputPin != lastOutputPin)
150+
{
151+
lastOutputPin = outputPin;
152+
digitalWrite(outputPin, LOW);
153+
pinMode(outputPin, OUTPUT);
154+
toneIsActive = true;
155+
}
161156

162157
// Enable TONE_TC
163158
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
164159
WAIT_TC16_REGS_SYNC(TONE_TC)
160+
161+
NVIC_EnableIRQ(TONE_TC_IRQn);
165162
}
166163

167164
void noTone (uint32_t outputPin)

0 commit comments

Comments
 (0)