基于立创GD32E230C8T6开发板的TB6612电机驱动模块移植与PWM调速实战
基于立创GD32E230C8T6开发板的TB6612电机驱动模块移植与PWM调速实战最近在做一个智能小车项目需要用到电机驱动对比了经典的L298N和TB6612后我果断选择了后者。TB6612效率更高、发热更小体积也小巧很多非常适合咱们这种嵌入式项目。但网上很多资料都是基于STM32或者Arduino的直接用立创的GD32E230开发板来驱动TB6612的完整教程不多。今天我就把我在GD32E230C8T6上成功移植并实现PWM调速的整个过程手把手分享给大家让你少走弯路。这篇文章就是一份详细的移植指南从硬件接线到软件配置再到PWM调速代码一步步带你搞定。无论你是刚开始接触GD32的学生还是正在做电机控制项目的工程师跟着做一遍就能让你的电机转起来并且想快就快想慢就慢。1. 认识我们的“主角”TB6612电机驱动模块在动手写代码之前咱们得先搞清楚要驱动的对象是什么。TB6612是一个双路直流电机驱动芯片简单理解它就是单片机和电机之间的“翻译官”和“大力士”。“翻译官”单片机只能输出微弱的数字信号0或3.3V而电机需要较大的电流比如几百毫安才能转动。TB6612接收单片机微弱的控制信号然后输出足够强的电流去驱动电机。“大力士”它能同时驱动两个直流电机每个通道最大能提供1.2A的持续电流瞬间能到3.2A驱动咱们玩具小车上的小电机绰绰有余。相比以前常用的L298NTB6612最大的优点是效率高、发热少。L298N是线性驱动工作时芯片本身会消耗不少功率变成热量而TB6612是MOSFET H桥驱动效率高得多基本不发热这样你的小车电池就能用更久。模块上有几个关键的引脚需要记住引脚名称作用说明注意事项VM电机电源输入接外部电源最高不要超过12V根据你的电机额定电压来。VCC逻辑电源输入接单片机的3.3V或5V给芯片内部逻辑电路供电。原文提到范围是2.7V~5.5V咱们的GD32开发板是3.3V系统直接接3.3V就行。GND电源地务必和单片机、电机电源共地STBY待机控制高电平接VCC模块才工作低电平接GND所有输出关闭电机停止。AIN1/AIN2A电机控制输入1和2接单片机的两个GPIO控制A电机的正反转和刹车。BIN1/BIN2B电机控制输入1和2控制B电机用法同A。PWMA/PWMBA/B电机PWM速度控制接单片机的PWM输出引脚通过调节PWM占空比来控制电机转速。AO1/AO2A电机输出接A电机的两根线。BO1/BO2B电机输出接B电机的两根线。注意接线时一定要细心再细心特别是电源正负极不要接反否则芯片可能瞬间损坏。2. 硬件连接把开发板和TB6612“手拉手”现在咱们把立创GD32E230C8T6开发板和TB6612模块连接起来。根据原文的代码我们这次先实现驱动一个电机A电机。连接步骤如下电源连接将开发板的3.3V引脚连接到TB6612的VCC。将开发板的GND引脚连接到TB6612的GND。将外部电机电源比如电池盒的7.4V的正极接到TB6612的VM负极接到GND与开发板共地。控制信号连接将开发板的PB0引脚连接到TB6612的AIN1。将开发板的PB1引脚连接到TB6612的AIN2。将开发板的PA6引脚连接到TB6612的PWMA。将TB6612的STBY引脚直接连接到3.3V使其一直处于工作状态。电机连接将你的直流电机的两根线随意连接到TB6612的AO1和AO2。如果转向和你预期相反交换这两根线即可。为什么选这几个引脚这是由原文代码和GD32E230的硬件资源决定的。PA6引脚有定时器2通道0TIMER2_CH0的复用功能可以输出PWM。PB0和PB1就是普通的GPIO用来控制方向。3. 软件工程搭建与代码移植硬件连好了接下来就是重头戏——软件部分。咱们的目标是把TB6612的驱动程序移植到你的GD32工程里。3.1 创建驱动文件首先在你的工程目录下比如User或BSP文件夹里新建两个文件bsp_tb6612.cbsp_tb6612.h这两个文件将包含所有控制TB6612的代码。把驱动逻辑独立出来是个好习惯以后换到别的项目或者别的板子上直接拷贝这两个文件改改引脚定义就能用。3.2 编写头文件 (bsp_tb6612.h)头文件主要做两件事引脚宏定义和函数声明。咱们根据之前的硬件连接来定义。#ifndef _BSP_TB6612_H #define _BSP_TB6612_H #include gd32e23x.h #include systick.h /* A电机方向控制引脚定义 */ #define RCU_AIN1 RCU_GPIOB #define PORT_AIN1 GPIOB #define GPIO_AIN1 GPIO_PIN_0 #define RCU_AIN2 RCU_GPIOB #define PORT_AIN2 GPIOB #define GPIO_AIN2 GPIO_PIN_1 /* A电机PWM引脚定义 (PA6 - TIMER2_CH0) */ #define RCU_PWMA RCU_GPIOA #define PORT_PWMA GPIOA #define GPIO_PWMA GPIO_PIN_6 #define AF_PWMA GPIO_AF_1 // PA6的复用功能AF1对应TIMER2 #define RCU_PWMA_TIMER RCU_TIMER2 #define BSP_PWMA_TIMER TIMER2 #define BSP_PWMA_CHANNEL TIMER_CH_0 /* 方向控制引脚输出宏函数方便使用 */ #define AIN1_OUT(X) gpio_bit_write(PORT_AIN1, GPIO_AIN1, (X)?SET:RESET) #define AIN2_OUT(X) gpio_bit_write(PORT_AIN2, GPIO_AIN2, (X)?SET:RESET) /* 函数声明 */ void TB6612_Init(uint16_t pre, uint16_t per); // 初始化函数 void AO_Control(uint8_t dir, uint32_t speed); // A电机控制函数 #endif /* _BSP_TB6612_H */代码解读RCU_xxx是时钟使能宏告诉单片机要使用哪个GPIO端口的时钟。PORT_xxx和GPIO_PIN_xx指定具体的端口和引脚。AF_PWMA定义了PA6引脚用于PWM输出时需要选择的复用功能编号这个值在GD32的数据手册里可以查到。AIN1_OUT(X)是一个宏函数X为1时输出高电平为0时输出低电平这样后面控制方向时代码更简洁。提示原文代码里还有B电机的定义但被注释掉了。我们这次只用一个电机所以先专注于A电机部分。如果你需要驱动两个电机把对应的注释取消并连接好BIN1、BIN2和PWMB引脚即可。3.3 编写源文件 (bsp_tb6612.c)这是驱动的核心包含了初始化和控制函数。#include bsp_tb6612.h /** * brief TB6612初始化函数 * param pre: 定时器预分频值 * param per: 定时器周期值 * note PWM频率计算公式PWM_Freq 系统时钟 / ((pre1) * (per1)) * 例如系统时钟72MHz, pre71, per999则PWM频率 72M / (72*1000) 1kHz */ void TB6612_Init(uint16_t pre, uint16_t per) { timer_parameter_struct timer_initpara {0}; // 定时器基本参数结构体 timer_oc_parameter_struct timer_ocinitpara {0}; // 定时器输出比较PWM参数结构体 /* 1. 开启相关外设时钟 */ rcu_periph_clock_enable(RCU_AIN1); // 使能AIN1 (PB0) 的GPIO时钟 rcu_periph_clock_enable(RCU_AIN2); // 使能AIN2 (PB1) 的GPIO时钟 rcu_periph_clock_enable(RCU_PWMA); // 使能PWMA (PA6) 的GPIO时钟 rcu_periph_clock_enable(RCU_PWMA_TIMER); // 使能TIMER2时钟 /* 2. 配置PWMA引脚(PA6)为复用推挽输出 */ gpio_mode_set(PORT_PWMA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PWMA); gpio_output_options_set(PORT_PWMA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PWMA); gpio_af_set(PORT_PWMA, AF_PWMA, GPIO_PWMA); // 将PA6复用为TIMER2_CH0 /* 3. 配置AIN1和AIN2引脚为普通推挽输出 */ gpio_mode_set(PORT_AIN1, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_AIN1); gpio_output_options_set(PORT_AIN1, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_AIN1); gpio_mode_set(PORT_AIN2, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_AIN2); gpio_output_options_set(PORT_AIN2, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_AIN2); /* 4. 配置定时器TIMER2产生PWM */ timer_deinit(BSP_PWMA_TIMER); // 先复位定时器 timer_struct_para_init(timer_initpara); // 初始化结构体 // 配置定时器参数 timer_initpara.prescaler pre - 1; // 预分频值 timer_initpara.alignedmode TIMER_COUNTER_EDGE; // 边沿对齐模式 timer_initpara.counterdirection TIMER_COUNTER_UP; // 向上计数 timer_initpara.period per - 1; // 自动重装载值周期 timer_initpara.clockdivision TIMER_CKDIV_DIV1; // 时钟分频 timer_initpara.repetitioncounter 0; // 重复计数器高级定时器用 timer_init(BSP_PWMA_TIMER, timer_initpara); // 初始化定时器 /* 5. 配置定时器通道0为PWM模式 */ timer_channel_output_struct_para_init(timer_ocinitpara); timer_ocinitpara.ocpolarity TIMER_OC_POLARITY_HIGH; // 输出极性高 timer_ocinitpara.outputstate TIMER_CCX_ENABLE; // 使能通道输出 timer_channel_output_config(BSP_PWMA_TIMER, BSP_PWMA_CHANNEL, timer_ocinitpara); // 设置初始PWM脉冲值占空比这里先设一个中间值后续可调 timer_channel_output_pulse_value_config(BSP_PWMA_TIMER, BSP_PWMA_CHANNEL, per / 2); // 配置为PWM模式0 timer_channel_output_mode_config(BSP_PWMA_TIMER, BSP_PWMA_CHANNEL, TIMER_OC_MODE_PWM0); // 使能定时器自动重装载影子寄存器保证参数更新同步 timer_auto_reload_shadow_enable(BSP_PWMA_TIMER); /* 6. 使能定时器开始输出PWM */ timer_enable(BSP_PWMA_TIMER); } /** * brief 控制A电机转动 * param dir: 方向1正转0反转 * param speed: 速度值范围 0 ~ (per-1)。0最慢停止(per-1)最快。 */ void AO_Control(uint8_t dir, uint32_t speed) { // 控制方向根据TB6612真值表AIN10, AIN21 为正转AIN11, AIN20 为反转 if(dir 1) // 正转 { AIN1_OUT(0); AIN2_OUT(1); } else // 反转 { AIN1_OUT(1); AIN2_OUT(0); } // 更新PWM占空比控制速度 timer_channel_output_pulse_value_config(BSP_PWMA_TIMER, BSP_PWMA_CHANNEL, speed); }关键点解析PWM频率计算TB6612_Init函数的两个参数pre预分频和per周期共同决定了PWM的频率。公式是PWM频率 系统主频 / ((pre1) * (per1))。比如原文例子中pre72,per16000系统主频72MHz计算出的PWM频率约为62.5Hz。这个频率对于电机调速来说偏低可能会听到电机啸叫实际项目中可以适当调高比如1kHz到20kHz之间。方向控制逻辑在AO_Control函数里我们通过设置AIN1和AIN2的高低电平组合来控制电机的正反转。这是根据TB6612芯片的数据手册真值表来的。速度控制速度参数speed直接设置到定时器的通道捕获比较寄存器timer_channel_output_pulse_value_config。这个值决定了PWM信号中高电平的宽度从而改变电机的平均电压实现调速。speed为0时占空比0%电机停止speed为per-1时占空比接近100%电机全速。4. 在主函数中调用与测试驱动写好了最后就是在主函数里调用它让电机转起来看看效果。#include gd32e23x.h #include systick.h #include bsp_tb6612.h int main(void) { uint32_t motor_speed 0; uint8_t direction 1; // 1-正转 0-反转 // 系统时钟和滴答定时器初始化用于延时 systick_config(); // 初始化TB6612驱动 // 参数72和16000产生约62.5Hz的PWM先测试用 TB6612_Init(72, 16000); while(1) { // 示例1让电机速度由慢到快循环 for(motor_speed 0; motor_speed 16000; motor_speed 500) { AO_Control(direction, motor_speed); delay_1ms(100); // 延时100ms观察速度变化 } // 停一下 AO_Control(direction, 0); delay_1ms(1000); // 切换方向 direction !direction; // 示例2固定几个速度点测试 // AO_Control(1, 4000); // 低速正转 // delay_1ms(2000); // AO_Control(1, 12000); // 中速正转 // delay_1ms(2000); // AO_Control(1, 0); // 停止 // delay_1ms(1000); // AO_Control(0, 8000); // 中速反转 // delay_1ms(2000); } }下载并测试将上面的代码编译后下载到GD32E230开发板。确保硬件连接正确特别是电源。上电后你应该能看到电机开始缓慢启动速度逐渐增加到达最高速后停止然后反方向重复这个过程。如果电机不转别着急按以下步骤排查查电源用万用表测量TB6612的VCC和VM引脚电压是否正常。查信号用示波器或者逻辑分析仪查看PA6引脚是否有PWM波形输出PB0/PB1的电平是否随程序变化。查代码检查引脚定义是否和你的实际连接一致初始化函数是否被成功调用。到这里你已经成功在立创GD32E230C8T6开发板上驱动了TB6612模块并实现了PWM调速。你可以尝试修改TB6612_Init中的pre和per参数来调整PWM频率或者修改AO_Control中的speed值来获得不同的转速应用到你的小车、机械臂或者其他有趣的项目中去了。