一、前言
本文主要分为三个部分,第一部分,介绍i2c总线应用的背景以及本文编译测试需要的开发环境;第二部分,介绍主要的源码及相关函数接口;第三部分,测试方法以及详细测试结果,i2c从设备的7bit器件地址可以在设备的datasheet查找。文章的最后会给大家分享本文的所有源码。
二、开发背景和环境
在做嵌入式相关工作时,需要配置i2c从设备的寄存器是常有的事,本文记录了利用i2c总线的去配置i2c从设备的方法。本例是针对i2c子系统来实现的应用程序,在我看来i2c子系统都是标准的,因此跟内核版本以及ARM具体型号没有关系,其中ioctl调用的。其中ARM s3c6410有2个i2c总线,但硬件只引出了i2c-0。
优点:(1)编写代码简单(相对不懂字符驱动架构来说,而且驱动方式需要一个驱动程序和一个应用测试程序),一个应用程序程序可以快速配置寄存器,很快看到结果;
(2)启动时间比较相比内核驱动的慢,应用程序是在文件系统初始化完成后才能运行;比如,用i2c配置显示模块,要显示屏尽快工作,把配置做在内核驱动比较合适。
缺点 :(1)在应用层运行软件时,i2c从设备需要多种功能操作时(比如修改摄像头的亮度、放大、曝光、分辨率等配置),把每个功能包装成函数模块,相对层次不明显,而且难以管理维护,建议用驱动的实行完成(内核驱动、.ko均可)
(2)应用层只能调用i2c子系统驱动程序所支持的接口,需要扩展使用内核的其他资源(在必要测试时,但理论很少发生,因内核的存在就是防止应用层随意访问内核的资源),要么修改i2c子系统的驱动,或者自己编写的i2c从设备的设备驱动 。
运行环境:ARM S3C6410平台
交叉编译器:ARM-LINUX-GCC
内核版本:2.6.28.6
三、源码的讲解
源码部分主要又分为两个部分,第一部分是我们工作中所要应用到的,无非是i2c总线的初始化(打开、关闭操作),还有i2c的读写(write、read操作);第二部分,解决部分疑问(变量、数据结构以及宏定义的来源),选择性了解,由Linux属于开源系统,所以源码中的任何定义都能够找到;
1、应用程序中用户的宏定义
#define I2C_READ_FLAGS 1
#define I2C_WRITE_FLAGS 0
#define I2C_BUS_NAME "/dev/i2c-0"
2、以下为打开和关闭i2c设备操作,其中包括了检测从设备:
int i2c_init(const char *filename, const int addr) {
// Check I2C connectivity
file = open(filename, O_RDWR);
if (file < 0) {
printf("Failed to open bus\n");
}
// Check connectivity to slave device
ak = ioctl(file, I2C_SLAVE, addr);
if (ak < 0) {
printf("Failed to acquire bus access and/or talk to slave\n");
}
return file;
}
void i2c_close(int fb){
// closes I2C connection
close(fb);
return;
}
2、以下函数为用总线i2c去写从设备地址的寄存器,因本文所介绍得i2c从设备每个寄存器地址是8bit(一个字节),而有的i2c从设备寄存器地址为16bit(2个字节),相应地改下work_queue数据包就行,buf存放的寄存器地址和将要向寄存器写的值,flags字段区别读还是写,读的函数同理,凡事多测试、实践就能找到窍门:
static int i2c_write(int i2c_fd, unsigned int reg_address ,unsigned int reg_val){
struct i2c_rdwr_ioctl_data work_queue;
int ret;
work_queue.nmsgs = 1;
work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs * sizeof(struct i2c_msg));
if(!work_queue.msgs){
printf("msgs memery alloc error\n");
close(i2c_fd);
return 0;
}
if ((work_queue.msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL){//本文buff是根据char(8位)一个字节分配的空间,可以根据寄存器地址长度自行分配
printf("buf memery alloc error...\n");
close(i2c_fd);
return 0;
}
(work_queue.msgs[0]).len = 2;
(work_queue.msgs[0]).flags = I2C_WRITE_FLAGS;
(work_queue.msgs[0]).addr = SLAVE_DEVICE_7BIT_ADDRESS;
(work_queue.msgs[0]).buf[0] =reg_address;
(work_queue.msgs[0]).buf[1] = reg_val;
ret = ioctl(i2c_fd, I2C_RDWR, (unsigned long) &work_queue);
if(ret < 0){
printf("Error during I2C_RDWR ioctl with error code: %d\n", ret);
return 0;
}
printf("Write Reg:0x%x Value:0x%x\n",reg_address, reg_val);
free(work_queue.msgs[0].buf);
free(work_queue.msgs);
}
3、以下函数为用总线i2c去读从设备地址的寄存器:
static int i2c_read(int i2c_fd, unsigned char reg_address){
struct i2c_rdwr_ioctl_data work_queue;
unsigned char val,reg;
int ret;
work_queue.nmsgs = 2;
work_queue.msgs = (struct i2c_msg *)malloc(work_queue.nmsgs *sizeof(struct i2c_msg));
if(!work_queue.msgs){
printf("Memery alloc error\n");
close(i2c_fd);
return 0;
}
if ((work_queue.msgs[0].buf = (unsigned char *)malloc(2 * sizeof(unsigned char))) == NULL){
printf("buf memery alloc error...\n");
close(i2c_fd);
return 0;
}
val