找回密码
 立即注册

QQ登录

只需一步,快速开始

帖子
查看: 2977|回复: 8
收起左侧

单片机继电器版本的PID水温调节?有这个咋不行?

[复制链接]
ID:190577 发表于 2020-3-22 15:56 | 显示全部楼层 |阅读模式
#include <reg51.h>
#define uint unsigned int
#define uchar unsigned char           //宏定义
#define LCD1602 P0
sbit SET=P3^2;                            //定义调整键
sbit DEC=P3^3;                            //定义减少键
sbit ADD=P3^4;                            //定义增加键
sbit BUZZ=P3^7;                            //定义蜂鸣器
sbit PWM=P1^0;                                //定义灯光报警
sbit ALAM1=P1^4;
sbit DQ=P1^1;                             //定义DS18B20总线I/O        
sbit RS = P2^7;
sbit EN = P2^6;
sbit rw=P2^5;
bit shanshuo_st;                            //闪烁间隔标志
bit beep_st;                                     //蜂鸣器间隔标志
uchar x=0;                                      //计数器
uchar code tab1[]={"Now Tem:   .  C "};
uchar code tab2[]={"TH:   C  TL:   C"};
uint c;
uchar Mode=0;                             //状态标志
signed char TH=40;                  //上限报警温度,默认值为40
signed char TL=10;                   //下限报警温度,默认值为10
#define Kp 18        //比例系数 18
#define Ki 13 //积分系数  13
#define Kd 0.8        //微分系数 0.3
int Real_temp;                //实际温度值
int Set_temp;                //设置温度
int Disp_temp;                //显示温度
int last_error;                //上次误差
float I_term;                //前面温差和
bit        key_hold;
int PID_MAX;
unsigned int out,PWMT,counter,kk,outp;
unsigned char test_temp;                //温度检定标志

int time;        //脉冲触发时刻

//============================================================================================
//====================================DS18B20=================================================
//============================================================================================
/*****延时子程序*****/
void Delay_DS18B20(int num)
{
  while(num--) ;
}
void delay(uint xms)//延时函数,有参函数
{
        uint x,y;
        for(x=xms;x>0;x--)
         for(y=110;y>0;y--);
}
/*****初始化DS18B20*****/
void Init_DS18B20(void)
{
  unsigned char x=0;
  DQ = 1;         //DQ复位
  Delay_DS18B20(8);    //稍做延时
  DQ = 0;         //单片机将DQ拉低
  Delay_DS18B20(80);   //精确延时,大于480us
  DQ = 1;         //拉高总线
  Delay_DS18B20(14);
  x = DQ;           //稍做延时后,如果x=0则初始化成功,x=1则初始化失败
  Delay_DS18B20(20);
}
/*****读一个字节*****/
unsigned char ReadOneChar(void)
{
  unsigned char i=0;
  unsigned char dat = 0;
  for (i=8;i>0;i--)
  {
    DQ = 0;     // 给脉冲信号
    dat>>=1;
    DQ = 1;     // 给脉冲信号
    if(DQ)
    dat|=0x80;
    Delay_DS18B20(4);
  }
  return(dat);
}
/*****写一个字节*****/
void WriteOneChar(unsigned char dat)
{
  unsigned char i=0;
  for (i=8; i>0; i--)
  {
    DQ = 0;
    DQ = dat&0x01;
    Delay_DS18B20(5);
    DQ = 1;
    dat>>=1;
  }
}
/*****读取温度*****/
unsigned int ReadTemperature(void)
{
  unsigned char a=0;
  unsigned char b=0;
  unsigned int t=0;
  float tt=0;
  Init_DS18B20();
  WriteOneChar(0xCC);  //跳过读序号列号的操作
  WriteOneChar(0x44);  //启动温度转换
  Init_DS18B20();
  WriteOneChar(0xCC);  //跳过读序号列号的操作
  WriteOneChar(0xBE);  //读取温度寄存器
  a=ReadOneChar();     //读低8位
  b=ReadOneChar();    //读高8位
  t=b;
  t<<=8;
  t=t|a;
  tt=t*0.0625;
// t= tt*10+0.5;     //放大10倍输出并四舍五入
  t= tt*10+0.5;
  return(t);
}

/*****读取温度*****/
void check_wendu(void)
{
        c=ReadTemperature();                          //获取温度值并减去DS18B20的温漂误差

}

/********液晶写入指令函数与写入数据函数,以后可调用**************/

void write_1602com(uchar com)//****液晶写入指令函数****
{
        RS=0;//数据/指令选择置为指令
        rw=0; //读写选择置为写
        LCD1602=com;//送入数据
        delay(1);
        EN=1;//拉高使能端,为制造有效的下降沿做准备
        delay(1);
        EN=0;//en由高变低,产生下降沿,液晶执行命令
}


void write_1602dat(uchar dat)//***液晶写入数据函数****
{
        RS=1;//数据/指令选择置为数据
        rw=0; //读写选择置为写
        LCD1602=dat;//送入数据
        delay(1);
        EN=1; //en置高电平,为制造下降沿做准备
        delay(1);
        EN=0; //en由高变低,产生下降沿,液晶执行命令
}


void lcd_init()//***液晶初始化函数****
{
        uchar a;
        write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据
        write_1602com(0x0c);//开显示不显示光标
        write_1602com(0x06);//整屏不移动,光标自动右移
        write_1602com(0x01);//清显示

        write_1602com(0x80);//日历显示固定符号从第一行第1个位置之后开始显示
        for(a=0;a<16;a++)
        {
                write_1602dat(tab1[a]);//向液晶屏写日历显示的固定符号部分
                delay(3);
        }
        write_1602com(0x80+0x40);//时间显示固定符号写入位置,从第2个位置后开始显示
        for(a=0;a<16;a++)
        {
                write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号
                delay(3);
        }

}

void display()
{
        if(Mode==0)
        {
                check_wendu();
                write_1602com(0x80+8);
                write_1602dat(c/1000+0x30);
                write_1602dat((c%1000)/100+0x30);
                write_1602dat(((c%1000)%100)/10+0x30);
                write_1602com(0x80+12);
                write_1602dat(((c%1000)%100)%10+0x30);
                write_1602com(0x80+13);
                write_1602dat(0xdf);
                write_1602com(0x80+0x40+3);
                write_1602dat(TH/10+0x30);
                write_1602dat(TH%10+0x30);
                write_1602dat(0xdf);
                write_1602com(0x80+0x40+12);
                write_1602dat(TL/10+0x30);
                write_1602dat(TL%10+0x30);
                write_1602dat(0xdf);                        
        }                                                                  
}
//=====================================================================================
void KEY()
{
                        //功能键
        if(SET==0)
        {
                BUZZ=0;
                delay(10);
                if(SET==0)
                {
                        Mode++;
                        if(Mode==3)
                        Mode=0;
                        BUZZ=1;
                }
                while(SET==0)
                {
                        if(Mode==0)
                                {
                                //        write_1602com(0x80+0x40+6);
                                        write_1602com(0x0c);
                                }        
                        else if(Mode==1)
                                {
                                        write_1602com(0x80+0x40+4);
                                        write_1602com(0x0f);
                                }        
                        else
                                {
                                        write_1602com(0x80+0x40+13);
                                        write_1602com(0x0f);
                                }                                                        
                }
        }
        //增加
        if(ADD==0&&Mode==1)
        {
                BUZZ=0;
                delay(10);
                if(ADD==0)        
                {
                        TH++;
                        if(TH>=99)        
                        TH=99;
                        write_1602com(0x80+0x40+3);
                        write_1602dat(TH/10+0x30);
                        write_1602dat(TH%10+0x30);
                        write_1602com(0x80+0x40+4);        
                        BUZZ=1;
                }
                while(ADD==0);
               
        }
        //减少
        if(DEC==0&&Mode==1)
        {
                BUZZ=0;
                delay(10);
                if(DEC==0)
                {
                        TH--;
                        if(TH==TL)        
                        TH=TL+1;
                        write_1602com(0x80+0x40+3);
                        write_1602dat(TH/10+0x30);
                        write_1602dat(TH%10+0x30);
                        write_1602com(0x80+0x40+4);        
                        BUZZ=1;
                }
                while(DEC==0);
        }
        if(ADD==0&&Mode==2)
        {
                BUZZ=0;
                delay(10);
                if(ADD==0)        
                {
                        TL++;
                        if(TL==TH)        
                        TL=TH-1;
                        write_1602com(0x80+0x40+12);
                        write_1602dat(TL/10+0x30);
                        write_1602dat(TL%10+0x30);
                        write_1602com(0x80+0x40+13);        
                        BUZZ=1;
                }
                while(ADD==0);
               
        }
        //减少
        if(DEC==0&&Mode==2)
        {
                BUZZ=0;
                delay(10);
                if(DEC==0)
                {
                        TL--;
                        if(TL<=0)        
                        TL=0;
                        write_1602com(0x80+0x40+12);
                        write_1602dat(TL/10+0x30);
                        write_1602dat(TL%10+0x30);
                        write_1602com(0x80+0x40+13);        
                        BUZZ=1;
                }
                while(DEC==0);               
        }
}

