我们这里提供左右两个电机PWM控制代码,在公众号:小白学移动机器人,发送:电机PWM控制,即可获得源码工程下载链接。
STM32单片机、直流减速电机、TB6612双路电机驱动器、keil5
具体到我们的电机,我们可以看看电机后面的图解。
中间的四根线(红绿白黑)是编码器的线,只是用于测速,和直流电机本身没有联系。
综上所述,我们只需控制施加在黄线和棕色线两端的直流电压大小和极性即可实现调试和换向。
要实现上面的调试和换向功能,我们可以使用单片机实现的,但是单片机IO 的带负载能力较弱,而直流电机是大电流感性负载,所以我们需要功率放大器件,在这里,我们选择了 TB6612FNG驱动器。
TB6612FNG 是东芝半导体公司生产的一款直流电机驱动器件,它具有大电流MOSFET-H 桥结构,双通道电路输出,可同时驱动 2 个电机。也许大家更熟悉 L298N,其实这两者的使用基本一致的。而且,相比 L298N 的热耗性和外围二极管续流电路,它无需外加散热片,外围电路简单,只需外接电源滤波电容就可以直接驱动电机,利于减小系统尺寸。对于PWM信号输入频率范围,高达100KHz的频率足以满足我们大部分的要求:
VM直接接电池即可,VCC是内部的逻辑供电,一般给3.3v或者5v都行,模块的3个GND接任意一个就行。STBY置高模块才能正常工作。
完成上面的接线之后,我们可以开始控制电机了,上图中红色的部分的5个引脚控制一路电机,蓝色部分控制另一路电机,这里以A路为例。A01和A02分别接电机的+和-。然后同PWMA,AIN2,AIN1控制电机。其中PWMA接到单片机的PWM引脚,一般10KHZ即可,并通过改变占空比调节电机的速度。下面是真值表:
AIN1接3.3-5v、AIN2接GND,PWMA接到3.3-5v。这样相当于控制电机满占空比正转,反转相反。
我这里就用通俗的话描述一下定时器的PWM。
首先明确一点,STM32单片机引脚PWM电压输出一般为0-3.3V。该电压属于有效值,看过波形的大家都知道,PWM一个周期内有高电平和低电平。切换电平标志就是0-自动重载值范围内的一个数,相比即可得到PWM的百分比大小。例如TIM1->CCR1这个寄存器就存放了一个这样的数字。当定时器的计数器比较当前数字等于TIM1->CCR1,立即切换引脚电平状态。
大家都知道每一个定时器的内部都有一个16计数器,根据计数模式的不同,该计数器存在自加、自减两种情况,这里按自加描述。定时器硬件配置的时候往往需要我们填入两个参数,自动重载值 arr、预分频值psc。
预分频值psc的作用:决定该定时器的时钟频率的大小。
自动重载值 arr的作用:范围在0-65535之间,决定该定时器溢出的位置大小。
举个例子,这里按照单片机系统时钟72MHz描述,如果psc=7200,那么该定时器的时钟频率为72M/7200=10KHz,也就是说该计数器每秒自加10000次数据。如果arr=1000,那么也就是说该定时器1000/10000=0.1溢出一次,也就是说该PWM的频率为1/0.1=10Hz。
(1)TB6612驱动器引脚配置、PWM配置
Motor_Init函数中的四个引脚选择根据每个人的情况而定,需要做必要的更改。
PWM_Init函数中具体的PWM引脚选择也是根据每个人的情况而定,如果可以的话,可以直接按照这里的配置使用。
#include "pwm.h"
/**************************************************************************
函数功能:motor相关引脚初始化
入口参数:tb6612驱动需要的引脚初始化,具体使用哪些引脚根据各自的情况而定
返回 值:无
**************************************************************************/
void Motor_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; //端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M
GPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOB
PWM_Init(arr,psc);
}
/**************************************************************************
函数功能:控制电机的两路PWM初始化
返回 值:无
**************************************************************************/
void PWM_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); //
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能GPIO外设时钟使能
//设置该引脚为复用输出功能,输出TIM1 CH1 CH4的PWM脉冲波形
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_11; //TIM_CH1 //TIM_CH4
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC1Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC4Init(TIM1, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_CtrlPWMOutputs(TIM1,ENABLE); //MOE 主输出使能
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH1预装载使能
TIM_OC4PreloadConfig(TIM1, TIM_OCPreload_Enable); //CH4预装载使能
TIM_ARRPreloadConfig(TIM1, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM1, ENABLE); //使能TIM1
}
(2)PWM电机控制
#include "motor.h"
//左右轮电机PWM变量
int motorLeft = 0;
int motorRight = 0;
/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:左轮PWM、右轮PWM
返回 值:无
**************************************************************************/
void Set_Pwm(int motorLeft,int motorRight)
{
if(motorLeft>0) AIN2=0, AIN1=1;
else AIN2=1, AIN1=0;
PWMA=myabs(motorLeft);
if(motorRight>0) BIN1=0, BIN2=1;
else BIN1=1, BIN2=0;
PWMB=myabs(motorRight);
}
/**************************************************************************
函数功能:绝对值函数
入口参数:int
返回 值:unsigned int
**************************************************************************/
int myabs(int a)
{
int temp;
if(a<0)
temp=-a;
else
temp=a;
return temp;
}
(3)main.c
#include "sys.h"
//====================自己加入的头文件===============================
#include "delay.h"
#include "led.h"
#include "pwm.h"
#include "motor.h"
#include
//===================================================================
int main(void)
{
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁用JTAG 启用 SWD
MY_NVIC_PriorityGroupConfig(2); //=====设置中断分组
delay_init(); //=====延时函数初始化
LED_Init(); //=====LED初始化 程序灯
Motor_Init(7199,0); //=====初始化PWM 10KHZ,用于驱动电机 如需初始化驱动器接口
while(1)
{
Set_Pwm(1000,-1000);
Led_Flash(30);
delay_ms(15);
}
}
其实本篇文章就是简单的实现一下直流减速电机PWM控制。还没有对电机的速度实现闭环控制,下一篇,我们来写光电\霍尔编码器的电机测速的原理以及代码实现。
搭建ROS小车真的难吗?
ROS小车软件结构以及控制流程