目录

步进电机(脉冲电机)

步进电机简介

步进电机分类

步进电机控制原理

单极性步进电机驱动原理

双极性步进电机驱动原理

细分器驱动原理

技术指标术语

静态指标术语

动态指标术语

步进电机驱动器介绍

F4电机开发板硬件接口

步进电机基础驱动控制(PWM模式)

步进电机基础驱动控制(比较输出翻转模式)

步进电机定位实验


步进电机(脉冲电机)

步进电机简介

步进电机是一种把电脉冲信号转换为角位移或线位移的电动机。

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

在非超载且不超频的情况下电机的旋转位置只取决于脉冲个数转速只取决脉冲信号的频率。所以说步进电机的开环能力非常优秀,目前也被广泛的应用在各种开环系统上。

常见的二相混合式步进电机步距角:1.8°

常见的三相混合式步进电机步距角:1.2°

常见的五相混合式步进电机步距角:0.72°

步进电机分类

按照磁激励进行分类:永磁式、反应式(磁阻式)、混合式。

类别优点缺点
永磁式动态性能好,输出力矩大步距角过大(一般7.5°或15°),精度较差
磁阻式结构简单,成本低,步距角小可达1.2°噪音和震动过大,动态性能较低
混合式力矩大,动态性能好,步距角小,精度高结构复杂,成本相对较高

按相数区分:相数是指电机内部的线圈的组数,常用的有二相、三相、四相、五相步进电机。

单极性:电流方向单一。

双极性:电流方向双向。

步进电机控制原理

步进电机的工作原理就是控制定子绕组周期性、交替得电,使定子产生磁场,进而控制步进电机转子一步一步的运动。

单极性步进电机驱动原理

单极性步进电机也分类整步和半步驱动方式

双极性步进电机可以通过改变电流方向来改变每相的磁场方向。但单极性不是,有一个公共端直接决定了电流方向。

1、公共端需一直通电,剩下的四相只要有一个相通电即可形成回路产生磁场。

2、公共端不通电,两个相邻的相通电形成回路产生磁场。

整步:

图左为单相激励步进,意思是每一步都只受到了一个磁场的力。

图右为双相激励步进,意思是每一步受到了两个磁场的力。更常用,因为力矩更大

半步: 

        A->AB->B->BC->C->CD->D->DA,转子每次只走半步45°。

双极性步进电机驱动原理

双极性整步驱动,力矩会比单极性驱动大。

图左为二相四线双极性步进电机整部驱动。单相激励步进。

通电顺序为:A相通电(A+接正)→B相通电(B+接正)→A相通电(A-接正)→B相通电(B-接正)。

图右也为二相四线双极性步进电机整部驱动。双相激励步进。更常用,因为力矩更大

通电顺序为:AB相通电(A+B+接正)→AB相通电(A-B+接正)→AB相通电(A-B-接正)→AB相通电(A+B-接正)。

双极性步进电机半步驱动:

通电顺序为:

A相通电(A+接正)→AB相通电(A+B+接正)→B相通电(B+接正)→AB相通电(A-B+接正)→

A相通电(A-接正)→AB相通电(A-B-接正)→B相通电(B-接正)→AB相通电(A+B-接正)。

细分器驱动原理

对于细分器驱动原理,不分单双极步进电机。

细分驱动:将步距角以电流分配方法进行细分化的技术。

原理:磁场强度和电流大小成正比

如果Ia = Ib,那么转子将停在相邻两个线圈的中间,如果不相等,转子将停在电流较大的一侧。

可进行超微小角度,更加平滑的运转,有效地降低振动和噪音的驱动方法。

改变定子的电流比例可以使转子在任意角度停止。

细分的原理:通过改变定子的电流比例,改变转子在一个整步中的不同位置,可以将一个整步分成多个小步来运行。

如果驱动器的细分能力很强,可以将其分成32细分、64细分等。提高了步进电机旋转的顺畅度而且提高了每步的精度。

技术指标术语

静态指标术语

