文章目录
一.I2C总线通信协议
1.I2C简介
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
I2C模块接收和发送数据,并将数据从串行转换成并行,或并行转换成串行。可以开启或禁止中断。接口通过数据引脚(SDA)和时钟引脚(SCL)连接到I2C总线。允许连接到标准(高达100kHz)或快速(高达400kHz)的I2C总线。
I2C通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
- 注意:IIC是为了与低速设备通信而发明的,所以IIC的传输速率比不上SPI
2.I2C物理层
I2C 总线在物理连接上非常简单,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。通信原理是通过对SCL和SDA线高低电平时序的控制,来产生I2C总线协议所需要的信号进行数据的传递。在总线空闲状态时,SCL和SDA被上拉电阻Rp拉高,使SDA和SCL线都保持高电平。
I2C通信方式为半双工,只有一根SDA线,同一时间只可以单向通信,485也为半双工,SPI和uart通信为全双工。
主机和从机的概念:主机就是负责整个系统的任务协调与分配,从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。
3.I2C协议层
主要是定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等。
I2C 总线在传送数据过程中共有三种类型信号, 它们分别是:开始信号、结束信号和应答信号。
- 开始信号SCL 为高电平时,SDA 由高电平向低电平跳变,开始传送数据。
- 结束信号SCL 为高电平时,SDA 由低电平向高电平跳变,结束传送数据。
- 应答信号接收数据的 IC 在接收到 8bit 数据后,向发送数据的 IC 发出特定的低电平脉冲,表示已收到数据。CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。若未收到应答信号,由判断为受控单元出现故障。
其中起始信号是必需的,结束信号和应答信号,都可以不要。
4.I2C模式选择
接口可以下述4种模式中的一种运行:
● 从发送器模式
● 从接收器模式
● 主发送器模式
● 主接收器模式
该模块默认地工作于从模式。接口在生成起始条件后自动地从从模式切换到主模式;当仲裁丢失或产生停止信号时,则从主模式切换到从模式。允许多主机功能。
空闲时候
:SDA数据线和SCL时钟线都是高电平
开始信号
:SCL处于高电平,SDA由高到低
结束信号
:SCL处于高电平,SDA由低到高
应答信号
:当传输完8位数据以后,在第9个SCL时钟周期
5.I2C特点
只需要一根数据线SDA和一根时钟线SCL,SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线。
SCL(Serial Clock)
:串行时钟线,传输CLK信号,一般是主设备向从设备提供
SDA(Serial Data)
:串行数据线,传输通信数据
通常我们为了方便把IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备。
- IIC主设备功能:主要产生时钟,产生起始信号和停止信号
- IIC从设备功能:可编程的IIC地址检测,停止位检测
- IIC的一个优点是它支持多主控(multimastering),
其中任何一个能够进行发送和接收的设备都可以成为主总线。一个主控能够控制信号的传输和时钟频率。当然,在任何时间点上只能有一个主控。 - 支持不同速率的通讯速度,标准速度(最高速度100kHZ),快速(最高400kHZ)
- SCL和SDA都需要接上拉电阻 (大小由速度和容性负载决定一般在3.3K-10K之间) 保证数据的稳定性,减少干扰。
- IIC是半双工,而不是全双工 ,同一时间只可以单向通信。
- 为了避免总线信号的混乱,要求各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出。
6.IIC传输通讯流程
-
写数据
:开始数据传输后,先发送一个起始位(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据,该读写标志位决定数据的传输方向),然后,主设备释放SDA线,并等待从设备的应答信号(ACK)。每一个字节数据的传输都要跟一个应答信号位。数据传输以停止位(P)结束,并且释放I2C总线 -
读数据
:开始通讯时,主设备先发送一个起始信号(S),主设备发送一个地址数据(由7bit的从设备地址,和最低位的写标志位组成的8bit字节数据),然后,主设备释放SDA线,并等待从设备的应答信号(ACK),从设备应答主设备后,主设备再发送要读取的寄存器地址,从设备应答主设备(ACK),主设备再次发送起始信号(Sr),主设备发送设备地址(包含读标志),从设备应答主设备,并将该寄存器的值发送给主设备 -
读取单字节数据
:主设备要读取的数据,如果是只有一个字节的数值,就要结束应答,主设备要先发送一个非应答信号(NOACK),再发送结束信号(P) -
读取多字节数据
:主设备要读取的数据,如果是大于一个字节的多个数据,就发送ACK应答信号(ACK),而不是非应答信号(NOACK),然后主设备再次接收从设备发送的数据,依次类推,直到主设备读取的数值是最后一个字节数据后,需要主设备给从设备发送非应答信号(NOACK),再发送结束信号(P),结束I2C通讯,并释放I2C总线 -
注意:所有的数据传输过程中,SDA线的电平变化必须在SCL为低电平时进行,SDA线的电平在SCL线为高电平时要保持稳定不变
7.软件IIC和硬件IIC
- 硬件I2C
直接利用 STM32 芯片中的硬件 I2C 外设。一块硬件电路,硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,硬件(固件)I2C是直接调用内部寄存器进行配置。
使用
: 只要配置好对应的寄存器,外设就会产生标准串口协议的时序。在初始化好 I2C 外设后,只需要把某寄存器位置 1,此时外设就会控制对应的 SCL 及 SDA 线自动产生 I2C 起始信号,不需要内核直接控制引脚的电平。
- 软件I2C
直接使用 CPU 内核按照 I2C 协议的要求控制 GPIO 输出高低电平,从而模拟I2C。
使用
: 需要在控制产生 I2C 的起始信号时,控制作为SCL 线的 GPIO 引脚输出高电平,然后控制作为 SDA 线的 GPIO 引脚在此期间完成由高电平至低电平的切换,最后再控制SCL线切换为低电平,这样就输出了一个标准的 I2C 起始信号。
-
两者的差别
硬件 I2C 直接使用外设来控制引脚,可以减轻 CPU 的负担。不过使用硬件I2C 时必须使用某些固定的引脚作为 SCL 和 SDA,软件模拟 I2C 则可以使用任意 GPIO 引脚,相对比较灵活。对于硬件I2C用法比较复杂,软件I2C的流程更清楚一些。如果要详细了解I2C的协议,使用软件I2C可能更好的理解这个过程。在使用I2C过程,硬件I2C可能通信更加快,更加稳定。 -
区分方式
1、可以看底层配置,比如IO口配置,如果配置了IO口的功能(I2C功能)那就是固件I2C,否则就是模拟;
2、可以看I2C写函数,看里面有木有调用现成的函数或者给某个寄存器赋值,如果有,则肯定是固件I2C功能,没有的话肯定是数据一个bit一个bit模拟发生送的,肯定用到了循环,则为模拟;
3、根据代码量判断,模拟的代码量肯定比固件的要大: 1)硬件I2C用法比较复杂,模拟I2C的流程更清楚一些。
2)硬件I2C速度比模拟快,并且可以用DMA 3)模拟I2C可以在任何管脚上,而硬件只能在固定管脚上。
二.STM32基于I2C协议的温湿度传感器的数据采集
1.题目要求
阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)。
2.代码分析
可以根据其他公司提供的示例代码进行修改与代码添加。(如正点原子或者野火)
相关的AHT20资料大家可以去官网进行查看与下载http://www.aosong.com/class-36.html
- ①AHT20芯片的使用过程
read_AHT20_once
函数
void read_AHT20_once(void)
{
delay_ms(10);
reset_AHT20();//重置AHT20芯片
delay_ms(10);
init_AHT20();//初始化AHT20芯片
delay_ms(10);
startMeasure_AHT20();//开始测试AHT20芯片
delay_ms(80);
read_AHT20();//读取AHT20采集的到的数据
delay_ms(5);
}
②AHT20芯片读取数据 read_AHT20
函数
void read_AHT20(void)
{
uint8_t i;
for(i=0; i<6; i++)
{
readByte[i]=0;
}
I2C_Start();//I2C启动
I2C_WriteByte(0x71);//I2C写数据
ack_status = Receive_ACK();//收到的应答信息
readByte[0]= I2C_ReadByte();//I2C读取数据
Send_ACK();//发送应答信息
readByte[1]= I2C_ReadByte();
Send_ACK();
readByte[2]= I2C_ReadByte();
Send_ACK();
readByte[3]= I2C_ReadByte();
Send_ACK();
readByte[4]= I2C_ReadByte();
Send_ACK();
readByte[5]= I2C_ReadByte();
SendNot_Ack();
//Send_ACK();
I2C_Stop();//I2C停止函数
//判断读取到的第一个字节是不是0x08,0x08是该芯片读取流程中规定的,如果读取过程没有问题,就对读到的数据进行相应的处理
if( (readByte[0] & 0x68) == 0x08 )
{
H1 = readByte[1];
H1 = (H1<<8) | readByte[2];
H1 = (H1<<8) | readByte[3];
H1 = H1>>4;
H1 = (H1*1000)/1024/1024;
T1 = readByte[3];
T1 = T1 & 0x0000000F;
T1 = (T1<<8) | readByte[4];
T1 = (T1<<8) | readByte[5];
T1 = (T1*2000)/1024/1024 - 500;
AHT20_OutData[0] = (H1>>8) & 0x000000FF;
AHT20_OutData[1] = H1 & 0x000000FF;
AHT20_OutData[2] = (T1>>8) & 0x000000FF;
AHT20_OutData[3] = T1 & 0x000000FF;
}
else
{
AHT20_OutData[0] = 0xFF;
AHT20_OutData[1] = 0xFF;
AHT20_OutData[2] = 0xFF;
AHT20_OutData[3] = 0xFF;
printf("读取失败!!!");
}
printf("\r\n");
//根据AHT20芯片中,温度和湿度的计算公式,得到最终的结果,通过串口显示
printf("温度:%d%d.%d",T1/100,(T1/10)%10,T1%10);
printf("湿度:%d%d.%d",H1/100,(H1/10)%10,H1%10);
printf("\r\n");
}
完整代码可点此工程代码
三.硬件连接
关于本实验我们采用的软件I2C实现,分析代码可以发现连接如下
- SCL–>PB6
- SDA–>PB7
如果采用硬件I2C进行实现,可以查看关于STM32的原理图,可以看到硬件I2C接口。图为正点原子stm32精英版原理图
四.结果演示
温湿度采集器实物图
这里笔者利用的是XCOM串口调试助手,设置好波特率115200,打开串口即可观察到数据。
然后可以将手放在温湿度传感器上,可以观察到下一次温度与湿度都发生了变化,证明此次实验成功啦~
五.总结与分析
笔者在一开始想过自己建立基本工程进行配置但到后期发现其中的一些模板还是有所不同,之后又通过查阅资料请教同学才最终完成该实验。
笔者在最开始进行实验时发现温度显示为0出错,而后发现是最开始的基础工程文件的芯片型号与笔者的C8T6没有完全匹配,在更改型号、启动文件等影响因素后顺利完成了这次实验。本次实验应注意不要将引脚插错,否则可能会出现一些不好的结果,线路要注意稳定性否则可能也达不到需要的效果。另外为了保证数据可靠地传送,任一时刻总线只能由某一台主机控制,各微处理器应该在总线空闲时发送数据。
笔者也是通过此次实验才使用了温湿度传感器,感觉还是很神奇,并且了解了I2C协议感觉受益匪浅,很有意义。
参考
1.I2C总线通信协议及实操stm32通过I2C实现温湿度(AHT20)采集
2.IIC原理超详细讲解—值得一看
3.stm32通过I2C接口实现温湿度(AHT20)的采集