MSP430 and ADC - Page 1
Forum section: electronics / general

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 03:33:21 pm
content: So I've got an MSP430 hooked up to an LTC2400 SPI ADC, and I'm reading out the data to send via serial.I managed to find a code example for arduino that I can use with the MSP430 and Energia, but I've got a problem. When the input voltage goes below zero, the output code goes up to 1677216 rather than just clipping at zero, and I'm also not getting the full resolution as the output code is only going up to 8388608 (2^24/2). I'd also like to use the extra four bits of resolution to take it up to 28bits.I've looked around everywhere, but it's so difficult to find anything on how to use the SPI communication with the MSP430s.If anyone could give me a bit of guidance with this, it would be greatly appreciated.

author: tszaboo
author_rank: Super Contributor
timestamp: February 23, 2014, 04:18:24 pm
content: Have you read the LTC2400 datasheet? Have you read it again? And page 11-12 again?

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 04:19:46 pm
content: Quote from: NANDBlog on February 23, 2014, 04:18:24 pmI've read those pages a good 50 times by now, I understand how I need to do what I want, but I don't know how to implement it in code.

author: dannyf
author_rank: Super Contributor
timestamp: February 23, 2014, 04:50:27 pm
content: QuoteYou need to read the datasheet a little bit better: pay attention to input range + status bits.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 05:16:10 pm
content: Quote from: dannyf on February 23, 2014, 04:50:27 pmSo does the output code go up when Vin goes below zero? And how can I fix this?Also I still don't understand why I'm only getting half the resolution...

author: dannyf
author_rank: Super Contributor
timestamp: February 23, 2014, 07:01:41 pm
content: It will help you tremendously to go back to the datasheet.Others may be able to give you an answer now but that doesn't fundamentally solve the problem for you.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 09:06:08 pm
content: Quote from: dannyf on February 23, 2014, 07:01:41 pmI've been staring at the datasheet for the past 15 minutes, I don't know what I'm missing.

author: fmaimon
author_rank: Unknown
timestamp: February 23, 2014, 09:21:10 pm
content: As are you still having problems with what the datasheet says, you may be better reading about binary representation of numbers,signedand unsigned. Try understanding your LTC2400 library and what each bit of your output value does represent.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 09:37:58 pm
content: Quote from: fmaimon on February 23, 2014, 09:21:10 pmThere's 24 bits in the output code from the MSB to the LSB, I can't work out why I'm not getting a range of 2^24?Could someone not explain this to me? I've tried pretty damn hard to understand the code, but obviously it's been written by someone else with no documentation so it's very difficult to understand since I haven't worked with SPI before.

author: dannyf
author_rank: Super Contributor
timestamp: February 23, 2014, 10:00:40 pm
content: Your issues are far less about the code than the (in)ability to approach a real-life problem. Two examples:1) fmaimon has laid it out for you bare: get the results, convert them to binary and compare them to the datasheet and ask yourself why you are getting what you are getting. How complicated is that?2) You asked people to help you with the code. Yet, you don't feel the need to provide people the code. How's anyone going to help you?All it takes, really, is common sense and hard work (reading the datasheet).

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 10:29:41 pm
content: Look I've read the data sheet, several times.It clearly states in the datasheet that the output code for Vref should be 111111111111111111111111, which converts to 16777215. I'm asking why I'm not getting the full resolution and why the output is not going all the way up to 16777215 but only half that.I'm not the sort of person that doesn't read up on things before they ask, I've spend literally several hours trying to find information on this and to work it out but I don't get it.Telling me to read the datasheet again is just really patronising after how much effort I've put into trying to work this out for myself.My code's here:Code:/*Interface between Arduino DM board and Linear Tech LTC2440 24-bit ADCNov. 12 2010 John BealeLTC2440 <----------> Arduino11: /CS <- to digital pin 10 (SS pin)7: MOSI <- to digital pin 11 (MOSI pin)12: MISO -> to digital pin 12 (MISO pin)13: SCLK <- to digital pin 13 (SCK pin)10: /EXT - ground1: GND - ground2: VDD3 - 5V supply*/#include <SPI.h> // include the SPI library// set I/O pins used in addition to clock, data in, data outconst byte slaveSelectPin = 10; // digital pin 10 for /CSconst byte resetPin = 9; // digital pin 9 for /RESETconst int nsamples = 1; // how many ADC readings to average together// SPI_CLOCK_DIV16 gives me a 1.0 MHz SPI clock, with 16 MHz crystal on Arduinovoid setup() {Serial.begin(9600); // set up serial comm to PC at this baud ratepinMode (slaveSelectPin, OUTPUT);pinMode (resetPin, OUTPUT);digitalWrite(slaveSelectPin,HIGH); // chip select is active lowdigitalWrite(resetPin,HIGH); // reset is active lowSPI.begin(); // initialize SPI, covering MOSI,MISO,SCK signalsSPI.setBitOrder(MSBFIRST); // data is clocked in MSB firstSPI.setDataMode(SPI_MODE0); // SCLK idle low (CPOL=0), MOSI read on rising edge (CPHI=0)SPI.setClockDivider(SPI_CLOCK_DIV16); // system clock = 16 MHz, chip max = 1 MHz}// =============================================================================// Main Loop:// acquire 'nsamples' readings, convert to units of volts, and send out on serial portvoid loop() {int i;long secs;float mins;double volts;long in; // incoming serial 32-bit wordlong sum = 0;for (i=0; i<nsamples; i++) {in = SpiRead();in &= 0x1FFFFFFF; // force high three bits to zeroin = in>>5; // truncate lowest 5 bitssum += in;delay(198); // (msec). Total Looptime: +2 msec (overhead for comms)}// volts = in * 2.5 / 8.388607; // 0x7fffff = 8388607volts = sum * (2.048/8388607); // microvoltsvolts = volts / nsamples;Serial.println(volts, 6);} // end main loop// =================================================================// SpiRead() -- read out 4 bytes from LTC2440 chip via SPI interface// =================================================================long SpiRead(void) {long result = 0;long b;// long result2 = 0;// MOSI/SDI pin 7 HIGH => 7 Hz, best resolutiondigitalWrite(slaveSelectPin,LOW); // take the SS pin low to select the chipdelayMicroseconds(1); // probably not needed, only need 25 nsec delayb = SPI.transfer(0xff); // B3result = b<<8;b = SPI.transfer(0xff); // B2result |= b;result = result<<8;b = SPI.transfer(0xff); // B1result |= b;result = result<<8;b = SPI.transfer(0xff); // B0result |= b;// take the SS pin high to de-select the chip:digitalWrite(slaveSelectPin,HIGH);return(result);}

