本小節介紹兩個在linux應用層訪問eeprom的方法,并給出示例代碼方便大家理解。第一個方法是通過sysfs文件系統對eeprom進行訪問,第二個方法是通過eeprom的設備文件進行訪問。這兩個方法分別對應了i2c設備驅動的兩個不同的實現,在后面的小結會詳細的分析。
我們嵌入式系統中的E2PROM 是 24C02.先簡單了解一下這款芯片:AT24C02的存儲容量為2Kb,內容分成32頁,每頁8B,共256B,操作時有兩種尋址方式:芯片尋址和片內子地址尋址。 (1)芯片尋址:AT24C02的芯片地址為1010,其地址控制字格式為 1010A2A1A0R/W。其中A2,A1,A0可編程地址選擇位。A2,A1,A0引腳接高、 低電平后得到確定的三位編碼,與1010形成7位編碼, 即為該器件的地址碼。R/W為芯片讀寫控制位,該位為0,表示芯片進行寫操作。 (2)片內子地址尋址:芯片尋址可對內部256B中的任一個進行讀/寫操作,其尋址范圍為00~FF,共256個尋址單位。
1. 通過sysfs文件系統訪問I2C設備
eeprom的設備驅動在/sys/bus/i2c/devices/0-0050/目錄下把eeprom設備映射為一個二進制節點,文件名為eeprom。對這個eeprom文件的讀寫就是對eeprom進行讀寫。
我們可以先用cat命令來看下eeprom的內容。
[root@FORLINX210]# cat eeprom
�����������X�����������������������������������������������
- 1
- 2
- 1
- 2
發現里面都是亂碼,然后用echo命令把字符串“test”輸入給eeprom文件,然后再cat出來。
[root@FORLINX210]# echo "test" > eeprom
[root@FORLINX210]# cat eeprom
test
�����������X�����������������������������������������������
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
就會發現字符串test已經存在eeprom里面了,我們知道sysfs文件系統斷電后就沒了,也無法對數據進行保存,為了驗證確實把“test”字符串存儲在了eeprom,可以把系統斷電重啟,然后cat eeprom,會發現test還是存在的,證明確實對eeprom進行了寫入操作。
當然,因為eeprom已經映射為一個文件了,我們還可以通過文件I/O寫應用程序對其進行簡單的訪問測試。比如以下程序對特定地址(0x40)寫入特定數據(Hi,this is an eepromtest!),然后再把寫入的數據在此地址上讀出來。
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>int main(void){int fd, size, len, i;char buf[50]= {0};char *bufw="Hi,this is an eepromtest!";//要寫入的數據len=strlen(bufw);//數據長度fd= open("/sys/bus/i2c/devices/0-0050/eeprom",O_RDWR);//打開文件if(fd< 0){printf("####i2c test device open failed####/n");return(-1);}//寫操作lseek(fd,0x40,SEEK_SET); //定位地址,地址是0x40if((size=write(fd,bufw, len))<0)//寫入數據{printf("write error\n");return 1;}printf("writeok\n");//讀操作lseek(fd,0x40, SEEK_SET);//準備讀,首先定位地址,因為前面寫入的時候更新了當前文件偏移量,所以這邊需要重新定位到0x40.if((size=read(fd,buf,len))<0)//讀數據{printf("readerror\n");return 1;}printf("readok\n");for(i=0; i< len; i++)printf("buff[%d]=%x\n",i, buf[i]);//打印數據close(fd);return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
2. 通過devfs訪問I2C設備
linux的i2c驅動會針對每個i2c適配器在/dev/目錄下生成一個主設備號為89的設備文件,簡單的來說,對于本例的eeprom驅動,/dev/i2c/0就是它的設備文件,因此接下來的eeprom的訪問就變為了對此設備文件的訪問。
我們需要用到兩個結構體i2c_msg和i2c_rdwr_ioctl_data。
struct i2c_msg { //i2c消息結構體,每個i2c消息對應一個結構體__u16 addr; /* 從設備地址,此處就是eeprom地址,即0x50 */__u16 flags; /* 一些標志,比如i2c讀等*/__u16 len; /* i2c消息的長度 */__u8 *buf; /* 指向i2c消息中的數據 */};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
struct i2c_rdwr_ioctl_data {struct i2c_msg __user *msgs; /* 指向一個i2c消息 */__u32 nmsgs; /* i2c消息的數量 */};
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
對一個eeprom上的特定地址(0x10)寫入特定數據(0x58)并在從此地址讀出寫入數據的示例程序如下所示。
#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>int main()
{int fd,ret;struct i2c_rdwr_ioctl_data e2prom_data;fd=open("/dev/i2c/0",O_RDWR);//打開eeprom設備文件結點if(fd<0){perror("open error");}e2prom_data.nmsgs=2; e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));//分配空間if(!e2prom_data.msgs){perror("malloc error");exit(1);}ioctl(fd,I2C_TIMEOUT,1);/*超時時間*/ioctl(fd,I2C_RETRIES,2);/*重復次數*//*寫eeprom*/e2prom_data.nmsgs=1;//由前面eeprom讀寫分析可知,寫eeprom需要一條消息(e2prom_data.msgs[0]).len=2; //此消息的長度為2個字節,第一個字節是要寫入數據的地址,第二個字節是要寫入的數據(e2prom_data.msgs[0]).addr=0x50;//e2prom 設備地址(e2prom_data.msgs[0]).flags=0; //寫(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);(e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 寫入目標的地址(e2prom_data.msgs[0]).buf[1]=0x58;//寫入的數據ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通過ioctl進行實際寫入操作,后面會詳細分析if(ret<0){perror("ioctl error1");}sleep(1);/*讀eeprom*/e2prom_data.nmsgs=2;//讀eeprom需要兩條消息(e2prom_data.msgs[0]).len=1; //第一條消息實際是寫eeprom,需要告訴eeprom需要讀數據的地址,因此長度為1個字節(e2prom_data.msgs[0]).addr=0x50; // e2prom 設備地址(e2prom_data.msgs[0]).flags=0;//先是寫(e2prom_data.msgs[0]).buf[0]=0x10;//e2prom上需要讀的數據的地址(e2prom_data.msgs[1]).len=1;//第二條消息才是讀eeprom,(e2prom_data.msgs[1]).addr=0x50;// e2prom 設備地址 (e2prom_data.msgs[1]).flags=I2C_M_RD;//然后是讀(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。(e2prom_data.msgs[1]).buf[0]=0;//初始化讀緩沖,讀到的數據放到此緩沖區ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通過ioctl進行實際的讀操作if(ret<0){perror("ioctl error2");}printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);/***打印讀出的值,沒錯的話,就應該是前面寫的0x58了***/close(fd);return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
3. 總結
本小節介紹了兩種在linux應用層訪問eeprom的方法,并且給出了示例程序,通過sysfs文件系統訪問eeprom操作簡單,無需了解eeprom的硬件特性以及訪問時序,而通過devfs訪問eeprom的方法則需要了解eeprom的讀寫時序。
后面分析后會發現,第一種通過sysfs文件系統的二進制結點訪問eeprom的方法是由eeprom的設備驅動實現的,是一種專有的方法;而第二種通過devfs訪問eeprom的方法是linux i2c提供的一種通用的方法,訪問設備的能力有限。