I am trying to communicate between two Seeeduino XIAO (chip: ATSAMD21G18A-MU) by way of TCC capture using register timers.
Square wave pulse duration varies from 200ns to 4us.
I found these two code samples, and I am now trying to change them up to make them work the way I intend to read and write these short pulses.
1) XIAO writing square wave (generate output)
This code works, but it is changing the pulse width and not the pulse period. Output is on pin D2 and D3. How can I make it change the polse period instead of the polse width? Source
// Output 300kHz dual slope PWM on TCC0 with complementary outputs and dead time insertion
void setup()
{
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable GCLK0
GCLK_CLKCTRL_GEN_GCLK0 | // Select GCLK0 at 48MHz
GCLK_CLKCTRL_ID_TCC0_TCC1; // Route GCLK0 to TCC0 and TCC1
PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 1; // Enable the port multiplexer for port pins PA10 and PA11
PORT->Group[PORTA].PINCFG[11].bit.PMUXEN = 1;
// Select the port pin multiplexer switch to option F for TCC0/WO[2] and TCC0/WO[3] on
// port pins PA10 and PA11 respectively
PORT->Group[PORTA].PMUX[10 >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;
TCC0->WAVE.reg = TCC_WAVE_POL2 | // Reverse the signal polarity on channel 2
TCC_WAVE_WAVEGEN_DSBOTTOM; // Dual slope PWM on TCC0
while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization
// 48,000,000 / 300,000 = 160
TCC0->PER.reg = 79; // Set the frequency of the PWM on TCC0 to 300kHz
while(TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[2].reg = 40; // Output a 50% duty-cycle
while(TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization
TCC0->CC[3].reg = 35; // Output a 43% duty-cycle
while(TCC0->SYNCBUSY.bit.CC3); // Wait for synchronization
TCC0->CTRLA.bit.ENABLE = 1; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop(){
TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD; // Set the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
TCC0->CCB[2].reg = 40; // Output a 50% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB2); // Wait for synchronization
TCC0->CCB[3].reg = 35; // Output a 43% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; // Clear the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
delay(1000); // Wait for 1 second
TCC0->CTRLBSET.reg = TCC_CTRLBSET_LUPD; // Set the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
TCC0->CCB[2].reg = 20; // Output a 25% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB2); // Wait for synchronization
TCC0->CCB[3].reg = 15; // Output a 18% duty-cycle
while(TCC0->SYNCBUSY.bit.CCB3); // Wait for synchronization
TCC0->CTRLBCLR.reg = TCC_CTRLBCLR_LUPD; // Clear the Lock Update (LUPD) bit
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchroniztion
delay(1000);
}
2) XIAO reading square wave (input capture); Period and Pulse-Width (PPW) Capture
This code works, but it reads the frequency over a period of 1 second, and not just the duration of a polse period. Input is on D6. How can I make these changes to the code for it to work? Source
// Count the number of pulses on pin D6 (PB08) over a 1 second period
void setup()
{
SerialUSB.begin(115200); // Initialise the native serial port
while(!SerialUSB); // Wait for the console to open
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; // Switch on the event system peripheral
////////////////////////////////////////////////////////////////////////////////////////
// Generic Clock Initialisation
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(1) | // Select clock divisor to 1
GCLK_GENDIV_ID(4); // Select GLCK4
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK
GCLK_GENCTRL_SRC_XOSC32K | // Select GCLK source as
// external 32.768kHz crystal (XOSC32K)
GCLK_GENCTRL_ID(4); // Select GCLK4
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable generic clock
GCLK_CLKCTRL_GEN_GCLK0 | // GCLK0 at 48MHz
GCLK_CLKCTRL_ID_TCC0_TCC1; // As a clock source for TCC0 and TCC1
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable generic clock
GCLK_CLKCTRL_GEN_GCLK4 | // GCLK4 at 32.768kHz
GCLK_CLKCTRL_ID_TCC2_TC3; // As a clock source for TCC2 and TC3
//////////////////////////////////////////////////////////////////////////////////////////////
// TCC2 Initialisation - reference timer: measures a 1s period
TCC2->PER.reg = 32767; // Set the period (PER) register for a
// PWM period of 1s
while (TCC2->SYNCBUSY.bit.PER); // Wait for synchronization
TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NPWM; // Set timer to Normal PWM mode (NPWM)
while (TCC2->SYNCBUSY.bit.WAVE); // Wait for synchronization
TCC2->CTRLBSET.reg = TCC_CTRLBSET_ONESHOT; // Enable oneshot operation
while(TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
NVIC_SetPriority(TCC2_IRQn, 0); // Set the Nested Vector Interrupt Controller
// (NVIC) priority for TCC2 to 0 (highest)
NVIC_EnableIRQ(TCC2_IRQn); // Connect TCC2 to Nested Vector Interrupt
// Controller (NVIC)
TCC2->INTENSET.reg = TCC_INTENSET_OVF; // Enable overflow (OVF) interrupts on TCC2
TCC2->CTRLA.bit.ENABLE = 1; // Enable TCC2
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
////////////////////////////////////////////////////////////////////////////////////////
// TCC0 Initialisation - measurement counter: counts the number of incoming of pulses
PORT->Group[PORTB].PINCFG[8].bit.PMUXEN = 1; // Enable the port multiplexer
// on port pin PB08 (D6)
PORT->Group[PORTB].PMUX[8 >> 1].reg |= PORT_PMUX_PMUXE_A; // Set-up PB08 (D6) as an
// EIC (interrupt)
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO8; // Enable event output on external interrupt 8
EIC->CONFIG[1].reg |= EIC_CONFIG_SENSE0_HIGH; // Set interrupt to detect a HIGH level
EIC->INTENCLR.reg = EIC_INTENCLR_EXTINT8; // Clear the interrupt flag on channel 8
EIC->CTRL.bit.ENABLE = 1; // Enable EIC peripheral
while (EIC->STATUS.bit.SYNCBUSY); // Wait for synchronization
EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) | // Attach the event user (receiver) to
// channel 0 (n + 1)
EVSYS_USER_USER(EVSYS_ID_USER_TCC0_EV_0); // Set the event user (receiver)
// as timer TCC0, event 0
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | // No event edge detection
EVSYS_CHANNEL_PATH_ASYNCHRONOUS | // Set event path as asynchronous
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_8) | // Set event generator
// (sender) as external interrupt 8
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender)
// to channel 0
TCC0->EVCTRL.reg = TCC_EVCTRL_TCEI0 | // Enable TCC0 event 0 inputs
TCC_EVCTRL_EVACT0_INC; // Increment the TCC0 counter on receiving an event 0
TCC0->CTRLA.bit.ENABLE = 1; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop()
{
startConversion(); // Start a conversion over the 1 second integration window
delay(3500); // Wait for 1 second
}
void startConversion()
{
TCC2->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger the timer TCC2 to
// start the integration window
while (TCC2->SYNCBUSY.bit.CTRLB); // Wait for synchronization
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_RETRIGGER; // Retrigger the timer TCC0 to
// start the count
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for synchronization
}
void TCC2_Handler()
{
TCC2->INTFLAG.bit.OVF = 1; // Clear the TCC2 overflow interrupt flag
TCC0->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC; // Trigger a read synchronization
// on the COUNT register
while (TCC0->SYNCBUSY.bit.CTRLB); // Wait for the CTRLB register write synchronization
while (TCC0->SYNCBUSY.bit.COUNT); // Wait for the COUNT register read sychronization
SerialUSB.println(TCC0->COUNT.reg); // Print the result
}
Some helpful page explaining these timers
https://www.picotech.com/support/topic24051.html
https://shawnhymel.com/1710/arduino-zero-samd21-raw-pwm-using-cmsis/