IIC通信详解

IIC总线:

IIC总线是一种多主机总线,连接在IIC总线上的器件分为主机和从机。主机有权发起和结束一次通信,而从机只能被主机呼叫;当总线上有多个主机同时启用总线时,IIC也具备冲突检测和仲裁的功能来防止错误产生;每个连接到IIC总线上的器件都有一个位移的地址(7bit),且每个器件都可以作为主机也可以作为从机(但同一时刻只能有一个主机),总线上的器件增加和删除不影响其他器件正常工作;IIC总线在通信时总线上发送数据的器件为发送器,接收数据的器件为接收器。(主机可以作为发送器也可以作为接收器)

IIC总线通信过程:

1.主机发送起始信号启用总线
2.主机发送一个字节数据指明从机地址和后续字节的传送方向
3.被寻址的从机发送应答信号回应主机
4.接收器发送一个字节数据
5.(循环上面的4.5)
6.通信完成后主机发送停止信号释放总线

IIC总线寻址方式:

(1)IIC总线上传送的数据是广义的,即包括地址,又包括真正的数据
(2)主机在发送起始信号后必须发送一个字节的数据,该数据的高7位为从机地址,最低位表示后续字节的传送方向,‘0’表示主机发送数据,‘1’表示主机接收数据;总线上所有的从机接收到该字节数据后都将这7位地址与自己的地址进行比较,如果相同,则认为自己被主机寻址,然后再根据第8位将自己定位发送器或接收器。(换句话说就是谁是发送器还是接收器,是由主机发送给从机的最后一位决定的。如果为0,主机为发送器;如果为1,主机为接收器)

起始信号和停止信号

(1)SCL为高电平时,SDA由高变低表示起始信号
(2)SCL为高电平时,SDA由低变高表示停止信号
(3)起始信号和停止信号都是由主机发出,其实信号产生后总线处于占用状态停止信号产生后总线处于空闲状态
在这里插入图片描述

同步信号:

(1)IIC总线在数据传送时,时钟线SCL为低电平期间发送器向数据线上发送一位数据,在此期间数据线上的信号允许发生变化,时钟线SCL为高电平期间接收器从数据线上读取一位数据,在此期间数据线上的信号不允许发生变化,必须保持稳定。
在这里插入图片描述SCL为时钟线,SDA为数据线。当SCL为低电平的时候,允许SDA变化,即为发送器发送数据。当SCL为高电平的时候,不允许SDA发生变化,即接收器接收数据。SCL就是告诉收发双方什么时候收数据和发数据。SCL是连接在收发双方上的,就解决了不同步的问题。

字节传送与应答:

(1)IIC总线通信时每个字节为8位长度,数据传送时,先传送最高位,后传送低位,发送器发送完一个字节数据后接收器必须发送1位应答位来回应发送器即一帧共有9位。
在这里插入图片描述当8位发送完后,从机会将SDA拉低,表示应答。如还是高电平,表示非应答,即可能没有接收到。

典型IIC时序:

(1)主机向从机发送数据(主机发送一个信号作为通信的开始,这个信号有8位,高7位表示的是从机的地址,最后一位表示的是读写位,0表示主机向从机发数据,1表示从机向主机发数据 ,A表示从机向主机发送了一个应答信号,表示收到,后面开始循环,主机发送一个数据,从机应答,主机发送一个数据,从机应答,直到从机发送A非,不想应答,或者主机发送停止信号,才截止这次通讯,停止信号永远是主机发送)
请添加图片描述
(2)从机向主机发送数据(这里指的从机向主机发数据,指的是在主机向从机寻完址之后,从机向主机发数据,第一个数据包含从机地址的必定是主机发给从机的)。这个是主机发给从机过程的相反的过程。
请添加图片描述
(3)主机先向从机发送数据,然后从机再向主机发送数据
请添加图片描述
注:阴影部分表示数据由主机向从机传送,无阴影部分表示数据由从机向主机传送;A表示应答,A非表示非应答,S表示起始信号,P表示终止信号。

硬件IIC实现流程:

(1)IIC控制器主机发送模式
请添加图片描述
(1)刚开始要把IIC控制器配置为主机发送模式,后续需要配置相关的寄存器
(2)写一个从机的地址到IIC寄存器的I2CDS
(3)向IIC的IICSTAT寄存器里面写入0xF0,产生一次起始信号,开始通信
(4)IICDS里面的数据(就是从机的地址)就会被发送
(5)在从机接收到数据之后就会产生中断,由interrupt寄存器响应,产生中断挂起,在发送或者接收到数据之后,这个中断位会变成1,表示从机已经应答。
(5)下面根据自己需求是选择继续通信还是停止通讯。
(5.1)下面向IICDS中写入数据,你想要发什么数据,那么就写入什么数据,这个时候中断挂起位已经挂起
(5.1)清除中断挂起位
(5.1)清除中断挂起位后,那么你写入IIC寄存器中的第二个数据就会发送
(5.1)返回到(5)
(5.2)往IICSTAT寄存器中写入0xD0,表示停止信号
(5.2)清除中断挂起位
(5.2)等待停止信号的结束
(2)IIC控制器作接收器
请添加图片描述(1)刚开始要把IIC控制器配置为主机接收模式,后续需要配置相关的寄存器
(2)写一个从机的地址到IIC寄存器的I2CDS,主机寻址
(3)向IIC的IICSTAT寄存器里面写入0xB0,产生一次起始信号,开始通信
(4)IICDS里面的数据(就是从机的地址)就会被发送
(5)在从机接收到数据之后就会产生中断,由interrupt寄存器响应,产生中断挂起,在发送或者接收到数据之后,这个中断位会变成1,表示从机已经应答。
(5)下面根据自己需求是选择继续通信还是停止通讯。
(5.1)外部的从机会向接收器发送数据,那么数据被存储在IICDS这个寄存器中,那么接收器读取IIDS之中的数据
(5.1)清除中断挂起位
(5.1)清除中断挂起位后,外部的从机又会发送一个字节给我
(5.1)返回到(5)
(5.2)往IICSTAT寄存器中写入0x90,表示主机发送停止信号
(5.2)清除中断挂起位
(5.2)等待停止信号的结束

IIC控制器中的相关寄存器:

(1)IICCON:主要用于控制IIC寄存器的相关功能
(2)IICSTAT:寄存器的相关功能和状态
(3)IICADD:设置IIC控制器的地址,每个IIC控制器都有一个7位的地址,一般作为从机的使用
(4)IICDS:发送和接收数据的寄存器,接收和发送的数据都放在这个寄存器,因为IIC属于是半双工的,所以接收和发送是不能同时进行的。所以不会产生冲突
(5)IICLC:滤波和延时相关的寄存器
请添加图片描述
(1)IICCON寄存器
请添加图片描述
(2)IICSTAT寄存器
请添加图片描述

(3)IICADD寄存器
请添加图片描述

(4)IICDS寄存器
请添加图片描述

(5)IICLC寄存器
请添加图片描述

软件IIC实现流程:

iic.h头文件的设置:

#include "stm32f10x.h"
#include "io_bit.h"
#include "delay.h"

#define IIC_GPIO_RCC RCC_APB2Periph_GPIOA
#define IIC_GPIO GPIOA
#define IIC_SCL_PIN GPIO_Pin_6		//宏定义第6脚定义为时钟线
#define IIC_SDA_PIN GPIO_Pin_7		//宏定义第7脚为数据线

#define IIC_SCL PAout(6)		//具体到是stm32的那个端口,A端口的第  6  脚,设置为输出模式
#define IIC_SDA PAout(7)		//具体到是stm32的那个端口,A端口的第  7  脚,设置为输出模式
#define IIC_SDA_IN PAin(7)	//同时设置A端口的第7脚为输入模式

void IIC_Pin_Init(void);//引脚初始化

void IIC_Start(void);//IIC开始函数声明
void IIC_Stop(void);//IIC停止
void IIC_Send_Ack(u8 ack);//ACK信号
u8 IIC_Send_Data(u8 data);//无符号字符型,IIC发送数据声明
u8 IIC_Read_Data(void);//无符号字符型,IIC读数据声明

