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);

运行与验证

  1. 使用STM32CubeMX生成工程骨架,将上述代码填入main.c对应位置,并实现dht11.c与lcd1602_i2c.c驱动(驱动函数需按接口实现,DHT11时序采用定时器微秒延时)。
  2. 编译烧录后,LCD第一行显示温度(单位0.1℃)与湿度(%RH),第二行显示光照强度百分比。
  3. 通过判定标准:
  • 用手靠近DHT11呼气,LCD温度值在2秒内上升至少1℃;
  • 遮挡光敏电阻,光照百分比明显下降(变化幅度>10%);
  • 当温度超过30℃或湿度超过70%时,PB0所接LED点亮,否则熄灭。

常见问题

  • LCD无显示:检查I2C地址是否为0x27,可用I2C扫描程序确认;检查背板对比度电位器。
  • DHT11读取失败:确认数据线上拉电阻(4.7kΩ至3.3V),且两次读取间隔大于1秒。
  • 光照数值跳变:ADC采样可做4次取平均,或在Light_Convert中增加滑动滤波。