学习I2C总线通信协议,实现每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)
前言
- 我们为什么要学习I2C通信?
1、Stm32的最常用的板间通信有很多,有I2C、SPI、CAN;I2C通信协议是我们stm32板间通信比较常用的、也是比较简单的;
2、I2C通讯协议
(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯
一、题目要求
- 学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:
(1)解释什么是“软件I2C”和“硬件I2C”? (阅读野火配套教材的第23章“I2C–读写EEPROM”原理章节);
(2)阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)
二、I2C总线通信协议
1. I2C总线简介
1.1 原理图
- 原理图如下(注意上拉电阻):
1.2 特点
-
只需要一根
数据线SDA
和一根时钟线SCL
,SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线
SCL(Serial Clock):串行时钟线,传输CLK信号,一般是主设备向从设备提供
SDA(Serial Data) :串行数据线,传输通信数据 -
实现真正的
多主机总线
,任何器件既可以作为主机又可以作为从机,但是同一时刻只能有一个主机 -
可以通过
外部连线检测
,便于系统故障诊断和调试 -
连接到相同总线上的IC数量
只受总线最大电容的限制
,串行的8位双向数据传输位速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s -
在
总线上消耗的电流很小
,因此,总线上扩展的器件数量主要由电容负载来决定,抗高噪声干扰,增加总线驱动器可以使总线电容扩大10倍,传输距离达到15m;兼容不同电压等级的器件,工作温度范围宽 -
接口电路为开漏输出
,需通过上拉电阻
接电源VCC,当总线空闲时,两根线都是高电平,连接总线的外同器件都是CMOS器件输出级也是开漏电路
1.3 数据传输
- 发送到
SDA线上的每个字节必须为8位
,每次传输可以发送的字节数量不受限制;每个字节后必须跟一个响应位。首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL后数据传输继续:
空闲时候
:SDA数据线和SCL时钟线都是高电平开始信号
:SCL处于高电平,SDA由高到低结束信号
:SCL处于高电平,SDA由低到高应答信号
:当传输完8位数据以后,在第9个SCL时钟周期- 主机释放SDA控制权交给从机,由于上拉电阻的作用,此时该电平为高,但是如果从机正确接受了数据,就会将SDA拉低
- 发送数据:SDA上的数据必须在SCL高电平周期时保持稳定,数据的高低电平翻转变化发生在SCL低电平时期
非应答信号
:如果第9个SCL时钟周期,SDA保持高电平,则代表非应该信号- 非应答信号可能是主机发出的也可能是从机产生的
有几种可能:
1、I2C总线上没有主机所指定地址的从机设备
2、从机正在执行一些操作,处于忙状态,还没有准备好与主机通讯
3、主机发送的一些控制命令,从机不支持
4、主机接收从机数据时,主机产生非应答信号,通知从机数据传输结束,不要再发数据了
1.4 通讯特征
- 1)
串行通信
:所有的数据以位为单位在SDA线上串行传输 - 2)
同步通信
:通过时钟同步 - 3)
非差分
:I2C通信速率不高,且通信距离近,使用电平信号通信 - 4)
低速率
:I2C一般是同一个板子上的两个IC芯片间通信,数据量不大,速率低;速率:几百KHz,速率可能不同,不能超过IC的最高速率
1.5 模式
标准模式
(Standard):100kbps快速模式
(Fast):400kbps快速模式
(Fast-Plus):1Mbps高速模式
(High-speed):3.4Mbps超快模式
(Ultra-Fast):5Mbps(单向传输)
2. I2C通信协议简介
2.1 起始位和结束位
-
I2C总线通讯由起始位开始通讯,由结束位停止通讯,并释放I2C总线。起始位和结束位都由主设备发出
起始位(S)
:在SCL为高电平时,SDA由高电平变为低电平
结束位(P)
:在SCL为高电平时,SDA由低电平变为高电平如下图所示:
2.2 数据格式与应答
-
I2C数据
以字节(即8bits)为单位传输
,每个字节传输完后都会有一个ACK应答信号;应答信号的时钟是由主设备产生的 -
应答(ACK)
:拉低SDA线,并在SCL为高电平期间保持SDA线为低电平
非应答(NOACK)
:不要拉低SDA线(此时SDA线为高电平),并在SCL为高电平期间保持SDA线为高电平 -
在传输期间,如果从设备来不及处理主设备发送的数据,从设备会保持SCL线为低电平,强迫主设备等待从设备释放SCL线,直到从设备处理完后,释放SCL线,接着进行数据传输
如下图所示:
2.3 传输通讯流程
写数据
:
开始数据传输后,先发送一个起始位(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线为高电平时要保持稳定不变
如下图所示:
2.4 I2C物理层
- 物理层的特点:
(1) 它是一个支持设备的总线
。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 I2C 通讯设备,支持多个通讯主机及多个通讯从机;
(2) 一个 I2C 总线只使用两条总线线路,一条双向串行数据线(SDA)
,一条串行时钟线(SCL)
,数据线即用来表示数据,时钟线用于数据收发同步;
(3) 每个连接到总线的设备都有一个独立的地址
,主机可以利用这个地址进行不同设备之间的访问;
(4) 总线通过上拉电阻
接到电源,当 I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平;
(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式
决定由哪个设备占用总线;
(6) 具有三种传输模式:标准模式
传输速率为 100kbit/s ,快速模式
400kbit/s ,高速模式
下可达 3.4Mbit/s,但目前大多 I2C 设备尚不支持高速模式;
(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制
2.5 I2C协议层
-
I2C 的协议定义了
通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播
等环节 -
I2C 通讯过程的
基本结构
:
-
通讯的
起始和停止信号
:
-
数据
有效性
:
-
每次数据传输都以字节为单位,每次传输的字节数不受限制
地址及数据方向
:
-
响应
:
3. 硬件I2C和软件I2C
-
所谓
硬件I2C
对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚
也是专用的;软件I2C一般是用GPIO管脚
,用软件控制管脚状态以模拟I2C通信波形; -
硬件I2C的效率要远高于软件的
,而软件I2C由于不受管脚限制,接口比较灵活; -
模拟I2C 是通过GPIO,软件模拟寄存器的工作方式,而硬件(固件)I2C是直接调用内部寄存器进行配置。如果要从具体硬件上来看,可以去看下芯片手册。因为固件I2C的端口是固定的,所以会有所区别;
-
至于如何区分它们?
1、可以看底层配置
,比如IO口配置,如果配置了IO口的功能(I2C功能)那就是固件I2C,否则就是模拟;
2、可以看I2C写函数
,看里面有木有调用现成的函数或者给某个寄存器赋值,如果有,则肯定是固件I2C功能,没有的话肯定是数据一个bit一个bit模拟发生送的,肯定用到了循环,则为模拟;
3、根据代码量
判断,模拟的代码量肯定比固件的要大:
1)硬件I2C用法
比较复杂,模拟I2C的流程更清楚一些。
2)硬件I2C速度
比模拟快,并且可以用DMA
3)模拟I2C可以在任何管脚
上,而硬件只能在固定管脚上。 -
软件i2c
是程序员使用程序控制SCL,SDA线输出高低电平,模拟i2c协议的时序;一般较硬件i2c稳定,但是程序较为繁琐,但不难 -
硬件i2c
程序员只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出;但是有些单片机的硬件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");
}
- 工程一览:
3. 项目完整代码
- 可跳转至本人的gitee代码仓库查看:
https://gitee.com/baogu_xuxu/embedded-system-development
四、实验结果展示
1. 温湿度采集器引脚一览表
- 引脚切勿连接错误,以免发生危险:
2. 硬件线路连接
2.1 usb to ttl ----> STM32F103C8T6核心开发板
- 3V3 —> 3V3
- GND —> GND
- RXD —> A9
- TXD —> A10
2.2 STM32F103C8T6核心开发板 ----> 温湿度采集器
- 3V3 —> 引脚1
- GND —> 引脚3
- PB6 —> SCL
- PB7 —> SDA
3. 工程文件编译及烧录
- 工程烧录:
- 打开FlyMcu:
4. 结果展示
-
串口结果展示:
-
可以用手握住收集器,观察输出温湿度的变化:
-
视频整体效果展示:
通过I2C实现温湿度(AHT20)采集
总结
通过本次实验课程,掌握了嵌入式系统开发环境下,对于I2C总线通信协议的理解,以及通过I2C协议实现温湿度(AHT20)采集的过程分析;
路途是艰辛的,前途是光明的,在实操过程中,由于代码是在开源基础上进行修正与改进的,所以出现了很多的问题,前前后后进行了不下五次的代码修改与调试,好在最终还是完满达到了实验要求!
同时也期待大家能够积极留言,指出我存在的问题,谢谢!
参考文献:
https://blog.csdn.net/u011727389/article/details/84858378
https://blog.csdn.net/qq_20017379/article/details/120635348