STM32 —— IIC 读取 ATH20(DTH20)温度传感器
STM32 —— IIC 读取 ATH20(DTH20)温度传感器
实验目的
学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出。具体任务:
阅读AHT20数据手册,编程实现:每隔2秒钟采集一次温湿度数据,并通过串口发送到上位机(win10)
实验原理
具体的实验原理可以看我的另外两篇博客:
这里我们只需要根据前面讲过的调用驱动代码中的函数对传感器进行调用即可
HAL 库方法
CubeMX 项目配置
RCC 配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121232749180-450140917.png)
SYS 配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121232830025-1713479017.png)
USART 配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121232910860-1697352474.png)
IIC 配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121232950014-692333895.png)
时钟配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121233111450-674372832.png)
引脚配置
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221121233057474-311048940.png)
修改官方历程(标准库 -> HAL 库)
由于官方历程给出的是基于标准库的函数,所以这里需要进行修改(文末提供官方历程下载连接),如果不像进行修改,可以z直接下载修改后的,基于 HAL 库的驱动源代码文件
引入头文件
首先我们需要在 ATH20.c 中引入如下头文件:
#include "main.h"
#include "AHT20.h"
#include "gpio.h"
#include "i2c.h"
然后将 ATH20.h 中引用的 stm32f10x.h 改为 HAL 库中的 stm32f1xx_HAL.h 使用即可
这样我们才能正常的嗲用基于 HAL 库的函数
修改延时函数
官方历程函数如下:
修改后代码如下:
void AHT20_Clock_Init(void) //延时函数
{
//RCC_APB2PeriphClockCmd(CC_APB2Periph_GPIOB,ENABLE);
__HAL_RCC_GPIOA_CLK_ENABLE();
}
修改 SDA 引脚对应控制函数
官方例程原函数如下:
修改后代码如下:
void SDA_Pin_Output_High(void) //将PB7配置为输出 , 并设置为高电平, PB15作为I2C的SDA
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);
}
void SDA_Pin_Output_Low(void) //将PB7配置为输出 并设置为低电平
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_RESET);
}
void SDA_Pin_IN_FLOATING(void) //SDA配置为浮空输入
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;// 浮空
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init( GPIOB,&GPIO_InitStruct);
}
这里我们配置好的 SDA 输出引脚是 PB7 ,而官方例程中给出的是 PB15 ,所以这里需要将 SDA 对应引脚改为 PB7 ,然后再将函数中标准库的用法改为对应的 HAL 库中的用法即可
修改 SCL 引脚对应控制函数
官方例程原函数如下:
修改后代码如下:
void SCL_Pin_Output_High(void) //SCL输出高电平,P14作为I2C的SCL
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_SET);
}
void SCL_Pin_Output_Low(void) //SCL输出低电平
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_6,GPIO_PIN_RESET);
}
这里我们配置好的 SCL 输出引脚是 PB6 ,而官方例程中给出的是 PB14 ,所以这里需要将 SDA 对应引脚改为 PB7 ,然后再将函数中标准库的用法改为对应的 HAL 库中的用法即可
修改初始化 I2C 接口函数
官方例程原函数如下:
修改后代码如下:
void Init_I2C_Sensor_Port(void) //初始化I2C接口,输出为高电平
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_7;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);//输出高电平
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB,& GPIO_InitStruct);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_7,GPIO_PIN_SET);//输出高电平
}
修改其他代码
后面会有很多处由于使用标准库代码或引脚定义问题而报错的地方,我们只需要将对应引脚修改为我们在 CubeMX 中配置的引脚即可,然后我们将标准库中对应的函数修改为 HAL 库对应的函数即可,下面给出修改的位置和 HAL 库中对应的函数:
//修改前标准库函数:
GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)
//修改后 HAL 库对应函数
HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_7)
然后我们需要将官方历程中给出的 AHT20.c 源文件中的主函数注释掉即可使用
代码设计
首先我们对官方历程中给出的 main 函数进行分析:
int32_t main(void)
{
uint32_t CT_data[2];
volatile int c1,t1;
/***********************************************************************************/
/**///①刚上电,产品芯片内部就绪需要时间,延时100~500ms,建议500ms
/***********************************************************************************/
Delay_1ms(500);
/***********************************************************************************/
/**///②上电第一次发0x71读取状态字,判断状态字是否为0x18,如果不是0x18,进行寄存器初始化
/***********************************************************************************/
if((AHT20_Read_Status()&0x18)!=0x18)
{
AHT20_Start_Init(); //重新初始化寄存器
Delay_1ms(10);
}
/***********************************************************************************/
/**///③根据客户自己需求发测量命令读取温湿度数据,当前while(1)循环发测量命令读取温湿度数据,仅供参考
/***********************************************************************************/
while(1)
{
AHT20_Read_CTdata(CT_data); //不经过CRC校验,直接读取AHT20的温度和湿度数据 推荐每隔大于1S读一次
//AHT20_Read_CTdata_crc(CT_data); //crc校验后,读取AHT20的温度和湿度数据
c1 = CT_data[0]*100*10/1024/1024; //计算得到湿度值c1(放大了10倍)
t1 = CT_data[1]*200*10/1024/1024-500;//计算得到温度值t1(放大了10倍)
////下一步客户处理显示数据,
}
}
这里已经明确的给出了只要我们先对温度传感器 AHT20 对应引脚和 I2c 进行初始化,然后在循环中调用温湿度读取函数并进行显示即可
代码如下:
我们这里需要引用两个头文件:
#include <AHT20.h>
#include <stdio.h>
首先我们这里使用 printf 进行输出,所以这里需要重写 fputc 函数
int fputc(int ch,FILE *f)
{
HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
//等待发送结束
while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET){
}
return ch;
}
后面我们直接按照官方例程去写就可以,main 函数代码如下:
int main(){
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_I2C1_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
uint32_t CT_data[2]={0,0}; //
volatile int c1,t1;
HAL_Delay(50);
/***********************************************************************************/
/**///①刚上电,产品芯片内部就绪需要时间,延时100~500ms,建议500ms
/***********************************************************************************/
AHT20_Init();
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET); // 使用 PC13 引脚上的板载小灯泡进行测试
while (1)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET); // 使用 PC13 引脚上的板载小灯泡进行测试——小灯泡亮
AHT20_Read_CTdata_crc(CT_data); //经过CRC校验,读取AHT20的温度和湿度数据 推荐每隔大于1S读一次
c1 = CT_data[0]*1000/1024/1024; //计算得到湿度值c1(放大了10倍)
t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
printf("正在检测");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
printf("\r\n");
printf("温度:%d%d.%d",t1/100,(t1/10)%10,t1%10); // 这里需要对温度进行计算后才能得到我们需要的温度值
printf("湿度:%d%d.%d",c1/100,(c1/10)%10,c1%10); // 这里同样需要对适度进行计算
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET); // 使用 PC13 引脚上的板载小灯泡进行测试——小灯泡灭
printf("\r\n");
printf("等待");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
HAL_Delay(100);
printf(".");
printf("\r\n");
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
标准库方法
标准库方法与 HAL 库方法中使用的读取 AHT20 温湿度传感器的函数相同,唯一不同的就是需要使用到标准库中,这里按照标准库的格式进行写即可,这里不进行过多介绍 ,可以官方给出的函数,就是基于标准库的函数。
运行测试
虚拟串口测试
这里只能够进行输出的测试,由于要使用外设 AHT20 传感器,并不能够测试是否正确调用传感器相关函数:
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221122091107211-945080553.png)
由图可见输出正常
Proteus 仿真模拟
这里由于需要使用 AHT20 传感器外设,所以无法使用 Proteus 进行仿真
接线示例
HAL 库接线示例
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221120012440435-758138043.jpg)
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221122093826141-783857088.jpg)
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221122093831016-567684948.jpg)
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221122093836516-113729915.jpg)
标准库接线示例
运行结果
![](https://img2022.cnblogs.com/blog/2829072/202211/2829072-20221122093136259-1387715839.gif)
结果分析
通过 HAL 库代码可以实现成功连接外设 AHT20 传感器,并且可以获取指定参数通过 USART 传输回上位机
错误解决方法
报错:
有些在第一次进行编译的时候会出现很多报错,那是因为 ATH20 的官方示例是基于 标准库给出的,所以这里要将标准库函数的语句改为 HAL 库中对应的语句
错误解决方法:
按照博客前面的内容,修改相关代码即可
报错:
IIC_ATH_CubeMX\IIC_ATH_CubeMX.axf: Error: L6200E: Symbol __ARM_use_no_argv multiply defined (by aht20.o and main.o).
错误解决方法:
这个报错说明是我么的代码中出现了两个或多个同名除或注释掉我们不需要的函数即可
注意,这里官方历程中也给出了一个 main 函数,我们需要删除或注释掉,否则会发生此报错
参考资料
推荐:
这篇文章给出了标准库驱动文件转为 HAL 库驱动代码的思路