1.设计灵感
一把普通的60W烙铁总是再焊接电路板时因为温度过高而对焊接的质量产生影响,但是如果可以用外部电路来控制烙铁的温度,就能很好的解决这个问题。
2.硬件采购
首先恒温烙铁那必须要有烙铁,我选取了60w的普通烙铁,因为便宜只要7块钱其次是这次的主控单片机stc12c5a60s2,这款单片机相较于普通单片机有更多的程序库,像PWM,ADC,也有更多的IO口,在做电路设计的时候,12单片机更为合适
然后是烙铁的控制,采用固态继电器,有更高的通断频率,也可以控制交流220V电压
最后是反馈元件k型热电偶,PID的整定需要反馈元件的反馈,而k型热电偶,加上max6675热电偶温度转换芯片,非常合适作为这次的反馈原件
3.位置式PID概述
1. 位置式PID公式
在离散系统中,位置式PID的控制量输出公式为:
u(k)=Kp⋅e(k)+Ki⋅T⋅∑j=0ke(j)+Kd⋅e(k)−e(k−1)Tu(k)=Kp⋅e(k)+Ki⋅T⋅j=0∑ke(j)+Kd⋅Te(k)−e(k−1)
-
参数说明:
-
u(k)u(k):第kk次采样时刻的控制量输出。
-
e(k)e(k):当前时刻的偏差(设定值与实际值之差)。
-
Kp,Ki,KdKp,Ki,Kd:比例、积分、微分系数。
-
TT:采样时间。
-
积分项通过累加历史偏差实现,微分项通过差分近似微分。
-
2. 实现特点
-
积分项处理:需要保存历史偏差的累加和,通常通过变量累积实现,无需存储所有历史数据。
-
微分项处理:仅需保存前一次偏差e(k−1)e(k−1)。
-
计算复杂度:每次计算需更新积分累加值和微分项的前次偏差。
3. 优缺点分析
-
优点:
-
直接输出绝对控制量,适用于阀门开度、温度设定等场景。
-
无需执行机构记忆前次输出,控制逻辑直观。
-
-
缺点:
-
积分饱和:持续偏差导致积分项累积,可能超出执行机构限幅,需采用抗饱和措施(如积分限幅、积分分离)。
-
对计算误差敏感,突发干扰可能导致输出突变。
-
4. 参数整定与采样时间
-
参数调整:与常规PID一致,可采用试凑法、Ziegler-Nichols法等,需注意积分项限幅。
-
采样时间TT:
-
过大:离散化误差显著,影响控制精度。
-
过小:计算负担增加,硬件要求高。
-
5. 与增量式PID对比
-
位置式:输出绝对控制量u(k)u(k),积分项为偏差累加,易积分饱和。
-
增量式:输出控制量变化Δu(k)=u(k)−u(k−1)Δu(k)=u(k)−u(k−1),积分效果通过偏差增量实现,抗饱和能力强,但需执行机构记忆前次输出。
6. 应用场景
-
位置式PID:适用于需直接设定执行机构位置的场合(如伺服电机位置控制、阀门开度调节)。
-
增量式PID:适合步进电机等需控制量变化的场景。
7. 抗积分饱和措施
-
积分限幅:限制积分项的最大值。
-
积分分离:偏差较大时暂停积分,避免过度累积。
-
变速积分:根据偏差大小动态调整积分速度。
位置式PID以其直接输出绝对控制量的特性,在工业控制中广泛应用,但需合理处理积分饱和问题并结合实际场景调整参数。
其实PID就是通过比例调节,积分调节,和微分调节,来输出一个结果,再通过反馈元件来回馈一个值,然后再循环这个过程,不断的整定和调节,来达到最终的输出效果
4.硬件接线图

单片机通过PWM输出控制固态继电器,固态继电器控制烙铁是否加热,热电偶读取烙铁的温度,反馈给单片机,oled实时显示当前控制进度,不断循环这个过程,来完成这个过程
5.程序实现
1.热电偶程序库
这是基于51内核单片机的热电偶程序,可直接使用,连接后以数码管形式输出。
cpp
#include <REGX52.H>
#include "intrins.h" //_nop_();延时函数
#define uchar unsigned char
#define uint unsigned int
sbit SO = P3^6; //P3.6与SO连接
sbit SCK = P3^4; //P3.4与SCK连接
sbit CS = P3^5; //P3.5与CS连接
sbit wx = P2^6;
sbit dx = P2^7;
const unsigned char DevID = 1; //本机设备ID
unsigned char buffer; //串口接收缓冲区
unsigned char rcv_buffer[20];
unsigned char send_buffer[20];
unsigned char flag_ok = 0; //接收完毕的标识
unsigned int MAX6675_Temp;
unsigned char Flag_connect;
unsigned char CRCH,CRCL;
unsigned char wd[2]; //wd[];BCD码的温度
unsigned char flag_dis; //显示刷新的标识
unsigned char dis[4] = { 0x00, 0x00, 0x00,0x00 };
unsigned char code SMG[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e};
unsigned int MAX6675_ReadReg(void)
{
unsigned char i;
unsigned int dat;
i = 0;
dat = 0;
CS = 0;
SCK = 0;
for(i=0; i<16; i++) //get D15-D0 from 6675
{
SCK = 1;
dat = dat<<1;
if( SO==1 )
dat = dat|0x01;
SCK = 0;
}
CS = 1;
return dat;
}
/*
* 功能;延时,若干毫秒
* 参数;毫秒数
* 返回值;无
*/
void DelayMs(unsigned int i) //一个延时函数,这个函数再11.092Mhz下延时时间大概为1ms
{
unsigned int j,k;
for(j=i; j>0; j--)
for(k=114; k>0; k--);
}
/*
* 数码管显示
*/
void SMG_DisChar(unsigned char place, unsigned char num)
{
unsigned char i;
i = 0x80 >> place;
P0 = SMG[num];
dx = 1;
dx = 0;
P0 = i;
wx = 1;
wx = 0;
DelayMs(1);
}
void Display(unsigned int temp)
{
if(Flag_connect==0) //Flag_connect为0,表示热电偶已经连接,这里显示温度
{
if(temp > 999)
{
SMG_DisChar(3,temp/1000);
}
if(temp > 99)
{
SMG_DisChar(2,(temp%1000)/100);
}
if(temp > 9)
{
SMG_DisChar(1,(temp/10)%10);
}
SMG_DisChar(0,temp%10);
}
else //Flag_connect为1,表示热电偶未连接,这里显示fff
{
SMG_DisChar(3, 0x0F);
SMG_DisChar(2, 0x0F);
SMG_DisChar(1, 0x0F);
SMG_DisChar(0, 0x0F);
}
}
void main(void)
{
unsigned char cnt = 11;
unsigned int t;
while(1)
{
t=MAX6675_ReadReg();
Flag_connect=t&0x04; //读出数据的D2位是热电偶掉电标志位,该位为1表示掉线,该位为0表示连接
Flag_connect=Flag_connect>>2; //MAX6675是否在线
t = t<<1; //读出数据的D3-D14是温度值
t = t>>4;
MAX6675_Temp = t/4; //测量单位为0.25,所以要乘0.25,(即除以4)才能得到准确的温度值
for(t=0; t<200; t++) //标识温度
{
Display(MAX6675_Temp);
}
}
}
2.PID设定值给定
在一个函数里把一下不需要随时改变的变量赋值,方便后面的算法直接拿取使用
cpp
void PID_Init()//PID设定值给定
{
sv=120;/默认设定温度
kd=3;//Dout输出
T=200;
Ti=2000;
Td=600;
PIDpwm=500;
OUT0=5;
}
3.PID计算
对于PID的计算,这里要注意的是,Pout,Iout以及Dout都有不同的加入时段,调节这三个输出的进入时间对于PID的控制也至关重要,下面的程序是我在PID控温时采取的PID计算
cpp
void PID_Calc()//PID计算
{
int DelEK;
float ti,Ki;
float Iout;
float Pout;
float Dout;
float td;
float out;
if(C10ms<T)
{
return;
}
if(C10ms>T)
{
EK=sv-pv; //当前偏差
Pout=kp*EK; //比例输出
if(pv<=(sv+4)&&pv>=(sv-4))
{
ki = 1.6;
kp = 8;
SEK+=EK; //历史偏差总和
ti=T/Ti;
Ki=ti*ki;
Iout=SEK*Ki*ki; //积分输出
num3=Iout;
out=Pout+Iout;//本次计算
DelEK=EK-EK_1;//最近两次偏差
td=Td/T*kd;
Dout=DelEK*td; //微分输出
out=Pout+Iout+Dout;//本次计算
}
else if(pv<=(sv+20)&&pv>=(sv-20))//设定Iout加入运算时间
{
if(pv<=(sv+20)&&pv>=sv)
{
kp = 0.5;
ki = 0.8;
}
else if(pv>=(sv-20)&&pv<=sv)
{
kp = 0.5;
ki = 1.2;
}
SEK+=EK; //历史偏差总和
ti=T/Ti;
Ki=ti*ki;
Iout=SEK*Ki*ki; //积分输出
num3=Iout;
out=Pout+Iout;//本次计算
}
else{out = Pout;kp = 3;}//单纯的Pout输出
num1=Dout;
num3=Iout;
num4=SEK;
if(out>PIDpwm)
{OUT=PIDpwm;}
else if(out<0)
{OUT=OUT0;}
else{OUT=out;}
num2 = out;
EK_1=EK; //更新偏差
C10ms=0;
}
}
4.PWM输出
我的PWM采取的是计时器输出,通过调节输出方波脉宽,来调节实际的输出结果
cpp
void Timer0_Init(void)
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 0x66;
TH0 = 0xFC;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA = 1;
}
void TimerO(void) interrupt 1
{
TL0 = 0x66;
TH0 = 0xFC;
Time1++;
C10ms++;
Time0++;
if(Time0>=PIDpwm){Time0=0;}
if(Time0>OUT){key=0;}
else{key=1;}
}
5.oled显示
oled的显示程序比较简单 ,初始化程序用来初始化和显示一些固定的内容后面的OLED显示程序用来显示当前的加热过程,配上按钮进行人机交互
cpp
void OLED1_Init()
{
OLED_Init();//初始化OLED
OLED_ColorTurn(0);
OLED_DisplayTurn(0);
OLED_ShowChinese(0,2,0,16);
OLED_ShowChinese(18,2,1,16);
OLED_ShowChinese(36,2,6,16);
OLED_ShowChinese(54,2,3,16);
OLED_ShowChinese(0,4,4,16);
OLED_ShowChinese(18,4,5,16);
OLED_ShowChinese(36,4,6,16);
OLED_ShowChinese(54,4,7,16);
}
void OLEDshow()
{
if(num1<=0){num1=0;}
if(num3<=0){num3=0;}
if(num4<=0){num4=0;}
OLED_ShowNum(80,0,num3,5,16);
OLED_ShowNum(0,6,num4,5,16);
OLED_ShowNum(0,0,num1,5,16);
OLED_ShowNum(80,6,OUT,5,16);
}
6.项目文件
链接: https://pan.baidu.com/s/1Sr8cdt3YnOtqlPE7zhhBoQ?pwd=t6qy 提取码: t6qy 复制这段内容后打开百度网盘
7.模块成果展示
模块搭建PID烙铁
8.集成电路板设计
1.原理图设计
主控依然是stc12单片机,采用220v交流电单电源供电,经过rc阻容耦合降压,在经过桥式整流,5v稳压管稳压,滤波后输出给后级电路,LED2为电源供电指示灯

2.PCB电路板

3.3D效果图

9.集成电路成果展示
集成板PID恒温烙铁