[SOLVED] How to configure ADC sampling rate on dsPIC?

Status
Not open for further replies.

dxdx347

Newbie level 3
Newbie level 3
Joined
Jan 15, 2013
Messages
3
Helped
0
Reputation
0
Reaction score
0
Trophy points
1,281
Visit site
Activity points
1,312
Hi!

I am using dsPIC Starter Kit which has a dsPIC33fj256gp506. I am trying to modify the ADC sampling rate using the example from Microchip.
Here is the code:

Code:
#ifndef __ADCCHANNELDRV_H__ 
#define __ADCCHANNELDRV_H__ 
 #define ADC_CHANNEL_FCY   40000000
[B]#define ADC_FSAMP    8000 /* Sampling Frequency */ [/B]
#define ADC_BUFFER_SIZE   128    /* This is the size of each buffer */ 
#define ADC_CHANNEL_DMA_BUFSIZE (ADC_BUFFER_SIZE*2)  
   
 #include "..\h\p33FJ256GP506.h" 
 typedef struct sADCChannelHandle { 
 int * buffer1; 
 int * buffer2; 
 volatile int bufferIndicator; 
 volatile int isReadBusy; 
  
}ADCChannelHandle; 
 void ADCChannelInit (ADCChannelHandle * pHandle,int * pBufferInDMA); 
void ADCChannelStart (ADCChannelHandle * pHandle); 
void ADCChannelRead (ADCChannelHandle * pHandle,int *buffer,int size); 
int ADCChannelIsBusy (ADCChannelHandle * pHandle); 
void ADCChannelStop (ADCChannelHandle * pHandle);
#define ADCON1VAL   0x0744    /* 12 bit ADC with signed fractional format 
                                                    * [B]Triggered by Timer 3 [/B]and auto start 
                                                     * sampling after conversion complete. */ 
#define ADCON2VAL   0x0000      /* AVdd and AVss voltage reference,  
                                                      * use channel 0 with no scan */
#define ADCON3VAL  0x0010     [B] /* Tad is 16 Tcy     */[/B]     
#define ADCHSVAL  0x0000  /* AN0 input on channel 0  */    
#define ADPCFGVAL  0xFFFE  /* AN0 input is Analog   */ 
#define ADCSSLVAL  0x0000   /* No channel scanning   */ 
 #endif 
 
  
 #include "..\h\ADCChannelDrv.h" 
 static ADCChannelHandle * thisADCChannel; 
 void ADCChannelInit (ADCChannelHandle * pHandle,int * pBufferInDMA) 
{ 
 /* This function will intialize the DMA  */ 
 /* DMA0 is used to read from the ADC  */ 
  
  thisADCChannel = pHandle; 
 pHandle->buffer1  = pBufferInDMA; /* Assign the ping pong buffers for the ADC DMA*/ 
 pHandle->buffer2  = (int *)((int)pBufferInDMA + ADC_BUFFER_SIZE); 
   
 DMA0CONbits.SIZE  = 0;  /* Word transfers       */ 
 DMA0CONbits.DIR  = 0;  /* From ADC1BUF to DMA     */ 
 DMA0CONbits.HALF  = 0;  /* Interrupt when all the data has been moved */ 
 DMA0CONbits.NULLW  = 0;  /* No NULL writes - Normal Operation   */ 
 DMA0CONbits.AMODE  = 0;  /* Register Indirect with post-increment mode */ 
 DMA0CONbits.MODE  = 2;  /* Continuous  ping pong mode enabled  */ 
  
 DMA0REQbits.FORCE  = 0;  /* Automatic transfer    */ 
 DMA0REQbits.IRQSEL = 0xD; /* ADC conversion complete   */ 
  
 DMA0STA = (int)(pHandle->buffer1) - (int)&_DMA_BASE; 
 DMA0STB  = (int)(pHandle->buffer2) - (int)&_DMA_BASE; 
  
  DMA0PAD = (int )&ADC1BUF0; 
 DMA0CNT = ADC_BUFFER_SIZE - 1; 
  AD1CON1  = ADCON1VAL;    /* Load the ADC registers with  value  */                             
 AD1CON2  = ADCON2VAL;    /* specified in 12bitADCDriver.h */  
 AD1CON3  = ADCON3VAL;                               
 AD1CHS0  = ADCHSVAL;                               
 AD1PCFGLbits.PCFG0 = 0; 
 AD1CSSL  = ADCSSLVAL;
[B]TMR3   = 0; 
 PR3  = (ADC_CHANNEL_FCY/ADC_FSAMP) - 1; [/B]
 } 
   
 void ADCChannelStart (ADCChannelHandle * pHandle) 
{ 
 pHandle->bufferIndicator  = 0; 
 pHandle->isReadBusy   = 1; 
 _DMA0IF = 0; 
 _DMA0IE = 1; 
 DMA0CONbits.CHEN = 1;  /* Enable the DMA Channel */ 
 AD1CON1bits.ADON = 1;  /* Enable ADC module  */ 
 T3CONbits.TON   = 1;  /* Enable Timer 3        */ 
 } 
 void ADCChannelStop(ADCChannelHandle * pHandle) 
{ 
 _DMA0IE = 0;    /* Disable the DMA interrupt */ 
 DMA0CONbits.CHEN = 0;  /* Disable the DMA Channel */ 
 AD1CON1bits.ADON = 0;  /* Disable ADC module  */ 
 T3CONbits.TON   = 0;  /* Disable Timer 3        */ 
}  
 void ADCChannelRead (ADCChannelHandle * pHandle,int *data,int size) 
{ 
 int *source; 
 int  i; 
  
 /* if the buffer indicator bit is 1, then use buffer 1 else use buffer2 */ 
 /* Since the DMA ping pongs between these buffer, you must know */ 
 /* which one to read. The bufferIndicators keep track of this  */ 
  
 source = (pHandle->bufferIndicator) ? pHandle->buffer1 : pHandle->buffer2; 
 if (size > ADC_BUFFER_SIZE) size = ADC_BUFFER_SIZE; 
   
 for(i = 0;i < size;i++) 
 { 
  data[i] = source[i]; 
 } 
 __asm__ volatile("disi #0x4"); /* disable interrupts */ 
 pHandle->isReadBusy = 1; 
 __asm__ volatile("disi #0x0"); /* enable interrupts */ 
 } 
 int ADCChannelIsBusy (ADCChannelHandle * pHandle) 
{ 
 return(pHandle->isReadBusy); 
} 
 void __attribute__((__interrupt__,no_auto_psv)) _DMA0Interrupt(void) 
{ 
  
 _DMA0IF = 0; 
 thisADCChannel->bufferIndicator   ^= 1;  /* Flip the indicator bit  */ 
 thisADCChannel->isReadBusy  = 0;   /* New frame is available */ 
}