相数:产生不同对极N、S磁场的激励线圈对数,即步进电机中线圈的组数。两相步进电机步距角一般为1.8°,三相步进电机步距角为1.2°。相数越多,步距角就越小。

拍数:完成一个磁场周期性变化所需脉冲数。以四相电机为例,有四相四拍运行方式即AB-BC-CD-DA-AB,四相八拍运行方式即A-AB-B-BC-C-CD-D-DA-A。

步距角:一个脉冲信号所对应的电机转动的角度,这个步距角他不一定是电机实际工作的真正步距角,真正的步距角与驱动器的细分有关。

定位转矩:电机在不通电状态下,电机转子自身的锁定力矩(由磁场齿形的谐波以及机械误差造成的)。

静转矩:电机在额定静态电压作用下,电机不作旋转运动时,电机转矩的锁定力矩。此力矩是衡量电机体积的标准,与驱动电压及驱动电源等无关。

动态指标术语

步距角精度:步进电机转动一个步距角的理论值与实际值的误差。用百分比表示:误差/步距角*100%。

失步:电机运转的步数,不等于理论上的步数。也叫丢步,一般都是因为负载太大或频率太快。

最大空载起动频率:在不加负载的情况下,能够直接起动的最大频率。

最大空载的运行频率:电机不带负载的最高转速频率。

运行矩频特性:输出转矩与输入脉冲频率的关系,是电机选型的根本依据。

电机正反转控制:改变通电顺序而改变电机的正反转。

步进电机驱动器介绍

驱动器的作用是将控制器信号放大或者转换。

控制流程:控制器输出脉冲信号给步进驱动器,步进驱动器输出控制电流的大小控制步进电机。

控制定子绕组周期性、交替得电,进而控制步进电机一步一步的向前运动的这个控制器就是步进电机驱动器。

有细分功能的步进驱动器可以改变步进电机的固有步距角,达到最大的控制精度、降低振动及提高输出转矩。

正点原子步进电机驱动器:ATK-2MD5050

特点:采用12~50VDC供电,输出峰值电流可达5.0A,适合驱动常用的42mm、57mm、86mm两相混合式步进电机,超高分辨率微步进,最大支持256细分。

适合场景:3D打印机、雕刻机、数控机床、包装机械等分辨率要求较高的设备。

建议设置:8细分,1.5A。额定电流1.7A,1.5A时电机过热,可以设置为0.75A。

Tips:拨码开关必须断开电源情况下才可拨动。

正点原子步进电机驱动器:ATK-2MD5050

驱动器和控制器接线有两种:共阴极接法(正引脚高电平有效)共阳极接法(负引脚低电平有效)

实验采用共阳极接法(整体接线如下图):

tips:接线顺序请先接控制信号线,其次连接步进电机动力线,最后接驱动器电源线。

引脚说明:

        使能引脚ENA+、ENA-:此信号有效时,驱动器将自动切断电机绕组电流,使电机处于自由状态(无保持转矩)。

        方向引脚DIR+、DIR-:此信号有效时,电机顺时针旋转;此信号无效时,电机逆时针旋转。

        脉冲引脚PUL+、PUL-:每接收一个脉冲,驱动步进电机旋转一个步距角脉冲频率与转速成正比。

        电机接线端子A+、A-、B+、B-:A+和A-是步进电机的A相绕组的两个接线柱。B+和B-是步进电机的B相绕组的两个接线柱。

F4电机开发板硬件接口

防止外部电流倒灌,损坏芯片,还有增强驱动能力的作用。

图中,使能引脚和方向引脚进行普通光耦隔离,脉冲引脚进行高速光耦隔离。JP2端子用于选择光耦输出信号的电平。 

当SE1_EN为高电平时,ST1_EN-导通ST-(即GND),共阳极接法中为有效电平,即停止电机。反之,SE1_EN为低电平时启动电机。

当SE1_DIR为高电平时,ST1_DIR-导通ST-(即GND),共阳极接法中为有效电平,即顺时针旋转。反之,SE1_EN为低电平时逆时针旋转。

