单片机如何写PID程序?

单片机如何写PID程序?,第1张

具体如下:

1、如果加入D抖动的特别厉害,试试只用PI控制。

2、还有PID参数都是一步一步调出来的,我建议你做个上位机,就是个简单的VB串口程序,用来设置PID参数

3、然后在单片机这边弄个串口接收程序,这里就是个简单的串口程序,人人都会,把接收到的PID存储在缓冲区里。

4、然后单片机程序直接调用。单片机带EEPROM的话,当接收到改变的PID参数时,存储这些参数。去STC官网下你的单片机资料,上面有EEPROM测试程序,直接套用。

include "stc12.h"

#include <intrins.h>

#include"pid.h"

typedef unsigned charuint8

typedef unsigned int uint16

typedef unsigned long intuint32

/*********************************函数声明******************

结构体设定

***********************************************************/

typedef struct PIDValue

{

uint32 Ek_Uint32[3] //差值保存,给定和反馈的差值

uint8 EkFlag_Uint8[3]//差值标志位符号,1则对应的为负数,0为对应的为正数

uint8 KP_Uint8 //比例系数

uint8 KI_Uint8 //积分系数

uint8 KD_Uint8 //微分显示

uint16 Uk_Uint16 //上一时刻的控制电压

uint16 RK_Uint16 //设定值

uint16 CK_Uint16 //实际值

uint8 Vaule_Flag //输出的值正负标志位,0为正,1为负

}PIDValueStr

PIDValueStr PID //定义一个结P构体

uint16out // 加热输出(PID运算后的输出值)

/***********************************************************************************

增量型PID算式:PID :Uk=KP*[E(k)-E(k-1)]+KI*E(k)+KD*[E(k)-2E(k-1)+E(k-2)]

函数入口: RK(设定值),CK(实际值),KP,KI,KD PID比例参数

函数出口: U(K)

PID运算函数

**************************************************************************************/

uint16 PID_Calc(uint16 PIDinput)

