一个STM32环境监测系统的课程设计实战(附:完整工程与源码解析)
2026/7/16 22:49:36 网站建设 项目流程

1. 项目背景与硬件选型

第一次接触STM32环境监测系统时,我对着开发板和一堆传感器零件完全无从下手。直到把DHT11温湿度传感器成功读出数据,LCD屏幕上跳出"25.3℃ 62%"那刻,才真正理解嵌入式开发的魅力。这个课程设计以STM32F103C8T6为核心,俗称"蓝 pill"的开发板性价比极高,某宝30元就能入手,特别适合学生党。

硬件选型直接影响项目成败,我的踩坑经验是:

  • 传感器:DHT11虽然精度一般(±2℃),但胜在价格便宜(5元/个)且数字信号输出,比模拟传感器省去ADC配置
  • 显示模块:选用0.96寸OLED还是LCD1602?实测发现OLED的I2C接口更省IO口,但LCD在强光下可视性更好
  • 报警模块:有源蜂鸣器比无源型号驱动简单,但要注意GPIO驱动能力不足时会声音微弱
  • 扩展性:预留的USART接口可连接ESP8266实现物联网功能(课程设计进阶玩法)

电路连接有个易错点:光敏电阻需要配合10KΩ上拉电阻,直接接3.3V会导致ADC值始终为4095。我在面包板上调试时因此浪费了两小时,后来用万用表量电压才发现问题。

2. 开发环境搭建

新手常卡在第一步环境配置,我推荐用这套"懒人方案":

  1. 软件组合:Keil MDK + STM32CubeMX(避免手动写初始化代码)
  2. 驱动安装:CH340串口驱动和ST-Link驱动必须装对版本
  3. 工程模板:直接使用正点原子提供的标准库模板(CSDN搜"STM32F103标准库")

遇到最典型的报错是"NO ST-LINK detected",解决方法分三步:

  1. 检查开发板跳线帽是否接在SWD模式
  2. 更新ST-Link固件(用ST官方工具)
  3. 换条质量好的MicroUSB线(劣质线会导致供电不稳)
// 用CubeMX生成时钟配置代码示例 void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; // 配置HSE晶振为8MHz RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 72MHz主频 HAL_RCC_OscConfig(&RCC_OscInitStruct); // 时钟树配置 RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // 36MHz RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // 72MHz HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2); }

3. 传感器数据采集实战

DHT11的时序要求极其严格,我最初用HAL_Delay()实现时序,结果成功率不到50%。后来改用寄存器级操作后稳定性大幅提升:

// 改进后的DHT11读取函数 uint8_t DHT11_Read_Data(u8 *temp, u8 *hum) { u8 buf[5] = {0}; u8 i,j; // 主机拉低18ms DHT11_OUT(0); DHT11_DQ_OUT(); HAL_Delay(18); // 主机拉高20-40us DHT11_OUT(1); delay_us(30); // 切换输入模式检测响应 DHT11_DQ_IN(); if(DHT11_IN()==1) return 1; // 检测从机响应 while(DHT11_IN()==0); // 等待80us低电平结束 while(DHT11_IN()==1); // 等待80us高电平结束 // 开始接收40bit数据 for(i=0;i<5;i++) { for(j=0;j<8;j++) { while(DHT11_IN()==0); // 等待50us低电平 delay_us(40); // 判断高电平持续时间 buf[i] <<= 1; if(DHT11_IN()==1) buf[i] |= 1; while(DHT11_IN()==1); // 等待高电平结束 } } // 校验和数据 if(buf[0]+buf[1]+buf[2]+buf[3]==buf[4]) { *hum = buf[0]; *temp = buf[2]; return 0; } return 2; // 校验失败 }

光敏电阻的处理更简单,直接用STM32的ADC采集:

// 获取光照强度值(0-100%) uint8_t Get_Light_Level(void) { HAL_ADC_Start(&hadc1); HAL_ADC_PollForConversion(&hadc1, 10); uint16_t adc_val = HAL_ADC_GetValue(&hadc1); return (uint8_t)(adc_val * 100 / 4095); // 12位ADC最大值4095 }

4. 人机交互设计

菜单系统是项目难点,我采用状态机模式实现三级菜单:

  1. 顶层:显示温湿度/光照实时数据
  2. 二级菜单:设备控制(LED/风扇/蜂鸣器)
  3. 三级菜单:阈值设置(温度报警值)

按键扫描使用定时器中断实现消抖:

// 按键状态机处理 void KEY_Scan(void) { static u8 key_state = 0; switch(key_state) { case 0: // 检测按下 if(KEY_UP==0 || KEY_DOWN==0 || KEY_OK==0) { HAL_Delay(10); // 消抖 key_state = 1; } break; case 1: // 确认按下 if(KEY_UP==0) key_val = KEY_UP_PRESS; else if(KEY_DOWN==0) key_val = KEY_DOWN_PRESS; else if(KEY_OK==0) key_val = KEY_OK_PRESS; else key_state = 0; if(key_val != KEY_NONE) key_state = 2; break; case 2: // 等待释放 if(KEY_UP==1 && KEY_DOWN==1 && KEY_OK==1) { key_state = 0; } break; } }