步进电机基础驱动控制(PWM模式)

功能设计:使用PWM模式方法实现控制步进电机的正反转以及控制转速(脉冲频率相关,对应修改定时器的ARR重装载值)。

硬件资源:TIM8_CH1(PI5)、DIR(PF14)、EN(PF15)。

实现步骤:

1、初始化定时器:设置ARR、PSC,计数方式以及脉冲输出模式,使能定时器等。

2、初始化IO:初始化定时器通道IO、方向引脚、脱机使能引脚、按键、LED等。

3、逻辑实现:改变转速(修改ARR大小),改变方向(修改方向引脚高低电平)。

定时器配置:

/* 高级定时器PWM */
TIM_HandleTypeDef g_atimx_handle;           /* 定时器x句柄 */
TIM_OC_InitTypeDef g_atimx_oc_chy_handle;   /* 定时器输出句柄 */ 
/******************************************************************************************/

/**
 * @brief       高级定时器TIMX PWM 初始化函数
 * @note
 *              高级定时器的时钟来自APB2, 而PCLK2 = 168Mhz, 我们设置PPRE2不分频, 因此
 *              高级定时器时钟 = 168Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void atim_timx_oc_chy_init(uint16_t arr, uint16_t psc)
{
    ATIM_TIMX_PWM_CHY_CLK_ENABLE();                             /* TIMX 时钟使能 */

    g_atimx_handle.Instance = ATIM_TIMX_PWM;                    /* 定时器x */
    g_atimx_handle.Init.Prescaler = psc;                        /* 定时器分频 */
    g_atimx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;       /* 向上计数模式 */
    g_atimx_handle.Init.Period = arr;                           /* 自动重装载值 */
    g_atimx_handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;   /* 分频因子 */
    g_atimx_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; /*使能TIMx_ARR进行缓冲*/
    g_atimx_handle.Init.RepetitionCounter = 0;                  /* 开始时不计数*/
    HAL_TIM_PWM_Init(&g_atimx_handle);                          /* 初始化PWM */
    
    g_atimx_oc_chy_handle.OCMode = TIM_OCMODE_PWM1;             /* 模式选择PWM1 */
    g_atimx_oc_chy_handle.Pulse = arr/2;
    g_atimx_oc_chy_handle.OCPolarity = TIM_OCPOLARITY_HIGH;     /* 输出比较极性为低 */
    g_atimx_oc_chy_handle.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    g_atimx_oc_chy_handle.OCFastMode = TIM_OCFAST_DISABLE;
    g_atimx_oc_chy_handle.OCIdleState = TIM_OCIDLESTATE_RESET;
    g_atimx_oc_chy_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
    HAL_TIM_PWM_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle, ATIM_TIMX_PWM_CH1); /* 配置TIMx通道y */
}


/**
 * @brief       定时器底层驱动,时钟使能,引脚配置
                此函数会被HAL_TIM_PWM_Init()调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == ATIM_TIMX_PWM)
    {
        GPIO_InitTypeDef gpio_init_struct;
        ATIM_TIMX_PWM_CHY_CLK_ENABLE();                             /* 开启通道y的CPIO时钟 */
        ATIM_TIMX_PWM_CH1_GPIO_CLK_ENABLE();                        /* IO时钟使能 */

        gpio_init_struct.Pin = ATIM_TIMX_PWM_CH1_GPIO_PIN;          /* 通道y的CPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                    /* 复用推完输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */
        gpio_init_struct.Alternate = ATIM_TIMX_PWM_CHY_GPIO_AF;     /* 端口复用 */
        HAL_GPIO_Init(ATIM_TIMX_PWM_CH1_GPIO_PORT, &gpio_init_struct);
    }
}

GPIO引脚初始化:

