eblot / pyftdi

FTDI device driver written in pure Python
Other
509 stars 212 forks source link

Cannot write to a i2c device that uses a 32 bit register value #284

Closed M62ChainSlapLover closed 2 years ago

M62ChainSlapLover commented 2 years ago

When using the write_to, it cannot use a 32 bit/4 byte value for a register. Using a TMP117 device, and attempting to write to address 0x7h (temperature offset), the max value that can be entered is 0xFF, however, the register calls for 4 bytes, not two, and thus cannot write a value to a register as it fails by sending a stop ack. When attempting to write 0x8000 "can't extend bytearray with int" is presented. Errors are calling specifically to the write_to and _make_buffer def's.

sifive-eblot commented 2 years ago

Did you call configure_register?

M62ChainSlapLover commented 2 years ago

The slave address itself is fine, it's the length of the register that I need to change from 2 to 4 bits. Is there a way I can make that change with configure_register? I understood it only changes the length of the i2c address.

sifive-eblot commented 2 years ago

Bytes or Bits? You initially reported an issue w/ bytes but you last message is about bits.

M62ChainSlapLover commented 2 years ago

Bytes or Bits? You initially reported an issue w/ bytes but you last message is about bits.

bytes, my apologies.

sifive-eblot commented 2 years ago

I2C is a byte oriented protocol. If I get it right you want to use integers.

You need to encode your integer, whatever format your I2C slave expects for them into bytes.

There are several ways in Python to encode (and decode) integers into bytes. The most versatile one is to use the struct package, for ex:

import struct

payload = struct.pack('<I', value)

Note that you need to replace < with > if you device use a big endian representation for integers (as again I2C payload are mere bytes, it "knows" nothing about integers).

Another way is to use

payload = (value).to_bytes(4, byteorder='little')
M62ChainSlapLover commented 2 years ago

I don't think I did a very good job explaining what I was trying to accomplish, and I apologize for that. Given that what I'm trying to accomplish would ideally use the following format to write to the register. write_to(0x07,0x8000) however, due to the byte limitation, I am limited to write_to(0x07,0xFF) at the maximum because it is limited to just those two bytes. However, it does not successfully write to the register because the register expects two more bytes before the stop acknowledgment. What changes do I need to make to implement that with that intended format successfully?

eblot commented 2 years ago

Have a look at Figure 7-9, page 22 of the latest TMP117 datasheet. You can see that as with any I2C communication, the data format are bytes, not integers. Bytes with this slave devices are big-endian encoded (i.e., most significant byte first). It seems you want to set the temp_offset register, i.e. register 7.

This means you need to send 3 bytes, in order:

from pyftdi.i2c import I2cController

i2c = I2cController()
i2c.configure('ftdi:///1')
port = i2c.get_port(0x48)  # or whatever alternative address
port.write_to(0x07, (0x8000).to_bytes(2, 'big'))
i2c.terminate()

same example, using struct and write()

from struct import pack
from pyftdi.i2c import I2cController

i2c = I2cController()
i2c.configure('ftdi:///1')
port = i2c.get_port(0x48)  # or whatever alternative address
payload = pack('>BH', 0x7, 0x8000)
port.write(payload)
i2c.terminate()
eblot commented 2 years ago

BTW, I do not see in the documentation 4-byte registers. There are 2-byte registers, with a register address prefix of 1 byte.

eblot commented 2 years ago

No feedback. Please use the Discussions to ask for support on how to use PyFtdi.