mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-29 15:11:24 +01:00
144 lines
6.0 KiB
C
144 lines
6.0 KiB
C
|
#ifndef ATSAMD21GTimer_h
|
||
|
#define ATSAMD21GTimer_h
|
||
|
|
||
|
#include "../VirtualTimer.h"
|
||
|
#include <Arduino.h>
|
||
|
|
||
|
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) {
|
||
|
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide 48MHz by 1
|
||
|
GCLK_GENDIV_ID(4); // Apply to GCLK4
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||
|
|
||
|
REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | // Enable GCLK
|
||
|
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
|
||
|
GCLK_GENCTRL_ID(4); // Select GCLK4
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||
|
|
||
|
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable generic clock
|
||
|
4 << GCLK_CLKCTRL_GEN_Pos | // Apply to GCLK4
|
||
|
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed GCLK to TCC0/1
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||
|
}
|
||
|
else if (timer == TCC2) {
|
||
|
REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) | // Divide 48MHz by 1
|
||
|
GCLK_GENDIV_ID(5); // Apply to GCLK4
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||
|
|
||
|
REG_GCLK_GENCTRL = GCLK_GENCTRL_GENEN | // Enable GCLK
|
||
|
GCLK_GENCTRL_SRC_DFLL48M | // Set the 48MHz clock source
|
||
|
GCLK_GENCTRL_ID(5); // Select GCLK4
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
|
||
|
|
||
|
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable generic clock
|
||
|
5 << GCLK_CLKCTRL_GEN_Pos | // Apply to GCLK4
|
||
|
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed GCLK to TCC0/1
|
||
|
while (GCLK->STATUS.bit.SYNCBUSY); // 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
|
||
|
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
|