STM32单片机温度控制系统源码

一、系统架构设计

1.1 硬件配置清单

主控芯片:STM32F103C8T6(72MHz,64KB Flash,20KB RAM)
温度传感器:
  - 主传感器:DS18B20(数字式,-55°C~125°C)
  - 备用传感器:NTC 10K热敏电阻(模拟量)
显示模块:0.96寸OLED(SSD1306,I2C接口)
控制输出:
  - 加热控制:继电器(220V/30A)
  - 风扇控制:PWM调速(12V/2A)
  - 报警输出:蜂鸣器+LED
用户输入:3个按键(设置/加/减)
通信接口:USB转串口(CH340G)
电源管理:LM2596降压模块(12V→5V)

1.2 系统功能框图

┌─────────────────────────────────────────────┐
│            STM32F103C8T6 温度控制系统         │
├─────────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐      │
│  │ DS18B20 │  │ NTC传感器 │  │ 按键输入 │      │
│  │  -55~125°C │  │ -40~150°C │  │ 设置/加减 │      │
│  └────┬────┘  └────┬────┘  └────┬────┘      │
│       │            │            │            │
│  ┌────▼────┐  ┌────▼────┐  ┌────▼────┐      │
│  │ 温度采集 │  │ 信号调理 │  │ 人机交互 │      │
│  │ 模块    │  │ 模块    │  │ 模块    │      │
│  └────┬────┘  └────┬────┘  └────┬────┘      │
│       └─────────────┼────────────┘            │
│                     ▼                         │
│            ┌─────────────────┐                │
│            │ PID控制算法      │                │
│            │ - 比例控制       │                │
│            │ - 积分消除静差   │                │
│            │ - 微分预测趋势   │                │
│            └────────┬────────┘                │
│                     ▼                         │
│  ┌─────────────────────────────────────┐      │
│  │ 输出控制模块                        │      │
│  │ - 继电器控制加热元件                │      │
│  │ - PWM控制风扇转速                  │      │
│  │ - 过热保护(>80°C强制断电)        │      │
│  └────────┬────────────────┬────────┘      │
│           ▼                ▼                │
│  ┌────────────┐    ┌────────────┐          │
│  │ 加热执行器 │    │ 散热风扇   │          │
│  │ 300W电热管 │    │ 12V/2A    │          │
│  └────────────┘    └────────────┘          │
└─────────────────────────────────────────────┘

二、完整工程源码

2.1 主程序(main.c)

/**
  * @file main.c
  * @brief STM32温度控制系统主程序
  * @version 2.0
  */
#include "stm32f10x.h"
#include "ds18b20.h"
#include "oled.h"
#include "pid.h"
#include "key.h"
#include "control.h"
#include "uart.h"
#include "timer.h"
#include "eeprom.h"

/* 系统参数 */
#define TEMP_MIN        0       // 最低温度
#define TEMP_MAX        100     // 最高温度
#define DEFAULT_TEMP    25      // 默认目标温度
#define OVERHEAT_TEMP   80      // 过热保护温度
#define HYSTERESIS      0.5f    // 迟滞回差

/* 全局变量 */
SystemState sys_state = SYS_INIT;
TempData temp_data;
ControlParam ctrl_param;
DisplayData disp_data;

/* 系统状态枚举 */
typedef enum {
    SYS_INIT = 0,        // 初始化
    SYS_NORMAL,         // 正常运行
    SYS_SETTING,        // 参数设置
    SYS_ALARM,          // 报警状态
    SYS_ERROR           // 故障状态
} SystemState;

/* 温度数据结构 */
typedef struct {
    float current;      // 当前温度
    float target;       // 目标温度
    float previous;     // 上次温度
    float max_record;   // 最高温度记录
    float min_record;   // 最低温度记录
    uint8_t sensor_ok;  // 传感器状态
} TempData;

/* 控制参数 */
typedef struct {
    float kp;           // 比例系数
    float ki;           // 积分系数
    float kd;           // 微分系数
    float output;       // 控制输出
    float integral;     // 积分项
    float derivative;   // 微分项
    uint8_t heat_duty;  // 加热占空比
    uint8_t fan_duty;   // 风扇占空比
} ControlParam;

