不讲原理 位置式PID控温实例 实战 (NTC温度采集代码加PID控温代码)

【本文发布于https://blog.csdn.net/Stack_/article/details/130532003,未经允许不得转载,转载须注明出处】


每篇PID文章都长篇大论数学原理,就是不教你怎么代码实现。理论是需要和实践相结合的,只看不做是学不会的。

此代码在STC8H上运行,摒弃了浮点运算,若是在32位单片机上,可根据需要改为浮点以提高精度

此代码能在我的加热器上将温度误差控制在0.5℃以内,如果需要达到更高的精度需要花更多时间调参数。


一、NTC温度采集


1、根据热敏电阻厂家提供的 阻值-温度 对照表计算出各温度下对应的理论AD值,并制表


#ifndef __NTC_LIST_H__
#define __NTC_LIST_H__

//[email protected]
//12位ADC,10K电阻分压,10K电阻在VCC端,5V电压
code unsigned int ntc_ad_list[] = 
{
    /*0℃ - 9℃*/
    3102, 3065, 3027, 2989, 2950, 2910, 2870, 2829, 2788, 2746,
    /*10℃ - 19℃*/
    2704, 2661, 2619, 2585, 2541, 2497, 2452, 2407, 2362, 2317,
    /*20℃ - 29℃*/
    2272, 2227, 2182, 2137, 2092, 2048, 2003, 1959, 1915, 1871,
    /*30℃ - 39℃*/
    1828, 1785, 1743, 1701, 1660, 1619, 1579, 1539, 1500, 1461,
    /*40℃ - 49℃*/
    1423, 1386, 1349, 1313, 1278, 1244, 1210, 1176, 1144, 1112,
    /*50℃ - 59℃*/
    1081, 1051, 1021,  992,  964,  936,  912,  886,  861,  836,
    /*60℃ - 69℃*/
     812,  789,  767,  745,  723,  703,  683,  663,  644,  625,
    /*70℃ - 79℃*/
     607,  590,  573,  557,  541,  525,  510,  496,  481,  468,
    /*80℃ - 89℃*/
     454,  441,  429,  417,  405,  394,  382,  372,  361,  351,
    /*90℃ - 99℃*/
     341,  332,  323,  314,  305,  297,  288,  281,  273,  265
};



#endif

2、根据AD值查表并插值计算出温度值


#ifndef __NTC_H__
#define __NTC_H__

#include "public.h"
    
extern uint8_t xdata ntc_exist;
extern uint16_t xdata ntc_temp;
extern uint8_t xdata ntc_shortcut;

void ntc_proc(void);

#endif

/**
  ******************************************************************************
  * @copyright
  * @file      
  * @author    [email protected]
  * @version
  * @date      
  * @brief
  ******************************************************************************
  * @attention     files encoding : GB2312
  *                      文件编码 :GB2312
  ******************************************************************************
  */
 #include "ntc.h"
 #include "ntc_list.h"

uint8_t xdata ntc_exist = 0;
uint16_t xdata ntc_temp;    //一位小数
uint8_t xdata ntc_shortcut = 0;

/**
  * @brief  
  * @note   
  * @param
  * @retval
  * @author PWH //[email protected]
  * @date   2023/2
  */
void ntc_proc(void)
{
    uint16_t xdata i, j;

    ntc_exist = 1;
    ntc_shortcut = 0;
    
    if (ad_val > 3800)  //ntc电阻不存在
    {
        ntc_exist = 0;
    }
    if (ad_val < 20)  //ntc电阻短路
    {
        ntc_shortcut = 1;
    }

    if (!ntc_exist || ntc_shortcut)
    {
        ntc_temp = 0;
        return;
    }
    
    if (ad_val >= ntc_ad_list[0])
    {
        ntc_temp = 0;
    }
    else if (ad_val <= ntc_ad_list[sizeof(ntc_ad_list) / 2 - 1])
    {
        ntc_temp = 990; //99度
    }
    else
    {
        for (i = 0; i < sizeof(ntc_ad_list) / 2 - 1; i++)   //查表
        {
            if (ad_val == ntc_ad_list[i])
            {
                ntc_temp = i * 10;
                break;
            }
            else if (ad_val == ntc_ad_list[i + 1])
            {
                ntc_temp = (i + 1) * 10;
                break;
            }
            else if (ad_val < ntc_ad_list[i] && ad_val > ntc_ad_list[i + 1])	//落在中间,将两点之间视为直线,进行插值
            {
                /*将 i 和 i+1 两点间视为直线,分为10段对应小数0-9*/
                for (j = 1; j < 10; j++)
                {
					ntc_temp = i * 10 + j;
                    if (ad_val >= ntc_ad_list[i] - (ntc_ad_list[i] - ntc_ad_list[i + 1]) * j / 10)
                    {
						return;
                    }
                }
				break;
            }
        }
    }

}






二、PID控温


需要根据实际的加热器对参数进行修改,例如 P、I、D系数积分限幅值温度补偿值开启PID控温的温度区间,在升温速度和超调之间取得一个平衡。


#ifndef __PTC_H__
#define __PTC_H__

#include "public.h"


typedef struct {
    int16_t pid_set_temp;          //设置温度
    int16_t pid_now_temp;          //现在温度
    int16_t pid_err_last;          //上次误差
    int16_t pid_err;               //误差
    int16_t pid_err_sum;           //历史误差和
    int16_t pid_result;
} PID_Para_t;

extern xdata PID_Para_t    PID_Para;



extern uint8_t timer_ptc; 
    
void ptc_proc(void);
void pid_init(void);

#endif


/**
  ******************************************************************************
  * @copyright
  * @file      
  * @author    //[email protected]
  * @version
  * @date      
  * @brief
  ******************************************************************************
  * @attention     files encoding : GB2312
  *                      文件编码 :GB2312
  ******************************************************************************
  */
 #include "ptc.h"

#define TEMPERATURE_COMPENSATION    1   //1:开启温度补偿
 
#define Kp          100       //比例常数  100倍值    100即1
#define Ki          18       //积分常数  100倍值   18即0.18
#define Kd          10       //微分常数  100倍值    90即0.9

#define I_MAX         2500
#define I_MIN         0



xdata PID_Para_t    PID_Para;


/**
  * @brief  
  * @note   
  * @param
  * @retval
  * @author PWH //[email protected]
  * @date   2023/2
  */
void pid_init(void)
{
    PID_Para.pid_set_temp = 30;
    PID_Para.pid_err_last = 0;
    PID_Para.pid_err_sum = 0;
}

/**
  * @brief  位置式PID
  * @note   
  * @param
  * @retval
  * @author PWH //[email protected]
  * @date   2023/2
  */
void pid(void)
{
    int16_t xdata P, D;
    
    PID_Para.pid_err = PID_Para.pid_set_temp - PID_Para.pid_now_temp;   //当前误差
    
    P = PID_Para.pid_err * Kp;  //比例量   P
    
    PID_Para.pid_err_sum += PID_Para.pid_err * Ki;  //积分量      I
    if (PID_Para.pid_err_sum > I_MAX) PID_Para.pid_err_sum = I_MAX;       //积分限幅 
    else if (PID_Para.pid_err_sum < I_MIN) PID_Para.pid_err_sum = I_MIN;
    
    D = (PID_Para.pid_err - PID_Para.pid_err_last) * Kd;    //微分量      D
    
    PID_Para.pid_err_last = PID_Para.pid_err;
    
    #if (1)
    PID_Para.pid_result = (P + PID_Para.pid_err_sum + D) / 100;
    #else
    PID_Para.pid_result = (P + D) / 100;
    #endif
    if (PID_Para.pid_result > 100) PID_Para.pid_result = 100;	//占空比 0-100
    else if (PID_Para.pid_result < 0) PID_Para.pid_result = 0;
}

uint8_t timer_ptc = 0;	//定时器中断10ms递增
/**
  * @brief  
  * @note   
  * @param
  * @retval
  * @author PWH //[email protected]
  * @date   2023/2
  */
void ptc_proc(void)
{
    uint16_t xdata temperature_now;
    uint16_t xdata target;
    static uint8_t xdata timer = 0;
    #if (TEMPERATURE_COMPENSATION == 1)
    uint8_t xdata compensation; //补偿温度,陶瓷表面温度比热敏电阻处高,加温目标需要比设置的低
    #endif
    
    if (!timer_ptc) return;	//每10ms执行一次
    
    timer_ptc = 0;

    if (ntc_exist)
    {
        #if (TEMPERATURE_COMPENSATION == 1)
        switch (app_para.target)	//app_para.target 目标温度无小数
        {
            case 30: compensation = 0; break;   //一位小数  eg. 33=3.3℃
            case 33: compensation = 0; break;
            case 37: compensation = 0; break;
            case 40: compensation = 0; break;
            case 43: compensation = 5; break;
            case 47: compensation = 5; break;
            case 50: compensation = 12; break;
            case 53: compensation = 15; break;
            case 57: compensation = 11; break;
            case 60: compensation = 16; break;
            case 65: compensation = 21; break;
        
            default: break;
        }
        #endif

        temperature_now = ntc_temp;
        
        #if (TEMPERATURE_COMPENSATION == 1)
        target = app_para.target * 10 - compensation;
        #else
        target = app_para.target * 10;
        #endif

        if (temperature_now < target - 5)   //达到目标温度-0.5℃前全速加热
        {
            Pin_Heater_Ctrl = HEATER_ON;    //
        }
        else if (temperature_now > target + 1 * 10) //超出设置值1℃时全关
        {
            Pin_Heater_Ctrl = HEATER_OFF;
        }   
        else    //PID控制
        {   //设定加热周期为1000ms,1000分之PID_Para.pid_result毫秒开加热器
            if (timer_ptc >= PID_Para.pid_result)
            {
                Pin_Heater_Ctrl = HEATER_OFF;
            }
            else
            {
                Pin_Heater_Ctrl = HEATER_ON;
            }
            
            if (++timer > 100)	//100 * 10ms = 1000ms周期
            {
                timer = 0;
                PID_Para.pid_set_temp = target;
                PID_Para.pid_now_temp = temperature_now;
                pid();
            }
        }
    }
    else
    {
        Pin_Heater_Ctrl = HEATER_OFF;
    }
    
    
    
    
}





你可能感兴趣的:(单片机,算法)