简体   繁体   English

使用 Linux 通过 I2C 读写 EEPROM

[英]Reading and writing EEPROM via I2C with Linux

I trying to read and write an Atmel 24C256 EEPROM with a Raspberry Pi B+ over I2C, but I'm having trouble getting it all to work right.我尝试通过 I2C 使用 Raspberry Pi B+ 读取和写入Atmel 24C256 EEPROM ,但无法正常工作。

Here is the code I have so far:这是我到目前为止的代码:

#include <stdio.h>
#include <stdlib.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <linux/i2c.h>

#define DEVICE_PATH "/dev/i2c-1"

#define PAGE_SIZE 64

#define DEVICE_ADDR 0x50 // 0b1010xxxx


int file_desc;
char buffer[PAGE_SIZE + 2]; // 64 bytes + 2 for the address

void teardownI2C()
{
    int result = close(file_desc);
}

void setupI2C()
{
    file_desc = open(DEVICE_PATH, O_RDWR);
    if(file_desc < 0)
    {
    printf("%s\n", strerror(errno));
    exit(1);
    }
    if(ioctl(file_desc, I2C_SLAVE, DEVICE_ADDR) < 0)
    {
    printf("%s\n", strerror(errno));
    teardownI2C();
    exit(1);

    }
}

int write_to_device(char addr_hi, char addr_lo, char * buf, int len)
{
     struct i2c_rdwr_ioctl_data msg_rdwr;
     struct i2c_msg i2cmsg;
     char my_buf[PAGE_SIZE + 2];
     if(len > PAGE_SIZE + 2)
     {
     printf("Can't write more than %d bytes at a time.\n", PAGE_SIZE);
     return -1;
     }
     int i;
     my_buf[0] = addr_hi;
     my_buf[1] = addr_lo;

     for(i= 0; i < len; i++)
     {
     my_buf[2+i] = buf[i];
     }
     msg_rdwr.msgs = &i2cmsg;
     msg_rdwr.nmsgs = 1;
     i2cmsg.addr  = DEVICE_ADDR;
     i2cmsg.flags = 0;
     i2cmsg.len   = 2+len;
     i2cmsg.buf   = my_buf;

    if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
    {
    printf("write_to_device(): %s\n", strerror(errno));
    return -1;
    }

    return 0;

}

int read_from_device(char addr_hi, char addr_lo, char * buf, int len)
{
    struct i2c_rdwr_ioctl_data msg_rdwr;
    struct i2c_msg             i2cmsg;



    if(write_to_device(addr_hi, addr_lo ,NULL,0)<0)
    {
    printf("read_from_device(): address reset did not work\n");
    return -1;
    }

    msg_rdwr.msgs = &i2cmsg;
    msg_rdwr.nmsgs = 1;

    i2cmsg.addr  = DEVICE_ADDR;
    i2cmsg.flags = I2C_M_RD;
    i2cmsg.len   = len;
    i2cmsg.buf   = buf;

    if(ioctl(file_desc,I2C_RDWR,&msg_rdwr)<0)
    {
    printf("read_from_device(): %s\n", strerror(errno));
    return -1;
    }


    return 0;
}

void fill_buffer(char *buf)
{
    int i = 0;
    while(i < PAGE_SIZE && *buf)
    {
    buffer[i+2] = *buf++;
    }
    while(i++ < PAGE_SIZE-1)
    {
    buffer[i+2] = '*'; // fill the buffer with something
    }
}


int main()
{

    setupI2C(); //setup

    fill_buffer("Here are some words.");
    write_to_device(0x01, 0x00, buffer, PAGE_SIZE);
    char newbuf[PAGE_SIZE];

    if(read_from_device(0x01, 0x00, newbuf, PAGE_SIZE)>0)
    {
    printf("%s\n", newbuf);
    }


    teardownI2C(); //cleanup
    return EXIT_SUCCESS;
}

Writing to the device like in the line write_to_device(0x01, 0x00, buffer, PAGE_SIZE);写入设备,如write_to_device(0x01, 0x00, buffer, PAGE_SIZE); doesn't generate any errors but when I try to read from the device, I have to write a "dummy" byte according to the spec sheet and then try to read from the device but for some reason writing the dummy byte results in an error "Input/output error".不会产生任何错误,但是当我尝试从设备读取时,我必须根据规格表写入一个“虚拟”字节,然后尝试从设备读取但由于某种原因写入虚拟字节会导致错误“输入/输出错误”。 I can't figure out how this works.我无法弄清楚这是如何工作的。 I am using two resources to guide me, the Linux I2C-Dev documentation and an example from a similar EEPROM device.我使用两个资源来指导我, Linux I2C-Dev 文档和来自类似 EEPROM 设备的示例 I'm sort of stuck here and don't know what to try.我有点卡在这里,不知道该尝试什么。 Any suggestions or pointers are greatly appreciated!非常感谢任何建议或指示!

Alternatively, you could access it via the kernel at24.c driver, if you're able to compile and install a different kernel device tree for your Raspberry Pi.或者,如果您能够为 Raspberry Pi 编译和安装不同的内核设备树,则可以通过内核at24.c驱动程序访问它。

The kernel device tree needs to specify the EEPROM's type and address, and which I²C bus it's connected to.内核设备树需要指定 EEPROM 的类型和地址,以及它连接到哪个 I²C 总线。 I'm not sure about Raspberry Pi, but for the BeagleBone Black EEPROM it goes like this:我不确定 Raspberry Pi,但对于 BeagleBone Black EEPROM,它是这样的:

&i2c0 {
    eeprom: eeprom@50 {
        compatible = "at,24c32";
        reg = <0x50>;
    };
};

For your device you'd specify compatible = "at,24c256";对于您的设备,您需要指定compatible = "at,24c256";

Ensure the kernel config specifies CONFIG_EEPROM_AT24=y (or =m ).确保内核配置指定CONFIG_EEPROM_AT24=y (或=m )。

Then you should be able to access the EEPROM memory from userspace at something like /sys/bus/i2c/devices/0-0050/eeprom or /sys/bus/i2c/drivers/at24/0-0050/eeprom .然后您应该能够从用户空间访问 EEPROM 内存,例如/sys/bus/i2c/devices/0-0050/eeprom/sys/bus/i2c/drivers/at24/0-0050/eeprom

maybe this here might help.也许这可能会有所帮助。 http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program since it handles apparently the device you are trying to program and also explains some caveats of addressing 24c256 http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program因为它显然可以处理您要编程的设备,并且还解释了寻址 24c256 的一些注意事项

Craig McQueen's answer got me on the right track, but it is not easy to figure the whole thing out on your own. Craig McQueen 的回答让我走上了正确的道路,但要自己弄清楚整件事并不容易。
Here is a AT24C256 device tree overlay that works for me on the Raspberry Pi:这是一个适用于我在 Raspberry Pi 上的 AT24C256 设备树覆盖:

/dts-v1/;
/plugin/;
/ {
  fragment@0 {
    target = <&i2c1>;
    overlay {
      pinctrl-names = "default";
      pinctrl-0 = <&i2c1_pins>;
      clock-frequency = <100000>;
      status = "okay";
      at24@50 {
        compatible = "atmel,24c256","at24";
        #address-cells = <1>;
        #size-cells = <0>;
        reg = <0x50>;
        pagesize = <64>;
        size = <32768>;
        address-width = <16>;
      };
    };
  };
};

Save it to "at24c256.dts", compile (might need to install the device tree compiler) it using:将其保存到“at24c256.dts”,使用以下命令编译(可能需要安装设备树编译器):

dtc -O dtb -o at24c256.dtbo -b 0 -@ at24c256.dts

and save it in "/boot/overlays".并将其保存在“/boot/overlays”中。 Then activate the overlay by adding:然后通过添加激活覆盖:

dtparam=i2c_arm=on    
dtoverlay=at24c256

to "/boot/config.txt" and reboot.到“/boot/config.txt”并重新启动。 You should now have a device file "/sys/class/i2c-dev/i2c-1/device/1-0050/eeprom" (if your I2C bus number is 1) which you can write to like a normal file.您现在应该有一个设备文件“/sys/class/i2c-dev/i2c-1/device/1-0050/eeprom”(如果您的 I2C 总线编号为 1),您可以像普通文件一样写入该文件。

Write to it using eg:使用例如写入它:

echo 'Hello World' | sudo tee /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom

Read from it using eg:使用例如读取它:

sudo more /sys/class/i2c-dev/i2c-1/device/1-0050/eeprom

Not sure how you can get around the su-rights for accessing the device though.不过,不确定如何绕过访问设备的 su 权限。 Adding the user to the i2c-group does not help...将用户添加到 i2c-group 没有帮助...

Small and simple program to understand the easy management of an eeprom了解eeprom轻松管理的小而简单的程序

/*
    Simple program to write / read the eeprom AT24C32.
    Developed and tested on the Raspberry pi3B jessie

    To create the executable use the following command:

        gcc -Wall -o thisprogram.exe thisprogram.c
*/

#include <stdio.h>
#include <sys/ioctl.h> // ioctl
#include <fcntl.h>     // open
#include <unistd.h>    // read/write usleep
#include <time.h>
#include <netinet/in.h> // htons
#include <linux/i2c-dev.h>

#pragma pack(1)

#define PAGESIZE 32
#define NPAGES  128
#define NBYTES (NPAGES*PAGESIZE)

#define ADDRESS 0x57  //  AT24C32's address on I2C bus 

typedef struct {
    ushort AW;
    char  buf[PAGESIZE+2];
}WRITE;

static WRITE AT = {0};

int main() {
  int fd;
  char bufIN[180] = {0};
  time_t clock=time(NULL);

  snprintf(AT.buf, PAGESIZE+1, "%s: my first attempt to write", ctime(&clock)); //  the buffer to write, cut to 32 bytes

  if ((fd = open("/dev/i2c-1", O_RDWR)) < 0) {  printf("Couldn't open device! %d\n", fd); return 1; }

  if (ioctl(fd, I2C_SLAVE, ADDRESS) < 0)     { printf("Couldn't find device on address!\n"); return 1; }

  AT.AW = htons(32);    //  I will write to start from byte 0 of page 1 ( 32nd byte of eeprom )

  if (write(fd, &AT, PAGESIZE+2) != (PAGESIZE+2)) { perror("Write error !");    return 1; }
  while (1) { char ap[4];  if (read(fd,&ap,1) != 1) usleep(500); else break; } //   wait on write's end 

  if (write(fd, &AT, 2) != 2) {  perror("Error in sending the reading address");    return 1;  }

  if (read(fd,bufIN,PAGESIZE) != PAGESIZE) { perror("reading error\n"); return 1;}
  printf ("\n%s\n", bufIN);

  close(fd);
  return 0;
}

My code:我的代码:

enter code here

__s32 write_eeprom(__s32 fd,__u32 offset,__u32 len,__u8 *buf)
{
    __s32 ret;
    struct i2c_rdwr_ioctl_data msg_set;
    struct i2c_msg iomsgs;
    __u32 sended, sending;
    __u8 temp[ONE_PAGE + 1];

    if((offset + len) > BYTES_MAX || len == 0)
    {
        printf("write too long than BYTES_MAX\n");
        return -1;
    }
    sended = 0;
    iomsgs.addr = DEVICE_ADDR;
    iomsgs.flags = 0;   //write
    iomsgs.buf = temp;
    msg_set.msgs = &iomsgs;
    msg_set.nmsgs = 1;
    while(len > sended)
    {
        if(len - sended > ONE_PAGE)
            sending = ONE_PAGE;
        else
            sending = len - sended;
        iomsgs.len = sending + 1;
        temp[0] = offset + sended;
        memcpy(&temp[1], buf + sended, sending);
        //printf("sending:%d sended:%d len:%d offset:%d \n", sending, sended, len, offset);
        ret = ioctl(fd, I2C_RDWR, (unsigned long)&msg_set);
        if(ret < 0)
        {
            printf("Error dring I2C_RDWR ioctl with error code: %d\n", ret);
            return ret;
        }
        sended += sending;
        usleep(5000);
    }

    return sended;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM