STM32控制多个步进电机:基于HAL库单定时器多通道中断精准控制脉冲数+多定时器单通道中断精准控制脉冲数


前一篇文章实现了基于HAL库定时器中断的闭环步进电机驱动,并且利用了定时器中断回调实现了精准控制步进电机脉冲数。
实现控制多个步进电机的原理和方法都和单步进电机一样,在上一篇文章都有介绍到,但是在实现上可能会遇到一些问题,因此我打算在这篇博客记录一下,避免以后再踩坑。

使用到的硬件:
单片机: STM32F407ZGT6
步进电机驱动器: Emm42_V4.0步进电机闭环驱动器
步进电机: 42步进电机
(外加电源板和航模电池)

一、直接控制多个步进电机

直接控制多个步进电机的话,只要掌握了步进电机驱动器控制方法即可。
想了解可以直接跳转到上一篇文章:STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数
在该链接文章中的记录了CubeMx配置和程序解析,且附带了控制4个步进电机的代码,在文章的 三、STM32F407定时器中断控制步进电机程序 有主要初始化程序和逻辑程序的解析。

二、单定时器多通道中断精准控制脉冲数

程序实现的效果是4个步进电机同时转动,每3秒内转动一圈后停止。

1、CubeMx配置、单圈脉冲数等问题

单定时器多通道的CubeMx配置和上篇文章一样,这里就不赘述了,链接就在上面。
还有步进电机旋转一圈所需脉冲数的计算定时器初始化步进电机的使能、脉冲、方向函数设计也在上一篇文章有详细解析。

2、主要程序

继承上一篇文章 四、闭环步进电机 精准控制脉冲数 的程序,修改了中断回调函数。这里需要注意的是,我把定时器判断依据修改成了通道判断,如果使用了其他定时器的话,那么可以在通道判断前加入一个判断定时器的前提条件,即

if(htim->Instance == htim2.Instance)	// 确认是否为步进电机脉冲中断的回调。

将通道判断和内部逻辑程序写入这个判断条件内即可。

中断回调函数程序:

/* ============================== 步进电机PWM中断回调函数 ============================== */
int Motor1_PWM_num = 0;		// 电机1当前脉冲数
int Motor2_PWM_num = 0;		// 电机2当前脉冲数
int Motor3_PWM_num = 0;		// 电机3当前脉冲数
int Motor4_PWM_num = 0;		// 电机4当前脉冲数

int road_PWM_num = 3200 * 1;	// 步进电机目标脉冲数(一圈3200脉冲)

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)	// 通道1
	{
    
    
		Motor1_PWM_num ++;
		if(Motor1_PWM_num >= road_PWM_num)
		{
    
    
			Motor1_PWM_num = 0;	// 跑完目标圈数了,重置当前值
			HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)	// 通道2
	{
    
    
		Motor2_PWM_num ++;
		if(Motor2_PWM_num >= road_PWM_num)
		{
    
    
			Motor2_PWM_num = 0;	// 跑完目标圈数了,重置当前值
			HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_2);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)	// 通道3
	{
    
    
		Motor3_PWM_num ++;
		if(Motor3_PWM_num >= road_PWM_num)
		{
    
    
			Motor3_PWM_num = 0;	// 跑完目标圈数了,重置当前值
			HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_3);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_4)	// 通道4
	{
    
    
		Motor4_PWM_num ++;
		if(Motor4_PWM_num >= road_PWM_num)
		{
    
    
			Motor4_PWM_num = 0;	// 跑完目标圈数了,重置当前值
			HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_4);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
}

main程序:
在定时器初始化之后我给了单片机一个1s的延时,如果此刻不给延时就直接去驱动步进电机的话会有部分步进电机无法被唤醒的可能,因此这个延时是必要的。程序实现的功能上述有提到,程序如下。

int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  Motor_PWM_MX_TIM2_Init();		// 定时器2初始化
	
  HAL_Delay(1000);		// 必须存在的延时,可长不可太短

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	Motor1_pwm_Set(50);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1);
	Motor1_course(1);
	Motor1_enable();

	Motor2_pwm_Set(50);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_2);
	Motor2_course(1);
	Motor2_enable();

	Motor3_pwm_Set(50);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_3);
	Motor3_course(1);
	Motor3_enable();

	Motor4_pwm_Set(50);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_4);
	Motor4_course(1);
	Motor4_enable();

	HAL_Delay(3000);
  }
  /* USER CODE END 3 */
}

三、多定时器单通道中断精准控制脉冲数

1、CubeMx配置

定时器配置如下所示:
在这里插入图片描述在这里插入图片描述
定时器2、3、4、5的配置都一样,如上图所示。
还要记得开启外部晶振:
在这里插入图片描述

扫描二维码关注公众号,回复: 16345294 查看本文章

时钟数配置如下所示:
在这里插入图片描述
再配置引脚高电平输出:
在这里插入图片描述 在这里插入图片描述在这里插入图片描述
完成!

2、程序设计思路和主要程序

多定时器单通道实现多步进电机精准控制脉冲数与上述方法不同,我们不采用HAL_TIM_PWM_PulseFinishedCallback的PWM中断回调函数,而是采用HAL_TIM_PeriodElapsedCallback的TIM定时器计数溢出的中断回调函数。
该方法利用定时器产生PWM的原理,即预分频后每一次计数对应一个PWM方波,也就是说我每发送一个脉冲就会产生一次计数溢出,那么在这种定时器用于产生PWM的情况下,计数溢出次数 = 脉冲数
该方法的定时器初始化配置需要在初始化后清空标志位,即加入以下代码:

