stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)

本程序编写基于秉火霸道STM32F103ZET6运行环境。stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第1张图片
今天来玩一下步进电机模块,电机控制,是一门非常高深的学问,如果想去走工控行业需要玩到电机方面的,那么步进电机一定少不了,不管怎么说,我们还是可以把它驱动起来的,以下是我买的一个步进电机驱动模块。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第2张图片
步进电机选用的是:28BYJ48-H12
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第3张图片
这里在软件编程上有一个比较重要参数需要了解一下,就是步距角。
那么什么是步距角呢?度娘给你答案,可以详细看看。
https://baike.baidu.com/item/%E6%AD%A5%E8%B7%9D%E8%A7%92/5946465?fr=aladdin
来看看下面这个换算公式,或许你就明白了。
如上图所示,步距角=5.625°/64
意思就是每64个脉冲步进电机就会转5.625度,因此我们很容易得出以下计算公式:
电机转一圈有360°,那么转一圈的脉冲数 = 360 / 5.625 * 64 = 4096 个脉冲。
进而很容易得到以下角度与脉冲的简单算法:

/*
	Rotation_Angle:旋转角度
	返回:Motor_Pulse 根据公式计算得出的脉冲个数
*/
int Motor_Angle_Cal(int Rotation_Angle)
{
    Motor_Pulse = (int)((double)(Rotation_Angle / 5.625) * 64) ;
    return Motor_Pulse ;
}

下面开始在CubeMX上配置管脚,然后编写程序。

1、stm32CubeMX配置

1.1、设置时钟

stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第4张图片
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第5张图片

1.2、设置步进电机管脚以及按键

PA4、PA5、PA6、PA7默认配置为输出,默认电平为低电平,即各个相默认不工作。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第6张图片
PA0、PC13即为按键,当按下即为高电平,否则为低电平。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第7张图片

1.3、设置调试串口

stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第8张图片

1.4、生成代码工程

stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第9张图片

2、代码逻辑编写

2.1、编写电机驱动程序

motor.h

#ifndef __MOTOR_H
#define __MOTOR_H
#include "main.h"

//4相控制定义
#define MOTOR_A_ON 	HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin, GPIO_PIN_SET);
#define MOTOR_A_OFF HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin, GPIO_PIN_RESET);

#define MOTOR_B_ON 	HAL_GPIO_WritePin(GPIOA, MOTOR_B_Pin, GPIO_PIN_SET);
#define MOTOR_B_OFF	HAL_GPIO_WritePin(GPIOA, MOTOR_B_Pin, GPIO_PIN_RESET);

#define MOTOR_C_ON 	HAL_GPIO_WritePin(GPIOA, MOTOR_C_Pin, GPIO_PIN_SET);
#define MOTOR_C_OFF	HAL_GPIO_WritePin(GPIOA, MOTOR_C_Pin, GPIO_PIN_RESET);

#define MOTOR_D_ON 	HAL_GPIO_WritePin(GPIOA, MOTOR_D_Pin, GPIO_PIN_SET);
#define MOTOR_D_OFF	HAL_GPIO_WritePin(GPIOA, MOTOR_D_Pin, GPIO_PIN_RESET);


extern int direction  ;
extern uint16_t Motor_Pulse ;

//电机脉冲计算
int Motor_Angle_Cal(int Rotation_Angle);
//电机8节拍控制
void MOTOR_CONTROLD(uint8_t step, uint8_t direction);
//关闭电机
void CLOSE_MOTOR(void);

#endif //__MOTOR_H

motor.c

#include "Motor.h"
//电机旋转的方向
int direction = 0 ;
//电机旋转的脉冲个数
uint16_t Motor_Pulse = 0 ;


