记录一下学习使用STM32+DRV8825模块驱动步进电机的3种方法。有需要可自取,有错误可留言或者私信我。
3种方法:
1.普通IO口+延迟函数。堵塞
2.普通IO口+定时器(定时器中断)。非堵塞
3.定时器PWM。非堵塞
DRV8825模块+硬件介绍请参考:https://blog.csdn.net/xiaokaidbb/article/details/160931219?spm=1001.2014.3001.5502
方法二:
普通IO口+定时器(定时器中断)。非堵塞版
一、代码介绍
1.建立hal工程

2.设置时钟


3.定时器中断设置
定时器参数设置
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-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;

4 使能GPIO
PA6--nEN,推挽模式,初始化高电平
PA7--DIR,推挽模式,初始化低电平
PB0--STEP,推挽模式,初始化低电平
GPIO不需要在这里配置,稍后会在motor.c中配置。
5.使能定时器中断

生成工程


代码编写
1.创建motor.c和motor.h文件
2. motor.h
#ifndef __MOTOR_H
#define __MOTOR_H
#include "stm32f1xx_hal.h"
// 引脚定义
#define MOTOR_EN_Pin GPIO_PIN_6
#define MOTOR_EN_GPIO_Port GPIOA
#define MOTOR_DIR_Pin GPIO_PIN_7
#define MOTOR_DIR_GPIO_Port GPIOA
#define MOTOR_STEP_Pin GPIO_PIN_0
#define MOTOR_STEP_GPIO_Port GPIOB
extern uint32_t motor_current_steps;
// 电机方向
typedef enum
{
CW = 1, // 正转
CCW = 0 // 反转
} Motor_DirTypeDef;
// 对外函数
void Motor_Init(void);
void Motor_Start(Motor_DirTypeDef dir, uint32_t total_steps, uint16_t normal_speed);
void Motor_Stop(void);
uint8_t Motor_IsBusy(void);
#endif
motor.c
#include "motor.h"
extern TIM_HandleTypeDef htim2;
uint32_t motor_target_steps = 0; //目标步数
uint32_t motor_current_steps = 0; //当前步数
uint8_t motor_running_flag = 0; // 0为空闲,1为电机正在运行,只有电机处于空闲状态,才能执行下一次电机指令
// ===================== 平滑启停参数 =====================
#define ACCEL_STEPS 100 // 启动加速步数
#define DECEL_STEPS 100 // 停止减速步数
#define START_SPEED 4000 // 启动最慢速度
// ========================================================
uint16_t user_normal_speed; // 你设定的正常速度
void Motor_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
// PA6 PA7
GPIO_InitStruct.Pin = MOTOR_EN_Pin | MOTOR_DIR_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// PB0
GPIO_InitStruct.Pin = MOTOR_STEP_Pin;
HAL_GPIO_Init(MOTOR_STEP_GPIO_Port, &GPIO_InitStruct);
HAL_GPIO_WritePin(MOTOR_EN_GPIO_Port, MOTOR_EN_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin, GPIO_PIN_RESET);
}
// 启动电机(非阻塞)(设置半周期最好在100-3000之间)
void Motor_Start(Motor_DirTypeDef dir, uint32_t steps, uint16_t half_period_us)
{
if(steps == 0 || motor_running_flag) return;
// 使能 + 方向
HAL_GPIO_WritePin(MOTOR_EN_GPIO_Port, MOTOR_EN_Pin, GPIO_PIN_RESET);
dir ? HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_SET) :
HAL_GPIO_WritePin(MOTOR_DIR_GPIO_Port, MOTOR_DIR_Pin, GPIO_PIN_RESET);
HAL_Delay(1);
motor_target_steps = steps;
motor_current_steps = 0;
motor_running_flag = 1;
// 保存用户设定的正常速度
user_normal_speed = half_period_us;
// 设置 TIM2 中断速度
__HAL_TIM_SET_AUTORELOAD(&htim2, START_SPEED);
// 【可选】立即生效,不等待当前计数结束(强制更新)
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清零计数器
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); // 清除更新标志
// 关键!!!启动中断
HAL_TIM_Base_Start_IT(&htim2);
}
void Motor_Stop(void)
{
motor_target_steps = 0;
motor_current_steps = 0;
motor_running_flag = 0;
HAL_GPIO_WritePin(MOTOR_EN_GPIO_Port, MOTOR_EN_Pin, GPIO_PIN_SET);
}
uint8_t Motor_IsBusy(void)
{
return motor_running_flag;
}
//// ==============================
//// 定时器中断:匀速
//// ==============================
//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//{
// if(htim==&htim2)
// {
// if(!motor_running_flag) return;
//
// // 只做引脚翻转,不做任何延时
// HAL_GPIO_TogglePin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin);
// // 只有当引脚拉低时,才算完成一个脉冲
// if(HAL_GPIO_ReadPin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin) == 0)
// {
// motor_current_steps++;
// if(motor_current_steps >= motor_target_steps)
// {
// Motor_Stop();
// HAL_TIM_Base_Stop_IT(&htim2); // 停止定时器中断,防止一直触发
// }
// }
//
// }
//
//}
// ==============================
// 定时器中断 + 平滑加减速(你要的功能在这里!)
// ==============================
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim2)
{
if(!motor_running_flag) return;
// 发脉冲
HAL_GPIO_TogglePin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin);
if(HAL_GPIO_ReadPin(MOTOR_STEP_GPIO_Port, MOTOR_STEP_Pin) == 0)
{
motor_current_steps++;
// ===================== 【平滑加减速核心】 =====================
uint16_t current_period = __HAL_TIM_GET_AUTORELOAD(&htim2);
// 1. 启动加速:从慢 → 正常速度
if(motor_current_steps <= ACCEL_STEPS)
{
if(current_period > user_normal_speed)
{
current_period -= 20; // 数值越小加速越柔
}
}
// 2. 停止减速:正常 → 慢
else if(motor_current_steps >= motor_target_steps - DECEL_STEPS)
{
if(current_period < START_SPEED)
{
current_period += 20;
}
}
// 3. 中间保持正常速度
else
{
current_period = user_normal_speed;
}
// 更新速度
__HAL_TIM_SET_AUTORELOAD(&htim2, current_period);
// ==============================================================
// 到达终点停止
if(motor_current_steps >= motor_target_steps)
{
Motor_Stop();
HAL_TIM_Base_Stop_IT(&htim2);
}
}
}
}
main.c
添加头文件
#include "motor.h"
在main()函数里面,while(1)函数之前添加
Motor_Init(); //初始化
HAL_Delay(1000);
if(!Motor_IsBusy())
{
Motor_Start(CCW, 6400, 600);
//反转1圈,600us半周期,想要加速,减小半周期值
}
HAL_Delay(1000);
if(!Motor_IsBusy())
{
Motor_Start(CW, 6400, 600);
//正转1圈,600us半周期
}
实验现象
上电后,反转1圈,延迟1秒,正转1圈。
速度说明:
运行Motor_Start(CW, 6400, 600);函数后,前100step,速度会从0逐步加速到设定的速度,然后按照规定速度匀速行驶,快到终点的前100步,速度会从设定速度逐步减小到0.

核心:
运行Motor_Start函数后
1. 使能电机
2. 设置方向
3. 设置定时器的ARR,(ARR可以控制转速),清空cnt,清除更新标志,开启定时器中断。
4. 在中断回调函数HAL_TIM_PeriodElapsedCallback中,翻转STEP引脚,motor_current_steps计数+1,motor_current_steps等于我们设置的步数后,当motor_current_steps==motor_target_steps后,停止中断。失能电机
工程文件下载链接:
链接: https://pan.baidu.com/s/1BLXntDO1Cg6tmF6i-T_Tig?pwd=rdgc 提取码: rdgc
499

被折叠的 条评论
为什么被折叠?



