一、系统总体设计
1、系统架构
复制代码
┌─────────────────────────────────────────────────────────┐
│ 恒定水温控制系统 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 温度采集层 │ │ 控制算法层 │ │ 执行控制层 │ │
│ │ │ │ │ │ │ │
│ │ • DS18B20 │───▶│ • PID控制 │───▶│ • PWM加热 │ │
│ │ • NTC热敏电阻│ │ • 自适应调节 │ │ • 固态继电器 │ │
│ │ • 滤波算法 │ │ • 抗积分饱和 │ │ • 散热风扇 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 人机交互层 │ │ 系统监控层 │ │ 通信接口层 │ │
│ │ │ │ │ │ │ │
│ │ • OLED显示 │ │ • 超温保护 │ │ • USART │ │
│ │ • 按键设置 │ │ • 传感器诊断 │ │ • RS485 │ │
│ │ • LED指示 │ │ • 故障报警 │ │ • WiFi可选 │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
2、技术指标
| 参数 |
指标 |
| 控温范围 |
20°C ~ 90°C |
| 控温精度 |
±0.5°C |
| 稳定时间 |
< 5分钟 |
| 加热功率 |
500W ~ 2000W |
| 显示分辨率 |
0.1°C |
| 工作电压 |
AC 220V / DC 12V |
二、硬件设计
1、核心器件选型
| 器件 |
型号 |
说明 |
| MCU |
STM32F103C8T6 |
72MHz,64KB Flash,20KB RAM |
| 温度传感器 |
DS18B20 |
数字输出,-55°C~125°C,±0.5°C |
| 备用传感器 |
NTC 10K |
模拟输出,高精度 |
| 加热元件 |
不锈钢加热管 |
1000W/1500W,220V |
| 功率控制 |
SSR-40DA |
固态继电器,40A/250VAC |
| 显示模块 |
OLED 128x64 |
I2C接口,0.96寸 |
| 电源模块 |
HLK-PM12 |
AC220V转DC12V |
| 散热风扇 |
5015直流风扇 |
12V,用于过热保护 |
2、硬件连接图
复制代码
STM32F103C8T6 引脚分配:
┌─────────────────────────────────────────────┐
│ GPIOA │
│ PA0 ── NTC_ANALOG (ADC1_IN0) │
│ PA1 ── HEATER_PWM (TIM2_CH2) │
│ PA2 ── FAN_PWM (TIM2_CH3) │
│ PA3 ── DS18B20_DQ (单总线) │
│ PA4 ── OLED_CS (SPI1_NSS) │
│ PA5 ── OLED_SCK (SPI1_SCK) │
│ PA6 ── OLED_MISO (SPI1_MISO) │
│ PA7 ── OLED_MOSI (SPI1_MOSI) │
│ PA8 ── KEY_SET (按键设置) │
│ PA9 ── USART1_TX (调试输出) │
│ PA10 ── USART1_RX (调试输入) │
│ │
│ GPIOB │
│ PB0 ── KEY_UP (按键+) │
│ PB1 ── KEY_DOWN (按键-) │
│ PB6 ── OLED_SDA (I2C1_SDA) │
│ PB7 ── OLED_SCL (I2C1_SCL) │
│ PB8 ── ALARM_LED (报警指示) │
│ PB9 ── STATUS_LED (状态指示) │
└─────────────────────────────────────────────┘
3、关键电路设计
加热控制电路
复制代码
STM32 PA1 ──┬── 1kΩ ──┬── SSR控制端+
│ │
└── 10kΩ ──┴── SSR控制端-
│
AC 220V ──── 加热管 ──── SSR负载端 ─── AC 220V
温度采集电路
复制代码
DS18B20:
VDD ── 3.3V
DQ ── PA3 (需4.7kΩ上拉)
GND ── GND
NTC 10K:
3.3V ── 10kΩ ──┬── PA0 (ADC)
│
└── NTC ── GND
三、软件系统设计
1、系统状态机
c
复制代码
typedef enum {
SYS_INIT = 0, // 系统初始化
SYS_IDLE, // 空闲状态
SYS_HEATING, // 加热状态
SYS_STABLE, // 恒温状态
SYS_COOLING, // 降温状态
SYS_ALARM, // 报警状态
SYS_MAINTENANCE // 维护状态
} System_State_t;
2、PID 控制器设计
c
复制代码
/* PID 控制器结构体 */
typedef struct {
float Kp; // 比例系数
float Ki; // 积分系数
float Kd; // 微分系数
float target; // 目标温度
float feedback; // 反馈温度
float error; // 当前误差
float error_last; // 上次误差
float integral; // 积分项
float derivative; // 微分项
float output; // 控制器输出
float out_max; // 输出上限
float out_min; // 输出下限
float integral_max; // 积分限幅
} PID_Controller_t;
3、主程序框架
c
复制代码
#include "stm32f10x.h"
#include "ds18b20.h"
#include "oled.h"
#include "pid.h"
#include "heater.h"
/* 全局变量 */
System_State_t system_state = SYS_INIT;
PID_Controller_t temp_pid;
float current_temp = 0.0f;
float target_temp = 60.0f; // 默认目标温度60°C
int main(void)
{
/* 系统初始化 */
System_Init();
/* PID 初始化 */
PID_Init(&temp_pid);
while(1) {
/* 温度采集 */
current_temp = DS18B20_GetTemp();
/* 系统状态机 */
switch(system_state) {
case SYS_INIT:
System_Init_Process();
break;
case SYS_HEATING:
Heating_Process();
break;
case SYS_STABLE:
Stable_Process();
break;
case SYS_ALARM:
Alarm_Process();
break;
default:
break;
}
/* 显示更新 */
Display_Update();
/* 延时 */
Delay_ms(100);
}
}
4、PID 控制器实现
c
复制代码
#include "pid.h"
/* PID 初始化 */
void PID_Init(PID_Controller_t *pid)
{
pid->Kp = 25.0f; // 比例系数
pid->Ki = 0.8f; // 积分系数
pid->Kd = 120.0f; // 微分系数
pid->out_max = 100.0f; // 输出限幅100%
pid->out_min = 0.0f;
pid->integral_max = 50.0f;
pid->integral = 0.0f;
pid->error_last = 0.0f;
}
/* PID 计算 */
float PID_Calculate(PID_Controller_t *pid, float target, float feedback)
{
pid->target = target;
pid->feedback = feedback;
pid->error = target - feedback;
/* 比例项 */
float proportional = pid->Kp * pid->error;
/* 积分项(带抗积分饱和) */
pid->integral += pid->Ki * pid->error;
if(pid->integral > pid->integral_max)
pid->integral = pid->integral_max;
if(pid->integral < -pid->integral_max)
pid->integral = -pid->integral_max;
/* 微分项 */
pid->derivative = pid->Kd * (pid->error - pid->error_last);
pid->error_last = pid->error;
/* PID 输出 */
pid->output = proportional + pid->integral + pid->derivative;
/* 输出限幅 */
if(pid->output > pid->out_max)
pid->output = pid->out_max;
if(pid->output < pid->out_min)
pid->output = pid->out_min;
return pid->output;
}
5、加热器控制
c
复制代码
#include "heater.h"
/* 加热器初始化 */
void Heater_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* PWM 定时器配置 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period = 999; // PWM周期 1kHz
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 72MHz/72 = 1MHz
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OC2PreloadConfig(TIM2, TIM_OCPreload_Enable);
TIM_Cmd(TIM2, ENABLE);
}
/* 设置加热功率 */
void Heater_SetPower(float power_percent)
{
uint16_t pwm_value;
if(power_percent > 100.0f) power_percent = 100.0f;
if(power_percent < 0.0f) power_percent = 0.0f;
pwm_value = (uint16_t)(power_percent * 10.0f); // 0-1000
TIM_SetCompare2(TIM2, pwm_value);
}
6、温度采集(DS18B20)
c
复制代码
#include "ds18b20.h"
/* DS18B20 初始化 */
uint8_t DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
DS18B20_Reset();
return DS18B20_Check();
}
/* 获取温度 */
float DS18B20_GetTemp(void)
{
uint8_t temp_l, temp_h;
int16_t temp;
float temperature;
DS18B20_Start();
DS18B20_WriteByte(0xCC); // Skip ROM
DS18B20_WriteByte(0xBE); // Read Scratchpad
temp_l = DS18B20_ReadByte();
temp_h = DS18B20_ReadByte();
temp = (temp_h << 8) | temp_l;
temperature = (float)temp * 0.0625f;
return temperature;
}
7、OLED 显示界面
c
复制代码
#include "oled.h"
/* 显示主界面 */
void Display_MainInterface(float current_temp, float target_temp, System_State_t state)
{
char buffer[20];
OLED_Clear();
/* 显示标题 */
OLED_ShowString(0, 0, "Water Temp Ctrl");
/* 显示当前温度 */
sprintf(buffer, "Now: %.1f C", current_temp);
OLED_ShowString(0, 2, buffer);
/* 显示目标温度 */
sprintf(buffer, "Set: %.1f C", target_temp);
OLED_ShowString(0, 4, buffer);
/* 显示系统状态 */
switch(state) {
case SYS_HEATING:
OLED_ShowString(0, 6, "Status: HEATING");
break;
case SYS_STABLE:
OLED_ShowString(0, 6, "Status: STABLE ");
break;
case SYS_ALARM:
OLED_ShowString(0, 6, "Status: ALARM ");
break;
default:
OLED_ShowString(0, 6, "Status: IDLE ");
break;
}
}
四、高级功能实现
1、自适应 PID 参数调整
c
复制代码
/* 自适应 PID 调整 */
void Adaptive_PID_Adjust(PID_Controller_t *pid, float error)
{
static float error_history[10];
static uint8_t index = 0;
/* 存储误差历史 */
error_history[index] = fabsf(error);
index = (index + 1) % 10;
/* 计算平均误差 */
float avg_error = 0;
for(int i = 0; i < 10; i++) {
avg_error += error_history[i];
}
avg_error /= 10;
/* 根据误差调整 PID 参数 */
if(avg_error > 5.0f) { // 大误差,增强比例作用
pid->Kp = 35.0f;
pid->Ki = 0.5f;
pid->Kd = 80.0f;
} else if(avg_error > 2.0f) { // 中等误差
pid->Kp = 25.0f;
pid->Ki = 0.8f;
pid->Kd = 120.0f;
} else { // 小误差,增强积分作用
pid->Kp = 15.0f;
pid->Ki = 1.2f;
pid->Kd = 150.0f;
}
}
2、安全保护机制
c
复制代码
/* 安全监控 */
void Safety_Monitor(float current_temp, float target_temp)
{
static uint32_t overheat_time = 0;
/* 超温保护 */
if(current_temp > 95.0f) {
Heater_SetPower(0); // 立即关闭加热
system_state = SYS_ALARM;
Alarm_Trigger(ALARM_OVERHEAT);
return;
}
/* 传感器故障检测 */
if(current_temp < -50.0f || current_temp > 150.0f) {
system_state = SYS_ALARM;
Alarm_Trigger(ALARM_SENSOR_FAULT);
return;
}
/* 长时间未达目标温度报警 */
if(fabsf(current_temp - target_temp) > 10.0f) {
if(overheat_time == 0) {
overheat_time = HAL_GetTick();
} else if(HAL_GetTick() - overheat_time > 1800000) { // 30分钟
system_state = SYS_ALARM;
Alarm_Trigger(ALARM_HEATING_TIMEOUT);
overheat_time = 0;
}
} else {
overheat_time = 0;
}
}
3、数据记录与统计
c
复制代码
/* 温度数据记录 */
typedef struct {
uint32_t timestamp;
float temperature;
float target_temp;
float heating_power;
System_State_t state;
} Temp_Record_t;
#define MAX_RECORDS 100
Temp_Record_t temp_records[MAX_RECORDS];
uint16_t record_index = 0;
void Record_Temperature(float temp, float target, float power, System_State_t state)
{
temp_records[record_index].timestamp = HAL_GetTick();
temp_records[record_index].temperature = temp;
temp_records[record_index].target_temp = target;
temp_records[record_index].heating_power = power;
temp_records[record_index].state = state;
record_index = (record_index + 1) % MAX_RECORDS;
}
参考代码 基于STM32F103C8T6的恒定水温控制系统 www.youwenfan.com/contentcsv/72753.html
五、系统调试与优化
1、PID 参数整定步骤
复制代码
1. 纯比例控制(Ki=0, Kd=0)
- 从小到大调整 Kp,直到系统出现小幅振荡
- 记录临界比例度 Ku 和振荡周期 Tu
2. Ziegler-Nichols 整定法
Kp = 0.6 * Ku
Ki = 1.2 * Ku / Tu
Kd = 0.075 * Ku * Tu
3. 微调优化
- 响应过快:减小 Kp,增大 Kd
- 稳态误差大:增大 Ki
- 振荡严重:减小 Kp,增大 Kd
2、性能测试指标
| 测试项目 |
测试方法 |
合格标准 |
| 稳态精度 |
设定60°C,稳定后测量 |
±0.5°C |
| 动态响应 |
从30°C升至60°C |
< 5分钟 |
| 抗干扰性 |
加入冷水扰动 |
恢复时间<2分钟 |
| 长期稳定性 |
连续运行24小时 |
温度漂移<1°C |
| 安全保护 |
人为制造超温 |
立即切断加热 |
六、扩展功能建议
1、远程监控(可选)
c
复制代码
/* WiFi 模块通信 */
void ESP8266_Send_Temp_Data(float temp, float target)
{
char json_buffer[100];
sprintf(json_buffer,
"{\"temp\":%.1f,\"target\":%.1f,\"status\":%d}",
temp, target, system_state);
USART_SendString(USART1, "AT+CIPSEND=0,");
USART_SendString(USART1, json_buffer);
}
2、多段温控程序
c
复制代码
/* 多段温控 */
typedef struct {
float target_temp;
uint32_t hold_time; // 保持时间(秒)
uint32_t ramp_time; // 升温时间(秒)
} Temp_Segment_t;
Temp_Segment_t temp_program[5] = {
{40.0f, 300, 120}, // 40°C,保持5分钟,2分钟升温
{60.0f, 600, 180}, // 60°C,保持10分钟,3分钟升温
{80.0f, 300, 120}, // 80°C,保持5分钟,2分钟升温
{60.0f, 600, 180}, // 降温到60°C
{40.0f, 300, 120} // 最终40°C
};