Recent

Author Topic: Example on how to read/write I2C devices on Raspberry Pi?  (Read 1696 times)

MarkMLl

  • Sr. Member
  • ****
  • Posts: 313
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #15 on: November 15, 2019, 07:34:19 pm »
Remember that you have to specify the slave address.
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

avra

  • Hero Member
  • *****
  • Posts: 1738
    • Additional info
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #16 on: November 15, 2019, 09:48:05 pm »
Quote
If there is no ready to use library providing such high level feature, then you have to get out of your developer comfort zone, dig into the world of electronics and learn the craft of reading datasheets.
Thanks for the pointers, but the problem I have is that I cannot get my head around the operating system interface in this case.

I have done a fair bit of embedded microcontroller development (Microchip PIC) including interfacing the I2C EEPROM devices.
Then I don't get it why don't you try out any of the microcontroller like libraries mentioned in the RPi wiki page? It should be much closer to your PIC experience then BaseUnix.
Because on Linux the I/O is owned by the operating system as I understand it. On a PIC or AVR one can pretty much do whatever on the pins without having to invoke the OS.
If you know what you are doing or if you use a proper library for your work, and you do not need nanosecond bit banging or some other hard core real time, then there is not much difference.

I have just connected my EEPROM to the RPi and verified it appears on address $50.
So now I will have to make a small test program with min overhead to test this.
With rpi_hal library for example (mentioned on wiki page I talked earlier), the core code without initialization would look something like this:
Code: Pascal  [Select]
  1. i2c_string_write($50, $05, 'ABC', NO_TEST); // write char A in reg 0x05, B in 0x06 and C in reg 0x07
  2. s := i2c_string_read($50, $05, 3, NO_TEST); // read 3 bytes into string s. Start reg is 0x05
  3. showstr(s); // guess what shows here

In case of trouble you can compare what you think your eeprom has with result of i2cdump -y 1 0x50.

Here are some useful reads for your case:
https://www.waveshare.com/wiki/Raspberry_Pi_Tutorial_Series:_I2C
https://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program
https://www.digitalpeer.com/blog/raspberry-pi-i2c-256k-eeprom-tutorial

Basically it doesn't matter if you find an Arduino, I2C linux or some other code example to access your I2C device. You have all the I2C commands needed to replicate that code functionality in freepascal.
« Last Edit: November 15, 2019, 10:28:06 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

BosseB

  • Jr. Member
  • **
  • Posts: 99
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #17 on: November 16, 2019, 05:05:37 pm »
OK thanks,
meanwhile I have written a Raspberry i2c class from what I have read here and elsewhere and tested it against my EEPROM sample (24LC32A) which I hooked up to the RPi yesterday.
It seems like my byte read/write functions work fine now! Had to weed out a few interpretation bugs while reading the datasheet but once done I can read/write just fine.

So THANK YOU to all contributors!

I have yet to make an attempt at read/write a lot of data from the EEPROM in one go.
To do that I have encapsulated the byte read/write functions in a loop for the length of the data.
So I am not using the functions to read sequentially after sending the start byte and address once.

A complete 1-byte read operation as seen on my oscilloscope takes 1.3 ms.
Of this the actual response from the EEPROM when doing a read takes about 0.48 ms.
So it could perhaps be sensible to at least try he sequential read in order to save on the preliminaries for each new byte to read back. If one stores kilobytes of data then it will take time on the order of seconds to read back using the loop...

I might want to test both the loop byte read and the sequential read for speed differences, but as long as the data is not very large it will not matter much.

Regarding i2cdump -y 1 0x50
In my case where I have written a few bytes here and there in the EEPROM I expected it to show up but it does not...
Here is what I got:
Code: Pascal  [Select]
  1. $ i2cdump -y 1 0x50
  2. No size specified (using byte-data access)
  3.      0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
  4. 00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  5. 10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  6. 20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  7. 30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  8. 40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  9. 50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  10. 60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  11. 70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  12. 80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  13. 90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  14. a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  15. b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  16. c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  17. d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  18. e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................
  19. f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff    ................

When I read using my own read function on the written addresses I get this:
Code: Pascal  [Select]
  1. Adr Data
  2. 05    B4
  3. 0F    4B
  4. 10    21
  5. 4B    AA
Reading these locations consistently give the same values with my own i2c debug program.
But with the i2cdump program everything is $FF.

So there is something strange with the i2cdump program...
« Last Edit: November 16, 2019, 05:08:59 pm by BosseB »
--
Bo Berglund
Sweden

MarkMLl

  • Sr. Member
  • ****
  • Posts: 313
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #18 on: November 16, 2019, 05:27:49 pm »
Incidentally, I spotted this a couple of days ago which might have something useful in it:

https://stackoverflow.com/questions/9974592/i2c-slave-ioctl-purpose

Also two which I think are generally useful: one from the Linux Kernel and another from a site generally oriented towards the RPi... I think I've referred to both of them in the past:

https://www.kernel.org/doc/Documentation/i2c/dev-interface
https://elinux.org/Interfacing_with_I2C_Devices

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