/**
 * @brief       初始化步进电机相关IO口, 并使能时钟
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void stepper_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef gpio_init_struct;

    STEPPER_DIR1_GPIO_CLK_ENABLE();                                 /* DIR1时钟使能 */  
    STEPPER_EN1_GPIO_CLK_ENABLE();                                  /* EN1时钟使能 */
    
    gpio_init_struct.Pin = STEPPER_DIR1_GPIO_PIN;                   /* DIR1引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                          /* 下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;                   /* 低速 */
    HAL_GPIO_Init(STEPPER_DIR1_GPIO_PORT, &gpio_init_struct);       /* 初始化DIR1引脚 */
    
    /*   脱机引脚初始化   */
    gpio_init_struct.Pin = STEPPER_EN1_GPIO_PIN;                    /* EN1引脚 */
    HAL_GPIO_Init(STEPPER_EN1_GPIO_PORT, &gpio_init_struct);        /* 初始化EN1引脚 */
    
    atim_timx_oc_chy_init(arr, psc);                                /* 初始化PUL引脚,以及脉冲模式等 */
}
stepper_init(1000 - 1, 168 - 1);         /* 168 000 000 / 168 = 1000 000 1Mhz的计数频率 */

开启、停止步进电机:

/**
 * @brief       开启步进电机
 * @param       motor_num: 步进电机接口序号
 * @retval      无
 */
