/*** *** The example has no copyright and can be used by anyone. *** The following example is based on Device File Package *** required to compile the macro definitions used. *** The Device File Package is available by downloading Atmel Studio 7. ***/ /*** *** In this example, The ADC is triggered by the TC0 every 0.001s (1kHz) to start a conversion. *** An interrupt is then generated once the ADC has fill a ADC_DATA_CAPTURE_SIZE samples buffer using the DMA. *** ADC is configured as Event USER using the Event system, *** TC0 is configured to be the Event GENERATOR in the Event System ***/ #define ADC_DATA_CAPTURE_SIZE 64 #define ADC_BUFFER_SIZE ADC_DATA_CAPTURE_SIZE /*** ADC data buffer ***/ extern uint16_t adc_data[ADC_BUFFER_SIZE]; /*** Create DMA descriptor ***/ extern volatile __attribute__((__aligned__(128))) DmacDescriptor descriptor_ADC; /*** Initial write back memory section. ***/ extern volatile __attribute__((__aligned__(128))) DmacDescriptor write_back_section_ADC; /*** *** Function to configure DMA Descriptor and *** initialize DMA Controler and channels ***/ void DMA_init(void) { /*** *** Descriptor configuration: *** - Validate the Descriptor *** - No Event out generated by the DMA *** - Channel will be disabled if it is the last *** block transfer in the transaction and block interrupt *** - 16-bit bus transfer *** - The source address is always the same *** - The destination address is incremented *** - Step size settings apply to the destination address *** - Next ADDR = ADDR + (BEATSIZE+1) * 1 *** - DMA_BITCOUNT_VALUE 16-bit transfer before waking up the core ***/ descriptor_ADC.BTCTRL.bit.VALID = 1; descriptor_ADC.BTCTRL.bit.EVOSEL = DMAC_BTCTRL_EVOSEL_DISABLE_Val; descriptor_ADC.BTCTRL.bit.BLOCKACT = DMAC_BTCTRL_BLOCKACT_INT_Val; descriptor_ADC.BTCTRL.bit.BEATSIZE = DMAC_BTCTRL_BEATSIZE_HWORD_Val; descriptor_ADC.BTCTRL.bit.SRCINC = 0; descriptor_ADC.BTCTRL.bit.DSTINC = 1; descriptor_ADC.BTCTRL.bit.STEPSEL = DMAC_BTCTRL_STEPSEL_DST_Val; descriptor_ADC.BTCTRL.bit.STEPSIZE = DMAC_BTCTRL_STEPSIZE_X1_Val; descriptor_ADC.BTCNT.reg = ADC_BUFFER_SIZE; /*** *** Set transfer size, *** source address and *** destination address: *** - source address is the ADC result register *** - destination address in the RAM *** ***/ descriptor_ADC.SRCADDR.reg = (uint32_t)(&(ADC->RESULT)); descriptor_ADC.DSTADDR.reg = (uint32_t)(&adc_data[0]+(ADC_BUFFER_SIZE *(DMAC_BTCTRL_STEPSIZE_X1_Val+1))); /*** Set next transfer descriptor address ***/ descriptor_ADC.DESCADDR.reg = (uint32_t)&descriptor_ADC; /*** *** Setup descriptor base address *** and write back section base address ***/ DMAC->BASEADDR.reg = (uint32_t)&descriptor_ADC; DMAC->WRBADDR.reg = (uint32_t)&write_back_section_ADC; /*** Enable all priority level at the same time ***/ DMAC->CTRL.reg &= ~(DMAC_CTRL_DMAENABLE) ; DMAC->CTRL.reg |= DMAC_CTRL_LVLEN(0xf); /*** *** Configure the DMAC channel (channel 0) *** Disable the channel before configuring it *** Perform a soft reset on the channel *** Channel must be able to run in STDBY *** - Priority for the channel (only one channel used) *** - DMA Trigger source is ADC result ready *** - Trig every BEAT *** - No event system action *** - No software command ***/ DMAC->CHID.reg = DMAC_CHID_ID(0x00); DMAC->CHCTRLA.reg &= ~DMAC_CHCTRLA_ENABLE; DMAC->CHCTRLA.reg = DMAC_CHCTRLA_SWRST; DMAC->CHCTRLA.reg = DMAC_CHCTRLA_RUNSTDBY; DMAC->CHCTRLB.reg = ( DMAC_CHCTRLB_LVL(0x0)| DMAC_CHCTRLB_TRIGSRC(ADC_DMAC_ID_RESRDY)| DMAC_CHCTRLB_TRIGACT_BEAT | DMAC_CHCTRLB_EVACT_NOACT | DMAC_CHCTRLB_CMD_NOACT ); /*** Enable DMA interrupts ***/ DMAC->CHID.reg = DMAC_CHID_ID(0x00); DMAC->CHINTENSET.reg = DMAC_CHINTENSET_TCMPL; NVIC_EnableIRQ(DMAC_0_IRQn); /*** Start the DMA ***/ /*** Enable DMA Channel 0 ***/ DMAC->CHID.reg = DMAC_CHID_ID(0x00); DMAC->CHCTRLA.reg |= DMAC_CHCTRLA_ENABLE; while(DMAC->CHSTATUS.bit.BUSY); /*** Start the DMA Controller ***/ DMAC->CTRL.reg |= (DMAC_CTRL_DMAENABLE); } /*** *** TC0 is used to trigger ADC Start conversionusing the Event System, *** at 1KHZ frequency based on XOSC32K. ***/ void init_TC0_1KHz(void) { /*** *** Enable XOSC32K (for RTC) ***/ OSC32KCTRL->XOSC32K.reg = OSC32KCTRL_XOSC32K_ENABLE| OSC32KCTRL_XOSC32K_XTALEN| OSC32KCTRL_XOSC32K_EN32K| OSC32KCTRL_XOSC32K_RUNSTDBY| OSC32KCTRL_XOSC32K_STARTUP(5); while(OSC32KCTRL->STATUS.bit.XOSC32KRDY==0); /*** *** Enable GCLK2 with XOSC32K as clock source *** for TC0 for ADC 1KHz Triggering ***/ GCLK->GENCTRL[2].reg = GCLK_GENCTRL_DIV(1) | CURRENT_32KOSC |GCLK_GENCTRL_GENEN; /*** (write synchronized) ***/ while((GCLK->SYNCBUSY.reg & GCLK_SYNCBUSY_GENCTRL2)); GCLK->PCHCTRL[GCLK_TC0].reg = (GCLK_PCHCTRL_CHEN|GCLK_PCHCTRL_GEN_GCLK2); /*** Disable TC0 (Enable Protected) ***/ TC0->COUNT8.CTRLA.bit.ENABLE = 0; /*** (write synchronized) ***/ while(TC0->COUNT8.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE); /*** *** TC0 in 32-bit counter with GLCK for *** synchronization and a DIV16 to get *** 1KHz event generation for the ADC. ***/ TC0->COUNT8.CTRLA.reg |= (TC_CTRLA_MODE(TC_CTRLA_MODE_COUNT8_Val) | TC_CTRLA_PRESCALER(TC_CTRLA_PRESCALER_DIV1_Val)); /*** *** TC0 Wave generator is configured *** as Match frequency generator (MFRQ) ***/ TC0->COUNT8.WAVE.reg = (TC_WAVE_WAVEGEN_MFRQ); /*** *** TC0 is an event generator *** based on compare value ***/ TC0->COUNT8.EVCTRL.reg = (TC_EVCTRL_MCEO0); /*** 32768/32 gives 1024Hz ***/ TC0->COUNT8.CC[0].reg = 32; /*** (write synchronized) ***/ while(TC0->COUNT8.SYNCBUSY.reg & TC_SYNCBUSY_CC0); /*** Enable TC0 ***/ TC0->COUNT8.CTRLA.bit.ENABLE = 1; /*** (write synchronized) ***/ while(TC0->COUNT8.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE); } /*** *** DMA Interrupt Handler function is used *** for the 1KHz buffered acquisition ***/ void DMAC_0_Handler(void) { /*** Clearing the interrupt falg and status ***/ DMAC->CHID.reg = DMAC_CHID_ID(0x00); DMAC->CHINTFLAG.reg = DMAC_CHINTFLAG_TCMPL; DMAC->INTPEND.reg = DMAC_INTPEND_ID(0x00)|DMAC_INTPEND_TCMPL; processAdcBuffer(); // !!! This function is not provided in the example!!! } /*** *** Initialize the ADC and start collecting at 1 kHz buffered acquisition, *** meaning, the ADC ISR is called at the end of the buffer fill. *** *** NOTE: 1KHz may be 1024 Hz or 1000 Hz depending on which is easier, as we *** don't want to penalize an MCU for requiring much higher speed clocks *** to subdivide down to one or the other. ***/ void ADC_Init_1kHz_buf(void) { /*DMAC Channel Description */ DMA_init(); /*** *** Configure ADC Start of conversion *** as user and RTC as a trigger generator: *** ADC Start user is linked to the Channel 0. *** NO detection because of the asynchronous path *** EVSYS Channel 0 can run in STBY (if running in low power) *** Channel 0 Asynchronous path *** Channel 0 TC0 Match/clear trigger adc) ***/ EVSYS->USER[EVSYS_ID_USER_ADC_START].reg= EVSYS_USER_CHANNEL(EVSYS_USER_CHANNEL_0); EVSYS->Channel[0].CHANNEL.reg = ( EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT| EVSYS_CHANNEL_RUNSTDBY| EVSYS_CHANNEL_ONDEMAND| EVSYS_CHANNEL_PATH_ASYNCHRONOUS| EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_TC0_MC0)); /*** Enable ADC ***/ ADC->CTRLA.bit.ENABLE = 0x1; /*** (write synchronized) ***/ while(ADC->SYNCBUSY.reg & ADC_SYNCBUSY_ENABLE); /*** Enable TC0 ***/ TC0->COUNT8.CTRLA.bit.ENABLE = 1; /*** (write synchronized) ***/ while(TC0->COUNT16.SYNCBUSY.reg & TC_SYNCBUSY_ENABLE); }