在各类项目开发中,我们会经常涉及到步进电机的控制。一般情况下,单片机都是通过操作步进电机驱动器或驱动芯片实现对步进电机的控制。步进电机驱动器(芯片)一般都是留有三个接口En,Dir,Pul。En是步进电机使能(或脱机)的信号,高电平时,步进电机会断电脱机。Dir是步进电机转向信号,高电平与低电平代表着正转与反转。Pul是步进电机运动的脉冲信号,每接受到一个脉冲,步进电机运行一步(或一个细分步)。
本步进电机驱动库主要实现了对步进电机驱动器上述三种信号的控制,同时也封装好了电机点动、长动、往复三种运动模式。通过将步进电机封装抽象为一个结构体类型,我们可以轻松实现对多个不同步进电机的运动控制。同时,本库函数配套的应用指南中也给出了步进电机与光电对管的配套使用例程。
头文件中定义了步进电机结构体类型,它是对步进电机(驱动器)控制的抽象,与平台无关。在代码注释中有对每个成员的描述。特别需要注意的是,void StepMotor_TryMove(StepMotor_Struct* StepMotor_P)是电机运动控制的核心函数,它主要负责实现了电机三种运动模式。需要在定时器的更新中断服务函数中周期性的调用此函数。
/* * stepmotor.h * * Created on: 2020年8月31日 * Author: Tao */ #ifndef SOURCE_ALWHALESLIB_HARDWARE_INC_STEPMOTOR_H_ #define SOURCE_ALWHALESLIB_HARDWARE_INC_STEPMOTOR_H_ #define USE_STEPMOTOR #ifdef USE_STEPMOTOR #include "stepmotor.h" #include "stm32f10x_conf.h" #define SETPMOTOR_NUM 4 /***步进电机的控制引脚映射***/ #define STEPMOTOR_1_EN GPIOB_OUT(15) #define STEPMOTOR_1_DIR GPIOB_OUT(13) #define STEPMOTOR_1_PUL GPIOB_OUT(14) #define STEPMOTOR_2_EN GPIOD_OUT(10) #define STEPMOTOR_2_DIR GPIOD_OUT(8) #define STEPMOTOR_2_PUL GPIOD_OUT(9) #define STEPMOTOR_3_EN GPIOD_OUT(13) #define STEPMOTOR_3_DIR GPIOD_OUT(11) #define STEPMOTOR_3_PUL GPIOD_OUT(12) #define STEPMOTOR_4_EN GPIOC_OUT(6) #define STEPMOTOR_4_DIR GPIOD_OUT(14) #define STEPMOTOR_4_PUL GPIOD_OUT(15) /*******************************/ typedef void (*SetState_FP)(uint8_t state); typedef void (*SetParam_FP)(uint32_t param); /** * @brief 步进电机的结构体封装 * triggerCount是电机单步运动的边沿次数,1: 单次,即电平跳变, 2: 双次,即脉冲 * SetPulse是驱动电机运动的函数指针,一般情况下,应该为它注册一个操作GPIO端口电平的指针 * 一般会在定时器的更新中断服务函数中调用SetPulse,每次调用都将端口电平翻转 * triggerCount = 1 时,定时器的更新频率 = 步进电机速度(步/s) * triggerCount = 2 时,定时器的更新频率/2 = 步进电机速度(步/s) * * 一般通过定时器驱动步进电机的运动,定时器的启停即为电机的启停,定时器的频率即为电机的速度 * SetExec设置电机运动启停的函数指针,一般情况下,应该为它注册一个可以打开、关闭驱动定时器的函数 * SetSpeed是设置电机运动速度的函数指针,一般情况下,应该为它注册一个可以设置驱动定时器的更新周期的函数 */ typedef struct { //电机是否正在运动 uint8_t IsRun; //电机单步运动的边沿次数(1: 单次,即电平跳变, 2: 双次,即脉冲) uint8_t triggerNum; //电机运行的步数计数 uint32_t StepCount; //电机循环计数(仅在工作模式为往复时生效) uint8_t LoopCount; //电机设置运动步数 uint32_t StepSV; //电机的工作模式(0:距离, 1:往复, 2:长动) uint8_t WorkMode; //驱动步进电机的硬件定时器指针, //此成员与硬件相关,设置它的主要目的是为了编写各处理函数时的方便 // TIM_TypeDef* DriveTimer; //设置激活电机的函数指针(激活电机后,电机将处于锁定状态,相当于通电) //只有处于激活状态的电机才能被控制 SetState_FP SetEn; //设置电机运动方向的函数指针(0:正方向, 1:逆方向, 2:翻转方向) SetState_FP SetDir; //电机单步运动的函数指针(0:低电平, 1:高电平, 2:翻转电平) SetState_FP SetPulse; //设置电机运动速度的函数指针 SetParam_FP SetSpeed; //设置电机运动启停的函数指针 SetState_FP SetExec; } StepMotor_Struct; extern StepMotor_Struct StepMotor[SETPMOTOR_NUM]; void StepMotor_Init(); void StepMotor_SetWorkMode(StepMotor_Struct* StepMotor_P, uint8_t mode); void StepMotor_SetLoopCount(StepMotor_Struct* StepMotor_P, uint32_t loopCount); void StepMotor_SetStep(StepMotor_Struct* StepMotor_P, uint32_t step); void StepMotor_TryMove(StepMotor_Struct* StepMotor_P); #endif #endif /* SOURCE_ALWHALESLIB_HARDWARE_INC_STEPMOTOR_H_ */在源文件中,静态函数static void StepMotor_1_SetEn_Handler(uint8_t state),…,static void StepMotor_4_SetExec_Handler(uint8_t state)是与平台相关的代码,它们用来注册步进电机结构体成员的各功能函数指针。
StepMotor_Struct StepMotor[SETPMOTOR_NUM]结构体数组在声明时就直接进行了初始化。当然也可以利用void StepMotor_Init()函数(本源码中没有实现)实现上述结构体数组的初始化。与平台相关的代码主要在静态函数中,它们是抽象电机控制的具体实现。
初始化结构体数组之后,所有对步进电机的控制都是围绕操作结构体成员展开。当然可以直接操作结构体成员,但笔者推荐使用封装好的常用步进电机操作函数来控制步进电机,例如void StepMotor_SetWorkMode(StepMotor_Struct* StepMotor_P, uint8_t mode),void StepMotor_SetStep(StepMotor_Struct* StepMotor_P, uint32_t step)等。
/* * stepmotor.c * * Created on: 2020年8月31日 * Author: Tao */ #include "stepmotor.h" #ifdef USE_STEPMOTOR /**********************************/ static void StepMotor_1_SetEn_Handler(uint8_t state) { STEPMOTOR_1_EN = state; } static void StepMotor_2_SetEn_Handler(uint8_t state) { STEPMOTOR_2_EN = state; } static void StepMotor_3_SetEn_Handler(uint8_t state) { STEPMOTOR_3_EN = state; } static void StepMotor_4_SetEn_Handler(uint8_t state) { STEPMOTOR_4_EN = state; } /**********************************/ static void StepMotor_1_SetDir_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_1_DIR = state; } else { STEPMOTOR_1_DIR = ~STEPMOTOR_1_DIR; } } static void StepMotor_2_SetDir_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_2_DIR = state; } else { STEPMOTOR_2_DIR = ~STEPMOTOR_2_DIR; } } static void StepMotor_3_SetDir_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_3_DIR = state; } else { STEPMOTOR_3_DIR = ~STEPMOTOR_3_DIR; } } static void StepMotor_4_SetDir_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_4_DIR = state; } else { STEPMOTOR_4_DIR = ~STEPMOTOR_4_DIR; } } /**********************************/ static void StepMotor_1_SetPulse_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_1_PUL = state; } else { STEPMOTOR_1_PUL = ~STEPMOTOR_1_PUL; } } static void StepMotor_2_SetPulse_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_2_PUL = state; } else { STEPMOTOR_2_PUL = ~STEPMOTOR_2_PUL; } } static void StepMotor_3_SetPulse_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_3_PUL = state; } else { STEPMOTOR_3_PUL = ~STEPMOTOR_3_PUL; } } static void StepMotor_4_SetPulse_Handler(uint8_t state) { if(state < 2) { STEPMOTOR_4_PUL = state; } else { STEPMOTOR_4_PUL = ~STEPMOTOR_4_PUL; } } /**********************************/ static void StepMotor_1_SetSpeed_Handler(uint32_t speed) { speed = speed * StepMotor[0].triggerNum; //frequency < 1k, psc = 7200 if (speed < 1000) { TIM5->PSC = 7200 - 1; } //frequency < 10k, psc = 720 else if (speed < 10000) { TIM5->PSC = 720 - 1; } //frequency < 100k, psc = 72 else if (speed < 100000) { TIM5->PSC = 72 - 1; } else { return; } //Set the update frequency of the timer. TIM5->ARR = 72000000.0 / (TIM5->PSC + 1) / speed - 1; return; } static void StepMotor_2_SetSpeed_Handler(uint32_t speed) { speed = speed * StepMotor[1].triggerNum; //frequency < 1k, psc = 7200 if (speed < 1000) { TIM6->PSC = 7200 - 1; } //frequency < 10k, psc = 720 else if (speed < 10000) { TIM6->PSC = 720 - 1; } //frequency < 100k, psc = 72 else if (speed < 100000) { TIM6->PSC = 72 - 1; } else { return; } //Set the update frequency of the timer. TIM6->ARR = 72000000.0 / (TIM6->PSC + 1) / speed - 1; return; } static void StepMotor_3_SetSpeed_Handler(uint32_t speed) { speed = speed * StepMotor[2].triggerNum; //frequency < 1k, psc = 7200 if (speed < 1000) { TIM7->PSC = 7200 - 1; } //frequency < 10k, psc = 720 else if (speed < 10000) { TIM7->PSC = 720 - 1; } //frequency < 100k, psc = 72 else if (speed < 100000) { TIM7->PSC = 72 - 1; } else { return; } //Set the update frequency of the timer. TIM7->ARR = 72000000.0 / (TIM7->PSC + 1) / speed - 1; return; } static void StepMotor_4_SetSpeed_Handler(uint32_t speed) { speed = speed * StepMotor[3].triggerNum; //frequency < 1k, psc = 7200 if (speed < 1000) { TIM8->PSC = 7200 - 1; } //frequency < 10k, psc = 720 else if (speed < 10000) { TIM8->PSC = 720 - 1; } //frequency < 100k, psc = 72 else if (speed < 100000) { TIM8->PSC = 72 - 1; } else { return; } //Set the update frequency of the timer. TIM8->ARR = 72000000.0 / (TIM8->PSC + 1) / speed - 1; return; } /**********************************/ static void StepMotor_1_SetExec_Handler(uint8_t state) { TIM_Cmd(TIM5, state); } static void StepMotor_2_SetExec_Handler(uint8_t state) { TIM_Cmd(TIM6, state); } static void StepMotor_3_SetExec_Handler(uint8_t state) { TIM_Cmd(TIM7, state); } static void StepMotor_4_SetExec_Handler(uint8_t state) { TIM_Cmd(TIM8, state); } StepMotor_Struct StepMotor[SETPMOTOR_NUM] = { //Initialization of step motor 1 { .IsRun = 0, .triggerNum = 2, .StepCount = 0, .LoopCount = 0, .StepSV = 100, .WorkMode = 0, .SetEn = StepMotor_1_SetEn_Handler, .SetDir = StepMotor_1_SetDir_Handler, .SetPulse = StepMotor_1_SetPulse_Handler, .SetSpeed = StepMotor_1_SetSpeed_Handler, .SetExec = StepMotor_1_SetExec_Handler, }, //Initialization of step motor 2 { .IsRun = 0, .triggerNum = 2, .StepCount = 0, .LoopCount = 0, .StepSV = 100, .WorkMode = 0, .SetEn = StepMotor_2_SetEn_Handler, .SetDir = StepMotor_2_SetDir_Handler, .SetPulse = StepMotor_2_SetPulse_Handler, .SetSpeed = StepMotor_2_SetSpeed_Handler, .SetExec = StepMotor_2_SetExec_Handler, }, //Initialization of step motor 3 { .IsRun = 0, .triggerNum = 2, .StepCount = 0, .LoopCount = 0, .StepSV = 100, .WorkMode = 0, .SetEn = StepMotor_3_SetEn_Handler, .SetDir = StepMotor_3_SetDir_Handler, .SetPulse = StepMotor_3_SetPulse_Handler, .SetSpeed = StepMotor_3_SetSpeed_Handler, .SetExec = StepMotor_3_SetExec_Handler, }, //Initialization of step motor 4 { .IsRun = 0, .triggerNum = 2, .StepCount = 0, .LoopCount = 0, .StepSV = 100, .WorkMode = 0, .SetEn = StepMotor_4_SetEn_Handler, .SetDir = StepMotor_4_SetDir_Handler, .SetPulse = StepMotor_4_SetPulse_Handler, .SetSpeed = StepMotor_4_SetSpeed_Handler, .SetExec = StepMotor_4_SetExec_Handler, }, }; /** * @brief 完成步进电机的初始化 */ void StepMotor_Init() { } /** * @brief 设置步进电机的运动步数 * 运动步数与内部的StepCount, StepSV的关系主要取决于triggerNum,即触发多少次电机实际运行1步。 * 在驱动信号的形式上即是单边沿触发(triggerNum = 1, 跳变信号)还是双边沿触发(triggerNum = 2, 脉冲信号) * @param StepMotor_P: 步进电机结构体指针 * @param step: 电机运动步数 */ void StepMotor_SetStep(StepMotor_Struct* StepMotor_P, uint32_t step) { StepMotor_P->StepSV = step * StepMotor_P->triggerNum; StepMotor_P->StepCount = StepMotor_P->StepSV; } /** * @brief 设置步进电机的工作模式 * @param StepMotor_P: 步进电机结构体指针 * @param mode: 电机工作模式 * @arg 0: 点动 * @arg 1: 往复 * @arg 2: 长动 */ void StepMotor_SetWorkMode(StepMotor_Struct* StepMotor_P, uint8_t mode) { StepMotor_P->WorkMode = mode; } /** * @brief 设置步进电机的循环次数 * @param StepMotor_P: 步进电机结构体指针 * @param loopCount: 电机循环次数(仅在工作模式为往复时生效) */ void StepMotor_SetLoopCount(StepMotor_Struct* StepMotor_P, uint32_t loopCount) { StepMotor_P->LoopCount = loopCount; } /** * @brief 在定时器的更新中断服务函数中周期性调用此函数 * @param StepMotor_P: 步进电机结构体指针 * 需要注意的是,传入的步进电机结构体的SetSpeed与SetExec指针所依赖的驱动定时器 * 应当与调用本函数的驱动定时器一致,否则会出现错误 */ void StepMotor_TryMove(StepMotor_Struct* StepMotor_P) { //正常工作模式 if (StepMotor_P->WorkMode == 0) { if (StepMotor_P->StepCount != 0) { //翻转一次脉冲端口电平 StepMotor_P->SetPulse(2); StepMotor_P->StepCount--; } else { //停止步进电机 StepMotor_P->SetExec(0); StepMotor_P->IsRun = 0; } } //往复工作模式 else if (StepMotor_P->WorkMode == 1) { if (StepMotor_P->LoopCount != 0) { if (StepMotor_P->StepCount != 0) { StepMotor_P->SetPulse(2); StepMotor_P->StepCount--; } else { StepMotor_P->LoopCount--; StepMotor_P->StepCount = StepMotor_P->StepSV; //翻转方向 StepMotor_P->SetDir(2); } } else { StepMotor_P->SetExec(0); StepMotor_P->IsRun = 0; } } //连续工作模式 else if (StepMotor_P->WorkMode == 2) { //翻转一次脉冲端口电平 StepMotor_P->SetPulse(2); } else { } } #endif