nonNoise / PyMCP2221A

MCP2221 & MCP2221A work in Python.
MIT License
36 stars 21 forks source link

Extend I2C functionality for longer I2C writes / reads #10

Open ElmarJongerius opened 3 years ago

ElmarJongerius commented 3 years ago

The I2C functions do not handle writes and reads which are longer than 1 block (60 bytes?).

I have hacked it into the _i2c_write function as follows, as I was not able to find a way to do this cleaner. I would be glad to help implement and test this; it seems there is a status read required to see if we can already write the next block.

#Support for longer I2C write packets def _i2c_write(self, addrs, data, buf): buf = buf + [0 for i in range(65 - len(buf))] buf[1 + 1] = (len(data) & 0x00FF) # Cancel current I2C/SMBus transfer (sub-command) buf[2 + 1] = (len(data) & 0xFF00) >> 8 # Set I2C/SMBus communication speed (sub-command) # The I2C/SMBus system clock divider that will be used to establish the communication speed buf[3 + 1] = 0xFF & (addrs << 1) while (len(data)): for i in range(min(len(data), 60)): #print ("{:d}: 0x{:02x}".format(i,data[i])) buf[4 + 1 + i] = data[i] # The I2C/SMBus system clock divider that will be used to establish the communication speed self.mcp2221a.write(buf) rbuf = self.mcp2221a.read(65) time.sleep(0.08) #print("\n\n") data = data[min(len(data), 60):]

ElmarJongerius commented 3 years ago

@nonNoise Could you point me in the right direction on how to implement longer reads and writes? I think at this moment the code is limited to single block transfers; and still it does crash a lot of times for me. (reading more than 20 bytes is almost impossible it seems).

I would like to add reliable read/write functions but cannot even find doc`s on microchips website. Hoe should I for example do a status check/read within the transfer?

nonNoise commented 3 years ago

Hello. thank you for contacting. I don't know the target because there is no scene to communicate more than 20 bytes. If you are assuming EEPROM etc., how about reading it in pieces?

Thanks for using it.

ElmarJongerius commented 3 years ago

Hi @nonNoise, The reason we are communicating longer transactions is speed and compatibility.

  1. There are huge delays due to USB bus transfers and commands, so reading (or writing) byte for byte is slow in my case. (need to transfer 30kb in a second or two)
  2. Compatibility. I am using the MCP2221A to validate and program my chip over I2C, this would need writes of 1030 bytes, which may use clockstretch etc, but splitting in multiple reads or writes does actually change the bus commands;m where I need to test other cases with longer commands.
  3. Compatibility (again): I would like to be able to tell the driver "send this chunk of data over I2C", without bothering about the maximum length supported.

Points 1 and 2 are most important to me.

If you could help me on some tips on implementing I would be very grateful.

nonNoise commented 3 years ago

Hmm? I checked the contents of MCP2221A, but I think the maximum number of bytes that can be transferred at one time is 59. (63 - 4 = 59Byte) I have a problem where it crashes at 20 bytes.

Do you know the target sensor or IC? I want to buy and try it.

Located in PyMCP2221A.py def _i2c_write (self, addrs, data, buf): in There is time.sleep (0.008).

This "time.sleep" is included to improve PC (or USB) speed and IC compatibility.

Number entered by experience.

Please adjust it.

I am happy if you can see the waveform during communication.

Thank you,

nonNoise commented 3 years ago

I forgot to tell you. PyMCP2221A Certainly slow. There are times when I can't stand it. Is there a problem with Python? Is there a problem with HID-API? Is there a problem with the MCP221A that uses HID in the first place? I don't know, but it's very slow. Especially when I read the accelerometer, I can't stand it. Temperatures and pressures of 1 second to 100 milliseconds are acceptable.

This area is quite difficult. It may not be possible to solve it.

ElmarJongerius commented 3 years ago

The target is a self developed ARM micro controller configured as I2C slave. Playing with the delay`s will not help; I do need to adjust how the library transfers I2C data.

Semicode: Write address Get status (ACK/NACK) While data available Write data Get status (ACK/NACK/BUSY)

nonNoise commented 3 years ago

Do you think this is a "library" problem? I am going according to the command of MCP2221A. Check the specifications of the MCP2221A. Please let me know if there is anything you want. https://www.microchip.com/wwwproducts/en/MCP2221A

Thank you,

ElmarJongerius commented 3 years ago

Well, the datasheet states: "Supports block reads/writes up to 65535 bytes long". Clearly that would need to be multiple USB block transfers; but I would really like to have this implemented :)

I have searched for a way how to use it (probably needs some status checking and extended data sending) but I couldn't figure out from the available documentation yet). I have requested support at Microchip so I'm hopeful they will send me some details which will help to bring these together.

Checking the I2C status might also make your sensors read out faster; as this delay is probably unnecessary when using status readback.

K4zuki commented 3 years ago

@ElmarJongerius , @nonNoise ,

I found "TABLE 3-2: RESPONSE 1 STRUCTURE" (page 25 of the datasheet pdf) has a great hint. in the table, byte index from 9 to 17:

index description
9 Lower byte (16-bit value) of the requested I2C transfer length
10 Higher byte (16-bit value) of the requested I2C transfer length
11 Lower byte (16-bit value) of the already transferred (through I2C) number of bytes
12 Higher byte (16-bit value) of the already transferred (through I2C) number of bytes
13 Internal I2C data buffer counter
14 Current I2C communication speed divider value
15 Current I2C time-out value
16 Lower byte (16-bit value) of the I2C address being used
17 Higher byte (16-bit value) of the I2C address being used
pond-fssw commented 2 years ago

Hi was this ever figured out? For my project I really need to read more than 60 per block, and I am struggling to make it work.

nonNoise commented 2 years ago

Hi I'm a little interested. "read more than 60 per block" What model number of IC are you using?

pond-fssw commented 2 years ago

I'm using the MCP2221A and trying to read 68 bytes

pond-fssw commented 2 years ago

Also, do you mind explaining what this portion of your code in the _i2c_read function is doing?

    buf[1 + 1] = (size & 0x00FF)  # Read LEN
    buf[2 + 1] = (size & 0xFF00) >> 8  # Read LEN
    buf[3 + 1] = 0xFF & (addrs << 1)  # addrs
    self.mcp2221a.write(buf)
    rbuf = self.mcp2221a.read(PACKET_SIZE)
    if (rbuf[1] != 0x00):
        # print("[0x91:0x{:02x},0x{:02x},0x{:02x}]".format(rbuf[1],rbuf[2],rbuf[3]))
        self.I2C_Cancel()
        self.I2C_Init()
        raise RuntimeError("I2C Read Data Failed: Code " + rbuf[1])
    time.sleep(self.MCP2221_I2C_SLEEP)

    buf = self.compile_packet([0x00, 0x40])
    buf[1 + 1] = 0x00
    buf[2 + 1] = 0x00
    buf[3 + 1] = 0x00
    self.mcp2221a.write(buf)
    rbuf = self.mcp2221a.read(PACKET_SIZE)
nonNoise commented 1 year ago

image image image

https://www.microchip.com/en-us/product/MCP2221A