dsPIC30F6014a ADC

Status
Not open for further replies.
The ADC relies on the 5V supply for accuracy. If you measure your supply and substitute your value (in mV) for the 5000 value in the calculations it should be correct. Your readings are high which suggests your supply is nearer 4V. Is your supply well regulated?

Mike.
 
It does look like the analog supply voltage or ADC ref. is lower than 5V.
You have two different setup_adc_ports() lines, which seems possibly wrong?


Also, what happens if you split this line?
ert_voltages_0=(ert_adc_value_0)*5000/4096;// last result of last conversion

to
ert_voltages_0 = ert_adc_value_0 * 5000L; // last result of last conversion
ert_voltages_0 /= 4096L;

Depending on the compiler, it may be using 16 bit maths as the literals are not defined as "long" - the L suffix does that for the literal numbers.
It also ensured the ordering of the operations, so it's not possibly optimising out an integer result of 5000 / 4096.

You could try temporarily making the settling delay rather longer, eg. 50uS, to see if that has any effect? it's very dependant on the external connections, as Nigel said earlier.
If that has no effect put it back to 10uS for speed.
 
Hi,
Attached is the code for reading multi-channel ADC from dpic30f6014a using internal clock and it works accordingly. From the attached code the sampling mode is continuous samples but how to change to 1 sample for every channels? Any good examples?

Regards
frahman3

CSS:
#include <30F6014A.h>
#device ADC=12
#include <float.h>
#include <string.h>
#fuses FRC_PLL16, NOWDT, NOPROTECT, PUT64, BORV27 //7.37MHz x16

#use delay(clock=117920000,internal)
#use rs232(baud=9600,UART1,ERRORS)


void main()
{
   set_TRIS_C(0x0000);
   set_TRIS_D(0x0000);
  
   SETUP_ADC(adc_OFF);
   setup_adc_ports(ALL_ANALOG); 
   setup_adc_ports(sAN0 |sAN1|sAN2|sAN3|sAN4|sAN5|sAN6|sAN7|sAN8|sAN9|sAN10|sAN11|sAN12|sAN13|sAN14|sAN15, VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_2);
   setup_adc(ADC_CLOCK_INTERNAL); 
   // Built-in A/D setup function
  
   //setup_adc_ports(ALL_ANALOG); 
   // Build-in A/D setup function
  
   signed int32 ert_adc_value_5;
   signed int32 ert_adc_value_6;
   signed int32 ert_adc_value_7;
   signed int32 ert_adc_value_8;
   signed int32 ert_adc_value_9;
   signed int32 ert_adc_value_10;
   signed int32 ert_adc_value_11;
   signed int32 ert_adc_value_12;
   signed int32 ert_adc_value_13;
 
  
  
   signed int32 ert_voltages_5;
   signed int32 ert_voltages_6;
   signed int32 ert_voltages_7;
   signed int32 ert_voltages_8;
   signed int32 ert_voltages_9;
   signed int32 ert_voltages_10;
   signed int32 ert_voltages_11;
   signed int32 ert_voltages_12;
   signed int32 ert_voltages_13;
  
   while(TRUE)
     {       
    
      //sample at TR5
      set_adc_channel(4);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_5=read_adc();    //starts conversion & store it in value
      ert_voltages_5=(ert_adc_value_5)*5000/4096;// last result of last conversion
      printf("Ch05=%u\r\n",ert_voltages_5);   
      
      //sample at TR6
      set_adc_channel(5);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_6=read_adc();    //starts conversion & store it in value
      ert_voltages_6=(ert_adc_value_6)*5000/4096;// last result of last conversion
      printf("Ch06=%u\r\n",ert_voltages_6);
      
      //sample at TR7
      set_adc_channel(6);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_7=read_adc();    //starts conversion & store it in value
      ert_voltages_7=(ert_adc_value_7)*5000/4096;// last result of last conversion
      printf("Ch07=%u\r\n",ert_voltages_7);
      
      //sample at TR8
      set_adc_channel(7);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_8=read_adc();    //starts conversion & store it in value
      ert_voltages_8=(ert_adc_value_8)*5000/4096;// last result of last conversion
      printf("Ch08=%u\r\n",ert_voltages_8);
      
      //sample at TR9
      set_adc_channel(8);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_9=read_adc();    //starts conversion & store it in value
      ert_voltages_9=(ert_adc_value_9)*5000/4096;// last result of last conversion
      printf("Ch09=%u\r\n",ert_voltages_9);
      
      //sample at TR10
      set_adc_channel(9);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_10=read_adc();    //starts conversion & store it in value
      ert_voltages_10=(ert_adc_value_10)*5000/4096;// last result of last conversion
      printf("Ch10=%u\r\n",ert_voltages_10);
      
      //sample at TR11
      set_adc_channel(10);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_11=read_adc();    //starts conversion & store it in value
      ert_voltages_11=(ert_adc_value_11)*5000/4096;// last result of last conversion
      printf("Ch11=%u\r\n",ert_voltages_11);
      
      //sample at TR12
      set_adc_channel(11);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_12=read_adc();    //starts conversion & store it in value
      ert_voltages_12=(ert_adc_value_12)*5000/4096;// last result of last conversion
      printf("Ch12=%u\r\n",ert_voltages_12);
      
      //sample at TR13
      set_adc_channel(12);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value_13=read_adc();    //starts conversion & store it in value
      ert_voltages_13=(ert_adc_value_13)*5000/4096;// last result of last conversion
      printf("Ch13=%u\r\n",ert_voltages_13);
      
        
      }

    
}//end main
 
how to change to 1 sample for every channels?

Move the while() loop

while(TRUE)
{

to after the code that reads and outputs the ADC values, so just above the inset brace before the //end main line. Add a ; between the braces:


while(TRUE)
{
;
}

}//end main


It will then run through the ADC routines once and stay in that loop until the board is reset.
 
Hi,
I'm using an dspic30F6014a
Crystal 20Mhz
Fuses set for FRC_PLL8= 7.37MHz * 8 = 58.96MHz.

From dspic30F data sheet:
TAD=334nSec
TRC(typ) =1.5uSec
1 conversion = 14TAD

Can someone explain to me the correlation between the TAD setting and the ADC frequency?
in the code : setup_adc(ADC_CLOCK_DIV_XX | ADC_TAD_MUL_XX) based on the given parameters how can I select the setting values for the CLK_DIV & TAD_MUL correctly?

Here is my calculation & correct me if i am wrong:
AD clock= FOSC/64=58960000Hz /64=921250 Hz
TAD=1.08uSec
To get reliable acquisition = 4* TAD= 4* 1.08uSec = 4.32uSec
Total time to acquisition & reading = 4 TAD + 14TAD = 18 TAD = 19.44uS =~ 20uSec.

From the above calculations the ADC setup code will be:
setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_16
is the above values are correct?
Because currently I received unreadable characters.

Thank you.
Arelone.

CSS:
#include <30F6014A.h>
#device ADC=12
#include <float.h>
#include <string.h>
#fuses FRC_PLL8,NOWDT, NOPROTECT, PUT64, BORV27,
#use delay(clock=58960000,internal) 
#use rs232(baud=9600,UART1,ERRORS)


void main()
{
   set_TRIS_C(0x0000);
   set_TRIS_D(0x0000);
  
   SETUP_ADC(adc_OFF);
   setup_adc_ports(ALL_ANALOG); 
   setup_adc_ports(sAN0 |sAN1|sAN2|sAN3|sAN4|sAN5|sAN6|sAN7|sAN8|sAN9|sAN10|sAN11|sAN12|sAN13|sAN14|sAN15, VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_16);
  
   signed int32 ert_adc_value_5;
   signed int32 ert_adc_value_6;
   signed int32 ert_adc_value_7;
    
 
   signed int32 ert_voltages_5;
   signed int32 ert_voltages_6;
   signed int32 ert_voltages_7;
  
  
 
   while(TRUE)
     {       
    
      output_high(PIN_D4); 
      delay_us(1);           
      output_low(PIN_D4);
      delay_us(2); 
      output_high(PIN_D4);
      delay_us(1); 
      output_low(PIN_D4); 
      delay_us(2);         
      
      
      //sample at TR5
      set_adc_channel(4);     //read from channel 4
      delay_us(20);           //delay is required after setting channel and bef.read
      //read_adc(ADC_START_ONLY);
      ert_adc_value_5=read_adc();    //starts conversion & store it in value
      ert_voltages_5=(ert_adc_value_5)*5000/4096;// last result of last conversion
      printf("Ch05=%u\n\r",ert_voltages_5);
      output_high(PIN_C2);
      
      //sample at TR6
      set_adc_channel(5);     //read from channel 5
      delay_us(20);           //delay is required after setting channel and bef.read
      //read_adc(ADC_START_ONLY);
      ert_adc_value_6=read_adc();    //starts conversion & store it in value
      ert_voltages_6=(ert_adc_value_6)*5000/4096;// last result of last conversion
      printf("Ch06=%u\r\n",ert_voltages_6);
      output_high(PIN_C3);
      
      //sample at TR7
      set_adc_channel(6);     //read from channel 6
      delay_us(20);           //delay is required after setting channel and bef.read
      //read_adc(ADC_START_ONLY);
      ert_adc_value_7=read_adc();    //starts conversion & store it in value
      ert_voltages_7=(ert_adc_value_7)*5000/4096;// last result of last conversion
      printf("Ch07=%u\r\n",ert_voltages_7);
      
          
      
      }

    //}//end kbhit
   //}//end while
}//end main
 
Hi,
If you are getting unreadable characters, it's not the ADC settings.

You basic clock setup looks to be wrong - you say you are using a 20MHz crystal, but the clock config appears to be RC Osc. mode; it should be "HS" if I remember right.

You looks to be using the CCS compiler?
If so you can configure the oscillator just after including the device header, with a line such as:

#USE DELAY(clock=80MHz, xtal=20MHz)

From a quick look at the data sheet that should be valid - "HS/2 w/PLL 8x"
Or "HS/3 w/PLL 16x" is also valid for a 20MHz xtal; that's presumably clock=106 or 107 MHz.

#use_delay sets all the clock-related fuses to match.
 
Hi,

Thanks for the reply. Now I know the clock setup causes the unreadable characters. I follow your advice and I come up with this error message: "option invalid. No PLL option". Any advice?

#fuses HS2_PLL8,NOWDT, NOPROTECT, PUT64, BORV27,
#use delay(clock=80000000, xtal=20000000)
#use rs232(baud=9600,UART1,ERRORS)

TQ
Arelone
 
Hi,

I'm using ccs c compiler ver 5.010. I think the error comes from the clock value (80MHz). But I had no idea how to calculate it.

Arelone
 
As I mentioned, the "use delay" function does all the clock setting - try it with the clock-related stuff removed from the fuses line.
 
I've just tried building it in mplab with the HS2_PLL8 taken out of the fuses line and the extra comma removed from the end of that.

It compiles fine with either

#include <30F6014A.h>
#device ADC=12
#include <float.h>
#include <string.h>
#fuses NOWDT, NOPROTECT, PUT64, BORV27
#use delay(clock=80MHz, xtal=20MHz)
#use rs232(baud=9600,UART1,ERRORS)

or

#include <30F6014A.h>
#device ADC=12
#include <float.h>
#include <string.h>
#fuses NOWDT, NOPROTECT, PUT64, BORV27
#use delay(clock=106MHz, xtal=20MHz)
#use rs232(baud=9600,UART1,ERRORS)

though I have no way of testing the result..

Edit - For info, I'm using MPLab-X version 5.10 and CCS compiler version 5.088
 
Last edited:
Hi,
Thanks for your help. I changed the xtal=20MHz to osc=20MHz and the code has no error. However the dspic is not with me and I could not test the result. I will update later.

TQ
Arelone.
 
Hi,
Thanks for the advice. Finally the UART works smoothly. I use the following code and change the crystal to 7.37MHz.
#fuses NOWDT, NOPROTECT, PUT64, BORV27
#use delay (clock=58982400, xtal=7372800)
 
Hi,
For the ADC, is there any other functions that works similar to the "printf " function? I noticed to complete the code below using "printf" function takes approx. 1.52 seconds for 16 adc values which is slow. I removed the "printf" function and found time to complete (without sending the value) was 30ms. Is there a way I can improve this?Please advice.

frahman3

//sample at TR5
set_adc_channel(4); //read from channel 4
delay_us(20); //delay is required after setting channel and bef.read
//read_adc(ADC_START_ONLY);
ert_adc_value_5=read_adc(); //starts conversion & store it in value
ert_voltages_5=(ert_adc_value_5)*5000/4096;// last result of last conversion
printf("Ch05=%u\n\r",ert_voltages_5);
output_high(PIN_C2);
 
I believe you are using printf() to output directly to a serial port? If that is true, I'd guess the delays will mainly be due to the time needed to send the data serially.

You could use sprintf() to store the formatted results in an array of character strings, then output the text later on after reading all the ADC values.
 
