3 Storing ADC Conversion Results by CPU

If the ADC conversion rate is 40 MSPS and the CPU clock is 200 MHz, the CPU has only 200 MHz / 40 MSPS = 5 instruction cycles to move one ADC result. The code in Capturing ADC Data at 40 MSPS Conversion Rate Using CPU stores 40 MSPS ADC data in RAM using the CPU. For robust timing, the CPU and ADC are clocked from the same PLL. During ADC conversions, the CPU is 100% utilized and cannot perform other tasks. Therefore, interrupts should be disabled during the ADC conversion period. The example code should be executed on the dsPIC33A Curiosity Platform Development Board (EV74H48A) with the dsPIC33AK512MPS512 GP DIM (EV80L65A).

Capturing ADC Data at 40 MSPS Conversion Rate Using CPU

// WDT is disabled
#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 = 4; // VCO divider 1:8 = 200MHz
    
    PLL1DIVbits.PLLFBDIV = 200; // VCO = 1600 MHz
    PLL1DIVbits.PLLPRE = 1;
    PLL1DIVbits.POSTDIV1 = 5; // PLL1 = 1600MHz:5 = 320 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 320 MHz or 40MSPS
    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    
    _RP113R = 19; // map UART1 TX to RP113/RH0 pin (board PKOB UART)
    U1BRG = FCY/2/UART_BAUD/4-1;    
    U1CONbits.BRGS = 1;   
    U1CONbits.TXEN = 1;
    U1CONbits.ON = 1;     
    
    // initialize ADC
    AD5CH0CON1bits.TRG1SRC = 1; // first trigger from software
    AD5CH0CON1bits.TRG2SRC = 2; // repeat conversions back-to-back
    AD5CH0CON1bits.PINSEL = 1; // convert AD5AN1/RA1 pin connected to J4 on board
    AD5CH0CON1bits.SAMC = 0; // 0.5 TAD    
    AD5CH0CON1bits.MODE = 2; // counter mode
    AD5CH0CNT = BUFFER_SIZE; // number of back-to-back conversions 
    AD5CONbits.ON = 1; // enable ADC
    while(AD5CONbits.ADRDY == 0); // wait when it is ready       

    while(1){        
        // capture 40MSPS data
        INTCON1bits.GIE = 0; // disable interrupts
        asm volatile(
            // buffer length
            "mov.l #%1, w1 \n"
            // load buffer address
            "mov.l #%0, w2 \n"
            // load ADC result address
            "mov.l #AD5CH0RES, w3 \n"        
            // clear ready flag 
            "mov.l AD5CH0RES, w0 \n"
            // start conversions
            "bset.l AD5SWTRG, #0 \n"         
            // wait for start(first result)
            "1: \n" 
            "btst AD5RSTAT, #0 \n"
            "bra z, 1b \n"
            // grab all data (200MHz CPU : 40MSPS = 5 instructions per sample)
            "2: \n"
            "mov.w [w3], [w2++]\n"
            "nop \n"
            "nop \n"
            "dtb w1, 2b \n"       
        ::"i"((unsigned long)buffer),"i"(BUFFER_SIZE):"w0","w1","w2","w3");
        INTCON1bits.GIE = 1; // enable interrupts       
        
        // 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;
}