目录

一、电路接线

二、只控制电机正反转代码

三、控制电机正反转并调速代码

四、控制电机闭环pid调速代码

五、串口控制电机代码


一、电路接线

信号线名称 I/O接口
PWM P12(可任意)
IN1 P11(可任意)
IN2 P10(可任意)
Encoder1 P32(外部中断0)
Encoder2 P33(外部中断1)

二、只控制电机正反转代码

#include "reg52.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int  u16;
sbit PWM=P1^2;	//将P1.2管脚定义为PWM
sbit IN1=P1^1;	//将P1.1管脚定义为IN1
sbit IN2=P1^0;	//将P1.0管脚定义为IN2

#define MotorForward  IN1=0,IN2=1 //正传指令
#define MotorBark     IN1=1,IN2=0 //反传指令

void delay_ms(u16 ms)		//@11.0592MHz
{
	u8 i, j;
  while(ms--)
  {
    _nop_();
    i = 2;
    j = 199;
    do
    {
      while (--j);
    } while (--i);
  }
}

void main()
{
  PWM=1;
	while(1)
	{
    MotorForward;
    delay_ms(500);
    MotorBark;
    delay_ms(500);
	}		
} 

三、控制电机正反转并调速代码

#include "reg52.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int  u16;
sbit PWM = P1^2;	//将P1.2管脚定义为PWM
sbit IN1 = P1^1;	//将P1.1管脚定义为IN1
sbit IN2 = P1^0;	//将P1.0管脚定义为IN2

#define MotorForward  IN1 = 0,IN2 = 1 //正传指令
#define MotorBark     IN1 = 1,IN2 = 0 //反传指令

u8 motor_pwm = 0;

void delay_ms(u16 ms)		//@11.0592MHz
{
  u8 i, j;
  while(ms--)
  {
    _nop_();
    i = 2;
    j = 199;
    do
    {
      while (--j);
    } while (--i);
  }
}

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x66;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;			  	//清除TF0标志
	TR0 = 1;			  	//定时器0开始计时
	ET0 = 1;			  	//使能定时器0中断
}

void main()
{
  Timer0_Init();
  motor_pwm = 30; //占空比为30%
  EA = 1;         // 开总中断
  while(1)
  {
    MotorForward;
    delay_ms(500);
    MotorBark;
    delay_ms(500);
  }		
} 

void Timer0_Isr(void) interrupt 1
{
  static u16 ms = 0;
  TL0 = 0x66;				//设置定时初始值
  TH0 = 0xFC;				//设置定时初始值
  if(ms < motor_pwm)
    PWM=1;
  else
    PWM=0;
  if(++ms > 99)
    ms = 0;
}

四、控制电机闭环pid调速代码

#include "reg52.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int  u16;
typedef struct 
{
	u8 enable_lim_sum_error;	//积分限幅使能
	u8 enable_lim_ouput;			//输出限幅使能
	float kp;  			          //比例参数
	float ki;  			          //积分参数
	float kd;  			          //微分参数
	float lim_sum_error;	    //误差积分限幅	
	float lim_up_output;		  //输出限幅 防止输出过大
	float lim_down_output;    //输出限幅 防止输出过小
	float sum_error;		      //误差积分
	float last_error;		      //上一次的误差
	float error;
}PID_InitTypeDef;

sbit PWM = P1^2;	    //将P1.2管脚定义为PWM
sbit IN1 = P1^1;	    //将P1.1管脚定义为IN1
sbit IN2 = P1^0;	    //将P1.0管脚定义为IN2
sbit Encoder1 = P3^2; //将P3.2管脚定义为Encoder1
sbit Encoder2 = P3^3; //将P3.3管脚定义为Encoder2

#define MotorForward  IN1 = 0,IN2 = 1 //正传指令
#define MotorBark     IN1 = 1,IN2 = 0 //反传指令

u8 motor_pwm = 0;
int Count_A=0;
int Count_B=0;
bit GetSpeed_flag=0;
PID_InitTypeDef pid1;

float my_fabs(float a)
{
  if(a < 0)
    a=-a;
  return a;
}

int PidProcess(PID_InitTypeDef* pid,float set, float measure)
{
	static float output = 0,error = 0;
	error = set - measure;         //计算误差
	pid->sum_error += error;
	if(pid->enable_lim_sum_error == 1 && my_fabs(pid->sum_error) > pid->lim_sum_error)
	{
    if(pid->sum_error > 0)
        pid->sum_error = pid->lim_sum_error;
    else
        pid->sum_error = -pid->lim_sum_error;
	}
	output =   pid->kp * error
					 + pid->ki * pid->sum_error
					 + pid->kd * (error - pid->last_error);
	if(pid->enable_lim_ouput == 1)
	{
    if(output > pid->lim_up_output)
       output = pid->lim_up_output;
    if(output < pid->lim_down_output)
       output = pid->lim_down_output;
	}
	pid->last_error = error;
	return (int)output;
}

