STM32F1基于HAL库的学习记录实用使用教程分享(六、TB6612电机驱动模块、霍尔编码器计数、编码器计算速度、定时器)

往期内容

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系统时基500HzBase Timer
TIM8PWM生成20kHzPWM 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 */

四、演示效果

霍尔编码器读数

总结

本篇内容略少,但这也是为后期的内容做铺垫衔接。后面的内容“干货”会更多一些,敬请期待。

又是经过一个充实而忙碌的夜晚,深知文章中仍不免有诸多省略和未及细讲之处。对于那些在阅读过程中仍感意犹未尽、存有疑问的同学,建议大家不妨拓宽视野,多阅读几篇来自不同作者、风格各异的优秀文章。相信通过多角度、多层次的学习,你们定能集百家之长从而对单片机有更加深入和全面的理解。

在此,我的初衷不仅是为初学者提供一份学习路上的指引,也是对自己学习历程的一次回顾与总结。通过分享,我希望能激发更多人对单片机技术的兴趣,共同探索这片充满挑战与机遇的领域。

为了保持内容的连贯性和避免不必要的重复,我计划在未来的博客中,对于本文中已提及但未深入展开的话题,将通过链接的方式引导大家回到本文进行查阅。同时,我也将不断优化和完善文章内容,力求为大家提供更加准确、全面、易于理解的学习资源。

感谢大家的关注与支持!接下来我可能新开一个栏目讲解舵机机械臂的控制


参考文献引用

STM32F103C8T6+TB6612+直流电机PID控制

TB6612电机驱动模块(STM32)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

藤樂.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值