当前位置: 首页 > article >正文

基于 STM32 的高精度 PID 温控系统设计与实现:采用 Pt1000 温度传感器与 PWM 控制技术

一、项目概述

随着科技的发展和工业自动化的普及,高精度的温度控制系统在各个领域的应用越来越广泛,如实验室设备、食品加工、电子元器件测试等。本项目基于 STM32 微控制器设计了一种快速、高精度的 PID 温控系统。该系统采用比例积分微分(PID)控制算法,能够实时监测和调整温度,确保控制精度。

项目目标

本项目旨在开发一个能够快速响应并高精度控制温度的系统,适用于对温控的响应速度和精度要求较高的场合。系统将通过上位机进行设定温度输入,同时监控系统的运行状态。

技术栈关键词

  • 硬件:STM32 微控制器、Pt1000 温度传感器、TLP521-1 光电耦合器、BD237 功率晶体管、FTA951 热电冷却器(TEC)

  • 软件:PID 控制算法、PWM 控制、串口通信

  • 开发环境:Keil MDK,STM32CubeMX

二、系统架构

设计系统架构

本系统采用 STM32 作为主控单元,温度采集电路通过 Pt1000 电阻温度计进行温度传感。TLP521-1 光电耦合器与 BD237 功率晶体管的组合用于驱动 TEC。PWM 信号用于控制 TEC 的工作状态,进而实现温度调节。

系统架构图

设定温度
读取温度
温度信号
PWM信号
驱动
控制
串口监控
上位机
STM32 MCU
Pt1000 温度传感器
TLP521-1 光电耦合器
BD237 功率晶体管
FTA951 热电冷却器TEC

组件选择

  • STM32 微控制器:选择 STM32F103C8T6,具有较强的处理能力和丰富的外设接口。

  • Pt1000 温度传感器:高精度,适用于温度测量。

  • TLP521-1 光电耦合器:用于隔离控制信号和功率电路。

  • BD237 功率晶体管:用于驱动 TEC,控制大电流。

  • FTA951 热电冷却器:用于温度控制,快速响应。

三、环境搭建和注意事项

环境搭建

  1. 硬件连接:

    • 将 Pt1000 温度传感器连接到 STM32 的 ADC 引脚。

    • 使用 TLP521-1 光电耦合器将 STM32 的 PWM 输出连接到 BD237 功率晶体管。

    • 将 BD237 的输出连接到 FTA951 热电冷却器。

  2. 软件准备:

    • 安装 STM32CubeMX 和 Keil MDK 开发环境。

    • 配置 STM32 的 ADC、PWM、串口等外设。

注意事项

  • 确保电路连接正确,避免短路和干扰。

  • 在调试过程中,注意温度传感器的校准,确保测量准确。

  • 避免 TEC 超负荷工作,以免损坏。

四、代码实现过程

在本节中,我们将详细介绍基于 STM32 的 PID 温控系统的代码实现过程,包括温度采集、PID 控制算法的实现、PWM 输出控制等。每个模块将配有代码示例和详细说明,以确保代码逻辑清晰、易于理解和维护。

1.主程序框架

代码示例

以下是 STM32 PID 温控系统的主程序框架,包含温度采集、PID 控制和 PWM 输出的核心代码。

#include "stm32f4xx.h"
#include "pid.h"
#include "temperature.h"
#include "pwm.h"
#include "usart.h"

// PID 控制参数和目标温度
float setpoint = 25.0; // 设定温度(目标温度)
PID_Controller pid;     // PID 控制器实例

void setup() {
    USART_Init();           // 初始化串口
    ADC_Init();             // 初始化ADC,用于温度采集
    PWM_Init();             // 初始化PWM,用于控制TEC
    PID_Init(&pid, 1.0, 0.1, 0.01); // 初始化PID控制器,设置Kp, Ki, Kd
}

int main() {
    setup();                // 进行系统初始化
    while (1) {
        // 获取当前温度
        float current_temp = Get_Temperature(); 

        // 计算PWM值
        float pwm_value = PID_Compute(&pid, setpoint, current_temp);

        // 根据计算得到的PWM值进行控制
        Set_PWM(pwm_value); 

        // 通过串口输出当前温度和PWM值,便于调试
        USART_Send(current_temp, pwm_value);

        // 延时,避免程序过快循环
        HAL_Delay(1000); // 每秒更新一次
    }
}

