往期内容
STM32F1基于HAL库的学习记录实用使用教程分享(一、GPIO_Output)
STM32F1基于HAL库的学习记录实用使用教程分享(二、GPIO_Input 按键)
STM32F1基于HAL库的学习记录实用使用教程分享(三、外部中断 按键)
STM32F1基于HAL库的学习记录实用使用教程分享(四、OLED IIC驱动 软件IIC 硬件IIC)
STM32F1基于HAL库的学习记录实用使用教程分享(五、PWM驱动舵机、呼吸灯)
文章目录
前言
本文记录了通过使用TB6612电机驱动模块驱动电机的学习
随着嵌入式行业的兴起,于此记录个人的一些学习过程并进行分享,本次是关于电机驱动模块的驱动。
声明:本文章在编写过程中,我始终致力于尊重并保护所有原创内容及其知识产权。然而,由于信息来源的多样性和复杂性,可能存在个别内容未明确标注出处、存在事实性错误或无意中侵犯了他人的知识产权的情况。对于任何可能存在的上述问题,我深感歉意,并在此提前向受影响的作者表示最诚挚的歉意。我始终秉持着尊重原创、维护知识产权的原则,绝无意侵犯任何人的合法权益。一旦收到您的反馈,我将立即核实并在第一时间内对文章进行修改。这包括但不限于补充相关引用信息、更正错误内容或删除涉嫌侵权的内容。再次感谢您的关注与支持,期待与您共同营造一个更加美好的知识共享空间。新手文章诸多不足,还望海涵。
一、TB6612&&霍尔编码器
TB6612
- 双H桥设计:每通道可输出1.2A持续电流的精密电流走廊内置两组独立全桥电路,支持两路直流电机或一路步进电机
- 电气参数:
- 最大输出电流:1.2A(持续)/3.2A(峰值)
- 工作电压范围:2.5-13.5V
- 低导通电阻:0.3Ω(上桥臂+下桥臂)
- 保护机制:
- 过热关机(TSD)
- 低压检测(UVLO)
- 短路保护
- PWM调速:通过占空比调节实现细腻的扭矩控制
- 待机模式:STBY引脚的巧妙运用可降低系统功耗
2.2 控制逻辑全解
输入信号组合 | 电机状态 | 电流路径 |
---|---|---|
AIN1=H | 正转 | VM→OUT1→电机→OUT2→GND |
AIN1=L | 反转 | VM→OUT2→电机→OUT1→GND |
AIN1=AIN2 | 刹车 | 电机两端短接 |
STBY=L | 待机 | 所有MOSFET关断 |
PWM调速原理:
// 典型PWM频率设置(TIM8通道1)
htim8.Instance->ARR = 99; // 周期值=100-1
htim8.Instance->PSC = 71; // 72MHz/(71+1)=1MHz → 10kHz PWM
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, duty); // 0-99对应0%-99%占空比
霍尔编码器
- 正交脉冲:A/B相信号相位差90度的位置密码
- 四倍频技术:在每周期4个边沿处捕捉运动的微妙变化
- 线数与减速比:13线编码器与30:1减速比构成精密传动系
定时器配置
定时器 | 功能 | 频率 | 工作模式 |
---|---|---|---|
TIM1 | 编码器接口A | - | Encoder Mode |
TIM6 | 系统时基 | 500Hz | Base Timer |
TIM8 | PWM生成 | 20kHz | PWM Generation |
速度换算公式的物理意义
速度计算公式如下:
v
=
N
×
f
R
×
P
×
4
v = \frac{N \times f}{R \times P \times 4}
v=R×P×4N×f
参数定义:
- N:编码器计数值(脉冲数)
- f:采样频率(100Hz,来自10ms周期)
- R:减速比(30:1)
- P:编码器线数(13线)
- 4:四倍频因子(编码器信号边缘检测)
公式解析
- 分子(N×f):
表示单位时间内编码器输出的总脉冲数,反映电机实际转速。 - 分母(R×P×4):
- R:减速比将电机转速转换为输出轴转速;
- P×4:编码器每转输出的等效脉冲数(线数×四倍频)。
- 物理意义:
公式将编码器脉冲信号转换为输出轴的实际线速度(单位:转/秒或米/秒)。
二、配置
在选择好芯片并进入配置界面后的操作如下进行
1.RCC
2.SYS
3.时钟树
以上三个部分再前三期文章中都已赘述过,而且也很基础,故在从今以后的文章中都会省略,有需要的同学可翻阅往期文章查阅。
STM32F1基于HAL库的学习记录实用使用教程分享(一、GPIO_Output)
4.引脚配置
定时器参数设置:
- Prescaler:1440-1 → 分频后时钟 = 72MHz / (1440-1) = 1/20MHz
- Counter Mode:Up(向上计数)
- AutoReload:99 → PWM频率 = 1MHz / (99+1) = 10kHz
- Pulse:初始占空比(例如500,对应50%)
三、代码
#include "motor.h"
#include "tim.h"
extern float MotorASpeed ;
extern float MotorBSpeed ;
extern float MotorCSpeed ;
extern float MotorDSpeed ;
/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:PWM
返回 值:无
**************************************************************************/
void Motor_Set(int motor_a,int motor_b,int motor_c,int motor_d)//赋值给PWM寄存器,+为向前;-为向后
{
if(motor_a>0)
{
AIN1_SET();
AIN2_RESET();
}
else
{
AIN1_RESET();
AIN2_SET();
}
if(motor_b>0)
{
BIN1_RESET();
BIN2_SET();
}
else
{
BIN1_SET();
BIN2_RESET();
}
if(motor_c>0)
{
CIN1_RESET();
CIN2_SET();
}
else
{
CIN1_SET();
CIN2_RESET();
}
if(motor_d>0)
{
DIN1_SET();
DIN2_RESET();
}
else
{
DIN1_RESET();
DIN2_SET();
}
if(motor_a==0)
{
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, 0);
}
else if(motor_a <0)
{
if(motor_a <-99) motor_a =-99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2, -motor_a);
}
else
{
if(motor_a >99) motor_a = 99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_2,motor_a);
}
if(motor_b==0)
{
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_4, 0);
}
else if(motor_b <0)
{
if(motor_b <-99) motor_b =-99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_4, -motor_b);
}
else
{
if(motor_b >99) motor_b = 99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_4,motor_b);
}
if(motor_c==0)
{
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, 0);
}
else if(motor_c <0)
{
if(motor_c <-99) motor_c =-99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3, -motor_c);
}
else
{
if(motor_c >99) motor_c = 99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_3,motor_c);
}
if(motor_d==0)
{
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, 0);
}
else if(motor_d <0)
{
if(motor_d <-99) motor_d =-99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1, -motor_d);
}
else
{
if(motor_d >99) motor_d = 99;
__HAL_TIM_SET_COMPARE(&htim8, TIM_CHANNEL_1,motor_d);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim6)//htim6 500HZ 2ms中断一次
{
TimerCount++;//每次进入中断 、中断计数变量递增
if(TimerCount %5 == 0)//每10ms 执行一次
{
EncodeACount = (short)__HAL_TIM_GET_COUNTER(&htim1);//获得当前编码器计数值并赋值 (short):是类型转化 -:是因为电机对侧安装
EncodeCCount = -(short)__HAL_TIM_GET_COUNTER(&htim2);
EncodeDCount = -(short)__HAL_TIM_GET_COUNTER(&htim3);//获得当前编码器计数值并赋值 (short):是类型转化 -:是因为电机对侧安装
EncodeBCount = (short)__HAL_TIM_GET_COUNTER(&htim4);
__HAL_TIM_SET_COUNTER(&htim1,0);//每次获得编码器计数值后都清零,这样每次计数值就是变化量
__HAL_TIM_SET_COUNTER(&htim2,0);//每次获得编码器计数值后都清零,这样每次计数值就是变化量
__HAL_TIM_SET_COUNTER(&htim3,0);//每次获得编码器计数值后都清零,这样每次计数值就是变化量
__HAL_TIM_SET_COUNTER(&htim4,0);//每次获得编码器计数值后都清零,这样每次计数值就是变化量
/* 电机速度速度 = 编码器计数值*编码器读取频率/减速比/编码器线数/4倍频 */
MotorASpeed = (float)EncodeACount*100/30/13/4;
MotorBSpeed = (float)EncodeBCount*100/30/13/4;
MotorCSpeed = (float)EncodeCCount*100/30/13/4;
MotorDSpeed = (float)EncodeDCount*100/30/13/4;
}
if(TimerCount %10 == 0)//每20ms执行一次
{
/*里程 += 时间*电机速度*周长*/
Mileage += 0.02*MotorASpeed*22;
/*控制电机转速*/
TimerCount=0;
Timer_20msFlag=1;
}
}
}
//别忘记启动定时器
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_1);//开启pwm输出
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);//开启pwm输出
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3);//开启pwm输出
HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_4);//开启pwm输出
HAL_TIM_Encoder_Start(&htim1,TIM_CHANNEL_ALL); //开启定时器1
HAL_TIM_Encoder_Start(&htim2,TIM_CHANNEL_ALL); //开启定时器2
HAL_TIM_Encoder_Start(&htim3,TIM_CHANNEL_ALL); //开启定时器3
HAL_TIM_Encoder_Start(&htim4,TIM_CHANNEL_ALL); //开启定时器4
HAL_TIM_Encoder_Start(&htim6,TIM_CHANNEL_ALL); //开启定时器6
HAL_TIM_Encoder_Start(&htim7,TIM_CHANNEL_ALL); //开启定时器7
HAL_TIM_Base_Start_IT(&htim1); //开启定时器1 中断
HAL_TIM_Base_Start_IT(&htim2); //开启定时器2 中断
HAL_TIM_Base_Start_IT(&htim3); //开启定时器3 中断
HAL_TIM_Base_Start_IT(&htim4); //开启定时器4 中断
HAL_TIM_Base_Start_IT(&htim6); //开启定时器4 中断
HAL_TIM_Base_Start_IT(&htim8); //开启定时器8 中断
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
Motor_Set(30,30,30,30);
}
/* USER CODE END 3 */
四、演示效果
霍尔编码器读数
总结
本篇内容略少,但这也是为后期的内容做铺垫衔接。后面的内容“干货”会更多一些,敬请期待。
又是经过一个充实而忙碌的夜晚,深知文章中仍不免有诸多省略和未及细讲之处。对于那些在阅读过程中仍感意犹未尽、存有疑问的同学,建议大家不妨拓宽视野,多阅读几篇来自不同作者、风格各异的优秀文章。相信通过多角度、多层次的学习,你们定能集百家之长从而对单片机有更加深入和全面的理解。
在此,我的初衷不仅是为初学者提供一份学习路上的指引,也是对自己学习历程的一次回顾与总结。通过分享,我希望能激发更多人对单片机技术的兴趣,共同探索这片充满挑战与机遇的领域。
为了保持内容的连贯性和避免不必要的重复,我计划在未来的博客中,对于本文中已提及但未深入展开的话题,将通过链接的方式引导大家回到本文进行查阅。同时,我也将不断优化和完善文章内容,力求为大家提供更加准确、全面、易于理解的学习资源。
感谢大家的关注与支持!接下来我可能新开一个栏目讲解舵机机械臂的控制