void stepper_star(uint8_t motor_num)
{
    switch(motor_num)
    {
        case STEPPER_MOTOR_1 :
        {
            HAL_TIM_PWM_Start(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     /* 开启对应PWM通道 */
            break;
        }
        default : 
            break;
    }
}

/**
 * @brief       关闭步进电机
 * @param       motor_num: 步进电机接口序号
 * @retval      无
 */
void stepper_stop(uint8_t motor_num)
{
    switch(motor_num)
    {
        case STEPPER_MOTOR_1 :
        {
            HAL_TIM_PWM_Stop(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     /* 关闭对应PWM通道 */
            break;
        }
        default : 
            break;
    }
}

脱机引脚和方向引脚控制:

/*----------------------- 方向引脚控制 -----------------------------------*/
/* 由于我们使用的是共阳极解法,并且硬件对电平做了取反操作,所以当 x = 1有效,x = 0时无效*/  
#define ST1_DIR(x)    do{ x ? \
                              HAL_GPIO_WritePin(STEPPER_DIR1_GPIO_PORT, STEPPER_DIR1_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(STEPPER_DIR1_GPIO_PORT, STEPPER_DIR1_GPIO_PIN, GPIO_PIN_RESET); \
                        }while(0)  

/*----------------------- 脱机引脚控制 -----------------------------------*/
/* 由于我们使用的是共阳极解法,并且硬件对电平做了取反操作,所以当 x = 1有效,x = 0时无效*/                          
#define ST1_EN(x)      do{ x ? \
                          HAL_GPIO_WritePin(STEPPER_EN1_GPIO_PORT, STEPPER_EN1_GPIO_PIN, GPIO_PIN_SET) : \
                          HAL_GPIO_WritePin(STEPPER_EN1_GPIO_PORT, STEPPER_EN1_GPIO_PIN, GPIO_PIN_RESET); \
                         }while(0)  

修改转速:

uint16_t ccr1 = 0;
ccr1 = __HAL_TIM_GetAutoreload(&g_atimx_handle);

if(KEY1)    /* 增加速度 */
{
    ccr1 -= 50;
    if(ccr1 <=100)
    {
        ccr1 = 100;
    }
    __HAL_TIM_SetAutoreload(&g_atimx_handle, ccr1);
    __HAL_TIM_SetCompare(&g_atimx_handle, ATIM_TIMX_PWM_CH1, ccr1, __HAL_TIM_GetAutoreload(&g_atimx_handle) >> 1);
}
if(KEY2)    /* 减小速度 */
{
    ccr1 += 50;
    if(ccr1 >=900)
    {
        ccr1 = 100;
    }
    __HAL_TIM_SetAutoreload(&g_atimx_handle, ccr1);
    __HAL_TIM_SetCompare(&g_atimx_handle, ATIM_TIMX_PWM_CH1, ccr1, __HAL_TIM_GetAutoreload(&g_atimx_handle) >> 1);
}

步进电机基础驱动控制(比较输出翻转模式)

功能设计:使用比较输出翻转模式方法实现控制步进电机的正反转以及控制转速。

实现步进电机的定位。

硬件资源:TIM8_CH1(PI5)、DIR(PF14)、EN(PF15)。

实现步骤:

1、初始化定时器:设置ARR、PSC,计数方式以及脉冲输出模式,使能定时器以及中断,设置优先级分组等。

2、初始化IO:初始化定时器通道IO、方向引脚、脱机使能引脚、按键、LED等。

3、逻辑实现:改变转速(修改比较值大小),改变方向(修改方向引脚高低电平)。

4、编写中断服务函数:设置比较值,控制运行速度。

翻转模式就是计数值每到比较值一次就会进行一次电平翻转,计数值不会重置。 

只需要修改比较值的增量即可修改频率大小,增量越大频率越小,增量越小频率越大。

定时器初始化:

/* 高级定时器PWM */
TIM_HandleTypeDef g_atimx_handle;               /* 定时器x句柄 */
TIM_OC_InitTypeDef g_atimx_oc_chy_handle;       /* 定时器输出句柄 */ 

/**
 * @brief       高级定时器TIMX,比较输出翻转模式初始化函数
 * @note
 *              高级定时器的时钟来自APB2, 而PCLK2 = 168Mhz, 我们设置PPRE2不分频, 因此
 *              高级定时器时钟 = 168Mhz
 *              定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
 *              Ft=定时器工作频率,单位:Mhz
 *
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void atim_timx_oc_chy_init(uint16_t arr, uint16_t psc)
{
    ATIM_TIMX_PWM_CHY_CLK_ENABLE();                                 /* TIMX 时钟使能 */

    g_atimx_handle.Instance = ATIM_TIMX_PWM;                        /* 定时器x */
    g_atimx_handle.Init.Prescaler = psc;                            /* 定时器分频 */
    g_atimx_handle.Init.CounterMode = TIM_COUNTERMODE_UP;           /* 向上计数模式 */
    g_atimx_handle.Init.Period = arr;                               /* 自动重装载值 */
    g_atimx_handle.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;       /* 分频因子 */
    g_atimx_handle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; /*使能TIMx_ARR进行缓冲*/
    g_atimx_handle.Init.RepetitionCounter = 0;                      /* 开始时不计数*/
    HAL_TIM_OC_Init(&g_atimx_handle);                               /* 初始化PWM */
    
    g_atimx_oc_chy_handle.OCMode = TIM_OCMODE_TOGGLE;               /* 模式选择输出比较翻转模式 */
    g_atimx_oc_chy_handle.Pulse = 0;
    g_atimx_oc_chy_handle.OCPolarity = TIM_OCPOLARITY_HIGH;         /* 输出比较极性为低 */
    g_atimx_oc_chy_handle.OCNPolarity = TIM_OCNPOLARITY_LOW;
    g_atimx_oc_chy_handle.OCFastMode = TIM_OCFAST_DISABLE;
    g_atimx_oc_chy_handle.OCIdleState = TIM_OCIDLESTATE_RESET;
    g_atimx_oc_chy_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
    HAL_TIM_OC_ConfigChannel(&g_atimx_handle, &g_atimx_oc_chy_handle, ATIM_TIMX_PWM_CH1); /* 配置TIMx通道y */
   	HAL_TIM_Base_Start(&g_atimx_handle);
}

/**
 * @brief       定时器底层驱动,时钟使能,引脚配置
                此函数会被HAL_TIM_PWM_Init()调用
 * @param       htim:定时器句柄
 * @retval      无
 */