代码说明

  1. 库文件引用:

    • #include "stm32f4xx.h":包含 STM32F4 系列的标准库。

    • 其他头文件如 pid.htemperature.hpwm.husart.h 分别用于 PID 控制、温度读取、PWM 控制和串口通信。

  2. 全局变量定义:

    • float setpoint = 25.0;:设定温度为 25°C,用户可以根据需求调整此值。

    • PID_Controller pid;:创建一个 PID 控制器实例,用于管理 PID 控制的参数和状态。

  3. 系统初始化:

    • USART_Init():初始化串口,用于数据传输和调试。

    • ADC_Init():初始化 ADC 模块,用于读取温度传感器的数据。

    • PWM_Init():初始化 PWM 模块,用于控制热电冷却器(TEC)的输出。

    • PID_Init(&pid, 1.0, 0.1, 0.01):初始化 PID 控制器,并设置比例(Kp)、积分(Ki)和微分(Kd)参数。

  4. 主循环:

    • Get_Temperature():调用温度读取函数,获取当前温度值。

    • PID_Compute(&pid, setpoint, current_temp):根据设定温度和当前温度计算 PWM 控制值。

    • Set_PWM(pwm_value):根据 PID 计算得到的 PWM 值调整 TEC 的工作状态。

    • USART_Send(current_temp, pwm_value):通过串口发送当前温度和 PWM 值,便于监控和调试。

    • HAL_Delay(1000):延时 1 秒,以避免循环过快导致 CPU 占用过高。

2.温度读取模块

代码示例

以下是温度读取模块代码,使用 ADC 读取 Pt1000 温度传感器的温度。

#include "temperature.h"

float Get_Temperature() {
    // 启动 ADC 转换
    HAL_ADC_Start(&hadc1);

    // 等待 ADC 转换完成
    HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);

    // 获取 ADC 转换结果
    uint32_t adc_value = HAL_ADC_GetValue(&hadc1);

    // 将 ADC 值转换为温度(假设线性关系)
    float temperature = (adc_value / 4095.0) * 100.0; // 线性转换,假设满量程为 100°C
    
    return temperature; // 返回温度值
}

代码说明

  • 函数定义:

    • float Get_Temperature():定义一个函数,用于读取温度传感器的值并返回当前温度。
  • ADC 启动和转换:

    • HAL_ADC_Start(&hadc1):启动 ADC 转换。hadc1 是 ADC 的句柄,需根据具体配置进行定义。

    • HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY):等待 ADC 转换完成。此函数会阻塞,直到转换结束。

  • 获取 ADC 值:

    • uint32_t adc_value = HAL_ADC_GetValue(&hadc1):获取 ADC 转换的结果,返回值为 ADC 的数字表示(0 到 4095,通常为 12 位分辨率)。
  • 温度转换:

    • float temperature = (adc_value / 4095.0) * 100.0:将 ADC 值转换为温度值。这里假设 Pt1000 温度传感器的输出为线性关系,满量程为 100°C,具体转换关系需根据温度传感器的特性曲线进行调整。
  • 返回温度值:

    • return temperature:返回计算得到的温度值。

3.PID 控制模块

代码示例

以下是 PID 控制模块的代码,实现 PID 控制算法。

#include "pid.h"

// 初始化 PID 控制器
void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd) {
    pid->Kp = Kp; // 比例增益
    pid->Ki = Ki; // 积分增益
    pid->Kd = Kd; // 微分增益
    pid->previous_error = 0.0; // 初始化误差
    pid->integral = 0.0; // 初始化积分
}

// 计算 PID 控制输出
float PID_Compute(PID_Controller *pid, float setpoint, float measured_value) {
    // 计算当前误差
    float error = setpoint - measured_value;

    // 计算积分
    pid->integral += error;

    // 计算微分
    float derivative = error - pid->previous_error;

    // 计算 PID 输出
    float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative;

    // 更新上一误差
    pid->previous_error = error;

    // 输出限制(根据系统要求进行限制)
    if (output > 100.0) output = 100.0; // 限制最大输出
    if (output < 0.0) output = 0.0;     // 限制最小输出

    return output; // 返回控制输出
}

