STM32智能温湿度监测系统完整代码
- STM32智能温湿度监测系统完整代码
-
- 工程结构
- [1. 配置文件 config.h](#1. 配置文件 config.h)
- [2. DHT22 驱动文件 dht22.h](#2. DHT22 驱动文件 dht22.h)
- [3. DHT22 驱动文件 dht22.c](#3. DHT22 驱动文件 dht22.c)
- [4. OLED 驱动文件 oled.h](#4. OLED 驱动文件 oled.h)
- [5. OLED 驱动文件 oled.c](#5. OLED 驱动文件 oled.c)
- [6. 蜂鸣器驱动文件 beep.h](#6. 蜂鸣器驱动文件 beep.h)
- [7. 蜂鸣器驱动文件 beep.c](#7. 蜂鸣器驱动文件 beep.c)
- [8. 主函数文件 main.c](#8. 主函数文件 main.c)
- [9. main.h 头文件](#9. main.h 头文件)
- [10. 使用说明](#10. 使用说明)
- 扩展建议
STM32智能温湿度监测系统完整代码
工程结构
project/
├── Core/
│ ├── Inc/
│ │ ├── dht22.h
│ │ ├── oled.h
│ │ ├── beep.h
│ │ └── config.h
│ └── Src/
│ ├── main.c
│ ├── dht22.c
│ ├── oled.c
│ └── beep.c
└── Drivers/
└── STM32F1xx_HAL_Driver/
1. 配置文件 config.h
c
#ifndef __CONFIG_H
#define __CONFIG_H
#ifdef __cplusplus
extern "C" {
#endif
// 系统配置参数
#define DEBUG_MODE 1 // 调试模式开关
#define SYS_CLOCK_FREQ 72000000 // 系统时钟频率 72MHz
// DHT22 配置
#define DHT22_PIN GPIO_PIN_0
#define DHT22_PORT GPIOA
#define DHT22_TIMEOUT 1000 // 超时时间(ms)
// OLED 配置 (I2C1)
#define OLED_I2C I2C1
#define OLED_ADDRESS 0x78 // OLED I2C地址
#define OLED_WIDTH 128 // OLED宽度
#define OLED_HEIGHT 64 // OLED高度
// 蜂鸣器配置
#define BEEP_PIN GPIO_PIN_1
#define BEEP_PORT GPIOA
#define BEEP_ON_TIME 500 // 蜂鸣持续时间(ms)
// 串口配置
#define DEBUG_UART USART1
#define UART_BAUDRATE 115200 // 波特率
// 报警阈值
#define TEMP_ALARM_THRESHOLD 30.0f // 温度报警阈值(℃)
#define HUMIDITY_ALARM_THRESHOLD 80.0f // 湿度报警阈值(%)
// 采样间隔
#define SAMPLE_INTERVAL_MS 2000 // 采样间隔(ms)
// 错误代码定义
typedef enum {
ERROR_NONE = 0,
ERROR_DHT22_TIMEOUT,
ERROR_DHT22_CHECKSUM,
ERROR_OLED_INIT_FAILED,
ERROR_OLED_COMM_FAILED,
ERROR_I2C_BUSY,
ERROR_I2C_ERROR
} ErrorCode_t;
#ifdef __cplusplus
}
#endif
#endif /* __CONFIG_H */
2. DHT22 驱动文件 dht22.h
c
#ifndef __DHT22_H
#define __DHT22_H
#include "stm32f1xx_hal.h"
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
// DHT22数据结构
typedef struct {
float temperature;
float humidity;
uint8_t is_valid;
ErrorCode_t last_error;
uint32_t last_read_time;
} DHT22_Data_t;
// 函数声明
void DHT22_Init(void);
ErrorCode_t DHT22_ReadData(DHT22_Data_t *data);
float DHT22_GetTemperature(void);
float DHT22_GetHumidity(void);
void DHT22_DelayMicroseconds(uint32_t us);
#ifdef __cplusplus
}
#endif
#endif /* __DHT22_H */
3. DHT22 驱动文件 dht22.c
c
#include "dht22.h"
#include <string.h>
#include <stdio.h>
// 全局变量
static DHT22_Data_t dht22_data = {0};
static TIM_HandleTypeDef htim_delay;
// 微秒级延时初始化
static void DHT22_DelayInit(void)
{
__HAL_RCC_TIM2_CLK_ENABLE();
htim_delay.Instance = TIM2;
htim_delay.Init.Prescaler = 72 - 1; // 72MHz/72 = 1MHz
htim_delay.Init.CounterMode = TIM_COUNTERMODE_UP;
htim_delay.Init.Period = 0xFFFFFFFF;
htim_delay.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim_delay);
HAL_TIM_Base_Start(&htim_delay);
}
// 微秒级延时函数
void DHT22_DelayMicroseconds(uint32_t us)
{
uint32_t start = __HAL_TIM_GET_COUNTER(&htim_delay);
while ((__HAL_TIM_GET_COUNTER(&htim_delay) - start) < us);
}
// DHT22初始化
void DHT22_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置PA0为输出模式(初始状态)
GPIO_InitStruct.Pin = DHT22_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(DHT22_PORT, &GPIO_InitStruct);
// 初始化延时定时器
DHT22_DelayInit();
// 设置初始状态为高电平
HAL_GPIO_WritePin(DHT22_PORT, DHT22_PIN, GPIO_PIN_SET);
dht22_data.is_valid = 0;
dht22_data.last_error = ERROR_NONE;
#if DEBUG_MODE
printf("[DHT22] Initialized successfully\r\n");
#endif
}
// 读取DHT22数据
ErrorCode_t DHT22_ReadData(DHT22_Data_t *data)
{
uint8_t bits[5] = {0};
uint8_t checksum = 0;
uint32_t timeout = 0;
uint8_t i, j;
if (data == NULL) {
return ERROR_DHT22_TIMEOUT;
}
// 1. 主机发送开始信号
HAL_GPIO_WritePin(DHT22_PORT, DHT22_PIN, GPIO_PIN_RESET);
DHT22_DelayMicroseconds(1000); // 拉低至少1ms
// 切换为输入模式
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = DHT22_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(DHT22_PORT, &GPIO_InitStruct);
// 2. 等待DHT22响应
DHT22_DelayMicroseconds(30);
// 等待DHT22拉低80us
timeout = 0;
while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN) == GPIO_PIN_RESET) {
timeout++;
DHT22_DelayMicroseconds(1);
if (timeout > 100) {
dht22_data.last_error = ERROR_DHT22_TIMEOUT;
#if DEBUG_MODE
printf("[DHT22] Timeout waiting for low response\r\n");
#endif
return ERROR_DHT22_TIMEOUT;
}
}
// 等待DHT22拉高80us
timeout = 0;
while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN) == GPIO_PIN_SET) {
timeout++;
DHT22_DelayMicroseconds(1);
if (timeout > 100) {
dht22_data.last_error = ERROR_DHT22_TIMEOUT;
return ERROR_DHT22_TIMEOUT;
}
}
// 3. 读取40位数据
for (i = 0; i < 5; i++) {
for (j = 0; j < 8; j++) {
// 等待低电平结束(50us)
timeout = 0;
while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN) == GPIO_PIN_RESET) {
timeout++;
DHT22_DelayMicroseconds(1);
if (timeout > 100) {
dht22_data.last_error = ERROR_DHT22_TIMEOUT;
return ERROR_DHT22_TIMEOUT;
}
}
// 测量高电平时间
uint32_t high_time = 0;
while (HAL_GPIO_ReadPin(DHT22_PORT, DHT22_PIN) == GPIO_PIN_SET) {
high_time++;
DHT22_DelayMicroseconds(1);
if (high_time > 100) {
break;
}
}
// 根据高电平时间判断是0还是1
bits[i] <<= 1;
if (high_time > 40) { // 高电平持续时间大于40us表示数据1
bits[i] |= 0x01;
}
}
}
// 4. 校验数据
checksum = bits[0] + bits[1] + bits[2] + bits[3];
if (checksum != bits[4]) {
dht22_data.last_error = ERROR_DHT22_CHECKSUM;
#if DEBUG_MODE
printf("[DHT22] Checksum error: %d != %d\r\n", checksum, bits[4]);
#endif
return ERROR_DHT22_CHECKSUM;
}
// 5. 解析温湿度数据
int16_t temp_raw = ((bits[2] & 0x7F) << 8) | bits[3];
int16_t hum_raw = (bits[0] << 8) | bits[1];
if (bits[2] & 0x80) {
temp_raw = -temp_raw; // 负数温度
}
dht22_data.temperature = temp_raw / 10.0f;
dht22_data.humidity = hum_raw / 10.0f;
dht22_data.is_valid = 1;
dht22_data.last_read_time = HAL_GetTick();
dht22_data.last_error = ERROR_NONE;
// 复制数据到输出参数
if (data != &dht22_data) {
memcpy(data, &dht22_data, sizeof(DHT22_Data_t));
}
#if DEBUG_MODE
printf("[DHT22] Read successful: Temp=%.1f°C, Hum=%.1f%%\r\n",
dht22_data.temperature, dht22_data.humidity);
#endif
return ERROR_NONE;
}
// 获取温度
float DHT22_GetTemperature(void)
{
if (!dht22_data.is_valid) {
DHT22_ReadData(NULL);
}
return dht22_data.temperature;
}
// 获取湿度
float DHT22_GetHumidity(void)
{
if (!dht22_data.is_valid) {
DHT22_ReadData(NULL);
}
return dht22_data.humidity;
}
4. OLED 驱动文件 oled.h
c
#ifndef __OLED_H
#define __OLED_H
#include "stm32f1xx_hal.h"
#include "config.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// OLED命令定义
#define OLED_CMD_SET_CONTRAST 0x81
#define OLED_CMD_DISPLAY_ALL_ON 0xA5
#define OLED_CMD_DISPLAY_NORMAL 0xA6
#define OLED_CMD_DISPLAY_INVERSE 0xA7
#define OLED_CMD_DISPLAY_OFF 0xAE
#define OLED_CMD_DISPLAY_ON 0xAF
#define OLED_CMD_SET_MEMORY_MODE 0x20
#define OLED_CMD_SET_COL_ADDR 0x21
#define OLED_CMD_SET_PAGE_ADDR 0x22
#define OLED_CMD_SET_START_LINE 0x40
#define OLED_CMD_SET_SEG_REMAP 0xA0
#define OLED_CMD_SET_MUX_RATIO 0xA8
#define OLED_CMD_SET_COM_SCAN_DIR 0xC0
#define OLED_CMD_SET_DISPLAY_OFFSET 0xD3
#define OLED_CMD_SET_COM_PINS 0xDA
#define OLED_CMD_SET_DISPLAY_CLK 0xD5
#define OLED_CMD_SET_PRECHARGE 0xD9
#define OLED_CMD_SET_VCOMH_DESELECT 0xDB
#define OLED_CMD_SET_CHARGE_PUMP 0x8D
// OLED结构体
typedef struct {
uint8_t width;
uint8_t height;
uint8_t is_initialized;
uint8_t buffer[128 * 8]; // 128x64 bits = 128*8 bytes
} OLED_t;
// 字体结构
typedef struct {
const uint8_t *data; // 字体数据
uint8_t width; // 字符宽度
uint8_t height; // 字符高度
uint8_t first_char; // 第一个字符
uint8_t last_char; // 最后一个字符
} FontDef_t;
// 函数声明
ErrorCode_t OLED_Init(I2C_HandleTypeDef *hi2c);
void OLED_Clear(void);
void OLED_Update(void);
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color);
void OLED_DrawChar(uint8_t x, uint8_t y, char ch, FontDef_t *font, uint8_t color);
void OLED_DrawString(uint8_t x, uint8_t y, const char *str, FontDef_t *font, uint8_t color);
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void OLED_DrawTemperature(float temp);
void OLED_DrawHumidity(float hum);
// 外部字体声明
extern FontDef_t Font_6x8;
extern FontDef_t Font_7x10;
extern FontDef_t Font_11x18;
extern FontDef_t Font_16x26;
#ifdef __cplusplus
}
#endif
#endif /* __OLED_H */
5. OLED 驱动文件 oled.c
c
#include "oled.h"
#include <string.h>
#include <stdio.h>
// 全局变量
static I2C_HandleTypeDef *hi2c_oled = NULL;
static OLED_t oled = {0};
// 6x8字体
static const uint8_t Font6x8[][6] = {
{0x00,0x00,0x00,0x00,0x00,0x00}, //
{0x00,0x00,0x5F,0x00,0x00,0x00}, // !
{0x00,0x07,0x00,0x07,0x00,0x00}, // "
// ... 更多字符定义(此处简化,实际需要完整字体)
{0x7C,0x12,0x11,0x12,0x7C,0x00}, // A
{0x7F,0x49,0x49,0x49,0x36,0x00}, // B
// ... 继续所有需要的字符
};
FontDef_t Font_6x8 = {
.data = (uint8_t *)Font6x8,
.width = 6,
.height = 8,
.first_char = ' ',
.last_char = '~'
};
// 发送命令到OLED
static ErrorCode_t OLED_WriteCommand(uint8_t cmd)
{
if (hi2c_oled == NULL) {
return ERROR_OLED_INIT_FAILED;
}
uint8_t data[2] = {0x00, cmd}; // Co=0, D/C#=0 表示命令
if (HAL_I2C_Master_Transmit(hi2c_oled, OLED_ADDRESS, data, 2, HAL_MAX_DELAY) != HAL_OK) {
#if DEBUG_MODE
printf("[OLED] Failed to send command: 0x%02X\r\n", cmd);
#endif
return ERROR_I2C_ERROR;
}
return ERROR_NONE;
}
// 发送数据到OLED
static ErrorCode_t OLED_WriteData(uint8_t *data, uint16_t size)
{
if (hi2c_oled == NULL || data == NULL) {
return ERROR_OLED_INIT_FAILED;
}
// 准备传输:Co=0, D/C#=1 表示数据
uint8_t buffer[size + 1];
buffer[0] = 0x40; // 数据模式
memcpy(&buffer[1], data, size);
if (HAL_I2C_Master_Transmit(hi2c_oled, OLED_ADDRESS, buffer, size + 1, HAL_MAX_DELAY) != HAL_OK) {
#if DEBUG_MODE
printf("[OLED] Failed to send data\r\n");
#endif
return ERROR_I2C_ERROR;
}
return ERROR_NONE;
}
// OLED初始化
ErrorCode_t OLED_Init(I2C_HandleTypeDef *hi2c)
{
if (hi2c == NULL) {
return ERROR_OLED_INIT_FAILED;
}
hi2c_oled = hi2c;
// 初始化命令序列
uint8_t init_cmds[] = {
OLED_CMD_DISPLAY_OFF, // 关闭显示
OLED_CMD_SET_DISPLAY_CLK, // 设置时钟分频
0x80, // 时钟=0xF,分频=0x0
OLED_CMD_SET_MUX_RATIO, // 设置复用率
0x3F, // 64MUX for 128x64
OLED_CMD_SET_DISPLAY_OFFSET, // 设置显示偏移
0x00, // 无偏移
OLED_CMD_SET_START_LINE | 0x00, // 设置起始行
OLED_CMD_SET_CHARGE_PUMP, // 设置电荷泵
0x14, // 启用电荷泵
OLED_CMD_SET_MEMORY_MODE, // 设置内存模式
0x00, // 水平寻址模式
OLED_CMD_SET_SEG_REMAP | 0x01, // 列地址127映射到SEG0
OLED_CMD_SET_COM_SCAN_DIR | 0x08, // 从COM[N-1]扫描到COM0
OLED_CMD_SET_COM_PINS, // 设置COM引脚硬件配置
0x12, // 64行模式
OLED_CMD_SET_CONTRAST, // 设置对比度
0x7F, // 中等对比度
OLED_CMD_SET_PRECHARGE, // 预充电周期
0xF1,
OLED_CMD_SET_VCOMH_DESELECT, // VCOMH取消选择电平
0x40, // ~0.77 * VCC
OLED_CMD_DISPLAY_ALL_ON | 0x00, // 不使用全亮模式
OLED_CMD_DISPLAY_NORMAL, // 正常显示(非反白)
OLED_CMD_DISPLAY_ON // 开启显示
};
// 发送初始化命令
for (uint16_t i = 0; i < sizeof(init_cmds); i++) {
if (OLED_WriteCommand(init_cmds[i]) != ERROR_NONE) {
return ERROR_OLED_COMM_FAILED;
}
HAL_Delay(10);
}
// 清屏
OLED_Clear();
OLED_Update();
oled.width = OLED_WIDTH;
oled.height = OLED_HEIGHT;
oled.is_initialized = 1;
#if DEBUG_MODE
printf("[OLED] Initialized successfully\r\n");
#endif
return ERROR_NONE;
}
// 清屏
void OLED_Clear(void)
{
memset(oled.buffer, 0, sizeof(oled.buffer));
}
// 更新显示
void OLED_Update(void)
{
if (!oled.is_initialized) {
return;
}
for (uint8_t page = 0; page < 8; page++) {
// 设置页地址
OLED_WriteCommand(0xB0 + page); // 设置页起始地址
OLED_WriteCommand(0x00); // 设置列低地址
OLED_WriteCommand(0x10); // 设置列高地址
// 发送该页的数据
OLED_WriteData(&oled.buffer[page * 128], 128);
}
}
// 绘制像素点
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color)
{
if (x >= OLED_WIDTH || y >= OLED_HEIGHT) {
return;
}
uint16_t byte_index = x + (y / 8) * 128;
uint8_t bit_mask = 1 << (y % 8);
if (color) {
oled.buffer[byte_index] |= bit_mask;
} else {
oled.buffer[byte_index] &= ~bit_mask;
}
}
// 绘制字符
void OLED_DrawChar(uint8_t x, uint8_t y, char ch, FontDef_t *font, uint8_t color)
{
if (font == NULL || ch < font->first_char || ch > font->last_char) {
return;
}
uint16_t char_index = ch - font->first_char;
const uint8_t *char_data = &font->data[char_index * font->width];
for (uint8_t i = 0; i < font->width; i++) {
uint8_t line = char_data[i];
for (uint8_t j = 0; j < font->height; j++) {
if (line & 0x01) {
OLED_DrawPixel(x + i, y + j, color);
}
line >>= 1;
}
}
}
// 绘制字符串
void OLED_DrawString(uint8_t x, uint8_t y, const char *str, FontDef_t *font, uint8_t color)
{
if (str == NULL || font == NULL) {
return;
}
uint8_t current_x = x;
while (*str) {
OLED_DrawChar(current_x, y, *str, font, color);
current_x += font->width + 1; // 字符宽度+1像素间隔
str++;
}
}
// 绘制温度
void OLED_DrawTemperature(float temp)
{
char buffer[32];
OLED_DrawString(10, 10, "Temperature:", &Font_6x8, 1);
snprintf(buffer, sizeof(buffer), "%.1f C", temp);
OLED_DrawString(10, 25, buffer, &Font_6x8, 1);
// 绘制温度计图标
OLED_DrawFilledRectangle(90, 10, 4, 30, 1); // 温度计主体
OLED_DrawRectangle(90, 10, 4, 30, 1); // 边框
OLED_DrawFilledCircle(92, 45, 6, 1); // 底部圆球
}
// 绘制湿度
void OLED_DrawHumidity(float hum)
{
char buffer[32];
OLED_DrawString(10, 45, "Humidity:", &Font_6x8, 1);
snprintf(buffer, sizeof(buffer), "%.1f %%", hum);
OLED_DrawString(10, 55, buffer, &Font_6x8, 1);
// 绘制水滴图标
OLED_DrawFilledCircle(92, 55, 6, 1);
}
// 绘制填充矩形(辅助函数)
void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color)
{
for (uint8_t i = 0; i < h; i++) {
for (uint8_t j = 0; j < w; j++) {
OLED_DrawPixel(x + j, y + i, color);
}
}
}
// 绘制填充圆(辅助函数)
void OLED_DrawFilledCircle(uint8_t x0, uint8_t y0, uint8_t r, uint8_t color)
{
int16_t f = 1 - r;
int16_t ddF_x = 1;
int16_t ddF_y = -2 * r;
int16_t x = 0;
int16_t y = r;
// 绘制水平线
OLED_DrawLine(x0 - r, y0, x0 + r, y0, color);
while (x < y) {
if (f >= 0) {
y--;
ddF_y += 2;
f += ddF_y;
}
x++;
ddF_x += 2;
f += ddF_x;
// 绘制水平线
OLED_DrawLine(x0 - x, y0 + y, x0 + x, y0 + y, color);
OLED_DrawLine(x0 - x, y0 - y, x0 + x, y0 - y, color);
OLED_DrawLine(x0 - y, y0 + x, x0 + y, y0 + x, color);
OLED_DrawLine(x0 - y, y0 - x, x0 + y, y0 - x, color);
}
}
6. 蜂鸣器驱动文件 beep.h
c
#ifndef __BEEP_H
#define __BEEP_H
#include "stm32f1xx_hal.h"
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
BEEP_OFF = 0,
BEEP_ON,
BEEP_BLINK
} BeepState_t;
typedef struct {
BeepState_t state;
uint32_t on_time;
uint32_t off_time;
uint32_t last_change_time;
uint8_t is_active;
} Beep_t;
// 函数声明
void BEEP_Init(void);
void BEEP_On(void);
void BEEP_Off(void);
void BEEP_Blink(uint32_t on_time, uint32_t off_time);
void BEEP_Update(void);
void BEEP_SetAlarm(uint8_t enable);
uint8_t BEEP_IsAlarmEnabled(void);
#ifdef __cplusplus
}
#endif
#endif /* __BEEP_H */
7. 蜂鸣器驱动文件 beep.c
c
#include "beep.h"
#include "main.h"
// 全局变量
static Beep_t beep = {0};
// 蜂鸣器初始化
void BEEP_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
// 配置蜂鸣器引脚为输出
GPIO_InitStruct.Pin = BEEP_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BEEP_PORT, &GPIO_InitStruct);
// 初始状态为关闭
BEEP_Off();
beep.state = BEEP_OFF;
beep.is_active = 0;
#if DEBUG_MODE
printf("[BEEP] Initialized successfully\r\n");
#endif
}
// 打开蜂鸣器
void BEEP_On(void)
{
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_SET);
beep.state = BEEP_ON;
beep.is_active = 1;
beep.last_change_time = HAL_GetTick();
}
// 关闭蜂鸣器
void BEEP_Off(void)
{
HAL_GPIO_WritePin(BEEP_PORT, BEEP_PIN, GPIO_PIN_RESET);
beep.state = BEEP_OFF;
beep.is_active = 1;
beep.last_change_time = HAL_GetTick();
}
// 设置蜂鸣器闪烁
void BEEP_Blink(uint32_t on_time, uint32_t off_time)
{
beep.state = BEEP_BLINK;
beep.on_time = on_time;
beep.off_time = off_time;
beep.last_change_time = HAL_GetTick();
beep.is_active = 1;
// 初始状态为打开
BEEP_On();
}
// 更新蜂鸣器状态(需要在主循环中调用)
void BEEP_Update(void)
{
if (!beep.is_active) {
return;
}
uint32_t current_time = HAL_GetTick();
uint32_t elapsed = current_time - beep.last_change_time;
switch (beep.state) {
case BEEP_ON:
if (elapsed >= beep.on_time) {
BEEP_Off();
}
break;
case BEEP_OFF:
if (elapsed >= beep.off_time) {
BEEP_On();
}
break;
case BEEP_BLINK:
if (elapsed >= beep.on_time &&
HAL_GPIO_ReadPin(BEEP_PORT, BEEP_PIN) == GPIO_PIN_SET) {
BEEP_Off();
} else if (elapsed >= beep.off_time &&
HAL_GPIO_ReadPin(BEEP_PORT, BEEP_PIN) == GPIO_PIN_RESET) {
BEEP_On();
}
break;
default:
break;
}
}
// 设置报警状态
void BEEP_SetAlarm(uint8_t enable)
{
if (enable) {
BEEP_Blink(BEEP_ON_TIME, BEEP_ON_TIME); // 500ms开,500ms关
#if DEBUG_MODE
printf("[BEEP] Alarm activated\r\n");
#endif
} else {
BEEP_Off();
beep.is_active = 0;
#if DEBUG_MODE
printf("[BEEP] Alarm deactivated\r\n");
#endif
}
}
// 检查报警是否启用
uint8_t BEEP_IsAlarmEnabled(void)
{
return beep.is_active;
}
8. 主函数文件 main.c
c
#include "main.h"
#include "dht22.h"
#include "oled.h"
#include "beep.h"
#include "config.h"
#include <stdio.h>
#include <string.h>
// 全局句柄
UART_HandleTypeDef huart1;
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim2;
// 系统状态
typedef struct {
float current_temperature;
float current_humidity;
uint8_t alarm_status;
uint32_t last_sample_time;
uint32_t system_uptime;
ErrorCode_t last_error;
} SystemStatus_t;
static SystemStatus_t system_status = {0};
// 重写fputc函数,支持printf
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
// 系统时钟配置
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
// 配置HSE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
// 配置系统时钟
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;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}
// USART1初始化
static void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = UART_BAUDRATE;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
#if DEBUG_MODE
printf("[UART] Initialized at %d baud\r\n", UART_BAUDRATE);
#endif
}
// I2C1初始化
static void MX_I2C1_Init(void)
{
hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 400000;
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler();
}
#if DEBUG_MODE
printf("[I2C] Initialized at 400kHz\r\n");
#endif
}
// 引脚初始化
static void GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 启用GPIO时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// USART1引脚 (PA9-TX, PA10-RX)
GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// I2C1引脚 (PB6-SCL, PB7-SDA)
GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 用户LED (PC13)
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
// 系统初始化
void System_Init(void)
{
// 初始化HAL库
HAL_Init();
// 配置系统时钟
SystemClock_Config();
// 初始化GPIO
GPIO_Init();
// 初始化外设
MX_USART1_UART_Init();
MX_I2C1_Init();
// 初始化各模块
DHT22_Init();
if (OLED_Init(&hi2c1) != ERROR_NONE) {
printf("[ERROR] OLED initialization failed\r\n");
Error_Handler();
}
BEEP_Init();
// 打印启动信息
printf("\r\n=================================\r\n");
printf("STM32 Temperature Monitor System\r\n");
printf("Version: 1.0.0\r\n");
printf("Compiled: %s %s\r\n", __DATE__, __TIME__);
printf("=================================\r\n\r\n");
printf("[SYSTEM] Initialization complete\r\n");
printf("[SYSTEM] Alarm threshold: %.1f°C\r\n", TEMP_ALARM_THRESHOLD);
}
// 采集传感器数据
void Sensor_Update(void)
{
uint32_t current_time = HAL_GetTick();
// 检查是否到达采样间隔
if (current_time - system_status.last_sample_time >= SAMPLE_INTERVAL_MS) {
DHT22_Data_t dht_data;
// 读取DHT22数据
ErrorCode_t error = DHT22_ReadData(&dht_data);
if (error == ERROR_NONE) {
system_status.current_temperature = dht_data.temperature;
system_status.current_humidity = dht_data.humidity;
system_status.last_error = ERROR_NONE;
// 检查温度是否超过阈值
if (system_status.current_temperature > TEMP_ALARM_THRESHOLD) {
if (!system_status.alarm_status) {
system_status.alarm_status = 1;
BEEP_SetAlarm(1); // 触发报警
printf("[ALARM] Temperature exceeds threshold: %.1f°C > %.1f°C\r\n",
system_status.current_temperature, TEMP_ALARM_THRESHOLD);
}
} else {
if (system_status.alarm_status) {
system_status.alarm_status = 0;
BEEP_SetAlarm(0); // 关闭报警
printf("[ALARM] Temperature back to normal: %.1f°C\r\n",
system_status.current_temperature);
}
}
// 更新显示
OLED_Clear();
OLED_DrawTemperature(system_status.current_temperature);
OLED_DrawHumidity(system_status.current_humidity);
OLED_Update();
// 串口输出数据
printf("[DATA] Temp=%.1f°C, Hum=%.1f%%, Alarm=%d\r\n",
system_status.current_temperature,
system_status.current_humidity,
system_status.alarm_status);
} else {
system_status.last_error = error;
printf("[ERROR] Failed to read DHT22 sensor: %d\r\n", error);
}
system_status.last_sample_time = current_time;
}
}
// 更新蜂鸣器状态
void System_Update(void)
{
// 更新蜂鸣器状态
BEEP_Update();
// 更新系统运行时间
system_status.system_uptime = HAL_GetTick();
// 用户LED闪烁指示系统运行
static uint32_t led_last_toggle = 0;
if (HAL_GetTick() - led_last_toggle > 500) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
led_last_toggle = HAL_GetTick();
}
}
// 主函数
int main(void)
{
// 系统初始化
System_Init();
printf("[MAIN] Entering main loop...\r\n");
// 主循环
while (1) {
// 采集传感器数据
Sensor_Update();
// 更新系统状态
System_Update();
// 空闲时延时,降低CPU占用率
HAL_Delay(10);
}
}
// 错误处理函数
void Error_Handler(void)
{
printf("[FATAL ERROR] System halted!\r\n");
// 蜂鸣器快速鸣叫指示错误
while (1) {
BEEP_On();
HAL_Delay(100);
BEEP_Off();
HAL_Delay(100);
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
printf("Assert failed: file %s on line %d\r\n", file, (int)line);
Error_Handler();
}
#endif
9. main.h 头文件
c
#ifndef __MAIN_H
#define __MAIN_H
#include "stm32f1xx_hal.h"
#include <stdio.h>
// 函数声明
void SystemClock_Config(void);
void Error_Handler(void);
void System_Init(void);
void Sensor_Update(void);
void System_Update(void);
// 外设句柄声明
extern UART_HandleTypeDef huart1;
extern I2C_HandleTypeDef hi2c1;
extern TIM_HandleTypeDef htim2;
#endif /* __MAIN_H */
10. 使用说明
硬件连接
STM32F103C8T6 (Blue Pill):
- PA0 -> DHT22 DATA
- PA1 -> Buzzer (+)
- PA9 -> USB-TX (通过CH340G)
- PA10 -> USB-RX (通过CH340G)
- PB6 -> OLED SCL
- PB7 -> OLED SDA
- PC13 -> User LED
- 3.3V -> DHT22 VCC, OLED VCC
- GND -> DHT22 GND, OLED GND, Buzzer (-)
编译和下载
- 使用STM32CubeMX生成工程框架
- 将上述文件添加到工程中
- 配置正确的包含路径
- 编译并下载到STM32
功能说明
- 数据采集:每2秒采集一次温湿度数据
- 显示:实时在OLED上显示温度和湿度
- 报警:温度超过30°C时触发蜂鸣器报警
- 通信:通过串口(115200)输出数据到上位机
- 错误处理:包含完善的错误检测和处理机制
上位机通信格式
[DATA] Temp=25.5°C, Hum=60.2%, Alarm=0
[ALARM] Temperature exceeds threshold: 31.2°C > 30.0°C
[ERROR] Failed to read DHT22 sensor: 1
扩展建议
- 添加按键:用于手动关闭报警或调整阈值
- 数据记录:添加SD卡模块存储历史数据
- 网络传输:添加ESP8266 WiFi模块上传数据到云端
- 低功耗模式:使用STM32的低功耗特性延长电池使用时间
- 多传感器:添加光照、气压等传感器