//电机控制,采用8节拍来做
//A->AB->B->BC->C->CD->D->DA
void MOTOR_CONTROLD(uint8_t step, uint8_t direction)
{
    uint8_t __step = step ;

    //判断电机的旋转方向,如果为1,则逆向旋转
    if(1 == direction)
        __step = 8 - step ;

    switch(__step)
    {
        //A
        case 0:
            MOTOR_A_ON;
            MOTOR_B_OFF;
            MOTOR_C_OFF;
            MOTOR_D_OFF;
            break ;

        //AB
        case 1:
            MOTOR_A_ON;
            MOTOR_B_ON;
            MOTOR_C_OFF;
            MOTOR_D_OFF;
            break ;

        //B
        case 2:
            MOTOR_A_OFF;
            MOTOR_B_ON;
            MOTOR_C_OFF;
            MOTOR_D_OFF;
            break ;

        //BC
        case 3:
            MOTOR_A_OFF;
            MOTOR_B_ON;
            MOTOR_C_ON;
            MOTOR_D_OFF;
            break ;

        //C
        case 4:
            MOTOR_A_OFF;
            MOTOR_B_OFF;
            MOTOR_C_ON;
            MOTOR_D_OFF;
            break ;

        //CD
        case 5:
            MOTOR_A_OFF;
            MOTOR_B_OFF;
            MOTOR_C_ON;
            MOTOR_D_ON;
            break ;

        //D
        case 6:
            MOTOR_A_OFF;
            MOTOR_B_OFF;
            MOTOR_C_OFF;
            MOTOR_D_ON;

        //DA
        case 7:
            MOTOR_A_ON;
            MOTOR_B_OFF;
            MOTOR_C_OFF;
            MOTOR_D_ON;
            break ;
    }
}

//关闭电机
void CLOSE_MOTOR(void)
{
	HAL_GPIO_WritePin(GPIOA, MOTOR_A_Pin | MOTOR_B_Pin | MOTOR_C_Pin | MOTOR_D_Pin, GPIO_PIN_RESET);
}

/*
	Rotation_Angle:旋转角度
	返回:Motor_Pulse 根据公式计算得出的脉冲个数
*/
int Motor_Angle_Cal(int Rotation_Angle)
{
    Motor_Pulse = (int)((double)(Rotation_Angle / 5.625) * 64) ;
    return Motor_Pulse ;
}

2.2、编写按键驱动程序

Key_Scan.h

#ifndef __KEY_SCAN_H
#define __KEY_SCAN_H
#include "main.h"

//按键0扫描
void Key0_Scan(uint8_t *pKeyValue);
//按键1扫描
void Key1_Scan(uint8_t *pKeyValue);


#endif //__KEY_SCAN_H

Key_Scan.c

#include "Key_Scan.h"


void Key0_Scan(uint8_t *pKeyValue)
{
    static char key_status = 0;
    static int key_time = 0 ;

    switch(key_status)
    {
        case 0:     //检测.消抖
            if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1)
                key_status++;

            break;

        case 1:     //确定
            if((key_time < 100) && HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 1)
            {
                key_time++;
                HAL_Delay(10);
            }
            else
            {
                if(key_time >= 80)
                    *pKeyValue = 2 ;		//长按返回2
                else
                    *pKeyValue = 1 ;		//短按返回1

                key_time = 0;
                key_status++;
            }

            /***********************************************************/
            break;

        case 2:     //等待松开
            if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == 0)
                key_status = 0;
            break;

        default:
            break;
    }
}

void Key1_Scan(uint8_t *pKeyValue)
{
    static char key_status = 0;
    static int key_time = 0 ;

    switch(key_status)
    {
        case 0:     //检测.消抖
            if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1)
                key_status++;

            break;

        case 1:     //确定
            if((key_time < 100) && HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 1)
            {
                key_time++;
                HAL_Delay(10);
            }
            else
            {
                if(key_time >= 80)
                    *pKeyValue = 2 ;	//长按返回2
                else
                    *pKeyValue = 1 ;	//短按返回1

                key_time = 0;
                key_status++;
            }

            /***********************************************************/
            break;

        case 2:     //等待松开
            if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == 0)
                key_status = 0;
            break;

        default:
            break;
    }
}

2.3、编写主程序控制逻辑

我们来实现一个,当短按KEY0的时候,顺时针转,长按KEY0时,顺时针微调1°,当短按KEY1的时候,逆时针转,长按KEY1时,逆时针微调1°。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第10张图片
在这里,我们定义使用嘀嗒定时器来驱动电机转动。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第11张图片
我们在中断服务函数这个文件这里讲嘀嗒定期器产生的中断处理添加到这里,跳转进去发现这是个弱函数,也就是带__weak关键字修饰的,这并不是标准C的语法,而是拓展的,并没有实现。
加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。所以我们可以在别的地方定义一个相同名字的函数,而不必也尽量不要修改之前的函数。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第12张图片
接下来我们定义个跟这个弱函数一模一样的处理函数,实现它。
main.c

/* USER CODE BEGIN 4 */
void HAL_SYSTICK_Callback(void)
{
    static uint8_t step = 0 ;
    //如果当前设置的脉冲数不为0
    if(Motor_Pulse)
    {
        //控制步进电机旋转
        MOTOR_CONTROLD(step, direction);
        step++ ;
        if(8 == step)
          step = 0 ;
        //脉冲计数不断减,当减到0时,说明控制到位,即需要执行else的关闭电机步骤。
        Motor_Pulse-- ;
    }
    else
    {
        //关闭电机
        CLOSE_MOTOR();
    }
}

在main.c中,也别忘了定义串口的发送函数噢。

#include 
//定义printf的重定向函数fputc,满足串口调试打印
int fputc(int ch, FILE* file)
{
    return HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, 100);
}

接下来实现主程序逻辑:

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    /* USER CODE BEGIN 1 */
		//角度微调
		int Angle_adjustment = 0 ;
    int count_angle = 0;
		int count_pulse = 0 ;
    uint8_t key0 = 0, key1 = 0;
    /* USER CODE END 1 */


    /* MCU Configuration--------------------------------------------------------*/

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* USER CODE BEGIN Init */

    /* USER CODE END Init */

    /* Configure the system clock */
    SystemClock_Config();

    /* USER CODE BEGIN SysInit */

    /* USER CODE END SysInit */

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_USART2_UART_Init();
    /* USER CODE BEGIN 2 */
		//设置按键一次步进的角度为45度
    count_angle = 45 ;
		//角度微调度数设置为1
		Angle_adjustment = 1 ;
    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */


    while (1)
    {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
        Key0_Scan(&key0);
        Key1_Scan(&key1);
				//控制正转
        if(1 == key0)
        {
            direction = 0 ;	
            count_pulse = Motor_Angle_Cal(count_angle);
						printf("旋转角度:%d度====>脉冲数:%d\n",count_angle,count_pulse);
            key0 = 0 ;
        }
				//正转微调
				else if(2 == key0)
				{
						direction = 0 ;
						count_pulse = Motor_Angle_Cal(Angle_adjustment);
						printf("微调角度:%d度====>脉冲数:%d\n",Angle_adjustment,count_pulse);
						key0 = 0 ;
				}
				
				//控制反转
        if(1 == key1)
        {
            direction = 1 ;	
            count_pulse = Motor_Angle_Cal(count_angle);
						printf("旋转角度:%d度====>脉冲数:%d\n",-count_angle,count_pulse);
            key1 = 0 ;
        }
				//反转微调
				else if(2 == key1)
				{
					direction = 1 ;
					count_pulse = Motor_Angle_Cal(Angle_adjustment);
					printf("微调角度:%d度====>脉冲数:%d\n",Angle_adjustment,count_pulse);
					key1 = 0 ;
				}
    }
    /* USER CODE END 3 */
}

运行结果:
与预期相符合。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第13张图片
源码工程链接:
链接:https://pan.baidu.com/s/11pvYa1HhJKa0-5P9fzIs-g
提取码:9dog

留个小项目给大家来实现:
如图所示,这是电热壶,通过触摸按键可以设置功能,其中加水的转轴可以用步进电机来实现,抽水可以用淘宝买的抽气泵来实现,至于其它的,可以搜索相应的模块来做。
stm32cubeMX学习十五、步进电机基本功能的使用(基于野火STM32F103ZET6开发板)_第14张图片

你可能感兴趣的:(stm32cubeMX)