author: Skimask
author_rank: Super Contributor
timestamp: February 23, 2014, 10:35:41 pm
content: Reread the 8th post. Then rereread it. Then rerereread it.Then Google any and all words in that post.Then rererereread it again.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 10:40:45 pm
content: Quote from: Skimask on February 23, 2014, 10:35:41 pmI guess I've got some reading to do

author: Harvs
author_rank: Super Contributor
timestamp: February 23, 2014, 10:43:54 pm
content: You're truncating the lowest 5 bits, when only the lowest 4 bits are "sub LSBs".Secondly if you want it to do anything reasonable when outside of 0<Vin<VRef then you need to check and handle the EXR bit. As already stated, the table on page 12 is what you need to be looking at.

author: tszaboo
author_rank: Super Contributor
timestamp: February 23, 2014, 10:44:14 pm
content: The thing is: People don't like spoon-feeding an adult person with information. Everything what you need is in the datasheet. But I will end your suffering:The first 4 bit is status bit (Read the friendly datasheet)You need to bit-wise AND your result with a number (not gonna tell what) and then shift it to get the result.From the four byte you read every information is there. You get a stupid big number because a status bit changes, get it? RTFD.And for god sake please learn how to write in hexadecimal. Your pointless 16777215 is a nice, almost round 0xFFFFFF in hex.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 10:47:22 pm
content: Quote from: Harvs on February 23, 2014, 10:43:54 pmSo should I change it to truncate the lowest 4 bits instead? I don't understand what the code's doing, that's my problem here...I'm just hoping someone can help me fix up my code so I can get the data output that I need, and I can sort the rest out from there.All I really want is to get the full 28bits out of the ADC, I'm not worried about the extended range but I need to be able to get it to clip at zero rather than jumping up to 4.096, but I can do that in software if I need to.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 10:53:11 pm
content: Quote from: NANDBlog on February 23, 2014, 10:44:14 pmYeah I get the 4 bits are status bits, but what I still don't get is why I'm not actually getting 0xFFFFFF but only half of that. I should be using all 24 bits.It's not about being spoon fed, it's about the fact that I've spent several bloody hours on trying to work this out for myself and I can't.

author: miguelvp
author_rank: Super Contributor
timestamp: February 23, 2014, 10:54:38 pm
content: I'm a noob too, so I'll give you a hand.Look at Table 2 on page 12. Bit 29 will flip sign when at 0V (actually can be 1 or 0) But then you need to look at bit 28 to see if you are outside of the range (you want that to be 0) if it changes to 1 then the counter will roll to max value and start decreasing as you decrease the voltage.Similar thing happens when you go past Vref, the sign won't change (Bit 29 stays on) but bit 28 (extended input range) will turn on, and it will go from max to 0 again.You could use LTSpice to simulate your circuit to see why you are not reaching Vref for the full 24 bits, or you can measure it to see if your input signal reaches your Vref (Max = Vcc).As far as using bit 29 and 28 to extend your range, i'm afraid you can only go up to 9/8*Vref (12.5% higher than Vref) or down to -1/8*Vref. So if you have 1 Volt Vref (Vcc) you can only go from -0.125V up to 1.125V. So your range would be not the full extra two bits of precision.You will get 25% extra range or 1 is to 2^24 as 1.25 is to x, so it's 20971520, so you getan extra 2,097,152 readings below 0V and also the same amount over Vref.I did spend 10 mins typing this and reading the datasheet so I might be off in something. But at least I hope it helps you understand what you are seeing.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 11:12:45 pm
content: Great, that's really helpfulI'm now truncating the lowest 4 bits and that seems to have solved my 2^24 problem. I now get 2^24 when Vin is connected to Vref.All I need now is to get the Sub LSBs out of it.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 23, 2014, 11:29:11 pm
content: I've been looking at my code and thinking about what's been said here, and I think I've got my head around most of the problem.I could still do with some help on reading out those additional four bits, if someone doesn't mind

