久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

立即注冊 登錄
返回首頁

uid:311330的個人空間

日志

基于PID的室溫控制

已有 714 次閱讀2018-4-28 13:49

#include?<reg51.h>
#include?<intrins.h>?
#define?uint?unsigned?int?
#define?uchar?unsigned?
char?sbit?sw=P2^0;???????//定義升溫?
sbit?jw=P2^1;???????//定義降溫?
sbit?beep?=?P2^2;???//定義揚聲器?
sbit?rs=P2^5;????//lcd的控制?
sbit?rw=P2^6;?
sbit?e=P2^7;?
sbit?DQ=P1^0;??
bit?flag=1;??

uchar??T=0,temp1=0,temp2=0,flag_dis=0;?
uchar?table[]={"Welcom?to?watch?"};?

uchar?table1[]={'0','1','2','3','4','5','6','7','8','9'};?
uchar?table2[]={"T=?"};




/******************************************************?
*?函數:1602?模塊?

*******************************************************/?
void?delay(uint?z)???//延時???
{?uchar?x,y;??
for(x=20;x>1;x--)???
for(y=z;y>1;y--);?
}?

void?lcd_writecom(uchar?com)???//寫地址函數?
{??rs=0;??
rw=0;??
P0=com;??
delay(10);?????
e=1;??
delay(10);??
e=0;?
}?

void?lcd_writedate(uchar?dat)???//寫數據函數?
{???
rs=1;
rw=0;??
P0=dat;??
delay(1);?????
e=1;??
delay(1);??
e=0;?
}?

void?init_1602(void)????//1602初始話?
{?delay(10);?
??
lcd_writecom(0x38);???
delay(5);?
??
lcd_writecom(0x38);???
delay(5);?
??
lcd_writecom(0x38);???
delay(5);?
??
lcd_writecom(0x01);????
delay(5);?
??
lcd_writecom(0x08);????
delay(5);?
??
lcd_writecom(0x06);?????
delay(5);
lcd_writecom(0x0C);?
}




/******************************************************????????????????
*?函數:18B20?模塊*?

*******************************************************/

bit?init_DS18B20(void)????
//18b20初始化程序?
{??bit?flag;???
uchar?time;???????????
DQ=1;????
_nop_();?
????
DQ=0;??????????????????//再將數據線從高拉低,要求保持480~960us?
for(time=0;time<200;time++)??//略微延時約600微秒//以向DS18B20發出?//一持續480~960us的低電平復位脈沖??
?DQ=1;???????????????????//釋放數據線(將數據線拉高)?

for(time=0;time<20;time++);?//延時約60us(釋放總線后需等待15~60us讓DS18B20輸出存在脈沖)?
flag=DQ;??????????????????//讓單片機檢測是否輸出了存在脈沖(DQ=0表示存在)???????

for(time=0;time<200;time++);??//延時足夠長時間,等待存在脈沖輸出完畢
return?(flag);???????//返回檢測成功標志?
}??

