0
votes

I'm struggling to get the ADC to work with my device. I'm using the dsPIC33FJ128GP802 and have attempted to start off slow with manual sampling and conversion.

My code is posted below, I've set every register for ADC and have then attempted to sample just once to get the voltage from a sensor I've got attached. The value I should be seeing is around 0.7V, but what I'm getting is in the region of -17408 (10111100 00000000). This can go up to somewhere around -2000, but the value shouldn't be negative in the first place.

#include <p33Fxxxx.h>

_FOSCSEL(FNOSC_FRCPLL) // select internal 7.37MHz osc with PPL
_FOSC(OSCIOFNC_OFF & POSCMD_XT) // no clock output, external OSC disabled
_FWDT(FWDTEN_OFF) // disable the watchdog timer
_FPOR(FPWRT_PWR1) // Turn off the power-up timers.

int ADCValue;

void DELAY(unsigned ms) {
    unsigned j;
    unsigned i;
    for (j = 0; j < ms; j++) {
        for (i = 0; i < 0x1F40; i++);
    }
 }

 int main(void) {

    // set up clock to 80MHz
    PLLFBD = 41; // sets M = 41+2 = 43
    CLKDIVbits.PLLPRE = 0; // sets N1 = 2
    CLKDIVbits.PLLPOST = 0; // sets N2 = 2
    while (!OSCCONbits.LOCK); // wait for PLL ready

    AD1CON1 = 0; // set everything to zero to start with.
    AD1CON1bits.ADON = 0; // turn ADC off.
    AD1CON1bits.ADSIDL = 0; // continue module operation in idle mode.
    AD1CON1bits.ADDMABM = 1; // DMA buffers are written in the order of conversion.
    AD1CON1bits.AD12B = 0; // set to 10bit mode.
    AD1CON1bits.FORM = 3; // set data output to signed fractional.
    AD1CON1bits.SSRC = 0; // manual conversion. clearing sample bit manually.
    AD1CON1bits.SIMSAM = 1; // collect samples from channels 0, 1, 2, 3 simultaneously.
    AD1CON1bits.ASAM = 0; // manual sample. samples when SAMP bit is set.
    AD1CON1bits.SAMP = 0; // sample enable bit.
    AD1CON1bits.DONE = 0; // ADC conversion status bit.

    AD1CON2 = 0; // set everything to zero to start with.
    AD1CON2bits.VCFG = 0; // converter voltage ref. set to AVdd and AVss.
    AD1CON2bits.CSCNA = 0; // input scan select bit. set to do not scan.
    AD1CON2bits.CHPS = 0; // channel select bits. set to just channel 0;
    AD1CON2bits.BUFS = 0; // buffer fill status (invalid as BUFM is 0);
    AD1CON2bits.SMPI = 0; // ADC interrupt is generated after every sample/conversion.
    AD1CON2bits.BUFM = 0; // buffer fill mode. set to always start filling from start address.
    AD1CON2bits.ALTS = 0; // Alternate input sample mode. set to always uses channel input from sample A.

    AD1CON3 = 0; // set everything to zero to start with.
    AD1CON3bits.ADRC = 0; // ADC conversion clock derived from system clock.
    AD1CON3bits.SAMC = 0; // auto sample time bits, TAD, set to 0.
    AD1CON3bits.ADCS = 0; // ADC conversion clock set to 0. 1 * TCY = TAD.

    AD1CON4 = 0; // set everything to zero to start with.
    AD1CON4bits.DMABL = 0; // allocates 1 word of buffer to each analogue input.

    AD1CHS123 = 0; // everything set to zero as not using channels 1, 2, or 3.

    AD1CHS0 = 0; // set everything to zero to start with.
    AD1CHS0bits.CH0NB = 0; // channel 0 negative input, set by CH0NA. sample B.
    AD1CHS0bits.CH0SB = 0; // channel 0 positive input, set by CH0SA. sample B.
    AD1CHS0bits.CH0NA = 0; // channel 0 negative input, for sample A. set to VREFL.
    AD1CHS0bits.CH0SA = 0; // channel 0 positive input is AN0.

    AD1CSSL = 0; // input scan register set to zero as not using it.

    AD1PCFGL = 0; // port configuration, set to analogue mode, ADC samples voltage.

    AD1CON1bits.ADON = 1; // turn on ADC

    AD1CON1bits.SAMP = 1; // Start sampling
    DELAY(1); // Wait for sampling time (1ms)
    AD1CON1bits.SAMP = 0; // Start the conversion
    while (!AD1CON1bits.DONE); // Wait for the conversion to complete
    ADCValue = ADC1BUF0; // Read the conversion result

    while (1);

}

I have the sensor powered using the same rails the PIC is using, and I have the output of the sensor to AN0 (pin 2), as I set it up in the code. The PIC is powered to the standard Vss and Vdd (pins 8 and 13), the analogue power pins AVdd and AVss (pins 28 and 27), and a 33uF capacitor across Vcap and Vss (pins 20 and 19). Is there anything else I need to do hardware-wise? I'm a little confused with the AD1CHS0bits.CH0NA register, as I don't know if I have to connect ground to VREFL or what to do in that instance.

Any help with what I should be doing to correct this issue will be most appreciated! Also, any help on how to convert the value once it's received correctly will be very helpful.

1
If you don't think the value should be negative, don't use an int. Your bit pattern interpreted as unsigned int is 48128.unwind
@unwind: If the comments in the code are accurate then the output format of the data is signed fixed point format. The output value in the conversion buffer ranges from -1.0 to +1.0 representing the voltage range from reference low to reference high. If the OP is only providing a 0V for the reference low then he probably needs to use an unsigned fractional format as well as using an unsigned data type in the C code. As it stands, -17408 can be divided by 32768 to the range -1.0 to 1.0 and then scaling that linearly for AVss to AVdd (I assume 3V3) gives a value of 0.77. Using 3V gives 0.7V.tinman
What voltage are you running your PIC from?tinman
Thanks, tinman. The voltage is indeed at 3.3V. You're saying that these values look correct? I guess I'm just converting the value incorrectly. The comments in the code are correct, the value is a signed integer.ritchie888
The value may be correct, it depends how close to 0.7 the value you should be seeing is. I'd try it with a couple of other known voltages if possible and check the conversion. If you're only putting a unipolar signal in to your input and your high and low references are >= 0V then you might want to use an unsigned format as unwind suggested, although it doesn't really matter since it's just a linear mapping from <ADC output minimum> to <ADC output maximum> to <reference low> to <reference high>.tinman

1 Answers

1
votes

If the value shouldn't be negative to start with, then you shouldn't be using this setting:

AD1CON1bits.FORM = 3; // set data output to signed fractional.

Were I would expect your value to be (evaluated using Python):

int((2**10) *      # 10-bit operation
    (0.7/3.3)      # 0.7 volts on a 3.3 volt system
    - (2**9)       # Center at VDD / 2 because of signed operation
    ) << 6         # Fractional output left-shifted the 10-bit up by 6 to fill 16-bits
= -18816

Which sounds about what your code is outputting.

Instead use:

AD1CON1bits.FORM = 0; // set data output to integer.

Using this settings, along with 10-bit mode, I would expect your value to be

int((2**10) *      # 10-bit operation
    (0.7/3.3))     # 0.7 volts on a 3.3 volt system
= 217