/* 显示数据结构 */
typedef struct {
    uint8_t page;       // 当前页面
    uint8_t blink;      // 闪烁标志
    uint32_t last_key;  // 上次按键时间
} DisplayData;

int main(void)
{
    /* 1. 系统初始化 */
    System_Init();
    
    /* 2. 外设初始化 */
    Delay_Init();
    OLED_Init();
    DS18B20_Init();
    KEY_Init();
    Control_Init();
    UART_Init(115200);
    Timer_Init();
    EEPROM_Init();
    
    /* 3. 加载保存的参数 */
    Load_Parameters(&ctrl_param, &temp_data);
    
    /* 4. 显示启动画面 */
    OLED_ShowString(0, 0, "Temp Control v2.0");
    OLED_ShowString(0, 2, "Initializing...");
    OLED_Refresh();
    Delay_Ms(2000);
    
    /* 5. 主循环 */
    while(1)
    {
        /* 5.1 温度采集(每1秒) */
        if(Get_Tick() - temp_data.last_sample > 1000)
        {
            temp_data.current = DS18B20_GetTemp();
            temp_data.previous = temp_data.current;
            
            /* 传感器故障检测 */
            if(temp_data.current < -50 || temp_data.current > 150)
            {
                temp_data.sensor_ok = 0;
                sys_state = SYS_ERROR;
            }
            else
            {
                temp_data.sensor_ok = 1;
                sys_state = SYS_NORMAL;
                
                /* 更新温度记录 */
                if(temp_data.current > temp_data.max_record)
                    temp_data.max_record = temp_data.current;
                if(temp_data.current < temp_data.min_record)
                    temp_data.min_record = temp_data.current;
            }
            
            temp_data.last_sample = Get_Tick();
        }
        
        /* 5.2 PID控制计算(每100ms) */
        if(Get_Tick() - ctrl_param.last_pid > 100)
        {
            if(sys_state == SYS_NORMAL && temp_data.sensor_ok)
            {
                PID_Calculate(&ctrl_param, temp_data.target, temp_data.current);
                Control_Output(&ctrl_param);
            }
            ctrl_param.last_pid = Get_Tick();
        }
        
        /* 5.3 按键处理 */
        Key_Process(&sys_state, &temp_data, &disp_data);
        
        /* 5.4 显示更新(每200ms) */
        if(Get_Tick() - disp_data.last_display > 200)
        {
            Display_Update(sys_state, temp_data, ctrl_param, disp_data);
            disp_data.last_display = Get_Tick();
        }
        
        /* 5.5 过热保护 */
        if(temp_data.current > OVERHEAT_TEMP)
        {
            Control_EmergencyStop();
            sys_state = SYS_ALARM;
            Alarm_Trigger(ALARM_OVERHEAT);
        }
        
        /* 5.6 串口输出数据 */
        if(Get_Tick() - uart_data.last_send > 1000)
        {
            UART_SendTempData(temp_data, ctrl_param);
            uart_data.last_send = Get_Tick();
        }
        
        /* 5.7 保存参数到EEPROM(每10分钟) */
        if(Get_Tick() - eeprom_data.last_save > 600000)
        {
            Save_Parameters(&ctrl_param, &temp_data);
            eeprom_data.last_save = Get_Tick();
        }
    }
}

/* 系统初始化 */
void System_Init(void)
{
    /* 系统时钟配置:72MHz */
    SystemClock_Init();
    
    /* 中断优先级分组 */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    
    /* 全局变量初始化 */
    temp_data.current = 0;
    temp_data.target = DEFAULT_TEMP;
    temp_data.max_record = -100;
    temp_data.min_record = 100;
    temp_data.sensor_ok = 1;
    temp_data.last_sample = 0;
    
    ctrl_param.kp = 2.0f;
    ctrl_param.ki = 0.5f;
    ctrl_param.kd = 0.1f;
    ctrl_param.output = 0;
    ctrl_param.integral = 0;
    ctrl_param.derivative = 0;
    ctrl_param.heat_duty = 0;
    ctrl_param.fan_duty = 0;
    ctrl_param.last_pid = 0;
    
    disp_data.page = 0;
    disp_data.blink = 0;
    disp_data.last_key = 0;
    
    sys_state = SYS_INIT;
}

2.2 PID控制算法(pid.c)

/**
  * @file pid.c
  * @brief PID控制算法实现
  */
