描述

这一篇文章紧接上一篇文章stm32按钮控制直流有刷电机(一)(Develop文章3.1)的内容,上一篇文章利用CubeMX对stm32已经进行了配置

这一篇文章将利用代码开发,产生PWM信号完成按钮控制直流有刷减速电机的过程。

研发平台:淘宝上购买的野火店铺的一款单片机,名字叫骄阳开发板,核心芯片是
stm32F407IGT6
电机及驱动板:电机使用的是一款直流有刷减速电机。电机驱动板也是野火家的,型号是L298N电机驱动板

研发步骤

  1. 我们要实现的目的,是按键控制电机的运转。我们的设置是这样的,5个设置管脚的按钮分别负责:电机使能、电机失能、电机速度加快、电机速度减小、电机运转方向转换。我们在生成的工程中,进行一些功能性的代码添加。
    main.c中的头文件用户自定义添加部分

      #include <stdio.h>
     #include <string.h>
    
     #define abs(x) ((x)>0?(x):-(x))
     #define MOTOR_SPEED_MAX 5500
     #define MOTOR_SPEED_MIN 1000
     #define MOTOR_SPEED_INITIAL 2000
     #define MOTOR_SPEED_DELTA 100
    

    我们定义了几个宏定义,MOTOR_SPEED_MAXMOTOR_SPEED_MINMOTOR_SPEED_INITIALMOTOR_SPEED_DELTA分别代表了电机的最大速度、最小速度、初始速度和变化速度。可以看出它们的值,都是和我们之前在CubeMX设置的重装载值是相关的,这些值都是PWM值。

    最大速度的最小被设置为了5500和1000,我们之后再来讨论

  2. main.c中的主函数运行之前的自定义部分,用于捕捉按键事件

      int get_key_status(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
     {
         if (HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_SET)
         {
                 while(HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_SET);
                 return 1;
         }
         else
         {
                 return 0;
         }
     }
    

    按键被摁下后进入循环,放开后,返回1;否则返回0。

  1. main.c中的主函数运行中,while函数运行之前的自定义部分,用于初始设置

      HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
     HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);
     int motor_1_direction = 1;
     int motor_1_speed_current = MOTOR_SPEED_INITIAL;
    
     printf("hello FUXI ROBOT!\n");
    

    HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2);的功能,就是使能电机的1通道和2通道

  2. main.c中的主函数运行中的while函数中的自定义部分,用于处理按钮被摁下后的电机控制代码。这段代码是包在while函数里的,我的工程下while函数中只有以下的部分。

      if (get_key_status(GPIOA, GPIO_PIN_0)) // enable motor
     {
             HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
             HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_2); 
    
             if (motor_1_direction == 1)
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_1_speed_current);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
             }
             else
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, abs(motor_1_speed_current));
             }
             printf("motor 1 on\n");
             printf("motor 1 speed up, current speed:%d, direction:%d\n", motor_1_speed_current, motor_1_direction);
     }
     if (get_key_status(GPIOG, GPIO_PIN_2)) // disable motor
     {
             HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_1);
             HAL_TIM_PWM_Stop(&htim1,TIM_CHANNEL_2); 
             motor_1_speed_current = MOTOR_SPEED_INITIAL * motor_1_direction;
             printf("motor 1 off\n");
     }
     if (get_key_status(GPIOC, GPIO_PIN_13)) // motor speed up
     {
             motor_1_speed_current = motor_1_speed_current + MOTOR_SPEED_DELTA * motor_1_direction;
             if (abs(motor_1_speed_current) >= abs(MOTOR_SPEED_MAX))
             {
                 motor_1_speed_current = MOTOR_SPEED_MAX * motor_1_direction;
             }
             if (abs(motor_1_speed_current) <= abs(MOTOR_SPEED_MIN))
             {
                 motor_1_speed_current = MOTOR_SPEED_MIN * motor_1_direction;
             }
             if (motor_1_direction == 1)
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_1_speed_current);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
             }
             else
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, abs(motor_1_speed_current));
             }
             printf("motor 1 speed up, current speed:%d, direction:%d\n", motor_1_speed_current, motor_1_direction);
     }
     if (get_key_status(GPIOG, GPIO_PIN_3)) // motor speed down 
     {
    
             motor_1_speed_current = motor_1_speed_current - MOTOR_SPEED_DELTA * motor_1_direction;
             if (abs(motor_1_speed_current) >= abs(MOTOR_SPEED_MAX))
             {
                 motor_1_speed_current = MOTOR_SPEED_MAX * motor_1_direction;
             }
             if (abs(motor_1_speed_current) <= abs(MOTOR_SPEED_MIN))
             {
                 motor_1_speed_current = MOTOR_SPEED_MIN * motor_1_direction;
             }
             if (motor_1_direction == 1)
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_1_speed_current);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
             }
             else
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, abs(motor_1_speed_current));
             }
             printf("motor 1 speed down, current speed:%d, direction:%d\n", motor_1_speed_current, motor_1_direction);
     }
     if (get_key_status(GPIOG, GPIO_PIN_4)) // motor direction reverse
     {
             motor_1_direction = - motor_1_direction;
             motor_1_speed_current = -motor_1_speed_current;
             if (motor_1_direction == 1)
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_1_speed_current);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);
             }
             else
             {
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0);
                 __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, abs(motor_1_speed_current));
             }
             printf("motor 1 direction reverse, current speed:%d, direction:%d\n", motor_1_speed_current, motor_1_direction);
     }
    

    代码阅读起来很容易,明显的每个按钮的功能分别被实现了。只说一点`__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, motor_1_speed_current);

        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_2, 0);`
        这两句就是通道1输出PWM,电机正转;else语句中写的,就是通道2输出PWM,电机反转。
    
  1. 除此之外,和之前读取串口数据一样,我们还需要在usart.c中的头文件用户定义部分添加
      #include "stm32f4xx_hal.h"
      #include <stdio.h>
    
