AT24C256是一个Atmel公司的EEPROM存储芯片,容量是256K个bit(也就是32K字节),I2C接口,而树莓派正好有I2C接口,如下图蓝框中的4个IO口,
把AT24C256和这4个口接在一起,这样硬件就准备好了。
本文讲述如何使用树莓派3B来读写AT24C256,其它树莓派版本也是类似的。有2种方法:
- 使用树莓派CPU的I2C控制器来读写
- 使用AT24C256在Linux系统中映射的文件来读写
读写AT24C256
1. 使能I2C接口
在树莓派系统终端下运行以下命令,
sudo raspi-config
然后弹出以下界面,按上下键来选择“3 Interface Options”并回车,
在新界面里选择“I4 I2C”并回车,
在新界面里按左右键来选择“Yes”
最后回到开始的界面,按左右键选择Finish并回车,
这样I2C功能就开启了。
2. 使用I2C控制器读写
这里使用i2c-tools来操作,树莓派默认已经安装了这个工具,如果没有就使用下面命令进行安装,
sudo apt install i2c-tools
i2c-tools提供了好几个工具:
- i2cdetect
- i2cdump
- i2cget
- i2cset
- i2c-stub-from-dump
- i2ctransfer
先使用i2cdetect来探测有多少I2C控制器,如下,显示有2个,
同时在/dev/下也能看到这2个I2C控制器,连接上AT24C256后则会使用i2c-1,
i2cget,i2cset和i2ctransfer都是用来通过I2C控制器来读写eeprom,区别是:i2cget和i2cset适合那种地址是一个字节的eeprom芯片,如AT24C02这种,但是本文使用的是AT24C256,地址需要2个字节,所以只能使用i2ctransfer,同样它也能读一字节地址的eeprom,
i2ctransfer帮助信息如下,
写数据
使用下面命令进行写,"0x50"是芯片的I2C地址, "0x00 0x05"是eeprom内部的地址,该地址可以存放数据,
i2ctransfer 1 w6@0x50 0x00 0x05 0x55 0x66 0x77 0x88
PS:该命令意思是从eeprom的0x0005位置开始存放4个字节,0x55 0x66 0x77 0x88,不过对于i2ctransfer来说,它只是传输6个字节
显示如下,
PS:如果想写代码来操作,可以参考i2c-tools的源码
读数据
写完后再执行下面命令进行读,注意这里先要写2个字节,就是eeprom的内部地址0x0005,然后再读4个字节,
i2ctransfer 1 w2@0x50 0x00 0x05 r4
显示如下,可以看到读取出来的数据和写入的是一样的,
3. 使用EEPROM在Linux系统中映射的文件来读写(推荐)
这个也是推荐的方法,因为EEPROM映射成一个文件,这样可以使用常规的open, read和write来读写EEPROM,但是需要注意的是:这个办法和使用控制器的办法同时只能使用一个,因为映射之后控制器会被占用,就不能直接用控制器去读写了。
先执行下面的命令加载AT24的模块,这个模块是AT24系列芯片的驱动,
sudo modprobe at24
加载完毕后,在/sys/class/i2c-adapter/i2c-1/下会出现new_device和delete_device,
然后再运行sudo -i
进入root模式,这点很重要,最后执行下面的命令,这样可以把芯片型号和I2C地址传给驱动,
echo "24c256 0x50" > /sys/class/i2c-adapter/i2c-1/new_device
实际操作如下,
最后执行exit
回到用户模式。
此时在/sys/class/i2c-adapter/i2c-1/能看到新增的目录1-0050,"1"表示是i2c-1,"0050"表示设备的i2c地址是0x50,
cd进入1-0050目录,可以看到有个文件eeprom,这个就是AT24C256映射的文件,
PS:如果想取消映射,可以执行下面命令进行操作,
sudo -i
echo "0x50" > /sys/class/i2c-adapter/i2c-1/delete_device
使用代码进行读写
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
int main() {
int fd; // 文件描述符
char buffer[256]; // 用于读写的缓冲区
off_t offset;
// 以读写模式打开文件
fd = open("/sys/class/i2c-adapter/i2c-1/1-0050/eeprom", O_RDWR, 0644);
if (fd == -1) {
perror("无法打开文件");
exit(EXIT_FAILURE);
}
// 向文件写入数据
uint8_t wr_buf[4] = {0x11, 0x12, 0x55, 0x66};
ssize_t bytes_written = write(fd, wr_buf, 4);
if (bytes_written == -1) {
perror("写入文件时出错");
close(fd);
exit(EXIT_FAILURE);
}
printf("成功写入 %ld 字节\n", bytes_written);
// 使用lseek改变文件读写位置到文件开头
offset = lseek(fd, 0, SEEK_SET);
if (offset == -1) {
perror("lseek失败");
close(fd);
exit(EXIT_FAILURE);
}
// 从文件读取数据到缓冲区
ssize_t bytes_read = read(fd, buffer, 4);
if (bytes_read == -1) {
perror("读取文件时出错");
} else {
for (uint32_t i = 0; i < 4; ++i)
{
printf("==> 0x%x\n", buffer[i]);
}
}
// 关闭文件
if (close(fd) == -1) {
perror("关闭文件时出错");
exit(EXIT_FAILURE);
}
return 0;
}
代码以0x0000位置为起始地址,写入"0x11 0x12 0x55 0x66",编译后运行程序并运行,
可以看到读写都没问题,同时也能发现映射后对eeprom的操作和读写一个普通文件没有区别。
PS:如果想修改起始地址,可以使用lseek函数来设置位置。
总结
本文讲述了2种方法来读写AT24C256,更推荐文件方式的办法,这种更符合Linux“万物皆文件”的思想。