51单片机电机驱动详解
目录
一、电路接线
二、只控制电机正反转代码
三、控制电机正反转并调速代码
四、控制电机闭环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