LCD显示优化技巧:

  • 使用sprintf生成格式化字符串
  • 局部刷新代替全屏刷新(减少闪烁)
  • 重要参数反色显示
// LCD显示示例 void Show_Main_Page(void) { char buf[20]; sprintf(buf, "Temp:%2dC Hum:%2d%%", temp, hum); LCD_ShowString(0, 0, buf); sprintf(buf, "Light:%3d%%", light); LCD_ShowString(0, 1, buf); // 报警状态特殊显示 if(alarm_flag) { LCD_ShowString(0, 3, "!ALARM!"); } }

5. 报警逻辑与外围控制

报警策略采用"软硬结合"方式:

  • 软件判断:主循环中持续监测阈值
  • 硬件响应:触发蜂鸣器+风扇+LED闪烁
// 报警控制函数 void Alarm_Control(void) { static u8 blink_cnt = 0; if(temp > temp_threshold || hum > hum_threshold) { // 启动报警 BUZZER_ON(); FAN_ON(); if(++blink_cnt >= 10) { // 0.5秒间隔闪烁 blink_cnt = 0; LED1_TOGGLE(); } alarm_flag = 1; } else { // 关闭报警 BUZZER_OFF(); FAN_OFF(); LED1_OFF(); alarm_flag = 0; } }

风扇控制有个坑:普通IO口驱动电流不够,需要加三极管放大。我用S8050搭建的驱动电路如下:

VCC(5V) --- [风扇+] --- [风扇-] --- [三极管C极] | E --- GND B --- 1K电阻 --- PA6

6. 工程架构优化建议

初期我的代码全写在main.c里,后来发现几个严重问题:

  1. 修改传感器要重新编译整个工程
  2. 功能添加导致代码越来越乱
  3. 多人协作无法并行开发

改进后的模块化架构:

├── Drivers │ ├── bsp_dht11.c // 传感器驱动 │ ├── bsp_lcd.c // 显示驱动 │ └── bsp_key.c // 按键驱动 ├── Middlewares │ ├── menu.c // 菜单系统 │ └── alarm.c // 报警逻辑 └── Application ├── app_main.c // 主业务流程 └── app_iot.c // 物联网扩展

关键技巧:

  • 使用头文件声明外部函数
  • 全局变量用extern声明
  • 模块间通过消息队列通信
// 典型头文件示例(bsp_dht11.h) #ifndef __DHT11_H #define __DHT11_H #include "stm32f1xx_hal.h" #define DHT11_GPIO_PORT GPIOB #define DHT11_GPIO_PIN GPIO_PIN_12 void DHT11_Init(void); uint8_t DHT11_Read_Data(u8 *temp, u8 *hum); #endif

7. 常见问题解决方案

调试过程中遇到的典型问题及解决方法:

  1. DHT11读取失败

    • 检查接线是否接触不良
    • 时序严格控制在微秒级
    • 增加失败重试机制
  2. LCD显示乱码

    • 确认初始化序列正确
    • 检查对比度电压(可调电阻)
    • 确保发送数据/命令标志位正确
  3. 按键响应迟钝

    • 消抖时间调整到10-20ms
    • 改用中断方式检测按键
    • 检查上拉电阻是否接好
  4. ADC值跳变严重

    • 增加软件滤波算法(中值+均值)
    • 在ADC引脚加0.1uF滤波电容
    • 避免与大功率设备共用电源
// 改进的ADC滤波算法 #define FILTER_LEN 5 uint16_t ADC_Filter(uint16_t raw_val) { static uint16_t buf[FILTER_LEN] = {0}; static uint8_t index = 0; uint32_t sum = 0; buf[index++] = raw_val; if(index >= FILTER_LEN) index = 0; // 冒泡排序找中值 for(uint8_t i=0; i<FILTER_LEN-1; i++) { for(uint8_t j=i+1; j<FILTER_LEN; j++) { if(buf[i] > buf[j]) { uint16_t temp = buf[i]; buf[i] = buf[j]; buf[j] = temp; } } } // 取中间3个值的平均 for(uint8_t i=1; i<FILTER_LEN-1; i++) { sum += buf[i]; } return sum / (FILTER_LEN-2); }

8. 项目扩展方向

完成基础功能后,可以尝试这些进阶玩法:

  1. 物联网接入

    • 通过ESP8266连接云平台
    • 使用MQTT协议上传数据
    • 手机APP远程监控
  2. 数据存储

    • 添加SPI Flash存储历史数据
    • 实现SD卡数据记录
    • 导出CSV格式报表
  3. 低功耗优化

    • 使用STM32的STOP模式
    • 定时唤醒采样
    • 关闭不必要的外设时钟
  4. GUI升级

    • 移植LittlevGL图形库
    • 实现触摸屏控制
    • 添加曲线显示功能
// 低功耗模式示例 void Enter_Stop_Mode(void) { // 配置唤醒源(如RTC或外部中断) HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); // 关闭外设时钟 __HAL_RCC_GPIOA_CLK_DISABLE(); __HAL_RCC_GPIOB_CLK_DISABLE(); // 进入STOP模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后重新初始化系统时钟 SystemClock_Config(); }

完整工程代码已打包上传,包含:

  • Keil完整工程文件
  • STM32CubeMX配置文件
  • 原理图PDF版本
  • 器件清单BOM表
  • 演示视频

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询