iic.c中程序设计:

#include "iic.h"


void IIC_Pin_Init(void)//IIC引脚的初始化,在头文件中以及预定义了IIC_SCL_PIN  6脚   IIC_SDA_PIN 7脚
{
	GPIO_InitTypeDef GPIO_InitStruct;//结构体声明
	
	RCC_APB2PeriphClockCmd(IIC_GPIO_RCC,ENABLE);//使能外部时钟
	
	//SCL
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;//设置GPIO模式为推挽输出模式
	GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN;//定义引脚类型    IIC_SCL_PIN GPIO_Pin_6
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚的传输速度
	GPIO_Init(IIC_GPIO,&GPIO_InitStruct);//GPIO初始化
	
	//SDA
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;//设置GPIO模式为开漏输出模式
	GPIO_InitStruct.GPIO_Pin = IIC_SDA_PIN;//定义引脚类型    IIC_SDA_PIN GPIO_Pin_7
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//设置引脚的传输速度
	GPIO_Init(IIC_GPIO,&GPIO_InitStruct);//GPIO初始化
}


void IIC_Start(void)//IIC通信开始函数
{
	IIC_SCL = 1;//根据IIC通信协议:先将SCL拉高
	IIC_SDA = 1;//再次将SDA拉低
	delay_us(5);
	IIC_SDA = 0;
	IIC_SCL = 0;//嵌住IIC总线,准备发送或接收数据
	delay_us(5);
}


void IIC_Stop(void)//IIC通信停止函数
{
	IIC_SCL = 0;
	IIC_SDA = 0;
	delay_us(5);
	IIC_SDA = 1;
	IIC_SCL = 1;
	delay_us(5);
	
}

 
void IIC_Send_Ack(u8 ack)//IIC发送应答信号
{
	IIC_SCL = 1;
	//为什么应答信号与非应答信号可以在一个函数中??
	//它们时钟信号相同,数据信号的初始电平不同??  分开写
	
	delay_us(5);
	IIC_SCL = 0;
	delay_us(5);
	
	if(ack)
	{
		IIC_SDA = 1;
	}
	else
	{
		IIC_SDA = 0;
	}
	
	delay_us(5);
	//保持周期完整性
	IIC_SCL = 1;
	delay_us(5);
	IIC_SCL = 0;
}




u8 IIC_Reception_Ack(void)//IIC接收应答信号
{
	IIC_SDA = 1;
	delay_us(5);
	
	IIC_SCL = 0;
	delay_us(5);
	IIC_SCL = 1;
	
	if(IIC_SDA_IN)
	{
		return 1;
	}
	
	return 0;
}
u8 IIC_Send_Data(u8 data)//IIC发送数据
{
	u8 i = 0;
	
//	IIC_Start();
	
	for(i = 0; i < 8; i++)
	{
		//一位一位的发
//		IIC_SCL = 1;
//		delay_us(5);
		IIC_SCL = 0; 
		if(data & 0x80)
		{
			IIC_SDA = 1;
		}
		else
		{
			IIC_SDA = 0;
		}
		data <<= 1;
		delay_us(5);
	//保持周期完整性
		IIC_SCL = 1;
		delay_us(5);
//		IIC_SCL = 0;
	}
	
	IIC_SCL = 0;
	return IIC_Reception_Ack();
	//接收应答
}


u8 IIC_Read_Data(void)//IIC读取数据
{
	u8 data = 0;
	u8 i = 0;
	
	IIC_SDA = 1;
	
	for(i = 0; i < 8; i++)
	{
		IIC_SCL = 0;
		delay_us(5);
		IIC_SCL = 1;
		
		if(IIC_SDA_IN)
		{
			data |= 1;
		}
		
		data <<= 1;
	}
	
	IIC_Send_Ack(1);
	
	return data;
}

以上程序,是哔站up主写的代码,本篇文章属个人学习使用,不得用于各种商业用途。


版权声明:本文为qq_52916060原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。