BosseB

  • Jr. Member
  • **
  • Posts: 99
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #19 on: November 16, 2019, 10:30:31 pm »
Checked the sequential read now and it is pretty much faster.
0.55 ms/byte + 0.78 ms to send the address at start.

See attached image from my oscilloscope SDC (top) and SDA (bottom)
--
Bo Berglund
Sweden

MarkMLl

  • Sr. Member
  • ****
  • Posts: 313
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #20 on: November 17, 2019, 10:21:03 am »
Well done. Can you throw any light on why you appeared to be getting erroneous results from i2cdump?

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

BosseB

  • Jr. Member
  • **
  • Posts: 99
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #21 on: November 17, 2019, 12:04:54 pm »
Well done. Can you throw any light on why you appeared to be getting erroneous results from i2cdump?

MarkMLl
Well, I do not know the command i2cdump very well myself but I found it existed on my system (probably part of some i2c tools).
So I just ran the command as being asked and it showed (and still does) the above.

When I run my own interrogation function it looks like this:
Code: Pascal  [Select]
  1. pi@rpi4-gui:~/projects/MonitorServer $ ./i2ctest 1 0 r 256
  2. EEPROM internal address = $0000
  3. Length of data = 256
  4. 0000   FF  FF  FF  FF  FF  B4  FF  FF  FF  FF  FF  FF  FF  FF  FF  4B
  5. 0010   21  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  6. 0020   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  7. 0030   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  8. 0040   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  AA  FF  FF  FF  FF
  9. 0050   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  10. 0060   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  11. 0070   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  12. 0080   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  13. 0090   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  14. 00A0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  15. 00B0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  16. 00C0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  17. 00D0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  18. 00E0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  19. 00F0   FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF  FF
  20.  
As you can see there are programmed bytes at addresses (hex) 05, 0F, 10, 4B.
Those are the locations I wrote myself using my i2c write function.


--
Bo Berglund
Sweden

kupferstecher

  • Sr. Member
  • ****
  • Posts: 325
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #22 on: November 17, 2019, 01:10:35 pm »
How did you implement the continuous read/write?

BosseB

  • Jr. Member
  • **
  • Posts: 99
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #23 on: November 17, 2019, 01:20:29 pm »
I have not yet tested the sequential write, but for read it is like this:

Code: Pascal  [Select]
  1. type
  2. TByteArray = array of byte;
  3.  
  4. function TRaspiI2C.ReadI2CByteStream(addr: Word; count: integer; var buf: TByteArray): integer;
  5. {Use sequential read to get the data}
  6. var
  7.   buftx: array[0..2] of byte;
  8.   iRet : Byte;
  9.   i: integer;
  10. begin
  11.   try
  12.     if count <> length(buf) then
  13.       SetLength (buf, count);
  14.     buftx[0] := addr shr 8;
  15.     buftx[1] := addr and $FF;
  16.     iRet := 0;
  17.     fpwrite(FHandle, buftx[0], 2);
  18.     for i := 0 to count -1 do
  19.     begin
  20.       fpread(FHandle, iRet, 1);
  21.       buf[i] := iRet;
  22.     end;
  23.     Result := count;
  24.   except
  25.     Result := 0;
  26.   end;
  27.  

In the main code I call Open() such that all reads and writes are being done inside the one Open-Close segment.
Note that I had to declare a type TByteArray in order to be able to set the array length inside the method.
Solution came from Michael Van Canneyt when I asked about a compile error on the Lazarus mail list.
« Last Edit: November 17, 2019, 01:22:49 pm by BosseB »
--
Bo Berglund
Sweden

BosseB

  • Jr. Member
  • **
  • Posts: 99
Re: Example on how to read/write I2C devices on Raspberry Pi?
« Reply #24 on: November 17, 2019, 08:17:02 pm »
I have now tested writing and the crucial thing is that one has to add a 5-6 ms delay (I chose 6) after each fpwrite() when the operation is write on the EEPROM,
So when I looped a full 1-byte write operation it would fail even though the single write did not. But when I inserted a delay of 6 ms after the write to i2c it worked also if I looped the 1-byte write operation.

I got the delay from the datasheet of the EEPROM I use, it states that after a write has been commanded and the stop condition is asserted the chip will go into internal write and does not respond to anything external. The write time in the datasheet is 5ms max.

Concerning sequential write it is more complex than the sequential read because the writes go to a circular buffer of 32 bytes before being written. So the streaming of data must be limited to a maximum of 32 bytes. So I partitioned my streaming function in such a way that it will write up to 32 bytes from the incoming buffer, then delay 6 ms before continuing with the next 32 bytes and so on.
The last packet will be shorter since it is the leftovers after writing n * 32 bytes.

To clarify:
Using I2C for EEPROM handling where typically a higher number of bytes are read/written in the operations it is beneficial to use the available "streaming" methods especially when writing.
For example when writing blocks of data there is a cost of 31 delays each 5 ms per block of 32 bytes if one writes the data byte by byte.

But when dealing with sensors where there probably only is a few bytes to read/write each time, then the trade-offs are not so obvious.

« Last Edit: November 17, 2019, 09:53:38 pm by BosseB »
--
Bo Berglund
Sweden