Trouble recombining 24bit value from 3 bytes

Go To Last Post
9 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

I'm working on a design project in university where we are interfacing over I2C to an energy monitor chip with an ATMega328P. The chip returns values in single, double, triple and quadruple byte strings. My 16 bit recombination seems to work fine, my 32 bit recombination seems to work fine, but the 24bit recombination works only 75% of the time. For some reason, certain values on returned values are interpreted as highbyte -1. For instance:
 

SENT    SEEN
089B10  079B10
07BAF8  06BAF8
06DAE0  05DAE0
060118  060118
0293A8  0193A8
01B6B8  00B6B8

I have confirmed that the values are both being sent over the I2C bus (using a bus sniffer) and are being received by the routine (by printing the individual bytes) but the recombination routine returns the wrong values.

Can anyone take a look at this routine and help me fix it?

 

uint32_t readEM_POWER(uint8_t SLA) {
	uint32_t tmp=0;										//The 32-bit value to return
	TWI_Start();										//Send the TWI Start
	TWI_sendByte(SLA);									//Send the slave address (write address)
	TWI_sendByte(0x05);									//Send the address to read from
	TWI_Start();										//Send a repeated start
	TWI_sendByte(SLA+1);								//Send the slave address+1 (read address)
	tmp = (TWI_readByte(1)*65536);						//Read data from TWI (high byte2) ACK
	tmp += (TWI_readByte(1)*256);						//Read data from TWI (high byte1) ACK
	tmp += TWI_readByte(0);								//Read data from TWI (low byte) NACK
	TWI_Stop();											//Send the TWI Stop
	return tmp;											//Return the 32-bit value
}

Thanks!

Greydesk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Would it be easier to do something with a structure/union to combine the bytes?

 

JIm

If you want a career with a known path - become an undertaker. Dead people don't sue! - Kartman

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greydesk wrote:
Can anyone take a look at this routine and help me fix it?

Encapsulate the routine into some test code, and run it through the simulator.

 

Greydesk wrote:
tmp = (TWI_readByte(1)*65536); //Read data from TWI (high byte2) ACK

And, since the high byte is the apparent problem area, this would be an "interesting" construct to look at.

 

In particular, consider the promotion rules of C and what it might do here. 

 

Do you get the same result if you cast the parameter to uint32 and/or make the constant UL?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As it always happens, I stumble on the answer AFTER I post a help request. For anyone else looking for the answer, here is the fixed code:

uint32_t readEM_POWER(uint8_t SLA) {
	uint32_t tmp=0;								//The 32-bit value to return
	TWI_Start();								//Send the TWI Start
	TWI_sendByte(SLA);							//Send the slave address (write address)
	TWI_sendByte(0x05);							//Send the address to read from
	TWI_Start();								//Send a repeated start
	TWI_sendByte(SLA+1);							//Send the slave address+1 (read address)
	tmp = TWI_readByte(1);						        //Read data from TWI (high byte2) ACK
	tmp = (tmp<<8) + TWI_readByte(1);					//Read data from TWI (high byte1) ACK
	tmp = (tmp<<8) + TWI_readByte(0);					//Read data from TWI (low byte) NACK
	TWI_Stop();								//Send the TWI Stop
	return tmp;								//Return the 32-bit value
}

The theory is, rather than moving the individual bytes into their position and then adding them, shift the first byte right one byte and add the second byte, then shift the whole thing right another byte and add the third byte. That takes care of dangling values and such. Works great now!

 

Greydesk

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greydesk wrote:

The theory is, rather than moving the individual bytes into their position and then adding them, shift the first byte right one byte and add the second byte, then shift the whole thing right another byte and add the third byte. That takes care of dangling values and such. Works great now!

 

You mean shift left of course ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
Would it be easier to do something with a structure/union to combine the bytes?
  Me think so tooo.

No messin' around with multiplications or shifts. Just put the bytes where they need to be:

typedef union {
	uint16_t complete;
	struct {
		uint8_t high;
		uint8_t low;
	};
}TAddress; 


TAddres foo;                  // Declare an object.

foo.low = 34;                 // Assign somethin to low byte.
foo.high = 0x23;              // Assign something to high byte.
printf( "%d", foo.complete);  // Voila, print your number.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Title said 3 bytes. If GCC I believe it has a 24 bit type but for "standard" then I guess that means a union of uint32_t and 4 bytes in fact (probably an array rather than naming each one?) and then only using 3.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paulvdh wrote:
Me think so tooo.

Often discussed over the years.  The P.C. police will chant "Not portable! Not portable!" as the layout and results will be different big/little endian.

 

But indeed, that's what I do (union with byte array).  I find I always need to repeat my experiment (which I have carried out over and over during a span of many decades) to remember/re-determine how 0x12345678 is sored in an unsigned long.  Maybe I need to repeat as sometimes I've done Moto work. ;)

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Greydesk wrote:
tmp = (TWI_readByte(1)*65536);

Could it be that the compiler is misinterpreting the value 65536 as signed short value? Would it be different with 65536UL? How about 0xFFFF?

 

In the past I also stumbled on this tutorial about how to handle a binary file. Maybe it will be useful here as well.

http://www.cplusplus.com/articles/DzywvCM9/