IIC通信(解决各传感器怎么接线的问题)
讲到IIC通讯就顺便讲讲串口通讯吧(以前上FPGA课程的资料)
处理器和外部设备通讯的两种方式(并行通信和串行通信)
并行通信
是指数据的各个位用多条数据线同时进行传输,传输速度很快,但是引脚资源占用较多

串行通信
将数据分为一位一位的形式在一条数据线上逐个传输,传输速度慢,引脚占用少

串行通信的通信方式:同步通信和异步通信
同步通讯
带时钟同步信号的数据传输;发送设备和接收设备在同一时钟的控制下,进行 同步传输数据

异步通信
不带时钟同步信号的数据传输,发送设备和接收设备使用各自的时钟控制数据 的发送和接收过程。


常见的串行通信接口:UART、SPI、IIC(三大串行总线协议)

UART:(Universal Asynchronous Receiver-Transmitter:通用异步收发器) 功能:在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行 数据转成成并行数据。 协议层:通信协议(包括数据格式、数据传输速率等...) 物理层:接口类型、电平标准等...

协议层:通信协议(数据格式)

SPI:(Serial Peripheral Interface:串行外设接口)**此处以FPGA芯片为例


IIC:(Inter-Integrated Circuit:集成电路总线):半双工**此处以FPGA芯片为例


IIC(Inter-Integrated Circuit)又称I2C(习惯读“I方C”),是IICBus简称,中文名为集成电路总线,它是一种串行通信总线,使用多主从架构,由飞利浦公司在1980年代为了让主板、嵌入式系统或手机用以连接低速周边设备而发展。适用于IC间的短距离数据传输。
IIC是单片机最常用的通讯方式之一,如果单片机需要扩展EEPROM存储芯片,就会用到IIC通信来读写EEPROM的数据。
除此很多传感器(如温湿度传感器,加速度传感器等)、外设(如OLED屏幕、LCD显示板等)也会采用IIC与单片机进行数据的传输。

硬件IIC:我们知道,STM32外设丰富,包括硬件级的IIC通信,也就是在其内部集成有专门用于IIC通信的硬件模块,只要在程序中配置相关的参数,就能启用该模块,再通过官方已经做好的库函数,就能很容易的实现IIC通信了。
但是由于STM32的硬件IIC存在着应答信号卡死的问题,所以很多开发者更喜欢使用软件模拟IIC通信。另外,软件模拟IIC还有方便移植到其他设备的优点。
IC物理接口:IIC串行总线有两根信号线,一根是双向的数据线SDA,另一根是时钟线SCL。所有接到I2C总线设备上的串行数据SDA都接到总线的SDA上,各设备的时钟线SCL接到总线的SCL上,属于一主多从总线。主机一次只能跟一个从机通信,如果要跟多个从机传输数据,则需要采用轮询的方式。通常会给SDA和SCL外接上拉电阻,确保无数据传输时,总线处于闲置状态。但是不接也可以,可以使用STM32的片内上拉电阻。

这两根线中,SCL是时钟线,用来同步时钟信号;SDA是数据线,用来传输数据。
但对于软件模拟IIC,就没有任何限制,可以定义任意两个IO口分别作为SCL与SDA。
定义SCL与SDA的引脚,以及两个引脚置高、置低的函数。
#define IIC_SCL_H() GPIO_SetBits(GPIOA,GPIO_Pin_1) //定义SCL置高为PA1置位
#define IIC_SCL_L() GPIO_ResetBits(GPIOA,GPIO_Pin_1) //定义SCL置低为PA1复位
#define IIC_SDA_H() GPIO_SetBits(GPIOA,GPIO_Pin_2) //定义SDA置高为PA2置位
#define IIC_SDA_L() GPIO_ResetBits(GPIOA,GPIO_Pin_2) //定义SDA置低为PA2复位
延时函数:
void Delay_us(int Time)
{
int i=0;
while(Time--)
{
i=8;
while(i--);
}
return;
}
由于SDA需要在输入输出模式之间切换(等待应答时需要将PA2切换成输入模式),所以还要定义这两个函数。输出模式用推挽输出即可,输入模式采用上拉输入模式(如果总线外接了上拉电阻,采用浮空输入也可以)。
void SDA_OUT(void) //SDA输出模式
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式 GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void SDA_IN(void) //SDA输入模式
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入模式
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
起始信号:
void IIC_Start(void)
{
SDA_OUT();
//SDA切换至输出模式
IIC_SDA_H(); //SDA置高
IIC_SCL_H();
//SCL置高
Delay_us(1);
IIC_SDA_L(); //SDA置低
Delay_us(1);
}
停止信号:
void IIC_Stop(void)
{
IIC_SCL_H(); //SCL置高
IIC_SDA_L(); //SDA置低
Delay_us(1);
IIC_SDA_H(); //SDA置高
Delay_us(1);
}
数据帧:
void IIC_WriteByte(u8 data) //发送一个数据串,data为要发送的数据
{
u8 i;
SDA_OUT(); //SDA设置为输出模式
for(i=0;i<8;i++) //循环发送,每次循环发送1bit,循环8次就是1Byte
{
IIC_SCL_L(); //SCL置低
if(data & 0x80) //如果Data的第八位是高电平
IIC_SDA_H(); //SDA置高
else
IIC_SDA_L(); //SDA置低
IIC_SCL_H(); //SCL置高
Delay_us(1); //延时
data<<=1; //data左移一位
}
}
应答信号:
u8 IIC_Wait_Ask(void)
{
int count=0;
IIC_SCL_L(); //SCL置低
Delay_us(1); //延时
IIC_SDA_H(); //SDA置高
SDA_IN(); //SDA切换成输入模式
IIC_SCL_H(); //SCL置高
Delay_us(1); //延时,等待从机发出应答信号
while(Read_IIC_SDA) //如果SDA为低电平
{
count++; //开始计数
if(count>250) //计数超过250,则认为数据发送失败,从机无响应
{
IIC_Stop(); //发出停止信号
return 1; //函数返回1
}
}
IIC_SCL_L(); //如果SDA为高电平,则认为收到应答,
SCL置低
Delay_us(1);
return 0; //函数返回0
}

文章参考:https://blog.csdn.net/qq_55203246/article/details/123944252