利用STM32的普通GPIO引脚驱动步进电机实现逐级加减速--代码分享

步进电机是一种将电脉冲信号转换成相应角位移或线位移的电动机。每输入一个脉冲信号,转子就转动一个角度或前进一步,其输出的角位移或线位移与输入的脉冲数成正比,转速与脉冲频率成正比。

脉冲波是指一种间断的持续时间极短的突然发生的电信号。凡是断续出现的电压或电流称为脉冲电压或脉冲电流。电信波形来说除了正弦波和由若个正弦分量合成的连续波以外,都可以称为脉冲波。

步进电机的控制方法有很多,不同的应用场景和使用不同的驱动设备所使用的控制方法也有些差异,本例使用的是基于THB6064MQ集成芯片的驱动模块驱动两相四线的42步进电机。

控制芯片是基于STM32F103C8T6的32位MCU,使用普通IO口的电平反转来输出脉冲驱动步进电机运转的,方法略显暴力@~@用来作步进电机驱动效果的演示,请大家共同探讨,批评指正,共同学习。
#ifndef __MAIN_DEFINE_H__
#define __MAIN_DEFINE_H__
/* 宏定义 --------------------------------------------------------------------*/
#define subdivide 1 //驱动器细分数
/*速度标称值:脉冲换向一次所需的循环数,*/
#define HAL_RCC_count 100000 //滴答定时器中断1us中断一次
#define FSPR 200// 步进电机单圈步数 步距角:1.8° 360/1.8 = 200 正常情况下需要200步转一圈
#define MICRO_STEP 1 // 步进电机驱动器细分数
#define SPR (FSPR*MICRO_STEP) // 旋转一圈需要的脉冲数
//转速(转/分钟)S = S1(转/秒)* 60 = S2 (脉冲/秒)
#define speed_max 1050 //(转/分钟)
#define speed_1 450 //(转/分钟)
#define speed_2 750 //(转/分钟)
#define speed_min 160 //(转/分钟)
#define Uniform_s 10 //匀速运动持续时间(秒)
//#define speed_max_pulse ( speed_max / 60 ) * SPR //每秒脉冲数
#define yunsu_count Uniform_s * HAL_RCC_count //匀速运转持续的循环数(与时间相关)
#define biansu_count 10000 //每个速度持续的循环数(与加减速率相关)进过这么多个循环数后加一次速度或减速一次
#endif // __MAIN_DEFINE_H__
//
#ifndef __StepMotor_H__
#define __StepMotor_H__
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* 类型定义 ------------------------------------------------------------------*/
typedef enum
{
StepMotor_Low = 0, //低电平
StepMotor_High = 1, //高电平
}StepMotor_State_TypeDef;
#define IS_StepMotor_STATE(STATE) (((STATE) == StepMotor_Low) || ((STATE) == StepMotor_High) || ((STATE) == StepMotor_TOGGLE))
/* 宏定义 --------------------------------------------------------------------*/
//PA3定义对应测试版上的步进电机R轴的PUL接口
#define StepMotor_RCC_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define StepMotor_GPIO_PIN GPIO_PIN_3
#define StepMotor_GPIO GPIOA
#define StepMotor_HIGH HAL_GPIO_WritePin(StepMotor_GPIO,StepMotor_GPIO_PIN,GPIO_PIN_SET) // 输出高电平
#define StepMotor_LOW HAL_GPIO_WritePin(StepMotor_GPIO,StepMotor_GPIO_PIN,GPIO_PIN_RESET) // 输出低电平
#define StepMotor_TOGGLE HAL_GPIO_TogglePin(StepMotor_GPIO,StepMotor_GPIO_PIN) // 输出反转
/* 扩展变量 ------------------------------------------------------------------*/
/* 函数声明 ------------------------------------------------------------------*/
void main_StepMorot(void);//步进电机驱动相关初始化程序
void while_StepMotor(void);//步进电机驱动单循环子程序
/******************************************************************************/
/**
******************************************************************************
* 文件名程: StepMotor.c
* 作 者: 老白
* 版 本: V1.0
* 编写日期: 2020-12-30
* 功 能: 驱动步进电机
******************************************************************************
* 说明:简单驱动步进电机之一:在主循环中交替GPIO输出高低电平方法驱动步进电机
*
******************************************************************************
*/
/* 包含头文件 ----------------------------------------------------------------*/
#include "StepMotor.h"
#include "main_define.h"
/* 私有类型定义 --------------------------------------------------------------*/
/* 私有宏定义 ----------------------------------------------------------------*/
/* 私有变量 ------------------------------------------------------------------*/
/* 扩展变量 ------------------------------------------------------------------*/
/* 私有函数原形 --------------------------------------------------------------*/
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: 步进电机IO引脚初始化.
* 输入参数: 无
* 返 回 值: 无
* 说 明:使用宏定义方法代替具体引脚号,方便程序移植,只要简单修改led.h
* 文件相关宏定义就可以方便修改引脚。
*/
void StepMotor_GPIO_Init(void)
{
/* 定义IO硬件初始化结构体变量 */
GPIO_InitTypeDef GPIO_InitStruct;
/* 使能(开启)LED引脚对应IO端口时钟 */
StepMotor_RCC_CLK_ENABLE();
/* 配置StepMotor引脚输出电压 */
HAL_GPIO_WritePin(StepMotor_GPIO, StepMotor_GPIO_PIN, GPIO_PIN_RESET);
/* 设定StepMotor对应引脚IO编号 */
GPIO_InitStruct.Pin = StepMotor_GPIO_PIN;
/* 设定StepMotor对应引脚IO为输出模式 */
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
/* 设定StepMotor对应引脚IO操作速度 */
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
/* 初始化StepMotor对应引脚IO */
HAL_GPIO_Init(StepMotor_GPIO, &GPIO_InitStruct);
}
//步进电机驱动相关初始化程序
void main_StepMorot(void)
{
StepMotor_GPIO_Init();//步进电机IO引脚初始化
}
//定义步进电机单循环循环数变量
uint8_t count=0;//定义步进电机单循环循环数变量
uint8_t s_index = HAL_RCC_count / ( ( speed_min / 60 ) * SPR ) ; //电机的加速循环计数
uint8_t jieduan_bz = 0 ;//0 加速阶段 1 高速匀速阶段 2 减速阶段 3 低速匀速阶段
uint32_t yunsu_index = 0 ; //匀速运转持续的循环计数(与时间相关)
uint32_t biansu_index = 0 ;//每个速度持续的循环数计数值
//步进电机驱动单循环子程序
void while_StepMotor(void)
{
if( count > s_index )
{ StepMotor_TOGGLE ; count = 0 ;}
else { count++ ;}
//以下代码与加减速有关
if ( jieduan_bz == 0 )//加速阶段
{
if( s_index > HAL_RCC_count / ( ( speed_1 / 60 ) * SPR ) ) //电机单脉冲需要的循环数递减(电机加速)
{
if ( biansu_index >= biansu_count ){ s_index-- ; biansu_index = 0 ; }//加速一次
else { biansu_index++; }
}
else
{
jieduan_bz = 1 ;//当速度到最大的时候变为高速匀速阶段
s_index = HAL_RCC_count / ( ( speed_1 / 60 ) * SPR ) ; //以第一速度匀速运行
yunsu_index = 0 ;
}
}
if ( jieduan_bz == 1 )//匀速阶段1
{
if ( yunsu_index > yunsu_count ){ jieduan_bz = 2;yunsu_index = 0 ; }
else { yunsu_index++; }
}
if ( jieduan_bz == 2 )//加速阶段
{
if( s_index > HAL_RCC_count / ( ( speed_2 / 60 ) * SPR ) ) //电机单脉冲需要的循环数递减(电机加速)
{
if ( biansu_index >= biansu_count ){ s_index-- ; biansu_index = 0 ; }//加速一次
else { biansu_index++; }
}
else
{
jieduan_bz = 3 ;//当速度到最大的时候变为高速匀速阶段
s_index = HAL_RCC_count / ( ( speed_2 / 60 ) * SPR ) ; //以第一速度匀速运行
yunsu_index = 0 ;
}
}
if ( jieduan_bz == 3 )//匀速阶段2
{
if ( yunsu_index > yunsu_count ){ jieduan_bz = 4;yunsu_index = 0 ; }
else { yunsu_index++; }
}
if ( jieduan_bz == 4 )//加速阶段
{
if( s_index > HAL_RCC_count / ( ( speed_max / 60 ) * SPR ) ) //电机单脉冲需要的循环数递减(电机加速)
{
if ( biansu_index >= biansu_count ){ s_index-- ; biansu_index = 0 ; }//加速一次
else { biansu_index++; }
}
else
{
jieduan_bz = 5 ;//当速度到最大的时候变为高速匀速阶段
s_index = HAL_RCC_count / ( ( speed_max / 60 ) * SPR ) ; //以第一速度匀速运行
}
}
if ( jieduan_bz == 5 )//匀速阶段3
{
if ( yunsu_index > yunsu_count ){ jieduan_bz = 6;yunsu_index = 0 ; }
else { yunsu_index++; }
}
if ( jieduan_bz == 6 )//减速阶段
{
if( s_index < HAL_RCC_count / ( ( speed_min / 60 ) * SPR ) ) //电机单脉冲需要的循环数递减(电机减速)
{
if ( biansu_index >= biansu_count ){ s_index++ ; biansu_index = 0 ; }//减速一次
else { biansu_index++; }
}
else
{
jieduan_bz = 7 ;//当速度到最小的时候变为高速匀速阶段
s_index = HAL_RCC_count / ( ( speed_min / 60 ) * SPR ) ; //以第一速度匀速运行
}
}
if ( jieduan_bz == 7 )//匀速阶段4
{
if ( yunsu_index > yunsu_count ){ jieduan_bz = 0;yunsu_index = 0 ; }
else { yunsu_index++; }
}
}
/**
* 函数功能: 系统滴答定时器中断回调函数
* 输入参数: 无
* 返 回 值: 无
* 说 明: 每发生一次滴答定时器中断进入该回调函数一次
*/
void HAL_SYSTICK_Callback(void)
{
while_StepMotor();
}
/*******************老白学习小组**********************************/
/**
******************************************************************************
* 文件名程: main.c
* 作 者: 老白嵌入式学习小组
* 版 本: V1.0
* 编写日期: 2021-01-04
* 功 能: 使用HAL库方法控制LED灯亮灭实现流水灯效果
******************************************************************************
*/
/* 包含头文件 ----------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
#include "StepMotor.h"
#include "main_define.h"

/* 私有函数原形 --------------------------------------------------------------*/
void SystemClock_Config(void);
/* 函数体 --------------------------------------------------------------------*/
/**
* 函数功能: 主函数.
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
int main(void)
{
/* 复位所有外设,初始化Flash接口和系统滴答定时器 */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
main_StepMorot();//步进电机驱动相关初始化程序
/* 无限循环 */
while (1) { }
}
/**
* 函数功能: 系统时钟配置
* 输入参数: 无
* 返 回 值: 无
* 说 明: 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; // 外部晶振,8MHz
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; // 9倍频,得到72MHz主时钟
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 系统时钟:72MHz
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; // AHB时钟:72MHz
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; // APB1时钟:36MHz
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; // APB2时钟:72MHz
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/HAL_RCC_count); // 配置并启动系统滴答定时器
/* 系统滴答定时器时钟源 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* 系统滴答定时器中断优先级配置 */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/*******************老白学习小组**********************************/
