)
从零打造桌面温湿度监测站STM32F103与TM1616的完美组合你是否厌倦了市面上千篇一律的电子温湿度计想亲手打造一个既实用又能展示技术实力的桌面小工具今天我们就用STM32F103C8T6这块经典单片机搭配TM1616数码管驱动芯片和DHT11传感器制作一个完全自定义的温湿度显示器。不同于简单的驱动测试我们将从实际应用角度出发解决显示闪烁、功耗优化等真实场景中的问题。1. 硬件选型与搭建1.1 核心组件介绍这个项目的硬件架构非常简单只需要三个主要部件STM32F103C8T6最小系统板作为主控价格低廉且性能足够TM1616数码管驱动模块支持4位7段数码管显示内置亮度调节DHT11温湿度传感器单总线接口测量范围20-90%RH和0-50℃提示选择TM1616而非TM1637的主要原因是前者支持8级亮度调节更适合需要长时间运行的桌面设备。1.2 硬件连接示意图将各模块按照以下方式连接STM32引脚连接目标备注PB6DHT11数据线需接4.7K上拉电阻PB7TM1616 STB片选信号PB8TM1616 CLK时钟信号PB9TM1616 DIO数据输入/输出3.3V各模块VCC供电GND各模块GND共地2. TM1616驱动深度优化2.1 基础驱动封装原始驱动代码虽然能用但直接放在主循环中会导致显示闪烁。我们需要将其封装成更健壮的显示库// tm1616.h #ifndef __TM1616_H #define __TM1616_H #include stm32f10x.h #define TM1616_STB_PIN GPIO_Pin_7 #define TM1616_CLK_PIN GPIO_Pin_8 #define TM1616_DIO_PIN GPIO_Pin_9 #define TM1616_PORT GPIOB typedef enum { LED_OFF 0x00, LED_ON 0x08 } TM1616_PowerState; typedef enum { BRIGHTNESS_1 0, BRIGHTNESS_2, // ... 其他亮度等级 BRIGHTNESS_8 } TM1616_Brightness; void TM1616_Init(void); void TM1616_DisplayDigits(uint8_t digits[4]); void TM1616_SetBrightness(TM1616_Brightness level); void TM1616_SetPower(TM1616_PowerState state); #endif2.2 显示刷新策略优化常见的显示闪烁问题通常源于不合理的刷新方式。我们采用双缓冲技术来解决// tm1616.c static uint8_t displayBuffer[4] {0}; static uint8_t backBuffer[4] {0}; void TM1616_UpdateDisplay(void) { // 原子操作切换缓冲区 memcpy(displayBuffer, backBuffer, 4); TM1616_DisplayDigits(displayBuffer); } void TM1616_SetDigit(uint8_t position, uint8_t value) { if(position 4) { backBuffer[position] value; } }3. 温湿度数据采集与处理3.1 DHT11驱动实现DHT11的通信时序要求严格我们需要精确的微秒级延时// dht11.c #define DHT11_PIN GPIO_Pin_6 #define DHT11_PORT GPIOB uint8_t DHT11_ReadData(float *temperature, float *humidity) { uint8_t data[5] {0}; // 主机发起开始信号 GPIO_ResetBits(DHT11_PORT, DHT11_PIN); Delay_us(18000); GPIO_SetBits(DHT11_PORT, DHT11_PIN); Delay_us(40); // 等待传感器响应 if(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) { uint32_t timeout 10000; while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) timeout--); timeout 10000; while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) timeout--); // 读取40位数据 for(uint8_t i0; i5; i) { for(uint8_t j0; j8; j) { while(!GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); Delay_us(30); data[i] 1; if(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)) { data[i] | 1; while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN)); } } } // 校验数据 if(data[4] (data[0]data[1]data[2]data[3])) { *humidity data[0] data[1]*0.1; *temperature data[2] data[3]*0.1; return 1; } } return 0; }3.2 数据滤波算法原始传感器数据常有波动我们需要添加简单的滑动平均滤波#define FILTER_WINDOW_SIZE 5 typedef struct { float tempBuffer[FILTER_WINDOW_SIZE]; float humiBuffer[FILTER_WINDOW_SIZE]; uint8_t index; } SensorFilter; void Filter_Init(SensorFilter *filter) { memset(filter, 0, sizeof(SensorFilter)); } void Filter_AddData(SensorFilter *filter, float temp, float humi) { filter-tempBuffer[filter-index] temp; filter-humiBuffer[filter-index] humi; filter-index (filter-index 1) % FILTER_WINDOW_SIZE; } float Filter_GetAverage(float *buffer) { float sum 0; for(uint8_t i0; iFILTER_WINDOW_SIZE; i) { sum buffer[i]; } return sum / FILTER_WINDOW_SIZE; }4. 系统整合与功耗优化4.1 主程序架构设计采用状态机模式管理显示内容切换和传感器读取typedef enum { SHOW_TEMP, SHOW_HUMI, SHOW_ALT } DisplayMode; void System_Run(void) { static DisplayMode mode SHOW_TEMP; static uint32_t lastReadTime 0; static SensorFilter filter; // 每2秒读取一次传感器 if(HAL_GetTick() - lastReadTime 2000) { float temp, humi; if(DHT11_ReadData(temp, humi)) { Filter_AddData(filter, temp, humi); } lastReadTime HAL_GetTick(); } // 每5秒切换显示模式 switch(mode) { case SHOW_TEMP: DisplayTemperature(Filter_GetAverage(filter.tempBuffer)); if(HAL_GetTick() % 10000 2000) mode SHOW_HUMI; break; case SHOW_HUMI: DisplayHumidity(Filter_GetAverage(filter.humiBuffer)); if(HAL_GetTick() % 10000 2000) mode SHOW_TEMP; break; } // 根据环境光线自动调节亮度 AutoAdjustBrightness(); }4.2 动态亮度调节技术利用TM1616的8级亮度控制功能实现自动亮度调节void AutoAdjustBrightness(void) { uint16_t lightLevel GetLightSensorLevel(); // 假设有光敏电阻 TM1616_Brightness brightness; if(lightLevel 800) brightness BRIGHTNESS_8; else if(lightLevel 600) brightness BRIGHTNESS_7; // ... 其他条件 else brightness BRIGHTNESS_2; TM1616_SetBrightness(brightness); }5. 进阶功能扩展5.1 温度报警功能当温度超过设定阈值时让数码管闪烁提示void CheckTemperatureAlert(float temp) { static uint8_t alertActive 0; static uint32_t lastBlinkTime 0; if(temp TEMP_ALERT_THRESHOLD) { alertActive 1; if(HAL_GetTick() - lastBlinkTime 500) { static uint8_t visible 0; if(visible) { DisplayTemperature(temp); visible 0; } else { TM1616_ClearDisplay(); visible 1; } lastBlinkTime HAL_GetTick(); } } else if(alertActive) { alertActive 0; DisplayTemperature(temp); } }5.2 低功耗模式实现利用STM32的睡眠模式进一步降低功耗void EnterLowPowerMode(void) { // 配置唤醒源 EXTI_InitTypeDef EXTI_InitStructure; EXTI_InitStructure.EXTI_Line EXTI_Line6; // DHT11引脚 EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); // 进入停止模式 TM1616_SetPower(LED_OFF); PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后重新初始化时钟 SystemInit(); TM1616_Init(); }6. 项目外壳设计与成品展示虽然代码是核心但一个好的外壳能让项目更加完整。这里有几个DIY建议3D打印外壳使用PLA材料打印一个简约的倾斜支架亚克力激光切割制作透明保护罩同时展示内部结构木质底座搭配胡桃木底座提升质感注意设计外壳时要考虑散热问题避免将单片机紧贴密闭空间。