扫码加入

  • 正文
  • 相关推荐
申请入驻 产业图谱

CW32L012的PID温度控制——实现思路及代码

01/01 08:55
579
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

一、系统框架

按键输入:通过按键来控制光标并更改设定温度值、Kp、Ki、Kd

ADC采集:采集NTC1的电压,转换成NTC1的电阻,再进行分度值查表,得到当前温度

PWM输出:将PID计算的结果通过PWM输出到执行器,控制板子加热

串口调试:将实际温度、设定温度值、Kp、Ki、Kd参数打印到上位机进行波形显示

屏幕显示:将实际温度、设定温度值、Kp、Ki、Kd、NTC1的电压、PWM当前的输出占空比参数显示到屏幕上

二、实验准备及接线

1.)先准备配套器材

主控:CW32L012开发板

辅助器材:杜邦线若干(公对母,母对母)、万用表(调试)

供电:USB 线或直接杜邦线供电(给实验板 + CW32L012开发板供电)

2.)基础接线步骤

电源连接

实验板5V→ CW32L012开发板 5V

实验板GND → CW32L012开发板 GND(共地很重要,避免信号干扰)

传感器接线

实验板NTC1排针接口 →CW32L012开发板ADC转换IO口PA4.

PWM输出接线

实验板PWM接口 →CW32L012开发板PWM输出口PB9

串口接线

CW32L012开发板PA9 →CH340或WCHlink的RX引脚

CW32L012开发板PA10 →CH340或WCHlink的TX引脚

CW32L012开发板3.3V →CH340或WCHlink的3.3V引脚

CW32L012开发板GND →CH340或WCHlink的GNDV引脚

接线完成图:

三、温度闭环控制实现

通过按键或初始化给定一个目标温度值

 

//温度控制系统的参数:实际温度,设定温度,P、I、D参数,PWM占空比float Temp_Para[6] =     {0,20,0,0,0,0};void key_handle(uint8_t key){        switch(key)        {                case 1://按键1,对应参数++                        if(para_Index==3) Temp_Para[para_Index]+=0.5;//如果是积分,每次变化0.5                else Temp_Para[para_Index]++;                        if(Temp_Para[para_Index] > Temp_Para_Max[para_Index])                                Temp_Para[para_Index] = Temp_Para_Min[para_Index];                        break;
                case 2://按键2,对应参数--                        if(Temp_Para[para_Index] > Temp_Para_Min[para_Index])                        {                                if(para_Index==3) Temp_Para[para_Index]-=0.5;//如果是积分,每次变化0.5                                else Temp_Para[para_Index]--;                        }                        else                                Temp_Para[para_Index] = Temp_Para_Max[para_Index];                        break;
                case 3://按键3,选择要操作的参数                        para_Index_old = para_Index;                        para_Index++;                        if(para_Index >= 5)                                para_Index= 1;                        break;        }}

ADC采集获取当前温度

采集NTC1的电压,转换成NTC1的电阻,再进行分度值查表,得到当前温度

部分代码:

 