代码说明

  • PID 控制器结构体:

    • PID_Controller 结构体包含 PID 控制器的参数和状态变量(如比例增益 Kp、积分增益 Ki、微分增益 Kd、上次误差、积分值等)。
  • 初始化 PID 控制器:

    • void PID_Init(PID_Controller *pid, float Kp, float Ki, float Kd):初始化 PID 控制器的参数,并将误差和积分初始化为 0。
  • 计算 PID 控制输出:

    • float PID_Compute(PID_Controller *pid, float setpoint, float measured_value):根据设定值和测量值计算 PID 控制输出。

    • float error = setpoint - measured_value:计算当前误差。

    • pid->integral += error:累加误差,计算积分项。

    • float derivative = error - pid->previous_error:计算微分项,表示误差的变化率。

    • float output = pid->Kp * error + pid->Ki * pid->integral + pid->Kd * derivative:计算 PID 控制输出。

    • pid->previous_error = error:更新上一误差,用于下次计算微分项。

  • 输出限制:为了防止输出超出系统控制范围,设置了输出的最大值和最小值。

4.PWM 控制模块

代码示例

以下是 PWM 控制模块的代码,负责根据计算得到的 PWM 值控制 TEC 的工作。

#include "pwm.h"

// 初始化 PWM
void PWM_Init() {
    // 1. 定义定时器句柄
    TIM_HandleTypeDef htim2;

    // 2. 启用定时器时钟
    __HAL_RCC_TIM2_CLK_ENABLE();

    // 3. 设置定时器基础参数
    htim2.Instance = TIM2; // 选择定时器2
    htim2.Init.Prescaler = 84 - 1; // 预分频器设置(假设72MHz的时钟频率,分频到1MHz)
    htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
    htim2.Init.Period = 1000 - 1; // 设定PWM周期(1ms)
    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;

    // 4. 初始化定时器
    HAL_TIM_PWM_Init(&htim2);

    // 5. PWM输出通道配置
    TIM_OC_InitTypeDef sConfigOC;
    sConfigOC.OCMode = TIM_OCMODE_PWM1; // PWM模式1
    sConfigOC.Pulse = 0; // 初始占空比为0%
    sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; // 输出极性:高电平有效
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; // 禁用快速模式

    // 6. 启用PWM通道
    HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动PWM通道
}

// 设置PWM输出
void Set_PWM(float pwm_value) {
    // 限制PWM值在0到1000之间
    if (pwm_value > 1000.0) pwm_value = 1000.0;
    if (pwm_value < 0.0) pwm_value = 0.0;

    // 设置PWM的占空比
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (uint32_t)pwm_value);
}

代码说明

  1. PWM初始化:

    • void PWM_Init():初始化 PWM 功能。

    • TIM_HandleTypeDef htim2;:定义一个定时器句柄,用于配置和控制定时器。

    • __HAL_RCC_TIM2_CLK_ENABLE();:启用 TIM2 定时器的时钟。

    • htim2.Init.Prescaler = 84 - 1;:设置预分频器,将 72MHz 的时钟频率分频到 1MHz。

    • htim2.Init.Period = 1000 - 1;:设定 PWM 周期为 1ms,即 1000 个时钟周期。

  2. 初始化定时器:

    • HAL_TIM_PWM_Init(&htim2);:初始化定时器以支持 PWM 功能。
  3. PWM输出通道配置:

    • TIM_OC_InitTypeDef sConfigOC;:定义 PWM 输出通道的配置结构体。

    • sConfigOC.OCMode = TIM_OCMODE_PWM1;:设置为 PWM 模式 1。

    • sConfigOC.Pulse = 0;:初始占空比为 0%。

    • HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);:配置 PWM 输出通道。

  4. 启动 PWM 通道:

    • HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);:启动 PWM 输出通道。
  5. 设置 PWM 输出:

    • void Set_PWM(float pwm_value):根据 PID 计算结果设置 PWM 输出。

    • if (pwm_value > 1000.0) pwm_value = 1000.0;:限制 PWM 值在 0 到 1000 之间,确保其在可接受范围内。

    • __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, (uint32_t)pwm_value);:设置 PWM 的占空比,以控制 TEC 的功率。

5. 串口通信模块

代码示例

以下是串口通信模块的代码,用于发送当前温度和 PWM 值,便于监控和调试。

#include "usart.h"

