Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2023-04

FPC/Lazarus on RPi4 - How to read/write I2C connected EEPROM?

33 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Bo Berglund

I need to read and write data to an EEPROM connected by I2C on a RaspberryPi4. The I2C channel is found in /dev as i2c-1:

$ ll /dev/i2*
crw-rw---- 1 root i2c 89, 1 2019-02-14 11:12 /dev/i2c-1

In this channel the EEPROM CAT24C128 is at address 0x50 The data on the device is organized as 256 pages of 64 bytes data each AFAIU.

I can check the EEPROM using i2c-tools, which I have installed, but I am unsure if the read is correct...

Question:
Can I use Linux file system commands to read/write the data on the i2c EEPROM memory device and if so how from fpc?

I have installed the current versions of Lazarus and Fpc on the RPi4 itself.

Bo Berglund Developer in Sweden

Reply by email

Unknown author
to Bo Berglund

On unix (and all of it's derivatives, everything is a file, including connections to other systems.  You can treat it like a file, and in (most) circumstances, it will operate just as if it were a local file.

Some devices require special handling, but for the most part, if it's listed as a file on the system, you can read it as one.

Of course, permissions apply, so creating files or reading them may be limited by user/group permissions, so there's no guarantee that just because the file is there, you'll be able to read it.

Bo Berglund wrote 19 lines
I need to read and write data to an EEPROM connected by I2C on a RaspberryPi4. The I2C channel is found in /dev as i2c-1: $ ll /dev/i2* crw-rw---- 1 root i2c 89, 1 2019-02-14 11:12 /dev/i2c-1 In this channel the EEPROM CAT24C128 is at address 0x50 The data on the device is organized as 256 pages of 64 bytes data each AFAIU. I can check the EEPROM using i2c-tools, which I have installed, but I am unsure if the read is correct... Question: Can I use Linux file system commands to read/write the data on the i2c EEPROM memory device and if so how from fpc? I have installed the current versions of Lazarus and Fpc on the RPi4 itself.

Reply by email

Giuliano Colla
to Bo Berglund: I need to read and write data to an EEPROM connected by I2C on a RaspberryPi4. The I2C…

Il 01/04/2023 21:56, Bo Berglund via fpc-pascal ha scritto:

Show quoted text 19 lines
I need to read and write data to an EEPROM connected by I2C on a RaspberryPi4. The I2C channel is found in /dev as i2c-1: $ ll /dev/i2* crw-rw---- 1 root i2c 89, 1 2019-02-14 11:12 /dev/i2c-1 In this channel the EEPROM CAT24C128 is at address 0x50 The data on the device is organized as 256 pages of 64 bytes data each AFAIU. I can check the EEPROM using i2c-tools, which I have installed, but I am unsure if the read is correct... Question: Can I use Linux file system commands to read/write the data on the i2c EEPROM memory device and if so how from fpc? I have installed the current versions of Lazarus and Fpc on the RPi4 itself.

I2c is a serial protocol. All what the hardware can see is just a pair of bits: a clock line and a data line. I2c-tools provides the required software to write and read those two bits. In your case for programming the EEPROM and reading it back. The linux kernel uses i2c-tools and knows what i2c-tools tell it.

I don't know if i2c-tools can be required to emulate a block device thus providing the system a block device, which then becomes readable by any means. But even if it does, and you can read your EEPROM from fpc, or whatever software you like, you will not gain any advantage, because the reading mechanism will be implemented by i2c-tools, and it will be exactly the same when verifying after programming. If reading was wrong, it will be wrong again, if it was good it'll be good again. Forget about writing. Writing an EEPROM is a complex procedure. If i2c-tools knows how to do it, let it do it.

If you really need an independent verification, you should find an adapter which accepts your EEPROM and makes it accessible via a block device interface, such as USB. I don't know if such adapter does exist, but it's the only way.

Giuliano

Do not do to others as you would have them do to you.They might have different tastes.

Reply by email

Unknown author
to Giuliano Colla

Generally, raspberry pi devices connected to other devices will (by default) use python for their programming.  The python libs have all the setup and tear down code built in.  You can use C as well, so I see no reason why FPC couldn't be used.  It's probably going to take some work though.  You'll need to either find the appropriate python files, and port them to FPC, or convert the C headers into something pascal can understand.  I've never made the attempt to do this, though now that you bring this up, I might just take a until now, I've used C (since I'm not a fan of python) when talking to raspberry connected devices, but I'm pretty sure I've seen posts where folks were using other languages such as PHP or Java to talk to the pi devices, so those header files have most certainly been ported to other languages. It's possible someone already did the pascal port, though I can't point to one.  I've not been messing with my raspberry pis for the last year or so because of moving, and things are just now getting back to normal (I hate moving), and I need a new pi anyhow, so perhaps I will get back to it this year, but the reason I say this, is because I've not kept up with the pi forums, so I don't know what's happening around the pi ecosystems lately, which is why I can't answer the question directly, though it's certainly more than possible.

Sorry I don't have more solid information for you.

Giuliano Colla wrote 50 lines
Il 01/04/2023 21:56, Bo Berglund via fpc-pascal ha scritto: > I need to read and write data to an EEPROM connected by I2C on a > RaspberryPi4. > The I2C channel is found in /dev as i2c-1: > > $ ll /dev/i2* > crw-rw---- 1 root i2c 89, 1 2019-02-14 11:12 /dev/i2c-1 > > In this channel the EEPROM CAT24C128 is at address 0x50 > The data on the device is organized as 256 pages of  64 bytes data > each AFAIU. > > I can check the EEPROM using i2c-tools, which I have installed, but I > am unsure > if the read is correct... > > Question: > Can I use Linux file system commands to read/write the data on the > i2c EEPROM > memory device and if so how from fpc? > > I have installed the current versions of Lazarus and Fpc on the RPi4 > itself. > > I2c is a serial protocol. All what the hardware can see is just a pair of bits: a clock line and a data line. I2c-tools provides the required software to write and read those two bits. In your case for programming the EEPROM and reading it back. The linux kernel uses i2c-tools and knows what i2c-tools tell it. I don't know if i2c-tools can be required to emulate a block device thus providing the system a block device, which then becomes readable by any means. But even if it does, and you can read your EEPROM from fpc, or whatever software you like, you will not gain any advantage, because the reading mechanism will be implemented by i2c-tools, and it will be exactly the same when verifying after programming. If reading was wrong, it will be wrong again, if it was good it'll be good again. Forget about writing. Writing an EEPROM is a complex procedure. If i2c-tools knows how to do it, let it do it. If you really need an independent verification, you should find an adapter which accepts your EEPROM and makes it accessible via a block device interface, such as USB. I don't know if such adapter does exist, but it's the only way. Giuliano

Reply by email

Christo Crause
to Bo Berglund: I need to read and write data to an EEPROM connected by I2C on a RaspberryPi4. The I2C…

On Sat, Apr 1, 2023 at 9:57 PM Bo Berglund via fpc-pascal <

Show quoted text 7 lines
wrote: Question: Can I use Linux file system commands to read/write the data on the i2c EEPROM memory device and if so how from fpc?

To some extent it is possible to handle i2c transactions as file read/write operations, but some ioctl calls are required to configure e.g. the device address [1]. Take note that only a subset of i2c functionality is available using read/write calls. Lazi2cdev [2] uses this approach.

For finer control over the i2c transactions (for example combined write/read transactions) low level ioctl calls are available in Linux. I wrote an i2c class [3] using this approach.

1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html
2. https://github.com/laz2wiringpi/lazI2cdev/blob/master/i2cdev_base.pas
2. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas

Reply by email

Bo Berglund
to Unknown author: Generally, raspberry pi devices connected to other devices will (by default) use python…
Travis Siegel wrote 7 lines
Generally, raspberry pi devices connected to other devices will (by default) use python for their programming.  The python libs have all the setup and tear down code built in.  You can use C as well, so I see no reason why FPC couldn't be used.  It's probably going to take some work though.  You'll need to either find the appropriate python files, and port them to FPC, or convert the C headers into something pascal can understand.

