I2C multi byte read - Page 1
Forum section: electronics / beginners

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 01:54:08 pm
content: So I am having a play with this little dev board:https://www.bitsbox.co.uk/index.php?main_page=product_info&cPath=347&products_id=2946To make matters a little more complicated I am communicating with the device via one of these:https://learn.adafruit.com/adafruit-ft232h-breakout/i2cFirst off, the i2c is working fine. I can control the device and read data. But I'm a little stumped when it comes to the device's protocol and reading all ADCs in a block.The data sheet:https://www.bitsbox.co.uk/data/modules/PCF8591.pdfSuggests that I would send:1. Address(Write) (0x48 << 1 | 0x00) = 0x902. Control word with auto_increment high = 0x04Then I can send my DAC value if I want. Currently I don't, so I try to read the ADCs.3. Address(Read) (0x48 << 1 | 0x01) = 0x91Here is where things go wrong. The data sheet suggests I will receive all ADC values one after the other. I can ACK them and it will send the next or I can NAK them and it will stop sending.The library I am using:https://github.com/adafruit/Adafruit_Python_GPIO/blob/master/Adafruit_GPIO/FT232H.pyDoes things a little bit weird.If I call:value0 = i2c.readRaw8()value1 = i2c.readRaw8()value2 = i2c.readRaw8()value3 = i2c.readRaw8()value4 = i2c.readRaw8()I get all 5 values correctly.When I look at the logic analyser trace the code is doing the following:Code:Time , Analyzer Name, Decoded Protocol Result1.353456833333333,I2C,Setup Write to [0b 1001 0000] + ACK1.353549041666667,I2C,0b 0000 0100 + ACK1.363848083333333,I2C,Setup Write to [0b 1001 0000] + ACK1.363949916666667,I2C,Setup Read to [0b 1001 0001] + ACK1.364042083333333,I2C,0b 1111 1111 + NAK1.384498833333333,I2C,Setup Write to [0b 1001 0000] + ACK1.384600708333333,I2C,Setup Read to [0b 1001 0001] + ACK1.384692875000000,I2C,0b 1010 0010 + NAK1.394937083333333,I2C,Setup Write to [0b 1001 0000] + ACK1.395038916666667,I2C,Setup Read to [0b 1001 0001] + ACK1.395131083333333,I2C,0b 1111 1111 + NAK1.415495291666667,I2C,Setup Write to [0b 1001 0000] + ACK1.415597166666667,I2C,Setup Read to [0b 1001 0001] + ACK1.415689333333333,I2C,0b 1001 0001 + NAK1.425907166666667,I2C,Setup Write to [0b 1001 0000] + ACK1.426009041666667,I2C,Setup Read to [0b 1001 0001] + ACK1.426101208333333,I2C,0b 0111 1111 + NAKThis seems so very inefficient and I don't understand why it's sending a SetupWrite to read. The datasheet does not seem to suggest this is required, just that the SetupRead ends with a stop or start condition.Is this library assuming to send the "Read" it has to Write the Read request, hence SetupWrite,SetupRead sequences?When I try and use the low level functions I get garbage.Code:i2c.writeRaw8(0x04)i2c._idle()i2c._transaction_start()i2c._i2c_start()i2c._i2c_write_bytes() # set up for readi2c._i2c_stop()values = i2c._transaction_end()i2c._idle()i2c._transaction_start()i2c._i2c_start()i2c._i2c_read_bytes(5) # Expect 5 bytes then send NAKi2c._i2c_stop()values = i2c._transaction_end()for val in values:print "Value: "+str(val) # Gives me just 1111 1111 for each byte.The reason this is failing is that the _read_bytes function is sending a SetupWrite. I'm certainly not sending it, but in the code it goes into bit banging the FT232h so I can't figure out what it's doing.Code:Time , Analyzer Name, Decoded Protocol Result1.568304208333333,I2C,Setup Write to [0b 1001 0000] + ACK1.568396416666667,I2C,0b 0000 0100 + ACK1.588809666666667,I2C,Setup Read to [0b 1001 0001] + ACK1.599491250000000,I2C,Setup Write to [0b 0000 0000] + ACK1.599582291666667,I2C,0b 1111 1111 + ACK1.599673333333333,I2C,0b 1111 1111 + ACK1.599764333333333,I2C,0b 1111 1111 + ACK1.599855375000000,I2C,0b 1111 1111 + NAKIf I don't use "i2c._i2c_stop()" before ending the transaction it doesn't send the SetupWrite, but I still get garbage back.Code:Time , Analyzer Name, Decoded Protocol Result3.857433083333333,I2C,Setup Write to [0b 1001 0000] + ACK3.857525291666667,I2C,0b 0000 0100 + ACK3.877955416666667,I2C,Setup Read to [0b 1001 0001] + ACK3.878045750000000,I2C,0b 0000 0000 + ACK3.888361875000000,I2C,0b 0111 1111 + NAK3.888452916666667,I2C,0b 0111 1111 + NAK3.888543916666667,I2C,0b 0111 1111 + NAK3.888634958333333,I2C,0b 0111 1111 + NAKIt also seems to be NAK'ing everything buy the first byte.It's not a big deal as I can read the device ADC values, but it's bugging me that I should be able to read all 5 bytes in one transaction. Maybe it's just a "feature" of this Adafruit library. Or maybe I'm missing something fundamental about I2C.The Arduino is below, but without digging into Wire.requestFrom(int,int) I can't determine how it does it.https://www.bitsbox.co.uk/index.php?main_page=page&id=26&pos=v&chapter=20

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 03:06:11 pm
content: I'm giving up on this. I traced things down into the FT232H commands and the _read_bytes(int) function is supposed to send:'\x20\x00\x00\x13\x00\x00'Which means, "Clock bytes in, 1 byte", "Clock bits out, 1 bit LOW"This should read one byte from the I2C bus and write a LOW ACK.It is supposed to queue one of these commands for each byte requested and finish with a:x13\x00\xFFSending an NAK bit to end transmission.When I output the "_command" array before calling _end_transaction it does not contain those sequences. Instead it contains a bunch of low level GPIO pin sequences. So something bizarre is going on.I've had enough, it was fun for a while but it's become annoying now.

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 03:31:57 pm
content: Meh!0x20 = SPACESo it gets printed as a " " on the output. Doh!

