智能温控器实战:PID + 加热棒 + OLED 显示,打造高精度恒温系统
👁 10 阅读 · 2026-08-20 · 项目 实战
本文带你从零构建一个基于STM32的智能温控器,融合增量式PID算法、PWM驱动加热棒、以及OLED实时显示。通过实战项目,你将掌握PID参数整定、温度采样滤波、PWM功率控制等核心技能,并学会如何设计一个稳定、可扩展的温控系统。文章提供完整代码与调试技巧,助你快速上手。
## 项目概述
智能温控器是嵌入式领域的经典实战项目,它融合了传感器采集、控制算法、人机交互等多个模块。本教程选用STM32F103作为主控,搭配DS18B20温度传感器、固态继电器(SSR)驱动的加热棒,以及0.96寸OLED显示屏,实现目标温度设定、实时温度显示和PID闭环控制。
## 系统架构与原理
### 硬件组成
- **主控**:STM32F103C8T6(72MHz,资源丰富)
- **温度传感器**:DS18B20(单总线,12位精度,-55~125°C)
- **加热执行器**:加热棒(220V/500W)通过SSR控制,SSR由PWM信号驱动
- **显示模块**:SSD1306驱动的OLED(I2C接口,128x64)
- **按键**:两个按键用于设定目标温度(加减)
### 控制原理
PID(比例-积分-微分)控制器通过计算偏差的线性组合来调节输出。本系统采用**增量式PID**,输出为PWM占空比的增量,避免积分饱和,适合加热棒这类惯性较大的对象。
公式:
```
Δu = Kp*(e(k)-e(k-1)) + Ki*e(k) + Kd*(e(k)-2*e(k-1)+e(k-2))
```
其中e(k)为当前偏差(目标温度-实际温度)。
## 关键模块实现
### 1. DS18B20驱动
DS18B20使用单总线协议,时序要求严格。以下为初始化与读取温度的核心代码(使用GPIO模拟时序):
```c
// 复位DS18B20
uint8_t DS18B20_Reset(void) {
uint8_t presence;
GPIO_InitTypeDef gpio;
// 配置为输出
gpio.Mode = GPIO_MODE_OUTPUT_OD;
gpio.Pin = DS18B20_PIN;
HAL_GPIO_Init(DS18B20_PORT, &gpio);
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_RESET);
delay_us(480);
HAL_GPIO_WritePin(DS18B20_PORT, DS18B20_PIN, GPIO_PIN_SET);
delay_us(60);
// 配置为输入
gpio.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(DS18B20_PORT, &gpio);
presence = HAL_GPIO_ReadPin(DS18B20_PORT, DS18B20_PIN);
delay_us(420);
return presence; // 0表示存在
}
// 读取温度(12位精度)
float DS18B20_ReadTemp(void) {
uint8_t tempL, tempH;
int16_t temp;
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 启动转换
while(DS18B20_ReadBit()==0); // 等待转换完成
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读取暂存器
tempL = DS18B20_ReadByte();
tempH = DS18B20_ReadByte();
temp = (tempH << 8) | tempL;
return temp * 0.0625; // 12位分辨率
}
```
### 2. 温度滤波
由于环境噪声,采样值需滤波。采用滑动平均滤波(窗口5):
```c
#define FILTER_SIZE 5
float filter_buf[FILTER_SIZE];
uint8_t filter_index = 0;
float Temperature_Filter(float new_val) {
float sum = 0;
filter_buf[filter_index] = new_val;
filter_index = (filter_index + 1) % FILTER_SIZE;
for (int i = 0; i < FILTER_SIZE; i++) sum += filter_buf[i];
return sum / FILTER_SIZE;
}
```
### 3. 增量式PID控制器
```c
typedef struct {
float Kp, Ki, Kd;
float target, actual;
float err, last_err, prev_err;
float output; // 占空比增量
} PID_TypeDef;
void PID_Init(PID_TypeDef *pid, float kp, float ki, float kd) {
pid->Kp = kp; pid->Ki = ki; pid->Kd = kd;
pid->err = pid->last_err = pid->prev_err = 0;
pid->output = 0;
}
float PID_Calc(PID_TypeDef *pid, float target, float actual) {
pid->target = target;
pid->actual = actual;
pid->prev_err = pid->last_err;
pid->last_err = pid->err;
pid->err = target - actual;
// 增量式输出
pid->output += pid->Kp*(pid->err - pid->last_err) +
pid->Ki*pid->err +
pid->Kd*(pid->err - 2*pid->last_err + pid->prev_err);
// 限幅(0~100%占空比)
if (pid->output > 100) pid->output = 100;
if (pid->output < 0) pid->output = 0;
return pid->output;
}
```
### 4. PWM控制加热棒
使用定时器2产生20kHz的PWM,通过比较值控制占空比。SSR需要低电平触发,因此PWM极性需配置为反相。
```c
// 初始化PWM
void PWM_Init(void) {
TIM_HandleTypeDef htim2;
TIM_OC_InitTypeDef sConfig;
__HAL_RCC_TIM2_CLK_ENABLE();
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1; // 1MHz计数频率
htim2.Init.Period = 50-1; // 20kHz
HAL_TIM_PWM_Init(&htim2);
sConfig.OCMode = TIM_OCMODE_PWM1;
sConfig.Pulse = 0; // 初始0%占空比
sConfig.OCPolarity = TIM_OCPOLARITY_LOW; // 反相,低电平有效
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfig, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
}
// 设置占空比(0~100)
void PWM_SetDuty(uint8_t duty) {
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty*50/100);
}
```
### 5. OLED显示
使用SSD1306库,显示目标温度、实际温度、PID输出和状态。
```c
void OLED_Display(float target, float actual, float duty) {
char buf[20];
OLED_Clear();
OLED_ShowString(0, 0, "Smart Temp Ctrl");
sprintf(buf, "Target: %.1f C", target);
OLED_ShowString(0, 2, buf);
sprintf(buf, "Actual: %.1f C", actual);
OLED_ShowString(0, 4, buf);
sprintf(buf, "PWM: %.0f%%", duty);
OLED_ShowString(0, 6, buf);
OLED_Refresh();
}
```
## 主程序与PID参数整定
主循环每200ms读取温度、计算PID并更新PWM。按键中断调整目标温度。
```c
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化各模块...
PID_TypeDef pid;
PID_Init(&pid, 20, 0.5, 5); // 初始参数
float target = 50.0;
while (1) {
float temp = Temperature_Filter(DS18B20_ReadTemp());
float duty = PID_Calc(&pid, target, temp);
PWM_SetDuty((uint8_t)duty);
OLED_Display(target, temp, duty);
HAL_Delay(200);
}
}
```
**参数整定经验**:
- 先设Ki=Kd=0,增大Kp直到系统振荡,记录临界Kp和振荡周期Tu。
- 然后按Ziegler-Nichols法:Kp=0.6*Kp_crit,Ki=2*Kp/Tu,Kd=Kp*Tu/8。
- 微调:若超调大,减小Kp或增大Kd;若稳态误差大,增大Ki。
## 注意事项
- **安全第一**:加热棒接220V,务必使用SSR隔离,并加装保险丝和过热保护。
- **DS18B20时序**:单总线对延时敏感,建议使用定时器或DWT精确延时,避免被中断干扰。
- **PID输出限幅**:防止积分饱和,增量式已天然抗饱和,但输出仍需限幅。
- **电源隔离**:数字电路与加热电路分开供电,避免干扰。
- **调试技巧**:通过串口打印温度与PWM值,用上位机绘制曲线,便于观察响应。
## 总结
通过本实战,你已掌握智能温控器的完整设计流程。从传感器驱动到PID算法,再到PWM控制与显示,每个环节都至关重要。你可以进一步扩展:加入WiFi远程监控、模糊PID自适应、或改用更高级的加热方式。动手实践吧,让温度控制变得精准而智能!