#include "pid.h"
#include "control.h"

/* PID结构体 */
static PID_TypeDef pid;

/* PID初始化 */
void PID_Init(PID_TypeDef* pid, float kp, float ki, float kd)
{
    pid->kp = kp;
    pid->ki = ki;
    pid->kd = kd;
    pid->integral = 0;
    pid->prev_error = 0;
    pid->output = 0;
    pid->max_output = 100;    // 最大输出100%
    pid->min_output = 0;      // 最小输出0%
    pid->integral_limit = 50; // 积分限幅
}

/* PID计算 */
float PID_Calculate(PID_TypeDef* pid, float target, float feedback)
{
    float error;
    float derivative;
    
    /* 计算误差 */
    error = target - feedback;
    
    /* 比例项 */
    pid->output = pid->kp * error;
    
    /* 积分项(带限幅) */
    pid->integral += error;
    if(pid->integral > pid->integral_limit)
        pid->integral = pid->integral_limit;
    if(pid->integral < -pid->integral_limit)
        pid->integral = -pid->integral_limit;
    
    pid->output += pid->ki * pid->integral;
    
    /* 微分项 */
    derivative = error - pid->prev_error;
    pid->output += pid->kd * derivative;
    pid->prev_error = error;
    
    /* 输出限幅 */
    if(pid->output > pid->max_output)
        pid->output = pid->max_output;
    if(pid->output < pid->min_output)
        pid->output = pid->min_output;
    
    return pid->output;
}

/* 温控专用PID计算 */
void PID_TempControl(ControlParam* param, float target, float current)
{
    float error;
    static float last_error = 0;
    static float integral = 0;
    float derivative;
    
    /* 计算误差 */
    error = target - current;
    
    /* 比例项 */
    param->output = param->kp * error;
    
    /* 积分项(抗积分饱和) */
    if(fabs(param->output) < 100)  // 只有在不饱和时才积分
    {
        integral += error;
        if(integral > 50) integral = 50;
        if(integral < -50) integral = -50;
    }
    param->output += param->ki * integral;
    
    /* 微分项 */
    derivative = error - last_error;
    param->output += param->kd * derivative;
    last_error = error;
    
    /* 输出限幅 */
    if(param->output > 100) param->output = 100;
    if(param->output < 0) param->output = 0;
    
    /* 更新控制参数 */
    param->integral = integral;
    param->derivative = derivative;
    
    /* 根据输出计算占空比 */
    if(param->output > 0)
    {
        param->heat_duty = (uint8_t)param->output;
        param->fan_duty = 0;
    }
    else
    {
        param->heat_duty = 0;
        param->fan_duty = (uint8_t)(-param->output);
    }
}

/* 增量式PID(更适合温控) */
float PID_Incremental(PID_TypeDef* pid, float target, float feedback)
{
    float error;
    float delta_output;
    
    error = target - feedback;
    
    /* 增量计算 */
    delta_output = pid->kp * (error - pid->prev_error)
                 + pid->ki * error
                 + pid->kd * (error - 2*pid->prev_error + pid->prev_prev_error);
    
    pid->output += delta_output;
    
    /* 限幅 */
    if(pid->output > pid->max_output) pid->output = pid->max_output;
    if(pid->output < pid->min_output) pid->output = pid->min_output;
    
    /* 保存历史误差 */
    pid->prev_prev_error = pid->prev_error;
    pid->prev_error = error;
    
    return pid->output;
}

2.3 控制输出模块(control.c)

/**
  * @file control.c
  * @brief 加热/风扇控制输出
  */
#include "control.h"
#include "tim.h"
#include "gpio.h"

/* 控制引脚定义 */
#define HEAT_RELAY_PIN    GPIO_Pin_0
#define HEAT_RELAY_PORT   GPIOB
#define FAN_PWM_PIN       GPIO_Pin_1
#define FAN_PWM_PORT      GPIOB
#define ALARM_PIN         GPIO_Pin_2
#define ALARM_PORT        GPIOB

/* 控制状态 */
static ControlState ctrl_state = CTRL_IDLE;