author: JXL
author_rank: Regular Contributor
timestamp: January 01, 2018, 06:40:19 pm
content: Use function readList(...) which specifies the number of bytes you want to read.

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 07:15:11 pm
content: Quote from: JXL on January 01, 2018, 06:40:19 pmBut function readList() takes a list of registers. I don't believe the PCF8591 have any registers and it doesn't have a "Read Register" instruction.I just checked the datasheet and it appears that to read a specific DAC you set it's address in the control byte and then read the byte back. It will continue to send that DAC value for every read unless auto_increment is set or you set another channel in the control byte.Maybe it won't send all 5 bytes in one block and I'm reading it wrong.Figure 9 - Page 9:https://www.bitsbox.co.uk/data/modules/PCF8591.pdfand Figure 17 - Page 14.Still it's only because it was bugging me. 5 successive read instructions each terminating the transmission after 1 byte works.Digging further into the datasheet I don't think 5 successive read instructions is semantically the same as reading 5 bytes from 1 read instruction.QuoteSo what those reads will get is:Read @ Time 0Previous channel 3 ADC value Time -1 ( could be minutes ago )Read @ Time 1Previous channel 0 ADC value Time 0Read @ Time 2Previous channel 1 ADC value Time 1Read @ Time 3Previous channel 2 ADC value Time 2Read @ Time 4Previous channel 3 ADC value Time 3*Assuming we sent the control word with ADC address 00 previously.