{

uint32 Temp[3] //中间临时变量

uint32 PostSum //正数和

uint32 NegSum //负数和

Temp[0] = 0 //给储存中间临时变量赋初值

Temp[1] = 0

Temp[2] = 0

PostSum = 0 //给存储所有的正数变量赋初值

NegSum = 0 //给存储所有的负值变量赋初值

PID.RK_Uint16=180 //设定值为180

PID.CK_Uint16=PIDinput //输入值

if( PID.RK_Uint16 >PID.CK_Uint16 ) //如果设定值大于实际值,就是当前的值比设定值小

{

if( PID.RK_Uint16 - PID.CK_Uint16 >10 )//计算偏差是否大于 piancha=10 ( 这里的10由 piancha 来设定大小,根据实际情况设定)

//if( PID.RK_Uint16 - PID.CK_Uint16 >piancha )

{ //如果偏差大于 piancha=10 不在设定的PID调控范围之内就全速加热

out = 100 //偏差大于piancha=10为上限幅值输出(全速加热)

// PID.Uk_Uint16 = full_speed //全速时的加热值,更具实际情况可自由设定 这里full_speed=100;

}

else //如果偏差小于 piancha=10 再调节的范围内就计算储存起来

{ //下面就是PID算法

Temp[0] = PID.RK_Uint16 - PID.CK_Uint16 // 计算出当前偏差值E(k)

PID.EkFlag_Uint8[1]=0 //E(k)为正数 的标志位 0为正,1为负

//数值移位

PID.Ek_Uint32[2] = PID.Ek_Uint32[1] //存储E(k-2)

PID.Ek_Uint32[1] = PID.Ek_Uint32[0] //储存E(k-1)

PID.Ek_Uint32[0] = Temp[0] //存储E(k)

/****************************************************************************************/

if( PID.Ek_Uint32[0] >PID.Ek_Uint32[1] )//E(k)>E(k-1) 为正数

{

Temp[0]=PID.Ek_Uint32[0] - PID.Ek_Uint32[1] //E(k)-E(k-1) 保存

PID.EkFlag_Uint8[0]=0 // 设定标志位 0为正,1为负

}

else //E(k)<E(k-1)

{

Temp[0]=PID.Ek_Uint32[1] - PID.Ek_Uint32[0] //E(k)-E(k-1)为负数

PID.EkFlag_Uint8[0]=1

}

/*****************************************************************************************/

Temp[2]=PID.Ek_Uint32[1]*2 // 2E(k-1)

if( (PID.Ek_Uint32[0]+ PID.Ek_Uint32[2])>Temp[2] ) //E(k-2)+E(k)>2E(k-1)

{

Temp[2]=(PID.Ek_Uint32[0]+ PID.Ek_Uint32[2])-Temp[2] //E(k-2)+E(k)-2E(k-1)为正数

PID.EkFlag_Uint8[2]=0

}

else //E(k-2)+E(k)-2E(k-1)为负数

{

Temp[2]=Temp[2]-(PID.Ek_Uint32[0]+ PID.Ek_Uint32[2]) //2E(k-1)-(E(k-2)+E(k))

PID.EkFlag_Uint8[2]=1

}

/**********************************************************************************************/

Temp[0] = (uint32)PID.KP_Uint8 * Temp[0] // KP*[E(k)-E(k-1)]

Temp[1] = (uint32)PID.KI_Uint8 * PID.Ek_Uint32[0] // KI*E(k)

Temp[2] = (uint32)PID.KD_Uint8 * Temp[2] // KD*[E(k-2)+E(k)-2E(k-1)]

/************************以下部分代码是讲所有的正数项叠加,负数项叠加**************************/

/**************************************KP*[E(k)-E(k-1)]********************************************/

if(PID.EkFlag_Uint8[0]==0)

PostSum += Temp[0] //正数和

else

NegSum += Temp[0]//负数和

/*************************************** KI*E(k)*************************************************/

if(PID.EkFlag_Uint8[1]==0)

PostSum += Temp[1]//正数和

else

//空 *** 作,E(K)>0

/************************************KD*[E(k-2)+E(k)-2E(k-1)]*************************************/

if(PID.EkFlag_Uint8[2]==0)

PostSum += Temp[2]//正数和

else

NegSum += Temp[2] //负数和

/**********************************************U(K)*************************************************/

//PostSum += (uint32)PID.Uk_Uint16

if(PostSum >NegSum ) // 是否控制量为正数

{

out= PostSum - NegSum

PID.Vaule_Flag=0 //PID调节值是正值

}

else //控制量输出为负数

{

out=NegSum-PostSum

PID.Vaule_Flag=1 //PID调节值是负值

}

}

//return out

}

else //如果设定值小于实际值,就是当前的值大于设定值,就不进行PID计算直接输出 0

{

out = 0

}

return out

}

你可以按照以下步骤编写STC8G1K08芯片的LED点亮和灭的时间控制程序:

设置芯片的时钟和计数器,以便实现时间控制。例如,可以使用定时器或延时函数来控制时间。

在主函数中编写程序,实现LED点亮和灭的时间控制。可以使用if语句或while语句来判断时间是否达到要求,然后控制LED灯的点亮和灭。

在main函数中添加延时函数,以实现上电后30秒后LED灯开始点亮的要求。

下面是一个简单的示例程序,仅供参考:

#include<reg52.h>

sbit LED=P1^0

void delay(unsigned int i)

{

while(i--)

}

void main()

{

unsigned int count=0  //计数器,用于实现30秒的延时

while(count<30000)  //上电后30秒开始执行程序

{

delay(1000)  //每次延时1秒

count+=1000

}

LED=1  //点亮LED

delay(3000)  //LED持续3秒

LED=0  //灭掉LED

while(1)  //芯片不工作

}

在上述程序中,使用了一个计数器来实现上电后30秒的延时,然后LED点亮并持续3秒后灭掉,最后芯片不工作。需要注意的是,示例程序仅供参考,具体实现方式需要根据实际情况进行调整和优化。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11823796.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存