General > General Technical Chat

Microchip MCP3550 - is the data sheet a load of tosh?

<< < (4/5) > >>

tszaboo:

--- Quote from: peter-h on October 28, 2023, 08:34:19 am ---I've been testing this chip a bit more and the spec is impressive. It is in the 0.01% ballpark. Not bad for 3 quid.

The Vcm goes down to about -0.3V and certainly full accuracy is maintained down to past -0.2V. This is the case for all these capacitor based delta-sigma chips; they all go a bit below the -ve rail. I limit Vcm anyway explicitly


--- End quote ---
Otherwise this ADC is an OK chip. It doesn't have a built-in ref, and I've seen it used for ratiometric measurements, overall good results, thousands of products shipped.


--- Quote from: peter-h on October 29, 2023, 08:39:04 am ---What I don't get is the total lack of QA within Microchip in this area. Other people must have reported this. The latest DS on their website is from 2014.

--- End quote ---
I worked together with an ex-Microchip employee, who was working on documentation (amongst other things). It's the lack of time, overtime, stress, foreigners who's mother thounge isn't english, combination of these.

Nominal Animal:

--- Quote from: peter-h on October 29, 2023, 09:04:14 pm ---The ADS1118 is also weird, for example; look at how it encodes the temp sensor value.
--- End quote ---
Nah, just logical shift left by 16 bits to left-align it to 32 bits, followed by arithmetic shift right by 18 bits, and you're done.  For Cortex-M3/M4/M7, a single 'SBFX rn, rn, #2, #14' does it (typically in a single cycle) in-place.
With GCC and Clang, you can use (((int32_t)(((uint32_t)value)<<16))>>18), which will compile to basically optimal code on Cortex-Ms (bit shifts on Cortex-M0/M0+), RISC-V, x86, and x86-64.

On 8-bit and 16-bit architectures (ADS1118 is from 2010), just arithmetic shift right by two bits.
On an AVR, that's just 'ASR rhi', 'LSR rlo', 'ASR rhi', 'LSR rlo'.
On an 8-bit PIC, I count nine instructions: 'CLRF tmp', 'BTFSC hi,7', 'DECF tmp,1', 'RRF tmp,1', 'RRF hi,1', 'RRF lo,1', 'RRF tmp,1', 'RRF hi,1', 'RRF lo,1', using an extra temporary byte (although I guess one can use the W accumulator as WREG on later PICs).  But PIC are wonky anyway.

For the MCP3550, one has to delete one bit (OVH) from the middle of the word, turn 0bABCD... into 0bAACD..., to get the 24-bit two's complement value.  Basically, swapping the order of <OVH> and <OVL> bits would have saved a lot of operations, and replacing <OVH> with <OVH OR OVL> made the highest bit into "overflow did occur" with the rest of the bits forming the (two's complement if negative) actual value.

Sure, we can deal with it.  It just feels .. wonky.  Like bicycle pedals that are tilted from horizontal.


--- Quote from: peter-h on October 29, 2023, 09:04:14 pm ---But such obvious discrepancies from one DS page to another...
--- End quote ---
True.  The fact that they (Microchip, and others) won't fix the damn datasheets really pisses me off.


--- Quote from: tszaboo on October 29, 2023, 10:44:03 pm ---It's the lack of time, overtime, stress, foreigners who's mother thounge isn't english, combination of these.
--- End quote ---
Yup: company culture directed by their leadership.  Even profit has its costs.

peter-h:
Yes the ADS1118 is simple if you do only the minimum :) In practice, stuff just gets fatter and fatter...


--- Code: ---

/*
  * Read the specified ADS1118
  * adc   = 0 for internal (ADS1118_ONBOARD), 1 for external (ADS1118_EXTERNAL, 655 board)
  * mux   = 3 bits of mux setting: 4/5/6/7 for AIN0 / AIN1 / AIN2 / AIN3 (see #defines in adc.h)
  * range = 3 bits of range setting 5/4/3/2/1 for _256mV/_512mV/_1024mV/_2048mV/_4096mV (see #defines in adc.h)
  * sps   = 3 bits of samples per second 8-860 - see #defines in adc.h
  * temp  = 0 for normal mode, 1 to read ADC temperature in degC * 100
  * Execution time depends on sps.
  * Uses single conversion mode. First it shifts in the command word (16 bits) and discards the returned value.
  * Then after waiting for it to finish it shifts in a "NOP" command to grab the returned value.
  * No other way to do it since we have no way to detect end of conversion.
  * If temp=1, the conversion speed is forced to the fastest - SPS_860 - and the difference is < 0.1C
  *
  * The chip temperature mode changes PC11 from the usual pulldown to a pullup, to ensure that an impossible-high
  * temperature is returned (supposedly 32767 i.e. +327.67 degC) if the analog option is not installed.
  *
  * In temp=1 mode the sps value must be the SPS_860 one, due to the simple delay used.
  *
  */

int16_t ADS1118_read ( uint8_t adc, uint8_t mux, uint8_t range, uint8_t sps, uint8_t temp)
{

uint16_t config = 0x8109; // bits 15,9,3,0 are always written with a 1
uint16_t waste;
uint16_t result = 0;
int16_t result2;
uint32_t tmp = 0;

// Force SPS_860 conversion time if temp=1.
if (temp==1) sps = SPS_860;

// wait times in ms for 8 16 32 64 128 250 475 860 conversions/sec (allow for +10%)
static const uint16_t conv_timer[8]  = { 141, 72, 37, 20, 11, 7, 6, 4 };

// assemble the config word
config |= ( mux & 0x07 ) << 12;
config |= ( range & 0x07 ) << 9;
config |= ( sps & 0x07 ) << 5;
config |= ( temp & 0x01 ) << 4;

SPI3_Lock();

// If current SPI3 init is not this device, initialise it
if ( g_spi3_current_config != SPI_MODE_ADS1118 )
{
ADS1118_cs(ADS1118_ONBOARD, 1);
ADS1118_cs(ADS1118_EXTERNAL, 1);
spi3_set_mode(SPI_MODE_ADS1118);
g_spi3_current_config = SPI_MODE_ADS1118;
}

// perform a normal conversion on the selected input
// note: if temp=1 then this works the same but all parameters except sps are ignored

// start single conversion, discard returned value
config |= 0x0002; // load 01 into NOP field
ADS1118_write_read(adc, config, &waste);

// wait for ADC to read it (this delay works only if RTOS is running)
sps = sps & 0x07;
osDelay( conv_timer [sps] );

// The above osDelay() fails if this function is called before RTOS is running (which happens
// in detect_analog_option()) so we delay manually. This delay is hard coded for the
// fastest ADC speed
if (temp==1)
{
hang_around_us(3000);
}

// issue a NOP command but retrieve the value
config &= 0xFFF9; // load 00 into NOP field

if (temp==1)
{
// Enable pullup on PC11 if temp=1 to detect non-installed analog option
tmp = GPIOC->PUPDR;
tmp &= ~(3 << 22); // zero both PUPDR11 bits
tmp |=  (1 << 22); // set them to 01 (pullup)
GPIOC->PUPDR = tmp;
}

ADS1118_write_read(adc, config, &result);

if (temp==1)
{
// Revert PC11 to a pulldown
tmp = GPIOC->PUPDR;
tmp &= ~(3 << 22); // zero both PUPDR11 bits
tmp |=  (2 << 22); // set them to 10 (pulldown)
GPIOC->PUPDR = tmp;
}

result2 = (int16_t) result;  // assume non-temp mode; return signed int

if (temp==1)
{

// This is to detect no analog option installed (result=0xffff)
// Needs a pullup on PC11 for reading ADS1118 if temp=1
if (result!=0xffff)
{
// now we have a 14 bit 2's complement value in leftmost 14 bits, in units of 0.03125C (1/32C)
// note: rightmost 2 bits are undefined, and even if they were 00 this will muck up -ve numbers

result = result >> 2;   // right-justify it
if ( result & 0x2000 )  // sign extend if negative (if bit 13 was 1)
{
result |= 0xc000;
}

result2 = (int16_t) result;
result2 = (result2*100)/32; // get result to be 100 * C
}
else
{
result2 = 32767; // return a silly high value
}

}

SPI3_Unlock();

return result2;

}


--- End code ---

Nominal Animal:

--- Quote from: peter-h on October 30, 2023, 07:11:24 am ---Yes the ADS1118 is simple if you do only the minimum :)
--- End quote ---
Yes, it has lots of options to configure, so if you expose them, you get a lot of code.

I prefer a slightly different approach, triggering the measurement separately from reading it, so my own code differs.
In particular, I don't like the fact that you keep SPI3 locked for the duration of the conversion.
Might be worth it to add a pair of SPI3_Unlock();, SPI3_Lock(); calls?

Alternatively, replace the fixed wait with a polling wait while the MISO pin is a GPIO with a pull-up, osDelay()ing in suitably short spurts until ADS1118 pulls MISO low.  When the osDelay() is a no-op, that becomes a busy wait, which is what I assume your hang_around_us() function does anyway.

C/C++ compilers do not generally optimize (x*100)/32 into the mathematically equivalent (x*25)/8 (because the result would differ when the multiplication overflows), so we basically have to optimize such ourselves.

You do realize that the temperature reading of 0xFFFF may occur naturally, if the two LSB happen to also be 1?  That is, a temperature result of 0b11111111111111xx corresponds to a temperature of -0.03125°C.
Thus, I'm not certain if your pull-up shenanigans are a reliable indication of the presence of the analog module.

Personally, I would do the same extra-GPIO shenanigans with ADS1118 as with MCP3550 I outlined in reply #11, if the SPI bus is used for anything other than the ADC.  Thus far, I've got by with just reserving the whole SPI bus for the ADC, though.

peter-h:

--- Quote ---I don't like the fact that you keep SPI3 locked for the duration of the conversion
--- End quote ---

You are right - I changed it. All still works :)

I struggle to think of a way to avoid the 0xffff case. It means that if you power cycled the unit at ADC chip temp of (what the sensor thinks is) exactly -0.03125C, it would disable the ADC option. I can live with that :)

Navigation

[0] Message Index

[#] Next page

[*] Previous page

There was an error while thanking
Thanking...
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod