找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7925|回复: 9
收起左侧

51单片机PID温度控制源程序

  [复制链接]
ID:800046 发表于 2020-7-8 22:27 | 显示全部楼层 |阅读模式
本帖最后由 ypc12345678 于 2020-7-13 20:52 编辑

实时温度测量及显示,超出温度范围相应的继电器工作,继电器可以驱动相应的加本数字温度报警器是基于51单片机及温度传感器DS18B20来设计的,温度测量范围0到99.9摄氏度,精度为0.1摄氏度,







元件清单
1.png

电路原理图




[)VKZXHET`GSEJHQ51G@BQN.png





源程序
#include<reg52.h>

#include<intrins.h>
#include<math.h>
#include<string.h>
struct PID {
unsigned int SetPoint; // 设定目标 Desired Value
unsigned int Proportion; // 比例常数 Proportional Const
unsigned int Integral; // 积分常数 Integral Const
unsigned int Derivative; // 微分常数 Derivative Const
unsigned int LastError; // Error[-1]
unsigned int PrevError; // Error[-2]
unsigned int SumError; // Sums of Errors
};
struct PID spid; // PID Control Structure
unsigned int rout; // PID Response (Output) 响应输出
unsigned int rin; // PID Feedback (Input)//反馈输入
unsigned char high_time,low_time,count=0;//占空比调节参数
#define uchar unsigned char
#define uint unsigned int
sbit output=P1^0;
sbit ds=P3^2;
sbit DQ=P3^2;//ds18b20与单片机连接口
sbit lcden=P2^7;//LCE使能引脚
sbit lcdrs=P2^5;
sbit lcdrw=P2^6;
sbit ledred=P1^6;
sbit ledgreen=P1^7;
sbit key0=P2^0;//按键引脚
sbit key1=P2^1;
uchar set[2]={0};
uchar code str1[]="now temp:      C";
uchar code str2[]="set temp:      C";
uchar code table[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
uchar n,num;
int set_temper=30,temper,temp; //温度变量定义
unsigned int s;
float  f_temp;//转换后的温度
uint tvalue;         
uchar tflag;//温度正负标志
void delay(i)//延时函数
{
uint j;
for(i;i>0;i--)
for(j=110;j>0;j--);
}

void wr_com(uchar ml)//写命令
{
lcdrs=0;
P0=ml;
delay(5);
lcden=1;
delay(5);
lcden=0;
}

void wr_data(uchar shuju)//写数据
{
lcdrs=1;
//lcden=1;
P0=shuju;
delay(5);
lcden=1;
delay(5);
lcden=0;
}
void init()  //按照时序操作的初始化
{
lcdrw=0;
wr_com(0x38);//显示模式设置,设置为16*2显示,5*7点阵,八位数据口
wr_com(0x0c);//开显示,但不开光标,光标不闪
wr_com(0x06);//显示光标移动设置
wr_com(0x01);// 清屏
wr_com(0x80); // 数据指针初始化
for(num=0;num<16;num++)
  {
   wr_data(str1[num]);//now temp
  }
wr_com(0x80+0x40); //地址初始化
for(num=0;num<16;num++)
  {
   wr_data(str2[num]);//set temp
  }  
}
/*************************DS1820程序****************************/
void delay_18B20(unsigned int i)//延时1微秒
{
   while(i--);
}
void ds1820rst(void)/*ds1820复位*/
{
unsigned char x=0;
DQ = 1;          //DQ复位
delay_18B20(4); //延时
DQ = 0;          //DQ拉低
   TR0=0;
delay_18B20(100); //精确延时大于
   TR0=1;
DQ = 1;          //拉高
delay_18B20(40);
}
uchar ds1820rd(void)/*读数据*/
{
unsigned char i=0;
unsigned char dat = 0;
   TR0=0;
for (i=8;i>0;i--)
{   
  DQ = 0; //给脉冲信号
  dat>>=1;
  DQ = 1; //给脉冲信号
  if(DQ)
  dat|=0x80;
  delay_18B20(10);
}
   return(dat);
}
void ds1820wr(uchar wdata)/*写数据*/
{
unsigned char i=0;
   TR0=0;
   for (i=8; i>0; i--)
   {
  DQ = 0;
  DQ = wdata&0x01;
  delay_18B20(10);
  DQ = 1;
  wdata>>=1;
   }
}

uint get_temper()//获取温度
{  
     
uchar a,b;
ds1820rst();   
ds1820wr(0xcc);//*跳过读序列号*/
ds1820wr(0x44);//*启动温度转换*/
ds1820rst();   
ds1820wr(0xcc);//*跳过读序列号*/
ds1820wr(0xbe);//*读取温度*/
a=ds1820rd();
b=ds1820rd();
   
tvalue=b;
tvalue<<=8;
tvalue=tvalue|a;
   TR0=1;
   if(tvalue<0x0fff)   tflag=0;
   else {tvalue=~tvalue+1;tflag=1;}
tvalue=tvalue*(0.625);//温度值扩大10倍,精确到1位小数
temp=tvalue;
return temp;
}

void dis_temp(int t)//显示温度
{
uchar d0,d1,d2,d3;
//t=26;
if(tflag==0)
{
  d0=t/1000+0x30;
  d1=t%1000/100+0x30;
  d2=t%100/10+0x30;
  d3=t%10+0x30;
  if(d0==0x30)
  {
   wr_com(0x80+9);
   wr_data(d1);
   wr_com(0x80+10);
   wr_data(d2);
   wr_com(0x80+11);
   wr_data(0x2e);
   wr_com(0x80+12);
   wr_data(d3);
  }
  else
  {
   wr_com(0x80+9);
   wr_data(d0);
   wr_com(0x80+10);
   wr_data(d1);
   wr_com(0x80+11);
   wr_data(d2);
   wr_com(0x80+12);
   wr_data(' ');
  }
  
}
else
{
  wr_com(0x80+9);
  wr_data('-');
  wr_com(0x80+10);
  wr_data(d1);
  wr_com(0x80+11);
  wr_data(d2);
  wr_com(0x80+12);
  wr_data(' ');
  //wr_com(0x80+12);
  //wr_data(d3);
}
wr_com(0x80+14);
wr_data(0xdf);
temper=t/10;
}

void keyscan()//键盘扫描
{  
   if(key0==0)
   {
  delay(1);
  if(key0==0)
  {
   while(!key0);
   delay(1);
   while(!key0);
   set_temper++;
  }
  set[0]=set_temper/10; //获得设置温度显示值
  set[1]=set_temper%10;
  wr_com(0x80+0x40+9);
  wr_data(table[set[0]]);
  delay(1);
  wr_com(0x80+0x40+10);
  wr_data(table[set[1]]);
  delay(1);
  //wr_com(0x80+0x40+11);
  //wr_data(0x2e);
  //wr_com(0x80+0x40+14);
  //wr_data(0xdf);
  delay(1);
}
if(key1==0)
   {
  delay(3);//延时去抖
  if(key1==0)
  {
   while(!key1);
   delay(3);
   while(!key1);
   set_temper--;//温度减
   if(set_temper==0)
   {set_temper=0;}
  }

  set[0]=set_temper/10; //获得设置温度显示值
  set[1]=set_temper%10;
  wr_com(0x80+0x40+9); //显示设置温度值
  wr_data(table[set[0]]);
  delay(1);
  wr_com(0x80+0x40+10);
  wr_data(table[set[1]]);
  delay(1);
  //wr_com(0x80+0x40+11);
  //wr_data(0x2e);
  wr_com(0x80+0x40+14);
  wr_data(0xdf);
  delay(1);
}
}
void PIDInit (struct PID *pp)
{
memset ( pp,0,sizeof(struct PID)); //用参数0初始化pp
}

unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint ) //PID计算
{
unsigned int dError,Error;
Error = pp->SetPoint - NextPoint; // 偏差
pp->SumError += Error; // 积分
dError = pp->LastError - pp->PrevError; // 当前微分
pp->PrevError = pp->LastError;
pp->LastError = Error;
return (pp->Proportion * Error//比例
+ pp->Integral * pp->SumError  //积分项
+ pp->Derivative * dError); //   微分项
}
/***********************************************************
温度比较处理子程序
***********************************************************/
void compare_temper(void)
{
unsigned char i;
if(set_temper>temper)  //设置温度大于当前温度
{
  ledred=0;
  ledgreen=1;
  if(set_temper-temper>1)  //温度相差1度以上
  {  
   high_time=100;
   low_time=0;
  }
  else  //设置温度不大于当前温度
  {
   for(i=0;i<10;i++)
   {
    get_temper();
    rin = s; // Read Input
    rout = PIDCalc ( &spid,rin ); // Perform PID Interation
   }
   if (high_time<=100)  high_time=(unsigned char)(rout/800);
   else high_time=100;
   low_time= (100-high_time);
  }
}
else if(set_temper<=temper)  //设置温度不大于当前温度
{
  ledred=1;
  ledgreen=0;
  if(temper-set_temper>0) //温度相差0度以上
  {
   high_time=0;
   low_time=100;
  }
  else
  {
   for(i=0;i<10;i++)
   {
    get_temper();
    rin = s; // Read Input
    rout = PIDCalc ( &spid,rin ); // Perform PID Interation
   }
   if (high_time<100) high_time=(unsigned char)(rout/10000);
   else  high_time=0;
   low_time= (100-high_time);
  }
}
}
/*****************************************************
T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
******************************************************/
void serve_T0() interrupt 1 using 1
{
if(++count<=(high_time))  output=0;
else if(count<=100)
{
  output=1;
}
else count=0;
TH0=0x2f;
TL0=0x40;
}

/***********主函数**********/
void main(void)
{
unsigned char i;
init();//LCD初始化
TMOD=0x01;
TH0=0x2f;
TL0=0x40;
EA=1;
ET0=1;
TR0=1;
high_time=50;
low_time=50;
PIDInit ( &spid ); // Initialize Structure
spid.Proportion= 10; // Set PID Coefficients
spid.Integral = 8;
spid.Derivative =6;
spid.SetPoint =100; // Set PID Setpoint
set[0]=set_temper/10;
set[1]=set_temper%10;
wr_com(0x80+0x40+9); //显示设置温度
wr_data(table[set[0]]);
   delay(1);
wr_com(0x80+0x40+10);
wr_data(table[set[1]]);
delay(1);
wr_com(0x80+0x40+14); //显示温度符号
wr_data(0xdf);
delay(1);
while(1)
{
  keyscan();  //按键扫描
  for(i=0;i<10;i++) //循环10次
  {
   dis_temp(get_temper()); //显示温度值
   if((key0==0)||(key1==0)) break; //如果有按键退出显示循环
  }
  if((key0!=0)&&(key1!=0))  compare_temper();  //比较温度
}
}
HL[CYIGBS5`{1O58[X0}]C0.jpg

评分

参与人数 2黑币 +100 收起 理由
Jeff_BlindCat + 10 共享资料的黑币奖励!给你凑100
admin + 90 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:495287 发表于 2020-7-11 23:02 | 显示全部楼层
管理员都说了,补全电路与仿真加100黑币,羡慕嫉妒但是不恨
回复

使用道具 举报

ID:451718 发表于 2020-7-14 16:01 | 显示全部楼层
本着学习的态度,努力的看完了你的PID算法。不过有2点想法,还是想提出来探讨一下。
1.  你确定你这个PID在89C52上面能够跑得起来?  我是想问一下,你自己有没有用示波器看一下你的输出? 4ms的周期方波。 这个分度不知道有没有效果哦。 另外,没有看懂你是通过什么来控制有效值的。
2.  STC是不支持浮点运算的,测温那里*0.625误差会很大哦, 反正是返回uint,最大值可以到30000多,扩大100倍不是更好?  另外,*0.625的运算,改成*5/8 ,程序效率会更高点吧。
回复

使用道具 举报

ID:653771 发表于 2020-7-14 17:56 | 显示全部楼层
对我来说,有点复杂
回复

使用道具 举报

ID:254411 发表于 2022-8-24 21:24 | 显示全部楼层
rin = s; // Read Input    此语句没有意义
回复

