(本文内容涉及的代码全部取自linux-2.6.36内核中,本文中的部分内容参考了Documentation/i2c/说明文档)
在自己使用8位单片机的时代,i2c就是一种最简单而且常用的总线,它可以提供一个实现多设备连接的低价低速率的通讯方式。
在开始进行i2c应用编成前先重新了解一下i2c的数据格式。
Simplesend transaction
===========================
Thiscorresponds to i2c_master_send.
S Addr Wr[A] Data [A] Data [A] ... [A] Data [A] P
Simplereceive transaction
===========================
Thiscorresponds to i2c_master_recv
S Addr Rd [A] [Data] A [Data] A ... A [Data]NA P
S (1 bit) : 起始位
P (1 bit) : 结束位
Rd/Wr(1 bit) : 读/写位. Rd = 1, Wr = 0.
A,NA (1 bit) : 接收位.
Addr (7 bits): I2C 7 位地址,可扩展到10位.
Comm (8 bits): 命令字.
Data (8 bits): 数据字
Count(8 bits): 块长度.
在进行编成前首先了解一个比较重要的结构定义:
/* This is thestructure as used in the I2C_SMBUSioctl call */
struct i2c_smbus_ioctl_data {
__u8read_write;
__u8command;
__u32size;
unioni2c_smbus_data __user *data;
};
/* This is thestructure as used in the I2C_RDWRioctl call */
struct i2c_rdwr_ioctl_data {
structi2c_msg __user *msgs; /* pointers to i2c_msgs */
__u32nmsgs; /* number of i2c_msgs */
};
上面两个结构是用来调用对应算法的结构定义,当需要使用I2C_SMBUS算法是即使用struct i2c_smbus_ioctl_data;同理当需要I2_RDWR算法是即调用struct i2c_rdwr_ioctl_data。
struct i2c_msg {
__u16addr; /* slave address */
__u16flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16len; /* msg length */
__u8*buf; /* pointer to msg data */
};
上面这个结构从结构注视中可以基本了解其功能,它是一个在用户空间使用的底层结构,i2c_transfer()进行处理这个结构,并且通过i2c-dev与用户空间进行数据交换。
下面开始具体代码的分析(本代码使用内核版本为2.6.36,使用tiny6410开发板进行调试):
/*
* 本程序实现对AT24C08进行一个字节的读写操作
*/
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<linux/i2c.h>
#include<linux/i2c-dev.h>
intmain(){
int res,count;
struct i2c_rdwr_ioctl_data e2prom;
res = open ("/dev/i2c-0", O_RDWR); //打开设备文件根据设备具体注册名打开
if(res < 0){
perror ("open i2c0");
}
e2prom.nmsgs = 2;
e2prom.msgs =(struct i2c_msg*)malloc(e2prom.nmsgs * sizeof(struct i2c_msg));
if (e2prom.msgs == 0){
perror ("i2c mem inital");
return 1;
}else {
perror ("i2c mem inital");
}
sleep (1);
e2prom.nmsgs = 1;
(e2prom.msgs[0]).len = 2;
(e2prom.msgs[0]).addr = 0x51;
(e2prom.msgs[0]).flags = 0;
(e2prom.msgs[0]).buf = (unsigned char*)malloc(2);
(e2prom.msgs[0]).buf[0] = 0x00;
(e2prom.msgs[0]).buf[1] = 0x88;
if((ioctl(res, I2C_RDWR, (unsigned long)&e2prom)) < 0)
{
perror("ioctl I2C_WR");
return 1;
}else {
perror("ioctl I2C_WR");
printf("Writedata:%d\n",(e2prom.msgs[0]).buf[1]);
}
for (count = 3;count != 0; count --){
printf("delay time%d\n",count);
sleep(1);
}
e2prom.nmsgs=2;
(e2prom.msgs[0]).len = 1;
(e2prom.msgs[0]).addr = 0x51;
(e2prom.msgs[0]).flags = 0;
(e2prom.msgs[0]).buf[0] = 0x00;
(e2prom.msgs[1]).len = 1;
(e2prom.msgs[1]).addr = 0x51;
(e2prom.msgs[1]).flags = I2C_M_RD;
(e2prom.msgs[1]).buf = (unsignedchar*)malloc(1);
(e2prom.msgs[1]).buf[0] = 0;
if((ioctl(res,I2C_RDWR,(unsigned long)&e2prom)) < 0)
{
perror("ioctl I2C_RD");
return 1;
}else {
perror("ioctl I2C_RD");
}
printf("Read data=%d\n",(e2prom.msgs[1]).buf[0]);
close(res);
return 0;
}
配置内核打开CONFIG_S3C_DEV_I2C0,并且加入
s3c_i2c0_set_platdata(NULL);
该函数是plat-samsung中,创建platform_device的功能。
(samsung用户的福音阿,不需要进行复杂的驱动编程就可以简单实现i2c设备的操作,但是它是如何与i2c子系统搞上关系的,也看的我一雾水)
I2c设备初始化采用Declarethe I2C devices bybus number的方式。
首先创建一个i2c的设备信息。
staticstruct i2c_board_infoi2c_devs0[] __initdata = {
{ I2C_BOARD_INFO("ov965x",0x60), },
};
然后在mini6410_machine_init中添加注册功能:
if (ARRAY_SIZE(i2c_devs0)) {
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
}
if (ARRAY_SIZE(i2c_devs1)) {
i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1));
}
运行结果:
i2c mem inital: Success
ioctlI2C_WR: Success
Writedata:136
delaytime 3
delaytime 2
delaytime 1
ioctl I2C_RD:Success
Read data =136