/**************************获取NTC1上端点电压函数返回值:电压(单位V)**************************/float get_ntc_v(void){        return adc_result*3.3/4095;}/**************************获取NTC1电阻函数传入:NTC1上端点电压值(单位V)返回值:电阻(单位欧姆)**************************/float get_ntc_r(float Vadc){        return (Vadc*10000)/(5-Vadc);}//0摄氏度~100摄氏度时NTC的电阻值,共101个电阻数据const uint16_t PID_NTC_Table[]={                 32108,        30544,        29066,        27669,        26346,        25095,        23910,        22788,        21724,        20716,                19760,        18856,        17997,        17181,        16405,        15667,        14965,        14297,        13662,        13058,                12483,        11936,        11415,        10920,        10449,        10000,        9573,        9166,        8778,        8409,                8057,        7722,        7402,        7097,        6806,        6529,        6264,        6011,        5770,        5539,                5319,        5109,        4908,        4716,        4533,        4357,        4190,        4029,        3876,        3729,                3588,        3454,        3325,        3201,        3083,        2970,        2861,        2757,        2658,        2562,                2470,        2383,        2299,        2218,        2140,        2066,        1994,        1926,        1860,        1796,                1735,        1677,        1620,        1566,        1514,        1464,        1416,        1370,        1325,        1282,                1241,        1201,        1163,        1126,        1091,        1057,        1024,        992,        961,        932,                903,        876,        849,        824,        799,        775,        752,        730,        708,        687,                667};/** * @brief  二分查找NTC电阻值对应的下标,无精确值时线性插值返回浮点下标 * @param  target_res: 要查找的NTC电阻值(uint16_t) * @retval 浮点型下标: *         - 精确匹配:返回整数下标(如25.0,对应25℃) *         - 插值匹配:返回小数下标(如25.5,对应25.5℃) *         - 超出范围:返回0.0(电阻>最大值)或100.0(电阻<最小值) */float NTC_FindIndex(uint16_t target_res){    // 边界1:目标电阻 > 数组最大值(0℃对应电阻)→ 返回0.0    if (target_res > PID_NTC_Table[0])    {        return 0.0f;    }    // 边界2:目标电阻 < 数组最小值(100℃对应电阻)→ 返回100.0    if (target_res < PID_NTC_Table[100 - 1])    {        return 100.0f;    }    // 二分查找初始化    int32_t low = 0;                  // 左边界下标    int32_t high = 100 - 1; // 右边界下标    int32_t mid = 0;    // 二分查找核心循环(适配单调递减数组)    while (low <= high)    {        mid = low + (high - low)/2; // 计算中间下标(避免溢出可写:low + (high - low)/2)
        if (PID_NTC_Table[mid] == target_res)        {            // 精确匹配,返回浮点型下标            return (float)mid;        }        else if (target_res < PID_NTC_Table[mid])        {            // 目标电阻更小 → 对应温度更高 → 向右(大下标)查找            low = mid + 1;        }        else        {            // 目标电阻更大 → 对应温度更低 → 向左(小下标)查找            high = mid - 1;        }    }    // 未找到精确值,执行线性插值(此时high < low 且 high + 1 = low)    // 插值公式(单调递减适配):    // 下标 = high + (目标值 - 高下标电阻) / (低下标电阻 - 高下标电阻)    float res_high = (float)PID_NTC_Table[high]; // high下标对应的电阻(更大)    float res_low  = (float)PID_NTC_Table[low];  // low下标对应的电阻(更小)    float index = (float)high + (target_res - res_high) / (res_low - res_high);    return index;}

对温度进行滑动均值滤波

将数据打印到串口显示波形,可以看到数据呈现明显的震荡,根据这个波形现象,我们采取合适的滤波,也就是滑动均值滤波:

可以直观看到滤波前后,波形明显平滑了不少,说明滤波效果好。

代码:

#define TEMP_FILTER_WINDOW_SIZE 5  // 滑动窗口大小(建议3~10,越大越平滑,实时性稍降)// ******************************************************/** * @brief 温度滑动平均滤波函数 * @param rawTemp 输入的原始浮点温度值(如传感器采集的温度) * @return 滤波后的浮点温度值 * @note 函数内部通过静态变量维护滤波窗口,无需外部初始化/销毁,调用即使用 */float tempMovingAverageFilter(float rawTemp){    // 静态变量:仅第一次调用初始化,后续调用保留值(维护滤波窗口状态)    static float tempBuffer[TEMP_FILTER_WINDOW_SIZE] = {0.0f};  // 温度缓冲区    static int bufferIndex = 0;                                // 下一个写入的索引(循环覆盖)    static int dataCount = 0;                                  // 已存入的有效数据个数    // 1. 将新温度值写入缓冲区(循环覆盖最旧数据)    tempBuffer[bufferIndex] = rawTemp;    // 2. 更新索引(循环:0→1→...→窗口大小-1→0)    bufferIndex = (bufferIndex + 1) % TEMP_FILTER_WINDOW_SIZE;    // 3. 更新有效数据数(窗口未满时累加,满后保持窗口大小)    if (dataCount < TEMP_FILTER_WINDOW_SIZE)    {        dataCount++;    }    // 4. 计算窗口内所有数据的平均值(滤波核心)    float sum = 0.0f;    for (int i = 0; i < dataCount; i++)    {        sum += tempBuffer[i];    }    return sum / (float)dataCount;}

PID计算

对采集到的温度和设定的温度进行运算,得出适合的加热功率并输出

代码:

typedef struct{    int16_t target;               //目标值    int16_t actual;               //实际值    float out;                    //输出值    float err;                    //偏差值    float err_last;               //上一个偏差值    float integral;               //积分值    float Kp;    float Ki;    float Kd;}pid;void set_pid_para(void)//更新pid参数{        temper_pid.actual=Temp_Para[0];        temper_pid.target=Temp_Para[1];        temper_pid.Kp=Temp_Para[2];        temper_pid.Ki=Temp_Para[3];        temper_pid.Kd=Temp_Para[4];}uint16_t pid_control(void){        set_pid_para();//更新pid参数        temper_pid.err=temper_pid.target-temper_pid.actual;//误差        if(temper_pid.err<=0) return 0;//设定温度低于等于实际温度,加热关闭        else if(temper_pid.err>5) return ARR_Value;//实际温度与设定温度相差大于5度,加热输出最大功率        else        {                temper_pid.integral+=temper_pid.err;//积分                temper_pid.integral=(temper_pid.integral>10)? 10:temper_pid.integral;//积分限幅                temper_pid.integral=(temper_pid.integral<-10)? -10:temper_pid.integral;                temper_pid.out=temper_pid.Kp*temper_pid.err+temper_pid.Ki*temper_pid.integral+temper_pid.Kd*(temper_pid.err-temper_pid.err_last);                temper_pid.err_last=temper_pid.err;//更新上一次误差                temper_pid.out=(temper_pid.out>ARR_Value)? ARR_Value:temper_pid.out;//输出限幅          return temper_pid.out;        }}

PWM输出

将控制算法结果作用到PWM,控制板子进行加热

void Set_pwm__ccr(uint32_t Angle)  {        Angle=(Angle>ARR_Value)? ARR_Value:Angle;        CW_GTIM1->CCR4 = Angle;}Set_pwm__ccr(pid_control());//输出

四、代码现象

屏幕:与屏幕对应的变量含义:

Temp PID

实际温度(Real Temp)

设定温度(Set Temp)

P(Kp)

I(Ki)

D(Kd)

输出的PWM占空比(PWM Duty)

采集到的NTC的电压(Vntc)

按键

按键1(左):对光标所选中的参数进行加操作

按键2(中):对光标所选中的参数进行减操作

按键3(右):切换光标选择的内容

PWM输出灯:LED亮的程度反应了输出PWM的占空比大小,占空比越大,灯越亮,反之则越暗

串口:使用VOFA+软件连接串口,CW32会将设定、实际温度,Kp,Ki,Kd几个参数打印到上位机,显示波形

扫码加入QQ群3群| 610403240

相关推荐

登录即可解锁
  • 海量技术文章
  • 设计资源下载
  • 产业链客户资源
  • 写文章/发需求
立即登录

以开放、共享、互助为理念,致力于构建武汉芯源半导体CW32系列MCU生态社区。无论是嵌入式MCU小自还是想要攻破技术难题的工程师,亦或是需求解决方案的产品经理都可在CW32生态社区汲取营养、共同成长。