void HAL_TIM_OC_MspInit(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == ATIM_TIMX_PWM)
    {
        GPIO_InitTypeDef gpio_init_struct;
        ATIM_TIMX_PWM_CHY_CLK_ENABLE();                             /* 开启通道y的CPIO时钟 */
        ATIM_TIMX_PWM_CH1_GPIO_CLK_ENABLE();                        /* IO时钟使能 */ 

        gpio_init_struct.Pin = ATIM_TIMX_PWM_CH1_GPIO_PIN;          /* 通道y的CPIO口 */
        gpio_init_struct.Mode = GPIO_MODE_AF_PP;                    /* 复用推完输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;              /* 高速 */
        gpio_init_struct.Alternate = ATIM_TIMX_PWM_CHY_GPIO_AF;     /* 端口复用 */
        HAL_GPIO_Init(ATIM_TIMX_PWM_CH1_GPIO_PORT, &gpio_init_struct);
        
        HAL_NVIC_SetPriority(ATIM_TIMX_INT_IRQn, 2, 2);
        HAL_NVIC_EnableIRQ(ATIM_TIMX_INT_IRQn);
    }
}

/**
 * @brief       高级定时器中断服务函数
 * @param       无
 * @retval      无
 */
void ATIM_TIMX_INT_IRQHandler(void)
{
    HAL_TIM_IRQHandler(&g_atimx_handle);    /* 定时器共用处理函数 */
}

GPIO引脚初始化:

/**
 * @brief       初始化步进电机相关IO口, 并使能时钟
 * @param       arr: 自动重装值
 * @param       psc: 时钟预分频数
 * @retval      无
 */
void stepper_init(uint16_t arr, uint16_t psc)
{
    GPIO_InitTypeDef gpio_init_struct;

    STEPPER_DIR1_GPIO_CLK_ENABLE();                                 /* DIR1时钟使能 */
    STEPPER_EN1_GPIO_CLK_ENABLE();                                  /* EN1时钟使能 */
    
    gpio_init_struct.Pin = STEPPER_DIR1_GPIO_PIN;                   /* DIR1引脚 */
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;                    /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLDOWN;                          /* 下拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;                   /* 低速 */
    HAL_GPIO_Init(STEPPER_DIR1_GPIO_PORT, &gpio_init_struct);       /* 初始化DIR1引脚 */
    
    /*   脱机引脚初始化   */
    gpio_init_struct.Pin = STEPPER_EN1_GPIO_PIN;                    /* EN1引脚 */
    HAL_GPIO_Init(STEPPER_EN1_GPIO_PORT, &gpio_init_struct);        /* 初始化EN1引脚 */
    
    atim_timx_oc_chy_init(arr, psc);                                /* 初始化PUL引脚,以及脉冲模式等 */
}
stepper_init(0xFFFF, 168 - 1);           /* 168 000 000 / 168 = 1000 000 1Mhz的计数频率 */

开启、停止步进电机:

/**
 * @brief       开启步进电机
 * @param       motor_num: 步进电机接口序号
 * @retval      无
 */
void stepper_star(uint8_t motor_num)
{
    switch(motor_num)
    {
        /* 开启对应PWM通道 */
        case STEPPER_MOTOR_1 :
        {
            if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_TOGGLE)
            {
                HAL_TIM_OC_Start_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     
            }         
            break;
        }
        
        default : 
            break;
    }
}

/**
 * @brief       关闭步进电机
 * @param       motor_num: 步进电机接口序号
 * @retval      无
 */
void stepper_stop(uint8_t motor_num)
{
    switch(motor_num)
    {
        case STEPPER_MOTOR_1 :
        {
            if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_TOGGLE)
            {
                HAL_TIM_OC_Stop_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     
            }
            break;
        }
        default : 
            break;
    }
}
uint16_t g_ccr1 = 500;
uint32_t g_count_val = 0;                   /* 计数值 */

/**
  * @brief  定时器比较中断
  * @param  htim:定时器句柄指针
  * @note   无
  * @retval 无
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    /*获取当前计数*/
    g_count_val = __HAL_TIM_GET_COUNTER(&g_atimx_handle);
    
     /*设置比较数值*/
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    __HAL_TIM_SET_COMPARE(&g_atimx_handle, ATIM_TIMX_PWM_CH1, (g_count_val + g_ccr_val)%0XFFFF);
}

 脱机引脚和方向引脚控制:

/*----------------------- 方向引脚控制 -----------------------------------*/
/* 由于我们使用的是共阳极解法,并且硬件对电平做了取反操作,所以当 x = 1有效,x = 0时无效*/  
#define ST1_DIR(x)    do{ x ? \
                              HAL_GPIO_WritePin(STEPPER_DIR1_GPIO_PORT, STEPPER_DIR1_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(STEPPER_DIR1_GPIO_PORT, STEPPER_DIR1_GPIO_PIN, GPIO_PIN_RESET); \
                        }while(0)  

/*----------------------- 脱机引脚控制 -----------------------------------*/
/* 由于我们使用的是共阳极解法,并且硬件对电平做了取反操作,所以当 x = 1有效,x = 0时无效*/                          
#define ST1_EN(x)      do{ x ? \
                          HAL_GPIO_WritePin(STEPPER_EN1_GPIO_PORT, STEPPER_EN1_GPIO_PIN, GPIO_PIN_SET) : \
                          HAL_GPIO_WritePin(STEPPER_EN1_GPIO_PORT, STEPPER_EN1_GPIO_PIN, GPIO_PIN_RESET); \
                         }while(0)  

修改转速:

extern uint32_t g_ccr_val;                   /* 比较值变值 */

if(KEY1)                           /* 加速 */
{
    g_ccr_val -= 20;
    if(g_ccr_val <= 20) 
    {
        g_ccr_val = 20;
    }
}
else if(KEY2)                           /* 减速 */
{
    g_ccr_val += 20;
    if(g_ccr_val >= 1000) 
    {
        g_ccr_val = 1000;
    }
}

步进电机定位实验

功能设计:实现步进电机的定位。

硬件资源:TIM8_CH1(PI5)、DIR(PF14)、EN(PF15)。

实现步骤:

1、初始化定时器:设置ARR、PSC,计数方式以及脉冲输出模式,使能定时器以及中断,设置优先级分组等。

2、初始化IO:初始化定时器通道IO、方向引脚、脱机使能引脚、按键、LED等。

3、逻辑实现:设置目标位置,转换成脉冲个数,控制脉冲个数输出。

4、编写中断服务函数:判断到指定位置时,停止脉冲输出。

角度与脉冲数之间的关系:

已知两相步进电机步距角为1.8°,驱动器使用8细分,问当电机开发板输出200个脉冲,给步进电机驱动器,此时电机会旋转多少角度?

        1细分--一个步距角为1.8°,1脉冲对应一个步距角,所以200 * 1.8° = 360°

        8细分--一个步距角为1.8° / 8 = 0.225°,1脉冲对应一个步距角,所以200 * 0.225° = 45°

可以得出公式:

        脉冲数 * 电机步距角 / 细分数 = 旋转角度

基础部分:

/******************************************************************************************/
/* 步进电机参数相关宏 */
#define PULSE_REV         1600.0          /* 每圈脉冲数(细分数8) */
#define MAX_STEP_ANGLE    0.225           /* 最小步距(1.8/PULSE_REV) */

typedef struct
{
    int angle;                          /* 设置需要旋转的角度 */
    uint8_t dir;                        /* 方向 */
    uint8_t en;                         /* 使能 */
    volatile uint32_t pulse_count;      /* 脉冲个数记录 */
    volatile int add_pulse_count;       /* 脉冲个数累计 */  
} STEPPER_MOTOR;

extern STEPPER_MOTOR g_stepper;
enum dir
{
    CCW = 0,                            /* 逆时针旋转 */
    CW ,                                /* 顺时针旋转 */
};

设置角度:

/**
 * @brief       将需要转动的角度转换成脉冲数
 * @param       angle    : 需要转动的角度值
 * @param       dir      : 旋转方向
 * @param       motor_num: 步进电机接口序号
 * @retval      无
 */
