编写APP直接访问EEPROM
参考资料:
- Linux驱动程序:
drivers/i2c/i2c-dev.c
- I2C-Tools-4.2:
https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
- AT24cxx.pdf
1. 硬件连接
- STM32MP157的I2C模块连接方法
- IMX6ULL的I2C模块连接方法
2. AT24C02访问方法
2.1 设备地址
从芯片手册上可以知道,AT24C02的设备地址跟它的A2、A1、A0引脚有关:
打开I2C模块的原理图(这2个文件是一样的):
STM32MP157\开发板配套资料\原理图\04_Extend_modules(外设模块)\eeprom.zip\i2c_eeprom_module_v1.0.pdf
IMX6ULL\开发板配套资料\原理图\Extend_modules\eeprom.zip\i2c_eeprom_module_v1.0.pdf
- 如下:
从原理图可知,A2A1A0都是0,所以AT24C02的设备地址是:0b1010000,即0x50。
2.2 写数据
2.3 读数据
可以读1个字节,也可以连续读出多个字节。
连续读多个字节时,芯片内部的地址会自动累加。
当地址到达存储空间最后一个地址时,会从0开始。
3. 使用I2C-Tools的函数编程
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <i2c/smbus.h>
#include "i2cbusses.h"
#include <time.h>
int main(int argc, char **argv)
{
unsigned char dev_addr = 0x50; //设备地址
unsigned char mem_addr = 0; //设备内部存储地址
unsigned char buf[32];
int file;
char filename[20];
unsigned char *str;
int ret;
struct timespec req;
if (argc != 3 && argc != 4)
{
printf("Usage:\n");
printf("write eeprom: %s <i2c_bus_number> w string\n", argv[0]);
printf("read eeprom: %s <i2c_bus_number> r\n", argv[0]);
return -1;
}
file = open_i2c_dev(argv[1][0]-'0', filename, sizeof(filename), 0);
if (file < 0)
{
printf("can't open %s\n", filename);
return -1;
}
if (set_slave_addr(file, dev_addr, 1)) //分配设备地址
{
printf("can't set_slave_addr\n");
return -1;
}
if (argv[2][0] == 'w') //写操作
{
// write str: argv[3]
str = argv[3];
req.tv_sec = 0;
req.tv_nsec = 20000000; /* 20ms */
while (*str)
{
ret = i2c_smbus_write_byte_data(file, mem_addr, *str);
if (ret)
{
printf("i2c_smbus_write_byte_data err\n");
return -1;
}
// wait tWR(10ms)
nanosleep(&req, NULL);
mem_addr++;
str++;
}
ret = i2c_smbus_write_byte_data(file, mem_addr, 0); // string end char
if (ret)
{
printf("i2c_smbus_write_byte_data err\n");
return -1;
}
}
else //读操作
{
// read
ret = i2c_smbus_read_i2c_block_data(file, mem_addr, sizeof(buf), buf);
if (ret < 0)
{
printf("i2c_smbus_read_i2c_block_data err\n");
return -1;
}
buf[31] = '\0';
printf("get data: %s\n", buf);
}
return 0;
}
4. 编译
4.1 在Ubuntu设置交叉编译工具链
-
STM32MP157
export ARCH=arm export CROSS_COMPILE=arm-buildroot-linux-gnueabihf- export PATH=$PATH:/home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin
-
IMX6ULL
export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin
4.2 使用I2C-Tools的源码
4.3 编译
为IMX6ULL编译时,有如下错误:
这是因为IMX6ULL的工具链自带的include目录中,没有smbus.h。
需要我们自己提供这个头文件,解决方法:
-
提供头文件:
-
修改Makefile指定头文件目录
all: $(CROSS_COMPILE)gcc -I ./include -o at24c02_test at24c02_test.c i2cbusses.c smbus.c
4.4 上机测试
以下命令在开发板中执行。
-
挂载NFS
-
vmware使用NAT(假设windowsIP为192.168.1.100)
[root@100ask:~]# mount -t nfs -o nolock,vers=3,port=2049,mountport=9999 192.168.1.100:/home/book/nfs_rootfs /mnt
-
vmware使用桥接,或者不使用vmware而是直接使用服务器:假设Ubuntu IP为192.168.1.137
[root@100ask:~]# mount -t nfs -o nolock,vers=3 192.168.1.137:/home/book/nfs_rootfs /mnt
-
-
复制、执行程序
[root@100ask:~]# cp /mnt/at24c02_test /bin
[root@100ask:~]# at24c02_test 0 w www.100ask.net
[root@100ask:~]# at24c02_test 0 r
get data: www.100ask.net
五、通用驱动i2c-dev分析
参考资料:
- Linux驱动程序:
drivers/i2c/i2c-dev.c
- I2C-Tools-4.2:
https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
- AT24cxx.pdf
1. 回顾字符设备驱动程序
怎么编写字符设备驱动程序?
- 确定主设备号
- 创建file_operations结构体
- 在里面填充drv_open/drv_read/drv_ioctl等函数
- 注册file_operations结构体
- register_chrdev(major, &fops, name)
- 谁调用register_chrdev?在入口函数调用
- 有入口自然就有出口
- 在出口函数unregister_chrdev
- 辅助函数(帮助系统自动创建设备节点)
- class_create
- device_create
2. i2c-dev.c注册过程分析
2.1 register_chrdev的内部实现
2.2 i2c-dev驱动的注册过程
3. file_operations函数分析
i2c-dev.c的核心:
static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.compat_ioctl = compat_i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
主要的系统调用:open, ioctl:
要理解这些接口,记住一句话:APP通过I2C Controller与I2C Device传输数据。