📄 wendu.c
字号:
/**************************************************************
* 文件名: Wendu.c
* 功能描述: 实现闭环温度控制,将给定值与测量值在数码管上显示
**************************************************************/
#include <reg51.h>
#include <absacc.h>
#include <math.h>
void pid(void);
void init(void);
void Display(void);
void clear();
int mmul(int x, int y); /*16位乘法,溢出赋极值*/
int madd(int x, int y); /*16位加法,溢出赋极值*/
int change32_16(int z, int t); /*将32位数转化成16位*/
char change16_8(int wd); /*将16位数转化成8位*/
#define C8255_A XBYTE[0x7F00]
#define C8255_B XBYTE[0x7F01]
#define C8255_C XBYTE[0x7F02]
#define C8255_CON XBYTE[0x7F03]
#define AD0809 XBYTE[0xFEFF]
sbit P17 = P1^7;
char TS = 0x64;
int X = 0x80;
char SPEC = 0x28; // 数字给定(40)
char IBAND = 0x60; // 积分分离值
int KP = 12; // 比例系数
char KI = 20; // 积分系数
char KD = 32; // 微分系数
int CK; // 控制量:PID算法产生用于控制的量
int TC; // 采样周期变量
char FPWM; // PWM脉冲中间标志位
int CK_1; // 控制量变量:记录上次控制量值
int AAAA; // PWM脉冲高电平时间计算
int VAA; // AAAA变量
int BBB; // PWM脉冲低电平时间计算
int VBB; // BBB变量
int TKMARK; // 采样标志值
int ADMARK; // AD转换结束标志位
int ADVALUE; // AD采样值保存
int YK; // 反馈:测量温度值
int EK;
int EK_1;
int AEK;
int BEK;
unsigned char DIS;
// BCD码显示表
unsigned char Led[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7F,0x6f};
unsigned char b[] = {0x00, 0x00, 0x00, 0x00}; // 位选
// 温度表
unsigned char code a[0x100]={0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
0x1D, 0x1E, 0x1E, 0x1F, 0x20, 0x21, 0x23, 0x24, 0x25,
0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
0x2F, 0x31, 0x32, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40,
0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x4F, 0x50, 0x51,
0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63,
0x64, 0x64, 0x65, 0x65, 0x66, 0x66, 0x67, 0x68, 0x69,
0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6E, 0x6F, 0x6F, 0x70,
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82,
0x83, 0x84, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
0x8B, 0x8C, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94,
0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9B, 0x9C,
0x9C, 0x9D, 0x9D, 0x9E, 0x9E, 0x9F, 0x9F, 0xA0, 0xA1,
0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA,
0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB0, 0xB1, 0xB2,
0xB3, 0xB4, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA,
0xBB, 0xBD, 0xBE, 0xBE, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5,
0xC6, 0xC8, 0xCA, 0xCC, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2,
0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC,
0xDD, 0xDE, 0xE3, 0xE6, 0xE9, 0xEC, 0xF0, 0xF2, 0xF6,
0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF };
void delay(unsigned int time)
{
unsigned int i;
for(i=0; i<time; i++);
}
void main()
{
init();
while (1)
{
while (1)
{
if (TKMARK == 0x01) //采样周期到否
break;
}
TKMARK = 0x00;
while (1)
{
if (ADMARK == 0x01) //AD转换是否结束
break;
}
ADMARK = 0x00;
YK = a[ADVALUE]; //查温度表
DIS = (char)YK;
pid(); //调PID运算函数
if( CK <= 0x80) //根据CK产生PWM
AAAA = 0x00;
else
AAAA = CK - 0x80;
BBB = 0x7f - AAAA;
}
}
void init(void) //初始化函数
{
YK = 0x00; //变量初始化
EK = 0x00;
EK_1 = 0x00;
AEK = 0x00;
BEK = 0x00;
CK = 0x00;
CK_1 = 0x00;
BBB = 0x00;
VBB = 0x00;
ADVALUE = 0x00;
TKMARK = 0x00;
ADMARK = 0x00;
TC = 0x00;
FPWM = 0x01;
AAAA = 0x7F;
VAA = 0x7F;
C8255_CON = 0x81;
Display();
clear();
TMOD = 0x11; // T1, T0 16位定时器
IP = 0x02; // pt0 定时器0中断为高优先级
IT1 = 1; // 外中断1为下降沿有效
EX1 = 1; // 允许INT1中断
TH0 = 0xD8;
TL0 = 0xEF;
TH1 = 0xD8;
TL1 = 0xEF;
ET0 = ET1 = 1; //使能定时器中断
TR0 = TR1 = 1; //启动定时器
EA = 1;
AD0809 = 1; // 启动AD
}
void myint3(void) interrupt 3 // 定时器1,LED显示
{
TH1 = 0xD8;
TL1 = 0xEF;
ET1 = 1;
Display();
clear();
}
void myint1(void) interrupt 2 // 外中断1,读AD转换结果
{
ADVALUE = AD0809;
ADMARK = 0x01;
}
void myint2(void) interrupt 1 // 定时器0,启动AD转换
{
TH0 = 0xD8;
TL0 = 0xEF;
ET0 = 1;
AD0809 = 1;
if (TC < TS)
TC++;
else
{
TKMARK = 0x01;
TC = 0x00;
}
if (FPWM == 0x01) //产生PWM
{
if (VAA != 0x00)
{
VAA = VAA - 1;
P17 = 0; // P10输出为低, 加热
}
else
{
FPWM = 0x02;
VBB = BBB / 2;
}
}
if (FPWM == 0x02)
{
if (VBB != 0x00)
{
VBB = VBB - 1;
P17 = 1; // P10输出为高, 停止加热
}
else
{
FPWM = 0x01;
VAA = AAAA / 2;
}
}
return;
}
void pid(void)
{
int K,P,I,D;
K=P=I=D=0;
EK=SPEC-YK; //得到偏差
BEK=EK-EK_1-AEK; //△2EK
AEK=EK-EK_1; //△EK 偏差变化量
if (abs(EK)>abs(IBAND)) I=0; //判积分分离
else I=(EK*TS)/KI; //计算积分项
P=AEK;
D=((KD/TS)*BEK)/10000; //计算微分项
//△UK=KP*△EK + KI*Ek + KD*(△EK-△EK_1) UK=△UK+UK_1
K=madd(I,P);
K=madd(D,K);
K=mmul(K,KP);
K=K/10;
CK=K+CK_1;
//将UK值转化成8位数据,取K的低8位值并取符号
CK=change16_8(CK);
CK_1=CK;
EK_1=EK;
CK=CK+X;
}
int mmul( int x,int y)
{
int t,z;
long s;
s=x*y;
z=(int)(s&0x0FFFF);
t=(int)((s>>16)&0x0FFFF);
s=change32_16(z,t);
return(s);
}
int change32_16(int z,int t) //t=高字,z=低字
{
int s;
if(t==0)
{
if((z&0x8000)==0) s=z;
else s=0x7fff;
}
else if ((t&0xffff)==0xffff)
{
if((z&0x8000)==0) s=0x8000;
else s=z;
}
else if ((t&0x8000)==0) s=0x7fff;
else s=0x8000;
return(s);
}
int madd(int x,int y)
{
int t;
t=x+y;
if(x>=0 && y>=0) //同号相乘,符号位变反说明溢出
{ if((t&0x8000)!=0) t=0x7fff; }
else if (x<=0 && y<=0)
{ if((t&0x8000)==0) t=0x8000; }
return(t);
}
char change16_8(int wd) //t=高字节,z=低字节
{
char z,t,s;
z=wd&0x0FF;
t=(wd>>8)&0x0FF;
if(t==0x00)
{
if((z&0x80)==0) s=z;
else s=0x7f;
}
else if ((t&0xff)==0xff)
{
if((z&0x80)==0) s=0x80;
else s=z;
}
else if((t&0x80)==0) s=0x7f;
else s=0x80;
return(s);
}
void Display() //数码管显示函数
{
unsigned char i, j = 0xF7;
b[3] = SPEC/10;
b[2] = SPEC%10;
b[1] = DIS/10;
b[0] = DIS%10;
for(i=0; i<4; i++)
{
C8255_A = j;
C8255_B = Led[b[i]];
delay(0x55);
j >>= 1;
}
}
void clear()
{
C8255_B = 0x00;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -