关于步进电机

步进电机在非常多的场合有着广泛的用途。通常情况下对运动控制有较高精度需求时就可以使用步进电机,初学来说常用的步进电机有42、57两种系列的步进电机。42电机的体积合适做一些小型的设备,它输出的扭矩较小,比较适合做小车的底盘驱动电机,小型3D打印机驱动电机,桌面机械臂的驱动电机等等。
相对于42步进电机,还有57步进电机,它的体积和质量有了较大的提升,当然其扭矩也有很大的提高。适用于做一些有负载需求的场景,比如小型的搬运机械臂驱动、特殊的滑台场景等。
总之选用步进之前要考虑到:对控制精度的需求,精度需求不高可以使用更简单稳定的直流电机。对负载输出的需求,负载输出较大时无论直流或者步进都需要考虑加合适的减速器。使用场景对电机性能要求,如果对电机转速,负载,精度等都有很高的要求那就考虑选用高品质的(无刷电机=¥¥¥¥)。
接下来针对两个驱动器:TB6600 DRV8825简单说明一下他们的控制42(同57)步进电机的驱动代码,至于硬件接线,理论上,只要硬件设计得当,电机使用场景不复杂,那这两种驱动器都只需要两根接线:脉冲信号输入+正反转控制

DRV8825+42步进

硬件的主要参考:https://zhuanlan.zhihu.com/p/210266085 建议在使用该模块之前一定要先认真研读一下这个网址的说明。
DRV8825模块的体积比较小,最大输出电流3A,最大细分32分。驱动的控制方式比较简单。核心就是3个脚:EN, STEP, DIR,其中EN负责控制驱动器的使能端口,STEP负责输入驱动脉冲信号,DIR负责控制电机的正反转。
接线图:

至于其它的控制细分数的引脚可以直接通过布线接需要的电平。

TB6600

硬件参考:TB6600的GPIO控制.
这个驱动器的接线核心和DRV8825其实差不多,主要也是三个信号:使能端口,脉冲信号输入,方向控制。不同的是TB6612内部需要差分信号,所以就需要有涉及共阴或者共阳的接线,一般来说我个人推荐使用共阳的接线方式,将信号阳极全部接到3.3V的电源正极,信号负极再一对一接到单片机的控制引脚。因为32的引脚驱动能力有限,在阴极输出可以保证信号的有效性。
接线图:(图中的EN没有接线,因为默认EN不接线时是有效状态,信号线接线是共阳极)

再另外的TB6600的驱动能力相对于DRV8825的驱动能力也要更强,它最大的带载能力达到了4A,24V,可以控制从小到大的接大部分42和57步进电机。限制它的应该就是驱动器的体积了。


废话不多说,直接来代码部分:

GPIO模拟

这里无论使用TB6612还是DRV8825,接线就不在赘述。
使用GPIO模拟的核心就是改变循环中间隔定时改变引脚的电平,模拟PWM的脉冲输出,这个方式是驱动代码写起来最简单的,但不是特别稳定。
代码参考文章最后的完整代码。

主从定时器模式控制

下面通过STM32F407控制器为例,使用两个定时器,TIM9+TIM10,主定时器负责定时,从定时器负责输出固定频率的脉冲。通过两个定时器的配合达到最终在固定时间段内(控制电机转速)输出一定脉冲(控制电机旋转角度)控制步进电机转动。
首先需要介绍一下头文件的宏定义,在代码中通过定义结构体的方式来定义一个电机的运动状态。并进行条件选择,在.文件中进行条件编译,达到一套代码适用多种情况的方式,更加便捷和易用。

#define TIMECount    0       //无编码器,主从定时器定时器控制模式
#define AS5600      0       //有编码器,编码器角度闭环模式
#define GPIO_Simulation 1   //无编码器,通过GPIO模拟控制电机转动/*** @brief 设置步进电机控制结构体* @par Direction:设置电机旋转方向,FORWARD为正转,REVERSE为反转* @par State:设置电机是否可以被设置,Free为使能,Busy为禁用* @par setAngle:电机旋转角度*/
typedef struct STEPMotor
{char Direction;char State;float setAngle;float realAngle;
}STEPMotor;#define DRIVER_DIR   PFout(3)    // DVR8825设置旋转方向
#define DRIVER_EN    PFout(5)   // 使能脚 低电平有效 #define     FORWARD         0       //步进正转
#define     REVERSE         1       //步进反转
#define     Free            0       //步进电机状态空闲
#define     Busy            1       //步进电机状态忙,不可被设置
#define     MotorCorrectionAngle  6400  //步进电机走360度需要的步数

好了我们来直接说一下具体的控制代码:

初始化电机状态结构体
/*** @brief 初始化步进电机控制机构体的所有数据* @param motor 步进电机控制机构体*/
void StepMotor_InitData_STEPMotorStruct(STEPMotor *motor)
{motor->Direction = FORWARD;motor->realAngle = 0;motor->setAngle = 0;motor->State = Free;
}
定时器10输出PWM脉冲初始化函数,TIM9初始化
/*** @brief 定时器10输出PWM脉冲初始化函数* @param Period :定时器自动重装载值* @param Prescaler :定时器分频系数* Time10时钟总线为APB2,是SYSCLK的2分频,为84MHz。当Prescaler=1680,Period=50时,输出频率为*                                                     (84M/1680)/50=1kHz*/
void StepMotor_TIM10PWMsteep_ProduceInit(int Period, int Prescaler)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10,ENABLE);   //TIM10时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); GPIO_PinAFConfig(GPIOF,GPIO_PinSource6,GPIO_AF_TIM10);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOF,&GPIO_InitStructure);TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_TimeBaseStructure.TIM_Prescaler=50;                  //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=1680;                     //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;  TIM_TimeBaseInit(TIM10,&TIM_TimeBaseStructure);            //x=9~14TIM_OCInitTypeDef  TIM_OCInitStructure;TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;             //选择PWM模式TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCNPolarity_High;      //输出极性低TIM_OC1Init(TIM10, &TIM_OCInitStructure);                      //初始化定时器x通道1,x=9~14TIM_OC1PreloadConfig(TIM10, TIM_OCPreload_Enable);  //使能定时器x在CCR1上的预装载寄存器,x=9~14TIM_ARRPreloadConfig(TIM10,ENABLE);//定时器x的ARPE使能,x=9~14TIM_Cmd(TIM10, DISABLE);TIM_SetCompare1(TIM10,420);
}/*** @brief 定时器9定时初始化* 定时频率为4kHz,用于计时使能TIM10输出脉冲*/
void StepMotor_TIM9Timing_ProduceInit(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE); ///使能TIM7时钟TIM_TimeBaseInitStructure.TIM_Period = 0;                       //自动重装载值TIM_TimeBaseInitStructure.TIM_Prescaler = 42000;               //定时器分频 频率4kHzTIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIM9, &TIM_TimeBaseInitStructure); //初始化TIM9TIM_ITConfig(TIM9, TIM_IT_Update, ENABLE); //允许定时器9更新中断NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_TIM9_IRQn;     //定时器9中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; //抢占优先级1//抢占优先级高的会优先抢占优先级低的,优先得到执行。(注意:优先级数字越小,优先级越高)NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级1//抢占优先级相同,不涉及到中断嵌套,响应优先级不同,响应优先级高的先响应NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}/*** @brief 定时器9的中断处理函数* */
void TIM1_BRK_TIM9_IRQHandler(void)
{if(TIM_GetITStatus(TIM9,TIM_IT_Update)==SET) //溢出中断{TIM_Cmd(TIM10,DISABLE);ActionReady = 0;     //定时器时长结束标志}TIM_ClearITPendingBit(TIM9,TIM_IT_Update);  //清除中断标志位TIM_Cmd(TIM9,DISABLE);
}
步进电机驱动器DRV8825驱动器初始化
/*** @brief 步进电机驱动器DRV8825驱动器初始化* //DIR 6
//STEP 5
//MS 4
//EN 3
//VCC 2
//GND 1*/
void StepMotor_Driver_GpioInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOG时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; // DRIVER_DIR DRIVER_OE对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;          //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;       // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOG, &GPIO_InitStructure);                  //初始化GPIOG3,4GPIO_ResetBits(GPIOG, GPIO_Pin_3); // PG3输出低 使能输出  DRIVER_ENAGPIO_SetBits(GPIOG, GPIO_Pin_4);   // PG4输出高 顺时针方向  DRIVER_DIRGPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; // DRIVER_OE对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                          //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;                       // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                        //上拉GPIO_Init(GPIOC, &GPIO_InitStructure);                                  //初始化GPIOG3,4GPIO_SetBits(GPIOC, GPIO_Pin_10);                                      //全部拉高32细分,可以不接这3个GPIO,在电路上全部给3.3VGPIO_SetBits(GPIOC, GPIO_Pin_11);GPIO_SetBits(GPIOC, GPIO_Pin_12);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_ResetBits(GPIOB, GPIO_Pin_2);}
步进电机控制转动给定角度函数
/*** @brief 步进电机控制转动给定角度函数* @param STEPMotor 传入电机控制结构体指针*  * @par Direction:设置电机旋转方向,FORWARD为正转,REVERSE为反转*  * @par State:设置电机是否可以被设置,Free为空闲,Busy为忙不可设置,最好不要人工改变,初始化的时候赋值为0就可以*  * @par setAngle:电机旋转角度,最小值0,最大值720度** 函数使用举例:*  在主函数中使用该函数控制步进电机正向旋转90度:int main() {......STEPMotor stpmotor = {0};stpmotor.Direction = FORWARD; //正转stpmotor.setAngle = 90;        //90度StepMotor_SetRotationAngle(&stpmotor); //旋转一次}*/
void StepMotor_SetRotationAngle(STEPMotor *motor)
{   if(ActionReady == 0) motor->State = Free;if(motor->State == Free){if(motor->Direction == FORWARD){DRIVER_DIR = 1;}else DRIVER_DIR = 0;float tim = 0;tim = (motor->setAngle/360)*MotorCorrectionAngle;tim = tim / 2000*4000; // 2000为PWM脉冲发出定时器TIM10的频率,4000为时长定时器TIM9频率motor->State = Busy;TIM_SetAutoreload(TIM9, tim);TIM_Cmd(TIM9, ENABLE);TIM_Cmd(TIM10, ENABLE);ActionReady = 1;}
}

注意在移植使用的时候要灵活选择配置引脚,32的每个外设都有多个引脚通道,能够合适的使用对应引脚会大大的降低硬件布线的复杂性,这里的代码主要是参考一个配置的思路。


编码器闭环模式

这里使用的编码器为AS5600磁霍尔式编码器,淘宝有成品可以买到,他是模拟采集数字输出的一个传感器,AD转换的精度达到了12位,在一般的场景中这个精度的编码器完全够用了。AS5600在一些店家的设计下有PWM输出和模拟电压输出以及I2C输出等等的方式,这里直接使用I2C模式,这个模式下读取到的数据直接就是编码器内部的寄存器的值,而且配置也更加简单。
在使用这个模式时,默认你应该明白了上面的使用一种驱动器让步进电机转起来,并了解I2C总线的原理。

IIC读取AS5600的角度数据
/*** @brief IIC读取AS5600的角度数据* @param deviceaddr 器件的从机地址,从机的7位地址是0x36 (二进制为0110110)* @param readaddr 需要读取的数据寄存器地址,AS5600的角度为两个0x0E(8:11)和0x0F(0:7)两个寄存器前7位组成* @return u8 返回一个寄存器中读出的1bit数据*/
u8 AS5600_IIC_Read_OneByte(u8 deviceaddr,u8 readaddr)
{u8 temp;IIC_Start();IIC_Send_Byte(deviceaddr&0xfe);IIC_Wait_Ack();IIC_Send_Byte(readaddr);IIC_Wait_Ack();IIC_Start();IIC_Send_Byte(deviceaddr|0x01);IIC_Wait_Ack();temp=IIC_Read_Byte(0);IIC_Stop();return temp;
}
读取当前编码器的旋转角度
/*** @brief 读取当前编码器的旋转角度* @param motor 步进电机控制机构体*/
void StepMotor_ReadAS5600_Date(STEPMotor *motor)
{unsigned int value = 5000;value =  AS5600_IIC_Read_OneByte((0x36<<1),0x0e);   value <<= 8;value |= AS5600_IIC_Read_OneByte((0x36<<1),0x0f); if(value<=4096)motor->realAngle = (float)(value/4096)*360;
}
步进电机角度初始化

这里相当于舵机归中,就是在上电的时候将步进轴旋转到初始位置。

/*** @brief 步进电机角度初始化*/
void StepMotor_Init_SetAngle(STEPMotor *motor)
{StepMotor_ReadAS5600_Date(motor);if(motor->realAngle > motor->setAngle){DRIVER_DIR = FORWARD;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if((motor->realAngle - motor->setAngle) <= 4) state = 0;}while (state);TIM_Cmd(TIM10,DISABLE);}else if(motor->realAngle < motor->setAngle){DRIVER_DIR = REVERSE;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if((motor->setAngle - motor->realAngle) <= 4) state = 0;}while (state);TIM_Cmd(TIM10,DISABLE);}
}
步进电机闭环旋转到固定角度
/*** @brief 步进电机闭环旋转到固定角度* @param motor 步进电机控制机构体* 使用函数举例:*         STEPMotor stpmotor;*        StepMotor_InitData_STEPMotorStruct(&stpmotor);*         StepMotor_Init_SetAngle(&stpmotor);*        stpmotor->setAngle = 98;*       StepMotor_SetRotationAngle(&stpmotor);*/
void StepMotor_SetRotationAngle(STEPMotor *motor)
{if (motor->State == Free){motor->State = Busy;if (motor->realAngle > motor->setAngle){DRIVER_DIR = FORWARD;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if ((motor->realAngle - motor->setAngle) <= 4)state = 0;} while (state);TIM_Cmd(TIM10, DISABLE);}else if (motor->realAngle < motor->setAngle){DRIVER_DIR = REVERSE;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if ((motor->setAngle - motor->realAngle) <= 4)state = 0;} while (state);TIM_Cmd(TIM10, DISABLE);}}
}

代码完整版

代码的配置是相对不变的,引脚的分配和控制的逻辑要灵活运用

StepperMotor.h

#ifndef __STEPPERMOTOR_H
#define __STEPPERMOTOR_H  #include "sys.h"#define TIMECount   0       //无编码器,主从定时器定时器控制模式
#define AS5600      0       //有编码器,编码器角度闭环模式
#define GPIO_Simulation 1/*** @brief 设置步进电机控制结构体* @par Direction:设置电机旋转方向,FORWARD为正转,REVERSE为反转* @par State:设置电机是否可以被设置,Free为使能,Busy为禁用* @par setAngle:电机旋转角度*/
typedef struct STEPMotor
{char Direction;char State;float setAngle;float realAngle;
}STEPMotor;#define DRIVER_DIR   PFout(3)    // DVR8825设置旋转方向
#define DRIVER_EN    PFout(5)   // 使能脚 低电平有效 #define     FORWARD         0       //步进正转
#define     REVERSE         1       //步进反转
#define     Free            0       //步进电机状态空闲
#define     Busy            1       //步进电机状态忙,不可被设置
#define     MotorCorrectionAngle  6400  //步进电机走360度需要的步数/************** 共用API **************/void StepMotor_TIM10PWMsteep_ProduceInit(int Period, int Prescaler);
void StepMotor_InitData_STEPMotorStruct(STEPMotor *motor);
void StepMotor_Driver_GpioInit(void);
void StepMotor_SetRotationAngle(STEPMotor *motor);/************** 定时器计数模式下私有API **************/#if TIMECount
void StepMotor_TIM9Timing_ProduceInit(void);
void TIM1_BRK_TIM9_IRQHandler(void);
#endif/************** 编码器闭环模式下私有API **************/
#if AS5600
u8 AS5600_IIC_Read_OneByte(u8 deviceaddr,u8 readaddr);
void StepMotor_ReadAS5600_Date(STEPMotor *motor);
void StepMotor_Init_SetAngle(STEPMotor *motor);
#endif/************** GPIO模拟脉冲模式下私有API **************/
#if GPIO_Simulation
#define Rise    0x04
#define Decline 0x05
void StepMotor1_SetRotationRise(uint8_t DIR);
void StepMotor2_SetRotationRise(uint8_t DIR);
void StepMotor3_SetRotationRise(uint8_t DIR);#endif#endif

StepperMotor.c

#include "StepperMotor.h"
#include "myiic.h"
#include "delay.h"/*** @brief 初始化步进电机控制机构体的所有数据* @param motor 步进电机控制机构体*/
void StepMotor_InitData_STEPMotorStruct(STEPMotor *motor)
{motor->Direction = FORWARD;motor->realAngle = 0;motor->setAngle = 0;motor->State = Free;
}/*** @brief 定时器10输出PWM脉冲初始化函数* @param Period :定时器自动重装载值* @param Prescaler :定时器分频系数* Time10时钟总线为APB2,是SYSCLK的2分频,为84MHz。当Prescaler=1680,Period=50时,输出频率为*                                                   (84M/1680)/50=1kHz*/
void StepMotor_TIM10PWMsteep_ProduceInit(int Period, int Prescaler)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM10,ENABLE);   //TIM10时钟使能RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE); GPIO_PinAFConfig(GPIOF,GPIO_PinSource6,GPIO_AF_TIM10);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;           GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;        //复用功能GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  //速度100MHzGPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽复用输出GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOF,&GPIO_InitStructure);TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;TIM_TimeBaseStructure.TIM_Prescaler=50;                  //定时器分频TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseStructure.TIM_Period=1680;                     //自动重装载值TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;  TIM_TimeBaseInit(TIM10,&TIM_TimeBaseStructure);            //x=9~14TIM_OCInitTypeDef  TIM_OCInitStructure;TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;             //选择PWM模式TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCNPolarity_High;      //输出极性低TIM_OC1Init(TIM10, &TIM_OCInitStructure);                      //初始化定时器x通道1,x=9~14TIM_OC1PreloadConfig(TIM10, TIM_OCPreload_Enable);  //使能定时器x在CCR1上的预装载寄存器,x=9~14TIM_ARRPreloadConfig(TIM10,ENABLE);//定时器x的ARPE使能,x=9~14TIM_Cmd(TIM10, DISABLE);TIM_SetCompare1(TIM10,420);
}#if TIMECount
char ActionReady = 0;/*** @brief 步进电机驱动器DRV8825驱动器初始化* //DIR 6
//STEP 5
//MS 4
//EN 3
//VCC 2
//GND 1*/
void StepMotor_Driver_GpioInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOG | RCC_AHB1Periph_GPIOB, ENABLE); //使能GPIOG时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4; // DRIVER_DIR DRIVER_OE对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;          //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;       // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOG, &GPIO_InitStructure);                  //初始化GPIOG3,4GPIO_ResetBits(GPIOG, GPIO_Pin_3); // PG3输出低 使能输出  DRIVER_ENAGPIO_SetBits(GPIOG, GPIO_Pin_4);   // PG4输出高 顺时针方向  DRIVER_DIRGPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12; // DRIVER_OE对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                          //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;                       // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                        //上拉GPIO_Init(GPIOC, &GPIO_InitStructure);                                  //初始化GPIOG3,4GPIO_SetBits(GPIOC, GPIO_Pin_10);                                      //全部拉高32细分,可以不接这3个GPIO,在电路上全部给3.3VGPIO_SetBits(GPIOC, GPIO_Pin_11);GPIO_SetBits(GPIOC, GPIO_Pin_12);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_ResetBits(GPIOB, GPIO_Pin_2);}/*** @brief 定时器9定时初始化* 定时频率为4kHz,用于计时使能TIM10输出脉冲*/
void StepMotor_TIM9Timing_ProduceInit(void)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM9, ENABLE); ///使能TIM7时钟TIM_TimeBaseInitStructure.TIM_Period = 0;                       //自动重装载值TIM_TimeBaseInitStructure.TIM_Prescaler = 42000;               //定时器分频 频率4kHzTIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInit(TIM9, &TIM_TimeBaseInitStructure); //初始化TIM7TIM_ITConfig(TIM9, TIM_IT_Update, ENABLE); //允许定时器9更新中断NVIC_InitStructure.NVIC_IRQChannel = TIM1_BRK_TIM9_IRQn;     //定时器9中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03; //抢占优先级1//抢占优先级高的会优先抢占优先级低的,优先得到执行。(注意:优先级数字越小,优先级越高)NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03; //子优先级1//抢占优先级相同,不涉及到中断嵌套,响应优先级不同,响应优先级高的先响应NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);
}/*** @brief 定时器9的中断处理函数* */
void TIM1_BRK_TIM9_IRQHandler(void)
{if(TIM_GetITStatus(TIM9,TIM_IT_Update)==SET) //溢出中断{TIM_Cmd(TIM10,DISABLE);ActionReady = 0;     //定时器时长结束标志}TIM_ClearITPendingBit(TIM9,TIM_IT_Update);  //清除中断标志位TIM_Cmd(TIM9,DISABLE);
}/*** @brief 步进电机控制转动给定角度函数* @param STEPMotor 传入电机控制结构体指针*  * @par Direction:设置电机旋转方向,FORWARD为正转,REVERSE为反转*  * @par State:设置电机是否可以被设置,Free为空闲,Busy为忙不可设置,最好不要人工改变,初始化的时候赋值为0就可以*  * @par setAngle:电机旋转角度,最小值0,最大值720度** 函数使用举例:*  在主函数中使用该函数控制步进电机正向旋转90度:int main() {STEPMotor stpmotor = {0};stpmotor.Direction = FORWARD; //正转stpmotor.setAngle = 90;        //90度StepMotor_SetRotationAngle(&stpmotor); //旋转一次}*/
void StepMotor_SetRotationAngle(STEPMotor *motor)
{   if(ActionReady == 0) motor->State = Free;if(motor->State == Free){if(motor->Direction == FORWARD){DRIVER_DIR = 1;}else DRIVER_DIR = 0;float tim = 0;tim = (motor->setAngle/360)*MotorCorrectionAngle;tim = tim / 2000*4000; // 2000为PWM脉冲发出定时器TIM10的频率,4000为时长定时器TIM9频率motor->State = Busy;TIM_SetAutoreload(TIM9, tim);TIM_Cmd(TIM9, ENABLE);TIM_Cmd(TIM10, ENABLE);ActionReady = 1;}
}
#endif#if AS5600
#include <math.h>/*** @brief 步进电机驱动器DRV8825驱动器初始化* //DIR 6       PF3
//STEP 5
//MS 4
//EN 3          PF5
//VCC 2
//GND 1*/
void StepMotor_Driver_GpioInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF , ENABLE); //使能GPIOG时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_5; // DRIVER_DIR DRIVER_EN对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;           //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;       // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOF, &GPIO_InitStructure);                  //初始化GPIOG3,4GPIO_ResetBits(GPIOF, GPIO_Pin_5); // PG3输出低 使能输出  DRIVER_ENAGPIO_SetBits(GPIOF, GPIO_Pin_3);   // PG4输出高 顺时针方向  DRIVER_DIR
}/*** @brief IIC读取AS5600的角度数据* @param deviceaddr 器件的从机地址,从机的7位地址是0x36 (二进制为0110110)* @param readaddr 需要读取的数据寄存器地址,AS5600的角度为两个0x0E(8:11)和0x0F(0:7)两个寄存器前7位组成* @return u8 返回一个寄存器中读出的1bit数据*/
u8 AS5600_IIC_Read_OneByte(u8 deviceaddr,u8 readaddr)
{u8 temp;IIC_Start();IIC_Send_Byte(deviceaddr&0xfe);IIC_Wait_Ack();IIC_Send_Byte(readaddr);IIC_Wait_Ack();IIC_Start();IIC_Send_Byte(deviceaddr|0x01);IIC_Wait_Ack();temp=IIC_Read_Byte(0);IIC_Stop();return temp;
}/*** @brief 读取当前编码器的旋转角度* @param motor 步进电机控制机构体*/
void StepMotor_ReadAS5600_Date(STEPMotor *motor)
{unsigned int value = 5000;value =  AS5600_IIC_Read_OneByte((0x36<<1),0x0e);   value <<= 8;value |= AS5600_IIC_Read_OneByte((0x36<<1),0x0f); if(value<=4096)motor->realAngle = (float)(value/4096)*360;
}/*** @brief 步进电机角度初始化*/
void StepMotor_Init_SetAngle(STEPMotor *motor)
{StepMotor_ReadAS5600_Date(motor);if(motor->realAngle > motor->setAngle){DRIVER_DIR = FORWARD;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if((motor->realAngle - motor->setAngle) <= 4) state = 0;}while (state);TIM_Cmd(TIM10,DISABLE);}else if(motor->realAngle < motor->setAngle){DRIVER_DIR = REVERSE;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if((motor->setAngle - motor->realAngle) <= 4) state = 0;}while (state);TIM_Cmd(TIM10,DISABLE);}
}/*** @brief 步进电机闭环旋转到固定角度* @param motor 步进电机控制机构体* 使用函数举例:*       STEPMotor stpmotor;*        StepMotor_InitData_STEPMotorStruct(&stpmotor);*         StepMotor_Init_SetAngle(&stpmotor);*        stpmotor->setAngle = 98;*       StepMotor_SetRotationAngle(&stpmotor);*/
void StepMotor_SetRotationAngle(STEPMotor *motor)
{if (motor->State == Free){motor->State = Busy;if (motor->realAngle > motor->setAngle){DRIVER_DIR = FORWARD;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if ((motor->realAngle - motor->setAngle) <= 4)state = 0;} while (state);TIM_Cmd(TIM10, DISABLE);}else if (motor->realAngle < motor->setAngle){DRIVER_DIR = REVERSE;int state = 1;TIM_Cmd(TIM10, ENABLE);do{StepMotor_ReadAS5600_Date(motor);if ((motor->setAngle - motor->realAngle) <= 4)state = 0;} while (state);TIM_Cmd(TIM10, DISABLE);}}
}#endif#if GPIO_Simulationvoid StepMotor_Driver_GpioInit(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOE | RCC_AHB1Periph_GPIOG, ENABLE); //使能GPIOG时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_3 | GPIO_Pin_1; // DRIVER_DIR DRIVER_EN对应引脚GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;          //普通输出模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;         //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;    // 100MGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉GPIO_Init(GPIOF, &GPIO_InitStructure);                  //初始化GPIOG3,4GPIO_ResetBits(GPIOF, GPIO_Pin_5 | GPIO_Pin_3 | GPIO_Pin_1); // PG3输出低 使能输出  DRIVER_ENAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOC, &GPIO_InitStructure);GPIO_ResetBits(GPIOC, GPIO_Pin_13);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6;GPIO_Init(GPIOE, &GPIO_InitStructure);GPIO_ResetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_6);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;GPIO_Init(GPIOE, &GPIO_InitStructure);GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_2);GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_Init(GPIOG, &GPIO_InitStructure);GPIO_ResetBits(GPIOG, GPIO_Pin_13);
}/*** @brief 通过GPIO模拟PWM信号,驱动步进电机旋转* * @param DIR 正反转信号*       PF3 PF1 PF5*/
void StepMotor1_SetRotationRise(uint8_t DIR)
{if(DIR == Rise){GPIO_SetBits(GPIOF, GPIO_Pin_3);}GPIO_SetBits(GPIOF, GPIO_Pin_1);int rise = 5000;int i = 0;while (i<rise){GPIO_SetBits(GPIOF, GPIO_Pin_5);delay_us(500);GPIO_ResetBits(GPIOF, GPIO_Pin_5);delay_us(500);i++;}GPIO_ResetBits(GPIOF, GPIO_Pin_5 | GPIO_Pin_3 | GPIO_Pin_1);
}/*** @brief PE6 PE4 PC13* * @param DIR */
void StepMotor2_SetRotationRise(uint8_t DIR)
{if(DIR == Rise){GPIO_SetBits(GPIOE, GPIO_Pin_6);}GPIO_SetBits(GPIOE, GPIO_Pin_4);int rise = 5000;int i = 0;while (i<rise){GPIO_SetBits(GPIOC, GPIO_Pin_13);delay_us(500);GPIO_SetBits(GPIOC, GPIO_Pin_13);delay_us(500);i++;}GPIO_ResetBits(GPIOE, GPIO_Pin_4 | GPIO_Pin_6);GPIO_ResetBits(GPIOC, GPIO_Pin_13);
}/*** @brief PG13 PE0 PE2* * @param DIR */
void StepMotor3_SetRotationRise(uint8_t DIR)
{if(DIR == Rise){GPIO_SetBits(GPIOG, GPIO_Pin_13);}GPIO_SetBits(GPIOE, GPIO_Pin_0);int rise = 5000;int i = 0;while (i<rise){GPIO_SetBits(GPIOE, GPIO_Pin_2);delay_us(500);GPIO_SetBits(GPIOE, GPIO_Pin_2);delay_us(500);i++;}GPIO_ResetBits(GPIOE, GPIO_Pin_0 | GPIO_Pin_2);GPIO_ResetBits(GPIOG, GPIO_Pin_13);
}#endif // DEBUG 