__HAL_TIM_CLEAR_IT(&htimx, TIM_IT_UPDATE);	// 清空标志位
// htimx对应下文中的htim2、htim3、htim4、htim5

总共4个定时器,则要分别添加4个定时器的标志位清空。
同样的,定时器中断的开启和停止函数都要替换成以下两个函数:

HAL_TIM_Base_Start_IT(&htimx);	// 中断开启
HAL_TIM_Base_Stop_IT(&htimx);	// 中断停止
// htimx对应下文中的htim2、htim3、htim4、htim5

中断回调函数程序:

/* ============================== 步进电机PWM中断回调函数 ============================== */
int Motor1_PWM_num = 0;		// 电机1当前脉冲数
int Motor2_PWM_num = 0;		// 电机2当前脉冲数
int Motor3_PWM_num = 0;		// 电机3当前脉冲数
int Motor4_PWM_num = 0;		// 电机4当前脉冲数

int road_PWM_num = 3200 * 1;	// 步进电机目标脉冲数(一圈3200脉冲)

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(htim->Instance == TIM2)	// 定时器2(步进电机1PWM)
	{
    
    
		Motor1_PWM_num ++;
		if(Motor1_PWM_num >= road_PWM_num)
		{
    
    
			Motor1_PWM_num = 0;
			HAL_TIM_Base_Stop_IT(&htim2);
			HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Instance == TIM3)	// 定时器3(步进电机2PWM)
	{
    
    
		Motor2_PWM_num ++;
		if(Motor2_PWM_num >= road_PWM_num)
		{
    
    
			Motor2_PWM_num = 0;
			HAL_TIM_Base_Stop_IT(&htim3);
			HAL_TIM_PWM_Stop(&htim3, TIM_CHANNEL_1);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Instance == TIM4)	// 定时器4(步进电机3PWM)
	{
    
    
		Motor3_PWM_num ++;
		if(Motor3_PWM_num >= road_PWM_num)
		{
    
    
			Motor3_PWM_num = 0;
			HAL_TIM_Base_Stop_IT(&htim4);
			HAL_TIM_PWM_Stop(&htim4, TIM_CHANNEL_1);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
	if(htim->Instance == TIM5)	// 定时器5(步进电机4PWM)
	{
    
    
		Motor4_PWM_num ++;
		if(Motor4_PWM_num >= road_PWM_num)
		{
    
    
			Motor4_PWM_num = 0;
			HAL_TIM_Base_Stop_IT(&htim5);
			HAL_TIM_PWM_Stop(&htim5, TIM_CHANNEL_1);	// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);	// 点灯(调试用的,不管)
		}
	}
}

在编写main函数之前,还需要修改步进电机PWM设置函数,以步进电机1为例:

/* ============================== 步进电机脉冲设置 ============================== */

void Motor1_pwm_Set(int motor1_n)
{
    
    
   Motor1_sConfigOC.Pulse = motor1_n;
   HAL_TIM_PWM_ConfigChannel(&htim2, &Motor1_sConfigOC, TIM_CHANNEL_1);
}

main程序:
同样,在初始化后也要加上一小段延时防止步进电机激活失败。
程序实现的功能和单定时器多通道一样,在3秒内步进电机都精准地转动一圈后停止,不断循环。

int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  Motor1_TIM2_Init();
  Motor2_TIM3_Init();
  Motor3_TIM4_Init();
  Motor4_TIM5_Init();

  HAL_Delay(1000);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		Motor1_pwm_Set(50);
		HAL_TIM_Base_Start_IT(&htim2);
		HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
		Motor1_enable();
		
		Motor2_pwm_Set(50);
		HAL_TIM_Base_Start_IT(&htim3);
		HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
		Motor2_course(1);
		Motor2_enable();
		
		Motor3_pwm_Set(50);
		HAL_TIM_Base_Start_IT(&htim4);
		HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
		Motor3_course(1);
		Motor3_enable();
		
		Motor4_pwm_Set(50);
		HAL_TIM_Base_Start_IT(&htim5);
		HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1);
		Motor4_course(1);
		Motor4_enable();
		
		HAL_Delay(3000);
  }
  /* USER CODE END 3 */
}

四、程序链接

程序已经打包好上传到csdn的资源里了。
CSDN:基于HAL库的单定时器多通道中断精准控制脉冲数(4个步进电机)
CSDN:基于HAL库的多定时器单通道中断精准控制脉冲数(4个步进电机)
也可以通过以下链接下载:

基于HAL库的单定时器多通道中断精准控制脉冲数(4个步进电机):
链接:https://pan.baidu.com/s/1uQG8ll_PjbqnKCArs-p0lQ
提取码:8rdk
基于HAL库的多定时器单通道中断精准控制脉冲数(4个步进电机):
链接:https://pan.baidu.com/s/1tYNfGQKdSbJEK-Tf7GAb9A
提取码:du7b

本人是一名学生,目前正在学习中,本篇文章也算是我的学习笔记,如有错误的话还请指正。

猜你喜欢

转载自blog.csdn.net/xztli/article/details/127201347