Hi mwolter,
Here's the code that reads the period and pulse width of a 45Hz signal on D12 with TCC2/DMAC and outputs the values in microseconds (us) to the console:
// Setup TCC2 to capture pulse-width and period with DMAC transfer
volatile uint32_t dmacPeriod;
volatile uint32_t dmacPulsewidth;
typedef struct // DMAC descriptor structure
{
uint16_t btctrl;
uint16_t btcnt;
uint32_t srcaddr;
uint32_t dstaddr;
uint32_t descaddr;
} dmacdescriptor ;
volatile dmacdescriptor wrb[12] __attribute__ ((aligned (16))); // Write-back DMAC descriptors
dmacdescriptor descriptor_section[12] __attribute__ ((aligned (16))); // DMAC channel descriptors
dmacdescriptor descriptor __attribute__ ((aligned (16))); // Place holder descriptor
void setup()
{
SerialUSB.begin(115200); // Send data back on the Zero's native port
while(!SerialUSB); // Wait for the SerialUSB port to be ready
DMAC->BASEADDR.reg = (uint32_t)descriptor_section; // Set the descriptor section base address
DMAC->WRBADDR.reg = (uint32_t)wrb; // Set the write-back descriptor base adddress
DMAC->CTRL.reg = DMAC_CTRL_DMAENABLE | DMAC_CTRL_LVLEN(0xf); // Enable the DMAC and priority levels
DMAC->CHID.reg = DMAC_CHID_ID(0); // Select DMAC channel 0
// Set DMAC channel 1 to priority level 0 (lowest), to trigger on TCC2 match compare 1 and to trigger every beat
DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(TCC2_DMAC_ID_MC_0) | DMAC_CHCTRLB_TRIGACT_BEAT;
//DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // Enable all DMAC interrupts
descriptor.descaddr = (uint32_t)&descriptor_section[0]; // Set up a circular descriptor
descriptor.srcaddr = (uint32_t)&TCC2->CC[0].reg; // Take the contents of the TCC2 counter comapare 0 register
descriptor.dstaddr = (uint32_t)&dmacPeriod; // Copy it to the "dmacPeriod" variable
descriptor.btcnt = 1; // This takes one beat
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_VALID; // Copy 16-bits (HWORD) and flag discriptor as valid
memcpy(&descriptor_section[0], &descriptor, sizeof(dmacdescriptor)); // Copy to the channel 0 descriptor
DMAC->CHID.reg = DMAC_CHID_ID(1); // Select DMAC channel 1
// Set DMAC channel 1 to priority level 0 (lowest), to trigger on TCC2 match compare 1 and to trigger every beat
DMAC->CHCTRLB.reg = DMAC_CHCTRLB_LVL(0) | DMAC_CHCTRLB_TRIGSRC(TCC2_DMAC_ID_MC_1) | DMAC_CHCTRLB_TRIGACT_BEAT;
//DMAC->CHINTENSET.reg = DMAC_CHINTENSET_MASK ; // Enable all DMAC interrupts
descriptor.descaddr = (uint32_t)&descriptor_section[1]; // Set up a circular descriptor
descriptor.srcaddr = (uint32_t)&TCC2->CC[1].reg; // Take the contents of the TCC2 counter comapare 0 register
descriptor.dstaddr = (uint32_t)&dmacPulsewidth; // Copy it to the "dmacPulseWidth" variable
descriptor.btcnt = 1; // This takes 1 beat
descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_VALID; // Copy 16-bits (HWORD) and flag discriptor as valid
memcpy(&descriptor_section[1], &descriptor, sizeof(dmacdescriptor)); // Copy to the channel 1 descriptor
PM->APBCMASK.reg |= PM_APBCMASK_EVSYS; // Switch on the event system peripheral
GCLK->GENDIV.reg = GCLK_GENDIV_DIV(3) | // Divide the 48MHz system clock by 3 = 48MHz/3 = 16MHz
GCLK_GENDIV_ID(5); // Set division on Generic Clock Generator (GCLK) 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC | // Set the duty cycle to 50/50 HIGH/LOW
GCLK_GENCTRL_GENEN | // Enable GCLK 5
GCLK_GENCTRL_SRC_DFLL48M | // Set the clock source to 48MHz
GCLK_GENCTRL_ID(5); // Set clock source on GCLK 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization*/
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN | // Enable the generic clock...
GCLK_CLKCTRL_GEN_GCLK5 | // ....on GCLK5
GCLK_CLKCTRL_ID_TCC2_TC3; // Feed the GCLK5 to TCC2 and TC3
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization
// Enable the port multiplexer on digital pin D12
PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].bit.PMUXEN = 1;
// Set-up the pin as an EIC (interrupt) peripheral on D12
PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg |= PORT_PMUX_PMUXO_A;
//attachInterrupt(12, NULL, HIGH); // Attach interrupts to digital pin 12 (external interrupt 3)
EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3; // Enable event output on external interrupt 3
EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH; // Set event detecting a HIGH level
EIC->CTRL.reg |= EIC_CTRL_ENABLE; // 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_TCC2_EV_1); // Set the event user (receiver) as timer TCC2, event 1
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_3) | // Set event generator (sender) as external interrupt 3
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to channel 0
TCC2->EVCTRL.reg |= TCC_EVCTRL_MCEI1 | // Enable the match or capture channel 1 event input
TCC_EVCTRL_MCEI0 | //.Enable the match or capture channel 0 event input
TCC_EVCTRL_TCEI1 | // Enable the TCC event 1 input
/*TCC_EVCTRL_TCINV1 |*/ // Invert the event 1 input
TCC_EVCTRL_EVACT1_PPW; // Set up the timer for capture: CC0 period, CC1 pulsewidth
TCC2->CTRLA.reg |= TCC_CTRLA_CPTEN1 | // Enable capture on CC1
TCC_CTRLA_CPTEN0 | // Enable capture on CC0
TC_CTRLA_PRESCSYNC_PRESC | // Set the reset/reload to trigger on prescaler clock
TCC_CTRLA_PRESCALER_DIV16; // Set prescaler to 16, 16MHz/16 = 1MHz
TCC2->CTRLA.bit.ENABLE = 1;
while (TCC2->SYNCBUSY.bit.ENABLE); // Wait for synchronization
DMAC->CHID.reg = DMAC_CHID_ID(0); // Select DMAC channel 0
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; // Enable DMAC channel 0
DMAC->CHID.reg = DMAC_CHID_ID(1); // Select DMAC channel 1
DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; // Enable DMAC channel 1
}
void loop()
{
SerialUSB.print(dmacPeriod); // Output the results
SerialUSB.print(F(" "));
SerialUSB.println(dmacPulsewidth);
}
If you only require pulse width, then it's possible to just comment out the period DMAC channel.