STM32控制步进电机运三种方式控制源码详解:主从定时器+编码器闭环+GPIO模拟(基于【TB6600】【DRV8825】驱动器)相关推荐

  1. STM32芯片烧录的三种方式介绍,串口、STM32 ST-LINK Utility以及STM32CubeProgrammer

    STM32芯片烧录的三种方式介绍,串口.STM32 ST-LINK Utility以及STM32CubeProgrammer 1 概述 1.1资源概述 1.2 STM32串口烧录方式 2.KEIL软件 ...

  2. java 文件下载详解_Java 从网上下载文件的几种方式实例代码详解

    废话不多说了,直接给大家贴代码了,具体代码如下所示: package com.github.pandafang.tool; import java.io.BufferedOutputStream; i ...

  3. (03)_k8s之flannel三种模型安装部署详解

    flannel三种模型安装部署详解 yht_1990关注[2020-10-04 12:13:47](javascript:

  4. 【虚幻引擎UE】UE5 三种模式调用API详解(案例基于免费Varest插件)

    [虚幻引擎UE]UE5 三种模式调用API详解(案例基于免费Varest插件) 想通过UE5 调用API实现GET和POST, 可以通过自己编写C++方法, 或基于相关HTTP请求插件, 如Vares ...

  5. IOC控制反转的三种方式

    IOC控制反转三种方式 (1).在介绍之前,来了解一下注入的概念:IoC(控制反转)即依赖注入,就是指程序在运行过程中,如果需要另外一个对象协助完成时,无需在代码中创建被调用者,而是依赖外部的注入获取 ...

  6. STM32三种BOOT启动模式详解(全网最全)

    一.三种boot启动模式 一般来说就是指我们下好程序后,重启芯片时,SYSCLK的第4个上升沿,BOOT引脚的值将被锁存.用户可以通过设置BOOT1和BOOT0引脚的状态,来选择在复位后的启动模式. ...

  7. Jenkins项目常用三种构建类型风格详解

    Jenkins构建的项目类型介绍 jenkins 的安装配置请参考:<jenkins war包安装部署,tomcat+JDK+maven> Jenkins中自动构建项目的类型有很多,常用的 ...

  8. SPI、I2C、UART(即串口)三种串行总线详解

    以下内容均来源于网络资源的学习与整理,如有侵权请告知删除. 参考博客 几个串口协议学习整理 UART IIC SPI_mainn的博客-CSDN博客 SPI.I2C.UART三种串行总线的原理.区别及 ...

  9. Docker学习:外部浏览器访问容器 | 容器访问容器 | 访问容器的常用5种方式 | -p -P 详解

    前言 本讲是从Docker系列讲解课程,单独抽离出来的一个小节,主要介绍容器间内部相互访问和外部访问容器的一些方法,它和前面两篇:容器五种(3+2)网络模式.容器之间单/双向通信 |--link /自 ...

  10. redis 三种集群模式详解

    概念:redis有三种集群模式,分别为主从模式,哨兵模式以及集群模式,其中主从是最常见的模式 三种集群模式说明 **·**Sentinel 哨兵模式是为了弥补主从复制集群中主机宕机后,主备切换的复杂性 ...

最新文章

  1. one pragmatical sqlhelper
  2. 11月4日下午:植物保护高端论坛 (白洋、韦中)
  3. H3C   ISIS与BGP 路由配置
  4. cocos2d-x 3.1.1 学习笔记[17] 关于这些活动功能
  5. 三体智能革命_《三体》之人类的科技文明发展历史其实很诡异
  6. Objective-C语法之KVC使用 有图有真相
  7. 计算机三级交换机允许中继配置,计算机三级交换机配置命令总结
  8. C# 8.x 先睹为快
  9. JavaEE实战班第19天
  10. WebStorm 关联 TFS(转)
  11. 2.privite私有变量的意义
  12. React 折腾记 - (4) 侧边栏联动Tabs菜单-增强版(结合Mobx)
  13. maven部署项目到tomcat8中
  14. 009-Shell 函数
  15. nero linux4 序列号,Nero Linux 4 Nero Linux v4.0.0.0b
  16. 路由器回执路由配置_IT菜鸟之路由器基础配置(静态、动态、默认路由)
  17. 五步帮你实现用户画像的数据加工
  18. vue如何引入外部js文件,待解决,急!!!
  19. 换购优惠 java代码_商品换购小程序
  20. 实时时钟芯片DS3231应用笔记,使用指南,FAQ全集

热门文章

  1. Codeforces 1090C New Year Presents
  2. python正则表达式提取字符串中的书名_使用python正则表达式从字符串中提取名称...
  3. 鼠标键盘长时间无动作、电脑空闲超过一定时间自动关机、自动睡眠软件 —— 定时执行专家
  4. 微信群导出群成员时间数据_教你一招轻松导出微信群名单
  5. 搭建mqtt服务器mosquitto以及添加CA证书
  6. 【基于Java语言的Android个人开发笔记,开屏引导页】利用ViewPagerFragment实现引导页
  7. monetDb列式存储架构分析
  8. Linux——vi编辑器及文件内容操作
  9. 服务器双路供电原理,服务器专用双电源解析
  10. Win11搜索框恢复成放大镜