找回密码
 立即注册

QQ登录

只需一步,快速开始

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

stm32单片机PID温控系统源程序 18b20,硬件验证通过

  [复制链接]
跳转到指定楼层
楼主
引脚说明:
//定义MCU与LCD的接口
/*******************************
*D4---->PB8
*D5---->PB5
*D6---->PB6
*D7---->PB7
*RS---->PA8
*RW---->PA11
*EN---->PA12

LCD1602
VCC---->5V
GND---->GND
VO----->接对比度调节滑动变阻器- -->GND
LED+ -->5v
LED- -->GND

PWM输出(12703半导体制冷片)
4A电机pwm驱动芯片
*制冷IN1---->PB10
*加热IN2---->PB11

18B20数字温度传感器
18B20OUT---->PB9
按键

KEY1---->PB12
KEY1---->PB13
KEY1---->PB14

AT24C16
1(A0)---->GND
2(A1)---->GND
3(A2)---->GND
4(GND)---->GND
4(SDA)---->PC13---->4.7K(R)---->VCC
6(SCL)---->PC14---->4.7K(R)---->VCC
7(WP)---->GND---->104(C)---->VCC
8(VCC)---->VCC

功能说明:通过18B20检测温度,通过pwm输出来调节控温,未进行精准pid调试,恒温时间大约在5min左右。
2021.8.25 :基本程序编写完成。
2021.8.26:修改程序,既可以制热也可以制冷。
2021.8.27:加入24c16用于存储设定温度和模式,优化lcd显示。

制作出来的实物图如下:


单片机源程序如下:
  1. #include "incrementpid.h"
  2. #include "usart.h"
  3. //增量式PID
  4. PID pid;
  5. u8 T_mode=1;//制冷(1)制热(0)模式切换
  6. void PIDParament_Init()  //
  7. {
  8.     pid.choose_model = MODEL_PID;//调节模式
  9.     pid.T=500;                //采样周期,定时器使用1ms,则最小执行PID的周期为500ms
  10.     pid.set =25;            //用户设定值,摄氏度
  11.     pid.Kp=0.6;                //比例系数
  12.     pid.Ti=100.0;                //微分系数常数ms
  13.     pid.Td=20.0;                //积分时间常数ms
  14.     pid.OUT0=0;                //一个维持的输出
  15.     pid.pwmcycle = 1999;    //PWM的周期
  16. }

  17.    
  18. void pid_calc()  //增量式PID调节函数
  19. {
  20.   float dk1;float dk2;
  21.   float t1,t2,t3;
  22.    
  23.     if(pid.Tdata < (pid.T))  //最小计算周期未到
  24.      {
  25.             return ;
  26.      }
  27.     pid.Tdata = 0;
  28.    
  29.     pid.En=pid.set-pid.curr;  //本次误差
  30.     dk1=pid.En-pid.En_1;   //本次偏差与上次偏差之差
  31.     dk2=pid.En-2*pid.En_1+pid.En_2;
  32.    
  33.    t1=pid.Kp*dk1;                            //比例
  34.    
  35.     t2=(pid.Kp*pid.T)/pid.Ti;      //积分
  36.     t2=t2*pid.En;
  37.    
  38.     t3=(pid.Kp*pid.Td)/pid.T;        //微分
  39.     t3=t3*dk2;
  40.    
  41.     switch(pid.choose_model)
  42.      {
  43.          case MODEL_P:     pid.Dout= t1;           printf("使用P运算\r\n") ;
  44.              break;
  45.          
  46.          case MODEL_PI:  pid.Dout= t1+t2;          printf("使用PI运算\r\n") ;
  47.              break;
  48.                  
  49.          case MODEL_PID: pid.Dout= t1+t2+t3;       printf("使用PID运算\r\n") ;
  50.              break;
  51.      }
  52.          
  53.    
  54.      
  55.   
  56. if(T_mode==0)//制热PID调节
  57. {        
  58.         pid.currpwm+=pid.Dout;  //本次应该输出的PWM
  59.     printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ;
  60.          
  61.         /*判断算出的数是否符合控制要求*/
  62.     if(pid.currpwm>pid.pwmcycle)            //算出的值取值,肯定是在0-pid.pwmcycle之间,不然的话PWM怎么输出
  63.     {
  64.       pid.currpwm=pid.pwmcycle;
  65.     }
  66.     if(pid.currpwm<0)
  67.     {
  68.      pid.currpwm=0;
  69.     }
  70.         TIM_SetCompare4(TIM2,pid.currpwm);        //pwm输出函数
  71.         TIM_SetCompare3(TIM2,0);        //pwm输出函数
  72.     printf("实际输出使用的OUT:\t%d\r\n",(int)pid.currpwm) ;
  73. }
  74. else if(T_mode==1)//制冷PID调节
  75. {          
  76.           pid.currpwm+=pid.Dout;  //本次应该输出的PWM
  77.          
  78.       printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ;
  79.          
  80.          
  81.     if(-pid.currpwm>pid.pwmcycle)//算出的值取值,肯定是在0-pid.pwmcycle之间,不然的话PWM怎么输出
  82.     {
  83.         pid.currpwm=-pid.pwmcycle; //pwm最大宽度       
  84.      
  85.     }
  86.     if(pid.currpwm>0)//当前的pwm宽度
  87.     {
  88.         pid.currpwm=0;       
  89.      
  90.     }
  91.         TIM_SetCompare3(TIM2,-pid.currpwm);        //pwm输出函数
  92.     TIM_SetCompare4(TIM2,0);
  93.     printf("实际输出使用的OUT:\t%d\r\n",(int)pid.currpwm) ;
  94. }
  95.        
  96.     pid.En_2=pid.En_1;
  97.     pid.En_1=pid.En;
  98. }
