IIC即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦公司研发出来的。是一种两线式串行总线,两条线可以挂多个参与通信的器件,即多机模式。一般由控制器作为主机。 IIC 有两个双向信号线:一个是数据线SDA,一个是时钟线SCL(IIC是半双工)。主从机之间的数据传输完全依靠这两个信号的配合。同时,只有主机才能进行时钟信号的生成。IIC总线式开漏引脚并联结构,因此需要在外部要添加上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。所有接入的器件保持高点平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平。 ??
1、通信起始信号和停止信号:
起始信号和停止信号是两种特殊的信号。SCL为高电平期间,SDA由高电平向低电平变化产生一个下降沿,表示起始信号。SCL为高电平期间,SDA由低电平向高电平变化产生一个上升沿,表示停止信号。起始和停止信号一般是由主机产生。
2、应答/非应答信号:
当主机发送一个字节后,从机需要进行一个应答信号,以此来判断信号是否完成了传输。从机应答主机所需要的时钟仍是主机提供的,每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据应答出现。在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答。
3、数据有效性
I2C使用SDA来传输数据,使用SCL来进行数据同步。每个时钟周期传输1位数据。在SCL时钟线为高电平时,也被称作采样时刻,此时SDA表示的数据有效,即SDA为高电平表示’1’,低电平表示‘0’,同时采样时刻SDA数据线必须保持稳定,因为在SCL高电平时,如果SDA发生动作,那么就会被判定成起始或停止信号。所以在传数据的时候,SCL为高时,SDA必须保持稳定,否则会导致此次数据传输失败。 当SCL时钟线为低电平时,SDA的数据无效,一般在这个时候是进行数据交换,为下次数据做准备。每次数据传输都以字节为单位,每次传输的字节数不受限制。
4、地址及数据方向
I2C总线上的设备,每个都有自己的独立地址。主机发起通讯时,通过SDA数据线发送设备地址来查找从机。I2C协议从机地址可以是7位或10位。在实际应用中,7位地址比较广泛,紧跟设备地址的一个数据位表示数据传输方向,它是数据的方向位(R/W),第8位或第11位。为’0’时,是主机向从机写数据;为‘1’时,是主机读取从机传输来的数据。
下面代码是51单片机的模拟I2C程序,需要注意的是51单片机的IO口是准双向IO。所以SDA引脚在数据传输过程中不需要进行方向的切换。这点需要注意。如果你的单片机GPIO有方向,一定注意切换。
代码如下:
iic.c
#include "reg52.h"
#include "type.h"
#include "iic.h"
#include "intrins.h"
#include "delay.h"
void I2C_Start(void)
{
SDA_H;
SCL_H;
SDA_L;
}
void I2C_Stop(void)
{
SDA_L;
SCL_H;
SDA_H;
}
void send_ACK(bit b_ACK)
{
SCL_L;
if(b_ACK)
{
SDA_L;
}
else
{
SDA_H;
}
SCL_H;
SCL_L;
}
void read_ACK(void)
{
uint8_t t=0;
SCL_H;
while((I2C_SDA==1)&&(t<200))t++;
SCL_L;
}
void I2C_init()
{
SDA_H;
SCL_H;
}
void write_byte(uint8_t date)
{
uint8_t mask;
SCL_L;
for(mask=0x80;mask!=0;mask>>=1)
{
if((mask&date)==0)
{
SDA_L;
}
else
{
SDA_H;
}
SCL_H;
SCL_L;
}
}
uint8_t read_byte()
{
uint8_t i=0,read_data=0;
SCL_L;
SDA_H;
for(i=0;i<8;i++)
{
SCL_H;
read_data=(read_data<<1)|I2C_SDA;
SCL_L;
}
return read_data;
}
void write_add(uint8_t address,uint8_t date)
{
I2C_Start();
write_byte(0XA0);
read_ACK();
write_byte(address);
read_ACK();
write_byte(date);
read_ACK();
I2C_Stop();
}
void write_MultipleByte(uint8_t address,uint8_t length,uint8_t *date)
{
I2C_Start();
write_byte(0XA0);
read_ACK();
write_byte(address);
read_ACK();
while(length--)
{
write_byte(*date++);
read_ACK();
}
I2C_Stop();
}
uint8_t read_add(uint8_t address,bit ACK)
{
uint8_t date;
I2C_Start();
write_byte(0XA0);
read_ACK();
write_byte(address);
read_ACK();
I2C_Start();
write_byte(0XA1);
read_ACK();
date=read_byte();
send_ACK(ACK);
I2C_Stop();
return date;
}
void read_MultipleByte(uint8_t *temp,uint8_t address,uint8_t data_size)
{
I2C_Start();
write_byte(0XA0);
read_ACK();
write_byte(address);
read_ACK();
I2C_Start();
write_byte(0XA1);
read_ACK();
while(data_size--)
{
*temp++=read_byte();
send_ACK(data_size);
}
I2C_Stop();
}
iic.h
#include "type.h"
#ifndef __IIC_H__
#define __IIC_H__
#define SCL_H I2C_SCL=1
#define SCL_L I2C_SCL=0
#define SDA_H I2C_SDA=1
#define SDA_L I2C_SDA=0
sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;
extern void I2C_Start(void);
extern void I2C_Stop(void);
extern void read_ACK(void);
extern void I2C_init();
extern void write_byte(uint8_t date);
extern uint8_t read_byte() ;
extern void write_add(uint8_t address,uint8_t date) ;
extern uint8_t read_add(uint8_t address,bit ACK);
extern void send_ACK(bit ACK);
extern void write_MultipleByte(uint8_t address,uint8_t length,uint8_t *date);
extern void read_MultipleByte(uint8_t *temp,uint8_t address,uint8_t data_size) ;
#endif
|