项目概述

智能温控器是嵌入式领域的经典实战项目,它融合了传感器采集、控制算法、人机交互等多个模块。本教程选用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模拟时序):

// 复位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):

#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控制器

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极性需配置为反相。

// 初始化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输出和状态。

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。按键中断调整目标温度。

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.6Kp_crit,Ki=2Kp/Tu,Kd=Kp*Tu/8。
  • 微调:若超调大,减小Kp或增大Kd;若稳态误差大,增大Ki。

注意事项

  • 安全第一:加热棒接220V,务必使用SSR隔离,并加装保险丝和过热保护。
  • DS18B20时序:单总线对延时敏感,建议使用定时器或DWT精确延时,避免被中断干扰。
  • PID输出限幅:防止积分饱和,增量式已天然抗饱和,但输出仍需限幅。
  • 电源隔离:数字电路与加热电路分开供电,避免干扰。
  • 调试技巧:通过串口打印温度与PWM值,用上位机绘制曲线,便于观察响应。

总结

通过本实战,你已掌握智能温控器的完整设计流程。从传感器驱动到PID算法,再到PWM控制与显示,每个环节都至关重要。你可以进一步扩展:加入WiFi远程监控、模糊PID自适应、或改用更高级的加热方式。动手实践吧,让温度控制变得精准而智能!