复制代码

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <math.h>

  5. #include "sys.h"
  6. #include "delay.h"
  7. #include "usart.h"
  8. #include "ds18b20.h"
  9. #include "LCD1602.h"
  10. #include "pwmout.h"
  11. #include "key.h"
  12. #include "timer3.h"
  13. #include "incrementpid.h"
  14. #include "24cxx.h"

  15. //float TM1;//存储温度值
  16. //s8 set_temp=30;//设定温度

  17. uint8_t ucDs18b20Id[8];//存储18b20数据
  18. u8 i;//循环读取18b20ID次数
  19. u8 a,b,c=0;//lcd循环显示
  20. u16 pwmval23=0,pwmval24=0;//占空比调节
  21. u8 cle_pwm1,cle_pwm2;//清除pwm显示
  22. int main(void)
  23. {               
  24. delay_init();                     //延时函数初始化          
  25. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
  26. uart_init(9600);         //串口初始化为9600
  27. LCD_init();//lcd1206初始化       
  28. PWM_Out_Init();        // 定时器输出pwm初始化       
  29. KEY_Input_Init();//GPIO IN初始化       
  30. TIM3_Init(999,71); ////TIM2 1ms中断       
  31. PIDParament_Init() ;//增量式PID初始化       
  32. AT24CXX_Init();//初始化IIC接口       
  33.        
  34.        
  35. TIM_SetCompare3(TIM2,0);        //pwm输出函数
  36. TIM_SetCompare4(TIM2,0);        //pwm输出函数               
  37.        
  38. LCD_set_xy(1,2);//写位置       
  39. LCD_write_data('T');        //写数据
  40. LCD_write_data('E');        //写数据
  41. LCD_write_data('M');        //写数据
  42. LCD_write_data('P');        //写数据
  43. LCD_write_data(':');        //写数据
  44. LCD_set_xy(1,1);//写位置       
  45. LCD_write_data('P');        //写数据
  46. LCD_write_data('W');        //写数据
  47. LCD_write_data('M');        //写数据
  48. LCD_write_data(':');        //写数据               

  49. while( DS18B20_Init() )        //DS18B20初始化
  50. {       
  51. printf("no ds18b20 exit\r\n");//初始化DS18B20,不初始化就等待这里?
  52. delay_ms(500);       
  53. break;               
  54. }

  55. DS18B20_ReadId(ucDs18b20Id); //读取18b20的ID
  56. //LCD_write_cmd(0x0f);//写命令       

  57.          for(i = 0;i < 8;i ++)  
  58.     {  
  59.      printf("DS18B20[%d] ID:%d \r\n ",i,ucDs18b20Id[i]); //读取18b20的ID      
  60.     }  
  61.        
  62. while(AT24CXX_Check())//检测不到24c02
  63.         {
  64.                 printf("检测不到24cXX\r\n") ;
  65.                 delay_ms(1000);
  66.                 break;       
  67.         }
  68.        
  69. pid.set=AT24CXX_ReadOneByte_minus(0);//设定温度数据读取,在AT24CXX指定地址读出一个数据
  70. T_mode=AT24CXX_ReadOneByte(4);//模式数据读取,在AT24CXX指定地址读出一个数据
  71.        
  72.         while(1)
  73.                 {
  74. /************************温度设定*****************************************/
  75.                        
  76.                 if( KEY_Scan(0,KEY1)){pid.set++;AT24CXX_WriteOneByte_minus(0,pid.set);/*在AT24CXX指定地址写入一个数据*/}
  77.                 if( pid.set>125)pid.set=125;       
  78.                        
  79.                 if( KEY_Scan(0,KEY2)){T_mode=!T_mode;AT24CXX_WriteOneByte(4,T_mode);/*在AT24CXX指定地址写入一个数据*/}//制冷制热模式切换
  80.                        
  81.                 if( KEY_Scan(0,KEY3)){pid.set--;AT24CXX_WriteOneByte_minus(0,pid.set);/*在AT24CXX指定地址写入一个数据*/}
  82.                 if( pid.set<-55)pid.set=-55;       
  83. /********************pwm输出**********************************************/       
  84.                        
  85.         pid_calc() ;//增量式PID调节函数
  86.                        
  87. /*********************制冷制热模式显示****************************/               
  88.                 LCD_set_xy(16,1);//写位置
  89.                 if(T_mode==1)//制冷           
  90.                         {
  91.                         LCD_write_data('L');        //写数据       
  92.                         cle_pwm1=1;
  93.                                
  94.                         }                       
  95.                 else//制热
  96.                    {
  97.                         LCD_write_data('H');        //写数据
  98.                         cle_pwm2=1;                  
  99.                    }       
  100.                   
  101.                  if(cle_pwm1&&cle_pwm2)
  102.                  {
  103.                         LCD_set_xy(5,1);//写位置
  104.                         LCD_write_data(' ');        //写数据       
  105.                         LCD_write_data('0');        //写数据       
  106.                         LCD_write_data('0');        //写数据       
  107.                         LCD_write_data('0');        //写数据       
  108.                         LCD_write_data('0');        //写数据
  109.                         cle_pwm1=0;       
  110.                         cle_pwm2=0;                         
  111.                  }
  112. /*********************pwm输出显示在lcd1602上****************************/
  113.                
  114.                 //if(pid.currpwm<0)//制冷
  115.             if(T_mode==1)//制冷
  116.                 {       
  117.                 pwmval23=-pid.currpwm;
  118.                 memset(Data_Partition,0x30,sizeof(Data_Partition));//初始化数组               
  119.                 conversion(pwmval23);//将数据拆分方便写入lcd1602               
  120.                 LCD_set_xy(5,1);//写位置
  121.                 LCD_write_data('-');        //写数据       
  122.                 LCD_write_data(Data_Partition[3]);        //写数据       
  123.                 LCD_write_data(Data_Partition[2]);        //写数据       
  124.                 LCD_write_data(Data_Partition[1]);        //写数据       
  125.                 LCD_write_data(Data_Partition[0]);        //写数据
  126.                                
  127.                 }
  128.                
  129.                 //else if(pid.currpwm>0)//制热
  130.                 else if(T_mode==0)//制冷
  131.                 {                       
  132.                 pwmval24=pid.currpwm;       
  133.                 memset(Data_Partition,0x30,sizeof(Data_Partition));//初始化数组0x30 LCD的字符0       
  134.                 conversion(pwmval24);//将数据拆分方便写入lcd1602
  135.                 LCD_set_xy(5,1);//写位置
  136.                 LCD_write_data('+');        //写数据       
  137.                 LCD_write_data(Data_Partition[3]);        //写数据       
  138.                 LCD_write_data(Data_Partition[2]);        //写数据       
  139.                 LCD_write_data(Data_Partition[1]);        //写数据       
  140.                 LCD_write_data(Data_Partition[0]);        //写数据
  141.                
  142.                 }
  143.                                                        
  144. /*****************设定温度显示在lcd1602上********************************/       
  145.                        
  146.                         if(pid.set>=0)        //lcd1602显示设定温度                       
  147.                   {
  148.                    b=Data_Line(pid.set);//获取显示数据位数       
  149.                    conversion(pid.set);//将数据拆分方便写入lcd1602          
  150.                           
  151.                    LCD_set_xy(13,2);//写位置  
  152.                    LCD_write_data(' ');        //清除“-”        号                 
  153.                   
  154.                         LCD_set_xy(14,2);//写位置                       
  155.                         for(a=0;a<b+1;a++)        //清空显示数据
  156.                         {
  157.                         LCD_write_data(' ');        //写数据       
  158.                         }
  159.                        
  160.                         LCD_set_xy(14,2);//写位置                       
  161.                         for(a=0;a<b;a++)        //整数部分显示,不限长度
  162.                         {
  163.                         LCD_write_data(Data_Partition[b-a-1]);        //写数据                                
  164.                         }
  165.                   }
  166.                   
  167. /****************************************************************/                          
  168.                   else if(pid.set<0)        //lcd1602显示设定温度                       
  169.                   {
  170.                    b=Data_Line(-pid.set);//获取显示数据位数       
  171.                    conversion(-pid.set);//将数据拆分方便写入lcd1602  
  172.                           
  173.            LCD_set_xy(13,2);//写位置  
  174.                    LCD_write_data('-');        //显示“-” 号   
  175.                          
  176.                         LCD_set_xy(14,2);//写位置                       
  177.                         for(a=0;a<b+1;a++)        //清空显示数据
  178.                     {
  179.                         LCD_write_data(' ');        //写数据       
  180.             }  
  181.                        
  182.                         LCD_set_xy(14,2);//写位置       
  183.                         for(a=0;a<b;a++)        //整数部分显示,不限长度
  184.                     {
  185.                         LCD_write_data(Data_Partition[b-a-1]);        //写数据                                
  186.             }
  187.                        
  188.                   }
  189.                   
  190. /**************************温度显示在lcd1602*****************************************/                  
  191.                  

  192.                  pid.curr=DS18B20_GetTemp_MatchRom ( ucDs18b20Id ); //读取18b20温度数据         
  193.                  if(pid.curr>=0)        //lcd1602显示温度                       
  194.                 {
  195.                
  196.                 b=Data_Line(pid.curr*100);//获取显示数据位数       
  197.                 conversion(pid.curr*100);//将数据拆分方便写入lcd1602       

  198. //                        LCD_set_xy(6,2);//写位置       
  199. //                        for(a=0;a<b+2;a++)        //清空显示数据
  200. //                    {
  201. //                        LCD_write_data(' ');        //写数据       
  202. //                        }
  203.                        
  204.                         LCD_set_xy(6,2);//写位置       
  205.                         for(a=0;a<b-2;a++)        //整数部分显示,不限长度
  206.                     {
  207.                         LCD_write_data(Data_Partition[b-a-1]);        //写数据                
  208.             }
  209.                         if(pid.curr<1)       
  210.                         {
  211.                         LCD_write_data('0');        //写数据                       
  212.                         }
  213.                         LCD_write_data('.');        //写数据                       
  214.                        
  215.             for(a=2;a>0;a--)        //小数部分显示
  216.                     {
  217.                         LCD_write_data(Data_Partition[a-1]);        //写数据                
  218.             }       
  219.                 }
  220. /******************************************************************************/               
  221.                 else if(pid.curr<0)        //lcd1602显示温度
  222.                 {
  223. ……………………

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

代码工程51hei附件下载:
PID控制-STM32F103C8T6 控温.7z (210.66 KB, 下载次数: 203)

评分

参与人数 1黑币 +80 收起 理由
admin + 80 共享资料的黑币奖励!

查看全部评分

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

使用道具 举报

沙发
ID:144730 发表于 2021-8-28 11:16 | 只看该作者
按钮作用:从左到右,温度-,模式切换(制冷/制热),温度+
回复

使用道具 举报

板凳
ID:144730 发表于 2021-8-28 11:18 | 只看该作者
图片未完全显示,详细请点击图片
回复

使用道具 举报

地板
ID:144730 发表于 2021-8-30 08:36 | 只看该作者
18b20输出引脚接4.7k电阻后接vcc
回复

使用道具 举报

5#
ID:339860 发表于 2023-5-31 15:10 | 只看该作者
刚好学到,下载看看
回复

使用道具 举报

6#
ID:1096092 发表于 2023-10-16 12:10 | 只看该作者
2479408246 发表于 2021-8-30 08:36
18b20输出引脚接4.7k电阻后接vcc

兄弟你还在吗,想请教请教你
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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