📄 pidcode.txt
字号:
PID算法汇集(C语言)
/*main.c*/
/*DA模块两路(OUT1、OUT2)输出三角波经AD10次采样并与之比较经数字滤波、双闭环PID调节后PWM生成以及经逆变后电压控制*/
#include "global.c"
void SystemInit();
void SPI_Init();
void DA_OUT(unsigned CHANNEL,unsigned int RNG,unsigned int SPI_DATA);
void AD_Sample();
void Que();
void ADCINT();
void PWM_Init();
void KickDog();
unsigned int i,j,numled0,AD_0,AD_8,AD_FLAG=0,AD0[10],AD8[10],Voltage=0,RESULT_0,RESULT_8,t0=0,t1=0,t2=625,t3=625,t0_back=0,PB6_FLAG=0;
unsigned int T1;
float U_KP=10,U_KI=0.1,U_KD=0.8,U_REF=310,U_EK,U_EK1,U_EK2,U_RK,U_RK1,U_U,U_ELPI,
I_KP=10,I_KI=0.1,I_REF,I_EK,I_EK1,I_EK2,I_RK,I_RK1,I_U,U_MAX=80,U_MIN=20,I_MIN=200,I_MAX=1000;
float AD_SIG0=0.0,AD_SIG8=0.0;
main()
{
SystemInit();
MCRA=MCRA & 0xC0FF; //IOPB0-6设为IO口模式
PBDATDIR=0xFFC2; //所有LED=0
PBDATDIR=PBDATDIR |0x003D; //所有LED=1
SPI_Init();
PWM_Init(); //定时器、EVA初始化
asm(" CLRC INTM ");
while(1)
{
if(AD_FLAG==1)
{
AD_FLAG=0;
for(i=0;i<10;i++)
{
AD_Sample();
AD0[i]=RESULT_0;
AD8[i]=RESULT_8;
}
Que(); //数字滤波(去极值平均滤波)
ADCINT(); //PIADJUSTS()
}
}
}
void SystemInit()
{
asm(" SETC INTM "); /* 关闭总中断 */
asm(" CLRC SXM "); /* 禁止符号位扩展 */
asm(" CLRC CNF "); /* B0块映射为 on-chip DARAM*/
asm(" CLRC OVM "); /* 累加器结果正常溢出*/
SCSR1=0x83FE; /* 系统时钟CLKOUT=20*2=40M */
/* 打开ADC,EVA,EVB,CAN和SCI的时钟*/
WDCR=0x006F; /* 禁止看门狗,看门狗时钟64分频 */
KickDog(); /* 初始化看门狗 */
IFR=0xFFFF; /* 清除中断标志 */
IMR=0x0003; /* 打开中断1、2*/
}
void SPI_Init()
{
MCRB=MCRB | 0x0014; //SPISIMO,SPICLK特殊功能方式
PBDATDIR=PBDATDIR | 0x0002; //CS_DA=1
SPICCR=0x004a; //11bit数据
SPICTL=0x0006; //禁止中断
SPIBRR=0x0027; //1M波特率,40M/40=1M
SPICCR=SPICCR | 0x80;
}
void DA_OUT(unsigned CHANNEL,unsigned int RNG,unsigned int SPI_DATA)
{
unsigned char flag=0;
SPITXBUF=(CHANNEL<<14)|(RNG<<13)|(SPI_DATA<<5) ; //bit10,9~CHANNEL;bit8~RNG倍数
while(1)
{
flag=SPISTS&0x40;
if(flag==0x40) break;
}
SPIRXBUF=SPIRXBUF;/*虚读寄存器以清除中断标志*/
PBDATDIR=PBDATDIR & 0xFFFD; //CS_DA=0,更新模拟信号输出
for(i=0;i<5;i++); //延时
PBDATDIR=PBDATDIR | 0x0002; //CS_DA=1,锁存数据
}
void AD_Sample()
{
ADCTRL1=0x4000; /* ADC模块复位 */
asm(" NOP ");
ADCTRL1=0x0020; /* 自由运行,启动/停止模式,双排序器工作模式 */
MAXCONV=0x0000;
CHSELSEQ1=0x0000; //第0通道
ADCTRL2=0x4000; //复位使排序器指针指向CONV00
ADCTRL2=0x2000; /* 启动ADC转换 */
while( (ADCTRL2&0x1000)==0x1000); /*等待转换完成 */
asm(" NOP ");
asm(" NOP ");
RESULT_0=RESULT0>>6;
MAXCONV=0x0000; //第8通道
CHSELSEQ3=0x0008;
ADCTRL2=0x0040;
ADCTRL2=0x0020;
while( (ADCTRL2&0x0010)==0x0010);
asm(" NOP ");
asm(" NOP ");
RESULT_8=RESULT8>>6;
}
void Que()
{
unsigned int MaxAD0=0;
unsigned int MinAD0=AD0[0];
unsigned int MaxAD8=0;
unsigned int MinAD8=AD8[0];
unsigned int tempAD0=0;
unsigned int tempAD8=0;
for(j=0;j<10;j++)
{
if(AD0[j]>MaxAD0) MaxAD0=AD0[j];
else if(AD0[j]<MinAD0) MinAD0=AD0[j];
if(AD8[j]>MaxAD8) MaxAD8=AD8[j];
else if(AD8[j]<MinAD8) MinAD8=AD8[j];
}
for(j=0;j<10;j++)
{
tempAD0=tempAD0+AD0[j];
tempAD8=tempAD8+AD8[j];
}
AD_0=(tempAD0-MaxAD0-MinAD0)/8;
AD_8=(tempAD8-MaxAD8-MinAD8)/8;
AD_SIG0=AD_0*4.983/1023; //1023~3.3*(10+5.1)/10=4.983V,对应满量程
AD_SIG8=AD_8*4.983/1023; //电路中采用的是5.1k和10k的电阻分压
asm(" NOP "); //请将光标移到此处设置断点,并用debug->Animate监测数据
}
// AD中断服务子程序
void ADCINT()
{
asm(" clrc SXM"); // 抑制符号位扩展
U_RK1=U_RK;
U_EK=AD0[i]-U_REF;
U_U=U_RK1+U_KP*(U_EK-U_EK1)+U_KI*U_EK+U_KD*(U_EK-2*U_EK1+U_EK2);
if(U_U<=1000) I_REF=0;
else if(U_U>1000) {I_REF=I_MAX;
U_EK2=U_EK1;U_EK1=U_EK;}
I_RK1=I_RK;
I_EK=AD8[i]-I_REF;
I_U=I_RK1+I_KP*(I_EK-I_EK1)+I_KI*I_EK;
T1=t2+t3;
if(I_U<=625) t3=625; // 输出更新脉冲截止时间t3
else if(I_U>=1875)t3=1875;
else t3=t3+50;
asm(" clrc INTM");
}
void PWM_Init()
{
MCRA=MCRA | 0x00C0;
EVAIMRA=0x0080; // 定时器1周期中断使能
EVAIFRA=0xFFFF; // 清除中断标志
GPTCONA=0x0100; // 定时器1周期中断标志启动AD转换
T1CNT=0;
T1CON=0x0C4E; //增/减模式, TPS系数40M/16=2.5M,T1使能
ACTRA=0x0006;
DBTCONA=0x0530; //使能死区定时器1,分频40M/16=2.5M,死区时间5*0.4us=2us
T1PR=T1;
CMPR1=t2; //占空比为0-100%
COMCONA=0xA600;
}
void c_int2() /*定时器1中断服务程序*/
{
if(PIVR!=0x27)
{ asm(" CLRC INTM ");
return;
}
T1CNT=0;
t1++;
numled0--;
if(numled0==0)
{
numled0=200;
if((PBDATDIR & 0x0001)==0x0001)
PBDATDIR=PBDATDIR & 0xFFFE; //IOPB0=0;LED灭
else
PBDATDIR=PBDATDIR |0x0101; //IOPB0=1;LED亮
}
if(Voltage<0) Voltage=0;
DA_OUT(0,0,Voltage); //Voltage范围0-255对应0-3.3V
DA_OUT(1,0,Voltage); //Voltage范围0-255对应0-3.3V
DA_OUT(2,0,192); //2.475V,Voltage范围0-255对应0-3.3V
DA_OUT(3,0,128); //1.65V,Voltage范围0-255对应0-3.3V
if(t0<255) Voltage++;
else Voltage--;
if(Voltage<0) Voltage=0;
if(t0==508) t0=0;
t0++;
if((AD_FLAG==0)&((t1%100)==0)) //定时AD采样
{
AD_FLAG=1;
}
EVAIFRA=0x80;
asm(" CLRC INTM ");
}
void KickDog()
{
WDKEY=0x5555;
WDKEY=0xAAAA;
}
/*====================================================================================================
这是从网上找来的一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID
参数必须由具体对象通过实验确定。由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算,
而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算,可
大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余
数补偿。这个程序只是一般常用pid算法的基本架构,没有包含输入输出处理部分。
=====================================================================================================*/
#include <string.h>
#include <stdio.h>
/*====================================================================================================
PID Function
The PID (比例、积分、微分) function is used in mainly
control applications. PIDCalc performs one iteration of the PID
algorithm.
While the PID function works, main is just a dummy program showing
a typical usage.
=====================================================================================================*/
typedef struct PID {
double SetPoint; // 设定目标 Desired Value
double Proportion; // 比例常数 Proportional Const
double Integral; // 积分常数 Integral Const
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -