EEPROM是Electrically Erasable Programmable Read-Only Memory的简写,可以翻译为电可擦可编程只读存储器,是一种掉电后数据不丢失的存储芯片。专门用于需要断电保存数据的情况下。据说有的eeprom设备可以在断电的情况下保存数据100年左右。EEPROM可以在电脑上或专用设备上擦除已有信息,重新编程。很遗憾我们的 开发板直接将写保护拉高,导致我们无法写,只能读。
CAT24C256w是我们开发板上使用的eeprom芯片,是安森美 半导体((ON Semiconductor)的产品,具有以下特点:256kbit串行电可擦的可编程只读存储器,8引脚双排直插式封装,具有结构紧凑、存储容量大等特点,可以在2线总线上并接4片该IC,特别适用于具有高容量数据储存要求的数据采集系统。AT24C256采用SOP-8封装。i2c编程接口,每页64字节,共512页,256kb的存储空间。
关于读写的i2c时序暂时不说,等后面可以测试完写操作后在一起总结。
下面首先来看看测试情况。
第一种情况,在uboot中的测试。
board/ ti/am43xx文件夹下board.c中的get_header函数为我们提供了可以读写cat24c256器件的例程,代码逻辑非常简洁,看看函数名字估计就可以猜到功能就不一一介绍,主要代码如下:
- int get_header(myir_header_t *header)
- {
- u32 i2c_bus_old = i2c_get_bus_num();
- printf("get_header i2c_bus_old=0x%xn", i2c_bus_old);
- if (!header)
- return -1;
- /* Set current i2c bus, the eeprom was mounted to i2c1 */
- if (i2c_set_bus_num(0)) {
- printf("Can not set the current i2c bus to 1n");
- goto err;
- }
- printf("get_header i2c_probe CONFIG_SYS_I2C_SLAVE=0x%x LCD_HEADER_I2C_ADDR=0x%xn", CONFIG_SYS_I2C_SLAVE, LCD_HEADER_I2C_ADDR);
- if (i2c_probe(LCD_HEADER_I2C_ADDR)) {
- printf("Not found the LCD header ICn");
- goto err;
- }
- printf("get_header i2c_read LCD_HEADER_I2C_ADDR=0x%x 0,2 header sizeof(header)n", LCD_HEADER_I2C_ADDR);
- if (i2c_read(LCD_HEADER_I2C_ADDR,
- 0,
- 2,
- (unsigned char *)header,
- sizeof(myir_header_t))) {
- printf("Failed to read lcd headern");
- goto err;
- }
- printf("header->page1=%s, header->page2=%sn", header->page1, header->page2);
- printf("header->header=0x%x header->type=%s, header->subtype=%s, header->revision=%s, header->rchksum=0x%xn", header->header, header->type, header->subtype, header->revision, header->rchksum);
- if (check_header(header))
- goto err;
-
- i2c_set_bus_num(i2c_bus_old);
- return 0;
-
- err:
- /* Restore current i2c bus to default */
- i2c_set_bus_num(i2c_bus_old);
- return -1;
- }
复制代码
注意:1.cat24c256器件在i2c总线0上,而不是原来代码中的1上;
2.cat24c256器件i2c地址为0x50,而不是原来的0x51;
3.为了方便我直接在header结构体中添加了两个64字节的字符数组。
下面是一些运行的log信息
- U-Boot 2013.10 (Nov 04 2016 - 09:10:36)
- I2C: ready
- DRAM: 512 MiB
- lcd_bl_init
- MMC: OMAP SD/MMC: 0, OMAP SD/MMC: 1
- SF: Detected S25FL128S_64K with page size 256 Bytes, erase size 64 KiB, total 16 MiB, mapped at 30000000
- Init vbus0: 500mA@5V, OFF
- Init vbus1: 500mA@5V, ON
- get_header i2c_bus_old=0x1
- get_header i2c_probe CONFIG_SYS_I2C_SLAVE=0x1 LCD_HEADER_I2C_ADDR=0x50
- get_header i2c_read LCD_HEADER_I2C_ADDR=0x50 0,2 header sizeof(header)
- header->page1=MYiR EEPROM SECOND TEST STR▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒, header->page2=▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
- header->header=0xffff header->type=▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒, header->subtype=▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒, header->revision=▒▒▒▒▒▒▒▒, header->rchksum=0xff
- MYIR_HEADER: Invalid header: 0x9FF8C173
- Net: cpsw
- Hit any key to stop autoboot: 0
- U-Boot#
复制代码
还有一中情况,可以使用uboot中cmd命令读eeprom。测试命令和运行结果如下:
- U-Boot# eeprom read 50 0 0 32
- EEPROM @0x50 read: addr 00000000 off 0000 count 50 ... do_eeprom pheader->page1=MYiR EEPROM SECOND TEST STR▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒, pheader->page2=▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
- 0x4d0x590x690x520x200x450x450x500x520x4f0x4d0x200x530x450x430x4f0x4e0x440x200x540x450x530x540x200x530x540x520xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff0xff
- done
复制代码
不过原来的代码有点问题,稍微修改一下即可。开始我对“▒”字符很不理解,现在就把它理解为0xff即可。
- int do_eeprom ( cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])
- {
- myir_header_t mheader;
- myir_header_t *pheader;
- pheader = &mheader;
- int i = 0;
- memset(&mheader, '\0', sizeof(myir_header_t));
- //省略1
- #if 1
- rcode = eeprom_read (dev_addr, off, (unsigned char *)pheader, sizeof(myir_header_t));
- #else
- rcode = eeprom_read (dev_addr, off, (uchar *) addr, cnt);
- #endif
- printf("do_eeprom pheader->page1=%s, pheader->page2=%sn", pheader->page1, pheader->page2);
- for(i = 0; i < 64; i ++)
- {
- printf("0x%x", *((pheader->page1) +i));
- } puts ("n");
- //省略2
- }
复制代码
uboot可以理解为一个小号的linux kernel,还可以做很多事情,只是这样需要反复拷贝编译文件到tf卡,我们还是专心使用tftp和nfs学习kernel的相关知识吧。
kernel对i2c的支持非常到位,关于i2c子系统的框架就不在介绍,首先看看i2c-0的设备情况,如下图:
查看0-0050设备节点的name,如下:
root@m437x-evm:/sys/class/i2c-dev/i2c-0/device/0-0050# cat name
24c256
与dts文件myir_ricoboard.dts里面的内容对应起来。
&i2c0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins &eeprom_wp_pin>;
ov2659@30 {
compatible = "ti,ov2659";
reg = <0x30>;
port {
ov2659_1: endpoint {
remote-endpoint = <&vpfe0_ep>;
mclk-frequency = <12000000>;
};
};
};
cat24c256@50 {
compatible = "24c256";
reg = <0x50>;
pagesize = <64>;
};
};
我们可以访问/dev/i2c-0这个设备节点,使用ioctl方式读取eeprom里面的内容,应用程序代码如下。
- #include
- #include
- #include
- #include
- #include
- #include
- #define CHIP_ADDR 0x50 //24CXX
- #define PAGE_SIZE 64 //根据自己的数据手册
- #define I2C_DEV "/dev/i2c-0"
- static int read_eeprom(int fd,char buff[],int addr,int count)
- {
- int res;
- lseek(fd, 64, SEEK_SET);
- if (write(fd,&addr,1)!=1)
- {
- printf("Can't write %s's addr %dn",I2C_DEV,addr);
- return -1;
- }
- res = read(fd,buff,count);
- printf("read %d bytes at 0x%02xnr",res,addr);
- return res;
- }
- static int write_eeprom(int fd,char buff[],int addr,int count)
- {
- int res;
- int i;
- static char sendbuffer[PAGE_SIZE+1];
-
- memcpy(sendbuffer+1,buff,count);
- sendbuffer[0]=addr;
-
- res = write(fd,&sendbuffer,count+1);
- printf("write %d bytes at 0x%02x buff[0]=%cn",res,sendbuffer[0], buff[0]);
- return res;
- }
- int main(void)
- {
- int fd,i,res;
- unsigned char str1[PAGE_SIZE] = "aaaaaaaaaaaaaaaaaaaaaaaaa";
- unsigned char s2[PAGE_SIZE] = "11111111111111111111111111111111";
- unsigned char buf[PAGE_SIZE] = {0};
- fd=open(I2C_DEV,O_RDWR);
- if(fd<0)
- {
- printf("Can't Open %s !!!nr",I2C_DEV);
- return -1;
- }
- res = ioctl(fd,I2C_TENBIT,0);
- res = ioctl(fd,I2C_SLAVE,CHIP_ADDR);
- for(i=0;i
- {
- // write_eeprom(fd,buf,i,1);
- }
- for(i=0;i
- {
- //printf("buf[0x%02x] = 0x%02xnr",i,buf[i]);
- }
- printf("0x4d=<%c> 0x4d=<%d>n", 0x4d, 0x4d);
- read_eeprom (fd,buf, 0, 32);
- printf("=2=buf = %sn 0x4d=<%c>r", buf, 0x4d);
- close(fd);
- return 0;
- }
复制代码
编译后运行结果如下:
- root@m437x-evm:/opt# ./eeprom_test
- 0x4d= 0x4d=<77>
- read 32 bytes at 0x00
- =2=buf = MYiR EEPROM SECOND TEST STR▒▒▒▒▒
复制代码
读出的内容跟uboot中的一样。这个程序使用了cat24c256设备的Immediate Address Read方式和Sequential Read方式。
在应用程序还可以使用另外一种方式来读取eeprom,因为代码比较多,我就只贴关键代码,其他代码见附件。
- int
- i2c_read_data(u16 addr, u8 offset, u8 *val)
- {
- int i,ret = 0;
- struct i2c_rdwr_ioctl_data *data;
- if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)
- return -1;
- data->nmsgs = 2;
- if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {
- ret = -1;
- goto errexit3;
- }
- if ((data->msgs[0].buf = (unsigned char *)malloc(sizeof(unsigned char) * 64)) == NULL) {
- ret = -1;
- goto errexit2;
- }
- if ((data->msgs[1].buf = (unsigned char *)malloc(sizeof(unsigned char) * 64)) == NULL) {
- ret = -1;
- goto errexit1;
- }
- data->msgs[0].addr = addr;
- data->msgs[0].flags = 0;
- data->msgs[0].len = 1;
- data->msgs[0].buf[0] = offset;
- data->msgs[1].addr = addr;
- data->msgs[1].flags = I2C_M_RD;
- data->msgs[1].len = 64; //original data is 1
- data->msgs[1].buf[0] = 0;
- if ((ret = __i2c_send(fd, data)) < 0)
- goto errexit0;
- printf("data->msgs[1].len=%dn", data->msgs[1].len);
- for(i = 0 ;i < data->msgs[1].len; i++)
- val[i] = data->msgs[1].buf[i];
- errexit0:
- free(data->msgs[1].buf);
- errexit1:
- free(data->msgs[0].buf);
- errexit2:
- free(data->msgs);
- errexit3:
- free(data);
- return ret;
- }
复制代码
这个程序使用了cat24c256设备的Selective Read方式。
第三个应用程序也可以读取eeprom的数据,只是需要kernel支持。
make ARCH= ARM CROSS_COMPILE=${RICO_CROSSTOOL} menuconfig
Device Drivers ---> Misc devices ---> EEPROM support ---> <*> I2C EEPROMs / RAMs / ROMs from most vendors
编译kernel,加载这个kernel。我们可以在发现如下信息,跟前面的相比多一个设备节点,直接cat这个设备节点就可以读到eeprom的全部内容。
root@m437x-evm:/sys/class/i2c-dev/i2c-0/device/0-0050# ls -l
lrwxrwxrwx 1 root root 0 Jun 9 22:40 driver -> ../../../../../bus/i2c/drivers/at24
-rw------- 1 root root 32768 Jun 9 22:40 eeprom
-r--r--r-- 1 root root 4096 Jun 9 22:40 modalias
-r--r--r-- 1 root root 4096 Jun 9 22:40 name
drwxr-xr-x 2 root root 0 Jun 9 22:40 power
lrwxrwxrwx 1 root root 0 Jun 9 22:39 subsystem -> ../../../../../bus/i2c
-rw-r--r-- 1 root root 4096 Jun 9 22:39 uevent
应用程序读取源代码如下:
- #include
- #include
- #include
- #define EEPROM_DEVICE "/sys/devices/44000000.ocp/44e0b000.i2c/i2c-0/0-0050/eeprom"
- #define TEST_STR "eeprom write/read test!"
- int main(void)
- {
- int fd;
- struct stat eeprom_stat;
- char read_buf[64] = { '\0' };
- fd = open(EEPROM_DEVICE, O_RDWR);
- if (fd < 0) {
- printf("open eeprom unsuccessn");
- return;
- }
- if (stat(EEPROM_DEVICE, &eeprom_stat) < 0) {
- perror("failed to get eeprom status");
- } else {
- printf("neeprom size: %d KBnn", eeprom_stat.st_size/1024);
- }
- /* lseek(fd, 64, SEEK_SET);
- printf("write '%s' to eepromn", TEST_STR);
- if (write(fd, TEST_STR, strlen(TEST_STR)) < 0) {
- perror("write to eeprom failedn");
- return;
- }
- */ lseek(fd, 0, SEEK_SET);
- if (read(fd, read_buf, sizeof(read_buf)) < 0) {
- printf("read back failedn");
- return;
- }
- printf("nget the following string from eeprom:n%snn", read_buf);
-
- return 0;
- }
复制代码
应用程序运行结果如下:
- root@m437x-evm:/opt# ./at24_eeprom_test
- eeprom size: 32 KB
- get the following string from eeprom:
- MYiR EEPROM SECOND TEST STR▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
复制代码
以上三个应用程序都是我参考他人的成果,特此向哪些好心人们表示感谢。
后面可以在eeprom上进行写操作在继续eeprom的学习。
0
|
|
|
|