单片机如何写PID程序?

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

具体如下:

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

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

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

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

//P1.1(T0):Count They Distance

//P0.4:Tx

//P0.5:Rx

#include <C8051F310.h>//SFR declarations

#include <stdio.h>//Standard I/O definition file

#include <math.h>//Math library file

#include <Intrins.h>

#include <absacc.h>

unsigned int j,i

char a=0

unsigned int t=0

//sbit led=P0^2

//P0.0(PWM0):给定左轮速度.

sbit vls=P0^4//P0.4(GPIO):给定左轮方向.

sbit vlf=P0^6//P0.6(T0) :反馈左轮速度.

sbit dlf=P1^0//P1.0(GPIO):反馈左轮方向.

//P0.2(PWM0):给定右轮速度.

sbit vrs=P0^5//P0.5(GPIO):给定右轮方向.

sbit vrf=P0^7//P0.7(T0) :反馈右轮速度.

sbit drf=P1^1//P1.1(GPIO):反馈右轮方向.

int ol//左轮给定值

int len

int len_1,len_2

int lyn_1,lyn_2

int vl1,vl2//反馈左轮速度值(取样周期内的方波数)

int lfz//运算后赋给PWM的值

int lyn,lynn

int lun=0,lun_1=0//偏差校正值 即校正PWM输出

int lunp,luni,lund//PID 校正值

int or//右轮给定值

int ren

int ren_1,ren_2

int ryn_1,ryn_2

int vr1,vr2//反馈右轮速度值(取样周期内的方波数)

int rfz//运算后赋给PWM的值

int ryn,rynn

int run=0,run_1=0//偏差校正值 即校正PWM输出

int runp,runi,rund//PID 校正值

float kp=2.0//比例系数1.8

float kd=0.2//微分系数0.4

float lki//积分系数

void pio_init(void)

void sys_init(void)

void t01_init(void)

void TIME3_INT(void)

void PID(void)

void interrupt_init(void)

void delay(unsigned int x)

void pwm1_1(void)

void main(void)

{

PCA0MD &= ~0x40//关闭

pio_init()//P11为测距输入端

sys_init()

t01_init()

pwm1_1()

TIME3_INT()

interrupt_init()

vls=1vrs=0

while(1)

{

ol=50

or=50

delay(1000)

ol=100

or=100

delay(1000)

ol=-50

or=50

delay(1000)

}

}

void PID(void)

{

/****************左轮PID调节******************/

if(dlf==1)

{

lyn=(vl2*256+vl1)//dlf是左轮反馈方向,0表示向前 vl=TL0

}

else

{

lyn=-(vl2*256+vl1)//dlf=1表示是向后退,速度应该为负值

}

len=ol-lyn//误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(len)<8)//30

{

lki=1.4//ki值的确定1.4

}

else

{

lki=0.05//积分系数:如果 | 给定值-反馈值 | 太大

} //则就可以不引入积分,或者引入的很小0.05

lunp=kp*(len-len_1)//比例校正

luni=lki*len//积分校正

lund=kd*(len-2*len_1+len_2)//微分校正

lun=lunp+luni+lund+lun_1//总校正

/*************新旧数据更新*************************/

len_2=len_1

len_1=len//len:当前取样周期内出现的速度偏差len_1:上次取样周期内出现的速度偏差

lun_1=lun//lun:当前取样周期内得出的PWM校正值lun_1:上次取样周期内得出的PWM校正值

/*************新旧数据更新*************************/

if(lun>255)

{

lun=255//正速度

}

if(lun<-255)

{

lun=-255//负速度

}

if(lun<0)

{

vls=1

PCA0CPH0=-lun

}

if(lun>=0)

{

vls=0

PCA0CPH0=lun

}

/****************右轮PID调节******************/

if(drf==0)

{

ryn=(vr2*256+vr1)//drf是右轮反馈方向,0表示向前 vl=TL0

}

else

{

ryn=-(vr2*256+vr1)//dlf=1表示是向后退,速度应该为负值

}

ren=or-ryn//误差=给定速度-反馈速度(取样周期内的方波数)

if(abs(ren)<8)//30

{

lki=1.4//ki值的确定1.4

}

else

{

lki=0.05//积分系数:如果 | 给定值-反馈值 | 太大

} //则就可以不引入积分,或者引入的很小0.05

runp=kp*(ren-ren_1)//比例校正

runi=lki*ren//积分校正

rund=kd*(ren-2*ren_1+ren_2)//微分校正

run=runp+runi+rund+run_1//总校正

/*************新旧数据更新*************************/

ren_2=ren_1

ren_1=ren//len:当前取样周期内出现的速度偏差len_1:上次取样周期内出现的速度偏差

run_1=run//lun:当前取样周期内得出的PWM校正值lun_1:上次取样周期内得出的PWM校正值

/*************新旧数据更新*************************/

if(run>255)

{

run=255//正速度

}

if(run<-255)

{

run=-255//负速度

}

if(run<0)

{

vrs=1

PCA0CPH1=-run

}

if(run>=0)

{

vrs=0

PCA0CPH1=run

}

//因为这里的PCA0CPH0越大,对应的电机速度越小,所以要255来减一下

}