float GetSpeed(void)//1ms更新一次速度
{	
  static float Speed=0;
  if(GetSpeed_flag == 1)
  {	
    GetSpeed_flag = 0;
    Speed = Count_A+Count_B;//求准确速度自己推速度公式
    Count_A = 0;//重新计数
    Count_B = 0;
  }
  return Speed;
 }

void initExtInt0() //初始化外部中断0用于左电机霍尔编码器
{
    IT0 = 1;       //电平下降沿触发
    EX0 = 1;       //开启INT0中断
}

void initExtInt1() //初始化外部中断1用于左电机霍尔编码器
{
    IT1 = 1;       //电平下降沿触发
    EX1 = 1;       //开启INT0中断
}

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;			  	//清除TF0标志
	TR0 = 1;			  	//定时器0开始计时
	ET0 = 1;			  	//使能定时器0中断
}

void main()
{
  Timer0_Init();
  initExtInt0();
  initExtInt1();
  MotorForward;
  pid1.enable_lim_sum_error=1;
  pid1.enable_lim_ouput=1;
  pid1.lim_sum_error    = 2000.0;
  pid1.lim_up_output    = 70.0;
  pid1.lim_down_output  = 0.0;
  pid1.kp               = 3;        //自行整定pid参数
  pid1.ki               = 0.01;
  pid1.kd               = 0;
  EA = 1;     // 开总中断
	while(1)
	{
    motor_pwm = PidProcess(&pid1,10,GetSpeed()); //设定速度为10
	}		
} 

void Timer0_Isr(void) interrupt 1
{
  static u16 ms = 0;
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
  if(ms < motor_pwm)
    PWM=1;
  else
    PWM=0;
  if(++ms > 99)
  {
    ms = 0;
    GetSpeed_flag = 1;
  }
}

void extInt0_ISR(void) interrupt 0  //外部中断0中断服务程序
{
	if(Encoder1 == 0 && Encoder2 == 1) //A相为高电平/B相为低电平  //正转
    Count_A++;
	if(Encoder1 == 1 && Encoder2 == 0) //A相为低电平/B相为高电平  //反转
		Count_A--;
}

void extInt1_ISR(void) interrupt 2  //外部中断1中断服务程序
{
	if(Encoder1 == 0 && Encoder2 == 1) //A相为高电平/B相为低电平  //正转
	  Count_B++;
	if(Encoder1 == 1 && Encoder2 == 0) //A相为低电平/B相为高电平  //反转
		Count_B--;
}

五、串口控制电机代码

串口数据包通信协议(手机app使用蓝牙调试器)

GUI控制界面

发送端

帧头(1bit) 数据(n bit) 校验位(1bit) 帧尾(1bit)
0XA5 编码器速度 数据的低8位之和

0X5A

接收端

帧头(1bit) 数据(n bit) 校验位(1bit) 帧尾(1bit)
0XA5 电机使能+转动方向+设定速度 数据的低8位之和

0X5A

#include "reg52.h"
#include "intrins.h"

typedef unsigned char u8;
typedef unsigned int  u16;
typedef struct 
{
	u8 enable_lim_sum_error;	//积分限幅使能
	u8 enable_lim_ouput;			//输出限幅使能
	float kp;  			          //比例参数
	float ki;  			          //积分参数
	float kd;  			          //微分参数
	float lim_sum_error;	    //误差积分限幅	
	float lim_up_output;		  //输出限幅 防止输出过大
	float lim_down_output;    //输出限幅 防止输出过小
	float sum_error;		      //误差积分
	float last_error;		      //上一次的误差
	float error;
}PID_InitTypeDef;

sbit PWM = P1^2;	    //将P1.2管脚定义为PWM
sbit IN1 = P1^1;	    //将P1.1管脚定义为IN1
sbit IN2 = P1^0;	    //将P1.0管脚定义为IN2
sbit Encoder1 = P3^2; //将P3.2管脚定义为Encoder1
sbit Encoder2 = P3^3; //将P3.3管脚定义为Encoder2

#define MotorForward  IN1 = 0,IN2 = 1 //正传指令
#define MotorBark     IN1 = 1,IN2 = 0 //反传指令

u8 motor_pwm = 0;
int Count_A=0;
int Count_B=0;
bit enable_pid=0;
bit GetSpeed_flag=0;
PID_InitTypeDef pid1;
u8 rec_str[5];
u8 transmit_str[4]={0XA5,0X00,0X00,0X5A};

float my_fabs(float a)
{
  if(a < 0)
    a=-a;
  return a;
}

int PidProcess(PID_InitTypeDef* pid,float set, float measure)
{
	static float output = 0,error = 0;
	error = set - measure;         //计算误差
	pid->sum_error += error;
	if(pid->enable_lim_sum_error == 1 && my_fabs(pid->sum_error) > pid->lim_sum_error)
	{
    if(pid->sum_error > 0)
        pid->sum_error = pid->lim_sum_error;
    else
        pid->sum_error = -pid->lim_sum_error;
	}
	output =   pid->kp * error
					 + pid->ki * pid->sum_error
					 + pid->kd * (error - pid->last_error);
	if(pid->enable_lim_ouput == 1)
	{
    if(output > pid->lim_up_output)
       output = pid->lim_up_output;
    if(output < pid->lim_down_output)
       output = pid->lim_down_output;
	}
	pid->last_error = error;
	return (int)output;
}