/* 初始化控制输出 */
void Control_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    /* 使能时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    /* 配置继电器控制引脚 */
    GPIO_InitStructure.GPIO_Pin = HEAT_RELAY_PIN | ALARM_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(HEAT_RELAY_PORT, &GPIO_InitStructure);
    
    /* 配置风扇PWM引脚 */
    GPIO_InitStructure.GPIO_Pin = FAN_PWM_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(FAN_PWM_PORT, &GPIO_InitStructure);
    
    /* 初始化状态 */
    GPIO_ResetBits(HEAT_RELAY_PORT, HEAT_RELAY_PIN);
    GPIO_ResetBits(ALARM_PORT, ALARM_PIN);
    
    /* 初始化PWM(TIM3_CH4) */
    TIM3_PWM_Init(1000-1, 72-1);  // 1kHz PWM
    TIM_SetCompare4(TIM3, 0);    // 风扇占空比0%
    
    ctrl_state = CTRL_NORMAL;
}

/* 设置加热输出 */
void Control_SetHeat(uint8_t duty)
{
    if(duty > 100) duty = 100;
    
    if(duty > 0)
    {
        GPIO_SetBits(HEAT_RELAY_PORT, HEAT_RELAY_PIN);
    }
    else
    {
        GPIO_ResetBits(HEAT_RELAY_PORT, HEAT_RELAY_PIN);
    }
}

/* 设置风扇输出 */
void Control_SetFan(uint8_t duty)
{
    if(duty > 100) duty = 100;
    
    uint16_t pwm_value = (duty * 1000) / 100;  // 转换为PWM值
    TIM_SetCompare4(TIM3, pwm_value);
}

/* 根据PID输出控制设备 */
void Control_Output(ControlParam* param)
{
    /* 加热控制 */
    if(param->heat_duty > 0)
    {
        Control_SetHeat(param->heat_duty);
    }
    else
    {
        Control_SetHeat(0);
    }
    
    /* 风扇控制 */
    if(param->fan_duty > 0)
    {
        Control_SetFan(param->fan_duty);
    }
    else
    {
        Control_SetFan(0);
    }
}

/* 紧急停止(过热保护) */
void Control_EmergencyStop(void)
{
    /* 关闭所有输出 */
    Control_SetHeat(0);
    Control_SetFan(0);
    GPIO_SetBits(ALARM_PORT, ALARM_PIN);  // 开启报警
    
    ctrl_state = CTRL_EMERGENCY;
}

/* 软启动控制(防止电流冲击) */
void Control_SoftStart(uint8_t target_duty)
{
    static uint8_t current_duty = 0;
    static uint32_t last_time = 0;
    
    if(Get_Tick() - last_time > 100)  // 每100ms增加1%
    {
        if(current_duty < target_duty)
        {
            current_duty++;
            Control_SetHeat(current_duty);
        }
        last_time = Get_Tick();
    }
}

/* 温度到达目标后的维持控制 */
void Control_Maintain(float current, float target)
{
    float diff = fabs(current - target);
    
    if(diff < 0.5f)
    {
        /* 温度稳定,小功率维持 */
        Control_SetHeat(10);  // 10%功率维持
        Control_SetFan(0);
    }
    else if(diff < 2.0f)
    {
        /* 温度接近,中等功率 */
        Control_SetHeat(30);
        Control_SetFan(10);
    }
    else
    {
        /* 温度偏差大,全功率 */
        Control_SetHeat(100);
        Control_SetFan(50);
    }
}

2.4 显示模块(oled.c)

/**
  * @file oled.c
  * @brief OLED显示屏驱动(SSD1306)
  */
#include "oled.h"
#include "i2c.h"
#include "font.h"

/* OLED显存 */
static uint8_t OLED_GRAM[128][8];