I have never used Oython, looked at it a while back and was taken aback by the idea that whitespace in source code has meaning....

Show quoted text 8 lines
I've never made the attempt to do this, though now that you bring this up, I might just take a until now, I've used C (since I'm not a fan of python) when talking to raspberry connected devices, but I'm pretty sure I've seen posts where folks were using other languages such as PHP or Java to talk to the pi devices, so those header files have most certainly been ported to other languages. It's possible someone already did the pascal port, though I can't point to one.

I wonder if Chat-GPT can help here?
Never tried it myself but my daughter tells me it can do things google cannot.

Show quoted text 7 lines
I've not been messing with my raspberry pis for the last year or so because of moving, and things are just now getting back to normal (I hate moving), and I need a new pi anyhow, so perhaps I will get back to it this year, but the reason I say this, is because I've not kept up with the pi forums, so I don't know what's happening around the pi ecosystems lately, which is why I can't answer the question directly, though it's certainly more than possible.

If you have missed it the RaspberryPi Foundation is one of these that have been hardest hit by the Covid pandemic's disruption of supply lines and semiconductor shortage! They use state-of-the-art level of silicon for their newer models and these are the hardest hit by the manufacturing problems in Taiwan. Expected resume of deliveries second half of 2023... Meanwhile only small quantities of RPi4 devices can be made and are on allocation.

Right now there is *no* availability of RPi4 devices for the general public at all!

Older models may appear in very small batches now and then..

Sometimes you may find a couple of RPiZero W devices for purchase, but then they are limited to a single item per customer (while stock lasts, which usually is a day or two).

Here is a RPiForum thread about this dilemma (last page of 16) : https://forums.raspberrypi.com/viewtopic.php?p=2095696#p2095696

And here is the PiLocator, which tries to keep track of where there are actual devices to buy:
https://rpilocator.com/

Except it seems like it is off-line right now, it worked a week ago when I could find and buy a PiZero.

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Christo Crause: On Sat, Apr 1, 2023 at 9:57 PM Bo Berglund via fpc-pascal < To some extent it is…
Christo Crause wrote 21 lines
On Sat, Apr 1, 2023 at 9:57?PM Bo Berglund via fpc-pascal < > wrote: > Question: > Can I use Linux file system commands to read/write the data on the i2c > EEPROM > memory device and if so how from fpc? > To some extent it is possible to handle i2c transactions as file read/write operations, but some ioctl calls are required to configure e.g. the device address [1]. Take note that only a subset of i2c functionality is available using read/write calls. Lazi2cdev [2] uses this approach. For finer control over the i2c transactions (for example combined write/read transactions) low level ioctl calls are available in Linux. I wrote an i2c class [3] using this approach. 1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html 2. https://github.com/laz2wiringpi/lazI2cdev/blob/master/i2cdev_base.pas 2. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas

Thanks for the reply and links!
Then I need to go the low-level way, I guess...

I had hoped it would be a simpler way using OS available functions on RPi.

I have used these EEPROM devices on some Microchip PIC24 projects where I had to do the nitty-gritty low level stuff myself including bit-banging the I2C bus...

With that approach I have to *read the details* on the EEPROM datasheets to see how they are handled, unless of course I can port my own i2c code (Ansi-C) to pascal...

Luckily on RPi the system is built to allow users to do low-level stuff on the I/O lines.

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund
Bo Berglund wrote 38 lines
On Sun, 2 Apr 2023 07:31:43 +0200, Christo Crause via fpc-pascal wrote: >On Sat, Apr 1, 2023 at 9:57?PM Bo Berglund via fpc-pascal < >> wrote: > >> Question: >> Can I use Linux file system commands to read/write the data on the i2c >> EEPROM >> memory device and if so how from fpc? >> > >To some extent it is possible to handle i2c transactions as file read/write >operations, but some ioctl calls are required to configure e.g. the device >address [1]. Take note that only a subset of i2c functionality is available >using read/write calls. Lazi2cdev [2] uses this approach. > >For finer control over the i2c transactions (for example combined >write/read transactions) low level ioctl calls are available in Linux. I >wrote an i2c class [3] using this approach. > >1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html >2. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas Thanks for the reply and links! Then I need to go the low-level way, I guess... I had hoped it would be a simpler way using OS available functions on RPi. I have used these EEPROM devices on some Microchip PIC24 projects where I had to do the nitty-gritty low level stuff myself including bit-banging the I2C bus... With that approach I have to *read the details* on the EEPROM datasheets to see how they are handled, unless of course I can port my own i2c code (Ansi-C) to pascal... Luckily on RPi the system is built to allow users to do low-level stuff on the I/O lines.

After consulting my old code and the datasheet for CAT24C128 and looking at the links you posted I have come to the following conclusion:

1) My old code for PIC24 is too close to the silicon and I don't see how I can use that on a Linux machine...

2) Your linked FPC unit i2c.pas looks promising to mee, I will test it.

Notes:
------
The EEPROM on the board is a 128 kbit (16 kbytes) version organized as 64 256 byte pages. The datsheet says this about a write cycle:

---------------------------------------------------------------- Byte Write
Upon receiving a Slave address with the R/W bit set to ‘0’, the CAT24C128 will interpret the next two bytes as address bytes. These bytes are used to initialize the internal address counter; the 2 most significant bits are ‘don’t care’, the next 8 point to one of 256 available pages and the last 6 point to a location within a 64 byte page. A byte following the address bytes will be interpreted as data. The data will be loaded into the Page Write Buffer and will eventually be written to memory at the address specified by the 14 active address bits provided earlier. The CAT24C128 will acknowledge the Slave address, address bytes and data byte. The Master then starts the internal Write cycle by issuing a STOP condition (Figure 6). During the internal Write cycle (tWR), the SDA output will be tri-stated and additional Read or Write requests will be ignored (Figure 7).
------------------------------------------------------------------

So here the sequence on the bus will contain a 2-byte address rather than a single byte address.
This needs to be taken into consideration and I believe it *is* in the i2c.pas unit where there are overloaded functions for byte and uint16 type addresses for read and write, one only needs to use the correct width of the address argument and it will be correctly chosen! :-)

3) Regarding the other unit you linked to (i2cdev_base.pas): I see it is not really relevant to me due to the type of EEPROM I have etc, so I will go with the i2c.pas.

I have just gotten to this reply so have not yet had time to fire up my RPi4 Lazarus on the system that has the board installed in order to test it..

I will do so shortly and report back what I found. The plan is to store the application settings on the EEPROM along with a hashed authentication code to unlock the application itself.

AGAIN:
I AM GRATEFUL FOR THE LINK TO THIS FPC UNIT!!!!
Hopefully it will solve my problems in using the EEPROM on the board.

Bo Berglund Developer in Sweden

Reply by email

Dennis Lee Bieber
to Bo Berglund

On Sun, 02 Apr 2023 16:16:46 +0200, Bo Berglund via fpc-pascal declaimed
the following:

---------------------------------------------------------------- Byte Write

<SNIP>

a location within a 64 byte page. A byte following the address bytes will be interpreted as data. The data will be loaded into the Page Write Buffer and will eventually be written to memory at the address specified by the 14 active

<SNIP>

Show quoted text 6 lines
The Master then starts the internal Write cycle by issuing a STOP condition (Figure 6). During the internal Write cycle (tWR), the SDA output will be tri-stated and additional Read or Write requests will be ignored (Figure 7). ------------------------------------------------------------------

This sounds suspiciously like one is expected (or is able) to write multiple bytes /within/ the "page" via multiple address/data operations, and have all those bytes "burned" into the memory using a single "stop" operation.

Reply by email

