adafruit / Adafruit_CircuitPython_Register

Python data descriptor classes to represent hardware registers on I2C devices.
MIT License
47 stars 21 forks source link

16-bit Registers #52

Closed rgrizzell closed 7 months ago

rgrizzell commented 7 months ago

I'm working on a driver for the GT911 touchscreen and the touch presses are returned via I2C. However, they're read from 16-bit registers rather than 8-bit. For example, to get the Product ID of the touchscreen you must read from 0x8140.

After looking through this library, adding support for 16-bit appears feasible. However, would that cause issues for 8-bit devices? Further more, I2C doesn't seem to set any limits on register size. Should support extend to sizes beyond 16 bits? Should I just implement my own read and write functions inside the driver instead?

Any insights will be appreciated, thanks!

rgrizzell commented 7 months ago

Just to be up-front, I only just started learning about I2C the other week and haven't worked with hardware on this level before. That said, I've attempted to patch in support for addresses of any length. It's a lot easier to work with when it's converted into a bytes. Unfortunately, when you plug the address into bytes(0x8140), you just end up with an object that's 33088 bytes in size.

After reading through a lot of different threads, this is what I piece together to get around that issue.

register_address_bytes = register_address.to_bytes((register_address.bit_length() + 7) // 8, 'big')
self.buffer = bytearray(len(register_address_bytes) + register_width)
self.buffer[0:len(register_address_bytes)] = register_address_bytes

The downside is that (register_address.bit_length() + 7) // 8 returns 0 when address 0x00 is specified. For that to address to work, its probably just better to create a function to handle the conversion.

def register_in_bytes(address: int):
    if address == 0x00:
        return bytearray(1)
    return address.to_bytes((address.bit_length() + 7) // 8, 'big')

If it's better to limit that conversion to just 8 and 16 bits, then bitwise works. I haven't figure out how to make this work for any length, though.

    if 0xFFFF >= register_address > 0xFF:
        self.buffer = bytearray(2 + register_width)
        self.buffer[0] = register_address >> 8
        self.buffer[1] = register_address & 0xFF
    else:
        self.buffer bytearray(1 + register_width)
        self.buffer[0] = register_address
tannewt commented 7 months ago

You can use i2c_struct with a type code for a 16 bit value: https://github.com/adafruit/Adafruit_CircuitPython_Register/blob/main/adafruit_register/i2c_struct.py

rgrizzell commented 7 months ago

You can use i2c_struct with a type code for a 16 bit value: https://github.com/adafruit/Adafruit_CircuitPython_Register/blob/main/adafruit_register/i2c_struct.py

I'm sorry, but I don't understand how to use that function. There are no existing examples where anyone else has used the i2c_struct to get a 16-bit register address.

Addr Access Descrption
0x8140 Read Product ID (first byte,ASCII)
0x8141 Read Product ID (second byte,ASCII)
0x8142 Read Product ID (third byte,ASCII)
0x8143 Read Product ID (fourth byte,ASCII)

Would you please give an example using the above? It'll go a long way toward helping me understand how to make this driver work.

tannewt commented 7 months ago

Sorry, I misread your issue. I read value instead of address. You are right that this library assumes 8 bit addresses.

It is probably easiest to make a version that works for you. Supporting arbitrary address lengths will increase code complexity that most thing won't use.

rgrizzell commented 7 months ago

It is probably easiest to make a version that works for you. Supporting arbitrary address lengths will increase code complexity that most thing won't use.

I'm inclined to agree, I haven't seen examples of other I2C devices that use 16-bit registers.

Thanks for the input!