Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Driver for M24C01 EEPROM #20202

Closed
leocordier opened this issue Dec 20, 2023 · 9 comments
Closed

Driver for M24C01 EEPROM #20202

leocordier opened this issue Dec 20, 2023 · 9 comments

Comments

@leocordier
Copy link

Description

I would like to use the external EEPROM i have on my board, with basics functions to write and read data from it.
The component is a 1Mbit EEPROM from ST Microelectronics. It use the i2c protocol for the data transfer.

Useful links

Official component page: https://www.st.com/en/memories/m24c01-r.html
Datasheet : https://www.st.com/resource/en/datasheet/m24c01-r.pdf

@maribu
Copy link
Member

maribu commented Feb 15, 2024

I wonder if the existing at24cxx driver would just work here. I was told that most EEPROMs are basically drop-in replacements for those.

@fabian18 might just need only a quick look into the datasheet to see if those are compatible.

@crasbe
Copy link
Contributor

crasbe commented Apr 12, 2024

I'm not fabian18, but a quick comparison of both datasheets did not yield significant potential issues. The only major difference is that the M24C01 does not support Fast Mode+ with 1MHz clock.

This is the datasheet I compared it against: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8871F-SEEPROM-AT24C01D-02D-Datasheet.pdf
It even feels like the datasheets were inspired from each other, since the order is exactly the same and the wording often similar :D

We did not have any M24C01 in stock, so I ordered some to test it out. The results might take some time though.

@crasbe
Copy link
Contributor

crasbe commented Apr 15, 2024

Okay, I actually did find M24C01 EEPROMs. I put them in my backpack weeks ago because I wanted to work on this issue 🤣

@maribu as you already assumed, the M24C01 works as it should as a drop-in replacement for the AT24C01. There is a difference in page size. The AT24C01 has a page size of 8 bytes and the M24C01 has a page size of 16 bytes. Partial page writes are allowed though, so from the user perspective, nothing changes.

On this note, the AT24CXXX library is already very universal and in the https://github.com/RIOT-OS/RIOT/blob/master/drivers/at24cxxx/include/at24cxxx_defines.h file, a lot of defines for different I2C EEPROMs already exist.
I think it would make sense to extend this library and perhaps add a note that even though it is called AT24CXXX library, it works for all (most?) I²C EEPROMs.
Furthermore I think it might make sense to add an example for the I2C EEPROM or at least extend the documentation a little bit. Currently there is not much of a hint that many configurations already exist.
Should this be discussed in this issue or in a separate issue?

@leocordier As stated above, for the time being you can just use the settings of the AT24C01A and everything should work fine. I'll attach my test code.

@crasbe
Copy link
Contributor

crasbe commented Apr 15, 2024

The code was tested with my NRF52840 Development Kit and an ST M24C01WP I2C EEPROM in DIP-8. Default Address, /WC line not connected. Just VCC, GND, SDA, SCL.

main.c

#include <stdio.h>
#include "at24cxxx.h"

int main(void)
{

    at24cxxx_t eeprom_dev;

    const at24cxxx_params_t eeprom_params = {
        .i2c = 0,
        .pin_wp = 2,
        .eeprom_size = 128,
        .dev_addr = 0x50,
        .page_size = 16,
        .max_polls = 8
    };


    int res = at24cxxx_init(&eeprom_dev, &eeprom_params);


    if(res != AT24CXXX_OK) {
        puts("Error while initializing the I2C EEPROM.");
    } else {
        puts("I2C EEPROM initialized successfully.");
    }

    char data[eeprom_dev.params.eeprom_size];

    at24cxxx_read(&eeprom_dev, 0, &data, eeprom_dev.params.eeprom_size);

    for(uint8_t i = 0; i < eeprom_dev.params.eeprom_size; i++) {
        printf("%02X ", data[i]);
    }
    printf("\n");

    fflush(stdout);


    while(1);

    return 0;
}

Makefile

APPLICATION = EEPROMTest
PROJECT_BASE ?= $(CURDIR)/../..
RIOTBASE ?= $(PROJECT_BASE)/RIOT
 
# Optionally, provide paths to where external boards and/or modules
# reside, so that they can be included in the app
EXTERNAL_MODULE_DIRS += $(PROJECT_BASE)/modules
EXTERNAL_BOARD_DIRS += $(PROJECT_BASE)/boards
 
BOARD ?= nrf52840dk

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

# Modules to include:
USEMODULE += at24cxxx

include $(RIOTBASE)/Makefile.include

Edit: Updated the code (double occurance of at24cxxx_t dev;).

@maribu
Copy link
Member

maribu commented Apr 15, 2024

@crasbe: Thanks for testing this ❤️

What we could do now is adding the parameters from the datasheet, similar to:

/**
* @name AT24MAC402/602 constants
* @{
*/
/**
* @brief 256 byte memory
*/
#define AT24MAC_EEPROM_SIZE (256U)
/**
* @brief 16 pages of 16 bytes each
*/
#define AT24MAC_PAGE_SIZE (16U)
/**
* @brief Delay to complete write operation
*/
#define AT24MAC_PAGE_WRITE_DELAY_US (5000U)
/**
* @brief Number of poll attempts
*/
#define AT24MAC_MAX_POLLS (1 + (AT24MAC_PAGE_WRITE_DELAY_US \
/ AT24CXXX_POLL_DELAY_US))
/** @} */

and select those by default if an m24c01 pseudo module is in used, similar to:

#elif IS_USED(MODULE_AT24MAC)
#define AT24CXXX_EEPROM_SIZE (AT24MAC_EEPROM_SIZE)
#define AT24CXXX_PAGE_SIZE (AT24MAC_PAGE_SIZE)
#define AT24CXXX_MAX_POLLS (AT24MAC_MAX_POLLS)

let the m24c01 pseudo module depend on at24cxx in:

ifneq (,$(filter at24c%,$(USEMODULE)))
USEMODULE += at24cxxx
endif

and finally, add m24c01 as pseudo modules to makefiles/pseudomodules.inc.mk.

With all that in place, one could just use USEMODULE += m24c01 and the at24cxx driver would be initialized with the correct parameters for the M24C01 (of course, only if a single I2C EEPROM chip is in use. With multiple connected, one would have to provide the parameters as array by hand).

Would you mind to open a PR to do that, so that you also get the credit in our git history, since you did the testing and digging through the data sheet? Otherwise I could also do that.

@crasbe
Copy link
Contributor

crasbe commented Apr 15, 2024

I'll open a PR. That's the most sensible option anyways since I have the hardware to test it here on my desk :)
The PR will have another commit for the updated documentation to make it clear(er) how universal the driver actually is already.

There is actually code to support the MTD subsystem, but I'm not sure if my small 128 byte EEPROM (it's actually 1KBit instead of 1MBit as stated above) is enough for a LittleFS filesystem 🤣

PXL_20240415_114645806

@crasbe
Copy link
Contributor

crasbe commented Apr 16, 2024

@maribu I think the drivers/at24cxxx/Makefile.include would be a better place to define the pseudomodule for the M24C01 (or rather M24C%), considering the at24c% pseudomodules are not defined there either. Then we avoid "spamming" the global pseudomodules Makefile.
I'll create the PR shortly.

Something else I noticed:
I noticed that the AT24CXXX driver is categorized under "Miscellaneous Drivers" instead of "Storage Drivers".
It was moved (along other things) from the root level/uncategorized to the Misc. category in 2020 in this PR: #13491

However this issue is probably not the right place to discuss this, even though I'm not sure where a better place would be...

@crasbe
Copy link
Contributor

crasbe commented Apr 16, 2024

This is my updated example/test code (now running on a nRF52832DK instead of the nRF52840DK, because I needed the latter for something else in the meantime.)

The makefile and code demonstrate that the m24c01 pseudomodule is working. It utilizes the AT24CXXX_PARAMS macro, which is based on the defines derived from the pseudomodule definition.

#include <stdio.h>
#include "at24cxxx.h"
#include "at24cxxx_params.h"

int main(void)
{
    at24cxxx_t eeprom_dev;
    at24cxxx_params_t eepronm_params = AT24CXXX_PARAMS;

    int res = at24cxxx_init(&eeprom_dev, &eepronm_params);

    if(res != AT24CXXX_OK) {
        puts("Error while initializing the I2C EEPROM.");
    } else {
        puts("I2C EEPROM initialized successfully.");
    }

    char data[eeprom_dev.params.eeprom_size];

    at24cxxx_read(&eeprom_dev, 0, &data, eeprom_dev.params.eeprom_size);

    for(uint8_t i = 0; i < eeprom_dev.params.eeprom_size; i++) {
        printf("%02X ", data[i]);
        fflush(stdout);
    }
    printf("\n");

    fflush(stdout);

    while(1);
    return 0;
}
APPLICATION = EEPROMTest2
PROJECT_BASE ?= $(CURDIR)/../..
RIOTBASE ?= $(PROJECT_BASE)/RIOT
 
# Optionally, provide paths to where external boards and/or modules
# reside, so that they can be included in the app
EXTERNAL_MODULE_DIRS += $(PROJECT_BASE)/modules
EXTERNAL_BOARD_DIRS += $(PROJECT_BASE)/boards
 
BOARD ?= nrf52dk

# Comment this out to disable code in RIOT that does safety checking
# which is not needed in a production environment but helps in the
# development process:
DEVELHELP ?= 1

# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

# Modules to include:
USEMODULE += m24c01

include $(RIOTBASE)/Makefile.include

This is the expected output on the serial terminal from this test program (depending on the content of the EEPROM of course...):

chris@ThinkPias:~/test-riot/apps/EEPROMTest2$ make term
/home/chris/test-riot/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "115200"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2024-04-16 16:11:26,889 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2024-04-16 16:11:31,452 # main(): This is RIOT! (Version: 2024.07-devel-20-g61148-HEAD)
2024-04-16 16:11:31,454 # I2C EEPROM initialized successfully.
2024-04-16 16:11:31,501 # 20 1E CE 1F 16 20 DE 20 2E 21 00 00 00 00 00 00 64 1B 3E 1F 3E 1F 3E 1F 3E 1F 00 00 00 00 00 00 00 00 40 02 B1 03 2F 04 40 02 00 00 00 00 00 00 00 00 AE 00 C6 00 AE 00 C8 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 08 03 02 BF D7 09 00 00 00 00 00 00 20 20 42 45 43 4B 45 52 41 57 47 37 34 39 43 20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

@crasbe
Copy link
Contributor

crasbe commented Apr 17, 2024

@maribu You can close this issue now :)

@maribu maribu closed this as completed Apr 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants