Skip to content

Commit 5d10da2

Browse files
committed
Merge pull request #55 from arduino/toneFix
Tone fix for #59 and optimizations
2 parents 317f409 + 823cbce commit 5d10da2

File tree

1 file changed

+60
-62
lines changed

1 file changed

+60
-62
lines changed

Diff for: cores/arduino/Tone.cpp

+60-62
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ 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
#define TONE_TC TC5
3435
#define TONE_TC_IRQn TC5_IRQn
3536
#define TONE_TC_TOP 0xFFFF
3637
#define TONE_TC_CHANNEL 0
38+
3739
void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler")));
3840

3941
static inline void resetTC (Tc* TCx)
@@ -55,6 +57,21 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
5557

5658
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
5759
{
60+
// Configure interrupt request
61+
NVIC_DisableIRQ(TONE_TC_IRQn);
62+
NVIC_ClearPendingIRQ(TONE_TC_IRQn);
63+
64+
if(!firstTimeRunning)
65+
{
66+
firstTimeRunning = true;
67+
68+
NVIC_SetPriority(TONE_TC_IRQn, 0);
69+
70+
// Enable GCLK for TC4 and TC5 (timer counter input clock)
71+
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
72+
while (GCLK->STATUS.bit.SYNCBUSY);
73+
}
74+
5875
if (toneIsActive && (outputPin != lastOutputPin))
5976
noTone(lastOutputPin);
6077

@@ -67,89 +84,70 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
6784

6885
ccValue = toneMaxFrequency / frequency - 1;
6986
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1;
70-
71-
if (ccValue > TONE_TC_TOP)
87+
88+
uint8_t i = 0;
89+
90+
while(ccValue > TONE_TC_TOP)
91+
{
92+
ccValue = toneMaxFrequency / frequency / (2<<i) - 1;
93+
i++;
94+
if(i == 4 || i == 6 || i == 8) //DIV32 DIV128 and DIV512 are not available
95+
i++;
96+
}
97+
98+
switch(i-1)
7299
{
73-
ccValue = toneMaxFrequency / frequency / 2 - 1;
74-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2;
75-
76-
if (ccValue > TONE_TC_TOP)
77-
{
78-
ccValue = toneMaxFrequency / frequency / 4 - 1;
79-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4;
80-
81-
if (ccValue > TONE_TC_TOP)
82-
{
83-
ccValue = toneMaxFrequency / frequency / 8 - 1;
84-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8;
85-
86-
if (ccValue > TONE_TC_TOP)
87-
{
88-
ccValue = toneMaxFrequency / frequency / 16 - 1;
89-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16;
90-
91-
if (ccValue > TONE_TC_TOP)
92-
{
93-
ccValue = toneMaxFrequency / frequency / 64 - 1;
94-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64;
95-
96-
if (ccValue > TONE_TC_TOP)
97-
{
98-
ccValue = toneMaxFrequency / frequency / 256 - 1;
99-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256;
100-
101-
if (ccValue > TONE_TC_TOP)
102-
{
103-
ccValue = toneMaxFrequency / frequency / 1024 - 1;
104-
prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024;
105-
}
106-
}
107-
}
108-
}
109-
}
110-
}
100+
case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break;
101+
102+
case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break;
103+
104+
case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break;
105+
106+
case 3: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break;
107+
108+
case 5: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break;
109+
110+
case 7: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break;
111+
112+
case 9: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break;
113+
114+
default: break;
111115
}
112116

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

115-
// Enable GCLK for TC4 and TC5 (timer counter input clock)
116-
GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5));
117-
while (GCLK->STATUS.bit.SYNCBUSY);
118-
119119
resetTC(TONE_TC);
120120

121-
// Set Timer counter Mode to 16 bits
122-
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16;
123-
124-
// Set TONE_TC mode as match frequency
125-
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ;
126-
127-
TONE_TC->COUNT16.CTRLA.reg |= prescalerConfigBits;
121+
uint16_t tmpReg = 0;
122+
tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits
123+
tmpReg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency
124+
tmpReg |= prescalerConfigBits;
125+
TONE_TC->COUNT16.CTRLA.reg |= tmpReg;
126+
WAIT_TC16_REGS_SYNC(TONE_TC)
128127

129128
TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue;
130129
WAIT_TC16_REGS_SYNC(TONE_TC)
131130

132-
// Configure interrupt request
133-
NVIC_DisableIRQ(TONE_TC_IRQn);
134-
NVIC_ClearPendingIRQ(TONE_TC_IRQn);
135-
NVIC_SetPriority(TONE_TC_IRQn, 0);
136-
NVIC_EnableIRQ(TONE_TC_IRQn);
137-
138131
portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg);
139132
portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg);
140133
portBitMask = (1ul << g_APinDescription[outputPin].ulPin);
141134

142135
// Enable the TONE_TC interrupt request
143136
TONE_TC->COUNT16.INTENSET.bit.MC0 = 1;
144-
145-
lastOutputPin = outputPin;
146-
digitalWrite(outputPin, LOW);
147-
pinMode(outputPin, OUTPUT);
148-
toneIsActive = true;
137+
138+
if (outputPin != lastOutputPin)
139+
{
140+
lastOutputPin = outputPin;
141+
digitalWrite(outputPin, LOW);
142+
pinMode(outputPin, OUTPUT);
143+
toneIsActive = true;
144+
}
149145

150146
// Enable TONE_TC
151147
TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
152148
WAIT_TC16_REGS_SYNC(TONE_TC)
149+
150+
NVIC_EnableIRQ(TONE_TC_IRQn);
153151
}
154152

155153
void noTone (uint32_t outputPin)

0 commit comments

Comments
 (0)