2 ADC Data Transfers by DMA

The bandwidth for all DMA channels is limited to six instruction cycles. Therefore, if the CPU clock is 200 MHz, all DMA channels cannot transfer data words faster than 200 MHz / 6 = 33.3 MHz (word transfers per second). The transferred word size does not matter, whether it is one byte, two bytes or four bytes. To move ADC data using DMA, the ADC conversion rate should be lower than 33 MSPS. Since all DMA channels share the same bandwidth, only one DMA channel should be used (active) in the application during ADC data transfers. In Capturing ADC Data Using DMA at 33 MSPS Conversion Rate, DMA channel 0 moves 33 MSPS ADC data to a RAM buffer. For robust timing, the CPU/DMA and ADC are clocked from the same PLL. The example code should be executed on the dsPIC33A Curiosity Platform Development Board (EV74H48A) with the dsPIC33AK256MPS306 GP DIM (EV02E57A).

Capturing ADC Data Using DMA at 33 MSPS Conversion Rate

// disable WDT
#pragma config FWDT_WDTEN = SW

#include <xc.h>

#define BUFFER_SIZE 800

unsigned short buffer[BUFFER_SIZE];

int main(){
    // initialize clock
    OSCCTRLbits.POSCEN = 1; // POSC enable
    OSCCFGbits.POSCMD = 0; // EC POSC clock on OSCI pin connected to 8MHz MEMS
    
    PLL1CONbits.ON = 1;
    OSCCTRLbits.PLL1EN = 1;
    while(OSCCTRLbits.PLL1RDY == 0);
    PLL1CONbits.FSCMEN = 0; // disable clock fail monitor
        
    VCO1DIVbits.INTDIV = 2; // VCO divider 1:4 = 200MHz
    
    PLL1DIVbits.PLLFBDIV = 100; // VCO = 800 MHz
    PLL1DIVbits.PLLPRE = 1;
    PLL1DIVbits.POSTDIV1 = 3; // PLL1 = 800MHz:3 = 266.6 MHz for ADC
    PLL1DIVbits.POSTDIV2 = 1;
    
    PLL1CONbits.DIVSWEN = 1;
    while(PLL1CONbits.DIVSWEN == 1);    
    
    PLL1CONbits.NOSC = 3; // clock from POSC EC (8MHz MEMS on board)

    PLL1CONbits.OSWEN = 1;
    while(PLL1CONbits.OSWEN == 1);        

    PLL1CONbits.FOUTSWEN = 1;
    while(PLL1CONbits.FOUTSWEN == 1);
    PLL1CONbits.PLLSWEN = 1;
    while(PLL1CONbits.PLLSWEN == 1);
    
    while(PLL1CONbits.CLKRDY == 0);   
       
    // CPU clock 200 MHz (Generator 1)
    CLK1CONbits.ON = 1;
    CLK1CONbits.NOSC = 7; // PLL1 VCO divider
    CLK1CONbits.OSWEN = 1;
    while(CLK1CONbits.OSWEN == 1); 
    while(CLK1CONbits.CLKRDY == 0);
    
    // ADC input clock (Generator 6)
    CLK6CONbits.ON = 1;
    CLK6CONbits.NOSC = 5; // PLL1 266.66 MHz or 33.3 MSPS
    CLK6CONbits.OSWEN = 1;
    while(CLK6CONbits.OSWEN == 1); 
    while(CLK6CONbits.CLKRDY == 0);

    // initialize UART
#define UART_BAUD (230400UL) // UART baud rate
#define FCY (200000000UL) // FCY frequencyu in Hz 
    _RP49R = 10;// map UART1 TX to RP49/RD0 pin (board PKOB UART)
    U1BRG = FCY/2/UART_BAUD/4-1;    
    U1CONbits.BRGS = 1;   
    U1CONbits.TXEN = 1;
    U1CONbits.ON = 1;   
    
    // DMA channel 0 moves 33MSPS data from ADC to RAM    
    DMACONbits.ON = 1;       
    // DMA accesses entire RAM
    DMALOW = 0;
    DMAHIGH = -1;
    DMA0CHbits.SIZE = 1; // 16-bit
    DMA0CHbits.DONEEN = 1; // interrupt enable
    DMA0CHbits.TRMODE = 1; // repeated one shot
    DMA0CHbits.DAMODE = 1; // increment buffer address
    DMA0CHbits.SAMODE = 0; // same ADC data register
    DMA0CHbits.RELOADC = 1; // reload counter
    DMA0CHbits.RELOADD = 1; // reload destination address
    DMA0SELbits.CHSEL = 47; // trigger from ADC 1 channel 0 (option 47)
    DMA0CHbits.CHEN = 1;    
    DMA0SRC = (unsigned long)&AD1CH0RES; // get data from ADC1 channel #0
    DMA0DST = (unsigned long)buffer; // store  ADC1 results in a buffer
    DMA0CNT = BUFFER_SIZE;    
   
    
    AD1CH0CON1bits.PINSEL = 1; // convert AD1AN1/RA4 pin connected to J4 on board
    AD1CH0CON1bits.SAMC = 0; // 0.5 TAD
    AD1CH0CON1bits.IRQSEL = 0; // DMA trigger each conversion
    AD1CH0CON1bits.TRG1SRC = 1; // first trigger from software
    AD1CH0CON1bits.TRG2SRC = 2; // repeat conversions back-to-back
    AD1CH0CON1bits.MODE = 2; // counter mode
    AD1CH0CNT = BUFFER_SIZE; // number of back-to-back conversions
            
    AD1CONbits.ON = 1; // enable ADC
    while(AD1CONbits.ADRDY == 0); // wait when it is ready    
    
    while(1){
        
        // start conversions
        _DMA0IF = 0;        
        AD1SWTRGbits.CH0TRG = 1;
        
        while(_DMA0IF == 0); // wait when done
        
		// output to UART
        unsigned short count = BUFFER_SIZE; 
        unsigned short* pointer = buffer;
   
        while(count--){
            while(U1STATbits.TXBE == 0); // wait for a free space in FIFO
            U1TXB = 0x55; // MPLAB Data Visualizer start token
            U1TXB = (unsigned char)*pointer; // low byte of UINT16
            U1TXB = (unsigned char)(*pointer>>8); // high byte of UINT16    
            U1TXB = 0xaa;// MPLAB Data Visualizer end token
            pointer++;
        }
        
    }
    return 1;
}