Bo Berglund
to Christo Crause: On Sat, Apr 1, 2023 at 9:57 PM Bo Berglund via fpc-pascal < To some extent it is…
Christo Crause wrote 21 lines
On Sat, Apr 1, 2023 at 9:57?PM Bo Berglund via fpc-pascal < > wrote: > Question: > Can I use Linux file system commands to read/write the data on the i2c > EEPROM > memory device and if so how from fpc? > To some extent it is possible to handle i2c transactions as file read/write operations, but some ioctl calls are required to configure e.g. the device address [1]. Take note that only a subset of i2c functionality is available using read/write calls. Lazi2cdev [2] uses this approach. For finer control over the i2c transactions (for example combined write/read transactions) low level ioctl calls are available in Linux. I wrote an i2c class [3] using this approach. 1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html 2. https://github.com/laz2wiringpi/lazI2cdev/blob/master/i2cdev_base.pas 3. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas

Hello again!

I have now tested the two demos in github [3] and they seem to work OK.

The i2cscan works and shows exactly the same as sudo i2cdetect -y 1 does from the i2c-tools package I installed via apt.

i2c_read_reg works too inasmuch as it does not error out, but it returns FF whatever addres I read like the i2c-tools command does. I am not sure that it is correct though, for that I need to create an i2c_write demo app first so I can write values at different locations and read them back. The EEPROM has until now not been used so it is in its factory state with no data.

Is there a reason that there is no i2c_write_reg demo in the github package?

I will try to write one so I can test that as well...

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Dennis Lee Bieber: On Sun, 02 Apr 2023 16:16:46 +0200, Bo Berglund via fpc-pascal declaimed the following:…
Dennis Lee Bieber wrote 25 lines
On Sun, 02 Apr 2023 16:16:46 +0200, Bo Berglund via fpc-pascal declaimed the following: > >---------------------------------------------------------------- >Byte Write <SNIP> >a location within a 64 byte page. A byte following the >address bytes will be interpreted as data. The data will be >loaded into the Page Write Buffer and will eventually be >written to memory at the address specified by the 14 active <SNIP> >The Master then starts the internal Write cycle by issuing a >STOP condition (Figure 6). During the internal Write cycle >(tWR), the SDA output will be tri-stated and additional Read >or Write requests will be ignored (Figure 7). >------------------------------------------------------------------ > This sounds suspiciously like one is expected (or is able) to write multiple bytes /within/ the "page" via multiple address/data operations, and have all those bytes "burned" into the memory using a single "stop" operation.

Exactly, you specify *start* as a 2-byte address (pageno/offset) and then you output as many sequential bytes as you need to write in the command.

All will wind up in the 256 byte on-chip write buffer until the stop arrives and then it will be dumped into memory. If the buffer address increments past 0xff it will wrap back into 0x0 in the buffer for following bytes and this is probably something you must not allow...

So an application using i2c must check the arguments for this page transition case and either error out or spawn a second operation for the following page in EEPROM to handle the excess bytes.

I have not yet tested this case because the github project does not have an i2c_write demo...
Must write one myself.

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Christo Crause
Christo Crause wrote 12 lines
On Sun, Apr 2, 2023 at 9:30?PM Bo Berglund via fpc-pascal < > wrote: > Is there a reason that there is no i2c_write_reg demo in the github > package? > I haven't gotten round to that, waiting for hardware to test the code with. I will try to write one so I can test that as well... > Would be nice if you could share that :-)

