STC15实现温控闭环控制,数码管显示温度,按键调节温度,PID控制
1.控温系统的PWM控制方案
① 加热元件和驱动电路设计
② 控温的工作原理及加热元件的数学模型,特别是模型参数的确定
③ PCA、PWM波形产生方法
ⅰ) 寄存器配置及编程
ⅱ) 8位PWM与16位PWM产生方法
2.温度采样原理与实现方法
① 温度传感器工作原理与输出关系
② 放大电路设计
③ A/D转换程序设计
3.温度控制闭环系统结构图与数学模型建立,以及与MCU的关系(参考下图设计出本系统结构)
① PID数字控制与编程方法
② PID参数整定
4.温度设定与显示
① LED数码管显示的电路设计及相关计算
② 数码管控制方案与温度显示的编程方法
③ 键盘设置的电路设计与编程方法
#include "STC15.H"
#include "intrins.h"
#define display P2
typedef unsigned char uchar;
typedef unsigned int uint;
sbit Led_CS1 = P0^0; //位定义
sbit Led_CS2 = P0^1;
sbit Led_CS3 = P0^2;
sbit Led_CS4 = P0^3;
sbit Key_Tup = P0^4;
sbit Key_Tdown = P0^5;
sbit Key_Confirm = P0^6;
//数码管数值码表 共阳极接法
uchar code TABLE[] = {0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8,0x80, 0x90};
char SetT = 40; //设定温度初始化为 40 摄氏度
float RealT = 25; //实际温度初始化为 25 摄氏度
float Error = 0; //当前误差
float ErrorInt = 0; //累积误差 设定温度太大可能溢出
float Kp = 25,Ki = 0.5e-3; //PID参数
float u; //控制量
float pwmduty; //PWM波占空比
//uint ADCRESValue; //ADC10位采样值 调试实际温度数码管闪烁时用
void IO_Init(); //IO端口初始化
void AD_Init(); //AD模块初始化
void PWM_Init(); //PWM模块初始化
void SetTemperature(); //设定温度
void KeyScan(); //按键扫描
void SetTdisp(); //设定温度显示
void RealTdisp(); //实际温度显示
void getRealT(); //测温
void PIDControl(); //PID调节
void PWMOutput(); //PWM/DA输出
//void ADCRESshow(); //AD采样后,寄存器值显示 调试实际温度数码管闪烁时用
void delay1ms();
void delay5ms();
void main()
{
IO_Init(); //与数码管段选、片选以及按键有关的Io端口初始化
AD_Init(); //AD模块初始化
PWM_Init(); //PWM模块初始化
while(1)
{
SetTemperature(); //设定温度
getRealT(); //测温
PIDControl(); //PID调节
PWMOutput(); //PWM/DA输出
SetTdisp(); //设定温度显示
RealTdisp(); //实际温度显示
// ADCRESshow(); //AD采样后,寄存器值显示 调试实际温度数码管闪烁时用
}
return;
}
void IO_Init() //与数码管段选、片选以及按键有关的Io端口初始化
{
P0M1 = 0xF0; // P0.0~P0.3 初始化为输出;P0.4~P0.7初始化为输入
P0M0 = 0x0F;
P2M1 = 0x00; // P2口初始化为输出
P2M0 = 0xFF;
return;
}
void AD_Init() //AD模块初始化
{
P1ASF = 0x01; //设定 P1.0 作为AD使用
ADC_CONTR = 0x60; //关闭ADC电源;90个时钟周期转换1次
//结束标志位、启动控制位清零;输入通道选择 P1.0
CLK_DIV &= 0xDF; //ADRJ:ADC转换结果存放位置调整
EA = 1; //CPU开放中断;禁止AD转换中断
EADC = 0;
PADC = 1; //AD转换中断为最高优先级
return;
}
void PWM_Init() //PWM模块初始化
{
P1M1 &= 0xFD; //P1.1设为输出
P1M0 |= 0x02;
CMOD = 0x80; //空闲模式下计数器停止计数;时钟源输入选择 SYSCLK/12
//禁止计数器溢出中断
CCON = 0x00; //溢出标志位CF清零;运动控制位清零;
//PCA模块0、1、2中断标志清零
CCAPM0 = 0x42; //PCA模块0工作于PWM模式;禁止CCF0中断
PCA_PWM0 = 0x00; //8位PWM功能;EPC0H = EPC0L = 0
P_SW1 &= 0xCF; //CCP切换为P1
CH = 0x00; //PCA装载值清零
CL = 0x00;
CCAP0H = 0xFF; //捕获寄存器值,初始化占空比为 1/256
CCAP0L = 0xFF;
CCON |= 0x40; //启动PWM输出
return;
}
void SetTemperature() //设定温度
{
if( !(Key_Tup & Key_Tdown) ) //检测 升温/降温 按键是否按下
{ //进入设定温度过程
uchar i,j;
do
{
KeyScan(); //按键扫描
getRealT(); //测温
for(i=0;i<2;i++) //设定温度闪烁显示,用来指示设定温度过程
{ //实际温度常量
if( i==0 ) //设定温度显示
for(j=0;j<20;j++)
{
SetTdisp();
RealTdisp();
}
if( i==1 ) //设定温度不显示
for(j=0;j<20;j++)
{
delay5ms();
delay5ms();
RealTdisp();
}
}
}while(Key_Confirm); //直到确认键按下,退出设定温度过程
}
return;
}
void KeyScan() //按键扫描
{
static uchar flag = 0; //设置标志位
if( !(Key_Tup & Key_Tdown) ) //检测 升温/降温 按键是否按下
{
if(!flag) //标志位为0时才进行 升温/降温,产生延时效果
{ //防止按键按下过程中 升温/降温 多次,同时不占用CPU
if(!Key_Tup) //升温
{
SetT++;
if(SetT > 99)
SetT = 0;
}
if(!Key_Tdown) //降温
{
SetT--;
if(SetT < 0)
SetT = 0;
}
}
flag++; //标志位加一
if(flag == 15); //标志位加到15清零
flag = 0;
}
else //无按键按下,标志位清零
flag = 0;
return;
}
void SetTdisp() //设定温度显示
{
Led_CS1 = 0;
Led_CS2 = 1;
Led_CS3 = 1;
Led_CS4 = 1;
display = TABLE[SetT/10];
delay5ms();
Led_CS1 = 1;
Led_CS2 = 0;
Led_CS3 = 1;
Led_CS4 = 1;
display = TABLE[SetT%10];
delay5ms();
Led_CS1 = 1;
Led_CS2 = 1;
Led_CS3 = 1;
Led_CS4 = 1;
return;
}
void RealTdisp() //实际温度显示
{
uchar T = (uchar) RealT;
Led_CS1 = 1;
Led_CS2 = 1;
Led_CS3 = 0;
Led_CS4 = 1;
display = TABLE[T/10];
delay5ms();
Led_CS1 = 1;
Led_CS2 = 1;
Led_CS3 = 1;
Led_CS4 = 0;
display = TABLE[T%10];
delay5ms();
Led_CS1 = 1;
Led_CS2 = 1;
Led_CS3 = 1;
Led_CS4 = 1;
return;
}
void getRealT() //测温
{
/*
uint AD_Data = 0;
uchar Temp = 0;
ADC_CONTR |= 0x80;
delay1ms();
ADC_CONTR |= 0x08;
while( !(ADC_CONTR & 0x10) );
ADC_CONTR &= 0x67;
// AD_Data = ADC_RES; //(1)无滤波
// AD_Data <<= 2;
// Temp = ADC_RESL;
// Temp &= 0x03;
// AD_Data |= Temp;
ADC_RESL &= 0x03; //(2)无滤波
RealT = (uchar)((ADC_RES*4+ADC_RESL)/1023.0*100); //出现bug,以后注意!!!
*/
uint temp = 0; //(3)均值滤波 效果最好
uchar i;
ADC_CONTR |= 0x80; //开电源
delay1ms(); //延时1ms
for(i = 0;i < 20;i++) //采样20次
{
ADC_CONTR |= 0x08; //开始采样
while( !(ADC_CONTR & 0x10) ); //查询转换是否完成
ADC_CONTR &= 0xE7; //停止转换,清标志位
ADC_RESL &= 0x03; //计算10位采样值
temp += ADC_RES*4+ADC_RESL;
}
ADC_CONTR &= 0x7F; //关电源
// ADCRESValue = temp/20; //计算20次10位采样值的平均值
//调试实际温度数码管闪烁时用
RealT = temp/20.0/1023.0*100; //计算20次平均温度
/*
uint SampleValue[31] = {0},temp; //(4)中值滤波 不太稳定
uchar i,j,k;
ADC_CONTR |= 0x80; //开电源
delay1ms(); //延时1ms
ADC_CONTR |= 0x08; //采第一个值
while( !(ADC_CONTR & 0x10) );
ADC_CONTR &= 0xE7;
ADC_RESL &= 0x03;
SampleValue[0] = ADC_RES*4+ADC_RESL;
for(i = 1;i < 31;i++) //再采20个(共21个) 同时将21个值排序
{
ADC_CONTR |= 0x08;
while( !(ADC_CONTR & 0x10) );
ADC_CONTR &= 0xE7;
ADC_RESL &= 0x03;
temp = ADC_RES*4+ADC_RESL;
for(j = 0;j < i;j++)
{
if(temp < SampleValue[j])
{
for(k = i;k > j;k--)
SampleValue[k] = SampleValue[k-1];
SampleValue[j] = temp;
break;
}
}
if(j == i)
{
SampleValue[j] = temp;
}
}
ADC_CONTR &= 0x7F; //关电源
// ADCRESValue = SampleValue[15]; //取中值 调试实际温度数码管闪烁时用
RealT = (uchar)( SampleValue[15]/1023.0*100 ); //计算实际温度
*/
return;
}
void PIDControl() //PID调节
{
Error = (float)SetT - RealT; //注意数据类型!!!
ErrorInt += Error;
u = Kp*Error + Ki*ErrorInt;
return;
}
void PWMOutput() //PWM/DA输出
{
pwmduty = u/255; //计算占空比
if(pwmduty >0.9) //占空比限幅
pwmduty = 0.9;
if(pwmduty < 0.1)
pwmduty = 0.1;
CCAP0H = (uchar)(256*(1-pwmduty)); //占空比调节
return;
}
void delay1ms()
{
unsigned char a,b;
for(b=4;b>0;b--)
for(a=248;a>0;a--);
_nop_(); //if Keil,require use intrins.h
}
void delay5ms()
{
unsigned char a,b;
for(b=19;b>0;b--)
for(a=130;a>0;a--);
}
//void ADCRESshow() //AD采样后,寄存器值显示
//{ //调试实际温度数码管闪烁时用
//
ADCRESValue = ADC_RES*4+ADC_RESL; //不使用滤波算法时使用
ADCRESValue = (uint)ErrorInt; //可以观测误差变化
ADCRESValue = (uint)ErrorInt; //可以观测误差累积的变化
//
// Led_CS1 = 0;
// Led_CS2 = 1;
// Led_CS3 = 1;
// Led_CS4 = 1;
// display = TABLE[ADCRESValue/1000];
// ADCRESValue -= (ADCRESValue/1000)*1000;
// delay5ms();
//
// Led_CS1 = 1;
// Led_CS2 = 0;
// Led_CS3 = 1;
// Led_CS4 = 1;
// display = TABLE[ADCRESValue/100];
// ADCRESValue -= (ADCRESValue/100)*100;
// delay5ms();
//
//
// Led_CS1 = 1;
// Led_CS2 = 1;
// Led_CS3 = 0;
// Led_CS4 = 1;
// display = TABLE[ADCRESValue/10];
// delay5ms();
//
// Led_CS1 = 1;
// Led_CS2 = 1;
// Led_CS3 = 1;
// Led_CS4 = 0;
// display = TABLE[ADCRESValue%10];
// delay5ms();
//
// Led_CS1 = 1;
// Led_CS2 = 1;
// Led_CS3 = 1;
// Led_CS4 = 1;
//
// return;
//}