使用道具 举报

ID:166475 发表于 2022-10-15 15:16 来自手机 | 显示全部楼层
我测试了。pid并没有具体意义
回复

使用道具 举报

ID:97678 发表于 2022-10-16 09:22 | 显示全部楼层
继电器控制设备能够实现PID功能吗?
这是什么继电器?
回复

使用道具 举报

ID:34149 发表于 2022-10-16 14:47 | 显示全部楼层
jovew 发表于 2022-10-16 09:22
继电器控制设备能够实现PID功能吗?
这是什么继电器?

到达设定值,输出控制。
输出到设定值,停止输出。
周而复始。让输出与输入保持恒定。
可以理解为PID。
回复

使用道具 举报

ID:97678 发表于 2022-10-16 16:36 | 显示全部楼层
如果这样的话,温度控制区间需要多大才行?
T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
这是不是控制?
这么快继电器可以正常工作吗?
回复

使用道具 举报

ID:491875 发表于 2023-5-27 15:10 | 显示全部楼层
dyx811 发表于 2022-10-16 14:47
到达设定值,输出控制。
输出到设定值,停止输出。
周而复始。让输出与输入保持恒定。

继电器不适合PID控制,继电器只适合区间控制,PID控制需要频繁动作,不适合继电器。PID最好是配合无触点开关使用。继电器可以作为超限保护控制。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表