Sure, I will probably continue with this tomorrow (I'm in Europe so it is soon midnight here...).

If I get it going I will post it or else I will ask about it here.

My hardware is ready and operational, in fact my board also has another EEPROM (same type) sitting on the i2c-0 bus prepared for being used as a HAT EEPROM. Haven't gotten that far on the HAT handling though so it is totally unused.

Bo Berglund Developer in Sweden

Reply by email

Jeffrey A. Wormsley
to Bo Berglund: On Sun, 2 Apr 2023 07:31:43 +0200, Christo Crause via fpc-pascal Hello again! I have now…
Show quoted text 5 lines
i2c_read_reg works too inasmuch as it does not error out, but it returns FF whatever addres I read like the i2c-tools command does. I am not sure that it is correct though...

Yes this is expected, as eeproms erase to $FF. You can write to the same byte as many times as you want, as long as you only change 1 bits to 0 bits. If you must change a 0 bit to a 1 bit, you have to erase back to $FF first. Be aware that erase means erasing the entire page, and not just a single byte. This is where things like "wear leveling" come into play, because a page can only be erased a limited number of times (it used to be around 10,000, but maybe newer devices have extended this). How you manage writes to eeprom can be tricky, best to use them for things that don't change very often, such as configuration data.

Jeff

On Sun, Apr 2, 2023 at 3:30 PM Bo Berglund via fpc-pascal <

Show quoted text 61 lines
wrote: On Sun, 2 Apr 2023 07:31:43 +0200, Christo Crause via fpc-pascal wrote: >On Sat, Apr 1, 2023 at 9:57?PM Bo Berglund via fpc-pascal < >> wrote: > >> Question: >> Can I use Linux file system commands to read/write the data on the i2c >> EEPROM >> memory device and if so how from fpc? >> > >To some extent it is possible to handle i2c transactions as file read/write >operations, but some ioctl calls are required to configure e.g. the device >address [1]. Take note that only a subset of i2c functionality is available >using read/write calls. Lazi2cdev [2] uses this approach. > >For finer control over the i2c transactions (for example combined >write/read transactions) low level ioctl calls are available in Linux. I >wrote an i2c class [3] using this approach. > >1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html >2. https://github.com/laz2wiringpi/lazI2cdev/blob/master/i2cdev_base.pas >3. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas Hello again! I have now tested the two demos in github [3] and they seem to work OK. The i2cscan works and shows exactly the same as sudo i2cdetect -y 1 does from the i2c-tools package I installed via apt. i2c_read_reg works too inasmuch as it does not error out, but it returns FF whatever addres I read like the i2c-tools command does. I am not sure that it is correct though, for that I need to create an i2c_write demo app first so I can write values at different locations and read them back. The EEPROM has until now not been used so it is in its factory state with no data. Is there a reason that there is no i2c_write_reg demo in the github package? I will try to write one so I can test that as well... -- Bo Berglund Developer in Sweden _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Bo Berglund
to Jeffrey A. Wormsley
"Jeffrey A. Wormsley wrote 18 lines
> > i2c_read_reg works too inasmuch as it does not error out, but it returns FF > whatever addres I read like the i2c-tools command does. > I am not sure that it is correct though... > Yes this is expected, as eeproms erase to $FF. You can write to the same byte as many times as you want, as long as you only change 1 bits to 0 bits. If you must change a 0 bit to a 1 bit, you have to erase back to $FF first. Be aware that erase means erasing the entire page, and not just a single byte. This is where things like "wear leveling" come into play, because a page can only be erased a limited number of times (it used to be around 10,000, but maybe newer devices have extended this). How you manage writes to eeprom can be tricky, best to use them for things that don't change very often, such as configuration data. Jeff

This is incorrect!
The EEPROM I am talking about is a small 8-pin chip specifically used in IoT devices to save non-volatile data and these devices are r/w on single bytes.

I have just tested it by executing command line commands to write a set of data to a specific address and then read it back and verified that it was there. Then I repeated the process for the same addresses but different data. Readback again showed only the new data correctly.

What you are talking about might be certain microcontrollers which simulate an EEPROM memory in their flash program memory data. That flash is commonly erasable only for full pages. Not the case here...

I am working on using the FreePascal GitHub code and expanding it into a class I can use in my RPi4 project. Since I want to generalize it as much as possible it takes a few more hours than I thought...

Bo Berglund Developer in Sweden

Reply by email

Jeffrey A. Wormsley
to Bo Berglund

I've been using i2c and SPI eeproms for almost two decades. All of them can read/write a single byte if that's what you want to do, but the write/erase restrictions I noted are there for any parts I've ever used. If you write a byte to $00, you cannot write it to $01 without first erasing the entire block that it is located in (usually 128 to 256 bytes). You can write $FF to $F0, then $F0 to $80, then $80 to $00, as these all only involve turning 1 bits into 0 bits, but if you try to write from a $00 to a $01, then it will not work, at least not on any part I've ever used, as that involves turning a 0 bit into a 1 bit. All of the parts I've seen, if you want to modify a single memory location (if the write involves turning a 0 bit into a 1 bit), they require you to read the block of data into your CPU, modify it there, erase the block, then write the entire block data back into the chip. I've seen some parts that have an on-chip RAM buffer that would let you read the block into this RAM buffer with one command, modify the RAM buffer with a single byte write, erase the block, then write the RAM buffer back to the chip, which is a lot faster than having to do this yourself with your CPU memory, but it still required the block erase and that block erase still counted towards your maximum number of erase cycles. I have not seen a chip that allowed completely random writes at the byte level before short of a static ram with battery backup.

If you have an i2c part that doesn't work like this, then I would be very interested in knowing the part number and seeing a link to the data sheet. Such a part would make using these sorts of devices much easier, but to date I have never seen one.

Jeff

On Mon, Apr 3, 2023 at 10:53 AM Bo Berglund via fpc-pascal <

Show quoted text 59 lines
wrote: On Mon, 3 Apr 2023 08:42:12 -0400, "Jeffrey A. Wormsley via fpc-pascal" wrote: >> >> i2c_read_reg works too inasmuch as it does not error out, but it returns FF >> whatever addres I read like the i2c-tools command does. >> I am not sure that it is correct though... >> > >Yes this is expected, as eeproms erase to $FF. You can write to the same >byte as many times as you want, as long as you only change 1 bits to 0 >bits. If you must change a 0 bit to a 1 bit, you have to erase back to $FF >first. Be aware that erase means erasing the entire page, and not just a >single byte. This is where things like "wear leveling" come into play, >because a page can only be erased a limited number of times (it used to be >around 10,000, but maybe newer devices have extended this). How you manage >writes to eeprom can be tricky, best to use them for things that don't >change very often, such as configuration data. > >Jeff > This is incorrect! The EEPROM I am talking about is a small 8-pin chip specifically used in IoT devices to save non-volatile data and these devices are r/w on single bytes. I have just tested it by executing command line commands to write a set of data to a specific address and then read it back and verified that it was there. Then I repeated the process for the same addresses but different data. Readback again showed only the new data correctly. What you are talking about might be certain microcontrollers which simulate an EEPROM memory in their flash program memory data. That flash is commonly erasable only for full pages. Not the case here... I am working on using the FreePascal GitHub code and expanding it into a class I can use in my RPi4 project. Since I want to generalize it as much as possible it takes a few more hours than I thought... -- Bo Berglund Developer in Sweden _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Jeffrey A. Wormsley
to Jeffrey A. Wormsley: Yes this is expected, as eeproms erase to $FF. You can write to the same byte as many…

I noticed you put the part number in the original email.

Interestingly, the data sheet mentions 64 byte pages, and 1,000,000 erase cycles, but does not mention the need to erase a page before writing. So perhaps this part does not have that restriction.

If you're making a library specifically for this part, then I guess you don't have to worry about this. If you're making a library for generic eeprom support, I think you might want to consider that not all parts behave this way.

Jeff

Show quoted text 82 lines
i2c_read_reg works too inasmuch as it does not error out, but it returns FF > whatever addres I read like the i2c-tools command does. > I am not sure that it is correct though... > Yes this is expected, as eeproms erase to $FF. You can write to the same byte as many times as you want, as long as you only change 1 bits to 0 bits. If you must change a 0 bit to a 1 bit, you have to erase back to $FF first. Be aware that erase means erasing the entire page, and not just a single byte. This is where things like "wear leveling" come into play, because a page can only be erased a limited number of times (it used to be around 10,000, but maybe newer devices have extended this). How you manage writes to eeprom can be tricky, best to use them for things that don't change very often, such as configuration data. Jeff On Sun, Apr 2, 2023 at 3:30 PM Bo Berglund via fpc-pascal < > wrote: > On Sun, 2 Apr 2023 07:31:43 +0200, Christo Crause via fpc-pascal > wrote: > > >On Sat, Apr 1, 2023 at 9:57?PM Bo Berglund via fpc-pascal < > >> wrote: > > > >> Question: > >> Can I use Linux file system commands to read/write the data on the i2c > >> EEPROM > >> memory device and if so how from fpc? > >> > > > >To some extent it is possible to handle i2c transactions as file > read/write > >operations, but some ioctl calls are required to configure e.g. the > device > >address [1]. Take note that only a subset of i2c functionality is > available > >using read/write calls. Lazi2cdev [2] uses this approach. > > > >For finer control over the i2c transactions (for example combined > >write/read transactions) low level ioctl calls are available in Linux. I > >wrote an i2c class [3] using this approach. > > > >1. https://www.kernel.org/doc/html/latest/i2c/dev-interface.html > >2. https://github.com/laz2wiringpi/lazI2cdev/blob/master/i2cdev_base.pas > >3. https://github.com/ccrause/rpiplc_pas/blob/main/native/i2c.pas > > Hello again! > > I have now tested the two demos in github [3] and they seem to work OK. > > The i2cscan works and shows exactly the same as sudo i2cdetect -y 1 does > from > the i2c-tools package I installed via apt. > > i2c_read_reg works too inasmuch as it does not error out, but it returns > FF > whatever addres I read like the i2c-tools command does. > I am not sure that it is correct though, for that I need to create an > i2c_write > demo app first so I can write values at different locations and read them > back. > The EEPROM has until now not been used so it is in its factory state with > no > data. > > Is there a reason that there is no i2c_write_reg demo in the github > package? > > I will try to write one so I can test that as well... > > > -- > Bo Berglund > Developer in Sweden > > _______________________________________________ > fpc-pascal maillist - > https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal >

Reply by email

Bo Berglund
to Jeffrey A. Wormsley: I've been using i2c and SPI eeproms for almost two decades. All of them can read/write a…
Show quoted text 51 lines
On Mon, Apr 3, 2023 at 10:53?AM Bo Berglund via fpc-pascal < > wrote: > On Mon, 3 Apr 2023 08:42:12 -0400, "Jeffrey A. Wormsley via fpc-pascal" > wrote: > > >> > >> i2c_read_reg works too inasmuch as it does not error out, but it > returns FF > >> whatever addres I read like the i2c-tools command does. > >> I am not sure that it is correct though... > >> > > > >Yes this is expected, as eeproms erase to $FF. You can write to the same > >byte as many times as you want, as long as you only change 1 bits to 0 > >bits. If you must change a 0 bit to a 1 bit, you have to erase back to > $FF > >first. Be aware that erase means erasing the entire page, and not just a > >single byte. This is where things like "wear leveling" come into play, > >because a page can only be erased a limited number of times (it used to be > >around 10,000, but maybe newer devices have extended this). How you > manage > >writes to eeprom can be tricky, best to use them for things that don't > >change very often, such as configuration data. > > > >Jeff > > > > This is incorrect! > The EEPROM I am talking about is a small 8-pin chip specifically used in > IoT > devices to save non-volatile data and these devices are r/w on single > bytes. > > I have just tested it by executing command line commands to write a set of > data > to a specific address and then read it back and verified that it was there. > Then I repeated the process for the same addresses but different data. > Readback again showed only the new data correctly. > > What you are talking about might be certain microcontrollers which > simulate an > EEPROM memory in their flash program memory data. That flash is commonly > erasable only for full pages. Not the case here... > > I am working on using the FreePascal GitHub code and expanding it into a > class I > can use in my RPi4 project. Since I want to generalize it as much as > possible it > takes a few more hours than I thought... >
"Jeffrey A. Wormsley wrote 28 lines
I've been using i2c and SPI eeproms for almost two decades. All of them can read/write a single byte if that's what you want to do, but the write/erase restrictions I noted are there for any parts I've ever used. If you write a byte to $00, you cannot write it to $01 without first erasing the entire block that it is located in (usually 128 to 256 bytes). You can write $FF to $F0, then $F0 to $80, then $80 to $00, as these all only involve turning 1 bits into 0 bits, but if you try to write from a $00 to a $01, then it will not work, at least not on any part I've ever used, as that involves turning a 0 bit into a 1 bit. All of the parts I've seen, if you want to modify a single memory location (if the write involves turning a 0 bit into a 1 bit), they require you to read the block of data into your CPU, modify it there, erase the block, then write the entire block data back into the chip. I've seen some parts that have an on-chip RAM buffer that would let you read the block into this RAM buffer with one command, modify the RAM buffer with a single byte write, erase the block, then write the RAM buffer back to the chip, which is a lot faster than having to do this yourself with your CPU memory, but it still required the block erase and that block erase still counted towards your maximum number of erase cycles. I have not seen a chip that allowed completely random writes at the byte level before short of a static ram with battery backup. If you have an i2c part that doesn't work like this, then I would be very interested in knowing the part number and seeing a link to the data sheet. Such a part would make using these sorts of devices much easier, but to date I have never seen one. Jeff

I just tested by writing four 0x00 bytes in a row, then verifying that the memory read shows these, then writing other data and again verifying that the data is there.

All using the i2c-tools commands:

$ i2ctransfer -y 1  0x00 0x00 0x00 0x00 0x00 0x00
$ i2ctransfer -y 1  0x00 0x00
0x00 0x00 0x00 0x00 0xff 0xff 0xff 0xff
$ i2ctransfer -y 1  0x00 0x00 0x70 0x30 0x55 0x40
$ i2ctransfer -y 1  0x00 0x00
0x70 0x30 0x55 0x40 0xff 0xff 0xff 0xff

The part number is: CAT24C32 by ON Semiconductor (www.onsemi.com) It is a 128 kbit (16 kbyte) part organized as 64 pages of 256 bytes.

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund

On Mon, 03 Apr 2023 20:55:43 +0200, Bo Berglund via fpc-pascal >On Mon, 3 Apr 2023 14:26:17 -0400, "Jeffrey A. Wormsley via fpc-pascal"

Show quoted text 46 lines
wrote: >I've been using i2c and SPI eeproms for almost two decades. All of them >can read/write a single byte if that's what you want to do, but the >write/erase restrictions I noted are there for any parts I've ever used. >If you write a byte to $00, you cannot write it to $01 without first >erasing the entire block that it is located in (usually 128 to 256 bytes). >You can write $FF to $F0, then $F0 to $80, then $80 to $00, as these all >only involve turning 1 bits into 0 bits, but if you try to write from a $00 >to a $01, then it will not work, at least not on any part I've ever used, >as that involves turning a 0 bit into a 1 bit. All of the parts I've >seen, if you want to modify a single memory location (if the write involves >turning a 0 bit into a 1 bit), they require you to read the block of data >into your CPU, modify it there, erase the block, then write the entire >block data back into the chip. I've seen some parts that have an on-chip >RAM buffer that would let you read the block into this RAM buffer with one >command, modify the RAM buffer with a single byte write, erase the block, >then write the RAM buffer back to the chip, which is a lot faster than >having to do this yourself with your CPU memory, but it still required the >block erase and that block erase still counted towards your maximum number >of erase cycles. I have not seen a chip that allowed completely random >writes at the byte level before short of a static ram with battery backup. > >If you have an i2c part that doesn't work like this, then I would be very >interested in knowing the part number and seeing a link to the data sheet. >Such a part would make using these sorts of devices much easier, but to >date I have never seen one. > >Jeff > I just tested by writing four 0x00 bytes in a row, then verifying that the memory read shows these, then writing other data and again verifying that the data is there. All using the i2c-tools commands: $ i2ctransfer -y 1 0x00 0x00 0x00 0x00 0x00 0x00 $ i2ctransfer -y 1 0x00 0x00 0x00 0x00 0x00 0x00 0xff 0xff 0xff 0xff $ i2ctransfer -y 1 0x00 0x00 0x70 0x30 0x55 0x40 $ i2ctransfer -y 1 0x00 0x00 0x70 0x30 0x55 0x40 0xff 0xff 0xff 0xff The part number is: CAT24C32 by ON Semiconductor (www.onsemi.com) It is a 128 kbit (16 kbyte) part organized as 64 pages of 256 bytes.

Sorry copied from the wrong datasheet here, should be CAT24C128

Bo Berglund Developer in Sweden

Reply by email

Dennis Lee Bieber
to Bo Berglund: On Mon, 3 Apr 2023 14:26:17 -0400, "Jeffrey A. Wormsley via fpc-pascal" I just tested by…

On Mon, 03 Apr 2023 20:55:43 +0200, Bo Berglund via fpc-pascal declaimed
the following:

I just tested by writing four 0x00 bytes in a row, then verifying that the memory read shows these, then writing other data and again verifying that the data is there.

Which may only mean the chip has an on-board controller and memory buffer, and the chip is performing the page level erase followed by buffer write.

Reply by email

Bo Berglund
to Jeffrey A. Wormsley: I noticed you put the part number in the original email. Interestingly, the data sheet…
"Jeffrey A. Wormsley wrote 10 lines
I noticed you put the part number in the original email. Interestingly, the data sheet mentions 64 byte pages, and 1,000,000 erase cycles, but does not mention the need to erase a page before writing. So perhaps this part does not have that restriction. If you're making a library specifically for this part, then I guess you don't have to worry about this. If you're making a library for generic eeprom support, I think you might want to consider that not all parts behave this way.

You alerted me to the page size which I have been mis-reading until now it seems like...
This 128 kbit part is apparently built like this:

- 16384 memory bytes
- Organized as 256 64 byte "pages"
- The page write buffer is 64 bytes, i.e. the same size as a memory "page"

Then there is a discusion about the way the write buffer maps onto the memory array, which I didn't understand fully...

[quote]
Page Write
By continuing to load data into the Page Write Buffer after the 1st data byte and before issuing the STOP condition, up to 64 bytes can be written simultaneously during one internal Write cycle (Figure 8). If more data bytes are loaded than locations available to the end of page, then loading will continue from the beginning of page, i.e. the page address is latched and the address count automatically increments to and then wraps-around at the page boundary. Previously loaded data can thus be overwritten by new data. What is eventually written to memory reflects the latest Page Write Buffer contents. Only data loaded within the most recent Page Write sequence will be written to memory.
[/quote]

Maybe it is like this:

The page write buffer is 64 bytes and incoming data are written to the buffer starting at an address (I assume) within that buffer given by the last 6 bits of the address being written in the command.

This to me reads like the same place in the buffer as eventually in the memory *page* when written.
And if one continues to send data to the part after reaching the end of the buffer it writes those to the beginning of the buffer instead (wrap-around, which is bad and will destroy data).

Finally when the transfer is ended (STOP condition) then the buffer somehow is moved onto the main memory.

Here I am unclear on how it works, one possibility would be that there is some bit field or similar on chip that marks bytes in the buffer that have been written by incoming data. Then all other bytes can be filled from the page in main memory by reading those to unwritten locations in the buffer.

Or else maybe simpler: when the write starts and the page is determined the buffer may be filled from a main memory page copy and then overwritten by incoming data...

Now the buffer has a content that is the same as in main memory except for the data that were sent in.
So then the part may erase the page in main memory as you stated and once that is done write the buffer content on top of the page that is now blanked out to 0xff cells.

I cannot think of any other procedure that matches what the datasheet says.. And if it is like this you are right that the cells must be bulk erased before one can write random data there...
But it is handled without the user having to do anything at all...

Now I have to go back into my code and adjust the handling for the write buffer, since I can only send in at most 64 bytes at a time and that only if the write starts at 6 bit address 0 in the buffer. Otherwise less data can be written in each cycle.

I am transfering the data to write to the write procedure as a TBytes array of random size and the code needs to start (in most cases) by sending in a smaller than 64 byte write packet and then fill with full 64 byte pages and then finalize with a short page at the end of the write procedure. Unless everything happens to align on 64 byte address boundaries...

Getting a bit dizzy...
But still the main Freepascal interface into Linux that I got from GitHub works so this is just a case of sequencing the commands correctly! :-)

Thanks for making me read closer into the datasheet!

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Dennis Lee Bieber: On Mon, 03 Apr 2023 20:55:43 +0200, Bo Berglund via fpc-pascal declaimed the following:…
Dennis Lee Bieber wrote 12 lines
On Mon, 03 Apr 2023 20:55:43 +0200, Bo Berglund via fpc-pascal declaimed the following: >I just tested by writing four 0x00 bytes in a row, then verifying that the >memory read shows these, then writing other data and again verifying that the >data is there. > Which may only mean the chip has an on-board controller and memory buffer, and the chip is performing the page level erase followed by buffer write.

Yeah, seems like this is happening as I just wrote in another reply...

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund: On Sun, 2 Apr 2023 23:14:25 +0200, Christo Crause via fpc-pascal Sure, I will probably…
Bo Berglund wrote 24 lines
On Sun, 2 Apr 2023 23:14:25 +0200, Christo Crause via fpc-pascal wrote: >On Sun, Apr 2, 2023 at 9:30?PM Bo Berglund via fpc-pascal < >> wrote: > >> Is there a reason that there is no i2c_write_reg demo in the github >> package? >> > >I haven't gotten round to that, waiting for hardware to test the code with. > >I will try to write one so I can test that as well... >> >Would be nice if you could share that :-) Sure, I will probably continue with this tomorrow (I'm in Europe so it is soon midnight here...). If I get it going I will post it or else I will ask about it here. My hardware is ready and operational, in fact my board also has another EEPROM (same type) sitting on the i2c-0 bus prepared for being used as a HAT EEPROM. Haven't gotten that far on the HAT handling though so it is totally unused.

