void Tone_Handler (void) { if (toggleCount != 0) { // Toggle the ouput pin *portToggleRegister = portBitMask; if (toggleCount > 0) --toggleCount; // Clear the interrupt TONE_TC->COUNT16.INTFLAG.bit.MC0 = 1; } else { resetTC(TONE_TC); *portClearRegister = portBitMask; toneIsActive = false; } }
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) { if (toneIsActive && (outputPin != lastOutputPin)) noTone(lastOutputPin); // // Calculate best prescaler divider and comparator value for a 16 bit TC peripheral // uint32_t prescalerConfigBits; uint32_t ccValue; ccValue = toneMaxFrequency / frequency - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 2 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 4 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 8 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 16 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 64 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 256 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; if (ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / 1024 - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; } } } } } } } toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1); // Enable GCLK for TC4 and TC5 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)); while (GCLK->STATUS.bit.SYNCBUSY); resetTC(TONE_TC); // Set Timer counter Mode to 16 bits TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16; // Set TONE_TC mode as match frequency TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; TONE_TC->COUNT16.CTRLA.reg |= prescalerConfigBits; TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue; WAIT_TC16_REGS_SYNC(TONE_TC) // Configure interrupt request NVIC_DisableIRQ(TONE_TC_IRQn); NVIC_ClearPendingIRQ(TONE_TC_IRQn); NVIC_SetPriority(TONE_TC_IRQn, 0); NVIC_EnableIRQ(TONE_TC_IRQn); portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg); portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg); portBitMask = (1ul << g_APinDescription[outputPin].ulPin); // Enable the TONE_TC interrupt request TONE_TC->COUNT16.INTENSET.bit.MC0 = 1; lastOutputPin = outputPin; digitalWrite(outputPin, LOW); pinMode(outputPin, OUTPUT); toneIsActive = true; // Enable TONE_TC TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; WAIT_TC16_REGS_SYNC(TONE_TC) }
void noTone (uint32_t outputPin) { resetTC(TONE_TC); digitalWrite(outputPin, LOW); toneIsActive = false; }
void tone (uint32_t outputPin, uint32_t frequency, uint32_t duration) { // Configure interrupt request NVIC_DisableIRQ(TONE_TC_IRQn); NVIC_ClearPendingIRQ(TONE_TC_IRQn); if(!firstTimeRunning) { firstTimeRunning = true; NVIC_SetPriority(TONE_TC_IRQn, 0); // Enable GCLK for TC4 and TC5 (timer counter input clock) GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_TC4_TC5)); while (GCLK->STATUS.bit.SYNCBUSY); } if (toneIsActive && (outputPin != lastOutputPin)) noTone(lastOutputPin); // // Calculate best prescaler divider and comparator value for a 16 bit TC peripheral // uint32_t prescalerConfigBits; uint32_t ccValue; ccValue = toneMaxFrequency / frequency - 1; prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1; uint8_t i = 0; while(ccValue > TONE_TC_TOP) { ccValue = toneMaxFrequency / frequency / (2<<i) - 1; i++; if(i == 4 || i == 6 || i == 8) //DIV32 DIV128 and DIV512 are not available i++; } switch(i-1) { case 0: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV2; break; case 1: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV4; break; case 2: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV8; break; case 3: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV16; break; case 5: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV64; break; case 7: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV256; break; case 9: prescalerConfigBits = TC_CTRLA_PRESCALER_DIV1024; break; default: break; } toggleCount = (duration > 0 ? frequency * duration * 2 / 1000UL : -1); resetTC(TONE_TC); uint16_t tmpReg = 0; tmpReg |= TC_CTRLA_MODE_COUNT16; // Set Timer counter Mode to 16 bits tmpReg |= TC_CTRLA_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency tmpReg |= prescalerConfigBits; TONE_TC->COUNT16.CTRLA.reg |= tmpReg; WAIT_TC16_REGS_SYNC(TONE_TC) TONE_TC->COUNT16.CC[TONE_TC_CHANNEL].reg = (uint16_t) ccValue; WAIT_TC16_REGS_SYNC(TONE_TC) portToggleRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTTGL.reg); portClearRegister = &(PORT->Group[g_APinDescription[outputPin].ulPort].OUTCLR.reg); portBitMask = (1ul << g_APinDescription[outputPin].ulPin); // Enable the TONE_TC interrupt request TONE_TC->COUNT16.INTENSET.bit.MC0 = 1; if (outputPin != lastOutputPin) { lastOutputPin = outputPin; digitalWrite(outputPin, LOW); pinMode(outputPin, OUTPUT); toneIsActive = true; } // Enable TONE_TC TONE_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; WAIT_TC16_REGS_SYNC(TONE_TC) NVIC_EnableIRQ(TONE_TC_IRQn); }