毕业设计做一个基于PID控制的恒温箱,编写代码并且用 proteus进行 仿真,出现错误,一步步缩小错误的范围,错误在十几行代码之中可是始终找不到哪里错了,怎么改都改不对,这一步没通过 PID控制器我也不知道编写的对不对。请给位老师帮忙看看。
运行之后按S4 两下电机应该转动,输出功率在代码中调的最大了,应该转速最高。可是一运行先停顿几秒,然后才慢慢的转,忽快忽慢,总体上升,代码错误最终缩小到的范围在图那里
- #include
- #include
- #include
- #define MAIN_Fosc 11059200UL //宏定义主时钟HZ
- #define uint unsigned int
- #define uchar unsigned char
- ***it DS = P2^2; //DS18B20单总线
- ***it DU=P2^0;
- ***it WE=P2^1;
- ***it pwm1=P2^3; //加热
- ***it pwm2=P2^4; //制冷
- ***it key_s1=P3^0;
- ***it key_s2=P3^1;
- ***it key_s3=P3^2;
- ***it key_s4=P3^3;
- ***it tempflag=P2^5;
- ***it tempflag1=P2^6;
- uint ge,shi,bai,temp,shu,wei,num,i,count;
- uchar L,t,M,pwm,flag,calcflag;
- uint PWM_T=0,Out;
- int DERR1 = 0; //
- int DERR2 = 0; //
-
- float Pout = 0; //比例结果
- float Iout = 0; //积分结果
- float Dout = 0; //微分结果
- unsigned int Out = 0; //总输出
- static unsigned int Out1; //记录上次输出
-
- static int ERR; //当前误差
- static int ERR1; //上次误差
- static int ERR2; //上上次误差
- uchar code duantable[]={
- 0x3f,0x06,0x5b,0x4f,
- 0x66,0x6d,0x7d,0x07,
- 0x7f,0x6f};
- uchar code weitable[]={0xfe, 0xfd, 0xfb, 0xf7};
- //毫秒级延时(多少)微秒
- void Delay_Ms(int ms)
- {
- int i;
- do{
- i = MAIN_Fosc / 96000;
- while(--i); //96T per loop
- }while(--ms);
- }
- //(多少)微秒 ds18b20用
- void Delay_us(uchar us)
- {
- while(us--);
- }
- //定时器0初始化
- void time1int()
- {
- TMOD = 0x10; //定时器1,工作模式1,
- TH0=0xA4; //写入预置初值 100微秒中断一次
- TL0=0xA4; //写入预置值 (
- TR0=1; //启动定时器
- ET0=1; //允许定时器0中断
- EA=1; //允许总中断
- PWM_T=0;//占空比初值 自行理解其实占空比此时是100 这样子帕尔贴才没有电流通过,电桥平衡状态
- }
- void HPIDCalc(int Sv,int Pv) //加热PID Sv设定温度值 Pv当前温度值 PID增量式 i和shu
- {
-
- /******************
-
- *以下四项是需要根据实际情况调试的
-
- ******************/
-
- static int pidt = 500; //500MS计算一次 计算周期
- static char Kp = 30; //比例系数
- static unsigned int Ti= 5000; //积分时间
- static unsigned int Td= 600; //微分时间
- ERR = Sv - Pv; //算出当前误差
- if(ERR>5)
- {
- Out = 100;
- }
- else
- {
- DERR1 = ERR - ERR1; //上次
- DERR2 = ERR - 2 * ERR1 + ERR2; //上上次
-
- Pout = Kp * DERR1; //输出P
- Iout = (float)(ERR * ((Kp * pidt) / Ti)); //输出I
- Dout = (float)(DERR2 * ((Kp * Td) / pidt)); //输出D
- Out = (unsigned int)(Out1 + Pout + Iout + Dout);
-
-
- if(Out >= 100) //如果输出大于等于上限
- {
- Out=100;
- }
- else if(Out <= 0) //如果输出小于等于下线
- {
- Out=0;
- }
-
- Out1 = Out; //记录这次输出的值
- ERR2 = ERR1; //记录误差
- ERR1 = ERR; //记录误差
- }
- // PWM_T=Out;//输出PWM 调节占空比
- }
- void LPIDCalc(int Sv,int Pv) //制冷PID Sv设定温度值 Pv当前温度值 PID增量式 i和shu
- {
- /******************
-
- *以下四项是需要根据实际情况调试的
-
- ******************/
-
- static int pidt = 500; //500MS计算一次 计算周期
- static char Kp = 30; //比例系数
- static unsigned int Ti= 5000; //积分时间
- static unsigned int Td= 600; //微分时间
- ERR = Pv - Sv; //算出当前误差
- if(ERR>5)
- {
- Out = 100;
- }
- else
- {
- DERR1 = ERR - ERR1; //上次
- DERR2 = ERR - 2 * ERR1 + ERR2; //上上次
-
- Pout = Kp * DERR1; //输出P
- Iout = (float)(ERR * ((Kp * pidt) / Ti)); //输出I
- Dout = (float)(DERR2 * ((Kp * Td) / pidt)); //输出D
- Out = (unsigned int)(Out1 + Pout + Iout + Dout);
-
-
- if(Out >= 100) //如果输出大于等于上限
- {
- Out=100;
- }
- else if(Out <= 0) //如果输出小于等于下线
- {
- Out=0;
- }
-
- Out1 = Out; //记录这次输出的值
- ERR2 = ERR1; //记录误差
- ERR1 = ERR; //记录误差
- }
- // PWM_T=Out;//输出PWM
- }
- //初值和百十个的赋值
- void chuzhi()
- {
- temp=265;
-
- bai=temp/100;
- shi=temp%100/10;
- ge=temp%10;
-
- }
- //数字加减函数部分
- void jiajian()
- {
- if(key_s1==0)
- {
- Delay_Ms(20);
- if(key_s1==0)
- {
- if(num<9)
- num++;
- while(!key_s1);
- }
- }
- if(key_s2==0)
- {
- Delay_Ms(20);
- if(key_s2==0)
- {
- if(num>0)
- num--;
- while(!key_s2);
- }
- }
- }
- //显示函数部分
-
- void display(int Value) //注意由于需要显示的数大于一个字节所有形参需为int型
- {
- //------------------------------
- WE = 0; //关闭位选
- P0 = weitable[0]; //第一位数码管
- WE = 1; //打开位选
- WE = 0; //关闭位选
- DU = 0; //关闭段选
- P1 = duantable[Value/100]; //数码管显示百位
- DU = 1; //打开段选
- DU = 0; //关闭段选
-
- Delay_Ms(15);
-
-
- WE = 0;
- P0 = weitable[1]; //第二位数码管
- WE = 1;
- WE = 0;
- DU = 0;
- P1 = duantable[Value%100/10]|0x80; //显示十位
- DU = 1;
- DU = 0;
-
- Delay_Ms(15);
-
- WE = 0;
- P0 = weitable[2]; //第三位数码管
- WE = 1;
- WE = 0;
- DU = 0;
- P1 = duantable[Value%10]; //显示个位
- DU = 1;
- DU = 0;
- Delay_Ms(15);
-
- }
- //设置函数 可以做到移位和加减数字
- void yiweitiaozheng()
- {
- if(flag==1)
- {
- while(1) //while1循环中 再按一次key4跳出循环
- {
- if(key_s3==0)//左移右移 也就是说 控制wei 是第几位 就能调第几位的大小
- {
- Delay_Ms(20);
- if(key_s3==0)
- {
- wei++;
- if(wei==3)
- {
- wei=0;
- }
-
-
- while(!key_s3);
- }
- }
- switch(wei) //移位函数
- {
- case 0: num=bai; jiajian(); bai=num; break;
- case 1: num=shi; jiajian(); shi=num; break;
- case 2: num=ge ; jiajian(); ge=num; break;
- }
- shu=ge+shi*10+bai*100;
- if(shu>=850)
- shu=849;
- if(shu<0)
- shu=0;
- display(shu); //设定好的温度值
- if(key_s4==0)
- {
- Delay_Ms(20);
- if(key_s4==0)
- {
- flag++; //flag由1变2
- break;
- while(!key_s4);
- }
- }
-
- }
- }
- }
- /*ds18b20部分*/
- /*单总线初始化时序*/
- bit ds_init()
- {
- bit i;
- DS = 1;
- _nop_();
- DS = 0;
- Delay_us(75); //拉低总线499.45us 挂接在总线上的18B20将会全部被复位
- DS = 1; //释放总线
- Delay_us(4); //延时37.95us 等待18B20发回存在信号
- i = DS;
- Delay_us(20); //141.95us
- DS = 1;
- _nop_();
- return (i);
- }
- /*写一个字节*/
- void write_byte(uchar dat)
- {
- uchar i;
- for(i=0;i<8;i++)
- {
- DS = 0;
- _nop_();//产生些时序
- DS = dat & 0x01;
- Delay_us(10);//76.95us
- DS = 1; //释放总线准备下一次数据写入
- _nop_();
- dat >>= 1;
- }
- }
- /*读一个字节*/
- uchar read_byte()
- {
- uchar i, j, dat;
- for(i=0;i<8;i++)
- {
- DS = 0;
- _nop_();//产生读时序
- DS = 1;
- _nop_();//释放总线
- j = DS;
- Delay_us(10);//76.95us
- DS = 1;
- _nop_();
- dat = (j<<7)|(dat>>1);
- }
- return (dat);
- }
- void wendujisuan()
- {
- ds_init();//初始化DS18B20
- write_byte(0xcc);//发送跳跃ROM指令
- write_byte(0x44);//发送温度转换指令
- ds_init();//初始化DS18B20
- write_byte(0xcc);//发送跳跃ROM指令
- write_byte(0xbe);//读取DS18B20暂存器值
- L = read_byte();
- M = read_byte();
- i = M;
- i <<= 8;
- i |= L;
- i = i * 0.0625 * 10 + 0.5;
- display(i); //温度计显示的温度
- }
- //void calc()
- //{
- //
- //}
- //主函数部分
- void main()
- {
- chuzhi();
- time1int();//初始化定时器1
- while(1)
- {
- if(key_s4==0)
- {
- Delay_Ms(20);
- if(key_s4==0)
- {
- flag++; //flag从0变1,进入设定温度部分,flag=2电机开始工作
- if(flag==3)
- {
- flag=0;
- }
- while(!key_s4);
- }
- }
- yiweitiaozheng(); //设定温度
- wendujisuan(); //箱内实时温度显示
- if(flag==2)
- {
-
- if(shu>=i) //设定温度高于箱内实际温度
- {
- tempflag=0;//加热 p2.5亮制热
- tempflag1=1;
- pwm1=pwm; //加热 电流正向
- pwm2=1;
- }
- else
- {
- tempflag1=0;//p2.6亮制冷
- tempflag=1;
- pwm2=pwm; //制冷 电流反向
- pwm1=1;
- }
- }
- if(count>=500)//定时0.5秒,0.5秒取样一次 100*5000=500毫秒
- {
- count=0;
- if(shu>=i)
- {
- // HPIDCalc(shu,i);
- Out=99;
- }
- else
- {
- // LPIDCalc(shu,i);
- Out=99;
- }
-
- PWM_T=Out;
- }
- }
- }
- //用定时器0控制pwm的占空比,,主程序中的PWM_T的大小(0--1000之间)就是占空比的大小
- timer1() interrupt 1 // 直接用中断函数输出pwm
- {
- count++;
- //
- t++; //每次定时器溢出加1
- if(t==100) //PWM周期 100个单位 100微秒*100=10毫秒,PWM周期10毫秒
- {
- t=0; //使t=0,开始新的PWM周期
- }
-
- if(t<=PWM_T) //按照当前占空比切换输出为高电平 此处由于H桥的这个电路在proteus中接低电平就会报错 没有办法 向电路妥协向proteus妥协
- {
- pwm=0;
- } //占空比就是高电平持续的时间 通电时间
- else
- {
- pwm=1;
- }
- }
-
复制代码
0
已退回10积分
|
|
|
|