学习目标
- 了解电机驱动原理
- 熟悉原理图
- 了解电机驱动芯片的驱动原理
- 掌握PWM控制电机转动
- 熟悉驱动封装
- 掌握逻辑分析仪使用方法
学习内容
电机
采用N20电机,电机上有两个触点,通电就可以转动。我们采用的N20电机的额定电压为6V,最大转速是300RPM(300圈/分钟)
通电的时候,无论正接还是反接,电机都可以转动,只是转动的方向不同。 :::warning 注意:
- 减速电机请尽量避免堵转或者超负载,否则会出现崩齿轮损耗。
- 正反转控制时,不要让齿轮在传动中立刻切换转向,建议在完全停止后切换转向
:::
原理图
每个电机分别对应一个电机驱动芯片,通过电机驱动芯片进行驱动电机转动。
电机驱动芯片又是通过两路pwm来控制的。
以下是电机驱动芯片的真值表:
两个引脚分别对应了采用互补的方式,可以驱动电机,达到电机可以正转反转的效果。
电机驱动帮助我们解决了正反转动态切换的问题驱动编写
```cifndef MOTORS_H
define MOTORS_H
include “Config.h”
void Motors_init();
// 0 -> 100 void Motors_forward(int speed);
// 0 -> 100 void Motors_backward(int speed);
// 0左转 void Motors_left(int speed);
// 0右转 void Motors_right(int speed);
// 原地转 void Motors_around(int speed);
// stop void Motors_stop();
endif
```c
#include "Motors.h"
#include "STC8H_PWM.h"
#include "GPIO.h"
#define RQ_B P15
#define RQ_F P14
#define PERIOD (MAIN_Fosc / 1000)
typedef struct {
int LQ_speed;
int LH_speed;
int RQ_speed;
int RH_speed;
} MotorCfg;
PWMx_Duty dutyA;
// -100 ------------------ 0 --------------------- 100 Speed
//B_Max 0 F_Max 速度
// 0 -------------------- 50 -------------------- 100 duty
int speed2duty(int speed) {
// speed > 0 前进
// speed < 0 后退
// [-100, 100]
// speed / 2 => [-50, 50]
// speed / 2 + 50 => [ 0, 100]
return speed / 2 + 50;
}
void Motors_config(MotorCfg cfg)
{
PWMx_InitDefine pwmInit;
int LQ_duty = speed2duty(cfg.LQ_speed);
int LH_duty = speed2duty(cfg.LH_speed);
int RQ_duty = speed2duty(cfg.RQ_speed);
int RH_duty = speed2duty(cfg.RH_speed);
// 配置PWM1 - LH 左后
pwmInit.PWM_Mode = CCMRn_PWM_MODE1; //模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
pwmInit.PWM_Duty = PERIOD * LH_duty / 100; //PWM占空比时间, 0~Period
pwmInit.PWM_EnoSelect = (cfg.LH_speed == 0 ? 0 : (ENO1P | ENO1N)); //输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
PWM_Configuration(PWM1, &pwmInit); //初始化PWM
// 配置PWM2 - RH 右后
pwmInit.PWM_Mode = CCMRn_PWM_MODE2; //模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
pwmInit.PWM_Duty = PERIOD * RH_duty / 100; //PWM占空比时间, 0~Period
pwmInit.PWM_EnoSelect = (cfg.RH_speed == 0 ? 0 : (ENO2P | ENO2N)); //输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
PWM_Configuration(PWM2, &pwmInit); //初始化PWM
// 配置PWM3 - RQ 右前
pwmInit.PWM_Mode = CCMRn_PWM_MODE1; //模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
pwmInit.PWM_Duty = PERIOD * RQ_duty / 100; //PWM占空比时间, 0~Period
pwmInit.PWM_EnoSelect = (cfg.RQ_speed == 0 ? 0 : (ENO3P | ENO3N)); //输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
PWM_Configuration(PWM3, &pwmInit);
// 配置PWM4 - LQ 左前
pwmInit.PWM_Mode = CCMRn_PWM_MODE2; //模式, CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
pwmInit.PWM_Duty = PERIOD * LQ_duty / 100; //PWM占空比时间, 0~Period
pwmInit.PWM_EnoSelect = (cfg.LQ_speed == 0 ? 0 : (ENO4P | ENO4N)); //输出通道选择, ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
PWM_Configuration(PWM4, &pwmInit);
// 配置PWMA
pwmInit.PWM_Period = PERIOD - 1; //周期时间, 0~65535
pwmInit.PWM_DeadTime = 0; //死区发生器设置, 0~255
pwmInit.PWM_MainOutEnable= ENABLE; //主输出使能, ENABLE,DISABLE
pwmInit.PWM_CEN_Enable = ENABLE; //使能计数器, ENABLE,DISABLE
PWM_Configuration(PWMA, &pwmInit); //初始化PWM通用寄存器, PWMA,PWMB
// 切换PWM通道
// LH 左后
PWM1_SW(PWM1_SW_P20_P21); //PWM1_SW_P10_P11,PWM1_SW_P20_P21,PWM1_SW_P60_P61
// RH 右后
PWM2_SW(PWM2_SW_P22_P23); //PWM2_SW_P12_P13,PWM2_SW_P22_P23,PWM2_SW_P62_P63
// RQ 右前
PWM3_SW(PWM3_SW_P14_P15); //PWM3_SW_P14_P15,PWM3_SW_P24_P25,PWM3_SW_P64_P65
// LQ 左前
PWM4_SW(PWM4_SW_P16_P17); //PWM4_SW_P16_P17,PWM4_SW_P26_P27,PWM4_SW_P66_P67,PWM4_SW_P34_P33
// 初始化PWMA的中断
NVIC_PWM_Init(PWMA,DISABLE,Priority_0);
}
void Motors_init() {
// 电机IO口
P1_MODE_IO_PU(GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7);
P2_MODE_IO_PU(GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3);
}
// 0 -> 100
void Motors_forward(int speed) {
MotorCfg cfg;
cfg.LQ_speed = speed;
cfg.LH_speed = speed;
cfg.RQ_speed = speed;
cfg.RH_speed = speed;
Motors_config(cfg);
}
// 0 -> 100
void Motors_backward(int speed) {
MotorCfg cfg;
cfg.LQ_speed = -speed;
cfg.LH_speed = -speed;
cfg.RQ_speed = -speed;
cfg.RH_speed = -speed;
Motors_config(cfg);
}
// 0左转
void Motors_left(int speed) {
MotorCfg cfg;
cfg.LQ_speed = 0;
cfg.LH_speed = 0;
cfg.RQ_speed = speed;
cfg.RH_speed = speed;
Motors_config(cfg);
}
// 0右转
void Motors_right(int speed) {
MotorCfg cfg;
cfg.LQ_speed = speed;
cfg.LH_speed = speed;
cfg.RQ_speed = 0;
cfg.RH_speed = 0;
Motors_config(cfg);
}
// 原地转
void Motors_around(int speed){
MotorCfg cfg;
cfg.LQ_speed = speed;
cfg.LH_speed = speed;
cfg.RQ_speed = -speed;
cfg.RH_speed = -speed;
Motors_config(cfg);
}
// stop
void Motors_stop() {
MotorCfg cfg;
cfg.LQ_speed = 0;
cfg.LH_speed = 0;
cfg.RQ_speed = 0;
cfg.RH_speed = 0;
Motors_config(cfg);
}
控制逻辑
- 前进:4个轮子同时转动即可
- 左转:右侧轮子的转速大于左侧即可
- 右转:左侧轮子的转速大于右侧即可
N20电机参数
参数表如下图:
额定电压与真实电压输入的范围要求:
- 3V电机电压输入范围:1.5V-5V
- 6V电机电压输入范围:3V-9V
- 12V电机电压输入范围:6V-12V
练习题
- pwm驱动单个电机的启停
- pwm驱动多路电机的启停
- 实现小车的前进后退转弯等功能