/*****************************************************************************/
/* John Wilkes              AdcContinuousDmaEpwm.c           1 December 2016 */
/*                                                                           */
/* This file contains a working example of how to create continuous          */
/* conversions from the ADC modules, triggered by an EPWM event and read     */
/* using DMA.                                                                */
/*                                                                           */
/* The code is based upon the examples supplied in controlSUITE,             */
/* specifically the 'adc_soc_continuous', 'adc_soc_epwm' and                 */
/* 'dma_gsram_transfer' examples. This example fits into the same structure  */
/* as those and uses the same header files, library code and linker files.   */
/* The code has been tested on a 28377S launch pad and should also run on a  */
/* 28379D launch pad with no changes.                                        */
/*                                                                           */
/* The specific problem solved is this: Upon an ePWM event, two ADC channels */
/* (A3 and B3) are converted simultaneously at the maximum sample rate for   */
/* 1024 samples. The resulting data end up in RAM at the end.                */
/*                                                                           */
/* The trigger event used is the timer equals zero event from EPWM module 2. */
/* This event is also brought out to an IO pin so that it can be monitored   */
/* externally if required. The output could also be used as a source for one */
/* of the ADC inputs (careful: need to pot down the voltage from the ePWM    */
/* pin as this exceeds the maximum ADC input voltage). EPWM module 2 is used */
/* because it is accessible on both target launch pads. Which, by the way,   */
/* is also the reason ADC channels A3 and B3 where chosen.                   */
/*                                                                           */
/* DMA is used to move data from the ADC result registers and into RAM.      */
/* GSRAM is used for this as LSRAM cannot be accessed by the DMA engine. A   */
/* DMA channel is required for each ADC channel.                             */
/*****************************************************************************/

/***********/
/* INCLUDE */
/***********/
#include "F28x_Project.h"

/**********/
/* LABELS */
/**********/

/* Extensions to the TI supplied register defines: Although the unioned bit  */
/* field approach used in the TI header files is useful, it can be           */
/* inefficient in some cases. Particularly when writing to a register that   */
/* contains a combination of read only and/or write-one-to-clear bits. The   */
/* bit field approach will result in the compiler generating a read,         */
/* followed by a bitwise OR and then, finally, a write. Since writing zero   */
/* to bits in this kind of register has no effect, it is quicker to do a     */
/* write to the whole register with relevant bits set. Hence I have defined  */
/* labels for the various bits I have used in the program. This is not an    */
/* complete list, only the registers I have used.                            */
#define DMA_RUN 0x1
#define DMA_HALT 0x2
#define DMA_SOFTRESET 0x4
#define DMA_PERINTFRC 0x8
#define DMA_PERINTCLR 0x10

#define ADC_INT1CLR 0x1
#define ADC_INT2CLR 0x2
#define ADC_INT3CLR 0x4
#define ADC_INT4CLR 0x8

#define ADC_TRIGSEL_EPWM1_SOCA 0x5
#define ADC_TRIGSEL_EPWM2_SOCA 0x7
#define ADC_TRIGSEL_EPWM7_SOCA 0x11

#define EPWM_ETINTCLR 0x1
#define EPWM_ETSOCACLR 0x4
#define EPWM_ETSOCBCLR 0x8

/* Size of results buffer must be a multiple of sixteen */
#define RESULTS_BUFFER_SIZE 1024

/***********************/
/* FUNCTION PROTOTYPES */
/***********************/
__interrupt void adcIsr(void);
__interrupt void dmaIsr(void);
void ePwmSetup (volatile struct EPWM_REGS * pwmRegs);
void configureADC(void);
void setupADCContinuous(volatile struct ADC_REGS * adcRegs, Uint16 channel);
void dmaInit();

/***********/
/* GLOBALS */
/***********/

/* The results buffer must be located in GSRAM as LSRAM is not accessible by */
/* the DMA engine                                                            */
#pragma DATA_SECTION(adcData0, "ramgs0");
#pragma DATA_SECTION(adcData1, "ramgs0");
Uint16 adcData0[RESULTS_BUFFER_SIZE];
Uint16 adcData1[RESULTS_BUFFER_SIZE];

/********/
/* CODE */
/********/