I want to set it to 60kHz. I set ADC_FSAMP = 60000, and calculate ADCS.

Tcy = 25ns (@40MHz)
Tc = 1/60000 = 16.66us = 16666ns (aprox.)
Tad = Tc/14 = 1190ns (aprox.)
ADC Conversion Clock: Tad=Tcy*(ADCS+1) => ADCS = (Tad/Tcy) - 1 = 46,61 = 47 (aprox.).
I set ADCS = 47, so I write:

Code:
[B]#define ADCON3VAL 0x002F[/B]

To test if the ADC is working correctly, I generate a 10kHz signal for the board using my PC sound card (using Visual Analyser 2011).
But the ADC won't finish sampling (I use a LED to signal when 128 samples have been acquired - when finished, the LED switches off). Also, if I generate a DC signal, the ADC won't finish sampling. More interesting is the fact that when I generate a 1kHz signal, the ADC works fine!! I used debug to view the samples, exported the values in a file and plotted them in Matlab.
Please help in solving this problem. I really need to get this ADC running for a project I am working on.
 

Hi,

find the attcahed code (dsPIC30F6014A) and read the comments. Hope this would help you.


Code:
//Functions:
//ADC_Init() is used to configure A/D to scan and convert 2 input channels
//per interrupt. The A/D is set up for a total sampling rate of 8KHz
//or 4KHz per channel. The internal counter in the A/D is used to provide
//Acquisition time delay. The input pins being scanned are AN2 and AN3.
//AN2 and AN3 are connected to the Temperature Sensor and the Potentiometer
//on the dsPICDEM2 board.

