// ********************************************************************************************* // PIC32MKxxMCFxx 20msps Interleaved ADC sample code example // // SUMMARY: // TARGET CPU: PIC32MKxxMCFxx // OSC TYPE = 24 MHz Clock oscillator (EC Mode) // SYSCLK=120 MHz // PBCLK = 120 MHz // ADC TAD = SYSCLK / 2 = 60 MHz = 16.666667ns // ADC SAMC=3 TAD // ADC SAMPLE RATE = 20msps // INTERLEAVED ADCS // ADC 0: 12bit mode, ADCTRG1 PWM Generator 1 trigger PTMR time base // ADC 1: 12bit mode, ADCTRG1 PWM Generator 2 trigger PTMR time base // ADC 2: 12bit mode, ADCTRG1 PWM Generator 3 trigger PTMR time base // ADC 3: 12bit mode, ADCTRG1 PWM Generator 4 trigger PTMR time base // ADC 4: 12bit mode, ADCTRG2 PWM Generator 5 trigger PTMR time base // ADC 5: 12bit mode, ADCTRG2 PWM Generator 6 trigger PTMR time base // // interleaved ADC0/1/2/3/4/5 with only AN0/AN2/AN5 inputs, (20 msps combined throughput rate) // // NOTE: // Motor Control PWM peripherals and hence PWM triggers only exists on // PIC32MKxxMCxx variants. If PWM used as ADC trigger sources then PWM motor control // functionality would be excluded. The two independent uses are exclusive of each other. // ********************************************************************************************* // NOTE: (FYI only) // A user could configure the PIC32MK to have multiple sets of // interleaved ADC's to measure multiple different analog input streams like: // ----------------------------- EXAMPLE 1 CONFIGURATION ----------------------------- // 1) ADC0-ADC1 (2) ADCs with triggers OC1 and TMR5 @ 7.5msps(max) combined // 2) ADC2-ADC3 (2) ADCs with triggers OC4 and TMR3 @ 7.5msps(max) combined // 3) ADC4-ADC5 (2) ADCs PWM Generator 1 & PWM Generator 2 trigger PTMR time base @ 7.5msps(max) // ----------------------------- EXAMPLE 2 CONFIGURATION ----------------------------- // 4) ADC0-ADC2 (3) ADCs with triggers OC1, OC2 and TMR3 @ 10msps(max) combined // 5) ADC3-ADC5 (3) ADCs with PWM Generator 1 / 2 / 3 trigger PTMR time base @ 10msps(max) combined // ----------------------------- EXAMPLE 3 CONFIGURATION ----------------------------- // 6) ADC0-ADC1 (2) ADCs with triggers OC1 and TMR3 @ 7.5msps(max) combined // 7) ADC2-ADC5 (4) ADC’s with PWM Generator 1 / 2 / 3 / 4 trigger PTMR time base @ 15msps(max) combined // ----------------------------- EXAMPLE 4 CONFIGURATION ----------------------------- // 8) ADC0-ADC5 (6) ADC’s with PWM Generator 1/2/3/4/5/6 trigger PTMR time base @ 20msps(max) combined // ********************************************************************************************* //USER NOTE: // Although the number of interleaved ADC's the user wishes to use is selectable, it is required // "FOR THIS CODE ONLY" that those ADCx modules be sequential starting from AN0 to the last sequential // ANx to be used for interleaving. In different code than below, any combination of interleaved ADC are allowed // // **************************************************************************** // F I L E I N C L U D E S // **************************************************************************** #include #include // **************************************************************************** // C O N F I G U R A T I O N W O R D S // **************************************************************************** // //---------------------------------------------------------- // DEVCFG0 //---------------------------------------------------------- #pragma config DEBUG = OFF #pragma config JTAGEN = OFF #pragma config ICESEL = ICS_PGx2 #pragma config TRCEN = OFF #pragma config BOOTISA = MIPS32 #pragma config FSLEEP = OFF #pragma config DBGPER = PG_ALL #pragma config SMCLR = MCLR_NORM #pragma config SOSCGAIN = GAIN_2X #pragma config SOSCBOOST = ON #pragma config POSCGAIN = GAIN_LEVEL_3 #pragma config POSCBOOST = ON #pragma config EJTAGBEN = NORMAL #pragma config CP = OFF //---------------------------------------------------------- // DEVCFG1 //---------------------------------------------------------- #pragma config FNOSC = SPLL #pragma config DMTINTV = WIN_127_128 #pragma config FSOSCEN = OFF #pragma config IESO = OFF #pragma config POSCMOD = EC //External 24Mhz Clock oscillator #pragma config OSCIOFNC = ON #pragma config FCKSM = CSECME #pragma config WDTPS = PS1048576 #pragma config WDTSPGM = STOP #pragma config FWDTEN = OFF #pragma config WINDIS = NORMAL #pragma config FWDTWINSZ = WINSZ_25 #pragma config DMTCNT = DMT31 #pragma config FDMTEN = OFF //---------------------------------------------------------- // DEVCFG2 //---------------------------------------------------------- #pragma config FPLLIDIV = DIV_3 #pragma config FPLLRNG = RANGE_5_10_MHZ #pragma config FPLLICLK = PLL_POSC #pragma config FPLLMULT = MUL_60 #pragma config FPLLODIV = DIV_4 #pragma config VBATBOREN = OFF #pragma config DSBOREN = ON #pragma config DSWDTPS = DSPS32 #pragma config DSWDTOSC = LPRC #pragma config DSWDTEN = OFF #pragma config FDSEN = ON #pragma config BORSEL = HIGH #pragma config UPLLEN = OFF //---------------------------------------------------------- // DEVCFG3 //---------------------------------------------------------- #pragma config USERID = 0x0ADC #pragma config FUSBIDIO2 = OFF #pragma config FVBUSIO2 = OFF #pragma config PGL1WAY = ON #pragma config PMDL1WAY = ON #pragma config IOL1WAY = ON #pragma config FUSBIDIO1 = OFF #pragma config FVBUSIO1 = OFF #pragma config PWMLOCK = OFF //---------------------------------------------------------- // BF1SEQ0 //---------------------------------------------------------- #pragma config TSEQ = 0x0000 #pragma config CSEQ = 0xffff // ************************************************************************************************* // U S E R D E F I N E S // Note: User must configure as required // ************************************************************************************************* #define TAD_TRIGGER_SOURCE_SPACING 3 // TAD trigger spacing value from ADC Table 14 w/12bit & 6 interleaved ADCs #define INTERLEAVED_ADC_COUNT 6 // Number of interleaved ADC (Cannot be <2 or >6). Changing this value will // automatically scale the number of interleaved ADC and triggers utilized in this code. // ************************************************************************************************* // EQUATION #1: (Assumptions: INTERLEAVED_ADC_COUNT=6) // TAD_TRIGGER_SOURCE_SPACING(min) = // = ((SAMC + (((#bits Resolution+1) + ADJ) * TAD)) / INTERLEAVED_ADC_COUNT) // = (3+12+1) + ADJ) * TAD)) / 6 ADCs) // = (16 + ADJ) * TAD)) / 6 ADCs // = (16 + 2) * TAD)) / 6 ADCs // = 3.0 TAD (minimum trigger spacing possible) // // NOTE: "ADJ" term = A user selected whole number adjustment factor between 0-4 // that must yield a Minimum TAD Trigger interval that equates to an exact // multiple of a ˝ TAD. This represents the minimum TAD_TRIGGER_SOURCE_SPACING // and therefore the fastest throughput possible. Additional TAD_TRIGGER_SOURCE_SPACING // and hence ADC Throughput Rates are possible in 1/2 TAD trigger spacing increments. // (See examples 1-3 below) // // NOTE: Once the minimum, TAD trigger spacing possible is determined, the user can configure for any // additional sampling frequency required if needed in 0.5 TAD trigger spacing increments. // // Example 1: (INTERLEAVED_ADC_COUNT=6) // TAD_TRIGGER_SOURCE_SPACING(min) = 3 // interleaved ADC Throughput rate = TAD clock freq / TAD_TRIGGER_SOURCE_SPACING // = 60Mhz / 3.0(min) // = 20msps // // Example 2: (INTERLEAVED_ADC_COUNT=6) // TAD_TRIGGER_SOURCE_SPACING = 3.5 // interleaved ADC Throughput rate = TAD clock freq / TAD_TRIGGER_SOURCE_SPACING // = 60Mhz / 3.5 // = 17.142857msps // // Example 3: (INTERLEAVED_ADC_COUNT=6) // TAD_TRIGGER_SOURCE_SPACING = 4.0 // interleaved ADC Throughput rate = TAD clock freq / TAD_TRIGGER_SOURCE_SPACING // = 60Mhz / 4.0 // = 15msps // // ************************************************************************************************* // ************************************************************************************************* // P R O G R A M D E F I N E S // (User Do Not Change) // Note: // TAD_TRIGGER_SOURCE_SPACING is multiplied by (2) because // PWM trigger source time base is 2x faster than an ADC TAD clock. // 2 PWM clocks = 1 ADC TAD Clock // ************************************************************************************************** // ------------------------------------------------------------------------------------------------------------------ // NOTE: Remember that the peripheral trigger clock frequency is 2x TAD clock frequency. // This is why the 2x. The -1 is because the Si peripheral triggers are always // on (match+1), so to compensate for correct timing subtract 1. // #define ADC_TRIG (TAD_TRIGGER_SOURCE_SPACING * 2) //6 #define PWM1_ADC_TRIG (ADC_TRIG - 1) // 5, ADC0 PWM1 Trig (Note: ADC Trig on match +1) #define PWM2_ADC_TRIG ((2 * ADC_TRIG) - 1) //11, ADC1 PWM1 Trig (Note: ADC Trig on match +1) #define PWM3_ADC_TRIG ((3 * ADC_TRIG) - 1) //17, ADC2 PWM2 Trig (Note: ADC Trig on match +1) #define PWM4_ADC_TRIG ((4 * ADC_TRIG) - 1) //23, ADC3 PWM3 Trig (Note: ADC Trig on match +1) #define PWM5_ADC_TRIG ((5 * ADC_TRIG) - 1) //29, ADC4 PWM4 Trig (Note: ADC Trig on match +1) #define PWM6_ADC_TRIG ((6 * ADC_TRIG) - 1) //35, ADC5 PWM5 Trig (Note: ADC Trig on match +1) #define ADC_CHANNEL_BUFFER_LENGTH (uint16_t) 128 // Allowed = 2n n=0-7: (128 Samples per ADC per Buffer. Each ADC has 2 buffers) #define HALF_BUFFER_LENGTH (uint16_t) (ADC_CHANNEL_BUFFER_LENGTH*INTERLEAVED_ADC_COUNT) #define TOTAL_BUFFER_LENGTH (uint16_t) (2*ADC_CHANNEL_BUFFER_LENGTH*INTERLEAVED_ADC_COUNT) #define ADC_SAMPLE_OFFSET (uint16_t) (ADC_CHANNEL_BUFFER_LENGTH*2) // Sample Offset between two sequential samples in RAW ADC DMA Buffer #define ADC_INTER_BUFFER_OFFSET ADC_CHANNEL_BUFFER_LENGTH // Sample offset between Buffer A and Buffer B #define DMA_BUFA_FULL (0x00000001 << (INTERLEAVED_ADC_COUNT-1)) #define DMA_BUFB_FULL (0x00010000 << (INTERLEAVED_ADC_COUNT-1)) #define DMA_BUFA_FULL_INT (0x00000100 << (INTERLEAVED_ADC_COUNT-1)) #define DMA_BUFB_FULL_INT (0x01000000 << (INTERLEAVED_ADC_COUNT-1)) #define DMA_INT_SRC (DMA_BUFA_FULL_INT | DMA_BUFB_FULL_INT) #define BUFFER_RPT_COUNT 1 // #if ((INTERLEAVED_ADC_COUNT < 2) | (INTERLEAVED_ADC_COUNT > 6)) #error INTERLEAVED_ADC_COUNT error, it is either less than 2 or greater than 6 #endif // ************************************************************************************************* // F U N C T I O N P R O T O T Y P E S // ************************************************************************************************* void init_PWM(void); void init_ADC(void); // ************************************************************************************************* // G L O B A L V A R I A B L E S // ************************************************************************************************* unsigned int ADC_var=0; char buffer_select = 0; uint8_t buffer_a_index, buffer_b_index, buffer_index; // ************************************************************************************************* // NOTE: // Each ADC has 2 buffers. Each buffer size can be up to 128 samples (256bytes). Thus, // total max buffer size per ADC is 256 samples (512 bytes). Since we are using 6 ADC // channels in this example, the total DMA BUffer size = // 128(samples per buffer)*2(buffer per adc)*6(number of adchannels) * BUFFER_RPT_COUNT // = 1536 samples or 3072 bytes. // ************************************************************************************************* uint16_t adc_dma_raw_buffer[TOTAL_BUFFER_LENGTH]; //Buffer where ADC Data is dumped by the DMA uint16_t adc_data_rearranged_buffer[BUFFER_RPT_COUNT][TOTAL_BUFFER_LENGTH]; // Buffer where ADC samples are re-arranged in sequentially uint16_t source_index, destination_index, adc_count, index_offset, sample_count; unsigned short *adc0_ptr,*adc1_ptr,*adc2_ptr,*adc3_ptr,*adc4_ptr,*adc5_ptr; unsigned short *dest_ptr, *end_ptr; // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ // $ M A I N R O U T I N E $ // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ int main(void) { // ************************************************************************************************* // CPU Performance Optimization: // ************************************************************************************************* register unsigned long tmp_cache; //KSEG0 cache enable asm("mfc0 %0,$16,0" : "=r"(tmp_cache)); tmp_cache = (tmp_cache & ~7) | 3; asm("mtc0 %0,$16,0" :: "r" (tmp_cache)); CHECONbits.PFMWS=3; //Flash wait states = 3 CPU clock cycles @ 120Mhz CHECONbits.PREFEN = 1; //PREFEN PRISSbits.PRI7SS = 1; //DMA Interrupt with priority level of 7 uses Shadow Set 1 INTCONbits.MVEC = 1; //Enable multi-vector interrupts __builtin_mtc0(12,0,(__builtin_mfc0(12,0) | 0x0001)); // Global Interrupt Enable SYSKEY = 0xAA996655; // Write Key1 to SYSKEY SYSKEY = 0x556699AA; //Unlock PB1DIVbits.PBDIV = 0; //0 = 1:1 = SYSCLK/1 = 120Mhz PB2DIVbits.PBDIV = 0; //0 = 1:1 = SYSCLK/1 = 120Mhz PB3DIVbits.PBDIV = 0; //0 = 1:1 = SYSCLK/1 = 120Mhz PB4DIVbits.PBDIV = 0; //0 = 1:1 = SYSCLK/1 = 120Mhz PB5DIVbits.PBDIV = 0; //0 = 1:1 = SYSCLK/1 = 120Mhz PB6DIVbits.PBDIV = 3; //0 = 4:1 = SYSCLK/4 = 30Mhz SYSKEY = 0x00000000; //Lock // ************************************************************************************************* // Initialize and enable ADC + ADC DMA then ADC PWM trigger sources // ************************************************************************************************* init_ADC(); //Initialize and enable interleaved ADCs first before PWM ADC triggers are enabled while(1) { // USERS CODE GOES HERE } } // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ // $ F U N T I O N S U B _ R O U T I N U E S $ // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ // ************************************************************************************************* // Interleaved ADC PWM Trigger initialization: // - PWM output pins disabled // - PWMs 1-x = 50% duty cycle // - PWM Period Timer = 6x ADC TAD trigger spacing // - PWM ADC conversion triggers staggered in 1/6 increments // NOTE: // 1): In this application, PWM functionality is exclusive to only the interleaved ADC trigger(s) // 2): PWM when used as ADC trigger(s) must never be enabled before ADC initialization 100% complete // ************************************************************************************************* void init_PWM(void) { PTPER = (INTERLEAVED_ADC_COUNT * ADC_TRIG); PDC1 = (PTPER >> 1); //50% of period TRIG1 = PWM1_ADC_TRIG; //PWM1 Trigger point PDC2 = PDC1; TRIG2 = PWM2_ADC_TRIG; //PWM2 Trigger point if(INTERLEAVED_ADC_COUNT > 2) { PDC3 = PDC1; TRIG3 = PWM3_ADC_TRIG; //PWM3 Trigger point } if(INTERLEAVED_ADC_COUNT > 3) { PDC4 = PDC1; TRIG4 = PWM4_ADC_TRIG; //PWM4 Trigger point } if(INTERLEAVED_ADC_COUNT > 4) { PDC5 = PDC1; TRIG5 = PWM5_ADC_TRIG; //PWM5 Trigger point } if(INTERLEAVED_ADC_COUNT > 5) { PDC6 = PDC1; TRIG6 = PWM6_ADC_TRIG; //PWM6 Trigger point } IOCON1 = 0x00030000; //Fault disabled, GPIO controls PWM1H/L pins IOCON2 = 0x00030000; //Fault disabled, GPIO controls PWM2H/L pins IOCON3 = 0x00030000; //Fault disabled, GPIO controls PWM3H/L pins IOCON4 = 0x00030000; //Fault disabled, GPIO controls PWM4H/L pins IOCON5 = 0x00030000; //Fault disabled, GPIO controls PWM5H/L pins IOCON6 = 0x00030000; //Fault disabled, GPIO controls PWM6H/L pins PTCONbits.PTEN = 1; } // ************************************************************************************************* // init_ADC // Interleaved ADC Initialization Function: // Used analog inputs if: // INTERLEAVED_ADC_COUNT = 2 (Analog inputs, AN0 only) // INTERLEAVED_ADC_COUNT = 3 (Analog inputs, AN0 & AN2 only) // INTERLEAVED_ADC_COUNT = 4 (Analog inputs, AN0 & AN2 only) // INTERLEAVED_ADC_COUNT = 5 (Analog inputs, AN0 & AN2 only) // INTERLEAVED_ADC_COUNT = 6 (Analog inputs, AN0 & AN2 & AN5 only) // ************************************************************************************************* // ********************************************************************************* // These steps are already done in this code example, this is just a reminder // to the user for any new original code development. // // CAUTION:(Required) // 1) The user SHOULD always set the minimum sampling time of each Class_1 ADC // equal to 3TAD as defined by ADCxTIME. // 2) The user should insure that the selected trigger source time base clock rate is // exactly 2x faster than the ADC clock TAD rate. This will provide the highest // timing resolution along with the most sampling rate options as defined in // Table 14 of this app note. // 3) The user selected trigger sources have to be able to be mapped to a single // synchronized time base. // For example: // a) TMR3 & OCx (x=1,3,5 PIC32MZxxxx) // b) TMR3 & OCx (x=1-4 PIC32MKxxGPxx) // c) PTMRx & TRIGx ( x=1-12 PIC32MKxxMCxx PWM) // d) PTMRx & STRIGx ( x=1-12 PIC32MKxxMCxx PWM) // 4) In a same group of interleaved ADC’s a user “MUST NOT” use two separate timer trigger sources. // Example: ADC0, ADC1 and ADC2 are being used in an interleaved group. // Trigger sources can be any combination of valid ADC OCx edge triggered sources synchronized to a SINGLE TMRx. // It would not be allowed to use in the same group of interleaved ADCs OCx, TMR3 & TMR5 for example. // Separate TMRx in different interleaved ADC groups is OK. // 5 If using the DMA for the ADC the user “MUST” use SYSCLK as the ADC // source clock, (i.e. ADCCON3). // 6) User must configure ADC for 512 TAD warm-up time // (ADCANCONbits.WKUPCLKCNT = 0x9) "BEFORE" enabling ADC. // 7) Users SW must wait for ADC BANGAP and ADC warm-up time "AFTER" // enabling ADC and before activating the ADC trigger sources. // 8) ADC must always be initialized 1st “BEFORE” activating the ADC trigger sources // // NOTE: Failure to follow these recommendations will result in inaccurate ADC // data acquisition and poor performance. //****************************************************************************** void init_ADC(void) { ADC0CFG = DEVADC0; //Load ADC0 Calibration values ADC1CFG = DEVADC1; //Load ADC1 Calibration values ADC2CFG = DEVADC2; //Load ADC2 Calibration values ADC3CFG = DEVADC3; //Load ADC3 Calibration values ADC4CFG = DEVADC4; //Load ADC4 Calibration values ADC5CFG = DEVADC5; //Load ADC5 Calibration values ADC7CFG = DEVADC7; //Load ADC7 Calibration values ADCANCONbits.WKUPCLKCNT = 0x9; // ADC Warm up delay = (512 * TADx) //****************************************************************************** // If using the DMA the user MUST use SYSCLK as the ADC source clock, (ADCCON3 = 3) //****************************************************************************** ADCCON3bits.ADCSEL = 3; // Select ADC input clock source = SYSCLK 120Mhz ADCCON3bits.CONCLKDIV = 0; // Analog-to-Digital Control Clock (TQ) Divider = SYSCLK Divide by 1 ADCCON1bits.FSSCLKEN = 1; //Fast synchronous SYSCLK to ADC control clock is enabled //****************************************************************************** // ADC0 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM1 Trigger //****************************************************************************** ADC0TIMEbits.ADCDIV = 1; // ADCx Clock Divisor, Divide by 2, 60Mhz ADC0TIMEbits.SAMC = 1; // 3 TAD (Hardware enforced sample time) ADC0TIMEbits.SELRES = 3; //ADC0 12bit resolution mode ADC0TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG1bits.TRGSRC0 = 10; // Set AN0 to trigger from PWM Trigger 1 ADCANCONbits.ANEN0 = 1; // Enable ADC0 analog bias/logic ADCCON3bits.DIGEN0 = 1; // Enable ADC0 digital logic //****************************************************************************** // ADC1 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM2 Trigger //****************************************************************************** ADC1TIMEbits.ADCDIV = 1; ADC1TIMEbits.SAMC = 1; // 3 TAD ADCTRGMODEbits.SH1ALT = 3; // AN0 is the input to ADC1 ADC1TIMEbits.SELRES = 3; //ADC1 12bit resolution mode ADC1TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG1bits.TRGSRC1 = 11; // Set AN1 to trigger from PWM Trigger 2 ADCANCONbits.ANEN1 = 1; // Enable ADC1 analog bias/logic ADCCON3bits.DIGEN1 = 1; // Enable ADC1 digital logic //****************************************************************************** // ADC2 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM3 Trigger //****************************************************************************** if(INTERLEAVED_ADC_COUNT > 2) { ADC2TIMEbits.ADCDIV = 1; ADC2TIMEbits.SAMC = 1; // 3 TAD ADCTRGMODEbits.SH2ALT = 0; // AN2 is the input to ADC2 ADC2TIMEbits.SELRES = 3; //ADC2 12bit resolution mode ADC2TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG1bits.TRGSRC2 = 12; // Set AN2 to trigger from PWM Trigger 3 ADCANCONbits.ANEN2 = 1; // Enable ADC2 analog bias/logic ADCCON3bits.DIGEN2 = 1; // Enable ADC2 digital logic } //****************************************************************************** // ADC3 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM4 Trigger //****************************************************************************** if(INTERLEAVED_ADC_COUNT > 3) { ADC3TIMEbits.ADCDIV = 1; ADC3TIMEbits.SAMC = 1; // 3 TAD ADCTRGMODEbits.SH3ALT = 1; // AN0 is the input to ADC3 ADC3TIMEbits.SELRES = 3; //ADC3 12bit resolution mode ADC3TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG1bits.TRGSRC3 = 13; // Set AN3 to trigger from PWM Trigger 4 ADCANCONbits.ANEN3 = 1; // Enable ADC2 analog bias/logic ADCCON3bits.DIGEN3 = 1; // Enable ADC2 digital logic } //****************************************************************************** // ADC4 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM5 Trigger //****************************************************************************** if(INTERLEAVED_ADC_COUNT > 4) { ADC4TIMEbits.ADCDIV = 1; ADC4TIMEbits.SAMC = 1; // 3 TAD ADCTRGMODEbits.SH4ALT = 3; // AN0 is the input to ADC4 ADC4TIMEbits.SELRES = 3; //ADC4 12bit resolution mode ADC4TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG2bits.TRGSRC4 = 14; // Set AN4 to trigger from PWM Trigger 5 ADCANCONbits.ANEN4 = 1; // Enable ADC2 analog bias/logic ADCCON3bits.DIGEN4 = 1; // Enable ADC2 digital logic } //****************************************************************************** // ADC5 Module setup: // TAD = SYSCLK/2, 12bit, DMA enb, SAMC=3TAD, PWM6 Trigger //****************************************************************************** if(INTERLEAVED_ADC_COUNT > 5) { ADC5TIMEbits.ADCDIV = 1; ADC5TIMEbits.SAMC = 1; // 3 TAD ADCTRGMODEbits.SH5ALT = 0; // AN5 is the input to ADC5 ADC5TIMEbits.SELRES = 3; //ADC5 12bit resolution mode ADC5TIMEbits.BCHEN=1; // ADC data saved in DMA system ram buffer ADCTRG2bits.TRGSRC5 = 15; // Set AN5 to trigger from PWM Trigger 6 ADCANCONbits.ANEN5 = 1; // Enable ADC2 analog bias/logic ADCCON3bits.DIGEN5 = 1; // Enable ADC2 digital logic } //****************************************************************************** // ADC DMA Master Configuration (PIC32MKxxxx Only) // 12 buffers @128 samples/buffer = (6 ping pong buffer A?s, 6 ping pong buffer B?s) //****************************************************************************** ADCCON1bits.DMABL = 7; // ADC Buffer length = 128 samples ADCDMAB = (unsigned int) &adc_dma_raw_buffer & 0x1FFFFFFF; ADCDSTATbits.DMAEN = 1; ADCDSTAT = (ADCDSTAT | DMA_INT_SRC); // Enb DMA Buff Full A&B Interrupt IFS3bits.AD1FCBTIF = 0; // Clear DMA Buffer full interrupt flag IPC26bits.AD1FCBTIP = 1; // Set DMA Buffer full interrupt priority IPC26bits.AD1FCBTIS = 1; // Set DMA Buffer full interrupt sub-priority IEC3bits.AD1FCBTIE = 1; // Enable DMA Buffer full interrupt ADCCON1bits.ON = 1; // Turn the ADC on //****************************************************************************** // Waiting for ADC warm-up time and ADC bandgap reference to stabilize //****************************************************************************** while(!ADCCON2bits.BGVRRDY); // Wait until the reference voltage is ready while(!ADCANCONbits.WKRDY0); // Wait until ADC0 is ready while(!ADCANCONbits.WKRDY1); // Wait until ADC1 is ready if(INTERLEAVED_ADC_COUNT > 2) while(!ADCANCONbits.WKRDY2); // Wait until ADC2 is ready if(INTERLEAVED_ADC_COUNT > 3) while(!ADCANCONbits.WKRDY3); // Wait until ADC3 is ready if(INTERLEAVED_ADC_COUNT > 4) while(!ADCANCONbits.WKRDY4); // Wait until ADC4 is ready if(INTERLEAVED_ADC_COUNT > 5) while(!ADCANCONbits.WKRDY5); // Wait until ADC5 is ready init_PWM(); //Initialize internal PWM interleaved triggers and //configure PWM pins for general purpose I/O or alternate PPS } // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ // $ I N T E R R U P T S E R V I C E R O U T I N E S $ // $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ // ************************************************************************************************* // D M A I S R (Interrupt Service Routine) // ************************************************************************************************* void __attribute__((interrupt(ipl7auto), at_vector(_ADC_DMA_VECTOR), aligned(16))) isr () { ADC_var = ADCDSTAT; // To preserve ADCSTAT reg after read //****************************************************************************** // DMA Ping-Pong Buffer A full processing // Each ADC samples (1 / INTERLEAVED_ADC_COUNT) of the analog input signal. For // CPU processing of the interleaved ADC data it must be read from each of the // active interleaved ADC's dedicated ping-pong buffer A and rearranged in // sequential order in a CPU ADC result system SRAM buffer, for signal processing //****************************************************************************** if(ADC_var & DMA_BUFA_FULL) //Last sequential ADC RAM DMA ping-pong Buffer A full. { adc0_ptr = (unsigned short*)&adc_dma_raw_buffer; // Initialize ADC0 Source pointer to ADC0_BUFFER_A location in SRAM adc1_ptr = adc0_ptr + ADC_SAMPLE_OFFSET; // Initialize ADC1 Source pointer to ADC1_BUFFER_A location in SRAM #if (INTERLEAVED_ADC_COUNT > 2) adc2_ptr = adc1_ptr +ADC_SAMPLE_OFFSET; // Init ADC2 Src ptr to ADC2_BUFFER_A #endif #if (INTERLEAVED_ADC_COUNT > 3) adc3_ptr = adc2_ptr +ADC_SAMPLE_OFFSET; // Init ADC3 Src ptr to ADC3_BUFFER_A #endif #if (INTERLEAVED_ADC_COUNT > 4) adc4_ptr = adc3_ptr +ADC_SAMPLE_OFFSET; // Init ADC4 Src ptr to ADC4_BUFFER_A #endif #if (INTERLEAVED_ADC_COUNT > 5) adc5_ptr = adc4_ptr +ADC_SAMPLE_OFFSET; // Init ADC5 Src ptr to ADC5_BUFFER_A #endif //****************************************************************************** // Initialize DMA destination pointer to base address where re-arranged // ADC data from Buffer A is stored in SRAM //****************************************************************************** dest_ptr = (unsigned short*)&adc_data_rearranged_buffer[buffer_index][0]; //****************************************************************************** // Initialize DMA End address value for re-arranged ADC Data from Buffer A. //****************************************************************************** end_ptr = (unsigned short*)&adc_data_rearranged_buffer[buffer_index][0]+ HALF_BUFFER_LENGTH; do { *dest_ptr++ = *adc0_ptr++; *dest_ptr++ = *adc1_ptr++; #if (INTERLEAVED_ADC_COUNT > 2) *dest_ptr++ = *adc2_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 3) *dest_ptr++ = *adc3_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 4) *dest_ptr++ = *adc4_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 5) *dest_ptr++ = *adc5_ptr++; #endif } while(dest_ptr < end_ptr); // Continue Re-arranging ADC data until all Buffer A data for active ADCs is complete buffer_a_index++; // Increment buffer_a_index by 1 at the end of re-arrangement of Buffer A data. } //****************************************************************************** // DMA Ping-Pong Buffer "B" full processing // Each ADC samples (1/INTERLEAVED_ADC_COUNT)of the analog input signal. For // CPU processing of the interleaved ADC data it must be read from each of the // active interleaved ADC's dedicated ping-pong buffer "B" and rearranged in // sequential order in the CPU ADC result system SRAM buffer. //****************************************************************************** if(ADC_var & DMA_BUFB_FULL) //Last sequential ADC RAM DMA ping-pong Buffer B full { adc0_ptr = (unsigned short*)&adc_dma_raw_buffer + ADC_INTER_BUFFER_OFFSET; // Init ADC0 Src ptr to ADC0_BUFFER_B in SRAM adc1_ptr = adc0_ptr +ADC_SAMPLE_OFFSET; // Init ADC1 Src ptr to ADC1_BUFFER_B in SRAM #if (INTERLEAVED_ADC_COUNT > 2) adc2_ptr = adc1_ptr +ADC_SAMPLE_OFFSET; // Init ADC2 Src ptr to ADC2_BUFFER_B #endif #if (INTERLEAVED_ADC_COUNT > 3) adc3_ptr = adc2_ptr +ADC_SAMPLE_OFFSET; // Init ADC3 Src ptr to ADC3_BUFFER_B #endif #if (INTERLEAVED_ADC_COUNT > 4) adc4_ptr = adc3_ptr +ADC_SAMPLE_OFFSET; // Init ADC4 Src ptr to ADC4_BUFFER_B #endif #if (INTERLEAVED_ADC_COUNT > 5) adc5_ptr = adc4_ptr +ADC_SAMPLE_OFFSET; // Init ADC5 Src ptr to ADC5_BUFFER_B #endif //****************************************************************************** // Initialize DMA destination pointer to base address where re-arranged // ADC data from Buffer B is stored in SRAM //****************************************************************************** dest_ptr = (unsigned short*)&adc_data_rearranged_buffer[buffer_index][0] + HALF_BUFFER_LENGTH; //****************************************************************************** // Initialize DMA End address value for re-arranged ADC Data from Buffer B. //****************************************************************************** end_ptr = (unsigned short*)&adc_data_rearranged_buffer[buffer_index][0]+TOTAL_BUFFER_LENGTH; // End addr for re-arranged ADC Data from Buffer B. do { *dest_ptr++ = *adc0_ptr++; *dest_ptr++ = *adc1_ptr++; #if (INTERLEAVED_ADC_COUNT > 2) *dest_ptr++ = *adc2_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 3) *dest_ptr++ = *adc3_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 4) *dest_ptr++ = *adc4_ptr++; #endif #if (INTERLEAVED_ADC_COUNT > 5) *dest_ptr++ = *adc5_ptr++; #endif } while(dest_ptr < end_ptr); // Continue Re-arranging ADC data until all Buffer A data for active ADCs is complete buffer_b_index++; // Increment buffer_a_index by 1 at the end of re-arrangement of Buffer A data. } //****************************************************************************** // adc_data_rearranged_buffer is a 2 dimensional buffer. If both Buffer A and // Buffer B has been transferred, increment the most significant buffer index // for next set of Buffer A and Buffer B data //****************************************************************************** if(buffer_a_index == buffer_b_index) buffer_index++; //****************************************************************************** // adc_data_rearranged_buffer is also circular in nature, after reaching the // end of the buffer, the index is re-initialized to start of the buffer. //****************************************************************************** if (buffer_index >= BUFFER_RPT_COUNT) buffer_index = 0; IFS3bits.AD1FCBTIF = 0; // Clear the ADC DMA Interrupt } //End DMA ISR