1. 智能家居环境监测系统
任务目标
构建一个基于STM32F407嵌入式平台的温湿度与光照监测系统:读取DHT11温湿度传感器和光敏电阻模块的模拟量,经ADC采集后换算为实际物理量,最终在LCD1602上实时刷新显示。系统具备越限指示功能,当温度或湿度超出设定阈值时点亮LED告警。
硬件与依赖
- 主控:STM32F407VET6开发板(HAL库)
- 传感器:DHT11(单总线协议)、光敏电阻模块(AO输出接PA0)
- 显示:LCD1602(I2C接口,地址0x27,PCF8574背板)
- 告警:LED接PB0(高电平点亮)
- 时钟:外部8MHz晶振,系统主频168MHz
- CubeMX配置:PA0为ADC1_IN0;PB0推挽输出;I2C1(PB8/PB9)速率400kHz;DHT11数据线接PB10(开漏输出上拉)
核心实现
// main.c 关键代码(HAL库)
#include "main.h"
#include "dht11.h"
#include "lcd1602_i2c.h"
#include <stdio.h>
ADC_HandleTypeDef hadc1;
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2; // 用于微秒延时
#define LIGHT_CHANNEL ADC_CHANNEL_0
#define TEMP_HIGH_ALERT 30.0f
#define HUMI_HIGH_ALERT 70.0f
static void ADC_Init(void) {
__HAL_RCC_ADC1_CLK_ENABLE();
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
}
static uint16_t ADC_Read(void) {
ADC_ChannelConfTypeDef sConfig = {0};
sConfig.Channel = LIGHT_CHANNEL;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_84CYCLES;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
return HAL_ADC_GetValue(&hadc1);
}
// 光敏电阻分压:Vout = VCC * R_photo / (R_fixed + R_photo)
// 光照越强,光敏电阻阻值越小,ADC电压越低
static float Light_Convert(uint16_t adc_val) {
float voltage = (adc_val / 4095.0f) * 3.3f;
// 假设固定电阻10k,VCC=3.3V,反推光敏电阻阻值
float r_photo = (voltage * 10000.0f) / (3.3f - voltage);
// 阻值越小光照越强,映射到0-100%光照强度(经验公式)
float lux = 100.0f * (10000.0f / (r_photo + 10000.0f));
return lux;
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 168MHz
ADC_Init();
DHT11_Init(&htim2, GPIOB, GPIO_PIN_10);
LCD1602_Init(&hi2c1);
GPIO_InitTypeDef gpio = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
gpio.Pin = GPIO_PIN_0;
gpio.Mode = GPIO_MODE_OUTPUT_PP;
HAL_GPIO_Init(GPIOB, &gpio);
LCD1602_Clear();
LCD1602_SetCursor(0, 0);
LCD1602_PrintString("Env Monitor v1.0");
uint8_t temp, humi;
char line1[16], line2[16];
while (1) {
if (DHT11_Read(&temp, &humi) == DHT11_OK) {
uint16_t light_adc = ADC_Read();
float light = Light_Convert(light_adc);
snprintf(line1, 16, "T:%d.%dC H:%d%%",
temp / 10, temp % 10, humi);
snprintf(line2, 16, "Lux:%5.1f%%", light);
LCD1602_SetCursor(0, 0);
LCD1602_PrintString(line1);
LCD1602_SetCursor(0, 1);
LCD1602_PrintString(line2);
// 越限告警
float temp_f = temp / 10.0f;
if (temp_f > TEMP_HIGH_ALERT || humi > HUMI_HIGH_ALERT) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
} else {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}
}
HAL_Delay(2000); // DHT11采样间隔至少1s
}
}
关键外设驱动
// dht11.h 接口
void DHT11_Init(TIM_HandleTypeDef *htim, GPIO_TypeDef *port, uint16_t pin);
uint8_t DHT11_Read(uint8_t *temp, uint8_t *humi); // 返回DHT11_OK或DHT11_ERROR
// lcd1602_i2c.h 接口
void LCD1602_Init(I2C_HandleTypeDef *hi2c);
void LCD1602_Clear(void);
void LCD1602_SetCursor(uint8_t col, uint8_t row);
void LCD1602_PrintString(char *str);
运行与验证
- 使用STM32CubeMX生成工程骨架,将上述代码填入main.c对应位置,并实现dht11.c与lcd1602_i2c.c驱动(驱动函数需按接口实现,DHT11时序采用定时器微秒延时)。
- 编译烧录后,LCD第一行显示温度(单位0.1℃)与湿度(%RH),第二行显示光照强度百分比。
- 通过判定标准:
- 用手靠近DHT11呼气,LCD温度值在2秒内上升至少1℃;
- 遮挡光敏电阻,光照百分比明显下降(变化幅度>10%);
- 当温度超过30℃或湿度超过70%时,PB0所接LED点亮,否则熄灭。
常见问题
- LCD无显示:检查I2C地址是否为0x27,可用I2C扫描程序确认;检查背板对比度电位器。
- DHT11读取失败:确认数据线上拉电阻(4.7kΩ至3.3V),且两次读取间隔大于1秒。
- 光照数值跳变:ADC采样可做4次取平均,或在Light_Convert中增加滑动滤波。