赞
踩
我又回来了!周末终于有时间做小车的项目了!这一次是难度最大,最复杂的电机驱动篇了,只要写完电机驱动程序,我们就完成了小车的基础服务函数的编写,就已经可以具备进行编写巡线逻辑与整车测试的条件了!然而,这一篇难度也是非常的大啊,这让我感到压力山大…
吐槽归吐槽,该做还是要做的。让我们一起加油吧。
我采用的是东芝的tb6612fng芯片来驱动电机,因为单片机驱动不了电机这种大电流部件(呜呜呜,好麻烦)。在此我先介绍一下这个电机模块吧。
(部分图片及文字来自https://blog.csdn.net/qq_38721302/article/details/83447870,遵循 CC 4.0 BY-SA 版权协议)
该模块最大输入电压为15V,最大输出电流为1.2A(平均),可以设置电机正反转。以下是这个芯片的图片。
焊得很丑,而且歪了,实在丢人…[捂脸]
可以看见该电机驱动模块有很多引脚,下面让我用图片来解释以下这些引脚有什么作用。
VM需要连接外部电源(电池),vcc和gnd就不用说了,其中要注意的是STBY需要置于高电平(或者接上3.3-5V的引脚)模块才可以正常工作。AO1、AO2、BO1、BO2都是输出(out)的模块,可以a和b可以分别控制一个电机,因此,一个驱动模块可以控制两个电机的运动,算是功能强大的模块了。
电机的运动方式和速度分别由IN引脚(AIN1,AIN2,BIN1,BIN2)与PWM(PWMA,PWMB)控制。通过两个IN口的高低电平设置可以控制前进停止与后退(如下图)
而通过调节PWM的占空比可以调节电机的转速。
由于还没有做出来,我先猜测pwm高电平时间越长,电机转速就越快(不负责任的猜想哈)
接下来应该想的是怎么接线了。接线真的是一个令人头疼的问题啊,单片机的5v和gnd线根本不够,这个芯片还要外接电源,所以只能想办法扩展5v和gnd的接口。
于是,左思右想,我搞成了这个样子。
使用面包板和排针扩展5v和gnd的数量。经过万用表的测量,这些接口确实能够使用。希望这些接口够用。
了解了芯片的作用之后,就应该开始考虑编写程序了。
首先考虑定时器模块,我将其命名为pwm_timer,用来与上篇的timer进行区分。pwm_timer的设置大体与上一篇的设置差不多,但是要注意的是,在这个文件中需要设置的不再是TIM_ITConfig与NVIC中断,而是TIM_OCxInit(如TIM_OC1Init)。同时,这次需要使能预装载寄存器(关于预装载寄存器的知识,建议百度影子寄存器与预装载寄存器)。
接下来就是电机驱动模块的函数编写。编写电机驱动模块的函数首先需要设置的参数,初步考虑应该有这些:
#car_driver.h(部分) // 参数:GPIOx,Pin,Mode // 对应MyGPIO_Set函数 #define PWMA_GPIO GPIOB #define PWMA_Pin GPIO_Pin_0 #define PWMA_init PWMA_GPIO, PWMA_Pin, GPIO_Mode_AF_PP #define PWMB_GPIO GPIOB #define PWMB_Pin GPIO_Pin_1 #define PWMB_init PWMB_GPIO, PWMB_Pin, GPIO_Mode_AF_PP #define AIN1_GPIO GPIOB #define AIN1_Pin GPIO_Pin_3 #define AIN1_init AIN1_GPIO, AIN1_Pin, GPIO_Mode_Out_PP #define AIN2_GPIO GPIOB #define AIN2_Pin GPIO_Pin_4 #define AIN2_init AIN2_GPIO, AIN2_Pin, GPIO_Mode_Out_PP #define BIN1_GPIO GPIOB #define BIN1_Pin GPIO_Pin_5 #define BIN1_init BIN1_GPIO, BIN1_Pin, GPIO_Mode_Out_PP #define BIN2_GPIO GPIOB #define BIN2_Pin GPIO_Pin_6 #define BIN2_init BIN2_GPIO, BIN2_Pin, GPIO_Mode_Out_PP
#car_driver.c(部分) // 初始化相应引脚 void MyGPIO_Set(GPIO_TypeDef* input_GPIOx, uint16_t input_Pin, GPIOMode_TypeDef input_Mode) { GPIO_InitTypeDef GPIO_init_set; if (input_GPIOx == GPIOA) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); else if (input_GPIOx == GPIOB) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); else if (input_GPIOx == GPIOC) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); else if (input_GPIOx == GPIOD) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); GPIO_init_set.GPIO_Mode = input_Mode; GPIO_init_set.GPIO_Pin = input_Pin; GPIO_init_set.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(input_GPIOx, &GPIO_init_set); } void CarDriverSet(CarDriverInitType* car_drive_set) { MyGPIO_Set(PWMA_init); MyGPIO_Set(PWMB_init); MyGPIO_Set(AIN1_init); MyGPIO_Set(AIN2_init); MyGPIO_Set(BIN1_init); MyGPIO_Set(BIN2_init); }
接下来是设置定时器输出通道,调用pwm_timer的函数进行设置。这个比较简单,就不再详述了。
经过模拟(使用测试代码),确认可以正常输出pwm波,可以继续写下去了。
开始写驱动服务函数了,我希望这个服务函数能够比较强大——能够输入正负的速度值,正速度值代表正转,负数的代表反转,零代表停止。
这个服务函数CarControl有两个参数,第一设置哪一个电机,第二个设置电机速度。通过if条件判断是否前进(停止),输入值限制在±7199(即arr的最大值)以内,超过范围的会被限制到范围的最大值(最小值)。接下来通过TIM_SetComparex(如TIM_SetCompare1)来设置比较值,这个关系到pwm的占空比,也正是通过占空比来控制电机的速度。
但是,需要注意的是,当输入的速度小于某一个值时(该值大于0),电机可能就会停止转动,因为pwm波映射的输出引脚的电压已经不足以驱动电机了。由于会出现这种情况,所以在接下来应该测试这个最小pwm占空比的大小。但是我在这篇文章就不做了(没有时间,还要复习预习,呜呜呜)。
最终的服务函数大概长这样子:
void CarControl(int which_motor, int input_motor_speed) { // 速度输入合理性判断 if (input_motor_speed < -PwmTIMER_Base_arr) { input_motor_speed = -PwmTIMER_Base_arr; } else if (input_motor_speed > PwmTIMER_Base_arr) { input_motor_speed = PwmTIMER_Base_arr; } if (input_motor_speed == 0) { if (which_motor == MOTORA) { GPIO_ResetBits(AIN1_GPIO, AIN1_Pin); GPIO_ResetBits(AIN2_GPIO, AIN2_Pin); } else if (which_motor == MOTORB) { GPIO_ResetBits(BIN1_GPIO, BIN1_Pin); GPIO_ResetBits(BIN2_GPIO, BIN2_Pin); } } else if (input_motor_speed > 0) { if (which_motor == MOTORA) { GPIO_SetBits(AIN1_GPIO, AIN1_Pin); GPIO_ResetBits(AIN2_GPIO, AIN2_Pin); if (PWMA_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, input_motor_speed); } else if (which_motor == MOTORB) { GPIO_SetBits(BIN1_GPIO, BIN1_Pin); GPIO_ResetBits(BIN2_GPIO, BIN2_Pin); if (PWMB_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, input_motor_speed); } } else if (input_motor_speed < 0) { if (which_motor == MOTORA) { GPIO_ResetBits(AIN1_GPIO, AIN1_Pin); GPIO_SetBits(AIN2_GPIO, AIN2_Pin); if (PWMA_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, -input_motor_speed); } else if (which_motor == MOTORB) { GPIO_ResetBits(BIN1_GPIO, BIN1_Pin); GPIO_SetBits(BIN2_GPIO, BIN2_Pin); if (PWMB_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, -input_motor_speed); } } }
经过测试,单片机可以正常运行。接下来放代码了。
#pwm_timer.h #ifndef __PWM_TIMER_H #define __PWM_TIMER_H #include "stm32f10x.h" void PwmTIMER_BaseSet(TIM_TypeDef* base_input_TIMx, u16 arr, u16 psc); // 基时设置 void PwmTIMER_Init(TIM_TypeDef* input_TIMx, int TIMER_OCx); // pwm初始化函数 #if (!defined TIMER_reset_ENABLE) && (!defined TIMER_reset_DISABLE) void TIMER_Stop(TIM_TypeDef* input_TIMx, int flag); void TIMER_Start(TIM_TypeDef* input_TIMx); #endif #ifndef TIMER_reset_ENABLE #define TIMER_reset_ENABLE 0 #endif #ifndef TIMER_reset_DISABLE #define TIMER_reset_DISABLE 1 #endif #define PwmTIMER_Base_arr 7199 #define PwmTIMER_Base_psc 0 #define PwmTIMER_Base PwmTIMER_Base_arr, PwmTIMER_Base_psc // 对应10kHz #define TIMER_OC1 1 #define TIMER_OC2 2 #define TIMER_OC3 3 #define TIMER_OC4 4 #endif
#pwm_timer.c #include "stm32f10x.h" #include "pwm_timer.h" void PwmTIMER_BaseSet(TIM_TypeDef* base_input_TIMx, u16 arr, u16 psc) { // arr计时器初值,psc分频系数 TIM_TimeBaseInitTypeDef TIM_base_init_set; TIM_base_init_set.TIM_ClockDivision = TIM_CKD_DIV1; TIM_base_init_set.TIM_CounterMode = TIM_CounterMode_Up; TIM_base_init_set.TIM_Period = arr; TIM_base_init_set.TIM_Prescaler = psc; TIM_TimeBaseInit(base_input_TIMx, &TIM_base_init_set); } void PwmTIMER_Init(TIM_TypeDef* input_TIMx, int TIMER_OCx) { TIM_OCInitTypeDef TIMER_pwm_init_set; // 使能定时器时钟 if (input_TIMx == TIM2) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); else if (input_TIMx == TIM3) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); else if (input_TIMx == TIM4) RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); if (input_TIMx -> ARR == 0 || input_TIMx->PSC == 0)PwmTIMER_BaseSet(input_TIMx, PwmTIMER_Base); TIMER_pwm_init_set.TIM_OCMode = TIM_OCMode_PWM1; // 小于为有效电平 TIMER_pwm_init_set.TIM_OutputState = TIM_OutputState_Enable; // pwm输出开启 TIMER_pwm_init_set.TIM_OCPolarity = TIM_OCPolarity_High; // 有效时高电平 if (TIMER_OCx == TIMER_OC1) { TIM_OC1Init(input_TIMx, &TIMER_pwm_init_set); TIM_OC1PreloadConfig(input_TIMx, TIM_OCPreload_Enable); } else if (TIMER_OCx == TIMER_OC2) { TIM_OC2Init(input_TIMx, &TIMER_pwm_init_set); TIM_OC2PreloadConfig(input_TIMx, TIM_OCPreload_Enable); } else if (TIMER_OCx == TIMER_OC3) { TIM_OC3Init(input_TIMx, &TIMER_pwm_init_set); TIM_OC3PreloadConfig(input_TIMx, TIM_OCPreload_Enable); } else if (TIMER_OCx == TIMER_OC4) { TIM_OC4Init(input_TIMx, &TIMER_pwm_init_set); TIM_OC4PreloadConfig(input_TIMx, TIM_OCPreload_Enable); } TIM_Cmd(input_TIMx, ENABLE); } void TIMER_Stop(TIM_TypeDef* input_TIMx, int flag) { TIM_Cmd(input_TIMx, DISABLE); if (flag == TIMER_reset_ENABLE)TIM_SetCounter(input_TIMx, 0); } void TIMER_Start(TIM_TypeDef* input_TIMx) { TIM_Cmd(input_TIMx, ENABLE); }
#car_driver.h #ifndef CAR_DRIVER_H #define CAR_DRIVER_H #include "stm32f10x.h" #include "pwm_timer.h" // 参数:GPIOx,Pin,Mode // 对应MyGPIO_Set函数 #define PWMA_GPIO GPIOB #define PWMA_Pin GPIO_Pin_0 #define PWMA_init PWMA_GPIO, PWMA_Pin, GPIO_Mode_AF_PP #define PWMB_GPIO GPIOB #define PWMB_Pin GPIO_Pin_1 #define PWMB_init PWMB_GPIO, PWMB_Pin, GPIO_Mode_AF_PP #define AIN1_GPIO GPIOB #define AIN1_Pin GPIO_Pin_3 #define AIN1_init AIN1_GPIO, AIN1_Pin, GPIO_Mode_Out_PP #define AIN2_GPIO GPIOB #define AIN2_Pin GPIO_Pin_4 #define AIN2_init AIN2_GPIO, AIN2_Pin, GPIO_Mode_Out_PP #define BIN1_GPIO GPIOB #define BIN1_Pin GPIO_Pin_5 #define BIN1_init BIN1_GPIO, BIN1_Pin, GPIO_Mode_Out_PP #define BIN2_GPIO GPIOB #define BIN2_Pin GPIO_Pin_6 #define BIN2_init BIN2_GPIO, BIN2_Pin, GPIO_Mode_Out_PP #define PWM_timer TIM3 #define PWMA_OC TIMER_OC3 // 即TIMx_CH3 #define PWMB_OC TIMER_OC4 // 即TIMx_CH4 void CarDriverSet(void); void CarControl(int which_motor, int input_motor_speed); #define MOTORA 1 #define MOTORB 2 #endif
#car_driver.c #include "stm32f10x.h" #include "car_driver.h" // 初始化相应引脚 void MyGPIO_Set(GPIO_TypeDef* input_GPIOx, uint16_t input_Pin, GPIOMode_TypeDef input_Mode) { GPIO_InitTypeDef GPIO_init_set; if (input_GPIOx == GPIOA) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); else if (input_GPIOx == GPIOB) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); else if (input_GPIOx == GPIOC) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); else if (input_GPIOx == GPIOD) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE); GPIO_init_set.GPIO_Mode = input_Mode; GPIO_init_set.GPIO_Pin = input_Pin; GPIO_init_set.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(input_GPIOx, &GPIO_init_set); } void CarDriverSet(void) { MyGPIO_Set(PWMA_init); MyGPIO_Set(PWMB_init); MyGPIO_Set(AIN1_init); MyGPIO_Set(AIN2_init); MyGPIO_Set(BIN1_init); MyGPIO_Set(BIN2_init); PwmTIMER_Init(PWM_timer, PWMA_OC); PwmTIMER_Init(PWM_timer, PWMB_OC); TIM_SetCompare3(PWM_timer, 0); TIM_SetCompare4(PWM_timer, 0); // 默认电机1停止 GPIO_ResetBits(AIN1_GPIO, AIN1_Pin); GPIO_ResetBits(AIN2_GPIO, AIN2_Pin); // 默认电机2停止 GPIO_ResetBits(BIN1_GPIO, BIN1_Pin); GPIO_ResetBits(BIN2_GPIO, BIN2_Pin); } void CarControl(int which_motor, int input_motor_speed) { // 速度输入合理性判断 if (input_motor_speed < -PwmTIMER_Base_arr) { input_motor_speed = -PwmTIMER_Base_arr; } else if (input_motor_speed > PwmTIMER_Base_arr) { input_motor_speed = PwmTIMER_Base_arr; } if (input_motor_speed == 0) { if (which_motor == MOTORA) { GPIO_ResetBits(AIN1_GPIO, AIN1_Pin); GPIO_ResetBits(AIN2_GPIO, AIN2_Pin); } else if (which_motor == MOTORB) { GPIO_ResetBits(BIN1_GPIO, BIN1_Pin); GPIO_ResetBits(BIN2_GPIO, BIN2_Pin); } } else if (input_motor_speed > 0) { if (which_motor == MOTORA) { GPIO_SetBits(AIN1_GPIO, AIN1_Pin); GPIO_ResetBits(AIN2_GPIO, AIN2_Pin); if (PWMA_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, input_motor_speed); else if (PWMA_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, input_motor_speed); } else if (which_motor == MOTORB) { GPIO_SetBits(BIN1_GPIO, BIN1_Pin); GPIO_ResetBits(BIN2_GPIO, BIN2_Pin); if (PWMB_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, input_motor_speed); else if (PWMB_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, input_motor_speed); } } else if (input_motor_speed < 0) { if (which_motor == MOTORA) { GPIO_ResetBits(AIN1_GPIO, AIN1_Pin); GPIO_SetBits(AIN2_GPIO, AIN2_Pin); if (PWMA_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, -input_motor_speed); else if (PWMA_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, -input_motor_speed); } else if (which_motor == MOTORB) { GPIO_ResetBits(BIN1_GPIO, BIN1_Pin); GPIO_SetBits(BIN2_GPIO, BIN2_Pin); if (PWMB_OC == TIMER_OC1)TIM_SetCompare1(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC2)TIM_SetCompare2(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC3)TIM_SetCompare3(PWM_timer, -input_motor_speed); else if (PWMB_OC == TIMER_OC4)TIM_SetCompare4(PWM_timer, -input_motor_speed); } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。