void??WriteOneChar?(?uchar?dat)???//18B20?寫?數據?
{?uchar?i,time;?
for?(i=0;?i<8;?i++)???
{?DQ=1;????
_nop_();??
?
DQ=0;??????????//將數據線從高拉低時即啟動寫時序??
for(time=0;time<1;time++);????????

DQ=dat&0x01;???//利用與運算取出要寫的某位二進制數據,并將其送到數據線上等待DS18B20采樣?

for(time=0;time<10;time++);//延時約30us,DS18B20在拉低后的約15~60us期間從數據線上采樣?

DQ=1;??????????//釋放數據線???????

for(time=0;time<1;time++)?????//延時3us,兩個寫時序間至少需要1us的恢復期?

dat>>=1;???????//將dat中的各二進制位數據右移1位???}?}?

?

uchar?ReadOneChar(void)???//18B20?讀數據

?
{??
uchar?time,i,date=0;???//儲存讀出的一個字節數據?
for?(i=0;i<8;i++)???
{?DQ=1;????
_nop_();??

DQ?=?0;??????//單片機從DS18B20讀書據時,將數據線從高拉低即啟動讀時序?
for(time=0;time<1;time++);????//等待一個機器周期??????

DQ?=?1;????//將數據線"人為"拉高,為單片機檢測DS18B20的輸出電平作準備?
for(time=0;time<2;time++);??//延時約6us,使主機在15us內采樣?
date>>=1;?????????
if(DQ==1)?
???
date|=0x80;??//如果讀到的數據是1,則將1存入dat???
else?
??????
date|=0x00;?????????????//如果讀到的數據是0,則將0存入dat????????

for(time=0;time<15;time++);??//延時3us,兩個讀時序之間必須有大于1us的恢復期??
??}??????????????????????
??
return(date);??????//返回讀出的十六進制數據?}??

void?tempchange_get(void)??//啟動18B20溫度轉換+?讀取數據


{???
??uchar?a,b[3],p,time,tempL,tempH;???
init_DS18B20();?????//18B20?復位???
WriteOneChar(0xcc);???//跳過讀ROM?指令???
WriteOneChar(0x44);??//寫溫度轉換指令????
delay(10);?
????
init_DS18B20();?????????//準備讀數據的初始化??????
for(time=0;time<2;time++);??
????
WriteOneChar(0xcc);???//跳過ROM?????
WriteOneChar(0xbe);???//讀暫存器?????
tempL?=?ReadOneChar();???//讀低8?位?????
tempH?=?ReadOneChar();????//讀高8?位????
a=tempH&0x80;?
if(a)?????????????????//為一則是負溫度,補碼修正,為零則是正溫度,不處理?
??
{?if(tempL==0){tempH--;tempL=255;}?????
tempL-=1;??
tempL=~tempL;??
tempH=~tempH;?????
flag_dis=1;???
}


else?????
flag_dis=0;????//對取出的數據處理?
??
temp1=tempL&0x0F;??????//取第一字節低四位???
b[0]=temp1&0x08;???
b[1]=temp1&0x04;???
b[2]=temp1&0x02;???
b[3]=temp1&0x01;????
??
b[0]>>=3;???
b[1]>>=2;???
b[2]>>=1;?
??
p=b[0]*5000+b[1]*2500+b[2]*1250+b[3]*625;???//小數位化整數???
temp2=p/1000;????????//第一個小數位???
??
tempL&=0xF0;?????????//取第一字節高四位???
tempL>>=4;?
??
temp1=tempH&0x0F;??//取第二字節低四位???
temp1<<=4;?
??
temp1=temp1&0x7F;???????//只取7位??
temp1=temp1|tempL;???//合并為整數位?數據?
}
/******************************************************?
*?函數:對18B20?獲取的溫度進行處理*?

*******************************************************/



void?deal(uint?t)?
{if(flag_dis==1)t=0;?

if((t<10)||(t>40))beep=0;?
else?beep=1;?
if(t<20)sw=0;
else?
sw=1;??
if(t>30)
jw=0;
else?
jw=1;??}????????

/******************************************************????????????
函數功能:顯示模塊*?

*******************************************************/?
void?display(uchar?temp1,uchar?temp2?)?
{??uchar?i;?
???
lcd_writecom(0x80);


delay(3);?
for(i=0;i<3;i++)?
??
{lcd_writedate(table2[i]);??????
delay(3);}?

if(flag_dis)lcd_writedate('-');?????//結果為負數顯示??
lcd_writedate(table1[temp1/100]);??//?百位為0不顯示????
delay(3);?
?
lcd_writedate(table1[temp1%100/10]);??????
delay(3);?
?
lcd_writedate(table1[temp1%100%10]);?????
delay(3);?
?
lcd_writedate('.');??//小數點?????
delay(3);?
?
lcd_writedate(table1[temp2]);??
lcd_writecom(0x88);???
lcd_writedate('0');?}?

void?error_ds(flag)?
{?uchar?i;?
if(flag==1)?
?
{lcd_writecom(0x80);???//等待的顯示


delay(3);??
for(i=0;i<16;i++)?
?
{?lcd_writedate(table[i]);?????
delay(3);????}??}?}?

/******************************************************???????????????
函數名稱:main(void);?*??

*******************************************************/?
void?main(void)?{???
????
init_1602();????//LCD?初始化?????
error_ds(flag);?

while(init_DS18B20());??//18b20初始化???

lcd_writecom(0x01);???//清屏?
while(1)???????
{?????
???
tempchange_get();??//溫度轉換


deal(temp1);????????//溫度處理????
display(temp1,temp2);???//????
delay(5);?????????????//掃描周期???}?}?

























































#include <at89x51.h>//用AT89C51時就用這個頭文件
#include <absacc.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <DS18B20.h>
#include "LCD1602.h"   ////液晶顯示頭文件
//sbit DQ = P3^4;     //定義DQ引腳為P3.4
unsigned char t[2],*pt;    //用來存放溫度值,測溫程序就是通過這個數組與主函數通信的
unsigned char  TempBuffer1[9]={0x2b,0x31,0x32,0x32,0x2e,0x30,0x30,0x43,'\0'};
        //顯示實時溫度,上電時顯示+125.00C
unsigned char  TempBuffer0[17]={0x54,0x48,0x3a,0x2b,0x31,0x32,0x35,0x20,
        0x54,0x4c,0x3a,0x2b,0x31,0x32,0x34,0x43,'\0'};
        //顯示溫度上下限,上電時顯示TH:+125 TL:+124C       
unsigned char code dotcode[4]={0,25,50,75};
/***因顯示分辨率為0.25,但小數運算比較麻煩,故采用查表的方法*******
再將表值分離出十位和個位后送到十分位和百分位********************/


void covert0( unsigned char TH, unsigned char TL) //將溫度上下限轉換為LCD顯示的數據
   if(TH>0x7F)                    //判斷正負,如果為負溫,將其轉化為其絕對值
   {
    TempBuffer0[3]=0x2d;      //0x2d為"-"的ASCII碼
 TH=~TH;
 TH++;
   }
   else TempBuffer0[3]=0x2b; //0x2B為"+"的ASCII碼

   if(TL>0x7f)
   {
   TempBuffer0[11]=0x2d;      //0x2d為"-"的ASCII碼
   TL=~TL+1;
   }
   else TempBuffer0[11]=0x2b; //0x2B為"+"的ASCII碼

  TempBuffer0[4]=TH/100+0x30;               //分離出TH的百十個位
  if( TempBuffer0[4]==0x30) TempBuffer0[4]=0xfe; //百位數消隱
  TempBuffer0[5]=(TH%100)/10+0x30;    //分離出十位
  TempBuffer0[6]=(TH%100)%10+0x30;     //分離出個位
  TempBuffer0[12]=TL/100+0x30;               //分離出TL的百十個位
  if( TempBuffer0[12]==0x30) TempBuffer0[12]=0xfe; //百位數消隱
  TempBuffer0[13]=(TL%100)/10+0x30;    //分離出十位
  TempBuffer0[14]=(TL%100)%10+0x30;     //分離出個位
}


void covert1(void) //將溫度轉換為LCD顯示的數據
{
   unsigned char x=0x00,y=0x00;
   t[0]=*pt;
   pt++;
   t[1]=*pt;
   if(t[1]>0x07)                    //判斷正負溫度
   {
    TempBuffer1[0]=0x2d;      //0x2d為"-"的ASCII碼
 t[1]=~t[1];    /*下面幾句把負數的補碼*/
 t[0]=~t[0];    /* 換算成絕對值*********/
 x=t[0]+1;     /***********************/
 t[0]=x;      /***********************/
 if(x>255)                /**********************/
 t[1]++;     /*********************/
   }
   else TempBuffer1[0]=0x2b; //0xfe為變"+"的ASCII碼
  t[1]<<=4;  //將高字節左移4位
  t[1]=t[1]&0x70;  //取出高字節的3個有效數字位
  x=t[0];     //將t[0]暫存到X,因為取小數部分還要用到它
  x>>=4;     //右移4位
  x=x&0x0f;     //和前面兩句就是取出t[0]的高四位 
  t[1]=t[1]|x;   //將高低字節的有效值的整數部分拼成一個字節
  TempBuffer1[1]=t[1]/100+0x30;               //+0x30 為變 0~9 ASCII碼
   if( TempBuffer1[1]==0x30) TempBuffer1[1]=0xfe; //百位數消隱
  TempBuffer1[2]=(t[1]%100)/10+0x30;    //分離出十位
  TempBuffer1[3]=(t[1]%100)%10+0x30;     //分離出個位
  t[0]=t[0]&0x0c;       //取有效的兩位小數
  t[0]>>=2;         //左移兩位,以便查表
  x=t[0];          
  y=dotcode[x];         //查表換算成實際的小數
  TempBuffer1[5]=y/10+0x30;       //分離出十分位
  TempBuffer1[6]=y%10+0x30;       //分離出百分位
}   

void delay(unsigned char i)
{
 while(i--);
}

main()
{
 unsigned char TH=110,TL=-20;         //下一步擴展時可能通過這兩個變量,調節上下限
                 //測溫函數返回這個數組的頭地址
 while(1)
 { 
    pt=ReadTemperature(TH,TL,0x3f);   //上限溫度-22,下限-24,分辨率10位,也就是0.25C
           //讀取溫度,溫度值存放在一個兩個字節的數組中,
 delay(100); 
 covert1();
 covert0(TH,TL);
 LCD_Initial();       //第一個參數列號,第二個為行號,為0表示第一行
           //為1表示第二行,第三個參數為顯示數據的首地址
 LCD_Print(0,0,TempBuffer0); 
 LCD_Print(0,1,TempBuffer1);     
 }
}












































//第一步:定義PID變量結構體,代碼如下:
struct _pid{
    float SetSpeed;            //定義設定值
    float ActualSpeed;        //定義實際值
    float err;                //定義偏差值
    float err_last;            //定義上一個偏差值
    float Kp,Ki,Kd;            //定義比例、積分、微分系數
    float voltage;          //定義電壓值(控制執行器的變量)
    float integral;            //定義積分值
}
//pid控制算法中所需要用到的參數在一個結構體中統一定義,方便后面的使用。
//第二部:初始化變量,代碼如下:
void PID_init(){
    printf("PID_init begin \n");
    pid.SetSpeed=0.0;
    pid.ActualSpeed=0.0;
    pid.err=0.0;
    pid.err_last=0.0;
    pid.voltage=0.0;
    pid.integral=0.0;
    pid.Kp=0.2;
    pid.Ki=0.015;
    pid.Kd=0.2;
    printf("PID_init end \n");
}
//統一初始化變量,尤其是Kp,Ki,Kd三個參數,調試過程當中,對于要求的控制效果,可以通過調節這三個量直接進行調節。
//第三步:編寫控制算法,代碼如下:
float PID_realize(float speed){
    pid.SetSpeed=speed;
    pid.err=pid.SetSpeed-pid.ActualSpeed;
    pid.integral+=pid.err;
    pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
    pid.err_last=pid.err;
    pid.ActualSpeed=pid.voltage*1.0;
    return pid.ActualSpeed;
}
//注意:這里用了最基本的算法實現形式,沒有考慮死區問題,沒有設定上下限,只是對公式的一種直接的實現,后面的介紹當中還會//逐漸的對此改進。
//到此為止,PID的基本實現部分就初步完成了。下面是測試代碼:
int main(){
    printf("System begin \n");
    PID_init();
    int count=0;
    while(count<1000)
    {
        float speed=PID_realize(200.0);
        printf("%f\n",speed);
        count++;
    }
return 0;
}

















//pid.h
#ifndef __PID__
#define __PID__
/*PID = Uk + KP*[E(k)-E(k-1)]+KI*E(k)+KD*[E(k)-2E(k-1)+E(k-2)];(增量型PID算式)
函數入口: RK(設定值),CK(實際值),KP,KI,KD
函數出口: U(K)*/
typedef struct PIDValue
{
    int8 KP;
    int8 KI;
    int8 KD;
    int8 F;
    int8 BITMOV;
    int EK[3];
     
    int UK;
    int RK;
    int CK;
    int UK_REAL;
 
}pid_str;
//PIDValueStr  PID;
void    pid_exe(pid_str *PID)  ;
#endif
 
//pid.c
/*PID = PID->UK_REAL + PID->KP*[E(k)-E(k-1)]+PID->KI*E(k)+PID->KD*[E(k)-2E(k-1)+E(k-2)];(增量型PID算式)
函數入口: PID->RK(設定值),PID->CK(實際值),PID->KP,PID->KI,PID->KD
函數出口: U(K)*/
#include"defines.h"
#include"pid.h"
#define MAXOUT 0xff
//#define MAXGAP 100
 
void pid_exe(pid_str*PID)
{
    PID->EK[2]=PID->EK[1];
    PID->EK[1]=PID->EK[0];
    PID->EK[0]=PID->RK-PID->CK;
    PID->UK_REAL=PID->UK_REAL
        +PID->KP*(PID->EK[0]-PID->EK[1])//微分一次后積分即原數
        +(float)PID->KI*PID->EK[0]/PID->F//直接積分
        +(float)PID->KD*(PID->EK[0]-2*PID->EK[1]+PID->EK[2])*PID->F;//二階微分后積分即一階微分
    if((PID->UK_REAL>>PID->BITMOV)>=MAXOUT)
    {
        PID->UK=MAXOUT;
    }else if(PID->UK_REAL>>PID->BITMOV<=-MAXOUT)
    {
        PID->UK=-MAXOUT;
    }else
    {
        PID->UK=PID->UK_REAL>>PID->BITMOV;
    }
         
}








路過

雞蛋

鮮花

握手

雷人

評論 (0 個評論)

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

返回頂部
主站蜘蛛池模板: 欧美午夜久久 | 亚洲国产成人av好男人在线观看 | 一级毛片免费看 | 亚洲国产精品一区二区久久 | 国产黄色小视频在线观看 | 中文字幕一区二区三区乱码在线 | 色99视频| 国产国语精品 | 国产精品爱久久久久久久 | 天啪| 天天色图 | 亚洲国产一区二区三区四区 | 国产免费一区二区 | 精品国产乱码一区二区三区a | 69堂永久69tangcom | 成av人电影在线 | 一区二区三区四区免费视频 | 欧美一级免费看 | 亚洲精品一区二区三区中文字幕 | 国产精品国产自产拍高清 | 午夜天堂精品久久久久 | 国产一区二区三区视频免费观看 | 成人不卡一区二区 | 欧美日韩精品 | 男女视频在线观看网站 | 国产精品久久国产精品 | 天天操天天拍 | 51ⅴ精品国产91久久久久久 | 亚洲精品成人 | 国产精品一区二区无线 | 亚洲福利在线视频 | 成人中文字幕在线观看 | 欧美专区日韩专区 | 亚洲午夜视频在线观看 | 精品一区二区三区在线观看 | 日韩av资源站 | 欧美精品在线观看 | 国产精品国产三级国产aⅴ原创 | 麻豆久久 | 成人性生交大片 | 毛片区 |