author: bson
author_rank: Unknown
timestamp: January 01, 2018, 08:47:13 pm
content: I2C reads from devices with addressable registers and memories are done by starting a write, issuing the register or start address to read from, then doing arestart read, which switches over to reading. The restart is simply a START followed by the usual addressing with R/W=1.A variable-length read, where the bus master keeps receiving until it decides it's done, is done like a plain single byte read, except the master doesn't ACK the last byte. Instead it issues a STOP immediately after receiving it. For many uC SoC peripherals which automatically ACK after each byte this requires telling it to stop before it's done receiving the last byte, or it's going to automatically ACK it. If your code doesn't expect anything more and thinks the transaction is done, but the slave is expecting to send more your bus will wedge. The only solution is to bang out clocks to the slave and issue a STOP, reset (if possible), or power cycle it (with e.g. a logic-level PFET controlled by a GPIO pin). Banging it out is done by reconfiguring the I2C pins to GPIO and well, banging it out, then return to the I2C configuration.Since the serial interface is asynchronous and buffered, by the time you get to receive a byte from the buffer it has already ACKed it and is receiving the next byte. So before your read of value4 you need to tell the serial peripheral to STOP; this is typically done by setting a particular bit in a register (not sure about Arduino or ST libraries and such). This way by the time you see value4 the serial controller will have issued a STOP and terminated the transfer.NAK is generated by a slave in response to a write, and implicitly when addressing a non-existent device (for the command byte). A master never NAKs on read. This looks like some confusion in the library over whether it's operating as a master or a slave...And, yes, I2C can be inefficient, especially with naive implementations that don't effectively restart or insist on doing single-byte reads.

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 09:39:08 pm
content: Quote from: bson on January 01, 2018, 08:47:13 pmYes, this is the only way I can get that library to work. Single reads. Read, NAK. Read, NAK. The ADAC I'm using will stop writing if you NAK a byte. You should, as you suggest, ACK all but the last byte. Then it will write each ADC value in turn.The serial adapter is a serial protocol emulator, not a basic serial adapter. In other words the implementation of I2C is on the serial chip, not in the PC over USB. The pattern appears to be stacking commands into an array (sort of like assembler) and transmitting that to the chip over serial. It then effectively replays that sequence to send and receive the I2C streams. Received data is transmitted back over serial. So you are correct in that I need to tell it in advance to ACK or NAK a byte, but it doesn't happen automatically.I can get the correct sequence for the"Read Address+READ""Read byte" - ACK"Read byte" - ACK"Read byte" - ACK"Read byte" - ACK"Read byte" - NAKHowever it doesn't send the STOP after the "Read Address+READ" so leaves the bus hanging open. Oddly the ADAC chip eventually times out after half a second or so and sends 5 bytes of rubbish anyway. The serial chip ACKs them anyway.There is still a chance I'm doing something wrong. There is a support thread for the library with developer ocasional presence, so I might go and asked there.I should probably hook it up direct to the Arduino and use the "Wire" library to see if I get the same or better results without having to tinker with the serial chip in the middle.Still it's going too far, I was just trying to play with this little board which was a random purchase. It has served it's purpose in me getting some hands on experience with intelligent serial adaptors, I2C and using a logic analyser for the first time to solve a problem. The ADAC itself is fairly useless, it's 8 bit, multiplexed and only pseudo differential. I have better ADACs on the Arduino. Though I'm not sure the DAC on this chip is an actual DAC or PWM, I believe it's the former.I think I want to move up to a 16 (or 24) bit I2C or SPI DAC to get any improvement over the Arduino one and this little learning project will have paved the way to getting up and running with that.Does anyone know of any good 16 bit AD/DC dev boards? RS Components don't seem to stock many and they are very expensive evaluation boards.

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 09:48:39 pm
content: Quote from: bson on January 01, 2018, 08:47:13 pmAre you sure about that? The datasheet for this chip mentions the master "No acknowledge"'ing by setting the data line high instead of low to end the transfer and if I send a NACK in response to the read the slave does stop sending.

author: bson
author_rank: Unknown
timestamp: January 01, 2018, 10:17:55 pm
content: Well, it seems like PC8591 has no registers and you simply read it to make conversions.And the python code bit bangs I2C using GPIO pins on the FT232H.So you want the I2CDevice:readRaw8() somewhat modified... something along the lines of:Code:def readRaw8List(self, num):"""Read an 8-bit value on the bus (without register)."""self._idle()self._transaction_start()self._i2c_start()self._i2c_write_bytes([self._address_byte(False)])self._i2c_stop()self._i2c_idle()self._i2c_start()self._i2c_write_bytes([self._address_byte(True)])self._i2c_read_bytes(num)self._i2c_stop()response = self._transaction_end()self._verify_acks(response[:-length])return response[-length:]Then call it with values = readRaw8List(i2c, 4)Should return 4 bytes