author: miguelvp
author_rank: Super Contributor
timestamp: February 23, 2014, 11:45:39 pm
content: Bits 3-0 are sub LSBs below the 24-bit level. Bits 3-0 may be included in averaging or discarded without loss of resolution.You can do two things,1) read the sub LSB bits before truncatingint subLSB = 0;...in = SpiRead();in &= 0x1FFFFFFF; // force high three bits to zerosubLSB = in&0xF;in = in>>4; // truncate lowest 4 bitsIn this case subLSB would be like a fix point float [0.]1111b or 2^-1+2^-2+2^-3+2^-4, that you can use for averaging your least significant bit on "in".2) Or use the full 28 bits. ie (add an extra F to your mask, Edit:no don't do this) and don't shift.One thing is that the most significant bit is the EXT bit, you should check that to see if your value is valid but don't use it for actually computing the value.Also I didn't read if the sub LSB bits are precise at all, they do mention to be use for averaging but up to you.By averaging they mean that if the subLSB is equal or greater than say 1000b (8decimal) you can increment your input by one.Edit: if you go for the full 28bits you have to change your other code.Edit2: if you use the subLSB your input will act pretty noisy, I would average to actually get the 24bit reading more stable.And by averaging I would keep an average of subLSB running so when the input is not changing much it helps a stable reading, not just the round up I mentioned before

author: miguelvp
author_rank: Super Contributor
timestamp: February 24, 2014, 12:12:15 am
content: Actually If I was going to write it myself, I wouldn't get rid of the upper 3 bits, only the upper 2.The code you use clears the sign bit, so you can't tell 0V-1LSB from Vref+1LSB (Edit, nevermind you can tell) but the sign will help you distinguish between those two readings.int signofIn, extRange, subLSB;int subLSBAvg =0;in= SpiRead();signofIn = (in&0x20000000)?1:-1; // bit 29 is 1 when positive, undetermined right at 0V and 0 when below 0VextRange = (in&0x10000000)?1:0; // 1 if outside 0-Vref, 0 if within. I would use true/false if you were doing c++subLSB = in&0xF;subLSBAvg = (subLSBAvg+subLSB)/2;in &= 0x0FFFFFFF; // force high 4 bits to zero (clear the EXT since you have extRange)in = in>>4; // truncate lowest 4 bitsetc..then you can use the data if extRange is not true.you can use the sgnofIn to determine if you went under 0Vuse the subLSB to increment your in by one if greater than 0x8 (or 8 ) to round up.or instead of rounding up with subLSB, use the subLSBAvg to round up get a stable signal.I wouldn't use the low 4bits for input, they would be very peaky.

author: Andreas
author_rank: Super Contributor
timestamp: February 24, 2014, 09:07:14 pm
content: Quote from: Jon86 on February 23, 2014, 11:12:45 pmWhy?Just some questions:- do you really know what the parameter 1.5uV noise (effective value) means as peak/peak value?- how many "noise free" bits will be left over for a single conversion?- how many values will you have to need to average to get near 24 noise free bits?- how much noise is generated by your reference voltage IC?- do you use floating point math on your MSP430?- If yes: what resolution (mantissa) have your floating point variables?- what happens if you specify "double" on your compiler. Will he cheat you and pressing the 8 bytes into 4 bytes?you see: many things to consider above 16 bits resolution.with best regardsAndreas

author: Jon86
author_rank: Frequent Contributor
timestamp: February 25, 2014, 09:32:48 am
content: Awesome, thanks for the help Andreas and MiguelI see what you mean about the last four bits, I guess it's not worth using them. I was hoping just to add them into the averaging on the PC, and get a bit more resolution out of it. I suppose there's not really much point. Thanks

author: dannyf
author_rank: Super Contributor
timestamp: February 25, 2014, 12:08:11 pm
content: QuoteSpot on. Most of those code pieces are written by Arduino people.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 25, 2014, 06:16:03 pm
content: Alright so I've spent a bit of time on this, and I've finally got some pretty simple code going that's doing exactly what I want it to doI know this probably isn't that orthodox, I don't know, I think Dave may have done it with his DAC program, but I'm clocking the SPI bus bit by bit in a for loop, and putting each bit into the variable. I'm just going to send the raw code to the software, along with the sign and extended range bit, and do all the calculations afterwards.I'm actually quite surprised how easy it is to do without any library, it worked first time and now I can super easily change how many bits I have, and which of the signal bits I'm using. I guess now I've done it once I can do it with any ADCThanks for the help guys.

author: dannyf
author_rank: Super Contributor
timestamp: February 25, 2014, 07:00:39 pm
content: QuoteBasically. The experience was painful and at time you might be tempted to give up. But in the end, you learned to read the datasheet and program the chip to the datasheet, which is a skill that will keep giving for the rest of your life.You get a lot more out of this than if someone had given you the answer.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 25, 2014, 07:04:15 pm
content: Quote from: dannyf on February 25, 2014, 07:00:39 pmYeah I really understand what I'm doing now. To be honest I've found it infinitely easier to read each byte individually and toggle the clock pin and so on, rather than just using a library. Also means I can read any bit that I want and do separate operations with it.

author: tszaboo
author_rank: Super Contributor
timestamp: February 25, 2014, 09:15:14 pm
content: the correct term is bit-banging. I really hope at some point we will use the hardware exclusivly this way ,with multiple high speed cores (200mhz). Kinda like xmos already does.

author: dannyf
author_rank: Super Contributor
timestamp: February 25, 2014, 09:46:16 pm
content: QuoteYour bit-banging routines can be made into a library as well. It helps with code clarity, readability and reliability.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 25, 2014, 10:32:34 pm
content: So you're saying that the way I've done it isn't impractical and sluggish?

