[英]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.