/*****************/
/* PROGRAM ENTRY */
/*****************/
void main(void)
{
	/**************************************/
	/* STEP 1 - INITIALISE SYSTEM CONTROL */
	/**************************************/

	/* PLL, WatchDog, enable Peripheral Clocks. This example function is */
	/* found in the F2837xX_SysCtrl.c file.                              */
	InitSysCtrl();

    /* ALSO: Change the LSPCLK divider to /1 so that LSPCLK is 200MHz */
    EALLOW;
    ClkCfgRegs.LOSPCP.bit.LSPCLKDIV = 0;
    EDIS;


    /*****************/
    /* STEP 2 - GPIO */
    /*****************/

    /* Expose ePWM 2A on GPIO pin 2. The GPIO setup functions are found in */
    /* the F2837xX_Gpio.c file                                             */
	GPIO_SetupPinMux(2, GPIO_MUX_CPU1, 1);

    /* Though not needed externally, we set up GPIO0 as an output and drive  */
	/* it low. This pin drives the EPWMxSYNC inputs to the EPWM modules and, */
	/* as we will enable the initialisation of the ET counter (so we can     */
	/* manually reset it), we do not want the SYNCI signal to also reset it  */
	/* - hence connecting SYNCI to a permanent low. If the application needs */
	/* to use the GPIO0 pin, then the SYNCI signal can be derived from       */
	/* another (unused) pin using the xbar registers.                        */
	GPIO_SetupPinMux(0, GPIO_MUX_CPU1, 0);
	GPIO_SetupPinOptions(0, GPIO_OUTPUT, 0);
	GPIO_WritePin(0, 0);

	/***********************/
	/* STEP 3 - INTERRUPTS */
	/***********************/

	/* Clear all interrupts and initialise PIE vector table */

	/* Disable CPU interrupts */
    DINT;

	/* Initialise the PIE control registers to their default state. The    */
    /* default state is all PIE interrupts disabled and flags are cleared. */
	/* This function is found in the F2837xX_PieCtrl.c file.               */
    InitPieCtrl();

    /* Disable CPU interrupts and clear all CPU interrupt flags */
    IER = 0x0000;
    IFR = 0x0000;

	/* Initialise the PIE vector table with pointers to the shell Interrupt */
    /* Service Routines (ISR). This will populate the entire table, even if */
    /* the interrupt is not used in this example. This is useful for debug  */
    /* purposes. The shell ISR routines are found in F2837xX_DefaultIsr.c.  */
    /* This function is found in F2837xX_PieVect.c.                         */
    InitPieVectTable();

    /* Now set ISRs used by this example */
    EALLOW;

    /* ISR for ADCA INT1 - occurs after first conversion */
    PieVectTable.ADCA1_INT = &adcIsr;

    /* ISR for DMA ch1 - occurs when DMA transfer is complete */
    PieVectTable.DMA_CH1_INT = &dmaIsr;

    EDIS;

    /* Enable specific CPU interrupts: INT1 for ADCs and INT7 for DMA */
	IER |= M_INT1;
	IER |= M_INT7;

	/* Enable specific PIE interrupts */

	/* ADCA INT1: Group 1, interrupt 1 */
	PieCtrlRegs.PIEIER1.bit.INTx1 = 1;

	/* DMA interrupt: Group 7, interrupt 1 */
	PieCtrlRegs.PIEIER7.bit.INTx1 = 1;

	/***********************************/
	/* STEP 4 - EXAMPLE SPECIFIC SETUP */
	/***********************************/

	/**********/
	/**********/
	/** EPWM **/
	/**********/
	/**********/

	/* Stop the EPWM clock */
	EALLOW;
	CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 0;
	EDIS;

	/* Call the set up function for ePWM 2. Function is located later in */
	/* this file                                                         */
	ePwmSetup(&EPwm2Regs);

	/* Start the EPWM clock */
	EALLOW;
	CpuSysRegs.PCLKCR0.bit.TBCLKSYNC = 1;
	EDIS;

	/*********/
	/*********/
	/** ADC **/
	/*********/
	/*********/

	/* ADC setup functions are located later in this file. Using channels A3 */
	/* and B3 as these are accessible on both 28377S and 28379D launch pads  */
	configureADC();
	setupADCContinuous(&AdcaRegs, 3); /* A3 */
	setupADCContinuous(&AdcbRegs, 3); /* B3 */

	/*********/
	/*********/
	/** DMA **/
	/*********/
	/*********/

	/* DMA setup function is located later in this file */
	dmaInit();

	/*************************************/
	/* STEP 5 - ENABLE GLOBAL INTERRUPTS */
	/*************************************/
	EINT;  /* Enable Global interrupt INTM           */
	ERTM;  /* Enable Global real time interrupt DBGM */

	/*--------------------------- SETUP COMPLETE ----------------------------*/

	/* That's the set up done - now we do a grab of the data. This block of */
	/* code could be re-called as part of the application to grab more data */

	/* Some of the registers here are EALLOW, so just set it for the */
	/* duration                                                      */
	EALLOW;

	/* Reset the sequence by clearing all pending interrupt flags */
	DmaRegs.CH1.CONTROL.all = DMA_PERINTCLR;
	DmaRegs.CH2.CONTROL.all = DMA_PERINTCLR;
	AdcaRegs.ADCINTFLGCLR.all =	ADC_INT1CLR | ADC_INT2CLR;
	AdcbRegs.ADCINTFLGCLR.all =	ADC_INT1CLR | ADC_INT2CLR;
	EPwm2Regs.ETCNTINITCTL.bit.SOCAINITFRC = 1;
	EPwm2Regs.ETCLR.all = EPWM_ETSOCACLR;
    PieCtrlRegs.PIEIFR7.bit.INTx1 = 0;
    PieCtrlRegs.PIEIFR1.bit.INTx1 = 0;

	/* Enable continuous operation by setting the last SOC to re-trigger the */
    /* first                                                                 */
	AdcaRegs.ADCINTSOCSEL1.bit.SOC0 = 2;
	AdcbRegs.ADCINTSOCSEL1.bit.SOC0 = 2;

	/* Enable the interrupt from the very first conversion: the ISR for this */
	/* (adcIsr) will remove the ePWM trigger and disable itself.             */
	PieCtrlRegs.PIEIER1.bit.INTx1 = 1;

    /* Start DMA - note not using the library function to do this: it does  */
	/* an EDIS!                                                             */
    DmaRegs.CH1.CONTROL.all = DMA_RUN;
    DmaRegs.CH2.CONTROL.all = DMA_RUN;

    /* Finally, enable the SOCA trigger from EPWM. This will kick off */
    /* conversions at the next ePWM event                             */
    EPwm2Regs.ETSEL.bit.SOCAEN = 1;

	/* The rest of the functionality of the ADC grab is done in the DMA and */
	/* ADC ISRs: adcIsr and dmaIsr. Alternatively: it can be done without   */
	/* using interrupts by the commented out code below. This reduces the   */
	/* risk of nasty interrupt timing issues, but will waste CPU time that  */
	/* may be better spent elsewhere. For 1024 samples, the while loops     */
	/* below will stall for about 300us                                     */

//	/* Wait for first conversion to complete */
//	while(0 == AdcaRegs.ADCINTFLG.bit.ADCINT1) ;
//
//	/* Remove EPWM trigger */
//	EPwm2Regs.ETSEL.bit.SOCAEN = 0;
//
//	/* Wait for transfer complete */
//	while(0 == PieCtrlRegs.PIEIFR7.bit.INTx1) ;
//
//	/* Stop the ADC by removing the trigger for SOC0 */
//	AdcaRegs.ADCINTSOCSEL1.bit.SOC0 = 0;
//	AdcbRegs.ADCINTSOCSEL1.bit.SOC0 = 0;

    EDIS;

	/* Stall */
	for (;;) {
		asm(" NOP");
	}

}

