Lazarus

Programming => General => Topic started by: BosseB on November 12, 2019, 12:14:35 pm

Title: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 12, 2019, 12:14:35 pm
Hi, is there some on-line examples somewhere on how to interface to devices (RTC, sensors) and EEPROM from a FreePascal program?
I am working on a project that uses I2C devices on a plug-in board as well as GPIO I/O.
I can handle the GPIO stuff using the PiGpio unit.
But I have not found any similar thing for the I2C connected devices...

I have heard about BaseUnix unit but I also need an I2C example or two if BaseUnix holds the needed resources.

Thanks.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl on November 12, 2019, 01:05:59 pm
Bo, attached is something that worked for me. It's a bit crude in places but illustrates running on a Raspberry Pi and using no non-standard libraries tto talk to an Arduino-compatible "piggy-backed" on top https://rasp.io/duino/ (https://rasp.io/duino/) via I2C.

MarkMLl
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: Thaddy on November 12, 2019, 01:44:17 pm
The free TMS components for Raspberry Pi comes with units you may want to examine.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: avra on November 12, 2019, 05:21:59 pm
is there some on-line examples somewhere on how to interface to devices (RTC, sensors) and EEPROM from a FreePascal program?
Start here: https://wiki.freepascal.org/Lazarus_on_Raspberry_Pi
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 14, 2019, 08:18:53 pm
OK thanks.
I am now struggling with how to actually read the content of an I2C connected EEPROM chip.
It is using the default address $50 and I would like to know if there is a way to read more data than one byte per operation.
Say in the extreme I want to read the full contents of the EEPROM starting at address 0.
Is there a single i2c command one can issue to the Raspbian driver, which would return the data?
The archive MarkMLl attached is aimed at talking to an i2c connected Arduino board and it uses the following to read the Arduino specific data(, I have removed a number of calls to "Assert", which I don't know what it is):

Code: Pascal  [Select]
  1.   function getResponse(Handle: cint): responseType;
  2.   begin
  3.     response := '........................';     (* 24 characters*)
  4.     Sleep(100);                         (* Milliseconds*)
  5.     if fpRead(Handle, response[1], 24) <> 24 then
  6.       if response[1] = '.' then         (* No response at all*)
  7.         exit(none)
  8.       else                              (* Didn't get to \r *)
  9.         exit(bad);
  10.     if (Pos('EIO', response) <> 1) or (Pos(#$0d, response) = 0) then
  11.       exit(bad)                         (* Not quite right*)
  12.     else begin                          (* Got it*)
  13.       SetLength(response, Pos(#$0d, response) - 1);
  14.       exit(ok)
  15.     end
  16.   end { getResponse } ;
  17.  
  18.  
  19. begin
  20.   fd := fpOpen('/dev/i2c-1', O_RDWR);
  21.   scratch := fpIoctl(fd, I2C_SLAVE, slave);
  22.   scratch := fpWrite(fd, ei[1], 3);  // <== What does this do???
  23.   case getResponse(fd) of
  24.     none: WriteLn('Read timed out');
  25.     bad:  WriteLn('Read incomplete')
  26.   otherwise
  27.     WriteLn(response)
  28.   end;
  29.   scratch := fpClose(fd);
  30.   WriteLn('OK')
  31. end.

I assume that the sleep is inserted in order for the system to have time to read the data into some buffer.
It seems like this expects EXACTLY 24 bytes data back, could this be right?
The call to fpWrite uses the file handle to the i2c device, plus the address to the buffer which shall receive the data. But what does the 3 mean? It could not really be the length of data expected? It is supposed to be 24 bytes, right? What I can see the last argument is the length of data...

So if I do not communicate over i2c like rs232 to another party, but want to read a specific amount of data from say an EEPROM, how is that set up?


Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: avra on November 15, 2019, 10:27:38 am
I would like to know if there is a way to read more data than one byte per operation.
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. For example, if your i2c eeprom device is 24LCxx family (other families are sometimes quite different), then you find datasheet (http://ww1.microchip.com/downloads/en/DeviceDoc/I2C%20Serial%20EE%20Family%20Data%20Sheet%2021930C.pdf) for it on the net and try to understand as much as you can. For the question you have, you search for the supported features and see what is of interest. Datasheet says that Byte Write, Page Write, Current Address Read, Random Read and Sequential Read I2C operations (functions) are available. So, for the most optimal block read/write you should use these 24LCxx internal I2C functions. For sub optimal block read/write you could forget all said, and just use a simple loop to read/write individual bytes. Since I2C protocol is byte based anyway, my wild guess is that in best case optimized read/write is about 2 times faster. If you can live without such speed then you can happily get away with suboptimal simple read/write loop. If speed is important, then you should also check what is the highest speed that your I2C eeprom chip supports and then initialize your I2C channel to that speed. You will agree that reading something with 100kbits/s is not he same as reading it with 400 Kbit/s, 1Mbit/s or 3,2 Mbit/s.

Using BaseUnix may be OK for classic linux developers, but if you find your self in position of translating some existing Arduino I2C library to FPC then you may want to look for something different and microcontroller like programming lib. All other libraries mentioned on that wiki page are like that. I could not find linux example, but on Atmel AVR accessing 24LCxx I2C eeprom looks like this (https://wiki.freepascal.org/AVR_Embedded_Tutorial_-_I²C_EEPROM/de).

Btw, linux kernel already has at24 and some other eeprom drivers (https://github.com/torvalds/linux/tree/master/drivers/misc/eeprom). It will be good to read some comments (https://lm-sensors.lm-sensors.narkive.com/aSVjZ22u/at24c-i2c-eeprom-driver) about it first. There is also a custom at24 block driver (https://github.com/tdrozdovskiy/i2c_eeprom_blk) so you might want to explore that path, too.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: kupferstecher on November 15, 2019, 11:58:26 am
@BosseB: You can use the pascalio lib. The usage is easier to understand than the code you posted.

By the way, be careful with the address, some datasheets talk about the 7-bit address, which is the first 7 bit of a byte, others talk about the 8-bit address which contains the least significant bit for the direction (read/write). So your $50 could be a $A0 or a $28 as well.

As avra said, you have to check the datasheet on how to access the chip. Normally you read (write) a byte or word out of (into) a register. The sequence then is something like slave-adress/register-address/data with start/stop conditions and acknowledge in certain order in between. A library like pascalio provides functions for that certain sequence already. E.g. WriteRegByte(aRegAddress, aData);
The chip may offer a continuous reading of the eeprom data (sending the address only once), but then you have to understand the protokoll, so its easier to do it "bytewise" with the library function (calling it for each byte you want to read). In the end its only a question of speed.

Here a snippet of my code accessing a PCA9685 (LED driver chip) using pascalio.

Code: Pascal  [Select]
  1. INTERFACE
  2. Uses fpi2c;
  3.  
  4. Type TPCA9685 = class
  5.   {$IFDEF RASPBERRY}
  6.   I2CPort: TI2CLinuxDevice; {$ENDIF}
  7.   Constructor Create(AAddress: ptrInt; AName: String = 'I2C');
  8. [...]
  9. end;
  10.  
  11. IMPLEMENTATION
  12. Constructor TPCA9685.Create(AAddress: ptrInt; AName: String = 'I2C');
  13. var
  14.   newMode1: byte;
  15.   ii: Integer;
  16. begin
  17.   Name:= AName;
  18.   Status.Write(Name+': I2C initialisation on address '+ Inttostr(AAddress)); Application.ProcessMessages;
  19.  
  20.  
  21.   if I2CPort <> nil then I2CPort.Free;
  22.   I2CPort:= TI2CLinuxDevice.Create(AAddress,1);
  23.   sleep(100);
  24.   try
  25.     I2CPort.WriteRegByte(regMODE2, bitOUTDRV);
  26.     I2CPort.WriteRegByte(regMODE1, bitALLCALL);
  27.     sleep(100); //wait for oscillator
  28.     newMode1:= I2CPort.ReadRegByte(regMODE1);
  29.     newMode1:= newMode1 and not bitSLEEP; //wake up (reset sleep)
  30.     I2CPort.WriteRegByte(regMODE1, newMode1);
  31.     sleep(100); //wait for oscillator
  32.  
  33.     SetPWMFreq(500);
  34.  
  35.   except
  36.     Status.Write(Name+': Error while initialisation of I2C');
  37.   end;
  38.  
  39. [...]
  40. end;
  41.  
  42.  
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 15, 2019, 12:03:25 pm
@avra,
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. And it has always been pretty simple when you have access to the actual pins on the device. I stored a whole lot of config data on these EEPROMs for the PIC projects.
And I had the opportunity to help other PIC developers as well, see here for help on bit-banging I2C (https://www.microchip.com/forums/FindPost/979446).

But the problem here for me is that the EEPROM sits on the other side of Linux as viewed from my side...
And I am not particularly insightful on the inner workings of Linux, that is why I am asking around here...
For high level work I have used Delphi since about 1995 and switched to FreePascal/Lazarus about 6-7 years ago.

Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 15, 2019, 12:08:34 pm
@BosseB: You can use the pascalio lib. The usage is easier to understand than the code you posted.

By the way, be careful with the address, some datasheets talk about the 7-bit address, which is the first 7 bit of a byte, others talk about the 8-bit address which contains the least significant bit for the direction (read/write). So your $50 could be a $A0 or a $28 as well.

As avra said, you have to check the datasheet on how to access the chip. Normally you read (write) a byte or word out of (into) a register. The sequence then is something like slave-adress/register-address/data with start/stop conditions and acknowledge in certain order in between. A library like pascalio provides functions for that certain sequence already. E.g. WriteRegByte(aRegAddress, aData);
The chip may offer a continuous reading of the eeprom data (sending the address only once), but then you have to understand the protokoll, so its easier to do it "bytewise" with the library function (calling it for each byte you want to read). In the end its only a question of speed.
Thanks, could you specify how I can find pascalio. Googling it draws a blank for me...
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl on November 15, 2019, 12:35:56 pm
@avra,
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.

Bo, are you doing your usual trick of not reading all the messages posted in response to your query? I gave you an example of the low-level OS interface several days ago- did you read it?

MarkMLl
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 15, 2019, 02:03:31 pm
@avra,
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.

Bo, are you doing your usual trick of not reading all the messages posted in response to your query? I gave you an example of the low-level OS interface several days ago- did you read it?
What do you mean by "usual trick"?
I thought that these forums are the place to ask when you get stuck on something...
And, yes I downloaded your file and I have tried to apply it too as you could well see a few posts above where I show parts of your code...

But I have not been able to understand how I can use it for my use case yet.
It seems to be about communicating terminal-style with an Arduino and I am not doing that type of process. Instead I want to read from an EEPROM at a specific address a certain number of bytes using the Linux (Raspbian) functions on a Raspberry Pi system.

Today I finally received my EEPROM IC:s (DIP packages so I can prototype) and I will breadboard one and hook it up to the RPi and see what can be done. I will for sure try to adapt your code, if I can.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl on November 15, 2019, 02:59:04 pm
That's using BaseUnix on an RPi to talk to an I2C slave. I don't believe that the fact that the slave is implemented by an Arduino is relevant.

Now, what are you having problems with? Have you looked for and read the description of the I2C ioctl()?

As (I think it was) Avra pointed out, you have to understand the format of the command the slave expects and the format of the return. In the example I have you, the command was a short sequence of characters and the response a lightly longer one.

MarkMLl
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: avra on November 15, 2019, 04:15:35 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.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: kupferstecher on November 15, 2019, 06:52:52 pm
Thanks, could you specify how I can find pascalio. Googling it draws a blank for me...
Here's the link:
https://github.com/SAmeis/pascalio
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB on November 15, 2019, 07:14:34 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.
But it seems like the way to go is something like this:
Read data from the device
- open the I2C channel itself (as a file, I guess)
- write a command consisting of the device address with the lsb set to the read state plus the start address in the EEPROM
- read the wanted number of bytes from the device
- close the I2C device

Writing seems to be a bit like that except now the LSB on the address is set to the write state
- open the I2C channel itself (as a file, I guess)
- write a command consisting of the device address plus the start address in the EEPROM
- write the wanted number of bytes from the device (repeat the same 1-byte write command over and over?)
- close the I2C device

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.

Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl on November 15, 2019, 07:34:19 pm
Remember that you have to specify the slave address.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: avra 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 (https://github.com/rudiratlos/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.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB 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...
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl 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
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB 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)
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: MarkMLl 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
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB 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.


Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: kupferstecher on November 17, 2019, 01:10:35 pm
How did you implement the continuous read/write?
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB 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.
Title: Re: Example on how to read/write I2C devices on Raspberry Pi?
Post by: BosseB 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.