|
【功能设计】
使用IAP15W4K58S4单片机、数码管和NTC3435热敏电阻、DC12V直流加热棒(带驱动板)组成一个自动加热系统。可以手动设定目标温度(30℃~90℃),设置步进1摄氏度。
请使用者仅用于参考学习,不要用于商业。
【使用流程描述】
1.上电程序会轮流显示设定温度和实际温度。配有指示灯表示当前所显示的是哪一个。
2.任意时刻按下增加或减少按键,程序进入设置模式,显示设定值并保持。后台加热仍然以原设置值为准。
3.当按下确定键且设定温度在有效范围内时,程序装载新设置值并进行自动调整,否则恢复原值。按下取消键也恢复原值。
4.降温操作通过被动散热进行,所以会比较慢。
5.后续需求有所变化,增加一个串口输出,传递设置值和实际值给远端单片机显示。
【主机参数设置】
0.MCU型号IAP15W4K58S4,频率11.0592MHz,下载程序时注意勾选“在程序区的结束处添加重要测试参数”。
1.ADC使用片内ADC,10位精度,使用内部基准源校正采集值。
2.PID部分借用他人程序,针对此设计做了调整
。参考程序来源已不可考,故不贴出。
3.NTC电阻采用B值3435防水型号,其查表程序为本人所写,详情请查阅对应C文件(移植很方便)。
4.串口设置9600 8N1。
【从机参数设置】
1.型号STC15W408AS,频率11.0592MHz。
2.从机使用IIC转并口的转接板连接LCD1602,极大地节约了IO口的使用。
这个小板原本为在Arduino上使用而设计的,但是既然遵守IIC协议,并且只是PCF8574芯片通信,那么51也可以才对。经过查找,在百度贴吧上找到了使用其驱动LCD1602的程序,并且重新排版编写使之清晰明了,工程中的LCD1602_IIC.c即为此转接板的驱动程序。
【实物快照】
显示部分会随着主机的显示进行更新
白天照的不清晰,数码管由于直连IO推挽方式驱动所以有些段选有残影。单片机PWM和加热部分之间使用MOS管驱动,供电12V。
【主机主程序】
- #include"STC15.H" //单片机寄存器定义
- #include"Binary.H" //提供二进制输入
- #include<intrins.h> //提供_nop_函数
- #include"NTC3435.c" //提供NTC查询表
- sbit Key_Add=P1^5;//设定值+
- sbit Key_Subtract=P1^6;//设定值-
- sbit Key_Submit=P1^7;//确定修改
- sbit Key_Cancel=P5^5;//取消修改
- sbit LED_Red=P3^6;//用于指示当前显示的温度为设定值
- sbit LED_Green=P3^7;//用于指示当前显示的温度为测得值
- sbit S1=P4^5;
- sbit S2=P4^2;
- sbit S3=P4^4;//数码管位选
- sbit PWM_FC=P1^0;//PWM输出引脚
- //初始化变量
- unsigned int code Voltage_BandGap_ROM _at_ 0xe7f7;//58K程序空间的MCU
- //全局变量表
- unsigned char smgduan[18]={0xfa,0x22,0xb9,0xab,0x63,0xcb,0xdb,0xa2,0xfb,0xeb,0xf3,0x5b,0x19,0x3b,0xd9,0xd1,0x01};
- unsigned char Display_Temperature_Act[3]={0,0,0};//温度测得值显示数组
- unsigned char Display_Temperature_Set[3]={0,0,0};//温度设定值显示数组
- int Temperature_Act=32; //温度测得值
- int Temperature_Set=40; //温度设定值
- int Shadow_Temperature_Set=40; //温度设定值影子值
- unsigned int T0_Cnt_1=0; //定时器0周期计数1:显示轮询
- unsigned int T0_Cnt_2=0; //定时器0周期计数2:PID控制
- unsigned char T0_Cnt_3=0; //定时器0周期计数3:串口发送
- bit En_Display_Standby=1; //显示轮询有效标志位
- bit Do_T0_3=0; //串口发送标志
- bit flag_display_mode=0; //显示标志位
- bit Temperature_Set_Choose=0;//温度设置值数据来源选择,用于PID设置,该值为0时数据参考来自设置变量,该值为1时数据参考来源来自影子变量
- bit flag_Key_Add=0; //按键动作执行标志位
- bit flag_Key_Subtract=0; //按键动作执行标志位
- bit flag_Key_Submit=0; //按键动作执行标志位
- bit flag_Key_Cancel=0; //按键动作执行标志位
- bit busy=0;//串口1发送状态
- //ADC测温相关变量
- unsigned int ADC_DATA; //读取到的ADC转换值
- unsigned int ADC_BandGap; //读取的BandGap转换值
- float VCC_Voltage; //计算得到的VCC电压值,单位mV
- float NTC_Voltage; //计算得到的NTC分压电压值,单位mV
- float NTC_Temperature; //查表计算得到的NTC对应温度值,单位℃
- unsigned long NTC_R_Comp=0; //待对比的电阻值,单位十倍欧姆
- bit flag_value_update=0; //测得值更新标志位
- bit En_ADC_Value=0; //ADC转换值标志 0:无效 1:有效
- //移植PID部分时的全局变量
- int e ,e1 ,e2 ;//pid
- float uk ,uk1 ,duk ;//pid
- float Kp=10,Ki=12,Kd=1.5;//pid 10,12,1.5
- int out=0;
- unsigned int PWMTime=0;
- unsigned int cnt=0;
- /************************************************************
- 名称:基于NTC3435的水温自动加热系统设计
- 平台:IAP15W4K58S4 11.0592MHz 9600 8N1
- 编写注意:数码管连接在P2口,单片机推挽输出方式直驱
- 程序编写:凌净清河
- 硬件制作:凌净欣羽
- 文稿排版:凌净欣羽
- 发布日期:2019年8月7日(星期三)
- 声明:如您直接使用本例程或进行较大程度的借鉴,请注明程序出处,谢谢!
- 所属:新矿城学习基地#2019
- ************************************************************/
- void PWMOUT()
- {
- if(cnt<PWMTime)
- {
- PWM_FC=1;
- }
- else
- {
- PWM_FC=0;
- }
- if(cnt>1000)cnt=0;
- }
- void PIDControl(void)
- {
- if(Temperature_Set_Choose)
- {
- e=Shadow_Temperature_Set-NTC_Temperature;
- }
- else
- {
- e=Temperature_Set-NTC_Temperature;
- }
- duk=(Kp*(e-e1)+Ki*e+Kd*(e-2*e1+e2));
- uk=uk1+duk;
- if(uk>1200)
- {
- uk=1000;
- }
- else if(uk<-1200)
- {
- uk=0;
- }
-
- out=(int)uk;
- if(out>1000)
- {
- out=1000;
- }
- else if(out<0)
- {
- out=0;
- }
- PWMTime=out;
- uk1=uk;
- e2=e1;
- e1=e;
- }
- void delay(unsigned int i)
- {
- while(i--);
- }
- void SendData(unsigned char dat)//串口1发送一个字节数据
- {
- while(busy);
- busy=1;
- SBUF=dat;
- }
- void Value_Send()//串口将数据发送出去,仅发送显示的部分
- {
- SendData(0xC7);
- delay(100);
- SendData(Display_Temperature_Act[0]);
- delay(100);
- SendData(Display_Temperature_Act[1]);
- delay(100);
- SendData(Display_Temperature_Act[2]);
- delay(100);
- SendData(Display_Temperature_Set[0]);
- delay(100);
- SendData(Display_Temperature_Set[1]);
- delay(100);
- SendData(Display_Temperature_Set[2]);
- delay(100);
- SendData(0x7C);
- }
- void VCC_Voltage_Read() //调用此函数更新电源电压值,存储在VCC_Voltage变量中,该函数改变ADC的设置
- {
- P1ASF=B00000000; //不设置P1ASF,以便上电读取BandGap电压的ADC转换值
- ADC_CONTR=B10000000; //开启ADC电源,设置转换速度540时钟周期,清空转换标志位,停止转换,模拟通道选择P1^0
- ADC_RES=0; //清除结果寄存器
- ADC_RESL=0; //清除结果寄存器
- CLK_DIV&=B11011111; //ADC_RES[7:0]存放高8位ADC结果,ADC_RES[1:0]存放低2位ADC结果
- //PADC=1; //设置A/D转换中断优先级为最高
-
- ADC_CONTR|=0x08; //开始转换
- _nop_();
- _nop_();
- _nop_();
- _nop_(); //按照例程延时4个周期
- while(!En_ADC_Value); //等待转换结束
- En_ADC_Value=0; //清除标志位
- ADC_BandGap=ADC_DATA; //保存BandGap的ADC转换值
- VCC_Voltage=Voltage_BandGap_ROM*1023.0/(float)ADC_BandGap;//电源电压值计算
- }
- void NTC_Voltage_Read() //调用此函数更新NTC电压值,存储在NTC_Voltage变量中,该函数改变ADC的设置
- {
- P1ASF=B00001000; //NTC接在P13
- ADC_CONTR=B11100011; //开启ADC电源,设置转换速度90时钟周期,清空转换标志位,停止转换,模拟通道选择P1^3
- ADC_RES=0; //清除结果寄存器
- ADC_RESL=0; //清除结果寄存器
- CLK_DIV&=B11011111; //ADC_RES[7:0]存放高8位ADC结果,ADC_RES[1:0]存放低2位ADC结果
- //PADC=1; //设置A/D转换中断优先级为最高
-
- ADC_CONTR|=0x08; //开始转换
- _nop_();
- _nop_();
- _nop_();
- _nop_(); //按照例程延时4个周期
- while(!En_ADC_Value); //等待转换结束
- En_ADC_Value=0; //清除标志位
- NTC_Voltage=Voltage_BandGap_ROM*(float)ADC_DATA/(float)ADC_BandGap;//NTC分压电压计算
- }
- /*
- 数值显示处理函数,将温度变量的值判断合法范围并按位取出送给显示数组
- 温度变量值的合理更新由其更新函数负责
- 为提高效率,该函数只在变量更新时执行一次,不随数码管显示函数轮询刷新
- */
- void Data_Process()
- {
- if(Temperature_Act>0&&Temperature_Act<100)
- {
- Display_Temperature_Act[0]=Temperature_Act/10;
- Display_Temperature_Act[1]=Temperature_Act%10;
- Display_Temperature_Act[2]=12;
- }
- else
- {
- if(Temperature_Act<0)
- {
- Temperature_Act*=(-1);//将值变为正数
- Display_Temperature_Act[0]=17;//显示负号
- Display_Temperature_Act[1]=Temperature_Act/10;
- Display_Temperature_Act[2]=Temperature_Act%10;
- }
- else//数值大于100℃
- {
- Display_Temperature_Act[0]=Temperature_Act/100;
- Display_Temperature_Act[1]=(Temperature_Act%100)/10;
- Display_Temperature_Act[2]=Temperature_Act%10;
- }
- }
- //由于设置值由设置函数保证合法性,此处不用判断,直接按位取出即可
- Display_Temperature_Set[0]=Temperature_Set/10;
- Display_Temperature_Set[1]=Temperature_Set%10;//温度设定值在30~90之间
- Display_Temperature_Set[2]=12;
-
- // Display_Temperature_Set[0]=PWMTime/100;
- // Display_Temperature_Set[1]=(PWMTime%100)/10;
- // Display_Temperature_Set[2]=PWMTime%10;
- // if(PWMTime==0)Display_Temperature_Set[2]=15;
- }
- /*
- 数码管显示函数,用于刷新显示设定的和实际的温度值
- 同时刷新LED的指示显示
- 参数为布尔量,0:显示设定值 1:显示测得值
- */
- void smg_display(bit choose)
- {
- unsigned char i;
- for(i=0;i<3;i++)
- {
- switch(i)
- {
- case 0:S1=0;S2=1;S3=1;break;
- case 1:S1=1;S2=0;S3=1;break;
- case 2:S1=1;S2=1;S3=0;break;
- }
- if(choose)
- {//测得值
- P2=smgduan[Display_Temperature_Act[i]];
- }
- else
- {//设定值
- P2=smgduan[Display_Temperature_Set[i]];
- }
- delay(100);
- P2=0x00;
- }
-
- if(choose)//在不同模式下点亮相应的指示灯
- {
- LED_Red=1;LED_Green=0;
- }
- else
- {
- LED_Red=0;LED_Green=1;
- }
- }
- void Timer0Init(void) //5毫秒@11.0592MHz
- {
- AUXR |= 0x80; //定时器时钟1T模式
- TMOD &= 0xF0; //设置定时器模式
- TL0 = 0x00; //设置定时初值
- TH0 = 0x28; //设置定时初值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- }
- void Timer1Init(void) //10微秒@11.0592MHz
- {
- AUXR |= 0x40; //定时器时钟1T模式
- TMOD &= 0x0F; //设置定时器模式
- TL1 = 0x91; //设置定时初值
- TH1 = 0xFF; //设置定时初值
- TF1 = 0; //清除TF1标志
- TR1 = 1; //定时器1开始计时
- }
- void UartInit(void) //9600bps@11.0592MHz
- {
- SCON = 0x50; //8???,?????
- AUXR |= 0x01; //??1?????2???????
- AUXR |= 0x04; //???2???Fosc,?1T
- T2L = 0xE0; //??????
- T2H = 0xFE; //??????
- AUXR |= 0x10; //?????2
- }
- void sys_init()
- {
- P0M1=0x00;
- P0M0=0x00;
- P1M1=0x00;
- P1M0=0x00;
- P2M1=0x00;
- P2M0=0xFF;//P2口推挽输出
- P3M1=0x00;
- P3M0=0x00;
- P4M1=0x00;
- P4M0=0x00;
- P5M0=0x00;
- P5M1=0x00;
- P2=0x00;
- LED_Red=0;
- LED_Green=0;
- Timer0Init();
- Timer1Init();
- UartInit();
- EA=1;
- ET0=1;
- ET1=1;
- ES=1;//开串口中断使能
- EADC=1;//开ADC转换中断
- VCC_Voltage_Read();//读取电源电压
- Data_Process();
- LED_Red=1;
- LED_Green=1;
- }
- void main()
- {
- sys_init();
- while(1)
- {
- smg_display(flag_display_mode);
- if(flag_value_update)
- {
- flag_value_update=0;
- NTC_Voltage_Read();//读取NTC的分压值(P13上的模拟电压)
- NTC_R_Comp=100000*NTC_Voltage/(VCC_Voltage-NTC_Voltage);//计算待对比的NTC阻值,单位十倍欧姆
- NTC_Temperature=NTC_103_3435_Compare(NTC_R_Comp);//查表计算NTC对应的温度值
- Temperature_Act=(char)NTC_Temperature;//取整
- Data_Process();
- }
- //***************************************************************************
- //此处添加PID调温的函数
- //Temperature_Set_Choose为温度设置值数据来源选择标志位,用于PID设置。
- //该值为0时温度设置值来自变量Temperature_Set,该值为1时设置值来自影子变量Shadow_Temperature_Set
- //温度的设置值为int型数据,范围为30-90
- //实时温度可以来自以下两个变量:
- //Temperature_Act为int型变量,存储显示的测得值
- //NTC_Temperature为float型变量,存储每次转换完成后的查表得到的温度值
- PWMOUT();
- //***************************************************************************
- if(Do_T0_3)
- {
- Do_T0_3=0;
- Value_Send();//串口发送显示的值给另一个单片机(设计要求?
-
- }
- if((Key_Add==0&&(!flag_Key_Add)))
- {
- delay(3000);
- if(Key_Add==0)
- {
- flag_Key_Add=1;
- //按键执行内容
- if(Temperature_Set_Choose==0)//当首次进入设置模式时
- {
- En_Display_Standby=0;//关闭中断中的显示轮询
- Shadow_Temperature_Set=Temperature_Set;//备份之前的设置值
- Temperature_Set_Choose=1;//切换PID数据来源
- flag_display_mode=0;//切换到设定值显示
- }
- Temperature_Set++;//增加设置值
- if(Temperature_Set>90)Temperature_Set=90;//设置上限
- Data_Process();//更新显示
- }
- }
- if((Key_Subtract==0&&(!flag_Key_Subtract)))
- {
- delay(3000);
- if(Key_Subtract==0)
- {
- flag_Key_Subtract=1;
- //按键执行内容
- if(Temperature_Set_Choose==0)//当首次进入设置模式时
- {
- En_Display_Standby=0;//关闭中断中的显示轮询
- Shadow_Temperature_Set=Temperature_Set;//备份之前的设置值
- Temperature_Set_Choose=1;//切换PID数据来源
- flag_display_mode=0;//切换到设定值显示
- }
- Temperature_Set--;//减少设置值
- if(Temperature_Set<30)Temperature_Set=30;//设置下限
- Data_Process();//更新显示
- }
- }
- if((Key_Submit==0&&(!flag_Key_Submit)))
- {
- delay(3000);
- if(Key_Submit==0)
- {
- flag_Key_Submit=1;
- //按键执行内容
- if(Temperature_Set_Choose)//确定键在设置模式时有效
- {
- En_Display_Standby=1;//开启中断中的显示轮询
- Temperature_Set_Choose=0;//切换PID数据来源
- }
- }
- }
- if((Key_Cancel==0&&(!flag_Key_Cancel)))
- {
- delay(3000);
- if(Key_Cancel==0)
- {
- flag_Key_Cancel=1;
- //按键执行内容
- if(Temperature_Set_Choose)//取消键在设置模式时有效
- {
- Temperature_Set=Shadow_Temperature_Set;//还原之前的设置值
- En_Display_Standby=1;//开启中断中的显示轮询
- Temperature_Set_Choose=0;//切换PID数据来源
- }
- }
- }
-
- if(Key_Add)flag_Key_Add=0;
- if(Key_Subtract)flag_Key_Subtract=0;
- if(Key_Submit)flag_Key_Submit=0;
- if(Key_Cancel)flag_Key_Cancel=0;
- }
- }
- void T0() interrupt 1
- {
- if(T0_Cnt_1==400)
- {
- T0_Cnt_1=0;
- if(En_Display_Standby)
- {
- flag_display_mode=!flag_display_mode;
- }
- flag_value_update=1;
- }
- else
- {
- T0_Cnt_1++;
- }
-
- if(T0_Cnt_2==10)
- {
- T0_Cnt_2=0;
- PIDControl();
- // PWMTime=PWMTime+100;
- // if(PWMTime>1000)
- // {
- // PWMTime=0;
- // }
- }
- else
- {
- T0_Cnt_2++;
- }
-
- if(T0_Cnt_3==100)
- {
- T0_Cnt_3=0;
- Do_T0_3=1;
- }
- else
- {
- T0_Cnt_3++;
- }
- }
- void T1() interrupt 3
- {
- cnt++;
- }
- void UART() interrupt 4
- {
- if(RI)
- {
- RI=0;
- }
- if(TI)
- {
- TI=0;//清除标志位
- busy=0;//上一数据发送完成
- }
- }
- void ADC() interrupt 5 //AD中断服务函数
- {
- ADC_CONTR&=!0x10; //清除ADC中断标志,例程
- ADC_DATA=(ADC_RES<<2)|ADC_RESL; //10位ADC结果拼接
- En_ADC_Value=1; //ADC转换值有效标志
- }
-
复制代码
【程序下载】
基于NTC3435的水温自动加热系统设计 凌净清河.zip
(103.38 KB, 下载次数: 221)
|
|