Hi,
Thanks for the advice. I created a simple code below to read from 2 adc channels using sprintf(). I compiled with no errors but no output displayed. How to write a code to store array of strings and (in this case) send out two adc values when last adc channel read. I'm using ccs c v.5.010
TQ
frahman3

CSS:
#include <30F6014A.h>
#device ADC=12
#include <float.h>
#include <string.h>
#fuses NOWDT, NOPROTECT, PUT64, BORV27
#use delay (clock=58982400, xtal=7372800)
#use rs232(baud=9600,UART1,ERRORS)

char adc_value_5[20];
char adc_value_6[20];


unsigned char ert_adc_value_5;
unsigned char ert_adc_value_6;

unsigned char ert_voltages_5;
unsigned char ert_voltages_6;

void task1()
     {
      set_adc_channel(4);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      //read_adc(ADC_START_ONLY);
      ert_adc_value_5=read_adc();    //starts conversion & store it in value
      ert_voltages_5=(ert_adc_value_5)*5000/4096;// last result of last conversion
      //printf("Ch04=%u\n\r",ert_voltages_5);
      sprintf(adc_value_5,"Ch05=<%u\r\n>",ert_voltages_5);
      }
      
void task2()
      {
      set_adc_channel(5);     //read from channel 0
      delay_us(10);           //delay is required after setting channel and bef.read
      //read_adc(ADC_START_ONLY);
      ert_adc_value_6=read_adc();    //starts conversion & store it in value
      ert_voltages_6=(ert_adc_value_6)*5000/4096;// last result of last conversion
      //printf("Ch05=%u\r\n",ert_voltages_6);
      sprintf(adc_value_6,"Ch05=<%u\r\n>",ert_voltages_6);
      }
      
      
void main(void)                     //void=nothing, no return value
{

set_TRIS_D(0x0000);
SETUP_ADC(adc_OFF);
   setup_adc_ports(ALL_ANALOG); 
   setup_adc_ports(sAN0 |sAN1|sAN2|sAN3|sAN4|sAN5|sAN6|sAN7|sAN8|sAN9|sAN10|sAN11|sAN12|sAN13|sAN14|sAN15, VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_64 | ADC_TAD_MUL_2);
   //setup_adc(ADC_CLOCK_INTERNAL);
  
while(TRUE)
     {
      task1();
      task2();
     }//end while
}//end main
 
Add this after reading the values:

C:
puts(adc_value_5);
putc('\n');
puts(adc_value_6);
putc('\n');
 
Hi,
I make the change as advised. However I received unreadable characters. What something obvious that I missed here?
frahman3

void task1()
{
set_adc_channel(5); //read from channel 0
delay_us(10); //delay is required after setting channel and bef.read
//read_adc(ADC_START_ONLY);
ert_adc_value_5=read_adc(); //starts conversion & store it in value
ert_voltages_5=(ert_adc_value_5)*5000/4096;// last result of last conversion
puts(ert_voltages_5);
putc('\n');
}
 
I thought the idea was to move the serial output away from the ADC section?
As it is now, the formatted data is never written to ert_voltages_5, so you are printing whatever random values are in memory.

Put the sprintf() back as it was, then add the extra lines in the while (true) loop, after calling the ADC routines.
(Sorry for short reply, in a rush - I'll explain more later).
 
Last edited:
Hi,
sorry again for the previous short answer, I was supposed to be leaving to visit a customer.

To make the overall program simpler, why not make a single generic routine that can read any ADC?

eg.

C:
unsigned int ert_adc_value[8];
unsigned int ert_voltages[8];
char adc_value[8][20];

void do_adc(int chan)
     {
      set_adc_channel(chan);     //read from channel
      delay_us(10);           //delay is required after setting channel and bef.read
      ert_adc_value[chan]=read_adc();    //starts conversion & store it in value
      ert_voltages[chan]=(ert_adc_value[chan])*5000/4096;// last result of last conversion
      sprintf(adc_value[chan],"Ch%02d=<%u\r\n>",chan, ert_voltages[chan]);
      }

Note that you have ADC result size set to 12, so you need an int to store it without the value being truncated.

You can call that routine eg. in a for() loop to get as many channel readings in to the arrays as you wish, or call it with specific values.

To send the results to the serial port, call puts() and pass each element of the string array; again either in a loop or individually.
Use putc('\n'); after each to add the new line character, so all the strings do not run in to each other.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…