STM32控制步进电机:基于HAL库定时器中断的闭环步进电机驱动+精准控制脉冲数

一、步进电机闭环驱动器

该篇文章中用到的步进电机闭环驱动器为Emm42_V4.0步进电机闭环驱动器。该闭环驱动器自带FOC矢量闭环控制算法,能实现力矩、速度、位置三环控制。
如下图所示,该42步进闭环电机驱动器的A+、A-、B+、B-连接步进电机,通过右侧的使能、脉冲、方向端对步进电机进行驱动控制。

二、CubeMx配置

首先要进行时钟配置。

1、Clock Configuration

在这里插入图片描述
配置时钟前要先打开Pinout & Configuration中的RCC,选择Crystal/Ceramic Resonator开启外部晶振。
再根据自己单片机的外部晶振频率设定参数,如下图所示。
在这里插入图片描述

2、脉冲端 定时器配置

再次回到Pinout & Configuration,在Timers中选择一个你想要的定时器,这里我选择的是定时器2,然后打开内部时钟(Clock Source:Intermal Clock)。
在配置通道功能为PWM输出(PWM Generation)。这里我使用了4个步进电机,因此选择了4个通道。(下文视频只演示了一个步进电机,其他步进电机也是一样的道理,接线再补充程序就ok了)
再打开NVIC Settings使能定时器中断。如下图所示。
在这里插入图片描述
接着,要根据你上一步设置的时钟和你的驱动器所能承载的频率,来设置该定时器的频率。
如我使用的驱动器最大脉冲频率是120KHZ,最高转速是2200+转,即我想要达到最高转速,可以配置1.8°步距角的步进电机为16细分,也就是(360 ÷ 1.8) × 16 = 3200个脉冲转一圈(这个下文控制脉冲数会提到),再用120K ÷ 3200 = 37.5转/s,即每分钟2250转。而如下图所示,我的tim2属于APB1,时钟设置是84MHZ。
在这里插入图片描述
我使用的单片机为STM32F407,因此此处APB1总线对应了通用定时器tim2 ~ 5,基本定时器tim6、tim7,通用定时器tim12~14。APB2总线对应了高级定时器tim1、tim8,通用定时器tim9 ~ 11。
所以这里千万千万要小心不要把APB1的定时器时钟频率错当用成APB2的时钟频率了。
因此,以我的配置为例,使用84M ÷ 120K = 700,再用这个700对预分频系数和计数周期进行分配,如下图所示。这里为了后面设置PWM占空比的方便,把计数周期定为100,那么PSC则为7。
公式:PWM输出频率 = 定时器时钟频率 ÷ ((psc+1) × (arr + 1))。
在这里插入图片描述

来用示波器检查一下频率和波形有没有问题,这里用到的程序在下面会详细讲到,占空比50%,频率120KHZ。
在这里插入图片描述

3、使能、方向端 引脚配置

如下图所示,左键你想要使用的引脚配置为输出即可,非常简单。这里我使用了PC0-4作为使能端,PG0-4作为方向端,可根据需求选择不会占用你所需功能的引脚作为单纯的高低电平输出引脚。
在这里插入图片描述
然后再进入Project Manager配置工程名字和编译环境、生成.c、.h文件等就ok了。

三、STM32F407定时器中断控制步进电机程序

先上张引脚配置图,程序看起来更清晰一点。

0、引脚配置图

引脚配置如下所示。
在这里插入图片描述

1、使用到的HAL库函数

这两个函数的功能下文会有解释。

扫描二维码关注公众号,回复: 16345296 查看本文章
HAL_TIM_PWM_Stop(&htimx,TIM_CHANNEL_x);
HAL_TIM_PWM_Start(&htimx,TIM_CHANNEL_x);

2、脉冲触发定时器初始化配置

在CubeMx生成的工程中,只需根据之前设置的定时器找到初始化程序,然后在程序最后加入以下程序即可,但是要根据自己配置的定时器和通道进行参数配置。如设置的为定时器2,则Ctrl F搜索MX_TIM2_Init即可。

// 添加程序
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4);

HAL_TIM_PWM_Start函数是启动PWM输出的函数,如上所示对应定时器2的4个通道。如下所示是我根据HAL库生成的初始修改后的初始化函数,把sConfigOC提出来是为了方便下面编写PWM设置函数,这两步都是必要的。

/* ============================== 步进电机脉冲触发定时器初始化 ============================== */

TIM_HandleTypeDef htim2;
TIM_OC_InitTypeDef Motor_PWM_sConfigOC = {
    
    0};

/* TIM2 init function */
void Motor_PWM_MX_TIM2_Init(void)
{
    
    

  TIM_ClockConfigTypeDef sClockSourceConfig = {
    
    0};
  TIM_MasterConfigTypeDef sMasterConfig = {
    
    0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 7-1;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 100-1;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  Motor_PWM_sConfigOC.OCMode = TIM_OCMODE_PWM1;
  Motor_PWM_sConfigOC.Pulse = 0;
  Motor_PWM_sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  Motor_PWM_sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &Motor_PWM_sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &Motor_PWM_sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &Motor_PWM_sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &Motor_PWM_sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    
    
    Error_Handler();
  }

  HAL_TIM_MspPostInit(&htim2);
	
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4);

}

3、步进电机脉冲设置

如下所示,拿步进电机1举例,先停止定时器2通道1的PWM输出,然后设置占空比,配置通道后再次开启PWM即可。HAL_TIM_PWM_Stop为停止定时器x通道x的PWM输出的函数。

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

4、步进电机使能、失能、改变方向

如下所示,配置高低电平即可。

void Motor1_enable(void)			// 电机1使能
{
    
    
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);
}
void Motor1_disable(void)			// 电机1失能
{
    
    
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);
}
void Motor1_course(char course_mod)			// 电机1改变方向
{
    
    
	if(course_mod == 1){
    
    HAL_GPIO_WritePin(GPIOG, GPIO_PIN_0, GPIO_PIN_SET);}		// 正转
	else if(course_mod == 0){
    
    HAL_GPIO_WritePin(GPIOG, GPIO_PIN_0, GPIO_PIN_RESET);}			// 反转
}

5、步进电机正转main.c程序

启动步进电机1,设置占空比为50%,正转。

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();
	Motor_PWM_MX_TIM2_Init();	// 定时器初始化

	Motor1_pwm_Set(50);		// 设置占空比为50%(50/100)
	Motor1_course(1);		// 正转
	Motor1_enable();		// 使能
  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}

6、视频效果演示

步进电机控制

四、闭环步进电机 精准控制脉冲数

1、使用到的HAL库函数

同样的,这三个函数的功能下文也会有解释。

HAL_TIM_PWM_Start_IT(&htimx, TIM_CHANNEL_x);
HAL_TIM_PWM_Stop_IT(&htimx, TIM_CHANNEL_x);
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim);

2、更改步进电机驱动脉冲频率

首先设置步进电机细分步数为16,如下图所示,此处使用的是步距角为1.8°的步进电机。在这种参数配置的情况下,发送16个脉冲可转动1.8°,因此转动一圈所需的脉冲为(360° ÷ 1.8°) × 16 = 3200个脉冲。
在这里插入图片描述
基于上文的步进电机驱动程序,我想把转速改为大概5转/s这样子,所以我们应当更改一下脉冲频率120KHZ。利用上述的一转3200脉冲,那么5转就需要16000个脉冲,因此将120KHZ改为16KHZ即可。那再计算一次PSC就是(84M ÷ 16K) ÷100 = 52.5。
将上文步进电机脉冲触发定时器初始化Motor_PWM_MX_TIM2_Init中的PSC改为53:

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 53-1;

同时也要删去之前的HAL_TIM_PWM_Start()函数,即删去以下程序:

  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_3);
  HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_4);

3、更改步进电机脉冲设置程序

因为我们不再使用HAL_TIM_PWM_Start和HAL_TIM_PWM_Stop,因此改程序也要删去这两个函数。以步进电机1为例,改为以下程序:

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

当然值得注意的一点是,我们定义的这个函数现在没有了停止PWM的程序,所以我们一定要记得在更改PWM前要调用 HAL_TIM_PWM_Stop_IT 关闭定时器输出PWM

4、定时器PWM中断回调函数

在使用定时器中断之前,要记得在你的程序里重定义定时器的回调函数。
可选择把以下程序添加到你的stm32f4xx_it.c里:

/* USER CODE BEGIN 1 */
int PWM_num = 0;	// 脉冲数
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    
    
	if(htim->Instance == htim2.Instance)	// 确认是否为步进电机脉冲中断的回调
	{
    
    
		PWM_num ++;
		if(PWM_num >= 3200)		// 第3200次(一圈)
		{
    
    
			PWM_num = 0;
			HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1);		// 停止输出PWM
			//HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_10);		// 翻转led灯电平 
		}
	}
}
/* USER CODE END 1 */

上述程序中可以看到PWM_num正在累加,每调用一次回调函数就会累加一次,我们会在main函数中调用一次 HAL_TIM_PWM_Start_IT 函数,大概猜一猜就能知道,该函数的用途就是开启定时器输出PWM,并且每发送一个脉冲就调用一次回调函数
如下图所示,箭头所指的函数HAL库已经帮我们写好了,会调用我们上述的中断回调函数。
在这里插入图片描述

5、步进电机正转一圈闭环main.c程序

以下为main函数程序:

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();
	
	Motor1_pwm_Set(50);
	HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1);
	Motor1_course(1);
	Motor1_enable();

  /* USER CODE END 2 */

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

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

6、视频效果演示

闭环步进电机——精准控制脉冲数

五、程序链接

程序已经打包好上传到csdn的资源里了。
CSDN:HAL库STM32F407定时器中断控制步进电机程序
CSDN:HAL库STM32F407定时器中断控制闭环步进电机精准控制脉冲数
也可以通过以下链接下载:

STM32F407定时器中断控制步进电机程序
链接:https://pan.baidu.com/s/1svRzVW_7elkHg7wwQ4AN5A
提取码:hpzq
STM32F407定时器中断控制闭环步进电机精准控制脉冲数
链接:https://pan.baidu.com/s/1QIyusKAbusqyMl_VTkjKxg
提取码:4rml

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

猜你喜欢

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