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 软件工具准备要完成本项目你需要Keil MDK-ARM或STM32CubeIDESTM32标准外设库或HAL库串口调试工具如Putty、Tera Term# 示例使用STM32CubeMX生成项目基础代码 stm32cubemx -m STM32F103C8Tx -p GPIO,I2C,TIM2.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采用单总线协议通信过程分为三个步骤主机启动信号拉低数据线至少1ms然后释放从机响应DHT22拉低80μs然后拉高80μs数据传输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); #endif3.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) retry100) { retry; Delay_us(1); } if(retry100) return 1; retry 0; while(!GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) retry100) { retry; Delay_us(1); } if(retry100) return 1; // 4. 开始接收40位数据 for(uint8_t i0; i5; i) { for(uint8_t j0; j8; j) { retry 0; while(!GPIO_ReadInputDataBit(DHT22_GPIO_PORT, DHT22_GPIO_PIN) retry100) { 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) retry100) { 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, pos10, 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 常见问题排查问题1DHT22无响应检查电源连接3.3V-5V确认数据线已正确连接且上拉检查时序是否精确特别是启动信号尝试更换DHT22模块问题2OLED显示异常确认I2C地址是否正确通常0x78或0x7A检查SCL/SDA线是否接反确保初始化序列完整尝试降低I2C时钟速度问题3数据读取不稳定增加读取失败重试机制在数据线靠近DHT22端添加滤波电容100nF避免在读取过程中被中断打断确保供电稳定可并联100μF电容5.3 性能优化技巧精确延时优化// 使用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((temp0x01) !(temp(116))); SysTick-CTRL 0x00; SysTick-VAL 0x00; }低功耗设计在两次读取之间让MCU进入睡眠模式使用定时器唤醒代替Delay延时适当降低系统时钟频率数据平滑处理#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 i0; iSAMPLE_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 i0; i8 iMAX_RECORDS; i) { uint8_t idx (record_index - i - 1) % MAX_RECORDS; sprintf(str, %2d:%.1fC %.1f%%, i1, 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 i0; ilen; i) { USART_SendData(USART1, buffer[i]); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); } }