文章写的是基于数字PID控制器的闭环电机速度控制系统,系统以STC89C51单片机为控制核心,通过电机驱动L298N(这里因为所用电机是个小马达,额定电流和堵转电流不算大,所以用的是TC1508S做驱动)控制电机转速,电机转速控制采用PWM控制。在对电机转速进行调速的过程同时,单片机对电机的转速进行实时采集,并在LCD1602显示屏上实时显示电机实际转速。由于PID参数整定的需要,在设计中利用按键实现PID参数显示和修改;同时使用按键控制电机的目标转速。
以下是主要硬件部分:
霍尔传感器测速的使用非常简单,在电机同轴上安装几个小磁铁,当磁铁靠近霍尔传感器的时候,输出端输出低电平。
下面部分代码,后面会附上代码ZIP,仅供参考。
主程序:
#include "include.h"
void main()
{
System_Init();
while(1)
{
_5_KEY_scan();
LCD_Display();
}
}
初始化程序:
#include <reg52.h>
#include "INIT.h"
#include "LCD_Drive.h"
#include "key.h"
#include "TIM.h"
#include "int0.h"
#include "PID.h"
#include<intrins.h>
#define kp 7.03
#define ki 2.39
#define kd 0
void Motor_Init(void);
void System_Init(void)
{
Motor_Init();
Int0Init();
LCD_init();
LCD_Motor_velocity_Interface();
Set_PID_Parameter(kp,ki,kd);
Timer0Init();
Timer1Init();
}
void Motor_Init(void)
{
Motor_IN0 =0; Motor_IN1 = 0;
}
PID的C文件:
#include "PID.h"
#include "int0.h"
#include "string.h"
PID Velocity_PID;
int OUT=0;
//------------------------------------------
// 设置PID参数
//------------------------------------------
void Set_PID_Parameter(float KP,float KI,float KD)
{
Velocity_PID.Kd = KD;
Velocity_PID.Ki = KI;
Velocity_PID.Kp = KP;
Velocity_PID.SumError = 0;
Velocity_PID.LastError=0;
Velocity_PID.PrevError=0;
Velocity_PID.SetPoint=0;
}
//增量式PID
float incPIDcalc(PID *PIDx,int nextpoint)
{
int iError, iincpid;
iError=PIDx->SetPoint-nextpoint;
iincpid= PIDx->Kp*( iError-PIDx->LastError) +PIDx->Ki*iError +PIDx->Kd*(iError-2*PIDx->LastError+PIDx->PrevError);
if(iincpid>100) iincpid = 100;
else if(iincpid<-100)iincpid = -100;
PIDx->PrevError=PIDx->LastError; //存储保存上次数据
PIDx->LastError=iError;//存储保存数据
PIDx->SumError += iincpid;
if(PIDx->SumError<0)PIDx->SumError=0;
return PIDx->SumError;
}
定时器C文件:
#include "TIM.h"
#include "delay.h"
#include "int0.h"
#include "key.h"
#include "PID.h"
bit Bling_bling=0;//闪烁标志位,选中的参数会闪烁,用于屏幕
void Timer0Init(void) //50毫秒@12.000MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01;
TL0 = 0xB0; //
TH0 = 0x3C; //
TF0 = 0; //清楚TF0标志
TR0 = 1; //开启定时器
ET0 = 1;
}
void Timer1Init(void) //100UÃë@12.000MHz
{
TMOD &= 0x0F; //ÉèÖö¨Ê±Æ÷ģʽ
TMOD |= 0x10; //ÉèÖö¨Ê±Æ÷ģʽ
TL1 = 0x9C; //ÉèÖö¨Ê±³õÖµ
TH1 = 0xFF; //ÉèÖö¨Ê±³õÖµ
TF1 = 0; //Çå³ýTF1±êÖ¾
TR1 = 1; //¶¨Ê±Æ÷1¿ªÊ¼¼ÆÊ±
ET1 = 1;
EA = 1; //Open master interrupt switch
}
void TIM0_isr() interrupt 1
{
static uint8 Time_count=0,_50ms_Dida;
uint8 i;
TL0 = 0xB0; //
TH0 = 0x3C; //
_50ms_Dida++;
if(Interface_flag)
{
Time_count++;
if(Time_count==10){
Bling_bling = !Bling_bling;
Time_count = 0;
}
}
if(_50ms_Dida==2)//100msµÃµ½Âö³å¸öÊý
{
/*********Âö³åдÔÚÕâÀï**********/
Pulse_sum[9]=Pulse/2;
SUM = 0;
//¶ÔPulseת»»Îª1sÏÔʾÔÚÆÁÄ»
for(i = 0;i<10;i++)
{
SUM+= Pulse_sum[i];
}
for(i=0;i<9;i++)
{
Pulse_sum[i]=Pulse_sum[i+1];
}
OUT= (int)incPIDcalc(&Velocity_PID,Pulse_sum[9]);
if(Velocity_PID.SetPoint==0)OUT = 0;
Pulse = 0;
/******************************/
_50ms_Dida=0;
}
}
void TIM1_isr() interrupt 3
{
static uint16 PWM=0;
TL1 = 0x9C; //ÉèÖö¨Ê±³õÖµ
TH1 = 0xFF; //ÉèÖö¨Ê±³õÖµ
PWM++;
if(PWM==100) PWM=0;
if(OFF_AND_ON)
{
if(dirct_flag)//Motor_IN0 = 1;Motor_IN1 = 0;//Õýת
{
if(PWM==0) Motor_IN0 = 1;//Motor_pwm=1;
if(PWM==OUT) Motor_IN0 = 0;//Motor_pwm=0;
}
else if(dirct_flag==0)//Motor_IN0 = 0;Motor_IN1 = 1;//·´×ª
{
if(PWM==0)Motor_IN1 = 1;//Motor_pwm=1;
if(PWM==OUT)Motor_IN1 = 0;// Motor_pwm=0;
}
}
}
按键C文件:
#include "key.h"
#include "LCD_Drive.h"
#include "PID.h"
int aim_velocity=0;
uint8 Parameter=0;
bit key_aux=0,Interface_flag=0;
bit dirct_flag=0,OFF_AND_ON=0;
void _5_KEY_scan(void)
{
if(Clockwise_Key==0 && key_aux==1)
{
key_aux = 0;
dirct_flag=!dirct_flag;
if(OFF_AND_ON==1)
{
if(dirct_flag == 1)
{
Motor_IN0 = 1;Motor_IN1 = 0;//Õýת
Write_Cmd(0x40+0x86);
Write_Data('+');
}
else if(dirct_flag==0)
{
Motor_IN0 = 0;Motor_IN1 = 1;//·´×ª
Write_Cmd(0x40+0x86);
Write_Data('-');
}
}
}
else if(Switch==0 && key_aux==1)//¿ª¹Ø¼üºÍÑ¡ÔñPID²ÎÊý¼ü
{
key_aux = 0;
if(!Interface_flag)
{
OFF_AND_ON=!OFF_AND_ON;
if(OFF_AND_ON==0)
{
Motor_IN0 = 0;Motor_IN1 = 0;
Write_Cmd(0x40+0x86);
Write_Data(' ');
Velocity_PID.SumError = 0;
Velocity_PID.LastError = 0;
}
if(OFF_AND_ON == 1)
{
if(dirct_flag == 1){
Write_Cmd(0x40+0x86);
Write_Data('+');
}else if(dirct_flag ==0)
{
Write_Cmd(0x40+0x86);
Write_Data('-');
}
}
}
else
{
Parameter++;
if(Parameter==4)Parameter=0;
}
}
if(Up_speed==0 && key_aux==1)
{
key_aux = 0;
if(!Interface_flag) Velocity_PID.SetPoint+=1;//¼ÓËÙ
else if(Interface_flag)
{
switch(Parameter)
{
case 1:Velocity_PID.Kp +=0.1;break;
case 2:Velocity_PID.Ki +=0.1;break;
case 3:Velocity_PID.Kd +=0.1;break;
}
}
}
if(Down_speed==0 && key_aux==1)
{
key_aux = 0;
if(!Interface_flag)
{
Velocity_PID.SetPoint-=1;//¼õËÙ
if(Velocity_PID.SetPoint<0)Velocity_PID.SetPoint = 0;
}
else if(Interface_flag)
{
switch(Parameter)
{
case 1:Velocity_PID.Kp -=0.1;if(Velocity_PID.Kp<0)Velocity_PID.Kp=0;break;
case 2:Velocity_PID.Ki -=0.1;if(Velocity_PID.Ki<0)Velocity_PID.Ki=0;break;
case 3:Velocity_PID.Kd -=0.1;if(Velocity_PID.Kd<0)Velocity_PID.Kd=0;break;
}
}
}
if(Interface_f == 0 && key_aux==1)//½çÃæÇл»°´¼ü
{
key_aux = 0;
Parameter = 0;//Çл»½çÃæË³±ã°ÑËüÖÃ0£¬Ï´βŲ»»áÊÇÑ¡Ôñ״̬
Interface_flag = !Interface_flag;
if(!Interface_flag){Write_Cmd(0x38); Write_Cmd(0x0c); Write_Cmd(0x01);LCD_Motor_velocity_Interface();}
else {Write_Cmd(0x38); Write_Cmd(0x0c); Write_Cmd(0x01);LCD_PIDparameter_Interface();}
}
if(Clockwise_Key && Switch && Up_speed && Down_speed && Interface_f)//松开
key_aux = 1;
}
显示屏的程序就不贴了,每个人使用的显示屏都可以不一样。只要信息能显示出来都可以了。
下面附上程序压缩包。Motor_incPID.zip