找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 12390|回复: 26
打印 上一主题 下一主题
收起左侧

单片机PID算法的恒温控制系统仿真与程序源码设计(DS18B20传感器)

  [复制链接]
跳转到指定楼层
#
楼主做的基于51单片机主控的PID算法的恒温控制系统,带加热指示灯与递增和递减按钮.采用DS18B20做的温度传感器

电路原理图如下:


仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)


恒温控制系统实物图:


pcb图:




恒温控制系统的单片机源程序如下:
  1. #include<reg52.h>
  2. #include<intrins.h>
  3. #include<math.h>
  4. #include<string.h>
  5. struct PID {
  6. unsigned int SetPoint; // 设定目标 Desired Value
  7. unsigned int Proportion; // 比例常数 Proportional Const
  8. unsigned int Integral; // 积分常数 Integral Const
  9. unsigned int Derivative; // 微分常数 Derivative Const
  10. unsigned int LastError; // Error[-1]
  11. unsigned int PrevError; // Error[-2]
  12. unsigned int SumError; // Sums of Errors
  13. };
  14. struct PID spid; // PID Control Structure
  15. unsigned int rout; // PID Response (Output) 响应输出
  16. unsigned int rin; // PID Feedback (Input)//反馈输入
  17. unsigned char high_time,low_time,count=0;//占空比调节参数
  18. #define uchar unsigned char
  19. #define uint unsigned int

  20. sbit output=P1^0;
  21. sbit ds=P3^2;
  22. sbit DQ=P3^2;//ds18b20与单片机连接口
  23. sbit lcden=P2^7;//LCE使能引脚
  24. sbit lcdrs=P2^5;
  25. sbit lcdrw=P2^6;
  26. sbit ledred=P1^6;
  27. sbit ledgreen=P1^7;

  28. sbit key0=P2^0;//按键引脚
  29. sbit key1=P2^1;

  30. uchar set[2]={0};
  31. uchar code str1[]="now temp:      C";
  32. uchar code str2[]="set temp:      C";
  33. uchar code table[]={0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};
  34. uchar n,num;
  35. int set_temper=30,temper,temp; //温度变量定义
  36. unsigned int s;
  37. float         f_temp;//转换后的温度

  38. uint tvalue;         
  39. uchar tflag;//温度正负标志

  40. void delay(i)//延时函数
  41. {
  42.         uint j;
  43.         for(i;i>0;i--)
  44.         for(j=110;j>0;j--);
  45. }


  46. void wr_com(uchar ml)//写命令
  47. {
  48.         lcdrs=0;
  49.         P0=ml;
  50.         delay(5);
  51.         lcden=1;
  52.         delay(5);
  53.         lcden=0;

  54. }



  55. void wr_data(uchar shuju)//写数据
  56. {
  57.         lcdrs=1;
  58.         //lcden=1;
  59.         P0=shuju;
  60.         delay(5);
  61.         lcden=1;
  62.         delay(5);
  63.         lcden=0;

  64. }

  65. void init()  //按照时序操作的初始化
  66. {        
  67.         lcdrw=0;
  68.         wr_com(0x38);//显示模式设置,设置为16*2显示,5*7点阵,八位数据口
  69.         wr_com(0x0c);//开显示,但不开光标,光标不闪
  70.         wr_com(0x06);//显示光标移动设置
  71.         wr_com(0x01);// 清屏
  72.         wr_com(0x80);        // 数据指针初始化
  73.         for(num=0;num<16;num++)
  74.                 {
  75.                         wr_data(str1[num]);//now temp
  76.                 }
  77.         wr_com(0x80+0x40); //地址初始化
  78.         for(num=0;num<16;num++)
  79.                 {
  80.                         wr_data(str2[num]);//set temp
  81.                 }         
  82. }

  83. /*************************DS1820程序****************************/
  84. void delay_18B20(unsigned int i)//延时1微秒
  85. {
  86.    while(i--);
  87. }

  88. void ds1820rst(void)/*ds1820复位*/
  89. {
  90.         unsigned char x=0;
  91.         DQ = 1;          //DQ复位
  92.         delay_18B20(4); //延时
  93.         DQ = 0;          //DQ拉低
  94.    TR0=0;
  95.         delay_18B20(100); //精确延时大于
  96.    TR0=1;
  97.         DQ = 1;          //拉高
  98.         delay_18B20(40);
  99. }

  100. uchar ds1820rd(void)/*读数据*/
  101. {
  102.         unsigned char i=0;
  103.         unsigned char dat = 0;
  104.    TR0=0;
  105.         for (i=8;i>0;i--)
  106.         {   
  107.                 DQ = 0; //给脉冲信号
  108.                 dat>>=1;
  109.                 DQ = 1; //给脉冲信号
  110.                 if(DQ)
  111.                 dat|=0x80;
  112.                 delay_18B20(10);
  113.         }
  114.    return(dat);
  115. }

  116. void ds1820wr(uchar wdata)/*写数据*/
  117. {
  118.         unsigned char i=0;
  119.    TR0=0;
  120.    for (i=8; i>0; i--)
  121.    {
  122.                 DQ = 0;
  123.                 DQ = wdata&0x01;
  124.                 delay_18B20(10);
  125.                 DQ = 1;
  126.                 wdata>>=1;
  127.    }
  128. }



  129. uint get_temper()//获取温度
  130. {  
  131.      
  132.         uchar a,b;

  133.         ds1820rst();   
  134.         ds1820wr(0xcc);//*跳过读序列号*/
  135.         ds1820wr(0x44);//*启动温度转换*/
  136.         ds1820rst();   
  137.         ds1820wr(0xcc);//*跳过读序列号*/
  138.         ds1820wr(0xbe);//*读取温度*/
  139.         a=ds1820rd();
  140.         b=ds1820rd();
  141.    
  142.         tvalue=b;
  143.         tvalue<<=8;
  144.         tvalue=tvalue|a;
  145.    TR0=1;
  146.    if(tvalue<0x0fff)   tflag=0;
  147.    else {tvalue=~tvalue+1;tflag=1;}
  148.         tvalue=tvalue*(0.625);//温度值扩大10倍,精确到1位小数
  149.         temp=tvalue;
  150.         return temp;
  151. }


  152. void dis_temp(int t)//显示温度
  153. {
  154.         uchar d0,d1,d2,d3;
  155.         //t=26;
  156.         if(tflag==0)
  157.         {
  158.                 d0=t/1000+0x30;
  159.                 d1=t%1000/100+0x30;
  160.                 d2=t%100/10+0x30;
  161.                 d3=t%10+0x30;
  162.                 if(d0==0x30)
  163.                 {
  164.                         wr_com(0x80+9);
  165.                         wr_data(d1);
  166.                         wr_com(0x80+10);
  167.                         wr_data(d2);
  168.                         wr_com(0x80+11);
  169.                         wr_data(0x2e);
  170.                         wr_com(0x80+12);
  171.                         wr_data(d3);
  172.                 }
  173.                 else
  174.                 {
  175.                         wr_com(0x80+9);
  176.                         wr_data(d0);
  177.                         wr_com(0x80+10);
  178.                         wr_data(d1);
  179.                         wr_com(0x80+11);
  180.                         wr_data(d2);
  181.                         wr_com(0x80+12);
  182.                         wr_data(' ');
  183.                 }
  184.                
  185.         }
  186.         else
  187.         {
  188.                 wr_com(0x80+9);
  189.                 wr_data('-');
  190.                 wr_com(0x80+10);
  191.                 wr_data(d1);
  192.                 wr_com(0x80+11);
  193.                 wr_data(d2);
  194.                 wr_com(0x80+12);
  195.                 wr_data(' ');
  196.                 //wr_com(0x80+12);
  197.                 //wr_data(d3);
  198.         }
  199.         wr_com(0x80+14);
  200.         wr_data(0xdf);
  201.         temper=t/10;
  202. }



  203. void keyscan()//键盘扫描
  204. {  

  205.    if(key0==0)
  206.    {
  207.                 delay(1);
  208.                 if(key0==0)
  209.                 {
  210.                         while(!key0);
  211.                         delay(1);
  212.                         while(!key0);
  213.                         set_temper++;

  214.                 }

  215.                 set[0]=set_temper/10;        //获得设置温度显示值
  216.                 set[1]=set_temper%10;
  217.                 wr_com(0x80+0x40+9);
  218.                 wr_data(table[set[0]]);
  219.                 delay(1);
  220.                 wr_com(0x80+0x40+10);
  221.                 wr_data(table[set[1]]);
  222.                 delay(1);
  223.                 //wr_com(0x80+0x40+11);
  224.                 //wr_data(0x2e);
  225.                 //wr_com(0x80+0x40+14);
  226.                 //wr_data(0xdf);
  227.                 delay(1);
  228.         }
  229.         if(key1==0)
  230.    {
  231.                 delay(3);//延时去抖
  232.                 if(key1==0)
  233.                 {
  234.                         while(!key1);
  235.                         delay(3);
  236.                         while(!key1);
  237.                         set_temper--;//温度减
  238.                         if(set_temper==0)
  239.                         {set_temper=0;}
  240.                 }

  241.         
  242.                 set[0]=set_temper/10;        //获得设置温度显示值
  243.                 set[1]=set_temper%10;
  244.                 wr_com(0x80+0x40+9);        //显示设置温度值
  245.                 wr_data(table[set[0]]);
  246.                 delay(1);
  247.                 wr_com(0x80+0x40+10);
  248.                 wr_data(table[set[1]]);
  249.                 delay(1);
  250.                 //wr_com(0x80+0x40+11);
  251.                 //wr_data(0x2e);
  252.                 wr_com(0x80+0x40+14);
  253.                 wr_data(0xdf);
  254.                 delay(1);
  255.         }
  256. }

  257. void PIDInit (struct PID *pp)
  258. {
  259.         memset ( pp,0,sizeof(struct PID)); //用参数0初始化pp
  260. }



  261. unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint ) //PID计算
  262. {
  263.         unsigned int dError,Error;
  264.         Error = pp->SetPoint - NextPoint; // 偏差
  265.         pp->SumError += Error; // 积分
  266.         dError = pp->LastError - pp->PrevError; // 当前微分
  267.         pp->PrevError = pp->LastError;
  268.         pp->LastError = Error;
  269.         return (pp->Proportion * Error//比例
  270.         + pp->Integral * pp->SumError  //积分项
  271.         + pp->Derivative * dError); //   微分项
  272. }
  273. /***********************************************************
  274. 温度比较处理子程序
  275. ***********************************************************/
  276. void compare_temper(void)
  277. {
  278.         unsigned char i;
  279.         if(set_temper>temper)         //设置温度大于当前温度
  280.         {
  281.                 ledred=0;
  282.                 ledgreen=1;
  283.                 if(set_temper-temper>1)         //温度相差1度以上
  284.                 {         
  285.                         high_time=100;
  286.                         low_time=0;
  287.                 }
  288.                 else         //设置温度不大于当前温度
  289.                 {
  290.                         for(i=0;i<10;i++)
  291.                         {
  292.                                 get_temper();
  293.                                 rin = s; // Read Input
  294.                                 rout = PIDCalc ( &spid,rin ); // Perform PID Interation
  295.                         }
  296.                         if (high_time<=100)         high_time=(unsigned char)(rout/800);
  297.                         else        high_time=100;
  298.                         low_time= (100-high_time);
  299.                 }
  300.         }
  301.         else if(set_temper<=temper)         //设置温度不大于当前温度
  302.         {
  303.                 ledred=1;
  304.                 ledgreen=0;
  305.                 if(temper-set_temper>0) //温度相差0度以上
  306.                 {
  307.                         high_time=0;
  308.                         low_time=100;
  309.                 }
  310.                 else
  311.                 {
  312.                         for(i=0;i<10;i++)
  313.                         {
  314.                                 get_temper();
  315.                                 rin = s; // Read Input
  316.                                 rout = PIDCalc ( &spid,rin ); // Perform PID Interation
  317.                         }
  318.                         if (high_time<100) high_time=(unsigned char)(rout/10000);
  319.                         else         high_time=0;
  320.                         low_time= (100-high_time);
  321.                 }
  322.         }
  323. }
  324. /*****************************************************
  325. T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期
  326. ******************************************************/
  327. void serve_T0() interrupt 1 using 1
  328. {
  329.         if(++count<=(high_time))         output=0;
  330.         else if(count<=100)
  331.         {
  332.                 output=1;
  333.         }
  334.         else count=0;
  335.         TH0=0x2f;
  336.         TL0=0x40;
  337. }


  338. /***********主函数**********/
  339. void main(void)
  340. {
  341.         unsigned char i;
  342.         init();//LCD初始化
  343.         TMOD=0x01;
  344.         TH0=0x2f;
  345.         TL0=0x40;
  346.         EA=1;
  347.         ET0=1;
  348.         TR0=1;
  349.         high_time=50;
  350.         low_time=50;
  351.         PIDInit ( &spid ); // Initialize Structure
  352.         spid.Proportion= 10; // Set PID Coefficients
  353.         spid.Integral = 8;
  354.         spid.Derivative =6;
  355.         spid.SetPoint =100; // Set PID Setpoint
  356.         set[0]=set_temper/10;
  357.         set[1]=set_temper%10;
  358.         wr_com(0x80+0x40+9);        //显示设置温度
  359.         wr_data(table[set[0]]);
  360.    delay(1);
  361.         wr_com(0x80+0x40+10);
  362. ……………………

  363. …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码

所有资料51hei提供下载:
1DS18B20 控温 源程序.rar (95.34 KB, 下载次数: 568)
仿真.rar (24.19 KB, 下载次数: 450)

评分

参与人数 3黑币 +85 收起 理由
liwenyi666 + 30 楼主能加个QQ私聊一下吗
dearluca + 5 回帖助人的奖励!
admin + 50 共享资料的黑币奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏21 分享淘帖 顶1 踩
回复

使用道具 举报

26#
ID:336378 发表于 2022-3-30 15:11 | 只看该作者
                        for(i=0;i<10;i++)
                        {
                                s=get_temper();//取得现在的温度。
                                rin = s; // Read Input
                                rout = PIDCalc ( &spid,rin ); // Perform PID Interation
                        }
回复

使用道具 举报

25#
ID:739402 发表于 2020-5-5 11:08 | 只看该作者
楼主能QQ私聊一下吗?救助,感激不尽
回复

使用道具 举报

24#
ID:732724 发表于 2020-5-5 10:56 来自手机 | 只看该作者
landing 发表于 2020-5-4 11:14
谢谢,刚好要写一个恒温PID控制,可以参考一下。

能否加个QQ传我 感激不尽
回复

使用道具 举报

23#
ID:732724 发表于 2020-5-5 10:55 来自手机 | 只看该作者
landing 发表于 2020-5-4 11:14
谢谢,刚好要写一个恒温PID控制,可以参考一下。

请问你能打开吗?我打不开,显示丢失什么文件
回复

使用道具 举报

22#
ID:739026 发表于 2020-5-4 11:14 | 只看该作者
谢谢,刚好要写一个恒温PID控制,可以参考一下。
回复

使用道具 举报

21#
ID:491642 发表于 2019-5-11 15:49 | 只看该作者
谢谢楼主分享
回复

使用道具 举报

20#
ID:509991 发表于 2019-4-16 21:17 | 只看该作者
楼主你这个是通过什么加热装置加热的呀
回复

使用道具 举报

19#
ID:507638 发表于 2019-4-13 19:45 | 只看该作者
楼主,没有降温处理吗?
回复

使用道具 举报

18#
ID:507638 发表于 2019-4-10 20:23 | 只看该作者
有没有解析呢??
回复

使用道具 举报

17#
ID:507638 发表于 2019-4-9 11:04 | 只看该作者
yzh123 发表于 2018-4-24 20:41
楼主你好,你的pid参数是怎么整定的?

要求精度不高的话通常设置都是一样的
回复

使用道具 举报

16#
ID:507638 发表于 2019-4-9 11:03 | 只看该作者
楼主能私聊下吗
回复

使用道具 举报

15#
ID:507638 发表于 2019-4-9 10:50 | 只看该作者
zxr961129 发表于 2018-6-7 21:13
感谢楼主,最近在做毕业设计,需要模糊pid方面的程序和仿真,很高兴楼主能够分享出来!

能互相交流下吗
回复

使用道具 举报

14#
ID:507638 发表于 2019-4-9 10:46 | 只看该作者
楼主能加QQ聊吗
回复

使用道具 举报

13#
ID:489404 发表于 2019-3-12 18:22 | 只看该作者
楼主你好,你的pid参数是怎么整定的?
回复

使用道具 举报

12#
ID:450056 发表于 2018-12-20 23:49 来自手机 | 只看该作者
谢谢,楼主给力
回复

使用道具 举报

11#
ID:245429 发表于 2018-12-20 20:58 | 只看该作者
学习了,下载下来以后试试
回复

使用道具 举报

10#
ID:407060 发表于 2018-10-9 16:05 | 只看该作者
求救楼主 仿真的电路板在哪里,我打不开
回复

使用道具 举报

9#
ID:280466 发表于 2018-9-1 19:52 | 只看该作者
可以吗???
回复

使用道具 举报

8#
ID:340241 发表于 2018-6-14 02:00 | 只看该作者
楼主流弊
回复

使用道具 举报

7#
ID:304629 发表于 2018-6-7 21:13 | 只看该作者
感谢楼主,最近在做毕业设计,需要模糊pid方面的程序和仿真,很高兴楼主能够分享出来!
回复

使用道具 举报

6#
ID:323102 发表于 2018-6-7 12:49 | 只看该作者
实际测量温度不显示谁帮帮我加qq2304146938
回复

使用道具 举报

5#
ID:323102 发表于 2018-6-7 12:48 | 只看该作者
实际测量温度不显示谁帮帮我加qq2304146938
回复

使用道具 举报

地板
ID:323102 发表于 2018-6-7 11:17 | 只看该作者
温度显示不出来怎么回事
回复

使用道具 举报

板凳
ID:311660 发表于 2018-4-25 19:37 | 只看该作者
楼主我想对你的程序提几个问题。在你的温度比较处理子程序里面有一段程序是这样的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);
  请问一下rin=s是不是表示的是PID的回馈输入,那么既然如此的话rin不应该是等于输出的温度吗?为什么是s呢?我看了一下程序除了一开始的定义unsigned int s;和刚刚那个地方s就没有别的用处了。
      else         //设置温度不大于当前温度     这一条语句的注释也不是很明白。  这个else是在if(set_temper-temper>1)         //温度相差1度以上      这条语句后面,那么我感觉他的注释应该是     温度相差小于一度
      最后的话是这一句  if (high_time<=100)         high_time=(unsigned char)(rout/800);
                        else        high_time=100;
                        low_time= (100-high_time);整个都不太明白 ,特别是(rout/800)
希望楼主有空的话可以赐教一下,谢谢!

评分

参与人数 1黑币 +80 收起 理由
admin + 80 赞一个!

查看全部评分

回复

使用道具 举报

沙发
ID:311660 发表于 2018-4-25 10:18 | 只看该作者
谢谢楼主,最近刚好在做这方面的东西
回复

使用道具 举报

楼主
ID:314339 发表于 2018-4-24 20:41 来自手机 | 只看该作者
楼主你好,你的pid参数是怎么整定的?
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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