NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Sensors, Actuators, and Robotics » External ADC with serial interface - Need help tweaking

February 20, 2012
by bcbarron
bcbarron's Avatar

I have put together the below code for interfacing an external ADC via the serial interface with the MCU. it communicates and the data is somewhat to be expected but there are some irregularities I can't figure out. The setup uses a thin beam load cell with a CS3002 amp and a MCP3553 22bit ADC. The system measures a relatively small amount of fluid flowing out of a vessel over a long period of time.

The problem I think I am having is that there is some error in the creation of the 32 bit number which is the ADC signal collected by the MCU from the 8bit bytes. I am getting output numbers that are relatively low (2,800 to 9,999) for the voltage going into the ADC (2.5 volts at rest, 3.75 volts at full weight). I am also getting output numbers that are linear but repeat. Once 9999 is reached it starts over at 1000 and continues to increase. With 22 bit resolution and the voltage difference mentioned above going into the ADC I should be getting numbers in the 28,000 to +/-60,000 range.

When I have used the internal ADC I encountered headroom issues where the numbers simply stopped at the upper end of the ADC resolution. This is different.

I may also be getting errors due to variable type declarations.

Code is posted below.

Any help would be great! Brent

include <mxapi.h>
#include <lcd.h>
#include <lcd.c>
#include <stdlib.h>
#include <stdio.h>
#include <avr/io.h>
//SPI PIN CONFIGURATIONS
    #define SPI_PORT    PORTB
    #define SPI_DDR     DDRB
    //PINS
    #define SCK_POS     PB5
    #define MISO_POS    PB4
    #define MOSI_POS    PB3
    #define SS_POS      PB2
//CS : Chip Select
#define CS_PORT PORTB
#define CS_DDR  DDRB
#define CS_POS PB2
#define CS_HIGH() CS_PORT|=(1<<CS_POS)
#define CS_LOW() CS_PORT&=(~(1<<CS_POS))
/////////////////////////////////////////////////////////////
unsigned long SPIWrite(data)
    {
    DDRB |= (1<<2)|(1<<3)|(1<<5);    // SCK, MOSI and SS as outputs
    DDRB &= ~(1<<4);                 // MISO as input

    SPCR |= (1<<MSTR);               // Set as Master
    SPCR |= (1<<SPR0)|(1<<SPR1);     // Clock / 128
    SPCR |= (1<<SPE);                // Enable SPI
    SPCR |= (0<<CPOL)|(0<<CPHA);

    SPSR |= (0<<SPI2X);

    delay_ms(24);

    while(1)
    {
        SPDR = data;                 // send the data
        while(!(SPSR & (1<<SPIF)));  // wait until transmission is complete

        unsigned char byte; 
        byte=SPDR;  
        return (byte);
    }
    }
//////////////////////////////////////////////////////////////////////////
unsigned long ReadADCEx()
    {
    CS_LOW(); 
    delay_ms(30);

    unsigned int high=SPIWrite(0x00);
    delay_ms(30);

    unsigned int mid=SPIWrite(0x00);
    delay_ms(30);

    unsigned int low=SPIWrite(0x00);
    delay_ms(30);

    CS_HIGH();

    unsigned long totalbyte=((high<<16)|(mid<<8)|(low));
    delay_ms(30);

    return(totalbyte);
    }
//////////////////////////////////////////////////////////////////////
int main()
    {
    lcd_init();
    FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);

    unsigned long result;

    while(1==1)
        {
        result=ReadADCEx(0);
        lcd_home();
        lcd_line_one();
        fprintf(&lcd_stream,("Strain: %ld"), result);
        delay_ms(20);                               
        }
    return 1;
    }
February 20, 2012
by pcbolt
pcbolt's Avatar

Hi bcbarron -

I'm surprised the compiler isn't screaming warnings at you. I know mine would :-)

In the SPIWrite declaration, your returning an unsigned long (32 bits) but actually returning an unsigned char (8 bits). Also, I don't think the while (1) loop needs to be there since it returns after running once only. None of this is really a problem, just irregular.

Also, when you call SPIWrite from ReadADCex(), your assigning the unsigned long to an unsigned int (16-bit). Again not really a problem from a result-oriented point of view.

The one problem that actually may BE a problem is on line 63. When you take the 16-bit variable "high" and shift it 16 bits left, I think you are just clearing it. In essence moving the data stored in "high" out of 16-bit range. Usually the compiler warns you against this, but it may be lost in a sea of warnings. I would change that line from:

unsigned long totalbyte=((high<<16)|(mid<<8)|(low));

To:

unsigned long totalbyte=((((unsigned long) high)<<16)|(mid<<8)|(low));

Essentially this "casts" a 16-bit number to a 32-bit and retains the numbers you need. Or you could just change the initialization of "high" to unsigned long. Granted none of this explains a rollover at the 9999 mark. Decimal limits usually have little meaning in the hex/binary world. Rollovers in hex world happen at 255 (8-bit) and 65536 (16-bit).

Just my $0.02

February 20, 2012
by pcbolt
pcbolt's Avatar

Brent-

Just a wild thought. I was thinking about the 9999 problem. Decimal ASCII text sent out would top out at 9999 using 32-bits of data (4 digits @ 8 bits each). I'm not sure how that would apply to your code. Worth a look I guess.

February 21, 2012
by bcbarron
bcbarron's Avatar

Thanks for the comments. My compiler has been warning about line 63 but I figured one thing at a time. I changed the code per your comments one portion at a time and tested it out as I went. The SPI write decleration and the high to unsigned long change modified the output. Getting rid of the while loop did not change it but did clean things up. Changing high to unsigned long had the affect of turning the output into a huge number(16,721,821 or so) but the voltage change of 2.5 volts to 3.2 volts from the amp only pruduced a numberical change of xx,xx1,820 to xx,xx2,840. This results in a resolution that is substantially less than the 12 bit ADC produced. Puzzled. Brent

February 21, 2012
by pcbolt
pcbolt's Avatar

Brent -

Just a shot in the dark...when you initialize the SPI, isn't there a bit you can set to pick between MSB and LSB? I don't have my datasheet handy. Also, is it possible the byte order of the 12-bit number is reversed? That would give you 4 possibilities to try. MSB with your byte order, LSB with your byte order, MSB with reversed byte order and LSB with reversed byte order. One you've tried, maybe one of the others is what you need.

February 24, 2012
by pcbolt
pcbolt's Avatar

Brent -

I'm a little confused about one thing. If the ADC is 12-bits, why does it take 3 bytes (24 bits) to store the reading? I'm guessing maybe the ADC only outputs four-bits at a time, but if so your bit-shifting formula needs to be revised. Also, the 12-bit number should only range between 0 and 4096.

February 27, 2012
by pcbolt
pcbolt's Avatar

Ok just read the data sheet for the MCP3553. Looks like you code should work with the changes you mentioned except for 1 thing. The "high" byte should be masked to only accept the lowest 5 bits. So

unsigned long totalbyte=((((unsigned long) high)<<16)|(mid<<8)|(low));

should become

unsigned long totalbyte=((((unsigned long) (high & 0x1F))<<16)|(mid<<8)|(low));

I see that the 12-ADC in you're last post was just a typo.

Post a Reply

Please log in to post a reply.

Did you know that you can generate hundreds of volts AC from your microcontroller with a little bit of circuitry? Learn more...