|
15 | 15 | License along with this library; if not, write to the Free Software
|
16 | 16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17 | 17 | */
|
| 18 | + |
| 19 | +#include "Tone.h" |
| 20 | +#include "variant.h" |
| 21 | + |
| 22 | +#define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.STATUS.bit.SYNCBUSY); |
| 23 | + |
| 24 | +uint32_t toneMaxFrequency = SystemCoreClock / 2; |
| 25 | +uint32_t lastOutputPin = 0xFFFFFFFF; |
| 26 | + |
| 27 | +volatile uint32_t *portToggleRegister; |
| 28 | +volatile uint32_t *portClearRegister; |
| 29 | +volatile uint32_t portBitMask; |
| 30 | +volatile int64_t toggleCount; |
| 31 | +volatile bool toneIsActive = false; |
| 32 | + |
| 33 | +#define TONE_TC TC5 |
| 34 | +#define TONE_TC_IRQn TC5_IRQn |
| 35 | +#define TONE_TC_TOP 0xFFFF |
| 36 | +#define TONE_TC_CHANNEL 0 |
| 37 | + |
| 38 | +static inline void resetTC (Tc* TCx) |
| 39 | +{ |
| 40 | + // Disable TCx |
| 41 | + TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; |
| 42 | + WAIT_TC16_REGS_SYNC(TCx) |
| 43 | + |
| 44 | + // Reset TCx |
| 45 | + TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; |
| 46 | + WAIT_TC16_REGS_SYNC(TCx) |
| 47 | + while (TCx->COUNT16.CTRLA.bit.SWRST); |
| 48 | +} |
| 49 | + |
| 50 | +void toneAccurateClock (uint32_t accurateSystemCoreClockFrequency) |
| 51 | +{ |
| 52 | + toneMaxFrequency = accurateSystemCoreClockFrequency / 2; |
| 53 | +} |
| 54 | + |
| 55 | +void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) |
| 56 | +{ |
| 57 | + if (toneIsActive && (outputPin != lastOutputPin)) |
| 58 | + noTone(lastOutputPin); |
| 59 | + |
| 60 | + // |
| 61 | + // Calculate best prescaler divider and comparator value for a 16 bit TC peripheral |
| 62 | + // |
| 63 | + |
| 64 | + uint32_t prescalerConfigBits; |
| 65 | + uint32_t ccValue; |
| 66 | + |
| 67 | + ccValue = toneMaxFrequency / frequency - 1; |
| 68 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; |
| 69 | + |
| 70 | + if (ccValue > TONE_TC_TOP) |
| 71 | + { |
| 72 | + ccValue = toneMaxFrequency / frequency / 2 - 1; |
| 73 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; |
| 74 | + |
| 75 | + if (ccValue > TONE_TC_TOP) |
| 76 | + { |
| 77 | + ccValue = toneMaxFrequency / frequency / 4 - 1; |
| 78 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; |
| 79 | + |
| 80 | + if (ccValue > TONE_TC_TOP) |
| 81 | + { |
| 82 | + ccValue = toneMaxFrequency / frequency / 8 - 1; |
| 83 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; |
| 84 | + |
| 85 | + if (ccValue > TONE_TC_TOP) |
| 86 | + { |
| 87 | + ccValue = toneMaxFrequency / frequency / 16 - 1; |
| 88 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; |
| 89 | + |
| 90 | + if (ccValue > TONE_TC_TOP) |
| 91 | + { |
| 92 | + ccValue = toneMaxFrequency / frequency / 64 - 1; |
| 93 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; |
| 94 | + |
| 95 | + if (ccValue > TONE_TC_TOP) |
| 96 | + { |
| 97 | + ccValue = toneMaxFrequency / frequency / 256 - 1; |
| 98 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; |
| 99 | + |
| 100 | + if (ccValue > TONE_TC_TOP) |
| 101 | + { |
| 102 | + ccValue = toneMaxFrequency / frequency / 1024 - 1; |
| 103 | + prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; |
| 104 | + } |
| 105 | + } |
| 106 | + } |
| 107 | + } |
| 108 | + } |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1); |
| 113 | + |
| 114 | + // Enable GCLK for TC4 and TC5 (timer counter input clock) |
| 115 | + GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)); |
| 116 | + while (GCLK->STATUS.bit.SYNCBUSY); |
| 117 | + |
| 118 | + resetTC(TONE_TC); |
| 119 | + |
| 120 | + // Set Timer counter Mode to 16 bits |
| 121 | + TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16; |
| 122 | + |
| 123 | + // Set TONE_TC mode as match frequency |
| 124 | + TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; |
| 125 | + |
| 126 | + TONE_TC->COUNT16.CTRLA.reg |= prescalerConfigBits; |
| 127 | + |
| 128 | + TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue; |
| 129 | + WAIT_TC16_REGS_SYNC(TONE_TC) |
| 130 | + |
| 131 | + // Configure interrupt request |
| 132 | + NVIC_DisableIRQ(TONE_TC_IRQn); |
| 133 | + NVIC_ClearPendingIRQ(TONE_TC_IRQn); |
| 134 | + NVIC_SetPriority(TONE_TC_IRQn, 0); |
| 135 | + NVIC_EnableIRQ(TONE_TC_IRQn); |
| 136 | + |
| 137 | + portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg); |
| 138 | + portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg); |
| 139 | + portBitMask = (1ul << g_APinDescription[outputPin].ulPin); |
| 140 | + |
| 141 | + // Enable the TONE_TC interrupt request |
| 142 | + TONE_TC->COUNT16.INTENSET.bit.MC0 = 1; |
| 143 | + |
| 144 | + lastOutputPin = outputPin; |
| 145 | + digitalWrite(outputPin, LOW); |
| 146 | + pinMode(outputPin, OUTPUT); |
| 147 | + toneIsActive = true; |
| 148 | + |
| 149 | + // Enable TONE_TC |
| 150 | + TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; |
| 151 | + WAIT_TC16_REGS_SYNC(TONE_TC) |
| 152 | +} |
| 153 | + |
| 154 | +void noTone (uint32_t outputPin) |
| 155 | +{ |
| 156 | + resetTC(TONE_TC); |
| 157 | + digitalWrite(outputPin, LOW); |
| 158 | + toneIsActive = false; |
| 159 | +} |
| 160 | + |
| 161 | +#ifdef __cplusplus |
| 162 | +extern "C" { |
| 163 | +#endif |
| 164 | + |
| 165 | +void Tone_Handler (void) |
| 166 | +{ |
| 167 | + if (toggleCount != 0) |
| 168 | + { |
| 169 | + // Toggle the ouput pin |
| 170 | + *portToggleRegister = portBitMask; |
| 171 | + |
| 172 | + if (toggleCount > 0) |
| 173 | + --toggleCount; |
| 174 | + |
| 175 | + // Clear the interrupt |
| 176 | + TONE_TC->COUNT16.INTFLAG.bit.MC0 = 1; |
| 177 | + } |
| 178 | + else |
| 179 | + { |
| 180 | + resetTC(TONE_TC); |
| 181 | + *portClearRegister = portBitMask; |
| 182 | + toneIsActive = false; |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | +void TC5_Handler (void) __attribute__ ((weak, alias("Tone_Handler"))); |
| 187 | + |
| 188 | +#ifdef __cplusplus |
| 189 | +} |
| 190 | +#endif |
0 commit comments