View Full Version : MCP 3421 Confusion...

01-25-2013, 06:15 PM
Hi everyone,

I am using a MCP3421 ADC (http://ww1.microchip.com/downloads/en/DeviceDoc/22003b.pdf) from Microchip as a ADC for a thermistor array. The chip offers resolutions from 12-18 bits, uses the I2C bus. I have no issues communicating with the chip, verifying when conversions have finished, etc. but the two's complement math is kicking my rear.

Since the values being returned by the function can be 18-bit (signed), I elected to define the return value as int32_t. In 18-bit operation, the chip sends 3 bytes of data, otherwise, it returns two. Bits are defined as 12-18.

int32_t Read_Thermistor_Value (byte Bits)
uint8_t Hi=0, Med=0, Lo=0;
uint8_t reads=2; // default number of bytes that have to be read for every conversion
int32_t ADC_Value=0; // return value

if (Bits==18) reads+=1; // add another round of reads if 18 bit mode is used.

Wire.requestFrom((int)MCP3421_address, (int) reads);
if (reads ==3) Hi = Wire.read(); //only read top byte if 18 bit conversion is used
Med = Wire.read(); //Otherwise, just read two bits
Lo = Wire.read();

//here use signed int32_t and right + left shifts to carry the sign and append the value as needed
//for 18-bit operations, only the last bit of the Hi byte is relevant. The remaining bits are sign bits
if (Bits == 18) ADC_Value = (((Hi << 30) | (Med << 22)) | (Lo << 14)) >> 14;

//for all other resolutions, the returned value is left shifted based on the resolution to preserve the sign,
//then right shifted back, i.e. if 16 bit resolution is needed, left shift Med 24 bits, while 12-byte values
//would require a 28-bit left shift.
else ADC_Value = ((Med << (40-Bits)) | (Lo << (32-Bits))) >> (32-Bits);

return ADC_Value;

Trouble is, I am getting great values under 18 bit operation and garbage for the rest. I know I'm doing something wrong, but can't figure it out... Too dumb for two's complement math. Argh!

01-25-2013, 06:29 PM
More egg on my face, just figured out that the MCU was not waiting to read back the results. That's fixed but the results still do not match the output from the MCP3421 library (http://interface.khm.de/index.php/lab-log/connect-a-mcp3421-18-bit-analog-to-digital-converter-to-an-arduino-board/) (which is not fully 32-bit compatible).

01-27-2013, 05:43 PM
Try this:

if (Bits == 18) {
ADC_Value = (Hi << 16) | (Med << 8) | Lo;
if (Hi & 0x80) ADC_Value |= 0xFF000000;