在`usart.c`用户代码部分添加
```c    
int fputc(int ch, FILE *f)
 {
   HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
   return ch;
 }
 int fgetc(FILE *f)
 {
   uint8_t ch = 0;
   HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
   return ch;
 }
 ```


  1. 编译并将代码下载到单片机上。

连接说明

代码说完了,接下来我们来说一下,配合本篇文章所提到的工程,完成任务的管脚连接。根据步骤4,我们设置了PWM的单片机输出管脚,打开本工程的CubeMX,我们可以看到TIm1的PWM波生成通道CH1和CH2的输出管脚分别是PE9和PE11

连接单片机——驱动板:我们首先将,单片机的PE9、PE11连接到L298N电机驱动板上的PWM1和PWM2管脚上(L298N上还有PWM3和PWM4管脚,下一篇用到双电机时我再进行讲解),将单片机上的GND管脚连接到L298N驱动板的GND上。驱动板上的EMA和ENB连接到5V上(我的板子上有两个跳帽,已经帮我连接好了)。完成了单片机到驱动板的连接

连接驱动板——直流电机:我使用的直流电机,上面引出了六根线。根据电机说明,我知道其中的4条是编码器的线,这里我们暂时不用。剩下的两条线,管脚上分别写着“M+”和“M-”,这就是直流电机的两根控制线了。我们将电机的“M+”连接到驱动板上电机A输出的1号管脚上,将电机的“M-”连接到驱动板上电机A输出的2号管脚上。完成了驱动板到电机的连接

连接驱动板——直流24V电源,驱动板上的“+”连接到24V电源正极,“-”连接到24V电源负极。

实验现象

我们会发现,按下不同的按钮可以实现不同的电机控制功能,有对应的输出提示,同时电机翻转后加速减速,是与现实一致的。

总结

本文讲述了如何利用CubeMX,对单片机进行设置,实现了按钮控制直流有刷电机的功能。接下来一篇文章,我们来进一步的学习直流电机的控制,包括这篇文章还没有解释清楚的参数设置问题。

最终下一篇文章将完成双直流电机的控制。

写的不容易,欢迎各位朋友点赞并加关注,谢谢。