I2C set register with negative number

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

I2C set register with negative number

844 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by renan on Mon Mar 10 20:26:49 MST 2014
Hello,

I'm trying to use the i2c library from lpcopen to communicate with a adxl345 accelerometer.
I'm just trying to set a register with for example: -25.

I'm using, for example this function:
void adxl345_write_reg (uint8_t reg, uint8_t data)
{
I2C_XFER_T xfer;
uint8_t TXBuffer[2];
uint8_t RXBuffer;

TXBuffer[0] = reg;
TXBuffer[1] = data;

xfer.slaveAddr = 0x53;
xfer.txBuff = TXBuffer;
xfer.txSz = 2;
xfer.rxBuff = &RXBuffer;
xfer.rxSz = 0;

Chip_I2C_MasterTransfer(I2C0, &xfer);
}


This is the declaration of the function in the last line:
int Chip_I2C_MasterTransfer(I2C_ID_T id, I2C_XFER_T *xfer)


And this is  I2C_XFER_T:
typedef struct {
uint8_t slaveAddr;/**< 7-bit I2C Slave address */
const uint8_t *txBuff;/**< Pointer to array of bytes to be transmitted */
int     txSz;/**< Number of bytes in transmit array,
   if 0 only receive transfer will be carried on */
uint8_t *rxBuff;/**< Pointer memory where bytes received from I2C be stored */
int     rxSz;/**< Number of bytes to received,
   if 0 only transmission we be carried on */
I2C_STATUS_T status;/**< Status of the current I2C transfer */
} I2C_XFER_T;


The problem is that my data is a negative number and the buffers are unsigned short (uint8_t).

How can I solve this problem?

Thanks
Renan
Labels (1)
0 Kudos
4 Replies

667 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by OldManVimes on Tue Mar 25 10:41:52 MST 2014
When performing the I2C transaction, the code must transfer individual bytes as you state. That is independent of the width or signedness of the registers in the device those bytes are written to.
You are correct that the endianness must be set correctly by the caller based on the spec of the target device. However that issue exists no matter what.

So let me give you the example you refer to. Let's assume that we treat the contents of the I2C EEPROM as a series of signed 32-bit values. The I2C code reads and writes them as a sequence of bytes. Assume we've written the values ourselves earlier meaning the endianness remains correct after reading. Before we read, we specify an unsigned start address of 16-bits as well.

uint16_t startAddress = 64 * 5;
int32_t eepromPage[16]; // 64 bytes total

xfer.txBuff = &startAddress;
xfer.txSz = sizeof(startAddress); 
xfer.rxBuff = eepromPage; // Alternative: = &(eepromPage[0]);
xfer.rxSz = sizeof(eepromPage);
// etc.


The point is that I2C is 'merely' a bus used for transmission. It does not have to be aware of the semantics of what it transfers. It merely processes byte arrays. The semantics (i.e. bit alignment, signedness and width) are only relevant to the target device and the software that addresses that device. So if txBuff is a const void *, then the I2C code must internally cast it to a const uint8_t *. Likewise for rxBuff. That is not a problem.

I can even do this.

#pragma pack(push, 1)
struct FooBar
{
  uint8_t magic;
  char header[5];
  int32_t val;
};
#pragma pop

struct FooBar fooBar;
// Init of  fooBar not shown
xfer.txBuff = &fooBar;
xfer.txSz = sizeof(fooBar); // is 10
xfer.rxSz = 0;
// Write the FooBar struct


All neat and elegant.
0 Kudos

666 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by rocketdawg on Tue Mar 25 09:34:08 MST 2014
I understand your concern.  The I2C spec says that the data on SDA is an 8 bit byte.
It makes no mention of signed or unsigned.
the target device (in this case) has a register definition that referes to a signed quantity, and does so for convenience or to aid in describing the contents of the register.
but it is really a value from 0 to 0xFF

so how does your implementation work if I access a I2C EEPROM that can read a whole 64 byte flash page?  Can not put that into someResults.
A byte array does not rely on the capabilities of the target device.

someCommand sizeof() is really 4 bytes and its RAM storage layout is dependent upon endianness.  (so does someResults)
0 Kudos

666 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by OldManVimes on Tue Mar 25 02:46:58 MST 2014
Hello Kevin,

I'm starting to get interested in LPCopen. I think the original question raises a valid issue. The I2C struct introduces an assumption that all I2C data is of type unsigned char, and clearly this is not always true. Data can be signed or unsigned and one- or multi-byte in length. So why does the struct force the semantics of the byte array? In other words, why aren't the pointers in the I2C struct defined as const void* and void*?

If the struct assumes a type for the pointers then type casts are needed whenever the data does not conform to the unsigned character type. With void pointers, the following is possible without type casts:

int16_t someCommand[2] = { 42, 1 };
int32_t someResult;
xfer.txBuff = someCommand; // No cast needed or required by the compiler
xfer.rxBuff = &someResult; // No cast needed or required by the compiler
// Perform I2C transaction now.
// code left out
// Now use the I2C result without any type cast.
if (someResult == 42) { }


After performing the I2C transaction the caller uses the original typed variables to access the result. The pointers in the struct are no longer used. In my opinion, this makes the I2C API easier to work with. The only down side is that the I2C code needs to cast the pointers to unsigned char types, but that is preferable to potentially forcing all users of the API to have to do so.

Regards,
OldManVimes
0 Kudos

666 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by wellsk on Tue Mar 11 09:06:11 MST 2014
You could typecast the value..something like this:

adxl345_write_reg(regNum, (uint8_t) -25);


uint8_t buffer[4];
buffer[0] = (uint8_t) -25;
xfer.rxBuff = buffer;

0 Kudos