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;
}
