STM32智能温湿度监测系统完整代码

2025博客之星年度评选已开启 10w+人浏览 2.8k人参与

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

#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

#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

#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

#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

#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

#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

#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

#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 头文件

#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 (-)

编译和下载

  1. 使用STM32CubeMX生成工程框架
  2. 将上述文件添加到工程中
  3. 配置正确的包含路径
  4. 编译并下载到STM32

功能说明

  1. 数据采集:每2秒采集一次温湿度数据
  2. 显示:实时在OLED上显示温度和湿度
  3. 报警:温度超过30°C时触发蜂鸣器报警
  4. 通信:通过串口(115200)输出数据到上位机
  5. 错误处理:包含完善的错误检测和处理机制

上位机通信格式

[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

扩展建议

  1. 添加按键:用于手动关闭报警或调整阈值
  2. 数据记录:添加SD卡模块存储历史数据
  3. 网络传输:添加ESP8266 WiFi模块上传数据到云端
  4. 低功耗模式:使用STM32的低功耗特性延长电池使用时间
  5. 多传感器:添加光照、气压等传感器
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值