#ifndef ATSAMC21Timer_h #define ATSAMC21Timer_h #include "../VirtualTimer.h" #include class Timer : public VirtualTimer { private: int pwmPeriod; unsigned long timer_resolution; unsigned long lastMicroseconds; public: void (*isrCallback)(); Tcc* timer; Timer(Tcc* timer) { this->timer = timer; if(timer == TCC0 || timer == TCC1) { timer_resolution = 16777216; } else { timer_resolution = 65536; } lastMicroseconds = 0; } void initialize() { if(timer == TCC0 || timer == TCC1) { MCLK->APBCMASK.bit.TCC0_ = 1; MCLK->APBCMASK.bit.TCC1_ = 1; GCLK->GENCTRL[4].reg = ( GCLK_GENCTRL_DIV(2) | GCLK_GENCTRL_SRC_DPLL96M | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_OE ); while ((GCLK->SYNCBUSY.bit.GENCTRL >> 4) & 1); // Wait for synchronization GCLK->PCHCTRL[28].reg = ( GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(4) ); // 28 = TCC0_TCC1 while ((GCLK->SYNCBUSY.bit.GENCTRL >> 4) & 1); // Wait for synchronization } else if (timer == TCC2) { MCLK->APBCMASK.bit.TCC2_ = 1; GCLK->GENCTRL[5].reg = ( GCLK_GENCTRL_DIV(2) | GCLK_GENCTRL_SRC_DPLL96M | GCLK_GENCTRL_IDC | GCLK_GENCTRL_GENEN | GCLK_GENCTRL_OE ); while ((GCLK->SYNCBUSY.bit.GENCTRL >> 5) & 1); // Wait for synchronization GCLK->PCHCTRL[29].reg = ( GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN(5) ); // 29 = TCC2 while ((GCLK->SYNCBUSY.bit.GENCTRL >> 5) & 1); // Wait for synchronization } timer->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Select NPWM as waveform while (timer->SYNCBUSY.bit.WAVE); // Wait for synchronization } void setPeriod(unsigned long microseconds) { if(microseconds == lastMicroseconds) return; lastMicroseconds = microseconds; const unsigned long cycles = F_CPU / 1000000 * microseconds; // cycles corresponds to how many clock ticks per microsecond times number of microseconds we want timer->CTRLA.bit.PRESCALER = 0; if(cycles < timer_resolution) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1_Val); pwmPeriod = cycles; } else if(cycles < timer_resolution * 2) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV2_Val); pwmPeriod = cycles / 2; } else if(cycles < timer_resolution * 4) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV4_Val); pwmPeriod = cycles / 4; } else if(cycles < timer_resolution * 8) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV8_Val); pwmPeriod = cycles / 8; } else if(cycles < timer_resolution * 16) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV16_Val); pwmPeriod = cycles / 16; } else if(cycles < timer_resolution * 64) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV64_Val); pwmPeriod = cycles / 64; } else if(cycles < timer_resolution * 1024) { timer->CTRLA.reg |= TCC_CTRLA_PRESCALER(TCC_CTRLA_PRESCALER_DIV1024_Val); pwmPeriod = cycles / 1024; } timer->PER.reg = pwmPeriod; while (timer->SYNCBUSY.bit.PER); } void start() { timer->CTRLA.bit.ENABLE = 1; // Turn on the output while (timer->SYNCBUSY.bit.ENABLE); // Wait for synchronization } void stop() { timer->CTRLA.bit.ENABLE = 0; // Turn on the output while (timer->SYNCBUSY.bit.ENABLE); // Wait for synchronization } void attachInterrupt(void (*isr)()) { isrCallback = isr; // Store the interrupt callback function timer->INTENSET.reg = TCC_INTENSET_OVF; // Set the interrupt to occur on overflow if(timer == TCC0) { NVIC_EnableIRQ((IRQn_Type) TCC0_IRQn); // Enable the interrupt (clock is still off) } else if(timer == TCC1) { NVIC_EnableIRQ((IRQn_Type) TCC1_IRQn); // Enable the interrupt (clock is still off) } else if(timer == TCC2) { NVIC_EnableIRQ((IRQn_Type) TCC2_IRQn); // Enable the interrupt (clock is still off) } } void detachInterrupt() { if(timer == TCC0) { NVIC_DisableIRQ((IRQn_Type) TCC0_IRQn); // Disable the interrupt } else if(timer == TCC1) { NVIC_DisableIRQ((IRQn_Type) TCC1_IRQn); // Disable the interrupt } else if(timer == TCC2) { NVIC_DisableIRQ((IRQn_Type) TCC2_IRQn); // Disable the interrupt } } }; extern Timer TimerA; extern Timer TimerB; extern Timer TimerC; #endif // ATSAMC21Timer_h