@@ -29,11 +29,13 @@ volatile uint32_t *portClearRegister;
2929volatile uint32_t portBitMask;
3030volatile int64_t toggleCount;
3131volatile 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+
3739void TC5_Handler (void ) __attribute__ ((weak, alias(" Tone_Handler" )));
3840
3941static inline void resetTC (Tc* TCx)
@@ -55,6 +57,21 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
5557
5658void 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
155153void noTone (uint32_t outputPin)
0 commit comments