簡體   English   中英

使用 Linux 通過 I2C 讀寫 EEPROM

[英]Reading and writing EEPROM via I2C with Linux

我嘗試通過 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 文檔和來自類似 EEPROM 設備的示例 我有點卡在這里,不知道該嘗試什么。 非常感謝任何建議或指示!

或者,如果您能夠為 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 )。

然后您應該能夠從用戶空間訪問 EEPROM 內存,例如/sys/bus/i2c/devices/0-0050/eeprom/sys/bus/i2c/drivers/at24/0-0050/eeprom

也許這可能會有所幫助。 http://www.richud.com/wiki/Rasberry_Pi_I2C_EEPROM_Program因為它顯然可以處理您要編程的設備,並且還解釋了尋址 24c256 的一些注意事項

Craig McQueen 的回答讓我走上了正確的道路,但要自己弄清楚整件事並不容易。
這是一個適用於我在 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>;
      };
    };
  };
};

將其保存到“at24c256.dts”,使用以下命令編譯(可能需要安裝設備樹編譯器):

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

並將其保存在“/boot/overlays”中。 然后通過添加激活覆蓋:

dtparam=i2c_arm=on    
dtoverlay=at24c256

到“/boot/config.txt”並重新啟動。 您現在應該有一個設備文件“/sys/class/i2c-dev/i2c-1/device/1-0050/eeprom”(如果您的 I2C 總線編號為 1),您可以像普通文件一樣寫入該文件。

使用例如寫入它:

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

使用例如讀取它:

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

不過,不確定如何繞過訪問設備的 su 權限。 將用戶添加到 i2c-group 沒有幫助...

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

我的代碼:

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