Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save nalex4711/2fc30672665dc0bad83e862bc2d59db0 to your computer and use it in GitHub Desktop.
Save nalex4711/2fc30672665dc0bad83e862bc2d59db0 to your computer and use it in GitHub Desktop.
Code inspired by ArduinoZeroTimer.ino (with Thanks) : file name: ArduinoZeroTimer_with_XOSC32K_at_8.192kHz.ino
/* 32 bit variables only for debugging */
uint32_t RG_TC5_COUNT_CC0 = 0;
uint32_t RG_GENDIV_REG = 0;
uint32_t RG_GCLK_GENCTRL = 0;
uint32_t RG_GCLK_CLKCTRL = 0;
/* actual 16 bit CC value */
// uint16_t CC_period = 1;
/* to be used as lowest margin:
period T_cycle_measured = 122.1 us
frequency f_cycle_measured = 8.192 kHz */
uint16_t CC_period = 1;
/* to be used as maximum permissible value (16 bit):
perid T_cycle_measured = 4 s
frequency f_cycle_measured = 0.25 Hz */
// uint16_t CC_period = 65535;
#define PULSE_PIN 11
bool state = 0;
void setup() {
/* Set up the external XOSC32K : 32kHz crystal clock */
SYSCTRL->XOSC32K.reg = 0x0
| SYSCTRL_XOSC32K_ENABLE
| SYSCTRL_XOSC32K_EN32K
| SYSCTRL_XOSC32K_XTALEN
| SYSCTRL_XOSC32K_STARTUP(0x5) // startup time 1000092μs us
| SYSCTRL_XOSC32K_AAMPEN // select GAIN automatic
;
while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY);
/* Set up the division unit */
GCLK->GENDIV.reg = 0x0
| GCLK_GENDIV_DIV(1) // Select clock divisor to 1
| GCLK_GENDIV_ID(3) // Select GLCKGEN3
;
/* 32 bit variables only fordebugging */
RG_GENDIV_REG = RG_GENDIV_REG | GCLK->GENDIV.reg;
/* Assign a generic clock controller
Select external 32.768kHz crystal XOSC32K
a. set duty cycle to 50%
b. enable GCLKGEN3
c. select XOSC32K precition crystal
d. select GCLKGEN3 */
GCLK->GENCTRL.reg = 0x0
| GCLK_GENCTRL_IDC // Set duty cycle to 50/50 HIGH/LOW
| GCLK_GENCTRL_GENEN // Enable GCLKGEN3
| GCLK_GENCTRL_SRC_XOSC32K
| GCLK_GENCTRL_ID(3) // Select GCLKGEN3
;
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
/* a. CLKCTRL enable
b. select GCLK0
c. select clocks TC4 and TC5 */
GCLK->CLKCTRL.reg = (uint16_t) ( GCLK_CLKCTRL_CLKEN
| GCLK_CLKCTRL_GEN_GCLK3
| GCLK_CLKCTRL_ID_TC4_TC5)
;
while (GCLK->STATUS.bit.SYNCBUSY);
/* 32 bit variables only fordebugging */
RG_GCLK_GENCTRL = GCLK->GENCTRL.reg;
RG_GCLK_CLKCTRL = GCLK->CLKCTRL.reg;
/* TC5 timer Initialisation
reset TC5 */
TC_Reset();
/* a. Set Timer counter TC5 Mode to COUNT16
b. Set TC5 waveform MFRQ to match frequency
c. Divide GCLK_TC by TC_CTRLA_PRESCALER_DIV1 = 1
d. Enable clock */
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16
| TC_CTRLA_WAVEGEN_MFRQ
| TC_CTRLA_PRESCALER_DIV1
| TC_CTRLA_ENABLE;
/* configure PULSE_PIN as output */
pinMode(PULSE_PIN, OUTPUT);
/* configure the timer to run at CC_period */
TC_Configure(CC_period);
/* start the timer */
TC_StartCounter();
}
void loop() {
}
void TC5_Handler(void) {
/* Function gets called as the interrupt emerges */
/* START OF My code : pulse PIN 11 */
if (state == true) {
digitalWrite(PULSE_PIN, HIGH);
} else {
digitalWrite(PULSE_PIN, LOW);
}
state = !state;
/* END OF My code */
/* Set MC0 clears the interrupt so that it will run again */
TC5->COUNT16.INTFLAG.bit.MC0 = 1;
}
void TC_Configure(int CC_period){
/* e. Case 1:
Set compare-capture register when using XOSC
System Clock runs at XOSC = 4MHz (default)
The uint16_t CC[0] should take acceptable
values 64 to 65535 for the XOSC
There is no acceptable accuracy below
CC[0] = 64 for the XOSC
TC5 runs at 4MHz/1(prescaler)= 4MHz
Time period for 1 count : Tcount = 0.25us
eg. at 64 counts f ~ 3.92 MHz
f. Case 2:
Set compare-capture register when using XOSC32K
System Clock runs at XOSC with f = 32768kHz
The uint16_t CC[0] takes acceptable
values 1 to 65535 for the XOSC32K
TC5 runs at 32.768 kHz/1(prescaler)= 32.768 kHz
Time period for 1 count : Tcount = 122.1 us
eg. at 1 count f = 32.768 kHz/4 = 8.192 kHz
is divided by 4 (i don't know why) */
TC5->COUNT16.CC[0].reg = CC_period;
/* 32 bit variables only fordebugging */
RG_TC5_COUNT_CC0 = RG_TC5_COUNT_CC0 | TC5->COUNT16.CC[0].reg;
while (TC_IsSyncing());
/* Configure Nonvolatile Interrupt Controler NIVC
a. disable TC5_IRQn (enum TC5_IRQn = 18)
b. clear TC5_IRQn
c. set priority TC5_IRQn
d. eneble TC5_IRQn */
NVIC_DisableIRQ(TC5_IRQn);
NVIC_ClearPendingIRQ(TC5_IRQn);
NVIC_SetPriority(TC5_IRQn, 0);
NVIC_EnableIRQ(TC5_IRQn);
/* Enable the TC5 interrupt request */
TC5->COUNT16.INTENSET.bit.MC0 = 0x1;
while (TC_IsSyncing());
}
bool TC_IsSyncing() {
/* Check if TC5 is syncing true when done */
return TC5->COUNT16.STATUS.reg & TC_STATUS_SYNCBUSY;
}
void TC_StartCounter() {
/* Enable TC5 and wait to ready */
/* set the CTRLA register */
TC5->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE;
while (TC_IsSyncing());
}
void TC_Reset() {
/* Reset TC5 */
TC5->COUNT16.CTRLA.reg = TC_CTRLA_SWRST;
while (TC_IsSyncing());
while (TC5->COUNT16.CTRLA.bit.SWRST);
}
@radioisotopes
Copy link

radioisotopes commented Jun 3, 2024

The original code works well but I'd like to be able to change the duty cycles by varying the amount loaded into TC5 every time the interrupt goes off. So I load a new number into TC5 at the start of the high period and leave the old number for the low period.
/* START OF My code : pulse PIN 11 /
if (state == true) {
digitalWrite(PULSE_PIN, HIGH);
TC5->COUNT16.CC[0].reg = CC_period
2;
} else {
digitalWrite(PULSE_PIN, LOW);
TC5->COUNT16.CC[0].reg = CC_period;
}
state = !state;
This should give a 60% high duty cycle. But it doesnt work .... my thinking is that I can control the duty cycle by varying the amount loaded into TC5 on the high signal, and on the low signal. The results I get are broadly unpredictable!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment