我们使用的单片机是STM32F103ZET6,电机是42步进电机(额定电流是1A)、驱动是TMC2209;但是暂时使用2160这个外接驱动(注意:2160为大电流电机驱动不能长时间带动这个42电机,否则会发烫烧电机)。
开启一个定时器2外设中断:为电机提供步进脉冲;
开启三个GPIO口:作为EN、STEP、DIR控制;
42步进电机:步距角1.8°、16细分、3200步每圈。
一、代码:
tim.c:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.c
* @brief This file provides code for the configuration
* of the TIM instances.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"
/* USER CODE BEGIN 0 */
// 在全局变量区添加
uint8_t StepperState;
volatile uint32_t step_counter;
volatile bool rotation_enable;
/* USER CODE END 0 */
TIM_HandleTypeDef htim2;
/* TIM2 init function */
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 36 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 100 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
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();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END TIM2_Init 2 */
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspInit 0 */
/* USER CODE END TIM2_MspInit 0 */
/* TIM2 clock enable */
__HAL_RCC_TIM2_CLK_ENABLE();
/* TIM2 interrupt Init */
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspInit 1 */
/* USER CODE END TIM2_MspInit 1 */
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{
if(tim_baseHandle->Instance==TIM2)
{
/* USER CODE BEGIN TIM2_MspDeInit 0 */
/* USER CODE END TIM2_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_TIM2_CLK_DISABLE();
/* TIM2 interrupt Deinit */
HAL_NVIC_DisableIRQ(TIM2_IRQn);
/* USER CODE BEGIN TIM2_MspDeInit 1 */
/* USER CODE END TIM2_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
/* 电机调速函数(TIM2重新配置频率函数) */
void Stepper_SetSpeed(uint32_t new_period, uint16_t new_prescaler)
{
HAL_TIM_Base_Stop_IT(&htim2);
htim2.Init.Prescaler = new_prescaler;
htim2.Init.Period = new_period;
if (HAL_TIM_Base_Init(&htim2) == HAL_OK) {
HAL_TIM_Base_Start_IT(&htim2);
}
}
/*
* @brief: 步进电机控制函数
* @param: step_mode: 步进模式,1为正常运动,2为正反转
* @return: 无
* @note: 步进电机控制函数,根据步进模式控制步进电机的运动
*/
void Stepper_Isr(uint8_t step_mode)
{
static uint8_t dir_switch = 0;
if(rotation_enable) {
// 生成步进脉冲(电平翻转)
if (StepperState == 0)
{
MOTOR_STEP_H;
StepperState = 1;
}
else
{
MOTOR_STEP_L;
StepperState = 0;
step_counter++;
}
if(step_counter >= (MOTOR_ONE_CIRCLE_STEP * MOTOR_NUM) && step_mode == 1) {
step_counter = 0;
static uint8_t motor_speed = 0;
switch (motor_speed)
{
case 0:
Stepper_SetSpeed(800 - 1, 36 - 1);
break;
case 1:
Stepper_SetSpeed(600 - 1, 36 - 1);
break;
case 2:
Stepper_SetSpeed(400 - 1, 36 - 1);
break;
case 3:
Stepper_SetSpeed(200 - 1, 36 - 1);
break;
case 4:
Stepper_SetSpeed(100 - 1, 36 - 1);
break;
default:
break;
}
motor_speed++;
if(motor_speed >= 5) {
motor_speed = 0;
}
}
// 仅在上升沿计数
if(step_counter >= 3200 && step_mode == 2) {
step_counter = 0;
// 切换方向
if (dir_switch == 0)
{
MOTOR_DIR_REVER;
dir_switch = 1;
}
else
{
MOTOR_DIR_CW;
dir_switch = 0;
}
}
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == (&htim2))
{
Stepper_Isr(0);
}
}
/* USER CODE END 1 */
tim.h
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file tim.h
* @brief This file contains all the function prototypes for
* the tim.c file
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __TIM_H__
#define __TIM_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern TIM_HandleTypeDef htim2;
/* USER CODE BEGIN Private defines */
#define MOTOR_ONE_CIRCLE_STEP 3200 // 电机每圈的步数
#define MOTOR_NUM 1 // 电机跑的圈数
/* USER CODE END Private defines */
void MX_TIM2_Init(void);
/* USER CODE BEGIN Prototypes */
void Stepper_Isr(uint8_t step_mode);
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __TIM_H__ */
stm32f1xx_it.c:
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
main.c
/* Private defines -----------------------------------------------------------*/
#define MOTOR_EN_Pin GPIO_PIN_0
#define MOTOR_EN_GPIO_Port GPIOF
#define MOTOR_DIR_Pin GPIO_PIN_1
#define MOTOR_DIR_GPIO_Port GPIOF
#define MOTOR_STEP_Pin GPIO_PIN_2
#define MOTOR_STEP_GPIO_Port GPIOF
/* USER CODE BEGIN Private defines */
#define MOTOR_EN_ON HAL_GPIO_WritePin(MOTOR_EN_GPIO_Port, MOTOR_EN_Pin, GPIO_PIN_RESET)
#define MOTOR_EN_OFF HAL_GPIO_WritePin(MOTOR_EN_GPIO_Port, MOTOR_EN_Pin, GPIO_PIN_SET)
#define MOTOR_DIR_CW HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_SET)
#define MOTOR_DIR_REVER HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_RESET)
#define MOTOR_STEP_H HAL_GPIO_WritePin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin, GPIO_PIN_SET)
#define MOTOR_STEP_L HAL_GPIO_WritePin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin, GPIO_PIN_RESET)
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOF, MOTOR_EN_Pin|MOTOR_DIR_Pin|MOTOR_STEP_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PFPin PFPin PFPin */
GPIO_InitStruct.Pin = MOTOR_EN_Pin|MOTOR_DIR_Pin|MOTOR_STEP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
}
/**
* @brief The application entry point.
* @retval int
*/
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();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
MOTOR_EN_ON;
MOTOR_DIR_CW;
rotation_enable = true;
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_Delay(1000);
}
/* USER CODE END 3 */
}
二、调速:
步进电机的速度通常是通过改变脉冲的频率来调节的,也就是调整定时器的中断频率。因此,调速的关键在于修改定时器的预分频器(Prescaler)或自动重装载值(Period)。
1、调速原理:
定时器频率计算公式:Freq = CPU_Freq / (Prescaler + 1) / (Period + 1)
假设CPU时钟72MHz,默认参数(36-1,1000-1)产生2kHz中断
周期值越小/预分频值越小 → 中断频率越高 → 电机转速越快
注意:实际使用时要确保新参数在定时器的有效范围内(0x0000-0xFFFF)
// 示例:提高速度(减小周期值)
Stepper_SetSpeed(500 - 1, 36 - 1); // 2倍速
// 示例:降低速度(增大周期值)
Stepper_SetSpeed(2000 - 1, 36 - 1); // 半速
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_64192009/article/details/145930210
|