使用GPIO模拟I2C总线进行通信
2013-10-13 15:42阅读:
I2C是由Philips公司发明的一种串行数据通信协议,仅使用两根信号线:SerialClock(简称SCL)和SerialData(简称SDA)。I2C是总线结构,1个Master,1个或多个Slave,各Slave设备以7位地址区分,地址后面再跟1位读写位,表示读(=1)或者写(=0),所以我们有时也可看到8位形式的设备地址,此时每个设备有读、写两个地址,高7位地址其实是相同的。
I2C数据格式如下:
无数据:SCL=1,SDA=1;
开始位(Start):当SCL=1时,SDA由1向0跳变;
停止位(Stop):当SCL=1时,SDA由0向1跳变;
数据位:当SCL由0向1跳变时,由发送方控制SDA,此时SDA为有效数据,不可随意改变SDA;
当SCL保持为0时,SDA上的数据可随意改变;
地址位:定义同数据位,但只由Master发给Slave;
应答位(ACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=0;
否应答位(NACK):当发送方传送完8位时,发送方释放SDA,由接收方控制SDA,且SDA=1。
当数据为单字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,停止位。
当数据为一串字节传送时,格式为:
开始位,8位地址位(含1位读写位),应答,8位数据,应答,8位数据,应答,……,8位数据,应答,停止位。
需要注意的是:
1,SCL一直由Master控制,SDA依照数据传送的方向,读数据时由Slave控制SDA,写数据时由Master控制SDA。当8位数据传送完毕之后,应答位或者否应答位的SDA控制权与数据位传送时相反。
2,开始位“Start”和停止位“Stop”,只能由Master来发出。
3,地址的8位传送完毕后,成功配置地址的Slave设备必须发送“ACK”。否则否则一定时间之后Master视为超时,将放弃数据传送,发送“Stop”。
4,当写数据的时候,Master每发送完8个数据位,Slave设备如果还有空间接受下一个字节应该回答“ACK”,Slave设备如果没有空间接受更多的字
节应该回答“NACK”,Master当收到“NACK”或者一定时间之后没收到任何数据将视为超时,此时Master放弃数据传送,发送“Stop”。
5,当读数据的时候,Slave设备每发送完8个数据位,如果Master希望继续读下一个字节,Master应该回答“ACK”以提示Slave准备下一个数据,如果Master不希望读取更多字节,Master应该回答“NACK”以提示Slave设备准备接收Stop信号。
6,当Master速度过快Slave端来不及处理时,Slave设备可以拉低SCL不放(SCL=0将发生“线与”)以阻止Master发送更多的数据。此时Master将视情况减慢或结束数据传送。
在实际应用中,并没有强制规定数据接收方必须对于发送的8位数据做出回应,尤其是在Master和Slave端都是用GPIO软件模拟的方法来实现的情况下,编程者可以事先约定数据传送的长度,slave不检查NACK,有时可以起到减少系统开销的效果。但是如果slave方是硬件i2c要求一定要标准的NACK,master方是GPIO软件模拟i2c并没有正确的发送NACK,就会出现“slave收不到stop”导致i2c挂死。
下面给出了模拟I2C总线进行读写的伪代码,用以说明如何使用GPIO实现I2C通信:
- #define SDA
254
//定义SDA所对应的GPIO接口编号
- #define SCL 255
//定义SCL所对应的GPIO接口编号
- #define OUTP 1
//表示GPIO接口方向为输出
- #define INP 0
//表示GPIO接口方向为输入
- int i2c_start()
- {
- //初始化GPIO口
- set_gpio_direction(SDA,
OUTP);
//设置SDA方向为输出
- set_gpio_direction (SCL,
OUTP);
//设置SCL方向为输出
-
set_gpio_value(SDA,
1);
//设置SDA为高电平
- set_gpio_value(SCL, 1);
//设置SCL为高电平
- delay();
//延时
- //起始条件
- set_gpio_value(SDA, 0);
//SCL为高电平时,SDA由高变低
- delay();
- }
- void i2c_stop()
- {
- set_gpio_value(SCL, 1);
- set_gpio_direction(SDA,
OUTP);
- set_gpio_value(SDA, 0);
- delay();
- set_gpio_value(SDA, 1);
//SCL高电平时,SDA由低变高
- }
- unsigned char
i2c_read_ack()
- {
- unsigned char r;
- set_gpio_direction(SDA,
INP);
//设置SDA方向为输入
- set_gpio_value(SCL,0);
// SCL变低
- r =
get_gpio_value(SDA);
//读取ACK信号
- delay();
- set_gpio_value(SCL,1);
// SCL变高
- delay();
- return r;
- }
- int i2c_send_ack()
- {
- set_gpio_direction(SDA,
OUTP);
//设置SDA方向为输出
- set_gpio_value(SCL,0);
// SCL变低
- set_gpio_value(SDA, 0);
//发出ACK信号
- delay();
- set_gpio_value(SCL,1);
// SCL变高
- delay();
- }
- void
i2c_write_byte(unsigned char b)
- {
- int i;
- set_gpio_direction(SDA,
OUTP);
//设置SDA方向为输出
- for
(i=7;
i>=0; i--) {
- set_gpio_value(SCL, 0);
// SCL变低
- delay();
- set_gpio_value(SDA, b
&
(1<</SPAN><</SPAN>i));
//从高位到低位依次准备数据进行发送
- set_gpio_value(SCL, 1);
// SCL变高
- delay();
- }
- i2c_read_ack();
//检查目标设备的ACK信号
- }
- unsigned char
i2c_read_byte()
- {
- int i;
- unsigned char
r =
0;
- set_gpio_direction(SDA,
INP);
//设置SDA方向为输入
- for
(i=7;
i>=0; i--) {
- set_gpio_value(SCL, 0);
// SCL变低
- delay();
- r =
(r
<</SPAN><</SPAN>1)
| get_gpio_value(SDA);
//从高位到低位依次准备数据进行读取
- set_gpio_value(SCL, 1);
// SCL变高
- delay();
- }
- i2c_send_ack();
//向目标设备发送ACK信号
- return r;
- }
- void i2c_read(unsigned
char addr, unsigned char* buf,
int len)
- {
- int i;
- unsigned char t;
- i2c_start();
//起始条件,开始数据通信
- //发送地址和数据读写方向
- t =
(addr
<</SPAN><</SPAN>
1) | 1;
//低位为1,表示读数据
- i2c_write_byte(t);
- //读入数据
- for
(i=0;
i<</SPAN>len;
i++)
- buf[i] =
i2c_read_byte();
- i2c_stop();
//终止条件,结束数据通信
- }
- void i2c_write
(unsigned char addr, unsigned
char* buf, int len)
- {
- int i;
- unsigned char t;
- i2c_start();
//起始条件,开始数据通信
- //发送地址和数据读写方向
- t =
(addr
<</SPAN><</SPAN>
1) | 0;
//低位为0,表示写数据
- i2c_write_byte(t);
- //写入数据
- for
(i=0;
i<</SPAN>len;
i++)
- i2c_write_byte(buf[i]);
- i2c_stop();
//终止条件,结束数据通信
- }
在上面的代码中,i2c_read和i2c_write这两个函数可以实现GPIO接口对I2C总线的模拟读写。
--------------------------------------------------------------------------------------------------
不是完整的代码,仅仅提供模拟操作部分供大家参考。
[cpp] view
plaincopyprint?
-
- #define DELAY
1
- #define SCL
89
- #define SDA
20
- #define RST
19
- #define IRQ
108
- void i2c_start(void)
- {
-
gpio_direction_output(SDA, 1);
-
gpio_direction_output(SCL, 1);
-
udelay(DELAY);
-
-
gpio_set_value(SDA, 0);
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 0);
-
udelay(DELAY);
- }
- void i2c_stop(void)
- {
-
gpio_set_value(SCL, 0);
-
gpio_set_value(SDA, 0);
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 1);
-
udelay(DELAY);
-
gpio_set_value(SDA, 1);
-
udelay(DELAY);
- }
- void i2c_send_ack(u8
ack)
- {
-
if(ack)
-
gpio_direction_output(SDA, 1);
-
else
-
gpio_direction_output(SDA, 0);
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 1);
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 0);
-
udelay(DELAY);
- }
- u8 i2c_receive_ack(void)
- {
- u8
rc = 0;
-
-
gpio_direction_input(SDA);
-
gpio_set_value(SCL, 1);
-
udelay(DELAY);
-
-
if(gpio_get_value(SDA)) {
-
rc
= 1;
- }
-
gpio_set_value(SCL, 0);
-
gpio_direction_output(SDA, 1);
-
return rc;
- }
- u8 i2c_send_byte(u8 send_byte)
- {
- u8
rc = 0;
- u8
out_mask = 0x80;
- u8
value;
- u8
count = 8;
-
while(count >
0) {
-
value
= ((send_byte &
out_mask) ? 1
: 0);
-
if (value ==
1) {
-
gpio_set_value(SDA, 1);
-
}
-
else {
-
gpio_set_value(SDA, 0);
-
}
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 1);
-
udelay(DELAY);
-
-
gpio_set_value(SCL, 0);
-
udelay(DELAY);
-
-
out_mask
>>= 1;
-
count--;
- }
-
-
gpio_set_value(SDA, 1);
- rc
= i2c_receive_ack();
-
return rc;
- }
- void i2c_read_byte(u8
*buffer, u8 ack)
- {
- u8
count = 0x08;
- u8
data = 0x00;
- u8
temp = 0;
-
-
gpio_direction_input(SDA);
-
while(count >
0) {
-
gpio_set_value(SCL, 1);
-
udelay(DELAY);
-
temp
= gpio_get_value(SDA);
-
data
<<= 1;
-
if (temp)
-
data
|= 0x01;
-
gpio_set_value(SCL, 0);
-
udelay(DELAY);
-
count--;
- }
-
i2c_send_ack(ack);//0 =
ACK
1 = NACK
-
*buffer = data;
- }
- //向client的某个寄存器写入多个字节,len是要写入的数据的长度
- u8 i2c_write(u8 device_id,
u8 reg_address, u8*
data, u8 len)
- {
- u8
rc = 0;
- u8
i;
-
i2c_start();
-
- rc
|= i2c_send_byte(
(device_id << 1)
| 0x00 );
- rc
|= i2c_send_byte(reg_address);
-
if(data==NULL ||0==len)
{
-
i2c_stop();
-
return rc;
- }
-
-
for(i=0; i<len;
i++) {
-
rc
|= i2c_send_byte(*data);
-
data++;
- }
-
-
i2c_stop();
-
if(rc) {
-
printk('ERROR!
ssd2531_i2c_write failed/n');
- }
-
return rc;
- }
- //从某个register中读取len个字节放在长度为len的缓冲区buffer中
- u8 i2c_read(u8 device_id,
u8 reg_address, u8
*buffer, u8 len)
- {
- u8
rc = 0;
- u8
i;
-
-
i2c_start();
- rc
|= i2c_send_byte(
(device_id << 1)
| 0x00 );
- rc
|= i2c_send_byte(reg_address);
-
i2c_start();//restart I2C
- rc
|= i2c_send_byte(
(device_id << 1)
| 0x01 );
-
-
for(i=0;i<len;i++) {
-
i2c_read_byte(buffer++,
!(len-i-1));//
!(len-i-1)
这个用来保证在读到每个字节后发送一个ACK并能在最后一个字节读完后发送一个NACK
- }
-
-
i2c_stop();
-
if(rc) {
-
printk('ERROR!
ssd2531_i2c_read failed/n');
-
return rc;
- }
-
return rc;
- }