author: dannyf
author_rank: Super Contributor
timestamp: February 25, 2014, 10:56:31 pm
content: I didn't see how you did it so it would be with basis for me to say if it is sluggish / fast or impractical / practical.I was simply commenting on the fact that you can write your own library to help improve various aspects of your code.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 25, 2014, 11:37:10 pm
content: Quote from: dannyf on February 25, 2014, 10:56:31 pmAh fair enough, well here's my code if you wanted to have a look, it'd be interesting to see what you think.Obviously this is the first time I've ever done anything like this, so sorry if the code is a bit messy.Code://Set up communication pinsint CS = 7;int MasterIn = 6;int SCLK = 5;int EXR = 0;int sign = 1;//Set up variablesint i;int shift;long buffer;long result = 0;void ClockToggle(){//Set clock high, pause and low againdigitalWrite(SCLK, HIGH);delayMicroseconds(10);digitalWrite(SCLK, LOW);delayMicroseconds(10);}void setup(){Serial.begin(9600);//Set up IOpinMode(CS, OUTPUT);digitalWrite(CS, HIGH);pinMode(MasterIn, INPUT);pinMode(SCLK, OUTPUT);delay(500);}void loop(){digitalWrite(CS, LOW);result = 0;delayMicroseconds(2);//Clock through bit 31 and 30ClockToggle();ClockToggle();//Read sign bitsign = digitalRead(MasterIn);ClockToggle();//Read extended range bitEXR = digitalRead(MasterIn);for(i=1; i<24; i++){ClockToggle();buffer = digitalRead(MasterIn);shift = 24-i;result |= buffer<<shift;}Serial.print(sign);Serial.print(",");Serial.print(EXR);Serial.print(",");Serial.println(result);//Deselect ChipdigitalWrite(CS, HIGH);delay(160);}

author: dannyf
author_rank: Super Contributor
timestamp: February 26, 2014, 01:58:13 am
content: This is not very efficient:1) you don't need 10us delay -> the datasheet should have specifications for minimum pulse width and it is unlikely us or more.2) I would write a routine that read them a byte at a time, using the shiftIn() (?) library or roll your own.It would look something like this:Code:unsigned long ltc2400_read(void) {unsigned long tmp=0;digitalWrite(LTC2400_CS, LOW); //pull cs low - idles hightmp = shiftIn(...); //read the msb (bit31..24)tmp = shiftIn(...) | (tmp << 8); //read the 2nd byte (bit 23..16)tmp = shiftIn(...) | (tmp << 8); //read the 3rd byte (bit 15..8)tmp = shiftIn(...) | (tmp << 8); //read the last byte (bit 7..0)digitalWrite(LTC2400_CS, HIGH); //pull cs high -> start next round of adcreturn tmp; //read tmp}...//read ltc2400do {unsigned long tmp = ltc2400_read();} while (tmp & LTC2400_EOC); //read ltc2400 - wait for the conversion to endsig = tmp & LTC2400_SIG; //retain the signageexr = tmp & LTC2400_EXR; //retain the signagesigned long ltc2400_adc=tmp & (0x0ffffffful); //strig adc of the signsif ((~sig) & exr) ltc2400_adc=-ltc2400_adc; //input is negative - sub-digits retained in ltc2400_adcThe resulting number is a signed long and retains all the digits from the adc + sign.

author: dannyf
author_rank: Super Contributor
timestamp: February 26, 2014, 01:59:11 am
content: After that piece of code is debugged, you can then move it to .h/.c files for use in future projects, knowing that it is fully debugged.