/* 初始化OLED */
void OLED_Init(void)
{
    /* 初始化I2C */
    I2C_Init();
    
    /* OLED初始化序列 */
    OLED_WriteCmd(0xAE);  // 关闭显示
    OLED_WriteCmd(0xD5);  // 设置时钟分频
    OLED_WriteCmd(0x80);
    OLED_WriteCmd(0xA8);  // 设置多路复用率
    OLED_WriteCmd(0x3F);
    OLED_WriteCmd(0xD3);  // 设置显示偏移
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0x40);  // 设置起始行
    OLED_WriteCmd(0x8D);  // 电荷泵设置
    OLED_WriteCmd(0x14);
    OLED_WriteCmd(0x20);  // 内存寻址模式
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0xA1);  // 段重映射
    OLED_WriteCmd(0xC8);  // 扫描方向
    OLED_WriteCmd(0xDA);  // 设置COM引脚硬件配置
    OLED_WriteCmd(0x12);
    OLED_WriteCmd(0x81);  // 对比度设置
    OLED_WriteCmd(0xCF);
    OLED_WriteCmd(0xD9);  // 预充电周期
    OLED_WriteCmd(0xF1);
    OLED_WriteCmd(0xDB);  // VCOMH取消选择级别
    OLED_WriteCmd(0x40);
    OLED_WriteCmd(0xA4);  // 整个显示开启
    OLED_WriteCmd(0xA6);  // 正常显示
    OLED_WriteCmd(0xAF);  // 开启显示
    
    /* 清屏 */
    OLED_Clear();
    OLED_Refresh();
}

/* 显示温度界面 */
void OLED_ShowTemp(float current, float target, uint8_t page)
{
    char str[20];
    
    OLED_Clear();
    
    /* 第一行:系统标题 */
    OLED_ShowString(0, 0, "TEMP CONTROL");
    
    /* 第二行:当前温度 */
    sprintf(str, "Now:%5.1f C", current);
    OLED_ShowString(0, 2, str);
    
    /* 第三行:目标温度 */
    sprintf(str, "Set:%5.1f C", target);
    OLED_ShowString(0, 4, str);
    
    /* 第四行:状态指示 */
    if(page == 0)
    {
        OLED_ShowString(0, 6, "Normal Mode");
    }
    else if(page == 1)
    {
        OLED_ShowString(0, 6, "Setting Mode");
    }
    
    OLED_Refresh();
}

/* 显示PID参数 */
void OLED_ShowPIDParam(float kp, float ki, float kd)
{
    char str[20];
    
    OLED_Clear();
    
    OLED_ShowString(0, 0, "PID Parameters");
    
    sprintf(str, "Kp:%6.2f", kp);
    OLED_ShowString(0, 2, str);
    
    sprintf(str, "Ki:%6.2f", ki);
    OLED_ShowString(0, 4, str);
    
    sprintf(str, "Kd:%6.2f", kd);
    OLED_ShowString(0, 6, str);
    
    OLED_Refresh();
}

/* 显示报警信息 */
void OLED_ShowAlarm(char* alarm_msg)
{
    OLED_Clear();
    
    OLED_ShowString(0, 0, "!!! ALARM !!!");
    OLED_ShowString(0, 3, alarm_msg);
    OLED_ShowString(0, 6, "Check System!");
    
    OLED_Refresh();
}

/* 更新显示 */
void Display_Update(SystemState state, TempData temp, ControlParam ctrl, DisplayData disp)
{
    switch(state)
    {
        case SYS_NORMAL:
            OLED_ShowTemp(temp.current, temp.target, 0);
            break;
            
        case SYS_SETTING:
            OLED_ShowPIDParam(ctrl.kp, ctrl.ki, ctrl.kd);
            break;
            
        case SYS_ALARM:
            OLED_ShowAlarm("Overheat!");
            break;
            
        case SYS_ERROR:
            OLED_ShowAlarm("Sensor Error!");
            break;
            
        default:
            break;
    }
}

2.5 按键处理(key.c)

/**
  * @file key.c
  * @brief 按键处理模块
  */
#include "key.h"
#include "delay.h"

/* 按键引脚定义 */
#define KEY_SET_PIN     GPIO_Pin_1
#define KEY_UP_PIN      GPIO_Pin_2
#define KEY_DOWN_PIN    GPIO_Pin_3
#define KEY_PORT        GPIOA

/* 按键状态 */
static uint8_t key_state[3] = {0};
static uint8_t key_count[3] = {0};

/* 初始化按键 */
void KEY_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = KEY_SET_PIN | KEY_UP_PIN | KEY_DOWN_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_PORT, &GPIO_InitStructure);
}

/* 读取按键状态 */
uint8_t KEY_Read(uint8_t key)
{
    uint8_t pin_state = 0;
    
    switch(key)
    {
        case KEY_SET:
            pin_state = GPIO_ReadInputDataBit(KEY_PORT, KEY_SET_PIN);
            break;
        case KEY_UP:
            pin_state = GPIO_ReadInputDataBit(KEY_PORT, KEY_UP_PIN);
            break;
        case KEY_DOWN:
            pin_state = GPIO_ReadInputDataBit(KEY_PORT, KEY_DOWN_PIN);
            break;
    }
    
    return (pin_state == 0) ? 1 : 0;  // 按下为低电平
}

