Hello,
my intention here (still learning SAMD internals) is regularly sampling ADC and the using DMA to copy data
For sampling I'm using a Timer (TC3) and start ADC sampling on TC3_IRQ. That is working, but with higher frequencies not practical. As well I'd like to save CPU for data processing, not employing as a postman between TC3 and ADC.
I found out in theory I could use EVSYS to pass an event from the TC3 timer to start ADC. And .. that is not working, apparently I missed setting some SAMD register and from the datasheet it is not always clear which one
When trying to pass a SWEVT to EVSYS, nothing happens (no ADC nor EVSYS interrupt as configured), so I'd assume I missed something on EVSYS configuration:
void setupClock() {
// Arduino code configures following clocks
// GCLK0 -> 48MHz Digital Frequency Locked Loop
// GCLK1 -> 32.768kHz external crystal, otherwise 32.768kHz internal oscillator (for CRYSTALLESS operation)
// GCLK2 -> 32.768kHz ultra low power internal oscillator
// GCLK3 -> 8MHz internal oscillator
// This leaves GCLK4 through to GCLK7 free to use as you wish.
// we will be using GLCK4 8MHz for sake of learning
// Run GLCK4 on 8M clock
SYSCTRL->OSC8M.bit.ENABLE = 1;
SYSCTRL->OSC8M.bit.PRESC = 0x00; // SYSCTRL_OSC8M_PRESC_1; // divide by 1
/* Wait for the clock / oscillator to be ready */
while(!SYSCTRL->PCLKSR.bit.OSC8MRDY);
/* Setup GCLK1 using the internal 8M oscillator */
GCLK->GENCTRL.reg =
GCLK_GENCTRL_ID(4) |
GCLK_GENCTRL_SRC_OSC8M |
// GCLK_GENCTRL_DIVSEL | /* apply prescaler divider */
GCLK_GENCTRL_IDC | /* Improve the duty cycle. */
GCLK_GENCTRL_GENEN;
/* Wait for the write to complete */
syncGLCK();
// https://blog.thea.codes/understanding-the-sam-d21-clocks/
// Enable TC3 on 8MHz
GCLK->CLKCTRL.reg =
GCLK_CLKCTRL_ID( GCM_TCC2_TC3 ) | // TC3 clock
// GCLK_CLKCTRL_ID( GCM_ADC ) | // Generic Clock ADC
GCLK_CLKCTRL_GEN_GCLK4 | // Generic Clock Generator 4 is source
GCLK_CLKCTRL_CLKEN ;
syncGLCK();
// Enable ADC
GCLK->CLKCTRL.reg =
// GCLK_CLKCTRL_ID( GCM_TCC2_TC3 ) | // TC3 clock
GCLK_CLKCTRL_ID( GCM_ADC ) | // Generic Clock ADC
GCLK_CLKCTRL_GEN_GCLK4 | // Generic Clock Generator 4 is source
GCLK_CLKCTRL_CLKEN ;
syncGLCK();
// Enable EVSYS chn 0 CLK4 (the same as the TC3 gen source allowing sync events)
GCLK->CLKCTRL.reg =
// GCLK_CLKCTRL_ID( EVSYS_GCLK_ID_0) | // Generic Clock for EVSYS
GCLK_CLKCTRL_ID(GCM_EVSYS_CHANNEL_0) |
GCLK_CLKCTRL_GEN_GCLK4 | // Generic Clock Generator 1 is source
GCLK_CLKCTRL_CLKEN ;
syncGLCK();
}
/* ------------------------------ */
void setupAdc() {
PM->APBCMASK.reg |= PM_APBCMASK_ADC;
ADC->CTRLA.bit.ENABLE = false;
syncADC();
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV16 | // Divide Clock by 512.
#ifdef TEST_ADC_FREERUN
ADC_CTRLB_FREERUN |
#endif
ADC_CTRLB_RESSEL_12BIT; // 10 bits resolution as default
// sampling_time = (SAMPLE_LEN+1) * CLK(ADC) / 2
ADC->SAMPCTRL.bit.SAMPLEN = 0x015; // Set max Sampling Time Length
syncADC();
ADC->INPUTCTRL.reg = ADC_INPUTCTRL_MUXNEG_GND; // No Negative input (Internal Ground)
// Averaging (see datasheet table in AVGCTRL register description)
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_2 | // 2 sample only (no oversampling nor averaging)
ADC_AVGCTRL_ADJRES(0x02ul); // Adjusting result according to the datasheet
syncADC();
// this should starting ADC sampling from EVSYS
// A new conversion will be triggered on any incoming event.
ADC->EVCTRL.bit.STARTEI = 1;
syncADC();
// setup IRQ
NVIC_DisableIRQ(ADC_IRQn);
NVIC_ClearPendingIRQ(ADC_IRQn);
ADC->INTENSET.bit.RESRDY = 1;
NVIC_SetPriority(ADC_IRQn, 1);
NVIC_EnableIRQ(ADC_IRQn);
/* Enable the ADC. */
ADC->CTRLA.bit.ENABLE = true;
syncADC();
// Set pin to input mode
PORT->Group[g_APinDescription[AUDIO_PIN].ulPort].PINCFG[g_APinDescription[AUDIO_PIN].ulPin].reg=(uint8_t)(PORT_PINCFG_INEN) ;
PORT->Group[g_APinDescription[AUDIO_PIN].ulPort].DIRCLR.reg = (uint32_t)(1<<g_APinDescription[AUDIO_PIN].ulPin) ;
}
/* --------------------- */
void setupTimer() {
// clock w/ prescaler on 8MHz
TC3->COUNT16.CTRLA.bit.ENABLE = 0;
syncTC3();
// set 16-bit mode and set waveform 'match frequency'
TC3->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16| TC_CTRLA_WAVEGEN_MFRQ;
syncTC3();
// let's keep 8MHz clock
// clock_freq / freq / divisor
// TC3 FREQ = 1 MHz ( 8MHz / 8)
TC3->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV8;
syncTC3();
// set compare register value
// period = 1000000 / freq = 125 us
// ticks_persiod = ;
TC3->COUNT16.COUNT.reg = 0;
// TC3->COUNT16.CC[0].reg = 125;
// 1kHz
TC3->COUNT16.CC[0].reg = 1000;
// count down
TC3->COUNT16.CTRLBCLR.bit.DIR = 1;
syncTC3();
// enable IRQ on TC3 MC0
// NVIC_DisableIRQ(TC3_IRQn);
// NVIC_ClearPendingIRQ(TC3_IRQn);
// NVIC_SetPriority(TC3_IRQn, 2);
// NVIC_EnableIRQ(TC3_IRQn);
// TC3->COUNT16.INTENSET.bit.MC0 = 1;
// syncTC3();
// enable TC3
TC3->COUNT16.CTRLA.bit.ENABLE = 1;
/*
The TC can generate the following output events:
• Overflow/Underflow (OVF)
• Match or Capture (MC)
Writing a '1' to an Event Output bit in the Event Control register (EVCTRL.MCEOx) enables the corresponding output
event. The output event is disabled by writing EVCTRL.MCEOx=0.
*/
TC3->COUNT16.EVCTRL.bit.MCEO0 = 1; // enable match event on EVT Chn 0
// EVACT seems to be for receiving events, not generating
// TC3->COUNT16.EVCTRL.bit.EVACT = TC_EVCTRL_EVACT(TC_EVCTRL_EVACT_RETRIGGER_Val); // restart TC on event
syncTC3();
}
/* -------------------------- */
void setupEVSYS() {
// EVSYS->CTRL.bit.SWRST = 1;
EVSYS->USER.reg = EVSYS_USER_CHANNEL(0+1) | // Attach the event user (receiver) to channel 0 (n + 1)
// EVSYS_USER_USER(EVSYS_ID_USER_ADC_START); // Set the event user (receiver) as timer TC3
EVSYS_USER_USER(EVSYS_ID_USER_ADC_START);
// while(EVSYS->CHSTATUS.bit.CHBUSY0!=0);
EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT | // No event edge detection
// When the asynchronous path is selected, the channel cannot generate any interrupts,
// and the Channel Status register (CHSTATUS) is always zero. No edge detection is available;
EVSYS_CHANNEL_PATH_RESYNCHRONIZED |
EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TC3_MCX_0) | // Set event generator (sender) as external interrupt 6
EVSYS_CHANNEL_CHANNEL(0); // Attach the generator (sender) to channel 0
EVSYS->CTRL.bit.GCLKREQ = 1;
while(EVSYS->CHSTATUS.bit.CHBUSY0!=0);
// enable IRQ on EVSYS Chnl 0
EVSYS->INTENSET.bit.EVD0 = 1;
while(EVSYS->CHSTATUS.bit.CHBUSY0!=0);
// The EVSYS has the following interrupt sources:
// Overrun Channel n (OVRn): for details, refer to The Overrun Channel n Interrupt section.
// we're using IRQ only to set/increase some flags so we see events are detected
NVIC_DisableIRQ(EVSYS_IRQn);
NVIC_ClearPendingIRQ(EVSYS_IRQn);
NVIC_SetPriority(EVSYS_IRQn, 2);
NVIC_EnableIRQ(EVSYS_IRQn);
}
full compilable (not working) code here: TC3-EVSYS-ADC test (not working) - Pastebin.com