学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出
文章目录
一、预备知识
(1) I2C通信协议
I2C通信协议是一种用于串行通信的协议,常被用于连接微控制器、传感器、外设等。它使用两根线(SDA和SCL)来进行双向通信。其中,SDA线用于数据传输,而SCL线用于时钟同步。在I2C通信中,所有设备都有一个固定的地址,用于唯一标识设备。主设备可以向从设备发送指令,也可以接收从设备的响应和数据。在传输数据时,每个数据字节都由一个起始位、8位数据位、1位确认位和1位停止位组成。通信过程中,串行数据传输的时钟频率可以在100KHz、400KHz甚至更高的速度下运行。
(2) I2C物理层
I2C通讯设备之间的常用连接方式:
物理层特点:
1.它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯主机及多个通讯从机。
2.一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
3.每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
4.总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
5.多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
(3)I2C时序
整体时序:
上图状态描述:
1.空闲状态:无数据传输,I2C设备不工作;
2.起始状态:等待数据输入,由SDA信号拉低控制;
3.读写状态:数据传输;
4.停止状态:从传输跳转到空闲,由SDA信号拉低控制。
传输状态时序:
串行时钟线SCL高电平期间,采集SDA数据信号,SDA信号保持稳定。在SCL低电平期间,SDA改变数据,以此串行传输数据。
当一个完整字节的指令或数据传输完成,从机设备正确接收到指令或数据后,会通过拉低SDA为低电平,向主机设备发送单比特的应答信号,表示数据或指令写入成功。若从机正确应答,可以结束或开始下一字节数据或指令的传输,否则表明数据或指令写入失败,主机就可以决定是否放弃写入或者重新发起写入
(4)硬件I2C和软件I2C
硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的;
软件I2C一般是用GPIO管脚,用软件控制管脚状态以模拟I2C通信;
二、AHT20温湿度传感器
上面介绍了I2C通信协议,下面介绍温湿度传感器模块,以及怎样实现用I2C通信协议与温湿度传感器模块通信,读取温湿度信息。
通过阅读产品手册,我们可以知道温湿度传感器模块的接口定义:
发送命令和指令集:
在启动传输后,随后传输的I2C首字节包括7位2 的I C设备地址 0x38和一个SDA方向位 x(读R:‘1’,写W:‘0’)。在第8个SCL时钟下降沿之后,通过拉低 SDA引脚 (ACK位),指示传感器数据接收正常。 在发送测量命令0xAC之后,MCU必须等到测量完成。
读取流程:
1.上电后要等待40ms,读取温湿度值之前, 首先要看状态字的校准使能位Bit[3]是否为 1(通过发送0x71可以获取一个字节的状态字),如果不为1,要发送0xBE命令(初始化),此命令参数有两个字节, 第一个字节为0x08,第二字节为0x00,然后等待10ms。
2.直接发送 0xAC命令(触发测量),此命令参数有两个字节,第一个字节为 0x33,第二个字节为0x00。
3.等待80ms待测量完成,如果读取状态字Bit[7]为0,表示测量完成,然后可以连续读取六个字节;否则继续等待。
4.当接收完六个字节后,紧接着下一个字节是CRC校验数据,用户可以根据需要读出,如果接收端需要CRC校验,则在接收完第六个字节后发ACK应答,否则发NACK结束,CRC初始值为0XFF,CRC8校验多项式 CRC[7:0]=1+x + x + x 4 5 8
计算温湿度:
三、CubeMX创建工程相关配置
开启I2C1
开启串口:
时钟配置
工程管理:
生成代码
四、代码编写
(1)自定义编写aht20.h和aht20.c文件
在工程目录/Core/Inc下创建aht20.h头文件
#ifndef INC_AHT20_H_
#define INC_AHT20_H_
#include "i2c.h"
//函数声明
void AHT20_Init(void);
void AHT20_Read(float *Temperature, float *Humidity);
#endif
在工程目录/Core/src下创建aht20.c文件
代码如下:
#include "aht20.h"
#define AHT20_ADDRESS 0x70 //{0x38,0}
uint32_t HAL_MAX_DALAY = 0xffff;
void AHT20_Init(void)
{
uint8_t readBuffer;
HAL_Delay(40);
HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,&readBuffer,1,HAL_MAX_DALAY);
if((readBuffer & 0x08) == 0x00) //判断校验状态字
{
uint8_t sendBuffer[3] = {0xBE,0x08,0x00};
HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DALAY);
}
HAL_Delay(20);
}
void AHT20_Read(float *Temperature, float *Humidity)
{
uint8_t sendBuffer[4] = {0xAC,0x33,0x00,0x71};
uint8_t readBuffer[6];
HAL_I2C_Master_Transmit(&hi2c1,AHT20_ADDRESS,sendBuffer,3,HAL_MAX_DALAY); //发送触发测量命令
HAL_Delay(80);
HAL_I2C_Master_Receive(&hi2c1,AHT20_ADDRESS,readBuffer,6,HAL_MAX_DALAY);
if(readBuffer[0] & 0x80 == 0x00)//判断忙状态bit[7]是否为0;
{
uint32_t data = 0;
//位拼接,提取湿度度数据
data = ((uint32_t)readBuffer[3] >> 4) + ((uint32_t)readBuffer[2] << 4) + ((uint32_t)readBuffer[1] << 12);
*Humidity = data * 100.0f / (1<<20);
data = ((uint32_t)readBuffer[3] & 0x0F << 16) + ((uint32_t)readBuffer[4] << 8) + (uint32_t)readBuffer[5] ;
*Temperature = data * 200.0f / (1<<20) - 50;
}
}
(2)主函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
AHT20_Init();
float temperature , humidity ;
while (1)
{
AHT20_Read(&temperature,&humidity);
printf("温度:%1.f ℃\r\n" ,temperature);
printf("湿度:%1.f %%\r\n" ,humidity);
HAL_Delay(1000);
}
}
五、上板验证
总结
本次实验,学习了iic通信协议,以及实操iic协议与温湿度模块通信,采集环境温湿度信息。观察实验结果,对于温湿度的数据真实性存在怀疑态度,并且会有异常数据,目前正思考原因,后续就针对如何提高数据准确性进行改进。