IIC总线

(1)概述

I2C(Inter-Integrated Circuit BUS) 集成电路总线,该总线由NXP(原PHILIPS)公司设计,多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。

经常IIC和SPI接口被认为指定是一种硬件设备,但其实这样的说法是不尽准确的,严格的说他们都是人们所定义的软硬结合体,分为物理层(四线结构)和协议层(主机,从机,时钟极性,时钟相位)。

IIC,SPI的区别不仅在与物理层,IIC比SPI有着一套更为复杂的协议层定义。下面来分别说明一下IIC的物理层和协议层。

(2)IIC的物理层

a.只要求两条总线线路,一条是串行数据线SDA,一条是串行时钟线SCL。(IIC是半双工,而不是全双工)。

b.每个连接到总线的器件都可以通过唯一的地址和其它器件通信,主机/从机角色和地址可配置,主机可以作为主机发送器和主机接收器。

c.IIC是真正的多主机总线,(而这个SPI在每次通信前都需要把主机定死,而IIC可以在通讯过程中,改变主机),如果两个或更多的主机同时请求总线,可以通过冲突检测和仲裁防止总线数据被破坏。

d.传输速率在标准模式下可以达到100kb/s,快速模式下可以达到400kb/s。

e.连接到总线的IC数量只是受到总线的最大负载电容400pf限制。

一个典型的IIC接口如下图(1)所示


图(1)

(3)IIC的协议层

IIC的协议层才是掌握IIC的关键。现在简单概括如下:

a.数据的有效性

在时钟的高电平周期内,SDA线上的数据必须保持稳定,数据线仅可以在时钟SCL为低电平时改变。

如图(2)所示:


      图(2)

b.起始和结束条件

起始条件:当SCL为高电平的时候,SDA线上由高到低的跳变被定义为起始条件,结束条件:当SCL为高电平的时候,SDA线上由低到高的跳变被定义为停止条件,要注意起始和终止信号都是由主机发出的,连接到I2C总线上的器件,若具有I2C总线的硬件接口,则很容易检测到起始和终止信号。总线在起始条件之后,视为忙状态,在停止条件之后被视为空闲状态,对起始条件和结束条件的描述如下图(3)所示。


图(3)

c.应答

每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据,从机应答主机所需要的时钟仍是主机提供的,应答出现在每一次主机完成8个数据位传输后紧跟着的时钟周期,低电平0表示应答,1表示非应答,如图(4)所示。


图(4)

d.数据帧格式

I2C总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。
在起始信号后必须传送一个从机的地址(7位),第8位是数据的传送方向位(R/T),用“0”表示主机发送数据(T),“1”表示主机接收数据(R)。{这里小编在驱动MPU6050模块的时候,就犯过这样的错误,它写的MPU6050从机地址是0x68,因为发送从机地址的时候,要加一位读写方向位,因为刚开始应该是向这个MPU6050里写从机里某个寄存器的地址,所以应该是7位地址   0x68(1101000)+二进制位0=11010000)也就是0xD0,表示要向该IIC设备里写东西,然后再紧接着写入IIC设备里的寄存器地址,而我直接写入了0x68,导致出错},每次数据传送总是由主机产生的终止信号结束。但是,若主机希望继续占用总线进行新的数据传送,则可以不产生终止信号,马上再次发出起始信号对另一从机进行寻址。
在总线的一次数据传输过程中,可以有以下几种组合方式:
[1]
主机向从机发送数据,数据传送方向在整个传送过程中不变:

注:有阴影部分表示数据由主机向从机传送,无阴影部分则表示 数据由从机向主机传送。
    A 表示应答(低电平), A 非表示非应答(高电平)。 S 表示起始信号, P 表示终止信号。
[2]主机在第一个字节后,立即从从机读数据:

[3]在传送过程中,当需要改变传送方向时,起始信号和从机地址都被重复产生一次,但两次读/写方向位正好反相:


一般情况下,[3]是比较常见的,比如MPU6050模块,
发送起始信号
等待从机应答
写一个从机地址+0(表示写),
等待从机应答
发送一个字节的MPU6050加速度存储寄存器地址,
等待从机应答
再发送一次起始信号
等待从机应答
写一个从机地址+1(表示读)
等待从机应答
读取MPU6050传感器数据
主机非应答
e.IIC信号的模拟
主机可以采用不带I2C总线接口的单片机,如80C51、AT89C2051等单片机,利用软件实现I2C总线的数据传送,即软件与硬件结合的信号模拟。即使是含有IIC硬件的单片机(如stm32 103系列)也有一定的缺陷,所以一般也会模拟IIC的时序。现将具体时间截图如下:

具体的程序代码如下:

#include “myiic.h”

//模拟IIC驱动PCF8563 PB13=SCL,PB12=SDA,PA0=INT 晶振为32.768KHz.

//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pins : PB12 PB13 */

GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12|GPIO_PIN_13, GPIO_PIN_SET);//PB12,PB13输出高
}

void Delay(uint8_t i) //us级延时
{
uint8_t k;
uint8_t j=32;
uint8_t temp=i*j;
for(k=0;k<temp;k++);
}

//配置SDA为输出模式
void SDA_OUT()
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pins : PB12 */

GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

//配置SDA为输出模式
void SDA_IN()
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOB_CLK_ENABLE();

/*Configure GPIO pins : PB12 */

GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT ;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}

//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA_H;
IIC_SCL_H;
Delay(4);
IIC_SDA_L;//START:when CLK is high,DATA change form high to low
Delay(4);
IIC_SCL_L;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL_L;
IIC_SDA_L;//STOP:when CLK is high DATA change form low to high
Delay(4);
IIC_SCL_H;
IIC_SDA_H;//发送I2C总线结束信号
Delay(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA_H;
Delay(1);
IIC_SCL_H;
Delay(1);
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12))
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL_L;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL_L;
SDA_OUT();
IIC_SDA_L;
Delay(2);
IIC_SCL_H;
Delay(2);
IIC_SCL_L;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL_L;
SDA_OUT();
IIC_SDA_H;
Delay(2);
IIC_SCL_H;
Delay(2);
IIC_SCL_L;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(uint8_t txd)
{
uint8_t t;
SDA_OUT();
IIC_SCL_L;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
//IIC_SDA=(txd&0x80)>>7;
if((txd&0x80)>>7)
IIC_SDA_H;
else
IIC_SDA_L;
txd<<=1;
Delay(2); //对TEA5767这三个延时都是必须的
IIC_SCL_H;
Delay(2);
IIC_SCL_L;
Delay(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t IIC_Read_Byte(void)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_L;
Delay(2);
IIC_SCL_H;
receive<<=1;
if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_12))
receive++;
Delay(1);
}
// if (!ack)
// IIC_NAck();//发送nACK
// else
// IIC_Ack(); //发送ACK
return receive;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值