@@ -29,11 +29,13 @@ volatile uint32_t *portClearRegister;
29
29
volatile uint32_t portBitMask;
30
30
volatile int64_t toggleCount;
31
31
volatile bool toneIsActive = false ;
32
+ volatile bool firstTimeRunning = false ;
32
33
33
34
#define TONE_TC TC5
34
35
#define TONE_TC_IRQn TC5_IRQn
35
36
#define TONE_TC_TOP 0xFFFF
36
37
#define TONE_TC_CHANNEL 0
38
+
37
39
void TC5_Handler (void ) __attribute__ ((weak, alias(" Tone_Handler" )));
38
40
39
41
static inline void resetTC (Tc* TCx)
@@ -55,6 +57,21 @@ void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency)
55
57
56
58
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
57
59
{
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
+
58
75
if (toneIsActive && (outputPin != lastOutputPin))
59
76
noTone (lastOutputPin);
60
77
@@ -67,89 +84,70 @@ void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration)
67
84
68
85
ccValue = toneMaxFrequency / frequency - 1 ;
69
86
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 )
72
99
{
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 ;
111
115
}
112
116
113
117
toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1 );
114
118
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
-
119
119
resetTC (TONE_TC);
120
120
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)
128
127
129
128
TONE_TC->COUNT16 .CC [TONE_TC_CHANNEL].reg = (uint16_t ) ccValue;
130
129
WAIT_TC16_REGS_SYNC (TONE_TC)
131
130
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
-
138
131
portToggleRegister = &(PORT->Group [g_APinDescription[outputPin].ulPort ].OUTTGL .reg );
139
132
portClearRegister = &(PORT->Group [g_APinDescription[outputPin].ulPort ].OUTCLR .reg );
140
133
portBitMask = (1ul << g_APinDescription[outputPin].ulPin );
141
134
142
135
// Enable the TONE_TC interrupt request
143
136
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
+ }
149
145
150
146
// Enable TONE_TC
151
147
TONE_TC->COUNT16 .CTRLA .reg |= TC_CTRLA_ENABLE;
152
148
WAIT_TC16_REGS_SYNC (TONE_TC)
149
+
150
+ NVIC_EnableIRQ (TONE_TC_IRQn);
153
151
}
154
152
155
153
void noTone (uint32_t outputPin)
0 commit comments