void stepper_set_angle(uint16_t angle, uint8_t dir, uint8_t motor_num)
{
    g_stepper.pulse_count = angle / MAX_STEP_ANGLE;
    if(g_stepper.pulse_count == 0) 
    {
        /* 关闭对应PWM通道 */
        if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM1||g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM2) 
        {
            HAL_TIM_PWM_Stop(&g_atimx_handle, ATIM_TIMX_PWM_CH1);       
        }
        if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_TOGGLE)
        {
            HAL_TIM_OC_Stop_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     
        }
    }
    else
    {
        ST1_DIR(dir);
        if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM1||g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM2) 
        {
            HAL_TIM_PWM_Start(&g_atimx_handle, ATIM_TIMX_PWM_CH1);      
        }
        if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_TOGGLE)
        {
            HAL_TIM_OC_Start_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);    
        }
    }
}
uint8_t i = 0;

/* 如果是PWM模式,则使用该回调,否则翻转模式使用下面回调 */
/* 中断回调函数 */
//void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

/**
  * @brief  定时器比较中断
  * @param  htim:定时器句柄指针
  * @note   无
  * @retval 无
  */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim)
{
    i++;									/* 每到达一次比较值会进入一次中断 */
    if(i % 2 == 0)							/* 一个脉冲 */
    {
        i = 0;
        g_run_flag = 1;
        g_stepper.pulse_count --;           /* 每一个完整的脉冲就-- */
        if(g_stepper.dir == CW)             /* 正转 */
        {
            g_stepper.add_pulse_count++;    /* 绝对位置++ */
        }else 
        {
            g_stepper.add_pulse_count--;    /* 绝对位置-- */ 
        } 
        if(g_stepper.pulse_count <= 0)      /* 当脉冲数等于0的时候 代表需要发送的脉冲个数已完成,停止定时器输出 */
        {
            printf("累计旋转的角度:%d\r\n",(int)(g_stepper.add_pulse_count * MAX_STEP_ANGLE));  /* 打印累计转动了多少角度 */
            /* 停止接口一输出 */
            /* 关闭对应PWM通道 */
            if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM1||g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_PWM2) 
            {
                HAL_TIM_PWM_Stop(&g_atimx_handle, ATIM_TIMX_PWM_CH1);       
            }
            if(g_atimx_oc_chy_handle.OCMode == TIM_OCMODE_TOGGLE)
            {
                HAL_TIM_OC_Stop_IT(&g_atimx_handle, ATIM_TIMX_PWM_CH1);     
            }
            g_run_flag = 0;
        }
    }
    
    /*获取当前计数*/
    g_count_val = __HAL_TIM_GET_COUNTER(&g_atimx_handle);
    
    /*设置比较数值*/
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
    __HAL_TIM_SET_COMPARE(&g_atimx_handle, ATIM_TIMX_PWM_CH1, (g_count_val + g_ccr_val)%0XFFFF);
}

操作: 

int angle = 0;

if(KEY1)
{
    if(g_run_flag == 0)
    {
        angle += 90;
        if(angle >= 0)
        {
            g_stepper.angle = angle;
            g_stepper.dir = CW;
        }else 
        {
            g_stepper.angle = -angle;
            g_stepper.dir = CCW;
        }
        printf("旋转的角度为:%d\r\n",angle);            
    }
}else if(KEY2)
{
    if(g_run_flag == 0)
    {
        angle -= 90;
        if(angle >= 0)
        {
            g_stepper.angle = angle;
            g_stepper.dir = CW;
        }else 
        {
            g_stepper.angle = -angle;
            g_stepper.dir = CCW;
        }
        printf("旋转的角度为:%d\r\n",angle);            
    }
}else if(KEY3)
{
    if(g_run_flag == 0)
    {
        angle = 0;                              /* 角度清0,以便下次设置 */
        stepper_set_angle(g_stepper.angle, g_stepper.dir, STEPPER_MOTOR_1); /* 开启旋转 */
        printf("开启旋转\r\n");           
    }
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