news 2026/6/20 23:01:15

STM32F103C8T6驱动DHT22温湿度传感器,OLED显示完整教程(附避坑指南)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32F103C8T6驱动DHT22温湿度传感器,OLED显示完整教程(附避坑指南)

STM32F103C8T6与DHT22温湿度传感器的实战开发指南

1. 硬件准备与连接

在开始编码之前,确保你已准备好以下硬件组件:

  • STM32F103C8T6最小系统板(蓝色药丸开发板)
  • DHT22温湿度传感器模块
  • 0.96寸OLED显示屏(I2C接口)
  • 杜邦线若干
  • 面包板(可选,方便连接)

硬件连接示意图

设备引脚STM32引脚备注
DHT22 VCC3.3V电源正极
DHT22 GNDGND电源地
DHT22 DATAPB9数据线
OLED VCC3.3V电源正极
OLED GNDGND电源地
OLED SCLPB6I2C时钟线
OLED SDAPB7I2C数据线

注意:DHT22的数据线需要连接一个4.7KΩ上拉电阻到3.3V,大多数模块已经内置此电阻

2. 开发环境配置

2.1 软件工具准备

要完成本项目,你需要:

  1. Keil MDK-ARM(或STM32CubeIDE)
  2. STM32标准外设库(或HAL库)
  3. 串口调试工具(如Putty、Tera Term)
# 示例:使用STM32CubeMX生成项目基础代码 stm32cubemx -m STM32F103C8Tx -p "GPIO,I2C,TIM"

2.2 工程文件结构

建议的项目目录结构:

Project/ ├── CMSIS/ # 核心支持文件 ├── StdPeriph_Driver/ # 标准外设驱动 ├── User/ │ ├── main.c # 主程序 │ ├── dht22.c # DHT22驱动 │ ├── dht22.h # DHT22头文件 │ ├── oled.c # OLED驱动 │ └── oled.h # OLED头文件 └── stm32f10x_conf.h # 库配置文件

3. DHT22驱动开发

3.1 理解DHT22通信协议

DHT22采用单总线协议,通信过程分为三个步骤:

  1. 主机启动信号:拉低数据线至少1ms,然后释放
  2. 从机响应:DHT22拉低80μs,然后拉高80μs
  3. 数据传输:40位数据(16位湿度+16位温度+8位校验和)

时序关键参数

阶段时间要求容错范围
主机启动低电平≥1ms1-20ms
主机释放等待20-40μs-
从机响应低电平80μs75-85μs
从机响应高电平80μs75-85μs
数据位0高电平26-28μs-
数据位1高电平70μs-

3.2 GPIO模拟单总线实现

// dht22.h 头文件关键定义 #ifndef __DHT22_H #define __DHT22_H #include "stm32f10x.h" #define DHT22_GPIO_PORT GPIOB #define DHT22_GPIO_PIN GPIO_Pin_9 #define DHT22_RCC RCC_APB2Periph_GPIOB // 方向控制宏 #define DHT22_INPUT() GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU #define DHT22_OUTPUT() GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP uint8_t DHT22_Init(void); uint8_t DHT22_Read_Data(float *temperature, float *humidity); #endif

3.3 数据读取实现

// dht22.c 核心读取函数 uint8_t DHT22_Read_Data(float *temp, float *humi) { uint8_t buf[5] = {0}; uint8_t retry = 0; // 1. 主机启动信号 DHT22_OUTPUT(); GPIO_ResetBits(DHT22_GPIO_PORT, DHT22_GPIO_PIN); Delay_ms(2); // 拉低至少1ms GPIO_SetBits(DHT22_GPIO_PORT, DHT22_GPIO_PIN); Delay_us(30); // 等待20-40μs // 2. 切换为输入模式等待响应 DHT22_INPUT(); // 3. 检测DHT22响应 while(GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) && retry<100) { retry++; Delay_us(1); } if(retry>=100) return 1; retry = 0; while(!GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) && retry<100) { retry++; Delay_us(1); } if(retry>=100) return 1; // 4. 开始接收40位数据 for(uint8_t i=0; i<5; i++) { for(uint8_t j=0; j<8; j++) { retry = 0; while(!GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) && retry<100) { retry++; Delay_us(1); } Delay_us(40); // 判断高低电平 if(GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN)) { buf[i] |= (1 << (7-j)); while(GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) && retry<100) { retry++; Delay_us(1); } } } } // 5. 校验数据 if(buf[0] + buf[1] + buf[2] + buf[3] == buf[4]) { *humi = (float)((buf[0]<<8)|buf[1]) / 10.0; *temp = (float)((buf[2]<<8)|buf[3]) / 10.0; return 0; } return 1; }