/********************************/
/********************************/
/** INTERRUPT SERVICE ROUTINES **/
/********************************/
/********************************/

/****************************************************************************/
/* adcIsr                                                               ISR */
/* This is called after the very first conversion and will disable the EPWM */
/* SOC to avoid re-triggering problems.                                     */
/****************************************************************************/
#pragma CODE_SECTION(adcIsr, ".TI.ramfunc");
__interrupt void adcIsr(void)
{
	/* Remove EPWM trigger */
    EPwm2Regs.ETSEL.bit.SOCAEN = 0;

	/* Disable this interrupt from happening again */
	PieCtrlRegs.PIEIER1.bit.INTx1 = 0;

	/* Acknowledge */
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP1;
}

/**********************************************************************/
/* dmaIsr                                                         ISR */
/* This is called at the end of the DMA transfer, the conversions are */
/* stopped by removing the trigger of the first SOC from the last.    */
/**********************************************************************/
#pragma CODE_SECTION(dmaIsr, ".TI.ramfunc");
__interrupt void dmaIsr(void)
{
	/* Stop the ADC by removing the trigger for SOC0 */
	EALLOW;
	AdcaRegs.ADCINTSOCSEL1.bit.SOC0 = 0;
	AdcbRegs.ADCINTSOCSEL1.bit.SOC0 = 0;
	EDIS;

	/* Acknowledge */
	PieCtrlRegs.PIEACK.all = PIEACK_GROUP7;
}

