1

Good morning, I am using the ADC1 of a dsPIC33EP512GM604 and getting incorrect converted values. To check this I made a cycle of 10 consecutive sampling/conversions. The first value is always quite different from the rest of them, but it is the nearest to the "right" value. Here is the relevant code:

/*  Setup ADC1 for measuring R  */
    ANSELBbits.ANSB3 = 1;       //ensure AN5 is analog
    TRISBbits.TRISB3 = 1;        //ensure AN5 is input

    AD1CON1 = 0;
    AD1CON1bits.ADSIDL = 1;
    AD1CON1bits.AD12B = 1;
    AD1CON1bits.SSRC = 7;
    AD1CON2 = 0;
    AD1CON2bits.VCFG = 0b001;
    AD1CON2bits.SMPI = 0;
    AD1CON3=0;
    AD1CON3bits.SAMC = 0b11111;
    AD1CON3bits.ADCS = 0;
    AD1CON4 = 0;                // no dma

    AD1CHS0bits.CH0NA = 0;
    AD1CHS0bits.CH0SA = 5;

    IFS0bits.AD1IF = 0;         // Clear the A/D interrupt flag bit
    IEC0bits.AD1IE = 0;         // Do Not Enable A/D interrupt
    
/*  Read voltage value  */    
    AD1CON1bits.ADON = 1;       // Enable the A/D converter
    __delay_us(25);
    for (N=0; N<10; N++) {
        AD1CON1bits.SAMP = 1;
        __delay_us(5);              // Wait for sampling time (min 3 TAD)
        AD1CON1bits.SAMP = 0;       // Start the conversion
        while (!AD1CON1bits.DONE);  // wait for conversion to finish
        res[N] = (double) ADC1BUF0;

        /* --- just for test ---*/
        sprintf(deb,"ADC1BUF0 = %.0f\r\n", res[N]);
        WriteStringUART1(deb);
        /* ---- end of test ----*/

And here the results, for a certain fixed input voltage corresponding to a value of 215: ADC1BUF0 = 222

ADC1BUF0 = 301

ADC1BUF0 = 296

ADC1BUF0 = 295

ADC1BUF0 = 295

ADC1BUF0 = 296

ADC1BUF0 = 296

ADC1BUF0 = 296

ADC1BUF0 = 296

ADC1BUF0 = 295

The first value 222 is acceptable close to the expected 215, to my purposes, the other values not. What am I doing wrong?

Progman
  • 16,827
  • 6
  • 33
  • 48
Guille
  • 326
  • 2
  • 10

3 Answers3

1

I've used dsPIC33FJ64MC802 and was able to use the ADC.

I don't have much idea why the readings are that way. The code below worked for me. However, I can't say for sure that it will work properly for you.

void initADC() {
    AD1CON1 = 0;
    AD1CON1bits.AD12B = 1;
    AD1CON2 = 0;
    AD1CON3 = 0;
    AD1CON3bits.ADCS = 2;
    AD1CHS0 = 0;

    AD1CON1bits.ADON = 1;
    delayMs(1);
}

int readADC(char pin, unsigned samplingCycles) {
    AD1CHS0bits.CH0SA = pin;
    AD1CON1bits.SAMP = 1;
    __delay32(samplingCycles);
    AD1CON1bits.SAMP = 0;
    while(!AD1CON1bits.DONE);
    return ADC1BUF0;
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
1

Thanks to everybody for the contributions. I finally got the trick. I then post the answer to my own question in case it may help someone. Here is the trick: The dsPIC can use different VrefH for the ADC1, ie. internal Vdd or external on PIN. I used a HW that takes 2.5V external Vref on a dsPIC pin to be used as VrefH, and set the ADC accordingly. The problem is that the dsPIC specs state that VrefH external should be greater than 2.7 V. So 2.5 was not sufficient to make it work well. That's foolishly it!

Guille
  • 326
  • 2
  • 10
0

Here is ADC code example which I made before and connection diagram for AN2 with dsPIC33EV.

enter image description here

#define FCY 3685000UL // __delay_ms() and __delay_us() depend on FCY. 

#include <xc.h> // p33EV256GM102.h
#include <libpic30.h>

void pins_init()
{
    // ANSELx has a default value of 0xFFFF
    // 1 = Pin is configured as an analog input
    // 0 = Pin is configured as a digital I/O pin
    ANSELA = ANSELB = 0x0000; // In most cases, it is better to initially set them to 0.
}

unsigned short adc1_raw = 0;

void adc1_init()
{
    // See 70621c.pdf, Example 16-3: Code Sequence to Set Up ADC Inputs

    ANSELBbits.ANSB0 = 1; // pin 4 - AN2/RPI32/RB0. 1 = Pin is configured as an analog input

    AD1CHS0bits.CH0SA = 2; // Channel 0 positive input is AN2. See AD1CON2bits.ALTS
    AD1CHS0bits.CH0NA = 0; // 0 = Channel 0 negative input is VREFL

    AD1CON1bits.FORM = 0;  // 00 = unsigned integer VREFL:0..VREFH:1023
    AD1CON1bits.ASAM = 0;  // 0 = Sampling begins when SAMP bit is set
    AD1CON1bits.AD12B = 0; // 0 = 10-bit, 4-channel ADC operation

    AD1CON2bits.VCFG = 0;  // AVdd for VREFH and AVss for VREFL
    AD1CON2bits.CHPS = 0;  // 00 = Converts CH0. Select 1-channel mode.
    AD1CON2bits.CSCNA = 0; // 0 = Does not scan inputs
    AD1CON2bits.ALTS = 0;  // 0 = Always uses channel input selects for Sample MUX A

    AD1CON3bits.ADRC = 0;  // 0 = clocked from the instruction cycle clock (TCY)
    AD1CON3bits.ADCS = 15; // Conversion Clock Select bits. Tad=Tcy*(ADCS+1)=(1/Fcy)*(ADCS+1)
                           // Tad = (1/3685000)*(15+1) = 4342 ns
                           // (10-bit) Tconv = 12 * Tad = 52104 ns = 52.1 us.

    IFS0bits.AD1IF = 0;   // Clear the A/D interrupt flag bit
    IEC0bits.AD1IE = 0;   // Do Not Enable A/D interrupt
    AD1CON1bits.ADON = 1; // turn on ADC
    __delay_us(20);       // ADC stabilization delay.
}

unsigned short adc1_read()
{
    AD1CON1bits.SAMP = 1; // start sampling
    __delay_us(100);      // Wait for (enough) sampling time
    AD1CON1bits.SAMP = 0; // start converting. See AD1CON1bits.ASAM
    while (!AD1CON1bits.DONE)
    {
        ; // Wait for the conversion to complete.
          // In bare metal programming, most infinite loop issues
          // are handled by watchdog reset.
    }

    return ADC1BUF0;
}
    
int main(void)
{
    pins_init();
    
    adc1_init();

    while (1)
    {
        ClrWdt();

        adc1_raw = adc1_read();
    }

    return 0;
}
JaeMann Yeh
  • 358
  • 2
  • 9