author: bson
author_rank: Unknown
timestamp: January 01, 2018, 10:22:56 pm
content: Quote from: paulca on January 01, 2018, 09:48:39 pmI may be mistaken, but it's superfluous since it's always followed by a stop anyway...The spec does seem to permit it:http://i2c.info/i2c-bus-specification

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 10:43:33 pm
content: Quote from: bson on January 01, 2018, 10:17:55 pmDifferent tactic which I will try, but I was trying to use those low level functions, I didn't take readRaw8 as a base and modify it though. Will give it a go.Ordered one of these for my next play project:https://www.ebay.co.uk/itm/162130900445

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 10:58:40 pm
content: Quote from: bson on January 01, 2018, 10:17:55 pmBingo, it worked. With a few fixes highlighted. Thank you very much for the help.I actually made it a local function as:Code:def readRaw8List(i2cDev, num):"""Read an 8-bit value on the bus (without register)."""i2cDev._idle()i2cDev._transaction_start()i2cDev._i2c_start()i2cDev._i2c_write_bytes([i2cDev._address_byte(False)])i2cDev._i2c_stop()i2cDev._i2c_idle()i2cDev._i2c_start()i2cDev._i2c_write_bytes([i2cDev._address_byte(True)])i2cDev._i2c_read_bytes(num)i2cDev._i2c_stop()response = i2cDev._transaction_end()i2cDev._verify_acks(response[:-num])return response[-num:]I now get:Code:Time , Analyzer Name, Decoded Protocol Result2.809669916666667,I2C,Setup Write to [0b 1001 0000] + ACK2.809762083333333,I2C,0b 0000 0100 + ACK2.820100333333333,I2C,Setup Write to [0b 1001 0000] + ACK2.820202250000000,I2C,Setup Read to [0b 1001 0001] + ACK2.820294416666667,I2C,0b 1010 0011 + ACK2.820385416666667,I2C,0b 1010 0011 + ACK2.820476500000000,I2C,0b 1111 1111 + ACK2.820567500000000,I2C,0b 1001 1010 + ACK2.820658500000000,I2C,0b 1110 0000 + NAKCode:paul@localhost ~/devel/FT232H/python $ sudo python pcf8591_ft232h.pyValue: 163Value: 163Value: 255Value: 154Value: 224A direct 5 byte read from a single read request and the values make sense.Cool.It has that annoying "Expecting to run as root" thing going on that I might need to put to bed. I have access to uucp devices as "paul" I don't need to be root to write to the usb serial port. That's just stupid.

author: paulca
author_rank: Super Contributor
timestamp: January 01, 2018, 11:11:48 pm
content: So I can also read a single ADC N times by reading N bytes at whatever I2C clock speed you have. Just have to set the channel number in the config byte and turn "auto increment" off.The trouble is I hit the FT232H limitations of sending all the big banging for the whole transaction over serial which eventually overflows around 20 byte reads and crashes it. With a proper microcontroller I wouldn't have that limitation I could just send the Read address and clock it, ack'ing the bytes until I want to stop it reading.Think I've taken this far enough now. Very cool toy for the day. Next!Aww. I forgot to play with the DAC

author: paulca
author_rank: Super Contributor
timestamp: January 02, 2018, 12:02:47 am
content: Writing to the DAC was easy now I'm on the right track:Code:def writeToDAC(i2cDev, value ):i2cDev._idle()i2cDev._transaction_start()i2cDev._i2c_start()i2cDev._i2c_write_bytes([i2cDev._address_byte(False), 0x64, value ])i2cDev._i2c_stop()response = i2cDev._transaction_end()i2cDev._verify_acks(response)print "Sweeping DAC\n"for i in range( 0, 255 ):print "{0!s},".format(i)writeToDAC(i2c, i )time.sleep(0.01)print "\n"

author: bson
author_rank: Unknown
timestamp: January 02, 2018, 09:20:56 pm
content: Nice! Yeah, having direct access with a microcontroller makes life easier in some ways, but harder in others.That particular ADC seems to only make conversions when accessed, so with the FT232 and USB it would be really hard to obtain consistently spaced samples. Obviously this would be easier with a microcontroller but even then I'd avoid using I2C for anything hard real time. It's fine for level sensors, panel lights (such as on a PCF8574 or MCP23008), 20x4 LCDs, and such, but mainly stuff where a few tens of msec here or there doesn't matter. So I always use it in the foreground and never interrupt driven - it's strictly foreground stuff in my book.