void ADC_Init(void)
{
        //ADCON1 Register
        
		ADCON1=0;

        //Set up A/D for Automatic Sampling, Auto-Convert

        //All other bits to their default state

		//ADCON1bits.SIMSAM = 1;          // Simultaneous Sampling enabled 

        ADCON1bits.SSRC = 7;			// Internal counter ends sampling and starts conversion (auto convert)

        ADCON1bits.ASAM = 1;			// 1 = Sampling begins immediately after last conversion completes
          
        ADCON1bits.FORM=0;				// 00 = Integer (DOUT = 0000 dddd dddd dddd)

        //ADCON2 Register
        //Set up A/D for interrupting after 2 samples get filled in the buffer
        //Also, enable Channel scanning
        //All other bits to their default state

        ADCON2bits.SMPI = 6;			// interuupt after taking seven samples

		ADCON2bits.VCFG = 3;  			//  External VREF+ pin and  External VREF- pin -----> +3.3 vref+

        ADCON2bits.CSCNA = 1;			// scan input selection for ch0 + s/h input for mux A input multiplexer setting bit

	        //ADCON3 Register
	        //Set up Acquisition time (Tacq) for 31 Tad periods
	        //where, Tad = A/D conversion clock time.
	        //Set up Tad period to be 20.5 Tcy (Tcy = instruction cycle time)
	        //Given that each conversion takes 14*Tad (=Tconv) periods,
	        //Total Sample Time = Acquisition Time + Conversion Time
	        // = (31 + 14)*Tad = 45*Tad periods
	        // = 45 * 20.5 * Tcy = 922.5*Tcy periods
	        //At 7.3728 MIPS, Tcy = 135 ns = Instruction Cycle Time
	        //So Tsamp = Tacq + Tconv = 45*Tad(in this example)= 125.1 microseconds
	        //So Fsamp = Sampling Rate ~= 8 KHz
	        //All other bits to their default state
        
		ADCON3bits.SAMC = 31; 			//Set up Acquisition time (Tacq) for 31 Tad periods
        
		ADCON3bits.ADRC=0; 				// clock derived fron system clock

		ADCON3bits.ADCS = 20;			// A/D Conversion Clock Select bits

	        //ADCHS Register
	        //When Channel scanning is enabled (ADCON2bits.CSCNA=1)
	        //AND Alternate mux sampling is disabled (ADCON2bits.ALTS=0)
	        //then ADCHS is a "don't care"
        
		ADCHS = 0x0000;

	        //ADCSSL Register
	        //Scan channels AN6,7,8,9,11,12,13 fast part of scanning sequence
	        //ADCSSL = 0x000C;

		ADCSSLbits.CSSL6 = 1;           // Select AN6 for input scanning
		ADCSSLbits.CSSL7 = 1;           // Select AN7 for input scanning
		ADCSSLbits.CSSL8 = 1;           // Select AN8 for input scanning 
		ADCSSLbits.CSSL9 = 1;           // Select AN9 for input scanning  
    	ADCSSLbits.CSSL11 = 1;          // Select AN11 for input scanning   
		ADCSSLbits.CSSL12 = 1;          // Select AN12 for input scanning 
		ADCSSLbits.CSSL13 = 1;          // Select AN13 for input scanning   
		
	        //ADPCFG Register
	        //Set up channels AN2, AN3 ... as analog inputs and leave rest as digital
	        //Recall that we configured all A/D pins as digital when code execution
	        //entered main() out of reset
	        //ADPCFGbits.PCFG2 = 0;

		ADPCFGbits.PCFG6 = 0;    		
		ADPCFGbits.PCFG7 = 0;    		
		ADPCFGbits.PCFG8 = 0;    		                          
                          ADPCFGbits.PCFG9 = 0;    				
                          ADPCFGbits.PCFG11 = 0;   				
                          ADPCFGbits.PCFG12 = 0;    		
		ADPCFGbits.PCFG13 = 0;   				
		IFS0bits.ADIF = 0; //Clear the A/D interrupt flag bit
	
	    //Set the A/D interrupt enable bit
	    //IEC0bits.ADIE = 1;
	
	    //Turn on the A/D converter
	    //This is typically done after configuring other registers
	     
//		IEC0bits.ADIE = 1;		// interrupt enable

//		ADCON1bits.ADON = 1;

}
 
I want to use the code as it is shown (using a timer to start the sample/conversion sequence). Someone on another forum said that this method works as long as a full sample conversion sequence is short enough to allow whatever timed sample/conversion is set. I.E, if a one input sample/conversion takes 5ms and the timer calls it every 50ms then there is plenty of room to shorten the timer.

From what I understand, I have to determine the sample/conversion sequence time to see how much I can shorten the timer (thus increasing the sampling frequency).


Tconv = 14*Tad and

Tad = 16*Tcy = 16/40M = 400ns.

So, Tconv = 14*Tad = 14*400ns = 5.6us => fconv = 178kHz.


I want the sampling frequency to be 60kHz => T = 16.6us. So if I set #define ADC_FSAMP 60000, it should be OK. I can set it at maximum 178kHz.



When I sample a 1kHz sine wave, it works perfectly. When I sample a 10kHz sine wave, the ADC won't finish sampling. I pause the program execution to read the samples. Here is what it samples:



I can't figure out the problem.


I ran the code as it is posted in the first message (without my modifications) and it worked perfectly.
 

I found out what was the problem. The signal level was too low and didn't cross the SOUND_THRESHOLD value which I used in my program (should have though of it from the beginning), so the program never got out of a loop. I reduced the threshold and it worked perfectly.
 

Status
Not open for further replies.

Similar threads