@@ -29,6 +29,7 @@ volatile uint32_t *portClearRegister;
2929volatile uint32_t portBitMask;
3030volatile int64_t toggleCount;
3131volatile 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+
4446void TC5_Handler (void ) __attribute__ ((weak, alias(" Tone_Handler" )));
4547
4648static inline void resetTC (Tc* TCx)
@@ -62,6 +64,25 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
6264
6365void 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
167164void noTone (uint32_t outputPin)
0 commit comments