001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
|
#include
typedef
unsigned
char
uChar8;
typedef
unsigned
int
uInt16;
typedef
unsigned
long
int
uInt32;
sbit ConOut = P1^1;
//加热丝接到P1.1口
typedef
struct
PID_Value
{
uInt32 liEkVal[3];
//差值保存,给定和反馈的差值
uChar8 uEkFlag[3];
//符号,1则对应的为负数,0为对应的为正数
uChar8 uKP_Coe;
//比例系数
uChar8 uKI_Coe;
//积分常数
uChar8 uKD_Coe;
//微分常数
uInt16 iPriVal;
//上一时刻值
uInt16 iSetVal;
//设定值
uInt16 iCurVal;
//实际值
}PID_ValueStr;
PID_ValueStr PID;
//定义一个结构体,这个结构体用来存算法中要用到的各种数据
bit g_bPIDRunFlag = 0;
//PID运行标志位,PID算法不是一直在运算。而是每隔一定时间,算一次。
/* ********************************************************
/* 函数名称:PID_Operation()
/* 函数功能:PID运算
/* 入口参数:无(隐形输入,系数、设定值等)
/* 出口参数:无(隐形输出,U(k))
/* 函数说明:U(k)+KP*[E(k)-E(k-1)]+KI*E(k)+KD*[E(k)-2E(k-1)+E(k-2)]
******************************************************** */
void
PID_Operation(
void
)
{
uInt32 Temp[3] = {0};
//中间临时变量
uInt32 PostSum = 0;
//正数和
uInt32 NegSum = 0;
//负数和
if
(PID.iSetVal > PID.iCurVal)
//设定值大于实际值否?
{
if
(PID.iSetVal - PID.iCurVal > 10)
//偏差大于10否?
PID.iPriVal = 100;
//偏差大于10为上限幅值输出(全速加热)
else
//否则慢慢来
{
Temp[0] = PID.iSetVal - PID.iCurVal;
//偏差<=10,计算E(k)
PID.uEkFlag[1] = 0;
//E(k)为正数,因为设定值大于实际值
/* 数值进行移位,注意顺序,否则会覆盖掉前面的数值 */
PID.liEkVal[2] = PID.liEkVal[1];
PID.liEkVal[1] = PID.liEkVal[0];
PID.liEkVal[0] = Temp[0];
/* =================================================================== */
if
(PID.liEkVal[0] > PID.liEkVal[1])
//E(k)>E(k-1)否?
{
Temp[0] = PID.liEkVal[0] - PID.liEkVal[1];
//E(k)>E(k-1)
PID.uEkFlag[0] = 0;
//E(k)-E(k-1)为正数
}
else
{
Temp[0] = PID.liEkVal[1] - PID.liEkVal[0];
//E(k)
PID.uEkFlag[0] = 1;
//E(k)-E(k-1)为负数
}
/* =================================================================== */
Temp[2] = PID.liEkVal[1] * 2;
//2E(k-1)
if
((PID.liEkVal[0] + PID.liEkVal[2]) > Temp[2])
//E(k-2)+E(k)>2E(k-1)否?
{
Temp[2] = (PID.liEkVal[0] + PID.liEkVal[2]) - Temp[2];
PID.uEkFlag[2]=0;
//E(k-2)+E(k)-2E(k-1)为正数
}
else
//E(k-2)+E(k)<2E(k-1)
{
Temp[2] = Temp[2] - (PID.liEkVal[0] + PID.liEkVal[2]);
PID.uEkFlag[2] = 1;
//E(k-2)+E(k)-2E(k-1)为负数
}
/* =================================================================== */
Temp[0] = (uInt32)PID.uKP_Coe * Temp[0];
//KP*[E(k)-E(k-1)]
Temp[1] = (uInt32)PID.uKI_Coe * PID.liEkVal[0];
//KI*E(k)
Temp[2] = (uInt32)PID.uKD_Coe * Temp[2];
//KD*[E(k-2)+E(k)-2E(k-1)]
/* 以下部分代码是讲所有的正数项叠加,负数项叠加 */
/* ========= 计算KP*[E(k)-E(k-1)]的值 ========= */
if
(PID.uEkFlag[0] == 0)
PostSum += Temp[0];
//正数和
else
NegSum += Temp[0];
//负数和
/* ========= 计算KI*E(k)的值 ========= */
if
(PID.uEkFlag[1] == 0)
PostSum += Temp[1];
//正数和
else
;
/* 空操作。就是因为PID.iSetVal > PID.iCurVal(即E(K)>0)才进入if的,
那么就没可能为负,所以打个转回去就是了 */
/* ========= 计算KD*[E(k-2)+E(k)-2E(k-1)]的值 ========= */
if
(PID.uEkFlag[2]==0)
PostSum += Temp[2];
//正数和
else
NegSum += Temp[2];
//负数和
/* ========= 计算U(k) ========= */
PostSum += (uInt32)PID.iPriVal;
if
(PostSum > NegSum)
//是否控制量为正数
{
Temp[0] = PostSum - NegSum;
if
(Temp[0] < 100 )
//小于上限幅值则为计算值输出
PID.iPriVal = (uInt16)Temp[0];
else
PID.iPriVal = 100;
//否则为上限幅值输出
}
else
//控制量输出为负数,则输出0(下限幅值输出)
PID.iPriVal = 0;
}
}
else
PID.iPriVal = 0;
//同上,嘿嘿
}
/* ********************************************************
/* 函数名称:PID_Output()
/* 函数功能:PID输出控制
/* 入口参数:无(隐形输入,U(k))
/* 出口参数:无(控制端)
******************************************************** */
void
PID_Output(
void
)
{
static
uInt16 iTemp;
static
uChar8 uCounter;
iTemp = PID.iPriVal;
if
(iTemp == 0)
ConOut = 1;
//不加热
else
ConOut = 0;
//加热
if
(g_bPIDRunFlag)
//定时中断为100ms(0.1S),加热周期10S(100份*0.1S)
{
g_bPIDRunFlag = 0;
if
(iTemp) iTemp--;
//只有iTemp>0,才有必要减“1”
uCounter++;
if
(100 == uCounter)
{
PID_Operation();
//每过0.1*100S调用一次PID运算。
uCounter = 0;
}
}
}
/* ********************************************************
/* 函数名称:PID_Output()
/* 函数功能:PID输出控制
/* 入口参数:无(隐形输入,U(k))
/* 出口参数:无(控制端)
******************************************************** */
void
Timer0Init(
void
)
{
TMOD |= 0x01;
// 设置定时器0工作在模式1下
TH0 = 0xDC;
TL0 = 0x00;
// 赋初始值
TR0 = 1;
// 开定时器0
EA = 1;
// 开总中断
ET0 = 1;
// 开定时器中断
}
void
main(
void
)
{
Timer0Init();
while
(1)
{
PID_Output();
}
}
void
Timer0_ISR(
void
) interrupt 1
{
static
uInt16 uiCounter = 0;
TH0 = 0xDC;
TL0 = 0x00;
uiCounter++;
if
(100 == uiCounter)
{
g_bPIDRunFlag = 1;
}
}
|