4. OLED显示实现

4.1 I2C初始化配置

// oled.c - I2C初始化 void OLED_I2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStructure; // 1. 使能时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 2. 配置GPIO GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); // 3. 配置I2C I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 400000; // 400kHz I2C_Init(I2C1, &I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); // 4. OLED初始化序列 OLED_WriteCmd(0xAE); // 关闭显示 OLED_WriteCmd(0xD5); // 设置时钟分频 OLED_WriteCmd(0x80); OLED_WriteCmd(0xA8); // 设置多路复用率 OLED_WriteCmd(0x3F); OLED_WriteCmd(0xD3); // 设置显示偏移 OLED_WriteCmd(0x00); // ... 更多初始化命令 OLED_WriteCmd(0xAF); // 开启显示 }

4.2 温湿度显示界面设计

// 显示刷新函数 void OLED_Refresh_Data(float temp, float humi) { char str[16]; // 温度显示 sprintf(str, "Temp: %.1fC", temp); OLED_ShowString(0, 2, (uint8_t *)str, 16); // 湿度显示 sprintf(str, "Humi: %.1f%%", humi); OLED_ShowString(0, 4, (uint8_t *)str, 16); // 添加简单的图形指示 static uint8_t pos = 0; OLED_DrawLine(pos, 63, pos+10, 63, 1); pos = (pos + 5) % 118; }

5. 系统集成与优化

5.1 主程序框架

// main.c #include "stm32f10x.h" #include "dht22.h" #include "oled.h" #include "delay.h" int main(void) { float temperature = 0, humidity = 0; // 硬件初始化 Delay_Init(); DHT22_Init(); OLED_Init(); OLED_Clear(); // 显示初始界面 OLED_ShowString(0, 0, (uint8_t *)"STM32 DHT22 Demo", 16); OLED_ShowString(0, 2, (uint8_t *)"Initializing...", 16); while(1) { if(DHT22_Read_Data(&temperature, &humidity) == 0) { OLED_Refresh_Data(temperature, humidity); } else { OLED_ShowString(0, 6, (uint8_t *)"Read Error!", 16); } Delay_ms(2000); // 2秒更新一次 } }

5.2 常见问题排查

问题1:DHT22无响应

  • 检查电源连接(3.3V-5V)
  • 确认数据线已正确连接且上拉
  • 检查时序是否精确(特别是启动信号)
  • 尝试更换DHT22模块

问题2:OLED显示异常

  • 确认I2C地址是否正确(通常0x78或0x7A)
  • 检查SCL/SDA线是否接反
  • 确保初始化序列完整
  • 尝试降低I2C时钟速度

问题3:数据读取不稳定

  • 增加读取失败重试机制
  • 在数据线靠近DHT22端添加滤波电容(100nF)
  • 避免在读取过程中被中断打断
  • 确保供电稳定(可并联100μF电容)

5.3 性能优化技巧

  1. 精确延时优化

    // 使用SysTick实现微秒级延时 void Delay_us(uint32_t nus) { uint32_t temp; SysTick->LOAD = SystemCoreClock/1000000 * nus; SysTick->VAL = 0x00; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; do { temp = SysTick->CTRL; } while((temp&0x01) && !(temp&(1<<16))); SysTick->CTRL = 0x00; SysTick->VAL = 0x00; }
  2. 低功耗设计

    • 在两次读取之间让MCU进入睡眠模式
    • 使用定时器唤醒代替Delay延时
    • 适当降低系统时钟频率
  3. 数据平滑处理

    #define SAMPLE_SIZE 5 float smooth_temp[SAMPLE_SIZE] = {0}; uint8_t index = 0; float Get_Smooth_Temperature(float new_temp) { smooth_temp[index] = new_temp; index = (index + 1) % SAMPLE_SIZE; float sum = 0; for(uint8_t i=0; i<SAMPLE_SIZE; i++) { sum += smooth_temp[i]; } return sum / SAMPLE_SIZE; }

6. 扩展功能实现

6.1 温湿度数据记录

// 简单的数据记录功能 #define MAX_RECORDS 24 // 记录24组数据 typedef struct { float temp; float humi; uint32_t time; } Record; Record records[MAX_RECORDS]; uint8_t record_index = 0; void Add_Record(float temp, float humi) { records[record_index].temp = temp; records[record_index].humi = humi; records[record_index].time = Get_System_Tick(); // 需要实现获取系统tick的函数 record_index = (record_index + 1) % MAX_RECORDS; } void Display_History() { char str[20]; for(uint8_t i=0; i<8 && i<MAX_RECORDS; i++) { uint8_t idx = (record_index - i - 1) % MAX_RECORDS; sprintf(str, "%2d:%.1fC %.1f%%", i+1, records[idx].temp, records[idx].humi); OLED_ShowString(0, i*2, (uint8_t *)str, 12); } }

6.2 阈值报警功能

// 报警阈值设置 float temp_high_limit = 30.0; float temp_low_limit = 10.0; float humi_high_limit = 80.0; float humi_low_limit = 30.0; void Check_Alarm(float temp, float humi) { static uint8_t alarm_state = 0; uint8_t new_alarm = 0; if(temp > temp_high_limit) { new_alarm |= 0x01; OLED_ShowString(90, 0, (uint8_t *)"HOT!", 12); } if(temp < temp_low_limit) { new_alarm |= 0x02; OLED_ShowString(90, 0, (uint8_t *)"COLD", 12); } if(humi > humi_high_limit) { new_alarm |= 0x04; OLED_ShowString(90, 2, (uint8_t *)"WET!", 12); } if(humi < humi_low_limit) { new_alarm |= 0x08; OLED_ShowString(90, 2, (uint8_t *)"DRY!", 12); } if(new_alarm && !alarm_state) { // 触发报警动作(如点亮LED、蜂鸣器等) GPIO_SetBits(GPIOA, GPIO_Pin_0); } else if(!new_alarm && alarm_state) { // 关闭报警 GPIO_ResetBits(GPIOA, GPIO_Pin_0); } alarm_state = new_alarm; }

6.3 通过串口输出数据

// 串口初始化 void USART_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // 配置TX(PA9) RX(PA10) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = 115200; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_Cmd(USART1, ENABLE); } // 串口发送数据 void Send_Sensor_Data(float temp, float humi) { char buffer[64]; int len = sprintf(buffer, "Temperature: %.1fC, Humidity: %.1f%%\r\n", temp, humi); for(int i=0; i<len; i++) { USART_SendData(USART1, buffer[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/20 14:56:16

Gemini 写作能力测评:事实密度、风格一致性与可读性评分

最近不少开发者开始把 Gemini 用在技术博客、产品文档、方案说明和知识整理里。相比单纯聊天&#xff0c;写作更考验模型的综合能力&#xff1a;它既要能组织信息&#xff0c;又要保证表达稳定&#xff0c;还不能写得太空。我这次从实战角度做了一轮体验&#xff0c;并借助 AI模…

作者头像 李华
网站建设 2026/6/20 23:01:13

Alien Swarm《异星虫群》: Reactive Drop 专用服务器搭建教程

Alien Swarm《异星虫群》: Reactive Drop 专用服务器搭建教程 Alien Swarm: Reactive Drop 是 Steam 平台上完全免费的俯视角合作射击游戏&#xff0c;由 Reactive Drop Team 在 Valve 原版 Alien Swarm 基础上大幅扩展开发。游戏支持最多 8 人同时联机&#xff0c;玩家组成特种…

作者头像 李华
网站建设 2026/6/20 22:57:14

Fooocus:零门槛AI绘画神器,3步生成专业级艺术图像

Fooocus&#xff1a;零门槛AI绘画神器&#xff0c;3步生成专业级艺术图像 【免费下载链接】Fooocus Focus on prompting and generating 项目地址: https://gitcode.com/GitHub_Trending/fo/Fooocus 想要体验AI绘画的魅力&#xff0c;却担心复杂的参数设置和繁琐的安装过…

作者头像 李华
网站建设 2026/5/20 14:56:00

KMS智能激活工具:3分钟永久激活Windows和Office的完整指南

KMS智能激活工具&#xff1a;3分钟永久激活Windows和Office的完整指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统频繁弹出激活提示而烦恼吗&#xff1f;Office文档突然变…

作者头像 李华