(本文內容涉及的代碼全部取自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