author: miguelvp
author_rank: Super Contributor
timestamp: February 26, 2014, 04:49:22 am
content: You can even go crazy and turn the value directly into a double.So I'm going to flex my programming muscles and sorry if I loose you, but hopefully you and others can get something from this quite long reply.Disclaimer, I'm a noob at electronics but not at coding, but I might make a mistake since I'm not even going to compile this to see if it does what I think it does.So lets start by declaring some not too obvious C structures (they also work for C++).Code://// Create a double, little-endian sample. i.e. Intel (NOT Motorola that would be big endian.)//// Define a single bit to fit on an unsigned 64 bit long.typedef unsigned long bit:1;// Define a structure used to access bits individually from 0 to 63.// not really needed but added for completeness.typedef struct{bit bits;} b_section;//// Define a structure that splits a double in sections so we can construct them from the different parts that form them.//// Double Precision Float notes, taken from IEEE 754 i.e. wikipedia//// If exponent is 0x000 is signed zero if mantissa is 0 (-0)// If exponent is 0x000 then is a subnormal if mantissa is not 0.// If exponent is 0x7ff then is infinite if mantissa is 0// If exponent is 0x7ff then is a NaN (Not a Number, ie. 0 divided by 0) if mantissa is not 0.//// Exponent is 1023 biased (0x3ff).// Value if exponent is > 0x000 and <0x7ff, then mantissa has a [1.] implied// -1*(sign)*2^(exponent-1023)*[1.]mantissa// This is what we want for values equal or greater than 1.0 (denoting Vref+1LSB or higher)//// Value on sub normals (if Exponent is 0 and mantissa is different than 0)// -1*(sign)*2^(1-1023)*[0.]mantissa// This is what we want for the range less than 1.0 and into the negative.//// Example values from wikipedia:// 0000 0000 0000 0000 = 0// 8000 0000 0000 0000 = -0// 7ff0 0000 0000 0000 = positive infinite// fff0 0000 0000 0000 = negative infinite//// Little endian so less significant bits go first.typedef struct {unsigned long mantissa:52;unsigned long exponent:11;unsigned long sign:1;} d_section;typedef union {double val;b_section b_val; // b_ for bit valued_section s_val; // s_ for sections value} convert_to_double;Note that a union allows you to see a memory location in different ways. So if you wanted to access bit 0 of a convert_to_double value you can access it by value.b_val.bits, or access bit 63 with value.b_val.bitsNow that we have all those structures and the converting union defined, you read your full 28 bit value (including the sub less significant bit (subLSB) 4 bit value data) as well as the sign and the extra range. First declare your result value of type convert_to_double and also declare the extended range variable for later use.Code:convert_to_double result;int extendedRange;Then, going to your old code (you can adjust this to your newer code if you want) read the whole 32 bits of data, but now we need the space and the default int (32 bits usually) is signed, so we need to go for a long and better make it unsigned. Even if the top two bits are 0 and it shouldn't flip the sign but just in case.Code:unsigned long in = SpiRead(); // input must be an unsigned long make sure SpiRead returns an unsigned long as well.Now you can assign the sign signal to the result (0 for positive in the double, 1 for negative in the double).Lets assign the extended range as well (1 for True, or 0 for False for later use).BTW this code:register = (condition_to_evaluate)? value_if_true : value_if_false;is the same as:if(condition_to_evaluate)register = value_if_true;elseregister = value_if_false;Note, don't use the construct with macro definitions, because it usually yields to bad behavior, but on normal constructs it should be just fine.Code:result.s_val.sign = (in&0x20000000ul)?0:1; // bit 29 is 1 when positive, undetermined right at 0V and 0 when bellow 0VextendedRange = (in&0x10000000ul)?1:0; // bit 28 is 1 if outside 0-Vref, 0 if within range.If we want Vref to represent the value 0.99999999999(9) (within range where under parenthesis means periodic) and 1.0000(0) to represent Vref+1LSB (outside of the range) then we need to use the mantissa as a sub normal. (meaning that we have to force the exponent to be 0. We know this is true if the sign is positive (result.s_val.sign == 0) and if we are not on the extended range.Code:// Subnormal between 0 to 0.9999 [.0] implied before mantissa will take the value 0, otherwise 1023 to imply [1.] before mantissaresult.s_val.exp = ((result.s_val.sign == 0)&&(extendedRange == 0))? 0 : 0x3ff;Note i'm using the logical and (&&) the bitwise and (&) will work too but makes it look strange, I could make it shorter and faster but then it would be bit harder to read.For example:Code:result.s_val.exp = (result.s_val.sign|extendedRange)? 0x3ff : 0;That would read if sign or extended range then exponent is biased by 1023 otherwise use subnormal.And that is fine and faster to compute, so use it instead if you want.Edit maniac added:Let's make it a bit faster but more obscure coding.Code:result.s_val.exp = (result.s_val.sign|extendedRange)*0x3ff;Almost there, we just need to put the mantissa in place.So we have 28 bits (including the extra 4 subLSB bits) and the mantissa can hold 52.So we need to shift to the left by the difference in precision, in our case is 52-28 so it's 24 left.Edit: noticed I forgot to mask off the higher 4 control bits so I fixed itCode:// Adjust reading into the mantissa.result.s_val.mantissa = (in&0xffffffful)<<24;Now if you don't want the low 4 bits, you still shift by 24 but you mask the 4 bits first.Code:// Adjust reading into mantissa discarding low sub LSB 4 bits.result.s_val.mantissa = (in&0xffffff0ul)<<24;A note for shifting: Since you are on base 2 (binary) if you shift right you divide by powers of 2, if you shift left you multiply by powers of 2. Most people think about the truncation side of shifting but shifting makes fast dividers/multipliers (as long as it's a power of 2 you want to multiply or divide by).Edit(nth) yet another edit to clarify one thing. So if you were doing this in assembly instead of C you will have the choice to rotate left with carry and other shift assembler instructions. it wont matter much other than the less significant bit will either stick while shifting or become 0, think about it as a math floor function or a ceil (ceiling) function. But in this context it wouldn't make a difference.So what do you get after all this?Well, you can access the double directly, say you want to return the value read, then you will return the following double (your function will have to be declared to return a double value as well).Code:// Return read value. Range will be from -0.125 up to 1.125.// [-0.125 to 0.0) for extended negative range.// [0.0 to 1.0) for within range// [1.0 to 1.125] for extended positive range.// multiply the value by Vref to obtain the actual voltage.return result.val;And there you have it, sorry for the length and detail of this, I hope it helps not just you and I also hope I didn't make any horrendous coding mistake, but like I said, I didn't test this or tried to compile it.Edit: on the last code (the return value) in the comments about the range, the square bracket "[" means included, the parenthesis "(" means excluded.One more Edit:If you want to print the double you can do so withprintf("Input as in percent of Vref = %lf\n",result.val);Yet one more edit:This code assumes 1 byte data alignment, some programs change that and that can mess up the structure and the union.Yet one more edit edit:To help with the visualization here is an image that shows the double:You can modify this to use floats instead but more constrained.32 bits total.8 bits exponent bias is 127 from the value depicted on the field23 bits mantissa so you will have to stick to the 24bits and truncate one of those as well.Apologize for the many edits, just correcting erratas as any good engineer (even if it's software) should do!Also just noticed that this will allow for positive and negative 0 I think, so when the datasheet mentions the sign is undetermined at 0, this will gracefully handle it. (Not!, read bellow)You can also define new structures to add into the union, like the full 64 bits divided by nibbles (half a byte or 4 bits), bytes, words and double words. then you can use those to help you read the data in more directly, reducing time.Ok, last edit I swear, but I'm really not sure on the undetermined sign bit flip at 0 volts and had plenty of wine so I don't want to think about it. but if sign is is 1 (positive or 0 in the on the double bit field) and and the extended range is 0 then the exponent is the subnormal, meaning implied [0.]mantissa or 0, but if the sign flips to 0 (negative or 1 on the sign bit field in the double) and the extended range is 0, then the exponent will be the normal exponent implying [1.] so figure out how to solve that edge case, because it will spike to -1*Vref when it's really 0. You can add the mantissa being 0 to determine using the subnormal exponent or not. (expensive at 52 bits compare to 0, well not really if the compiler does the right thing and can use the z flag after some operation with the mantissa holding register to determine if it's 0 or not).Anyways, that will fix it but don't use it as illustrated above because the bug (-1Vref spike) will be hard to find.

