SmallTop · 1 天前

【基于灵动 Mini-F5375-OB 开发板测评】42BYGH47步进电机控制

项目介绍
由于刚刚好手上有学长遗留下来的步进电机驱动器和步进电机,便使用灵动 Mini-F5375-OB 开发做主控驱动步进电机。
该项目基于灵动 Mini-F5375-OB 开发板结合开关电源LRS-75-24(24v输出电压)、硬石QY1249步进电机驱动器、42BYGH47步进电机套件。实现控制步进电机转动,换向,刹车启动实验。

开关电源LRS-75-24

6e6b9eaeddd119e3380c2b8e205447df.png
参数信息
输出电压:24V
功能:交流电转直流电(AC-DC)
连接方式: 全桥式连接

步进电机介绍

步进电机又称为脉冲电机,基于最基本的电磁铁原理,它是一种可以自由回转的电磁铁,其动作原理是依靠气隙磁导的变化来产生电磁转矩。
电机的转速和停止位置只取决于脉冲信号的频率和数量,步进电机的脉冲与步进旋转的角度成正比,脉冲的频率与步进的转速成正比,步进电机只有周期性的误差,使得在速度、位置等控制领域用步进电机来控制变的非常的简单。
本次实验使用42BYGH47步进电机,如下图:
906b275adc8c08d45dc4b8acad82819b.png
参数信息
型号: 42步进电机
电源方式: 直流电
电压: ≤36V
步角距: 1.8°
保持转矩: 0.4NM
额定电流: 1.5A
相电感: 4mH
相 电阻: 2.19Ω
重量: 0.3KG
电机长度: 40MM
出线数量: 4
防护等级: IP54

工作原理

步进电机的转子为永磁体,当电流流过定子绕组时,定子绕组产生一矢量磁场。磁场会带动转子旋转一定的角度, 使得转子的一对磁场方向与定子的磁场方向一致。当定子的矢量磁场旋转一个角度。转子也随着该磁场转步距角。 每输入一个电脉冲,电动机转动一个角度前进一步。
步进电机图:
05d61108cee402b8b2c32bc510bb39db.png

步进电机极性区分

步进电机又分为单极性的步进电机和双极性的步进电机。
ed59b31f0e977b18ad0e97914403e82d.png
左侧为单极性步进电机,右侧为双极性的步进电机。
图中的红色箭头为电流的走向。
单双极性是指一个步进电机里面有几种电流的流向,四根线的电流走向汇总到公共线。
双极性电机是指电机中有两个电流的回路。

单极性步进电机驱动原理

205f9e154e7ee41ec3c6152b2d9d84bf.png
单极性步进电机整步旋转的过程,在图示中分为5根线,分别为A、B、C、D和公共端(+),公共端需要一直通电, 剩下ABCD相中只要有一个相通电,即可形成回路产生磁场。
图中的通电顺序为A->B->C->D,即可完成上图中的顺时针旋转, 如果想要逆时针旋转只需要将其倒序即可。

单相通电产生的整步旋转,两相通电也可以产生,两个相邻的相通电。图中的通电顺序为AB->BC->CD->DA,同理逆时针旋转的顺序为逆序。
具体看下图:
820882844945a7be549253e9e64d3f49.png
如果通电顺序为:A->AB->B->BC->C->CD->D->DA 转子每次只走半步45度,所以这也被称为半步驱动,与整步相比半步的旋转方式旋转起来更加的顺滑。

双极性步进电机驱动原理

双极性的步进电机整步,步进顺序。

  1. 单相激励步进,每次通电产生磁性的相只有一个,要么A相,要么B相。

第一步:将A相通电,根据电磁铁原理,产生磁性,并且因异性相吸,所以磁场将转子固定在第一步的位置;
第二步:当A相关闭,B相通电时,转子会旋转90°;
第三步:B相关闭、A相通电,但极性与第1步相反,这促使转子再次旋转90°。
第四步:A相关闭、B相通电,极性与第2步相反。
重复该顺序促使转子按90°的步距角顺时针旋转。
86c657e4026f2e07a9497142e2141364.png

  1. 双相激励,两相同时通电的旋转顺序,一次只能换相一次。

第一步:A相通电,B相不通电
第二步:A、B相全部通电,且电流相同,产生相同磁性
第三步:B相通电,A断电
第四步:B相通电,A相通电,且电流相等,产生相同磁性
第五步:A相通电,B断电
第六步:A、B相全部通电,且电流相同,产生相同磁性
第七步:B相通电,A断电
第八步:B相通电,A相通电,且电流相等,产生相同磁性
一个90°的步进电机将每半步移动45°,具体见下图
c9136b2b04d819367c69dfc6c6ebc385.png

细分器驱动原理

细分的原理就是:通过改变定子的电流比例,改变转子在一个整步中的不同位置,可以将一个整步分成多个小步来运行。
66ec1236b66e347d1b2555847a703df1.png
图(a)为A相电流很大,B相的电流极其微弱,接近0;
图(C)为A相和B相的电流相同。
图(b)和图(d)这两个是由于A相和B相的电流不同产生位置情况;

驱动器简介

驱动器起到将控制器信号放大或者转换的作用,如下图所示,控制器输出方向信号和脉冲信号来控制步进电机驱动器, 驱动器将其功率放大然后作用到步进电机上。
c390801c9dc7ebe624f1dfcb05cf5da7.png
f3be95e9ed9868cb1a1858b8aed71ded.png
a509e8d41c062fd8f97c74dae973f1bf.png
驱动器特定:

参数

说明

输入电压:

DC9-40V

输出电流:

0.5-4.0A

细 分:

1,2/A,2/B,4,8,16,32

湿 度:

不能结露,不能有水珠

重 量:

0.2 千克

输入电流:

推荐使用开关电源功率 5A

最大功耗:

160W

温 度:

工作温度-10~45℃;存放温度-40℃~70℃

2) 输入输出端说明
信号输入端
EN-:电机脱机控制负。
EN+:电机脱机控制正。 电机绕组连接
DIR-:电机正、反转控制负。
DIR+:电机正、反转控制正。
PUL-:脉冲信号输入负。
PUL+:脉冲信号输入正。
电机绕组连接
A+:连接电机绕组 A+相。
A-:连接电机绕组 A-相。
B+:连接电机绕组 B+相。
B-:连接电机绕组 B-
电源电压连接
GND:电源负端“-”
VCC:电源正端“+”
接线方式
输入信号接口有两种接法:采用共阳极接法或共阴极接法。
共阴极接法如图所示:
94304f30751117c2d8dcaf6daf97cc5b.png
共阳极接法如图所示:
e22ba33f982ca74c7940d339e8463a19.png

细分数设定

细分数是以驱动板上的拨码开关选择设定的,用户可根据驱动器外盒上 的细分选择表的数据设定(最好在断电情况下设定) 。细分后步进电机步距 角按下列方法计算:步距角=电机固有步距角/细分数。如:一台固有步距角 为 1.8°的步进电机在 4 细分下步距角为 1.8° /4=0.45°驱动板上拨码开关 1、 2、 3、分别对应 S1、 S2、 S3。
628863f35479f69e3b1d71626c72d769.png

电流大小设定

驱动板上拨码开关 4、 5、 6 分别对应 S4、 S5、 S6.
9c70dabc0ec760e0781057e5ea15f844.png

硬件连接

采用共阳极接法:

MM32F5375

硬石QY1249步进电机驱动器

42BYGH47步进电机

PC6

PUL-

黑 (A+)

PC8

DIR-

绿 (A-)

PC9

ENA-

红 (B+)

5V

PUL+/DIR+/ENA+

蓝 (B-)

VCC:接开关电源输出端+;GND:接开关电源输出端-;
注:接线仅供参考,具体情况具体分析;
由于资源有限,最好开发板端IO口需要和驱动器隔离开(光耦隔离:可以有效防止开发板烧毁)如下图:
15c478f372ef18c808d01c8813c4345e.png

软件设计

工程创建:

工程基于灵动官方hal库进行搭建,具体工程目录如下图:
b167ee818aa2ea60df2b55da3adabf95.png
libraries目录:
2c37bf628591944f05ca469f3bdedb92.png
hardware目录:
16ebff7209edbb52190730a004d4a1c7.png
project目录:
d034509973ea1347ec8e7b48f99eb00c.png
mdk目录:
9e22907d2d490b491b6cb8dce26b7039.png
user目录:
240f61553ddde40d1411a837b687c300.png
工程分布:
1cda7f60b9ca2eb91b4b6727151bd6bb.png

代码设计(使用PWM比较输出):

编程要点

  1. LED GPIO初始化以及控制函数。
  2. 按键 GPIO初始化以及控制函数。
  3. 使用RCC实现Delay函数。
  4. 步进电机相关GPIO初始化及控制函数。
  5. 步进电机定时器初始化。

gpio.h

#ifndef _GPIO_H
#define _GPIO_H     

#include "HAL_conf.h" 

//LED
#define LED_1_PORT      GPIOB
#define LED_1_PIN       GPIO_Pin_14
#define LED_2_PORT      GPIOB 
#define LED_2_PIN       GPIO_Pin_15
//KEY
#define KEY_1_PORT      GPIOB
#define KEY_1_PIN       GPIO_Pin_0
#define KEY_2_PORT      GPIOB
#define KEY_2_PIN       GPIO_Pin_1
//步进电机DIR(方向) ENA(使能)
#define STEP_DIR_PORT     GPIOC
#define STEP_DIR_PIN     GPIO_Pin_8
#define STEP_ENA_PORT     GPIOC
#define STEP_ENA_PIN     GPIO_Pin_9

typedef enum
{
    LED1,
    LED2, 
} LEDn_TypeDef;

typedef enum
{
    LED_OFF,
    LED_ON
}LEDs_TypeDef;

typedef enum
{
    KEY1,
    KEY2, 
}KEYn_TypeDef;

typedef enum
{
    KEY_RELEASED ,
    KEY_PRESSED 
}KEYs_TypeDef;
//步进电机开关
typedef enum
{
    STEP_OFF,
    STEP_ON
}STEPs_TypeDef;
//正反转
typedef enum
{
    STEP_DIR_Z,
    STEP_DIR_F
}STEPd_TypeDef;

void led_init(void);
void led_control(LEDn_TypeDef number, LEDs_TypeDef status);

void key_init(void);
KEYs_TypeDef key_scan(KEYn_TypeDef number);

void stepmotor_gpio_init(void);
void stepmotor_enable_control(STEPs_TypeDef status);
void stepmotor_dir_control(STEPd_TypeDef dir);

gpio.c

#include "gpio.h"
#include "delay.h"

void led_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); 

    GPIO_InitStructure.GPIO_Pin  =  LED_1_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(LED_1_PORT, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin  =  LED_2_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(LED_2_PORT, &GPIO_InitStructure);
    
    GPIO_SetBits(LED_1_PORT, LED_1_PIN);
    GPIO_SetBits(LED_2_PORT, LED_2_PIN);   
}

void key_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); 
    
    GPIO_InitStructure.GPIO_Pin  = KEY_1_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(KEY_1_PORT, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin  = KEY_2_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(KEY_2_PORT, &GPIO_InitStructure);
}

void stepmotor_gpio_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); 
    
    GPIO_InitStructure.GPIO_Pin  = STEP_DIR_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(STEP_DIR_PORT, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin  = STEP_ENA_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(STEP_ENA_PORT, &GPIO_InitStructure);
    
    GPIO_SetBits(STEP_DIR_PORT, STEP_DIR_PIN);
    GPIO_ResetBits(STEP_ENA_PORT, STEP_ENA_PIN);
}

void led_control(LEDn_TypeDef number, LEDs_TypeDef status)
{
    switch(number)
    {
        case LED1:
            if (status)
            {
                GPIO_ResetBits(LED_1_PORT, LED_1_PIN);
            } 
            else
            {
                GPIO_SetBits(LED_1_PORT, LED_1_PIN);
            }
        break;
            case LED2:
            if (status)
            {
                GPIO_ResetBits(LED_1_PORT, LED_2_PIN);
            } 
            else
            {
                GPIO_SetBits(LED_2_PORT, LED_2_PIN);
            }
        break;
    }
}

KEYs_TypeDef key_scan(KEYn_TypeDef number)
{
    if (KEY1 == number)
    {
        if (GPIO_ReadInputDataBit(KEY_1_PORT, KEY_1_PIN))
        {    
            delay_ms(10);
            if (GPIO_ReadInputDataBit(KEY_1_PORT, KEY_1_PIN))
            {
                while (GPIO_ReadInputDataBit(KEY_1_PORT, KEY_1_PIN));
                return KEY_PRESSED;
            }
        }
        return KEY_RELEASED;
    }
    if (KEY2 == number)
    {
        if (GPIO_ReadInputDataBit(KEY_2_PORT, KEY_2_PIN))
        {    
            delay_ms(10);
            if (GPIO_ReadInputDataBit(KEY_2_PORT, KEY_2_PIN))
            {
                while (GPIO_ReadInputDataBit(KEY_2_PORT, KEY_2_PIN));
                return KEY_PRESSED;
            }
        }
        return KEY_RELEASED;
    }
}

void stepmotor_enable_control(STEPs_TypeDef status)
{
    if (status)
    {
        GPIO_SetBits(STEP_ENA_PORT, STEP_ENA_PIN);
    } 
    else
    {
        GPIO_ResetBits(STEP_ENA_PORT, STEP_ENA_PIN);
    }
}
    
void stepmotor_dir_control(STEPd_TypeDef dir)
{
    if (dir)
    {
        GPIO_SetBits(STEP_DIR_PORT, STEP_DIR_PIN);
    } 
    else
    {
        GPIO_ResetBits(STEP_DIR_PORT, STEP_DIR_PIN);
    }
}

delay.h

#ifndef _DELAY_H
#define _DELAY_H     

#include "HAL_conf.h" 

#ifdef _PLATFORM_C_
#define EXTERN
#else
#define EXTERN extern
#endif

EXTERN volatile uint32_t DelayTick;

void delay_init(void);
void delay_ms(uint32_t t);

#endif

delay.c

#define _PLATFORM_C_

#include "delay.h"

void delay_init(void)
{
    RCC_ClocksTypeDef RCC_Clocks;
    RCC_GetClocksFreq(&RCC_Clocks);
    
    if (SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000))
    {
        while (1)
        {
        }
    }

    NVIC_SetPriority(SysTick_IRQn, 0x0);
}

void delay_ms(uint32_t t)
{
    DelayTick = t;

    while (0 != DelayTick)
    {
    }
}

timer.h

#ifndef _TEMER_H
#define _TEMER_H     

#include "HAL_conf.h" 

void stepmotor_tim_init(void);

#endif

timer.c

#include "timer.h"

static void tim8_gpio_init(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_3);   /* TIM8_CH1 */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStruct);
}

static void tim8_oc_init(void)
{
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
    TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
    TIM_TimeBaseStruct.TIM_Prescaler         = 72;
    TIM_TimeBaseStruct.TIM_CounterMode       = TIM_CounterMode_Up;
    TIM_TimeBaseStruct.TIM_Period            = 100;
    TIM_TimeBaseStruct.TIM_ClockDivision     = TIM_CKD_Div1;
    TIM_TimeBaseStruct.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStruct);
    TIM_Cmd(TIM8, ENABLE);
    
    TIM_OCInitTypeDef       TIM_OCInitStruct;
    TIM_OCStructInit(&TIM_OCInitStruct);
    TIM_OCInitStruct.TIM_OCMode       = TIM_OCMode_Toggle; //使用翻转模式
    TIM_OCInitStruct.TIM_OutputState  = TIM_OutputState_Enable;
    TIM_OCInitStruct.TIM_Pulse        = 0;
    TIM_OCInitStruct.TIM_OCPolarity   = TIM_OCPolarity_High;
    TIM_OCInitStruct.TIM_OCIdleState  = TIM_OCIdleState_Set;
    TIM_OCInitStruct.TIM_Pulse = 1;
    TIM_OC1Init(TIM8, &TIM_OCInitStruct);
    TIM_CtrlPWMOutputs(TIM8, ENABLE);
}

void stepmotor_tim_init(void)
{
    tim8_oc_init();
    tim8_gpio_init();
}

mm32f370\_it.c

void SysTick_Handler(void)
{
    if (0 != DelayTick)
    {
        DelayTick--;
    }
}

main.c

#define _MAIN_C_

/* Files include */
#include "main.h"
#include "gpio.h"
#include "delay.h"
#include "timer.h"

/* Private typedef ****************************************************************************************************/

/* Private define *****************************************************************************************************/

/* Private macro ******************************************************************************************************/

/* Private variables **************************************************************************************************/
uint8_t stepmotor_enable_flag = 0; 
uint8_t stepmotor_dir_flag = 0;
/* Private functions **************************************************************************************************/

/***********************************************************************************************************************
  * @brief  This function is main entrance
  * @note   main
  * @param  none
  * @retval none
  *********************************************************************************************************************/
int main(void)
{
    delay_init();
    led_init();
    key_init();
    stepmotor_gpio_init();
    stepmotor_tim_init();
    while (1)
    {
        if (key_scan(KEY1))
        {
            stepmotor_enable_flag = !stepmotor_enable_flag;
            stepmotor_enable_control(stepmotor_enable_flag);
            led_control(LED1, stepmotor_enable_flag);
        }
        if (key_scan(KEY2))
        {
            stepmotor_dir_flag = !stepmotor_dir_flag;
            stepmotor_dir_control(stepmotor_dir_flag);
            led_control(LED2, stepmotor_dir_flag);
        }
        delay_ms(100);
    }
}
/********************************************** (C) Copyright MindMotion **********************************************/

下载验证

  1. 将开发板与驱动器连接好;将电机、驱动、电源连接好;
  2. 给开发板供电,编译下载配套源码,复位开发板;

演示效果
按下key1进行步进电机和led1开启和关闭,按下key2进行步进电机的方向控制和led2的开启和关闭(差不了视频,不知道为啥)。
d4f58587a7f0d6fa80543ebfd1e75e3c.png

总结与扩展

本文介绍了灵动 Mini-F5375-OB 开发板结合开关电源LRS-75-24(24v输出电压)、硬石QY1249步进电机驱动器、42BYGH47步进电机套件。实现控制步进电机转动,换向,刹车启动实验,为 MM32F5 系列单片机的应用和快速开发提供了参考。
由于手上没有编码器,所以没有做速度闭环等更高阶的控制,感兴趣的小伙伴可以进行扩展噢。

推荐阅读
关注数
0
文章数
1
嵌入式开发爱好者
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息