So now I have created a new class TI2c_eeprom, that uses the i2c.pas file to do the actual i2c handling.
In this class I have tested the read and write functionality and I have the following findings:

1) The read works just fine for what I have done so far.

2) But I have problems with the write functions for multiple data

I use the method:

i2cMaster.WriteBytesToReg(i2caddress: byte; regAddress: uint16; data: PByte;
size: byte): boolean;)

With data specified as follows in the call:

if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, @source[0], NumBytes) then

Here the source argument is a TBytes array of bytes so I supply the address of element 0 as the data argument (expected to be a PByte).

When I step through this in the debugger and get into the method itself I can see that the source argument contains the data I have loaded into it, so the data is there.

When I run that method to the end and it finishes without error, I still cannot see the data in the EEPROM at the given address.

So for testing I changed the call to be:
if not i2cMaster.WriteByteToReg(FEEPROMAddr, startaddress, source[0]) then

This uses the method to write a single byte to the i2c device and it works just fine!

So either there is a problem with the WriteBytesToReg() method or else my use of it...
What comes to mind is the data type PByte, which I have never used before and do not know if it is valid.

I am keeping the data in a dynamic array of bytes (the pascal type TBytes) and send the address of element 0 of that array as the argument to the method. Is this wrong?
If so what should I use instead?
Can I typecast it like this:

if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, PByte(source), NumBytes) then
(Tried it and it does not work)

????

Seems like I am getting close but missing something important...

Bo Berglund Developer in Sweden

Reply by email

Jeffrey A. Wormsley
to Bo Berglund: On Mon, 3 Apr 2023 14:37:48 -0400, "Jeffrey A. Wormsley via fpc-pascal" You alerted me to…
Page Write ...

What this means is that if you start in the middle of a 64 byte block, let's say at byte 60 (0 based) which is on page 0, and you write 4 bytes, then you will write into 60, 61, 62 and 63 as expected. But if you write 6 bytes, you will write into bytes 60, 61, 62, 63 and then bytes 0 and 1 *on page 0*. You will not write into 64 and 65, as those are on the next page, page 1. Also, if you start at byte 0 (page 0) and write 128 bytes, then the first 64 bytes will be overwritten by the second 64 bytes, so when you send the STOP only that last 64 bytes will be written starting at byte 0, not 128 bytes onto page 0 and page 1 as you might have expected.

You can write a generic routine that starts anywhere and finishes anywhere on the chip, as long as you keep track of the 64 byte pages. You may have a partial block at the beginning, zero or more full blocks, and potentially a partial block at the end, depending on your start and end address and the amount of data you want to write. It is usually useful to write such a function, because then your high level code never has to know about the block structure. It makes switching to different chips with different arrangements much easier. You write a .pas file for each specific chip, but the "WriteData" function is the same for all of them, you only have to change the uses clause. OOP makes this even easier, as you create an instance of the proper chip driver and all of the read/write calls are the same (at some cost to code size). Usually, when I do this in C, I create groups of #defines for the various page sizes, memory size, erase sector size, and so on, and use an #ifdef to select the correct set for the chip I'm using, and the code is the same for almost all chips, I don't even need different source files, just a master .h with all of the chips I support. I'm sure this should be easy enough to do in Pascal as well, but I must confess I don't often use Pascal for my embedded work, usually only on the PC side of things.

Jeff

On Mon, Apr 3, 2023 at 4:41 PM Bo Berglund via fpc-pascal <

Show quoted text 125 lines
wrote: On Mon, 3 Apr 2023 14:37:48 -0400, "Jeffrey A. Wormsley via fpc-pascal" wrote: >I noticed you put the part number in the original email. > >Interestingly, the data sheet mentions 64 byte pages, and 1,000,000 erase >cycles, but does not mention the need to erase a page before writing. So >perhaps this part does not have that restriction. > >If you're making a library specifically for this part, then I guess you >don't have to worry about this. If you're making a library for generic >eeprom support, I think you might want to consider that not all parts >behave this way. You alerted me to the page size which I have been mis-reading until now it seems like... This 128 kbit part is apparently built like this: - 16384 memory bytes - Organized as 256 64 byte "pages" - The page write buffer is 64 bytes, i.e. the same size as a memory "page" Then there is a discusion about the way the write buffer maps onto the memory array, which I didn't understand fully... [quote] Page Write By continuing to load data into the Page Write Buffer after the 1st data byte and before issuing the STOP condition, up to 64 bytes can be written simultaneously during one internal Write cycle (Figure 8). If more data bytes are loaded than locations available to the end of page, then loading will continue from the beginning of page, i.e. the page address is latched and the address count automatically increments to and then wraps-around at the page boundary. Previously loaded data can thus be overwritten by new data. What is eventually written to memory reflects the latest Page Write Buffer contents. Only data loaded within the most recent Page Write sequence will be written to memory. [/quote] Maybe it is like this: The page write buffer is 64 bytes and incoming data are written to the buffer starting at an address (I assume) within that buffer given by the last 6 bits of the address being written in the command. This to me reads like the same place in the buffer as eventually in the memory *page* when written. And if one continues to send data to the part after reaching the end of the buffer it writes those to the beginning of the buffer instead (wrap-around, which is bad and will destroy data). Finally when the transfer is ended (STOP condition) then the buffer somehow is moved onto the main memory. Here I am unclear on how it works, one possibility would be that there is some bit field or similar on chip that marks bytes in the buffer that have been written by incoming data. Then all other bytes can be filled from the page in main memory by reading those to unwritten locations in the buffer. Or else maybe simpler: when the write starts and the page is determined the buffer may be filled from a main memory page copy and then overwritten by incoming data... Now the buffer has a content that is the same as in main memory except for the data that were sent in. So then the part may erase the page in main memory as you stated and once that is done write the buffer content on top of the page that is now blanked out to 0xff cells. I cannot think of any other procedure that matches what the datasheet says.. And if it is like this you are right that the cells must be bulk erased before one can write random data there... But it is handled without the user having to do anything at all... Now I have to go back into my code and adjust the handling for the write buffer, since I can only send in at most 64 bytes at a time and that only if the write starts at 6 bit address 0 in the buffer. Otherwise less data can be written in each cycle. I am transfering the data to write to the write procedure as a TBytes array of random size and the code needs to start (in most cases) by sending in a smaller than 64 byte write packet and then fill with full 64 byte pages and then finalize with a short page at the end of the write procedure. Unless everything happens to align on 64 byte address boundaries... Getting a bit dizzy... But still the main Freepascal interface into Linux that I got from GitHub works so this is just a case of sequencing the commands correctly! :-) Thanks for making me read closer into the datasheet! -- Bo Berglund Developer in Sweden _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Christo Crause
to Bo Berglund: On Sun, 02 Apr 2023 23:21:38 +0200, Bo Berglund via fpc-pascal So now I have created a…

See comments below.

On Tue, Apr 4, 2023 at 6:52 PM Bo Berglund via fpc-pascal <

Show quoted text 17 lines
wrote: I use the method: i2cMaster.WriteBytesToReg(i2caddress: byte; regAddress: uint16; data: PByte; size: byte): boolean;) With data specified as follows in the call: if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, @source[0], NumBytes) then Here the source argument is a TBytes array of bytes so I supply the address of element 0 as the data argument (expected to be a PByte).

This is the correct way of passing the starting address of your data buffer. Is your startaddress variable an unsigned 16 bit integer (word/uint16)? If not, type cast this to a uint16 to ensure the compiler calls the correct overloaded method.

Show quoted text 21 lines
When I step through this in the debugger and get into the method itself I can see that the source argument contains the data I have loaded into it, so the data is there. When I run that method to the end and it finishes without error, I still cannot see the data in the EEPROM at the given address. So for testing I changed the call to be: if not i2cMaster.WriteByteToReg(FEEPROMAddr, startaddress, source[0]) then This uses the method to write a single byte to the i2c device and it works just fine! So either there is a problem with the WriteBytesToReg() method or else my use of it...

Note that I haven't tested WriteByteToReg with a device supporting 10 bit register addresses, or in your case 16 bit...

What comes to mind is the data type PByte, which I have never used before and do not know if it is valid.

PByte is a pointer to a byte.

Show quoted text 5 lines
I am keeping the data in a dynamic array of bytes (the pascal type TBytes) and send the address of element 0 of that array as the argument to the method. Is this wrong?

Passing the address of the first element of a TBytes array as you did above is correct.

If so what should I use instead?

Show quoted text 6 lines
Can I typecast it like this: if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, PByte(source), NumBytes) then (Tried it and it does not work)

No, this does not take the address of source, which is waht is required.

???? Seems like I am getting close but missing something important...

I will look at the EEPROM datasheet, in case the details do not agree with the WriteBytesToReg implementation.

Reply by email

Bo Berglund
to Christo Crause
Christo Crause wrote 12 lines
> if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, @source[0], > NumBytes) then > > Here the source argument is a TBytes array of bytes so I supply the > address of > element 0 as the data argument (expected to be a PByte). > This is the correct way of passing the starting address of your data buffer. Is your startaddress variable an unsigned 16 bit integer (word/uint16)? If not, type cast this to a uint16 to ensure the compiler calls the correct overloaded method.

OK, I have tested various similar ways but all fail to work.

Note that I haven't tested WriteByteToReg with a device supporting 10 bit register addresses, or in your case 16 bit...

Actually it is more like 14 bits (16 kbytes)

Passing the address of the first element of a TBytes array as you did above is correct.

OK

I will look at the EEPROM datasheet, in case the details do not agree with the WriteBytesToReg implementation.

I have two cases here, writing a single byte, which works and writing an array of byte which does not work...
Everything else seems to work fine.

So I have been fiddling with this the whole evening and I am not getting it to work the supposed way...

So I changed as a test to use the following code instead: Source is a TBytes container, startaddress and addr are uint16

    j := 0;
    for i := 0 to Length(Source)-1 do
    begin
      addr := startaddress+i;
      if not i2cMaster.WriteByteToReg(FEEPROMAddr, addr, source[i]) then break;
      Inc(j);
      Delay(10);//Delay(1) does not work...
    end;
    Writeln('Wrote ' + IntToStr(j) + 'bytes');

With this in place it works to write a TBytes data to the EEPROM but only 100 bytes/second.

But this does not work (nothing winds up into the EEPROM):

pSource := @source[0];
NumBytes := Length(source);
i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, pSource, NumBytes)

This does not generate any errors but nothing winds up into the EEPROM...

Note: I have not changed anything inside the i2c.pas file...

Bo Berglund Developer in Sweden

Reply by email

Bo Berglund
to Bo Berglund
Bo Berglund wrote 7 lines
On Tue, 4 Apr 2023 23:43:21 +0200, Christo Crause via fpc-pascal wrote: >Is your startaddress variable an unsigned 16 bit integer >(word/uint16)? If not, type cast this to a uint16 to ensure the compiler >calls the correct overloaded method.

Did not answer the type question:
Yes, that has always been a uint16 for the reason you outline: to select the correct overloaded method.

> >Passing the address of the first element of a TBytes array as you did above >is correct.

OK, I thought so. It has been there with some variations all the time.

Note: I have not changed anything inside the i2c.pas file...

This was just to point out that I am not modifying this base file, just using it.

Another observation:

I *can* write a sequence of bytes into the EEPROM succsessfully using an i2c-tools command like this:

i2ctransfer -y 1 0x00 0x38 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f

This writes data starting towards the end of the page and wraps around such that part of the data gets to the start of the page.

It shows:
- It is possible to write the data in one go
- If the data length is past the end of the page it wraps around to the beginning

So if the problem cannot be solved then I could either use the loop with delay or call the i2c-tools command i2ctransfer as a TProcess execution to write multiple bytes in one command.

But I would rather not use the TProcess method at all since it relies on an external tool being present and the argument list can be pretty long and tedious to build...

Bo Berglund Developer in Sweden

Reply by email

Christo Crause
to Bo Berglund: On Wed, 05 Apr 2023 00:33:50 +0200, Bo Berglund via fpc-pascal Did not answer the type…

On Wed, Apr 5, 2023 at 7:55 AM Bo Berglund via fpc-pascal <

Show quoted text 15 lines
wrote: On Wed, 05 Apr 2023 00:33:50 +0200, Bo Berglund via fpc-pascal I *can* write a sequence of bytes into the EEPROM succsessfully using an i2c-tools command like this: i2ctransfer -y 1 0x00 0x38 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f This writes data starting towards the end of the page and wraps around such that part of the data gets to the start of the page.

Looking at the i2ctransmit transaction data:
$ i2ctransfer 1 0x00 0x38 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
WARNING! This program can confuse your I2C bus, cause data loss and worse! I will send the following messages to device file /dev/i2c-1: msg 0: addr 0x50, write, len 18, buf 0x00 0x38 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f

This can be simulated by calling the WriteBytes method, see new example: https://github.com/ccrause/rpiplc_pas/blob/main/native/examples/i2c_write_test.lpr

It shows:

Show quoted text 14 lines
- It is possible to write the data in one go - If the data length is past the end of the page it wraps around to the beginning So if the problem cannot be solved then I could either use the loop with delay or call the i2c-tools command i2ctransfer as a TProcess execution to write multiple bytes in one command. But I would rather not use the TProcess method at all since it relies on an external tool being present and the argument list can be pretty long and tedious to build...

Looking at the datasheet for CAT24C128, Fig. 6 Byte Write Sequence, shows no repeated start condition between register address and data bytes. I suspect that the second i2c message should contain the I2C_M_NOSTART flag, else the address and data parts of the transaction will be separated by a repeated start signal. I have modified the WriteBytesToReg methods in i2c.pas, please feel free to pull from Github and test this again.