void pio_init(void)

{

XBR0=0x00//0000 0001

XBR1=0x72//0111 0010 时能弱上拉 T0T1连接到脚口P06、P07 CEX0、CEX1连接到脚口P00、P01

P0MDIN=0xff//模拟(0)数字(1) 1111 0011

P0MDOUT=0xc3//开漏(0)推挽(1) 1111 1111

P0SKIP=0x3c//0011 1100

P1MDIN=0xff//1111 1111

P1MDOUT=0xfc//

P1SKIP=0x00//1111 1111

}

void sys_init(void) //12MHz

{

OSCICL=0x43

OSCICN=0xc2

CLKSEL=0x00

}

void pwm1_1(void) //PWM的初始化

{

PCA0MD=0x08//PCA时钟为12分频

PCA0CPL0=200//左轮

PCA0CPM0=0x42//设置左轮为8位PWM输出

PCA0CPH0=200

PCA0CPL1=200//平衡校正

PCA0CPM1=0x42//设置为8位PWM输出

PCA0CPH1=200

PCA0CN=0x40//允许PCA工作

}

void t01_init(void)

{

TCON=0x50//计数器1、2允许

TMOD=0x55//定时器1、2采用16位计数功能

CKCON=0x00

TH1=0x00//用于采集左轮的速度

TL1=0x00

TH0=0x00//用于采集右轮的速度

TL0=0x00

}

void TIME3_INT(void)

{

TMR3CN = 0x00//定时器3为16位自动重载

CKCON &= ~0x40

TMR3RLL = 0xff

TMR3RLH = 0xd7

TMR3L = 0xff

TMR3H = 0xd7

TMR3CN |= 0x04

}

void T3_ISR() interrupt 14 //定时器3中断服务程序

{

//led=~led

EA=0

TCON &=~0x50//关闭计数器0、1

vl1=TL0//取左轮速度值

vl2=TH0

vr1=TL1//取右轮速度值

vr2=TH1

TH1=0x00

TL1=0x00

TH0=0x00

TL0=0x00

PID()//PID处理

TMR3CN &=~0x80//清中断标志位

TCON |=0x50//重新开计数器0、1

EA=1

}

void interrupt_init(void)

{ IE=0x80

IP=0x00

EIE1|=0x80

EIP1|=0x80

}

void delay(unsigned int m) //延时程序

{

for(i=0i<2000i++)

{

for(j=0j<mj++){_nop_()_nop_()}

}

}

//PID算法温控C语言2008-08-17 18:58

#include<reg51.h>

#include<intrins.h>

#include<math.h>

#include<string.h>

struct PID {

unsigned int SetPoint// 设定目标 Desired Value

unsigned int Proportion// 比例常数 Proportional Const

unsigned int Integral// 积分常数 Integral Const

unsigned int Derivative// 微分常数 Derivative Const

unsigned int LastError// Error[-1]

unsigned int PrevError// Error[-2]

unsigned int SumError// Sums of Errors

}

struct PID spid// PID Control Structure

unsigned int rout// PID Response (Output)

unsigned int rin// PID Feedback (Input)

sbit data1=P1^0

sbit clk=P1^1

sbit plus=P2^0

sbit subs=P2^1

sbit stop=P2^2

sbit output=P3^4

sbit DQ=P3^3

unsigned char flag,flag_1=0

unsigned char high_time,low_time,count=0//占空比调节参数

unsigned char set_temper=35

unsigned char temper

unsigned char i

unsigned char j=0

unsigned int s

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

延时子程序,延时时间以12M晶振为准,延时时间为30us×time

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

void delay(unsigned char time)

{

unsigned char m,n

for(n=0n<timen++)

for(m=0m<2m++){}

}

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

写一位数据子程序

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

void write_bit(unsigned char bitval)