int GetSpeed(void)//100ms更新一次速度
{	
  static int Speed=0;
  Speed = Count_A+Count_B;//求准确速度自己推速度公式
  Count_A = 0;            //重新计数
  Count_B = 0;
  return Speed;
 }

void send_bit(u8 byte)
{
  SBUF = byte;	
  while(!TI);
  TI = 0;
}

void send_string(u8 *str)
{
  u8 i=4;
  while(i)
  {
    send_bit(*str++);
    i--;
  }
}

void initExtInt0() //初始化外部中断0用于左电机霍尔编码器
{
    IT0 = 1;       //电平下降沿触发
    EX0 = 1;       //开启INT0中断
}

void initExtInt1() //初始化外部中断1用于左电机霍尔编码器
{
    IT1 = 1;       //电平下降沿触发
    EX1 = 1;       //开启INT0中断
}

void Timer0_Init(void)		//1毫秒@11.0592MHz
{
	TMOD &= 0xF0;			//设置定时器模式
	TMOD |= 0x01;			//设置定时器模式
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
	TF0 = 0;			  	//清除TF0标志
	TR0 = 1;			  	//定时器0开始计时
	ET0 = 1;			  	//使能定时器0中断
}

void UartInit(void)		//9600bps@11.0592MHz
{
	PCON &= 0x7F;		//波特率不倍速
	SCON = 0x50;		//8位数据,可变波特率
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x20;		//设置定时器模式
	TL1 = 0xFD;			//设置定时初始值
	TH1 = 0xFD;			//设置定时重载值
	ET1 = 0;			//禁止定时器中断
	TR1 = 1;			//定时器1开始计时
	ES  = 1;		  //串口中断打开
}

void main()
{
  int encoder_speed,set_speed;
  Timer0_Init();
  initExtInt0();
  initExtInt1();
  UartInit();
  
  pid1.enable_lim_sum_error=1;
  pid1.enable_lim_ouput=1;
  pid1.lim_sum_error    = 2000.0;
  pid1.lim_up_output    = 70.0;
  pid1.lim_down_output  = 0.0;
  pid1.kp               = 3;      //自行整定pid参数
  pid1.ki               = 0.01;
  pid1.kd               = 0;
  
  EA = 1;     // 开总中断
	while(1)
	{
    if(rec_str[1])          //急停
    {
      if(rec_str[2])        //控制方向
        MotorForward;
      else
        MotorBark;
      set_speed = rec_str[3];
    }
    else
    {
      IN1=1;
      IN2=1;
      set_speed = 0;
    }
    if(GetSpeed_flag)//编码器速度更新100ms
    {	
      encoder_speed = GetSpeed();
      transmit_str[1] = (int)my_fabs(encoder_speed);
      transmit_str[2] = transmit_str[1];
      send_string(&transmit_str);
    }
    if(enable_pid)    //pid计算周期10ms
    {
      motor_pwm = PidProcess(&pid1,set_speed,encoder_speed); //设定速度为10
    }
	}		
} 

void Timer0_ISR(void) interrupt 1
{
  static u16 ms = 0;
	TL0 = 0x18;				//设置定时初始值
	TH0 = 0xFC;				//设置定时初始值
  if(ms < motor_pwm)
    PWM=1;
  else
    PWM=0;
  if(++ms > 99)
  {
    ms = 0;
  }
  if(ms%100==0)
    GetSpeed_flag = 1;
  else 
    GetSpeed_flag = 0;
  if(ms%10==0)
    enable_pid = 1;
  else 
    enable_pid = 0;
}

void extInt0_ISR(void) interrupt 0  //外部中断0中断服务程序
{
	if(Encoder1 == 0 && Encoder2 == 1) //A相为高电平/B相为低电平  //正转
    Count_A++;
	if(Encoder1 == 1 && Encoder2 == 0) //A相为低电平/B相为高电平  //反转
		Count_A--;
}

void extInt1_ISR(void) interrupt 2  //外部中断1中断服务程序
{
	if(Encoder1 == 0 && Encoder2 == 1) //A相为高电平/B相为低电平  //正转
	  Count_B++;
	if(Encoder1 == 1 && Encoder2 == 0) //A相为低电平/B相为高电平  //反转
		Count_B--;
}

void Uart_ISR() interrupt 4
{
  static u8 byte,i = 0,step = 0;
	if(RI)	//检测是否接收完成
	{
		byte = SBUF;	  //取出接收缓存器的值
    if(byte == 0XA5 && step == 0)
    {
      step++;
    }
    else if(step == 1)
    {
      rec_str[i] = byte;
      i++;
      if(byte == 0x5A)
        step++;
    }
    else if(step == 2)
    {
      step = 0;
      i = 0; 
    }
    else
    {
      step = 0;
      i = 0;
    }
		RI = 0;
	}
}

作者:Xiaoen_Lee

物联沃分享整理
物联沃-IOTWORD物联网 » 51单片机电机驱动详解

发表回复