这篇文章很早就写了,但是今天才发现没发出来
STM32 的 GPIO 输出的电流电压是不足以驱动直流减速电机的,因此需要再加上一个电机驱动板和一个直流电源。
我买平衡车底座带电机的时候,发现卖家店里也有 TB6612 这款电机驱动,并且集成了稳压模块,可以输出 3 路 3.3V 和 3 路 5V,所以就一块儿买了。
硬件参数
硬件 | 参数 |
---|---|
电源 | 12V 18600 锂电池组(也可以是航模电池,但是我买不起 =。=) |
直流减速电机 | 型号 MG513P30_12V(额定电压 12V,减速比 30) |
编码器 | 13 线增量式霍尔编码器 |
电机驱动 | TB6612,集成了降压模块、编码器输入输出 |
引脚说明
引脚名 | 作用 |
---|---|
E1A | 电机 1 的 A 相编码值,输入 STM32 |
E1B | 电机 1 的 B 相编码值,输入 STM32 |
PWMA | 电机 A 的 PWM 输入,由 STM32 生成 |
AIN1 | 电机 A 控制位 1 |
AIN2 | 电机 A 控制位 2 |
E2A | 电机 2 的 A 相编码值 |
E2B | 电机 2 的 B 相编码值 |
PWMB | 电机 B 的 PWM 输入 |
BIN1 | 电机 B 的控制位 1 |
BIN2 | 电机 B 的控制位 2 |
STBY | TB6612 电机驱动使能,高电平时使能驱动 |
BAT_ADC | 供电电压检测输出,接 STM32 的 ADC 采集电压 |
- 编码值由编码器提供,用来让 STM32 感知电机的旋转速度和旋转方向
- STM32 通过调节生成的 PWM 信号的占空比来调节电机的旋转速度。
- AIN1/AIN2、BIN1/BIN2 用于控制电机的正反转,其真值表如下:
停止 | 正转 | 反转 | |
---|---|---|---|
AIN1 | 0 | 0 | 1 |
AIN2 | 0 | 1 | 0 |
ADC 端口的实际输出电压为 1.114V,对应电池输出电压为 12.4V
接线
引脚名 | 分配网络名 | 使用的功能 |
---|---|---|
PA0 | E1A | TIM2_CH1_ETR,读取电机 1 的 A 相编码值 |
PA1 | E1B | TIM2_CH2,读取电机 1 的 B 相编码值 |
PA2 | BAT-ADC | ADC123_IN2,采集供电电压大小 |
PA6 | E2A | TIM3_CH1,读取电机 2 的 A 相编码值 |
PA7 | E2B | TIM3_CH2,读取电机 2 的 B 相编码值 |
PA8 | PWMA | TIM1_CH1,生成 PWM 信号,控制电机 A 旋转 |
PA9 | PWMB | TIM1_CH2,生成 PWM 信号,控制电机 B 旋转 |
PA10 | STBY | 推挽,电机驱动使能 |
PB12 | BIN2 | 推挽,控制电机 B 的旋转状态 |
PB13 | BIN1 | 推挽,控制电机 B 的旋转状态 |
PB14 | AIN2 | 推挽,控制电机 A 的旋转状态 |
PB15 | AIN1 | 推挽,控制电机 A 的旋转状态 |
软件设计
编码器的相关实现可以看这篇文章:
头文件
#ifndef __TB6612_H
#define __TB6612_H
#include "stm32f1xx.h"
/**
* 电机 A = 电机 1
* 电机 B = 电机 2
*/
/**********引脚定义**********/
#define TB6612_PWMA_PIN GPIO_PIN_8 //电机 A 的 PWM 输出
#define TB6612_AIN1_PIN GPIO_PIN_15 //电机 A 的控制位 1
#define TB6612_AIN2_PIN GPIO_PIN_14 //电机 A 的控制位 2
#define TB6612_PWMA_PORT GPIOA
#define TB6612_AIN1_PORT GPIOB
#define TB6612_AIN2_PORT GPIOB
#define TB6612_PWMA_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define TB6612_AIN1_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define TB6612_AIN2_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define TB6612_PWMB_PIN GPIO_PIN_9 //电机 B 的PWM输出
#define TB6612_BIN1_PIN GPIO_PIN_13 //电机 B 的控制位 1
#define TB6612_BIN2_PIN GPIO_PIN_12 //电机 B 的控制位 2
#define TB6612_PWMB_PORT GPIOA
#define TB6612_BIN1_PORT GPIOB
#define TB6612_BIN2_PORT GPIOB
#define TB6612_PWMB_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define TB6612_BIN1_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define TB6612_BIN2_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define BAT_ADC_PIN GPIO_PIN_2 //供电电压采集
#define BAT_ADC_PORT GPIOA
#define BAT_ADC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define TB6612_STBY_PIN GPIO_PIN_10 //TB6612使能
#define TB6612_STBY_PORT GPIOA
#define TB6612_STBY_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
/*********PWM定义***********/
#define TB6612_PWM_TIM_RCC_CLK_ENABLE() __HAL_RCC_TIM1_CLK_ENABLE()
#define TB6612_PWM_TIMx TIM1
#define TB6612_PWMA_TIM_CHANNELx TIM_CHANNEL_1
#define TB6612_PWMB_TIM_CHANNELx TIM_CHANNEL_2
/*********供电电压ADC定义****/
#define BAT_ADC_ADCx ADC1
#define BAT_ADC_ADC_CHANNEL ADC_CHANNEL_2
#define BAT_ADC_ADC_CLK_ENABLE() __HAL_RCC_ADC1_CLK_ENABLE()
#define BAT_ADC_IRQ ADC1_IRQn
#define BAT_ADC_INT_FUNCTION ADC1_IRQHandler
extern ADC_HandleTypeDef ADC_Handle;
extern uint16_t ADC_Value;//采集电压的原始数据
#define BAT_LEVEL_HIGH 1.10 //高电量
#define BAT_LEVEL_LOW 0.85 //低电量
/*************旋转方向**************/
//因为两个电机是对称关系,所以电机B的正转就是电机A的反转
//电机 A 正转
#define MOTOR_A_REVERSE_ROTATION {HAL_GPIO_WritePin(TB6612_AIN1_PORT,TB6612_AIN1_PIN,GPIO_PIN_RESET);\
HAL_GPIO_WritePin(TB6612_AIN2_PORT,TB6612_AIN2_PIN,GPIO_PIN_SET);}
//电机 A 反转
#define MOTOR_A_FORWARD_ROTATION {HAL_GPIO_WritePin(TB6612_AIN1_PORT,TB6612_AIN1_PIN,GPIO_PIN_SET);\
HAL_GPIO_WritePin(TB6612_AIN2_PORT,TB6612_AIN2_PIN,GPIO_PIN_RESET);}
//电机 A 停转
#define MOTOR_A_STOP_ROTATION {HAL_GPIO_WritePin(TB6612_AIN1_PORT,TB6612_AIN1_PIN,GPIO_PIN_RESET);\
HAL_GPIO_WritePin(TB6612_AIN2_PORT,TB6612_AIN2_PIN,GPIO_PIN_RESET);}
//电机 B 正转
#define MOTOR_B_FORWARD_ROTATION {HAL_GPIO_WritePin(TB6612_BIN1_PORT,TB6612_BIN1_PIN,GPIO_PIN_RESET);\
HAL_GPIO_WritePin(TB6612_BIN2_PORT,TB6612_BIN2_PIN,GPIO_PIN_SET);}
//电机 B 反转
#define MOTOR_B_REVERSE_ROTATION {HAL_GPIO_WritePin(TB6612_BIN1_PORT,TB6612_BIN1_PIN,GPIO_PIN_SET);\
HAL_GPIO_WritePin(TB6612_BIN2_PORT,TB6612_BIN2_PIN,GPIO_PIN_RESET);}
//电机 B 停转
#define MOTOR_B_STOP_ROTATION {HAL_GPIO_WritePin(TB6612_BIN1_PORT,TB6612_BIN1_PIN,GPIO_PIN_RESET);\
HAL_GPIO_WritePin(TB6612_BIN2_PORT,TB6612_BIN2_PIN,GPIO_PIN_RESET);}
/*************电机编号**************/
#define MOTOR_A 0
#define MOTOR_B 1
/*************电机驱动使能**********/
#define TB6612_ON HAL_GPIO_WritePin(TB6612_STBY_PORT,TB6612_STBY_PIN,GPIO_PIN_SET);
#define TB6612_OFF HAL_GPIO_WritePin(TB6612_STBY_PORT,TB6612_STBY_PIN,GPIO_PIN_RESET);
/**********函数声明**********/
/**
* @brief 初始化电机驱动
* @date 2023-05-25
*/
void TB6612_Init(void);
/**
* @brief 设置电机的旋转方向和速度
*
* @param motor 电机编号
* @param speed 旋转速度,范围:0~100
*/
void TB6612_SetSpeed(uint8_t motor,uint8_t speed);
/**
* @brief 获取电池电压
*
* @return float 电池电压
*/
float TB6612_GetBatLevel(void);
#endif
源文件
/**
* @file tb6612.c
* @brief 电机驱动
* @version 0.1
* @date 2023-05-23
*
* @copyright Copyright (c) 2023
*
*/
#include "tb6612.h"
ADC_HandleTypeDef ADC_Handle;
uint16_t ADC_Value = 0;
TIM_HandleTypeDef PWM_TIM_Base;//PWM
void TB6612_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OC_InitTypeDef OCconfig;
RCC_PeriphCLKInitTypeDef ADC_CLKInit;
ADC_ChannelConfTypeDef ADC_ChannelConf;
/*********************开启时钟*********************/
//GPIO时钟
TB6612_PWMA_CLK_ENABLE();
TB6612_PWMB_CLK_ENABLE();
TB6612_AIN1_CLK_ENABLE();
TB6612_AIN2_CLK_ENABLE();
TB6612_BIN1_CLK_ENABLE();
TB6612_BIN2_CLK_ENABLE();
TB6612_STBY_CLK_ENABLE();
BAT_ADC_CLK_ENABLE();
//PWM时钟
TB6612_PWM_TIM_RCC_CLK_ENABLE();
//ADC时钟
BAT_ADC_ADC_CLK_ENABLE();
/*******************配置时基单元*******************/
//配置 PWM 定时器
PWM_TIM_Base.Instance = TB6612_PWM_TIMx;
PWM_TIM_Base.Init.Prescaler = 71;//PSC
PWM_TIM_Base.Init.CounterMode = TIM_COUNTERMODE_UP;
PWM_TIM_Base.Init.Period = 99;//ARR
PWM_TIM_Base.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&PWM_TIM_Base);
//配置ADC
ADC_CLKInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
ADC_CLKInit.AdcClockSelection = RCC_ADCPCLK2_DIV6;//设置为6分频
HAL_RCCEx_PeriphCLKConfig(&ADC_CLKInit);
/******************配置输出比较单元****************/
OCconfig.OCMode = TIM_OCMODE_PWM1;//PWM模式1
OCconfig.Pulse = 0;//CCR
OCconfig.OCPolarity = TIM_OCPOLARITY_HIGH;//高极性
OCconfig.OCFastMode = TIM_OCFAST_DISABLE;
OCconfig.OCNPolarity = TIM_OCPOLARITY_HIGH;
OCconfig.OCIdleState = TIM_OCIDLESTATE_SET;
OCconfig.OCNIdleState = TIM_OCIDLESTATE_RESET;
HAL_TIM_PWM_ConfigChannel(&PWM_TIM_Base,&OCconfig,TB6612_PWMA_TIM_CHANNELx);
HAL_TIM_PWM_ConfigChannel(&PWM_TIM_Base,&OCconfig,TB6612_PWMB_TIM_CHANNELx);
/*********************初始化ADC*******************/
ADC_Handle.Instance = BAT_ADC_ADCx;
ADC_Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;//右对齐
ADC_Handle.Init.ScanConvMode = DISABLE;//关闭扫描
ADC_Handle.Init.ContinuousConvMode = DISABLE;//关闭连续转换
ADC_Handle.Init.NbrOfConversion = 1;//需要转换的通道只有1个
ADC_Handle.Init.DiscontinuousConvMode = DISABLE;//关闭非连续转换
ADC_Handle.Init.NbrOfDiscConversion = 0;
ADC_Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;//软件触发
HAL_ADC_Init(&ADC_Handle);
ADC_ChannelConf.Channel = BAT_ADC_ADC_CHANNEL;//ADC通道
ADC_ChannelConf.Rank = 1;//ADC转换顺序
ADC_ChannelConf.SamplingTime = ADC_SAMPLETIME_55CYCLES_5;//采样时间为55.5个ADC周期
HAL_ADC_ConfigChannel(&ADC_Handle,&ADC_ChannelConf);
/**********************配置GPIO*******************/
//初始化方向控制引脚
GPIO_InitStructure.Pin = TB6612_AIN1_PIN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
HAL_GPIO_Init(TB6612_AIN1_PORT,&GPIO_InitStructure);
GPIO_InitStructure.Pin = TB6612_AIN2_PIN;
HAL_GPIO_Init(TB6612_AIN2_PORT,&GPIO_InitStructure);
GPIO_InitStructure.Pin = TB6612_BIN1_PIN;
HAL_GPIO_Init(TB6612_BIN1_PORT,&GPIO_InitStructure);
GPIO_InitStructure.Pin = TB6612_BIN2_PIN;
HAL_GPIO_Init(TB6612_BIN2_PORT,&GPIO_InitStructure);
//初始化TB6612使能引脚
GPIO_InitStructure.Pin = TB6612_STBY_PIN;
HAL_GPIO_Init(TB6612_STBY_PORT,&GPIO_InitStructure);
//初始化 PWM 输出引脚
HAL_GPIO_DeInit(TB6612_PWMA_PORT,TB6612_PWMA_PIN);
GPIO_InitStructure.Pin = TB6612_PWMA_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;//复用推挽输出
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TB6612_PWMA_PORT,&GPIO_InitStructure);
GPIO_InitStructure.Pin = TB6612_PWMB_PIN;
HAL_GPIO_Init(TB6612_PWMB_PORT,&GPIO_InitStructure);
//初始化供电电压采集引脚
HAL_GPIO_DeInit(BAT_ADC_PORT,BAT_ADC_PIN);
GPIO_InitStructure.Pin = BAT_ADC_PIN;
GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;//模拟输入
GPIO_InitStructure.Pull = GPIO_NOPULL;
HAL_GPIO_Init(BAT_ADC_PORT,&GPIO_InitStructure);
/*********************运行控制********************/
HAL_TIM_PWM_Start(&PWM_TIM_Base,TB6612_PWMA_TIM_CHANNELx);//启动PWM输出
HAL_TIM_PWM_Start(&PWM_TIM_Base,TB6612_PWMB_TIM_CHANNELx);
/*********************配置中断*******************/
//配置中断优先级
HAL_NVIC_SetPriority(BAT_ADC_IRQ,3,0);
//使能中断
HAL_NVIC_EnableIRQ(BAT_ADC_IRQ);
}
void TB6612_SetSpeed(uint8_t motor,uint8_t speed)
{
if(speed <= 100){
//电机A
if(motor == MOTOR_A){
__HAL_TIM_SET_COMPARE(&PWM_TIM_Base,TB6612_PWMA_TIM_CHANNELx,speed);
}else if (motor == MOTOR_B)
{
//电机B
__HAL_TIM_SET_COMPARE(&PWM_TIM_Base,TB6612_PWMB_TIM_CHANNELx,speed);
}
}
}
float TB6612_GetBatLevel(void)
{
float BatLevel = 0;
//触发ADC,采集电压
HAL_ADC_Start_IT(&ADC_Handle);//开始采集供电电压
//转换电压
BatLevel = (float)ADC_Value * (3.3/4096);
//返回
return BatLevel;
}
中断处理函数
/**
* @brief ADC1中断处理函数
*
*/
void ADC1_IRQHandler(void)
{
HAL_ADC_IRQHandler(&ADC_Handle);
}
/**
* @brief ADC转换完成中断回调函数(非阻塞模式)
*
* @param AdcHandle
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *AdcHandle)
{
ADC_Value = HAL_ADC_GetValue(AdcHandle);
}