{

EA=0

DQ=0/*拉低DQ以开始一个写时序*/

if(bitval==1)

{

_nop_()

DQ=1/*如要写1,则将总线置高*/

}

delay(5)/*延时90us供DA18B20采样*/

DQ=1/*释放DQ总线*/

_nop_()

_nop_()

EA=1

}

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

写一字节数据子程序

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

void write_byte(unsigned char val)

{

unsigned char i

unsigned char temp

EA=0 /*关中断*/

TR0=0

for(i=0i<8i++) /*写一字节数据,一次写一位*/

{

temp=val>>i/*移位 *** 作,将本次要写的位移到最低位*/

temp=temp&1

write_bit(temp)/*向总线写该位*/

}

delay(7)/*延时120us后*/

// TR0=1

EA=1/*开中断*/

}

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

读一位数据子程序

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

unsigned char read_bit()

{

unsigned char i,value_bit

EA=0

DQ=0/*拉低DQ,开始读时序*/

_nop_()

_nop_()

DQ=1/*释放总线*/

for(i=0i<2i++){}

value_bit=DQ

EA=1

return(value_bit)

}

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

读一字节数据子程序

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

unsigned char read_byte()

{

unsigned char i,value=0

EA=0

for(i=0i<8i++)

{

if(read_bit()) /*读一字节数据,一个时序中读一次,并作移位处理*/

value|=0x01<<i

delay(4)/*延时80us以完成此次都时序,之后再读下一数据*/

}

EA=1

return(value)

}

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

复位子程序

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

unsigned char reset()

{

unsigned char presence

EA=0

DQ=0/*拉低DQ总线开始复位*/

delay(30)/*保持低电平480us*/

DQ=1/*释放总线*/

delay(3)

presence=DQ/*获取应答信号*/

delay(28)/*延时以完成整个时序*/

EA=1

return(presence)/*返回应答信号,有芯片应答返回0,无芯片则返回1*/

}

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

获取温度子程序

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

void get_temper()

{

unsigned char i,j

do

{

i=reset()/*复位*/

}while(i!=0)/*1为无反馈信号*/

i=0xcc/*发送设备定位命令*/

write_byte(i)

i=0x44/*发送开始转换命令*/

write_byte(i)

delay(180)/*延时*/

do

{

i=reset()/*复位*/

}while(i!=0)

i=0xcc/*设备定位*/

write_byte(i)

i=0xbe/*读出缓冲区内容*/

write_byte(i)

j=read_byte()

i=read_byte()

i=(i<<4)&0x7f

s=(unsigned int)(j&0x0f)

s=(s*100)/16

j=j>>4

temper=i|j/*获取的温度放在temper中*/

}

/*====================================================================================================

Initialize PID Structure

=====================================================================================================*/

void PIDInit (struct PID *pp)

{

memset ( pp,0,sizeof(struct PID))

}

/*====================================================================================================

PID计算部分

=====================================================================================================*/

unsigned int PIDCalc( struct PID *pp, unsigned int NextPoint )

{

unsigned int dError,Error

Error = pp->SetPoint - NextPoint// 偏差

pp->SumError += Error// 积分

dError = pp->LastError - pp->PrevError// 当前微分

pp->PrevError = pp->LastError

pp->LastError = Error

return (pp->Proportion * Error//比例

+ pp->Integral * pp->SumError //积分项

+ pp->Derivative * dError)// 微分项

}

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

温度比较处理子程序

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

compare_temper()

{

unsigned char i

if(set_temper>temper)

{

if(set_temper-temper>1)

{

high_time=100

low_time=0

}

else

{

for(i=0i<10i++)

{ get_temper()

rin = s// Read Input

rout = PIDCalc ( &spid,rin )// Perform PID Interation

}

if (high_time<=100)

high_time=(unsigned char)(rout/800)

else

high_time=100

low_time= (100-high_time)

}

}

else if(set_temper<=temper)

{

if(temper-set_temper>0)

{

high_time=0

low_time=100

}

else

{

for(i=0i<10i++)

{ get_temper()

rin = s// Read Input

rout = PIDCalc ( &spid,rin )// Perform PID Interation

}

if (high_time<100)

high_time=(unsigned char)(rout/10000)

else

high_time=0

low_time= (100-high_time)

}

}

// else

// {}

}

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

T0中断服务子程序,用于控制电平的翻转 ,40us*100=4ms周期

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

void serve_T0() interrupt 1 using 1

{

if(++count<=(high_time))

output=1

else if(count<=100)

{

output=0

}

else

count=0

TH0=0x2f

TL0=0xe0

}

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