/*****报警子程序*****/
void Alarm()
{
        if(x>=10){beep_st=~beep_st;x=0;}
        if(Mode==0)
        {
                if((c/10)>=TH)
                {
//                        ALAM=0;
                        ALAM1=1;
                        if(beep_st==1)
                        BUZZ=0;
                        else
                        BUZZ=1;
                }
                else if((c/10)<TL)
                {
                        ALAM1=0;
//                        ALAM=1;
                        if(beep_st==1)
                        BUZZ=0;
                        else
                        BUZZ=1;
                }
                else
                {
                        BUZZ=1;
                //        ALAM=1;
                        ALAM1=1;               
                }
        }
        else
        {
                BUZZ=1;
                //ALAM=1;
                ALAM1=1;
        }
}

int PID(int Set_value,int Real_value) //标准PID温度控制算法
{
        float uk ,uk1 ,duk;
        int pid_out,e ,e1 ,e2;
        e=Set_value-Real_value;//误差量
        duk=Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2);  //+Kd*(e-2e1+e2)
        uk=uk1+duk;
        pid_out=(int)uk;
        uk1=uk;
        e2=e1;
        e1=e;
        if(pid_out>2000) //1000
        {
                pid_out=990;//        990
        }
        else if(pid_out<1000)         //10
        {
                pid_out=1000;                //10
        }
        outp=pid_out;

        return(pid_out);
               
}
void T0_int(void) interrupt 1
{
        TH0= (65535-2000)/256;                                //PWM=1高位初值计算
        TL0 = (65535-2000)%256;                                          //PWM=1低位初值计算



        kk++;
        if(kk>2000)
        kk=0;
        if(kk>outp)
        {PWM=1;
        // jia=1;
          }
        else {PWM=0;
             //jia=0;
        }
        
         
}


/*****主函数*****/
void main(void)
{
        uint z;
        delay(1);
        lcd_init();
        delay(1);
//        InitTimer();    //初始化定时器
    TMOD=0x01;                        //定时器0模式1               
        TR0=1;                                                                  
        ET0=1;
        //IT0=1;
//        EX0=1;
        EA=1;
        for(z=0;z<100;z++)
        {
                check_wendu();
                delay(1);        
        }
        PWM=1;
   // jia=1;

        I_term=0;
        last_error=0;
        TH=40;                //初始设定温度为41度
        Real_temp=TH*10;

        while(1)
        {
                counter++;
                if(counter>40)        //if(counter>40)
                {
                        test_temp=1;        //进行一次温度检定
                        counter=0;                                
                }
                if(test_temp)         //温度检定标志置位,进入温度PID调节
                {
                        check_wendu();
                        Real_temp=c; //采集当前实际温度
                        if(Real_temp>TH*10)
                        PWM=1;
                        else                        
                        PID(TH*10,Real_temp); //PID程序
                        test_temp=0;                                 //检定完成,清温度检定标志
                }        

                display();
                KEY();
                Alarm();
               
        }
}


回复

举报

ID:149799 发表于 2020-3-22 17:38 | 显示全部楼层
谢谢,适合我初学者学习啊。
回复

举报

ID:420836 发表于 2020-3-23 02:37 | 显示全部楼层
你能详细告诉我们问题出在哪里吗? 你的代码写得很好
回复

举报

ID:190577 发表于 2020-3-25 09:17 | 显示全部楼层
TTQ001 发表于 2020-3-23 02:37
你能详细告诉我们问题出在哪里吗? 你的代码写得很好

就是PID这个几个参数这么设置出来?
回复

举报

ID:89515 发表于 2020-3-27 07:46 来自触屏版 | 显示全部楼层
PID,用继电器控制不现实吧,来不到多久就坏了。无法用PWM只能比例控制,那么这里PID没意义
回复

举报

ID:190577 发表于 2020-3-30 22:24 | 显示全部楼层
mengzhixinheng 发表于 2020-3-27 07:46
PID,用继电器控制不现实吧,来不到多久就坏了。无法用PWM只能比例控制,那么这里PID没意义

您好。客户要求的,那一般用什么控制呢?
回复

举报

ID:161164 发表于 2022-4-2 00:19 | 显示全部楼层
bbxyliyang 发表于 2020-3-30 22:24
您好。客户要求的,那一般用什么控制呢?

固态继电器
回复

举报

ID:883242 发表于 2022-4-2 00:22 | 显示全部楼层
bbxyliyang 发表于 2020-3-25 09:17
就是PID这个几个参数这么设置出来?

搜“Nichols-Ziegler method”
回复

举报

ID:883242 发表于 2022-4-2 00:25 | 显示全部楼层
mengzhixinheng 发表于 2020-3-27 07:46
PID,用继电器控制不现实吧,来不到多久就坏了。无法用PWM只能比例控制,那么这里PID没意义

外面厂家要求我的设备做26万次全寿命周期寿命试验,顺便测出来了国产继电器实测10万次簧片就酥了,因为电流远小于额定电流所以我没实测过触电寿命。但是楼主没说负载是什么,用固态继电器还是可以的,固态继电器没有寿命的概念,只有MTBF的概念。
回复

举报

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

本版积分规则

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

Powered by 单片机教程网

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