author: Jon86
author_rank: Frequent Contributor
timestamp: February 26, 2014, 07:21:59 am
content: Awesome, thanks for the helpThere's a lot to understand there, but I think I got a good majority of it

author: BravoV
author_rank: Super Contributor
timestamp: February 26, 2014, 07:42:40 am
content: miguelvp, much appreciated, great post you've done there, especially for coding noob "AND" electronics noob like me.Your post there is now bookmarked.

author: miguelvp
author_rank: Super Contributor
timestamp: February 26, 2014, 08:13:50 am
content: Thanks guys, I guess I went a bit overboard but hopefully when I need help on electronics I don't get the RTFM treatmentI always read them!BravoV, if you bookmarked it, please note that the comments of the readings apply to the datasheet of the LTC2400 24-Bit ADC chip.Other ADCs will change parameters.Also if going to float (loosing just one bit precision) all the sample has to be adjusted including the structures and the union.Edit: yes i'm an edit fiend! No disrespect on the RTFM or the you should learn the hard way guys, but I don't expect someone to catch up with me after 30 years of programming, I rather teach than enforce!like my son says.... peace out (hopping no flame war i think, but speaking my mind never the less)

author: BravoV
author_rank: Super Contributor
timestamp: February 26, 2014, 09:41:26 am
content: Quote from: miguelvp on February 26, 2014, 08:13:50 amI'm pretty confident majority of the electronics tinkerers that are interested with precision ADC, must at least read LTC 2400 datasheet once in their life.Yes, I'm very familiar with LTC 2400 datasheet.Quote from: miguelvp on February 26, 2014, 08:13:50 amHence, thats why I bookmarked it instead of saving it off-line.Reading your posts there, I had a feeling you're the type who loves to mentor noobs, which is quite rare these days.(mentoring != spoon feeding)

author: miguelvp
author_rank: Super Contributor
timestamp: February 26, 2014, 10:25:55 am
content: Quote from: BravoV on February 26, 2014, 09:41:26 amDon't get me wrong I can tell between noobs and I won't put the effin effort to learn this crap. But yeah, even if the young people where I work at think they can do better than me, they really don't know what I'm capable off, it's beyond their realm.So why will I want to get someone to learn the hard way (i.e. before the internet) when now re-enforcing and spreading the knowledge is (or should be) the norm!.Granted, we used to push pixels at 300 baud, not they get pushed faster but.... when we couldn't push them faster we figured ways to optimize them, so z80 3.25 MHz (zx81 my first owned computer) to say 3 GHz now with 8 cores and hyperthreading so virtually 16 core pipes and GPU multi processing?... it doesn't matter!!!! same principles back then apply now, meaning. I don't have to push pixels as efficiently as I did before, but. If I do? then.. i'm pushing pixels faster than the competition.So I'm a bit over half a century old, so what, I forgot more things than kids learn now. And by forgot I mean, I learned them so if I have to be exposed to it again I'll pick it up way faster than anyone.Go guess one thing... nah I'll tell you, English is my 4th human language, but Basic as my 1st computer one then C, C++ etc... The first game I made required (I think) a 56K modem to race motorcycles online for 30 people playing at the same time and with roger wilko (voice protocol) at the same time and this is 14 years ago, now they can't do that with who knows how many Megabits per second from you home.My point again is teach, specially the old ways. Spartan or the KISS framework (keep It Simple Stupid) like Lotus did (yes I'm lucky enough to have such a fine spartan nonsense vehicle that can do 0-60mph in 4.7 seconds with just a 190 horsepower engine.My point (again) is that throwing cycles doesn't matter if you waste most of them. Then again I live in Chicago so my car has been in the garage for over a year and I might sell it because I don't want to put it through this town's nightmares.Don't get me wrong, love Chi-Town (or Chi-beria) like they've been calling it this last months. But I don't really need to drive anymore and I rather pay for the garage than to drive my car. Would be like measuring an e-bay power supply with a 66 GHz oscilloscope like the signal tap guy did ended up watching netflix (futurama I think) on the half a million dollar scope.Ok so I'm way rambling toughts, don't care really. my point is this. Keep teaching and passing on information. For my Country, keep those high level University students here, they are taking our knowledge away (I mean seriously that should be NSA's most important thing ever!)Now we are teaching them (yes I still believe we have the best Universities our there!) but now instead of staying here they are going back home?? that's is just messed up!Not because they want to go back, just because at their age, I rather stay here since the mindset was beyond anywhere in the planet.Ok. Not turning into politics so I'm done. Yeah wine is not helping the cause but at least you are getting my raw thoughts, as it should be.

author: dannyf
author_rank: Super Contributor
timestamp: February 26, 2014, 12:29:43 pm
content: QuoteWhy do you want to do that?

author: miguelvp
author_rank: Super Contributor
timestamp: February 26, 2014, 05:07:01 pm
content: Quote from: dannyf on February 26, 2014, 12:29:43 pmBecause you can do more with a normalized real number (0 to 1) than an integer?Specially when you are keeping all the precision of your input signal and have more room to spare without loosing the signal integrity?Also you could do some fancy DSP functions on the resulting value, I don't know what he is trying to measure, but converting it to a normalized value that multiplied by Vref gives you the actual voltage.And if you read the full post, you'll realize that it will use the extended range of the chip including the 12.5% under 0V and over Vref...Code:// Return read value. Range will be from -0.125 up to 1.125.// [-0.125 to 0.0) for extended negative range.// [0.0 to 1.0) for within range// [1.0 to 1.125] for extended positive range.// multiply the value by Vref to obtain the actual voltage.return result.val;

author: dannyf
author_rank: Super Contributor
timestamp: February 26, 2014, 05:47:30 pm
content: QuoteFor that, you are willing to incorporate 64-bit floating point math on a 8/16bit machine with often limited ram?I can do all of that in 32-bit integer math. No, thanks.

author: tszaboo
author_rank: Super Contributor
timestamp: February 26, 2014, 06:48:37 pm
content: Quote from: dannyf on February 26, 2014, 12:29:43 pmNot to mention you need extra good reference, precision components, layout and everything to get more than 16 noise free bits. You might need float for calculations, but even that is enough for a 8.5 digit multimeter grade measurements.

author: paf
author_rank: Regular Contributor
timestamp: February 26, 2014, 08:44:35 pm
content: Quote from: miguelvp on February 26, 2014, 04:49:22 amSorry, but could you please not write this kind of stuff?When you have a 16 bit processor using floats is a BIG performance mistake. Also it is very easy to understand integer arithmetic, but doing your own floating point operations in a correct way is very hard (and slow) in a 16 bit processor.Just because you can do floats does not mean that you should do it.Caveat:In a 32 bit processor in C, converting from an int to a float, you may loose precision.

author: suicidaleggroll
author_rank: Super Contributor
timestamp: February 27, 2014, 01:32:22 am
content: Quote from: paf on February 26, 2014, 08:44:35 pmEspecially when there is no math/floating point co-processor. Not only does it slow things WAY down, it also bloats your code by about a half a kB when it has to bring in all of those floating point libraries. Considering many msp430 models only have single digit kBs of flash to begin with, that's a big impact.

author: vvanders
author_rank: Regular Contributor
timestamp: February 27, 2014, 01:47:53 am
content: Quote from: paf on February 26, 2014, 08:44:35 pmNot to pile on the bandwagon(because I appreciate that the op took the time to explain things) but a float -> int conversion will also stall on many architecture that do have a dedicated FPU. It's best to keep things on one domain or the other.

author: miguelvp
author_rank: Super Contributor
timestamp: February 27, 2014, 02:33:22 am
content: Apologies to all,I was focusing more on the actual LTC2400 chip than the processor he was using so I got carried away when I noticed the mapping from the input of the chip just is so easy to convert to a double (or a float).I should have read the MSP430 datasheet, and somewhat thought it was an ARM based microcontroller, but I guess it is not. (I'm a noob at electronics after all)But it will still be useful for others that have processors with DSP and floating point capabilities, or at least to understand some things.Btw, floats and doubles are easy and fast to operate with regular processors as well specially if they are denormals (with out the assumed [1.] before the mantissa.) or at the range that we are talking about in here.You are really just operating them the same as you will any integer, and you can use the carry bit to extend it to multi byte operations.Multiplications are just shifts and adds when the bit is 1 or go to the next when is 0. Also there are fast ways to do adds and multiplies in subsections if you have integer multiplication and division built in.Divisions are just multiplications with inverse, but I guess using vanilla libraries suck oh well, lesson learned.Back to my FPGA I guess, were I could do these operations with no problems at all at the cost of power of course!Again sorry for misleading the OP but I did see some floating point being use on his code and the mention of double precision by others. Next time I'll keep it to myself.

author: BravoV
author_rank: Super Contributor
timestamp: February 27, 2014, 02:40:58 am
content: Quote from: miguelvp on February 27, 2014, 02:33:22 amWell, at least for me, your long post there actually quite useful since I use ARM M4F 80 Mhz core (still learning though) which has a dedicated FPU and with all the whole shebang for hardware based floating point operations in it.

author: miguelvp
author_rank: Super Contributor
timestamp: February 27, 2014, 06:23:29 am
content: Quote from: BravoV on February 27, 2014, 02:40:58 amAt least the effort was not in vainI guess I could follow up with how to implement double/float arithmetic in assembler (C and C++ can't really do carries directly), I also find it amazing that my ZX spectrum dedicated a big chunk of their precious ROM to handle the basic float arithmetic with different mantissas and all, I mean 512 bytes of code is just atrocious when you have only 64K of memory, it's almost a percent of the whole memory space!Well to be real, it's more than 512 bytes since their extra math, like trig, exponent square root, etcetera routines took way more than just half a kilobyte, now we are talking 4K with all those trig and exponent tables and that's a whole 6.25% of resources for 16 bit address space.Anyways, say you have a 3.3V Vref voltage that will be 11.01(0011)b and the parenthesis mean periodic, meaning the 0011 pattern can be saved and the multiplication of that can be stored and just shifted 4 bits right and added to get the whole thing.Since I don't want to confuse the matter too much more I'll just go with 5V Vref since it will be easier 5d=101b meaning copy the mantissa shift left 2 bits and add the original mantissa. the result? your double or float in Volts, but that's just too much work to do to get an accurate reading I guess.I could go on detail how to perform fast floating point math with assembler since before the 8087 no one use floats then I guess it's not worth itBut a hint for all, if you work with normalized vector components (0.0 to 1.0) math gets really easy for multiplication, adds and subtracts; divisions a bit more cumbersome but not that hard either. Math co-processors are indeed optimized for doing general math but I'm not talking about getting e^pi-pi = 20.00000000 scenario in here and yeah our dear 8087 gets 19.999099979189475767266442984669 as a result as well.Edit: the e^pi-pi is really a joke and a well known mathematical coincidence. Do not try to solve it or prove it.

author: paf
author_rank: Regular Contributor
timestamp: February 27, 2014, 10:51:08 am
content: In a computer one can make calculations using integers, floating point or fixed point.What MiguelVP is calling "normalized vector components" and "floating point tricks", the rest of the world knows as "fixed point".Using the right words is the first step in good engineering.And having a constant with an infinite binary representation like the 3.3 is also a bad idea.If you have an integer and you want to multiply it by 3.3, just multiply by 33 (32+1) and then divide by 10.If you really want/need Floating Point it is not enough to implement the basic operations, you need to uderstand what kind of errors can happen, and what to do when those errors happen.Some references:What Every Computer Scientist Should Know About Floating-Point Arithmetic:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.htmlSimplified Versionhttp://floating-point-gui.deHandbook of Floating-Point Arithmetichttp://www.springer.com/birkhauser/mathematics/book/978-0-8176-4704-9Implementation of Mathematical functions (sometimes in fixed point):Computer Approximations, John Hart1968 John Wiley & SonsSoftware Manual for the Elementary Functions, William Cody & William Waite1980 Prentice-HallMath Toolkit for Realtime Programming, Jack Crenshawhttp://www.amazon.com/Math-Toolkit-Real-Time-Programming-Crenshaw/dp/1929629095Modern Computer Arithmetic, Brent & Zimmermann, Cambridge University Presshttp://www.loria.fr/~zimmerma/mca/pub226.htmlElementary Functions: Muller, Jean-Michelhttp://www.springer.com/birkhauser/computer+science/book/978-0-8176-4372-0And if you are doing embedded work, you should have this book:http://www.hackersdelight.org

author: dannyf
author_rank: Super Contributor
timestamp: February 27, 2014, 12:26:42 pm
content: QuoteNo apologies needed. But more context for your approach would be helpful to your readers.

author: miguelvp
author_rank: Super Contributor
timestamp: February 27, 2014, 05:20:24 pm
content: Quote from: paf on February 27, 2014, 10:51:08 amNice reference links thanks.by "normalized vector components" I mean just that, not fix point at all.A unified vector of N dimensions also called "normalized" vector has a length of 1 unit.These are used on DSP and 3D graphics all around, you can make matrices with them and all kinds of real math with them (not floating point tricks at all, just pure unadulterated math).fix point is a different beast since by nature the decimal point can be at any bit index. I guess a normalized value of 0-1 could be expressed in a fix point where the decimal point is past the most significant bit but that would be a special case."Using the right words is the first step in good engineering. "You betcha.Dividing by 10 (2*5) is not a good approach since 1/5 can't be represented in binary very well. But summations will help.Anyways, I don't get the "math is hard" thing I guess I never had a toy telling me that.We are talking basic algebra here nothing really complicatedEdit:Let me hint you on something else that might be too obvious but no one actually thinks about this often.power is intensity times volts and of course intensity is volts by resistance.So you end up with p=V^2/RSo 5 volts square is pretty high, so it's 3.3 v squared, but what about 1.0V? or under 1.0V?

author: miguelvp
author_rank: Super Contributor
timestamp: February 27, 2014, 07:16:32 pm
content: One more thing, on base 10 (decimal) when you multiply two numbers together you apply a multiplication table [10 by 10] and then shift the result and add it.On base 2, your multiplication table is very simple0*0 = 00*1 = 01*0 = 01*1 = 1So look that as a truth table, looks like an AND gate to me. so you keep the full value when the multiplicand is 1 or skip it when 0shift left then add.It's the same thing but computers can do it very fast sequentiallyFPGAs can do this in parallel without using a lot of fabric.Why does NVidia use FPGAs to develop their GPUs? what is really a shader for graphics?it's just vector processing but with custom data paths and actually not very efficient since you have to denormalize the value, do the operation then normalize the result and repeat.But you can do your own algorithms that don't need to denormalize if it's already on the same exponent and you keep it that way. The same is true for the whole range, you can keep it denormal in memory and keep computing until you are done, then normalize the result.So we have ands, adds, shifts, what else we need, well memory that is convenient to use.How about the stack? you got it, push and pops.Edit: don't forget the flags, Carry Zero Overflow etc.. for multi byte operations3d animation is just that too, push pops of transform matrices and vector operations.Computer math also uses reverse polish notation, by organizing the values and the operands in the stack, so you don't need parenthesis. I mean, this is pretty basic and you shouldn't rely on a C compiler to do things for you to begin with if you use micro controllers that are too tiny.One more edit:Because I realized that even doing the math is not even really that important.I don't think, and correct me if I'm wrong that you'll have to link with the floating point library to print a number. Having it in memory in the format that the printf function can handle will give you a readable value.-0.125 to (0-1LSB) if you are under Vref on the lower EXT range0 to 0.99999999(9) when you are within range (with special case at 0 that the EXT range bit can flip within 0 having really two zeros.1.0 to 1.125 when you are above range.if you get a reading under or higher than those values then you know you are outside the parameters of the ADC.