串行口中断服务程序,用于上位机通讯

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

void serve_sio() interrupt 4 using 2

{

/* EA=0

RI=0

i=SBUF

if(i==2)

{

while(RI==0){}

RI=0

set_temper=SBUF

SBUF=0x02

while(TI==0){}

TI=0

}

else if(i==3)

{

TI=0

SBUF=temper

while(TI==0){}

TI=0

}

EA=1*/

}

void disp_1(unsigned char disp_num1[6])

{

unsigned char n,a,m

for(n=0n<6n++)

{

// k=disp_num1[n]

for(a=0a<8a++)

{

clk=0

m=(disp_num1[n]&1)

disp_num1[n]=disp_num1[n]>>1

if(m==1)

data1=1

else

data1=0

_nop_()

clk=1

_nop_()

}

}

}

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

显示子程序

功能:将占空比温度转化为单个字符,显示占空比和测得到的温度

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

void display()

{

unsigned char code number[]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6}

unsigned char disp_num[6]

unsigned int k,k1

k=high_time

k=k%1000

k1=k/100

if(k1==0)

disp_num[0]=0

else

disp_num[0]=0x60

k=k%100

disp_num[1]=number[k/10]

disp_num[2]=number[k%10]

k=temper

k=k%100

disp_num[3]=number[k/10]

disp_num[4]=number[k%10]+1

disp_num[5]=number[s/10]

disp_1(disp_num)

}

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

主程序

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

main()

{

unsigned char z

unsigned char a,b,flag_2=1,count1=0

unsigned char phil[]={2,0xce,0x6e,0x60,0x1c,2}

TMOD=0x21

TH0=0x2f

TL0=0x40

SCON=0x50

PCON=0x00

TH1=0xfd

TL1=0xfd

PS=1

EA=1

EX1=0

ET0=1

ES=1

TR0=1

TR1=1

high_time=50

low_time=50

PIDInit ( &spid )// Initialize Structure

spid.Proportion = 10// Set PID Coefficients

spid.Integral = 8

spid.Derivative =6

spid.SetPoint = 100// Set PID Setpoint

while(1)

{

if(plus==0)

{

EA=0

for(a=0a<5a++)

for(b=0b<102b++){}

if(plus==0)

{

set_temper++

flag=0

}

}

else if(subs==0)

{

for(a=0a<5a++)

for(b=0a<102b++){}

if(subs==0)

{

set_temper--

flag=0

}

}

else if(stop==0)

{

for(a=0a<5a++)

for(b=0b<102b++){}

if(stop==0)

{

flag=0

break

}

EA=1

}

get_temper()

b=temper

if(flag_2==1)

a=b

if((abs(a-b))>5)

temper=a

else

temper=b

a=temper

flag_2=0

if(++count1>30)

{

display()

count1=0

}

compare_temper()

}

TR0=0

z=1

while(1)

{

EA=0

if(stop==0)

{

for(a=0a<5a++)

for(b=0b<102b++){}

if(stop==0)

disp_1(phil)

// break

}

EA=1

}

}

//DS18b20 子程序

#include <REG52.H>

sbit DQ=P2^1 //定义端口

typedef unsigned char byte

typedef unsigned int word

//延时

void delay(word useconds)

{

for(useconds>0useconds--)

}

//复位

byte ow_reset(void)

{

byte presence

DQ=0//DQ低电平

delay(29) //480us

DQ=1//DQ高电平

delay(3)//等待

presence=DQ//presence信号

delay(25)

return(presence)

} //0允许,1禁止

//从1-wire 总线上读取一个字节

byte read_byte(viod)

{

byte i

byte value=0

for (i=8i>0i--)

{

value>>=1

DQ=0

DQ=1

delay(1)

if(DQ)value|=0x80

delay(6)

}

return(value)

}

//向1-wire总线上写一个字节

void write_byte(char val)

{

byte i

for (i=8i>0i--) //一次写一个字节

{

DQ=0

DQ=val&0x01

delay(5)

DQ=1

val=val/2

}

delay(5)

}

//读取温度

char Read_Temperature(void)

{

union{

byte c[2]

int x

}temp

ow_reset()

write_byte(0xcc)

write_byte(0xBE)

temp.c[1]=read_byte()

temp.c[0]=read_byte()

ow_reset()

write_byte(0xCC)

write_byte(0x44)

return temp.x/2

}


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

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

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

发表评论

登录后才能评论

评论列表(0条)

保存