一,何为IIC?
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。
IIC是半双工通信方式。
二,IIC通信的6种状态
1.空闲状态
2.开始信号
3.停止信号
4.应答信号
5.数据的有效性
6.数据传输
三.用代码实现I2C协议的模拟
1.空闲状态:当SDA以及SCL两条总线都处于高电平状态时,处于空闲状态
void IIC_init()
{
//声明一个GPIO的结构体变量
GPIO_InitTypeDef GPIO_InitStructure;
//使能GPIO外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//定义该GPIO结构体
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8 |GPIO_Pin_9; //两个GPIO
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz; //100MHz
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP; //推挽输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP; //上拉
//初始化GPIOB
GPIO_Init(GPIOB,&GPIO_InitStructure);
//初始化I2C总线,使之成为空闲状态
IIC_SCL=1;
IIC_SDA=1;
}
2.开始状态:当SCL为高电平时,SDA从高电平到低电平进行跳变,然后SCL跳变为低电平,时刻装备接收数据
按照上图实现如下代码 :(注意:SDA有输入,输出两种状态)
void IIC_start()
{
//将SDA的IO设置为输出状态
SDA_OUT();
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;
delay_us(4);
IIC_SCL=0; //拉低数据总线,时刻准备接收数据
}
3.结束状态:当SCL总线为高时,SDA从低到高进行跳变
按照上图实现代码如下 :(在SCL为高电平时,SDA进行跳变)
//产生停止信号
void IIC_stop()
{
//数据输出
SDA_OUT();
IIC_SCL=0;
IIC_SDA=0;
delay_us(4);
IIC_SCL=1; //按照上图,在SCL为高电平时,SDA进行跳变
IIC_SDA=1;
delay_us(4);
}
4.应答信号:
当发送器每发送一个字节,就会在第9周期释放数据总线(即SDA为高电平),等待接收器发送一个应答信号
该应答信号:(接收应答信号时,SDA需改变为输入状态)
当读到来自接收器的低电平,表明已经接收设备已经接收到数据。
当读到来自接收器的低电平,表明接收设备未能正常接收数据。
因此我们应该实现,应答信号,非应答信号,等待应答信号三个函数,以下为应答信号的实现
根据上图以及数据的有效性(在SCL信号由低到高,再由高到低的过程中,高电平保持期间,SDA保持不变,SDA即发送的数据),可以写出以下代码:
//产生应答信号
void IIC_ack()
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
5.非应答信号
上图中的not acknowledge 就是非应答信号。
根据上图以及数据的有效性(在SCL信号由低到高,再由高到低的过程中,高电平保持期间,SDA保持不变,SDA即发送的数据),可以写出以下代码:
//产生非应答信号
void IIC_nack()
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
6.等待应答的信号
在SCL的有效周期期间,读到SDA线上的数据
//将数据总线进行释放
//SDA为输入模式,进行应答信号的读取
//返回值: 1,无应答
// 0,有应答
u8 IIC_Wait_Ack()
{
u8 ucErrTime=0;
//SDA输入模式设置
SDA_IN();
IIC_SDA=1;
delay_us(1);
IIC_SCL=1;
delay_us(1);
while(READ_SDA)//应答信号的读取
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_stop();
return 1;
}
}
IIC_SCL=0;
return 0;
}
7.数据的接收
在SCL有效周期内对数据进行接收(SCL=0 ---> SCL=1 读SDA --->SCL=0 形成数据的有效性)
(注意:receive需将其初始化为0,否则会出现数据传输错误,因为局部变量的值为脏值,在栈上的值)
u8 IIC_Read_Byte(unsigned char ack)
{
u8 receive=0,t;
SDA_IN();
for(t=0;t<8;t++)
{
IIC_SCL=0; //SCL=0 -> SCL=1 读SDA ->SCL=0 形成数据的有效性
delay_us(2);
IIC_SCL=1;
receive<<=1; //接收一位数据为0
if(READ_SDA)
{
receive++; //接收一位数据为1
}
delay_us(1);
}
if(!ack)
{
IIC_nack();
}else
{
IIC_ack();
}
return receive;
}
8.数据的发送(SCL=0 ---> SCL=1 发送SDA --->SCL=0 形成数据的有效性)
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;//将最高位的数据进行输出
txd<<=1; //将最高位的数据溢出,准备发送次高位的数据
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
以上为I2C通信协议的实现(部分代码未给出),后续文章为I2C协议的使用