/* 按键消抖处理 */
uint8_t KEY_Scan(uint8_t mode)
{
    static uint8_t key_up = 1;
    uint8_t key_val = KEY_NONE;
    
    if(key_up && (KEY_Read(KEY_SET) || KEY_Read(KEY_UP) || KEY_Read(KEY_DOWN)))
    {
        Delay_Ms(10);  // 消抖
        key_up = 0;
        
        if(KEY_Read(KEY_SET)) key_val = KEY_SET;
        else if(KEY_Read(KEY_UP)) key_val = KEY_UP;
        else if(KEY_Read(KEY_DOWN)) key_val = KEY_DOWN;
    }
    else if(!KEY_Read(KEY_SET) && !KEY_Read(KEY_UP) && !KEY_Read(KEY_DOWN))
    {
        key_up = 1;
    }
    
    return key_val;
}

/* 按键处理函数 */
void Key_Process(SystemState* state, TempData* temp, DisplayData* disp)
{
    uint8_t key = KEY_Scan(0);
    
    if(key != KEY_NONE)
    {
        disp->last_key = Get_Tick();
        
        switch(*state)
        {
            case SYS_NORMAL:
                if(key == KEY_SET)
                {
                    *state = SYS_SETTING;
                    disp->page = 0;
                }
                else if(key == KEY_UP)
                {
                    if(temp->target < TEMP_MAX)
                        temp->target += 1.0f;
                }
                else if(key == KEY_DOWN)
                {
                    if(temp->target > TEMP_MIN)
                        temp->target -= 1.0f;
                }
                break;
                
            case SYS_SETTING:
                if(key == KEY_SET)
                {
                    disp->page++;
                    if(disp->page > 2)  // 循环切换PID参数
                    {
                        *state = SYS_NORMAL;
                        disp->page = 0;
                    }
                }
                else if(key == KEY_UP)
                {
                    switch(disp->page)
                    {
                        case 0:  // 调整Kp
                            ctrl_param.kp += 0.1f;
                            break;
                        case 1:  // 调整Ki
                            ctrl_param.ki += 0.05f;
                            break;
                        case 2:  // 调整Kd
                            ctrl_param.kd += 0.01f;
                            break;
                    }
                }
                else if(key == KEY_DOWN)
                {
                    switch(disp->page)
                    {
                        case 0:
                            if(ctrl_param.kp > 0.1f) ctrl_param.kp -= 0.1f;
                            break;
                        case 1:
                            if(ctrl_param.ki > 0.05f) ctrl_param.ki -= 0.05f;
                            break;
                        case 2:
                            if(ctrl_param.kd > 0.01f) ctrl_param.kd -= 0.01f;
                            break;
                    }
                }
                break;
                
            case SYS_ALARM:
            case SYS_ERROR:
                if(key == KEY_SET)
                {
                    *state = SYS_NORMAL;  // 确认报警
                    GPIO_ResetBits(ALARM_PORT, ALARM_PIN);
                }
                break;
        }
    }
}

2.6 数据存储(eeprom.c)

/**
  * @file eeprom.c
  * @brief EEPROM存储参数(使用STM32内部Flash模拟EEPROM)
  */
#include "eeprom.h"
#include "flash.h"

/* EEPROM存储地址(Flash最后一页) */
#define EEPROM_START_ADDR  0x0800FC00  // 64KB Flash的最后一页

/* 存储数据结构 */
typedef struct {
    float target_temp;     // 目标温度
    float kp;              // PID参数
    float ki;
    float kd;
    uint32_t max_temp;     // 最高温度记录
    uint32_t min_temp;     // 最低温度记录
    uint8_t checksum;      // 校验和
} EEPROM_Data;

/* 初始化EEPROM */
void EEPROM_Init(void)
{
    /* 解锁Flash */
    FLASH_Unlock();
    
    /* 清除所有挂起标志 */
    FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
}