// 初始化串口
void USART_Init() {
    // 1. 定义串口句柄
    UART_HandleTypeDef huart1;

    // 2. 启用USART1时钟
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟

    // 3. 配置GPIO引脚
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10; // TX和RX引脚
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 复用推挽输出
    GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 高速
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 4. 配置USART参数
    huart1.Instance = USART1;
    huart1.Init.BaudRate = 9600; // 波特率
    huart1.Init.WordLength = UART_WORDLENGTH_8B; // 8位数据位
    huart1.Init.StopBits = UART_STOPBITS_1; // 1位停止位
    huart1.Init.Parity = UART_PARITY_NONE; // 无奇偶校验
    huart1.Init.Mode = UART_MODE_TX_RX; // 收发模式
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; // 无硬件流控制
    huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16倍过采样
    HAL_UART_Init(&huart1); // 初始化UART
}

// 发送数据通过串口
void USART_Send(float temperature, float pwm_value) {
    char buffer[100];
    int len = sprintf(buffer, "Current Temp: %.2f °C, PWM Value: %.2f\r\n", temperature, pwm_value); // 格式化字符串
    HAL_UART_Transmit(&huart1, (uint8_t *)buffer, len, HAL_MAX_DELAY); // 发送数据
}

代码说明

  1. 串口初始化:

    • void USART_Init():该函数用于配置和初始化串口。

    • UART_HandleTypeDef huart1;:定义一个串口句柄,用于管理 USART1 的配置。

  2. 启用时钟:

    • __HAL_RCC_USART1_CLK_ENABLE();:使能 USART1 时钟。

    • __HAL_RCC_GPIOA_CLK_ENABLE();:使能 GPIOA 时钟,用于串口引脚配置。

  3. 配置 GPIO 引脚:

    • GPIO_InitTypeDef GPIO_InitStruct;:定义 GPIO 初始化结构体。

    • GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10;:TX(发送)和 RX(接收)引脚的配置。

    • GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;:设置引脚为复用推挽输出模式。

    • HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);:初始化 GPIO 引脚。

  4. 配置 USART 参数:

    • huart1.Init.BaudRate = 9600;:设置波特率为 9600。

    • huart1.Init.WordLength = UART_WORDLENGTH_8B;:数据位长度为 8 位。

    • huart1.Init.StopBits = UART_STOPBITS_1;:设置 1 位停止位。

    • huart1.Init.Parity = UART_PARITY_NONE;:设置无奇偶校验。

    • HAL_UART_Init(&huart1);:根据配置初始化 USART1。

  5. 发送数据:

    • void USART_Send(float temperature, float pwm_value):该函数用于通过串口发送当前温度和 PWM 值。

    • char buffer[100];:定义字符数组用于存储发送的数据。

    • int len = sprintf(buffer, "Current Temp: %.2f °C, PWM Value: %.2f\r\n", temperature, pwm_value);:格式化输出字符串。

    • HAL_UART_Transmit(&huart1, (uint8_t *)buffer, len, HAL_MAX_DELAY);:通过 USART 发送数据。


http://www.kler.cn/a/321830.html

相关文章:

  • 博物馆实景复刻:开启沉浸式文化体验的新篇章
  • Qt 日志文件的滚动写入
  • 决策树基本 CART Python手写实现
  • 两种鼠标hover切换对应图片方法对比
  • 【学习日记】notebook添加JAVA支持
  • 从0学习React(11)
  • HT5169内置BOOST升压的11W I2S输入D类音频功放
  • 【游戏设计】游戏中需要管理的数据分类
  • MYSQL-查看表中字段属性语法(三)
  • 找质数的方式
  • MATLAB中的无线通信系统测试和验证方法有哪些
  • 代码随想录Day17 图论-1
  • 调和级数枚举+前缀和,CF 731F - Video Cards
  • flutter 设置字体大小,适应各种屏幕
  • 【LeetCode:2535. 数组元素和与数字和的绝对差 + 模拟】
  • 16.面试算法-树的层次遍历与相关面试题
  • ConfigurationManager类功能如何使用
  • 网络原理 - TCP/IP
  • SkyWalking 环境搭建部署
  • 【JAVA开源】基于Vue和SpringBoot的网上租赁系统
  • 获取鼠标当前位置上的元素
  • mysql配置相关命令
  • Mysql的隔离级别
  • SQL 查询语句的顺序详解
  • vue3 + ts + pnpm:nprogress / 页面顶部进度条
  • [数据库] Redis学习笔记(二):Redis Java客户端(Jedis/SpringDataRedis)