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 安全保护
- 双重温度监测:DS18B20 + NTC双传感器互备
- 硬件看门狗:STM32内置IWDG(1秒超时)
- 过流保护:ACS712电流传感器监测加热电流
- 断电保护:EEPROM保存运行状态
5.2 性能优化
- 自适应PID:根据温差自动调整PID参数
- 模糊控制:结合PID和模糊逻辑
- 远程监控:通过WiFi/蓝牙上传数据
- 数据记录: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;
浙公网安备 33010602011771号