这篇文章很早就写了,但是今天才发现没发出来

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

接线

引脚名 分配网络名 使用的功能
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);
}
最后修改:2024 年 04 月 25 日
如果觉得我的文章对你有用,请随意赞赏