/********************************/
/********************************/
/** PERIPHERAL SETUP FUNCTIONS **/
/********************************/
/********************************/

/****************************************************************************/
/* ePwmSetup                                                       FUNCTION */
/* Sets up the specified ePWM module so that the A output has a period of   */
/* 40us with a 50% duty. The SOCA signal is coincident with the rising edge */
/* of this.                                                                 */
/****************************************************************************/
void ePwmSetup (volatile struct EPWM_REGS * pwmRegs)
{
	/* Make the timer count up with a period of 40us */
	pwmRegs->TBCTL.all = 0x0000;
	pwmRegs->TBPRD = 4000;

	/* Set the A output on zero and reset on CMPA */
	pwmRegs->AQCTLA.bit.ZRO = AQ_SET;
	pwmRegs->AQCTLA.bit.CAU = AQ_CLEAR;

	/* Set CMPA to 20us to get a 50% duty */
	pwmRegs->CMPA.bit.CMPA = 2000;

	/* Start ADC when timer equals zero (note: don't enable yet) */
	pwmRegs->ETSEL.bit.SOCASEL = ET_CTR_ZERO;
	pwmRegs->ETPS.bit.SOCAPRD =  ET_1ST;

	/* Enable initialisation of the SOCA event counter - since we are     */
	/* disabling the ETSEL.SOCAEN bit, we need a way to reset the SOCACNT */
	/* Hence, enable the counter initialise control                       */
    pwmRegs->ETCNTINITCTL.bit.SOCAINITEN = 1;
}

/**************************************************************************/
/* configureADC                                                  FUNCTION */
/* Write ADC configurations and power up the ADC for both ADC A and ADC B */
/**************************************************************************/
void configureADC(void)
{
    EALLOW;

    /* Set pre-scale for 50MHz operation (divide SYSCLK by 4) */
    AdcaRegs.ADCCTL2.bit.PRESCALE = 6;
    AdcbRegs.ADCCTL2.bit.PRESCALE = 6;

    /* Set Mode */
    AdcSetMode(ADC_ADCA, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);
    AdcSetMode(ADC_ADCB, ADC_RESOLUTION_12BIT, ADC_SIGNALMODE_SINGLE);

    /* Late interrupt */
    AdcaRegs.ADCCTL1.bit.INTPULSEPOS = 1;
    AdcbRegs.ADCCTL1.bit.INTPULSEPOS = 1;

    /* Power up */
    AdcaRegs.ADCCTL1.bit.ADCPWDNZ = 1;
    AdcbRegs.ADCCTL1.bit.ADCPWDNZ = 1;

    /* Delay for 1ms to allow ADC time to power up */
    DELAY_US(1000);

    EDIS;
}

/****************************************************************************/
/* setupADCContinuous                                              FUNCTION */
/* Set up all the SOCs of the specified ADC module to convert the specified */
/* channel at maximum rate (i.e. minimum sample window). SOC0 will trigger  */
/* from an EPWM SOC and all other SOCs trigger from the EOC of SOC0 - the   */
/* round robin mechanism ensures they convert in order. Prior to enabling   */
/* the trigger, SOC0 should be set to also trigger from SOC15 EOC (INT2)    */
/* which will cause continuous converting until this is removed.            */
/****************************************************************************/
void setupADCContinuous(volatile struct ADC_REGS * adcRegs, Uint16 channel)
{
	/* Use minimum acquisition window for 12 bit resolution */
	/* Sample window is acqps + 1 SYSCLK cycles             */
    Uint16 acqps = 14;

    /* Set all SOCs to convert the same channel (wot no loop?) */
    EALLOW;
    adcRegs->ADCSOC0CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC1CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC2CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC3CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC4CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC5CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC6CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC7CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC8CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC9CTL.bit.CHSEL  = channel;
    adcRegs->ADCSOC10CTL.bit.CHSEL = channel;
    adcRegs->ADCSOC11CTL.bit.CHSEL = channel;
    adcRegs->ADCSOC12CTL.bit.CHSEL = channel;
    adcRegs->ADCSOC13CTL.bit.CHSEL = channel;
    adcRegs->ADCSOC14CTL.bit.CHSEL = channel;
    adcRegs->ADCSOC15CTL.bit.CHSEL = channel;

    /* Set all SOCs to minimum acquisition window */
    adcRegs->ADCSOC0CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC1CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC2CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC3CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC4CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC5CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC6CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC7CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC8CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC9CTL.bit.ACQPS  = acqps;
    adcRegs->ADCSOC10CTL.bit.ACQPS = acqps;
    adcRegs->ADCSOC11CTL.bit.ACQPS = acqps;
    adcRegs->ADCSOC12CTL.bit.ACQPS = acqps;
    adcRegs->ADCSOC13CTL.bit.ACQPS = acqps;
    adcRegs->ADCSOC14CTL.bit.ACQPS = acqps;
    adcRegs->ADCSOC15CTL.bit.ACQPS = acqps;

    /* Trigger SCO0 from EPWM */
    adcRegs->ADCSOC0CTL.bit.TRIGSEL = ADC_TRIGSEL_EPWM2_SOCA;

    /* Trigger all other SOCs from INT1 (EOC on SOC0) */
    adcRegs->ADCINTSOCSEL1.bit.SOC1 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC2 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC3 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC4 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC5 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC6 = 1;
    adcRegs->ADCINTSOCSEL1.bit.SOC7 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC8 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC9 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC10 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC11 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC12 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC13 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC14 = 1;
    adcRegs->ADCINTSOCSEL2.bit.SOC15 = 1;

    /* Enable interrupts 1 and 2 */
    adcRegs->ADCINTSEL1N2.bit.INT1E = 1; // Enable INT1 flag
    adcRegs->ADCINTSEL1N2.bit.INT2E = 1; // Enable INT2 flag
    adcRegs->ADCINTSEL3N4.bit.INT3E = 0;
    adcRegs->ADCINTSEL3N4.bit.INT4E = 0;

    /* Enable continuous mode on the active interrupts - otherwise           */
    /* conversions will stall waiting for acknowledgement that never happens */
    adcRegs->ADCINTSEL1N2.bit.INT1CONT = 1;
    adcRegs->ADCINTSEL1N2.bit.INT2CONT = 1;

    /* Set source of interrupts */
    adcRegs->ADCINTSEL1N2.bit.INT1SEL = 0;  /* end of SOC0  */
    adcRegs->ADCINTSEL1N2.bit.INT2SEL = 15; /* end of SOC15 */

    EDIS;
}

/*****************************************************************************/
/* dmaInit                                                          FUNCTION */
/* This sets up the DMA to scoop up data from the ADC result registers and   */
/* throw it into the result arrays in RAM. Each burst is triggered from the  */
/* EOC of ADC SOC15 and will move all sixteen results into RAM. The bursts   */
/* repeat until the results arrays are full. The setup functions used here   */
/* are found in F2837xX_DMA.c.                                               */
/*                                                                           */
/* NOTE ABOUT 16 BIT and 32 BIT TRANSFER SIZES:                              */
/* This is not documented at all well (indeed, at all as far as I can see),  */
/* The following is the result of trial and error and observations on a real */
/* device:                                                                   */
/*                                                                           */
/* When using 16 bit transfer size:                                          */
/*  * The burst size is one less than the number of 16 bit words that need   */
/*    to be transferred in the burst. E.g. for 8 16 bit words the burst size */
/*    needs to be set to 7.                                                  */
/*  * The burst step sizes (src and dst) need to reflect the number to add   */
/*    to the address pointer after each word is transferred. E.g If the      */
/*    words transferred in a burst are from contiguous addresses, the step   */
/*    size is 1.                                                             */
/*  * At the end of a burst, the address pointer is still pointing at the    */
/*    last word that was transferred. I.e. the last burst step has NOT been  */
/*    applied. The transfer step needs to take this into account. E.g. If    */
/*    the burst was 8 words long (i.e. burst size = 7) and the next burst    */
/*    needs to go back and transfer the same block of memory again, the      */
/*    transfer step should be -7. If the next burst needs to continue on     */
/*    from the previous one, then the transfer step size should be 1.        */
/*                                                                           */
/* This is subtly different to when using 32 bit transfer size:              */
/*  * The burst size is the number of 16 bit words that need to be           */
/*    transferred - this is the one thing that the documentation is clear    */
/*    about, but notice that it is not less one for 32 bit transfers. E.g.   */
/*    for 4 32 bit words the burst size needs to be set to 8.                */
/*  * The burst step sizes (src and dst) need to reflect the number to add   */
/*    to the address pointer after each word is transferred. This is the     */
/*    same rule as in 16 bit transfer size, but remember each 32 bit         */
/*    transfer consumes 2 pointer addresses. E.g If the words transferred in */
/*    a burst are from contiguous addresses, the step size is 2.             */
/*  * At the end of a burst, the address pointer is pointing at the next     */
/*    word to transfer. I.e. the last burst step HAS been applied. The       */
/*    transfer step needs to take this into account. E.g. If the burst was 4 */
/*    32 words long (i.e. burst size = 8) and the next burst needs to go     */
/*    back and transfer the same block of memory again, the transfer step    */
/*    should be -8. If the next burst needs to continue on from the previous */
/*    one, then the transfer step size should be 0.                          */
/*                                                                           */
/* In both cases the transfer size is one less than the number of bursts in  */
/* the transfer. I.e. if there are to be 64 bursts, the transfer size should */
/* be 63.                                                                    */
/*                                                                           */
/* Haven't investigated the effect on wrap operation as I haven't had cause  */
/* to use it!                                                                */
/*****************************************************************************/
void dmaInit()
{
    /* Initialise DMA */
    DMAInitialize();

    /* DMA set up for first ADC */
    DMACH1AddrConfig(adcData0, &AdcaResultRegs.ADCRESULT0);
    /* This commented out bit is for 16 bit transfers - see above */
    //DMACH1BurstConfig(15, 1, 1);
    //DMACH1TransferConfig((RESULTS_BUFFER_SIZE>>4)-1,-15,1);
    DMACH1BurstConfig(16, 2, 2);
    DMACH1TransferConfig((RESULTS_BUFFER_SIZE>>4)-1, -16, 0);
    DMACH1ModeConfig(
		DMA_ADCAINT2,
		PERINT_ENABLE,
		ONESHOT_DISABLE,
		CONT_DISABLE,
		SYNC_DISABLE,
		SYNC_SRC,
		OVRFLOW_DISABLE,
		//SIXTEEN_BIT,
		THIRTYTWO_BIT,
		CHINT_END,
		CHINT_ENABLE
	);

    /* DMA set up for second ADC */
    DMACH2AddrConfig(adcData1, &AdcbResultRegs.ADCRESULT0);
    /* This commented out bit is for 16 bit transfers - see above */
    //DMACH2BurstConfig(15, 1, 1);
    //DMACH2TransferConfig((RESULTS_BUFFER_SIZE>>4)-1,-15,1);
    DMACH2BurstConfig(16, 2, 2);
    DMACH2TransferConfig((RESULTS_BUFFER_SIZE>>4)-1, -16, 0);
    DMACH2ModeConfig(
		DMA_ADCAINT2,
		PERINT_ENABLE,
		ONESHOT_DISABLE,
		CONT_DISABLE,
		SYNC_DISABLE,
		SYNC_SRC,
		OVRFLOW_DISABLE,
		//SIXTEEN_BIT,
		THIRTYTWO_BIT,
		CHINT_END,
		CHINT_DISABLE
	);
}