/* 保存参数到EEPROM */
void Save_Parameters(ControlParam* ctrl, TempData* temp)
{
    EEPROM_Data data;
    uint8_t* p = (uint8_t*)&data;
    uint8_t checksum = 0;
    
    /* 填充数据 */
    data.target_temp = temp->target;
    data.kp = ctrl->kp;
    data.ki = ctrl->ki;
    data.kd = ctrl->kd;
    data.max_temp = (uint32_t)(temp->max_record * 100);
    data.min_temp = (uint32_t)(temp->min_record * 100);
    
    /* 计算校验和 */
    for(int i = 0; i < sizeof(EEPROM_Data)-1; i++)
    {
        checksum += p[i];
    }
    data.checksum = checksum;
    
    /* 擦除Flash页 */
    FLASH_ErasePage(EEPROM_START_ADDR);
    
    /* 写入数据 */
    for(int i = 0; i < sizeof(EEPROM_Data); i += 4)
    {
        uint32_t word = *(uint32_t*)(p + i);
        FLASH_ProgramWord(EEPROM_START_ADDR + i, word);
    }
}

/* 从EEPROM加载参数 */
void Load_Parameters(ControlParam* ctrl, TempData* temp)
{
    EEPROM_Data* data = (EEPROM_Data*)EEPROM_START_ADDR;
    uint8_t* p = (uint8_t*)data;
    uint8_t checksum = 0;
    
    /* 验证校验和 */
    for(int i = 0; i < sizeof(EEPROM_Data)-1; i++)
    {
        checksum += p[i];
    }
    
    if(checksum == data->checksum)
    {
        /* 加载参数 */
        temp->target = data->target_temp;
        ctrl->kp = data->kp;
        ctrl->ki = data->ki;
        ctrl->kd = data->kd;
        temp->max_record = data->max_temp / 100.0f;
        temp->min_record = data->min_temp / 100.0f;
    }
    else
    {
        /* 校验失败,使用默认值 */
        temp->target = DEFAULT_TEMP;
        ctrl->kp = 2.0f;
        ctrl->ki = 0.5f;
        ctrl->kd = 0.1f;
        temp->max_record = -100;
        temp->min_record = 100;
    }
}

2.7 串口通信(uart.c)

/**
  * @file uart.c
  * @brief 串口通信模块
  */
#include "uart.h"
#include "stdio.h"

/* 串口接收缓冲区 */
static uint8_t rx_buffer[256];
static uint16_t rx_index = 0;

/* 初始化串口 */
void UART_Init(uint32_t baudrate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    
    /* 使能时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    /* 配置TX引脚 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 配置RX引脚 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    /* 配置串口参数 */
    USART_InitStructure.USART_BaudRate = baudrate;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    
    /* 使能串口 */
    USART_Cmd(USART1, ENABLE);
    
    /* 使能接收中断 */
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    /* 配置NVIC */
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/* 发送字符串 */
void UART_SendString(char* str)
{
    while(*str)
    {
        USART_SendData(USART1, *str++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    }
}

/* 发送温度数据 */
void UART_SendTempData(TempData temp, ControlParam ctrl)
{
    char buffer[128];
    
    sprintf(buffer, "TEMP:%.1f,SET:%.1f,HEAT:%d%%,FAN:%d%%,KP:%.2f,KI:%.2f,KD:%.2f\r\n",
            temp.current, temp.target, ctrl.heat_duty, ctrl.fan_duty,
            ctrl.kp, ctrl.ki, ctrl.kd);
    
    UART_SendString(buffer);
}

/* 串口中断处理函数 */
void USART1_IRQHandler(void)
{
    uint8_t data;
    
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        data = USART_ReceiveData(USART1);
        
        if(rx_index < sizeof(rx_buffer)-1)
        {
            rx_buffer[rx_index++] = data;
        }
        
        /* 处理命令 */
        if(data == '\n' || data == '\r')
        {
            rx_buffer[rx_index] = '\0';
            Process_Command((char*)rx_buffer);
            rx_index = 0;
        }
    }
}

/* 处理串口命令 */
void Process_Command(char* cmd)
{
    if(strstr(cmd, "GET_TEMP"))
    {
        char response[50];
        sprintf(response, "CURRENT_TEMP:%.1f\r\n", temp_data.current);
        UART_SendString(response);
    }
    else if(strstr(cmd, "SET_TEMP:"))
    {
        float new_temp = atof(cmd + 9);
        if(new_temp >= TEMP_MIN && new_temp <= TEMP_MAX)
        {
            temp_data.target = new_temp;
            UART_SendString("OK\r\n");
        }
        else
        {
            UART_SendString("ERROR: Invalid temperature\r\n");
        }
    }
}

三、Keil工程配置

3.1 工程设置

Target:
  - Device: STM32F103C8T6
  - Clock: 8MHz HSE → 72MHz PLL
  - RAM: 20KB
  - Flash: 64KB

C/C++:
  - Define: USE_STDPERIPH_DRIVER, STM32F103C8T6
  - Include Paths:
    .\
    ..\Libraries\CMSIS\Include
    ..\Libraries\STM32F10x_StdPeriph_Driver\inc
    ..\User
    ..\User\drivers

Linker:
  - IROM1: 0x08000000, 0x10000  (64KB Flash)
  - IRAM1: 0x20000000, 0x5000   (20KB RAM)

3.2 编译优化

Optimization Level: -O1
One ELF Section per Function: ✓

参考代码 stm32单片机温度控制系统 www.youwenfan.com/contentcnu/56404.html

四、测试与调试

4.1 测试用例

/* 测试1:温度采集测试 */
void Test_TempSensor(void)
{
    float temp;
    
    temp = DS18B20_GetTemp();
    printf("Current Temp: %.2f C\r\n", temp);
    
    if(temp < -50 || temp > 150)
    {
        printf("Sensor Error!\r\n");
    }
}

/* 测试2:PID控制测试 */
void Test_PIDControl(void)
{
    ControlParam test_ctrl = {2.0f, 0.5f, 0.1f, 0, 0, 0, 0, 0};
    float target = 50.0f;
    float current = 25.0f;
    
    for(int i = 0; i < 100; i++)
    {
        PID_TempControl(&test_ctrl, target, current);
        current += (test_ctrl.output - 50) * 0.1;  // 模拟温度变化
        
        printf("Step %d: Output=%.1f%%, Current=%.1f°C\r\n", 
               i, test_ctrl.output, current);
        
        if(fabs(target - current) < 1.0f)
        {
            printf("PID Stable!\r\n");
            break;
        }
    }
}

/* 测试3:加热控制测试 */
void Test_HeaterControl(void)
{
    printf("Testing Heater...\r\n");
    
    Control_SetHeat(0);
    Delay_Ms(1000);
    
    Control_SetHeat(50);
    printf("Heater 50%% ON\r\n");
    Delay_Ms(2000);
    
    Control_SetHeat(100);
    printf("Heater 100%% ON\r\n");
    Delay_Ms(2000);
    
    Control_SetHeat(0);
    printf("Heater OFF\r\n");
}

4.2 调试输出示例

Temp Control v2.0
Initializing...

TEMP:25.5,SET:25.0,HEAT:10%,FAN:0%,KP:2.00,KI:0.50,KD:0.10
TEMP:26.2,SET:25.0,HEAT:30%,FAN:10%,KP:2.00,KI:0.50,KD:0.10
TEMP:25.1,SET:25.0,HEAT:5%,FAN:0%,KP:2.00,KI:0.50,KD:0.10
PID Stable!

GET_TEMP
CURRENT_TEMP:25.10

五、安全与优化建议

5.1 安全保护

  1. 双重温度监测:DS18B20 + NTC双传感器互备
  2. 硬件看门狗:STM32内置IWDG(1秒超时)
  3. 过流保护:ACS712电流传感器监测加热电流
  4. 断电保护:EEPROM保存运行状态

5.2 性能优化

  1. 自适应PID:根据温差自动调整PID参数
  2. 模糊控制:结合PID和模糊逻辑
  3. 远程监控:通过WiFi/蓝牙上传数据
  4. 数据记录:SD卡存储历史温度曲线

5.3 扩展功能

/* 定时控制 */
typedef struct {
    uint8_t enable;
    uint8_t hour;
    uint8_t minute;
    float temp;
} ScheduleTask;

/* 多区域控制 */
typedef struct {
    float zone1_temp;
    float zone2_temp;
    uint8_t zone1_output;
    uint8_t zone2_output;
} MultiZoneControl;
posted @ 2026-05-06 09:10  w199899899  阅读(59)  评论(0)    收藏  举报