Reply by email

Christo Crause
to Jean SUZINEAU: May be you could get the source code of i2ctransfer from i2c-tools and compare it with…

On Wed, Apr 5, 2023 at 11:22 AM Jean SUZINEAU via fpc-pascal <

Show quoted text 5 lines
wrote: May be you could get the source code of i2ctransfer from i2c-tools and compare it with the code in i2c.pas ?

Good idea, that led to the suggestion to try the WriteBytes method which is similar to how i2ctransfer constructed the i2c transaction. Hopefully the change to the WriteBytesToReg methods also fixes the original problem, since it is a little bit more convenient to work with.

Reply by email

Bo Berglund
to Christo Crause: On Wed, Apr 5, 2023 at 7:55 AM Bo Berglund via fpc-pascal < Looking at the i2ctransmit…
Christo Crause wrote 6 lines
Looking at the datasheet for CAT24C128, Fig. 6 Byte Write Sequence, shows no repeated start condition between register address and data bytes. I suspect that the second i2c message should contain the I2C_M_NOSTART flag, else the address and data parts of the transaction will be separated by a repeated start signal. I have modified the WriteBytesToReg methods in i2c.pas, please feel free to pull from Github and test this again.

Thanks, I did so and used it in my code but it still does not write the EEPROM...

Here is the code I use to test (in a command line program), maybe you can see a usage problem?

var
  i2cbus,
  dev_addr,
  reg_addr,
  data_cnt: integer;
  indata: TBytes;
procedure main_12c_write(); //This is the main application procedure
var
  i: integer;
  num: uint16;
  i2cEeprom: TI2c_eeprom;
begin
  processInputs; //Read command line and write to variables
  //Now process the data towards the EEPROM
  i2cEeprom := TI2c_eeprom.Create; //Defaults are set for my EEPROM
  try
    num := i2cEeprom.WriteData(reg_addr, data_cnt, indata);
    Writeln('Wrote ' + IntToStr(num) + ' bytes to address $' +
IntToHex(reg_addr,4) );
  finally
    i2cEeprom.Free;
  end;
function TI2c_eeprom.WriteData(StartAddress, NumBytes: uint16; Source: TBytes):
uint16;
var
  endpos, capacity, addr: uint16;
  i2cMaster: TI2cMaster;
  i2cbus: TI2CBus;
  sourcelen, i, j: integer;
  pSource: PByte;
begin
  Result := 0;
  //Check valid indata:
  //snip....
  //Prepare the handler for action...
  i2cbus := TI2CBus(FI2cBusNo);
  i2cMaster := TI2cMaster.Create;
  try
    if not i2cMaster.Initialize(i2cbus) then
    begin
      writeln('Error opening i2c device: ', FI2cBusNo);
      exit;
    end;
    //Check if data will fit into a single page start at given address:
    if ((StartAddress and $ffc0) = (endpos and  $ffc0)) then //Fits in page
    begin
      //Perform a single write cycle into the buffer
      pSource := @source[0];
      if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, pSource,
NumBytes) then
      begin
        Writeln('Error: Could not write to I2C device!');
        exit;
      end
      else
        Result := NumBytes;
    end
    else  //We need to split data over several commands
    begin
      Writeln('Not yet implmented data split into several writes');
    end;
  finally
    i2cMaster.Free;
  end;
Bo Berglund Developer in Sweden

Reply by email

Christo Crause
to Bo Berglund

On Wed, Apr 5, 2023 at 12:43 PM Bo Berglund via fpc-pascal <

Show quoted text 11 lines
wrote: On Wed, 5 Apr 2023 11:40:09 +0200, Christo Crause via fpc-pascal wrote: >Looking at the datasheet for CAT24C128, Fig. 6 Byte Write Sequence, shows >no repeated start condition between register address and data bytes. I >suspect that the second i2c message should contain the I2C_M_NOSTART flag, >else the address and data parts of the transaction will be separated by a >repeated start signal. I have modified the WriteBytesToReg methods in >i2c.pas, please feel free to pull from Github and test this again.

I tested the i2c code with a PCA9685 IC. It also supports multi-byte writes, although it only has 8 bit register addresses. I realised that my original code used two i2c transactions and that each transaction starts with sending the device address. The multibyte write obviously doesn't work like this, so I changed the WriteBytesToReg methods (in i2c.pas) to combine the register address and data into one buffer, which is then transmitted using the WriteBytes method.

This was tested and confirmed when writing 4 bytes to sequential registers to a PCA9685 IC with the new WriteBytesToReg method ( https://github.com/ccrause/rpiplc_pas/blob/dc568bbfba2cda50f7ba394469480109e652247d/native/pwm_pca9685.pas#L227 ).

Only the WriteBytesToReg for 16 bit register addresses are now untested, although it follows a similar pattern so I have a reasonable expectation that it should work.

If this update still doesn't work, I will need a logic analyser trace of an i2c write transaction that is successful (say using i2ctrace) and one which is unsuccessful using TI2cMaster.WriteBytesToReg. Or I need to find a similar EEPROM to test...

Thanks, I did so and used it in my code but it still does not write the

Show quoted text 75 lines
EEPROM... Here is the code I use to test (in a command line program), maybe you can see a usage problem? var i2cbus, dev_addr, reg_addr, data_cnt: integer; indata: TBytes; procedure main_12c_write(); //This is the main application procedure var i: integer; num: uint16; i2cEeprom: TI2c_eeprom; begin processInputs; //Read command line and write to variables //Now process the data towards the EEPROM i2cEeprom := TI2c_eeprom.Create; //Defaults are set for my EEPROM try num := i2cEeprom.WriteData(reg_addr, data_cnt, indata); Writeln('Wrote ' + IntToStr(num) + ' bytes to address $' + IntToHex(reg_addr,4) ); finally i2cEeprom.Free; end; function TI2c_eeprom.WriteData(StartAddress, NumBytes: uint16; Source: TBytes): uint16; var endpos, capacity, addr: uint16; i2cMaster: TI2cMaster; i2cbus: TI2CBus; sourcelen, i, j: integer; pSource: PByte; begin Result := 0; //Check valid indata: //snip.... //Prepare the handler for action... i2cbus := TI2CBus(FI2cBusNo); i2cMaster := TI2cMaster.Create; try if not i2cMaster.Initialize(i2cbus) then begin writeln('Error opening i2c device: ', FI2cBusNo); exit; end; //Check if data will fit into a single page start at given address: if ((StartAddress and $ffc0) = (endpos and $ffc0)) then //Fits in page begin //Perform a single write cycle into the buffer pSource := @source[0]; if not i2cMaster.WriteBytesToReg(FEEPROMAddr, startaddress, pSource, NumBytes) then begin Writeln('Error: Could not write to I2C device!'); exit; end else Result := NumBytes; end else //We need to split data over several commands begin Writeln('Not yet implmented data split into several writes'); end; finally i2cMaster.Free; end;

The use of WriteBytesToReg in the TI2c_eeprom.WriteData method seems fine, perhaps retest with the updated WriteBytesToReg method.

Reply by email

Bo Berglund
to Christo Crause
Christo Crause wrote
The use of WriteBytesToReg in the TI2c_eeprom.WriteData method seems fine, perhaps retest with the updated WriteBytesToReg method.

I pulled the i2c.pas file from GitHub (actually downloaded the zip since I don't really know how git works (I use SVN myself), earlier today (at 12:01 in fact) and made the tests using that.

But now I saw that there is an even newer version available, so I downloaded that and lo-and-behold, the write does complete and the data are there in the EEPROM!

Thanks for your updates to i2c.pas!

Now I will continue by handling the case of write page overlaps so it will be dealt with in the code!

;-)

Bo Berglund Developer in Sweden

Reply by email