/***
 *** 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);    
}