使用Linux通过I2C读写EEPROM

时间:2015-04-28 23:59:04

标签: c linux raspberry-pi i2c eeprom

我尝试通过I2C读取和写入带有Raspberry Pi B +的Atmel 24C256 EEPROM,但是我无法正常工作。

这是我到目前为止的代码:

#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;
}

在行write_to_device(0x01, 0x00, buffer, PAGE_SIZE);中写入设备不会产生任何错误但是当我尝试从设备读取时,我必须根据规格表写一个“虚拟”字节,然后尝试从器件读取但由于某种原因写入虚拟字节会导致错误“输入/输出错误”。我无法弄清楚它是如何工作的。我正在使用两个资源来指导我,Linux I2C-Dev documentation以及来自similar EEPROM device.的示例我有点卡在这里,不知道该尝试什么。非常感谢任何建议或指示!

5 个答案:

答案 0 :(得分:11)

或者,如果您能够为Raspberry Pi编译和安装不同的内核设备树,则可以通过内核at24.c驱动程序访问它。

内核设备树需要指定EEPROM的类型和地址,以及它所连接的I²C总线。我不确定Raspberry Pi,但对于BeagleBone Black EEPROM,它是这样的:

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

对于您的设备,您需要指定compatible = "at,24c256";

确保内核配置指定CONFIG_EEPROM_AT24=y(或=m)。

然后,您应该可以通过/sys/bus/i2c/devices/0-0050/eeprom/sys/bus/i2c/drivers/at24/0-0050/eeprom等用户空间访问EEPROM内存。

答案 1 :(得分:2)

也许这可能有帮助。 http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program因为它显然处理你正在尝试编程的设备,并解释了一些解决24c256的注意事项

答案 2 :(得分:1)

克雷格·麦昆(Craig McQueen)的回答使我走上了正确的轨道,但要自己弄清整个问题并不容易。
这是一个在Raspberry Pi上对我有用的AT24C256设备树覆盖:

LOBBY

将其保存到“ at24c256.dts”,使用以下命令对其进行编译(可能需要安装设备树编译器):

/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>;
      };
    };
  };
};

并将其保存在“ / boot / overlays”中。然后通过添加以下内容来激活叠加层:

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

到“ /boot.config.txt”并重新启动。现在,您应该拥有一个设备文件“ / sys / class / i2c-dev / i2c-1 / device / 1-0050 / eeprom”(如果您的I2C总线号为1),可以像普通文件一样写入。

使用例如:

dtparam=i2c_arm=on    
dtoverlay=at24c256

使用例如:

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

虽然不确定如何访问该设备的权限。将用户添加到i2c组无济于事...

答案 3 :(得分:0)

我的代码:

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;
}

答案 4 :(得分:0)

了解易于管理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;
}
相关问题