这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Let'sdo2025年第4期】基于单片机的直流电机控制-过程贴

共1条 1/1 1 跳转至

【Let'sdo2025年第4期】基于单片机的直流电机控制-过程贴

工程师
2026-02-01 14:51:35     打赏

本期我们要谈的是如何控制电机的正反转以及调速

首先我们先简单介绍一下电机驱动模块

motor-drive.png

我们控制电机的正反转,只需要改变ain1ain2的高低电平组合就可以

举例ain1输出高 ,ain2输出低    电机正转

        ain1输出低 ,ain2输出高    电机反转转


电机转速快慢是由PWMA进行控制的,100%占空比时电机全速运行

0%占空比时电机停止运行


下面shi一些手册描述内容:

drive-way.png

大概原理就是这样

我们需要两路gpio输出高低电平组合信号

                  一路gpio输出占空比可调的PWM信号


同时使用开发板上两个按键用于输入采集

        一个按键控制电机正反转

        一个按键控制PWM信号是否输出以实现电机启停的控制


或不多说,接下来开始编写代码


我们本次基础工程使用的是frdmmcxa153_freertos_hello

以下是两个按键所连接的引脚

接下来通过RGB等的两个引脚去控制电机正反转,这样可以通过灯的颜色轻松辨别电机控制信号引脚状态

rgb-led.png



接下来在放上一张引脚布局图,后面从这里边找一个引脚用于输出pwm信号

pin-out.png

首先我们导入工程

hello-project.png

根据上述原理图配置引脚关系

pin-config.png

至于如何实现按键输入检测及pwm波形控制可参考如下工程

企业微信截图_17699278213940.png

接下来我们添加代码

/*
 * Copyright 2023 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef _APP_H_
#define _APP_H_
/*******************************************************************************
 * Definitions
 ******************************************************************************/
/*${macro:start}*/
#define AIN_1_GPIO       BOARD_LED_RED_GPIO
#define AIN_1_GPIO_PIN   BOARD_LED_RED_GPIO_PIN
#define AIN_2_GPIO       BOARD_LED_GREEN_GPIO
#define AIN_2_GPIO_PIN   BOARD_LED_GREEN_GPIO_PIN
#define KEY_1_GPIO        BOARD_SW2_GPIO
#define KEY_1_GPIO_PIN    BOARD_SW2_GPIO_PIN
#define KEY_1_NAME        BOARD_SW2_NAME
#define KEY_1_IRQ         BOARD_SW2_IRQ
#define KEY_1_IRQ_HANDLER BOARD_SW2_IRQ_HANDLER
#define KEY_2_GPIO        BOARD_SW3_GPIO
#define KEY_2_GPIO_PIN    BOARD_SW3_GPIO_PIN
#define KEY_2_NAME        BOARD_SW3_NAME
#define KEY_2_IRQ         BOARD_SW3_IRQ
#define KEY_2_IRQ_HANDLER BOARD_SW3_IRQ_HANDLER
#define CTIMER          CTIMER1         /* Timer 1 */
#define CTIMER_MAT_OUT  kCTIMER_Match_2 /* Match output 2 */
#define CTIMER_CLK_FREQ CLOCK_GetCTimerClkFreq(1U)
/*${macro:end}*/
/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*${prototype:start}*/
void BOARD_InitHardware(void);
/*${prototype:end}*/
#endif /* _APP_H_ */
/*
 * Copyright 2023 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
/*${header:start}*/
#include "pin_mux.h"
#include "fsl_clock.h"
#include "fsl_reset.h"
#include "board.h"
#include <stdbool.h>
/*${header:end}*/

/*${function:start}*/
void BOARD_InitHardware(void)
{
    CLOCK_EnableClock(kCLOCK_GateGPIO3);

    /* CTimer functional clock needs to be greater than or equal to SYSTEM_CLK */
    CLOCK_SetClockDiv(kCLOCK_DivCTIMER1, 1u);
    CLOCK_AttachClk(kFRO_HF_to_CTIMER1);
   
    BOARD_InitPins();
    BOARD_InitBootClocks();
    BOARD_InitDebugConsole();
}
/*${function:end}*/
/*
 * Copyright 2025 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#ifndef _MCUX_CONFIG_H_
#define _MCUX_CONFIG_H_

#define DUTY_CYCLE 20U
#define CONFIG_CPU_CORTEX_M 1
#define CONFIG_FLASH_BASE_ADDRESS 0x0
#define CONFIG_FLASH_DEFAULT_APP_OFFSET 0x0
#define CONFIG_HAS_FLASH_LOAD_OFFSET 1
#define CONFIG_FLASH_LOAD_OFFSET 0x0
#define CONFIG_UART_RETRY_TIMES 0
// #define CONFIG_FLASH_DRIVER_EXECUTES_FROM_RAM 0
// #define CONFIG_ENABLE_QUICKACCESS_SECTION_IN_XSPI_DRIVER 0
// #define CONFIG_STREAM_FLASH 0
// #define LIB_JPEG_USE_HW_ACCEL 0
// #define USE_PNGDEC_DRIVER 0
// #define CONFIG_BOOT_CUSTOM_DEVICE_SETUP 0
// #define CONFIG_FREERTOS_USE_CUSTOM_CONFIG_FRAGMENT 0
// #define MPU_SUPPORT 0
// #define USE_PERCEPIO_TRACELYZER 0

#endif /* _MCUX_CONFIG_H_ */
/*
 * Copyright (c) 2015, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"

/* Freescale includes. */
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#if defined(FSL_FEATURE_SOC_PORT_COUNT) && (FSL_FEATURE_SOC_PORT_COUNT)
#include "fsl_port.h"
#endif
#include "fsl_gpio.h"
#include "fsl_common.h"
#include "pin_mux.h"
#include "board.h"
#include "app.h"

#include "fsl_ctimer.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
/* Task priorities. */
#define hello_task_PRIORITY (configMAX_PRIORITIES - 1)

#ifndef CTIMER_MAT_PWM_PERIOD_CHANNEL
#define CTIMER_MAT_PWM_PERIOD_CHANNEL kCTIMER_Match_3
#endif

#define MAX_MOTOR_SPEED 50U
#define MIN_MOTOR_SPEED 0U

#define APP_DELAY_TICK 33U

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static void hello_task(void *pvParameters);

/*******************************************************************************
 * Variables
 ******************************************************************************/
/* Whether the SW button is pressed */
volatile bool g_Button_1_Press = true;
volatile bool g_motor_dir = false;
volatile bool g_Button_2_Press = false;

volatile bool g_motor_run_flag = true;
volatile bool g_motor_speed_dir = true;

volatile uint8_t g_motor_speed = MIN_MOTOR_SPEED;

volatile uint32_t g_timerClock= 0U;
volatile uint32_t g_pwmPeriod   = 0U;
volatile uint32_t g_pulsePeriod = 0U;

/*******************************************************************************
 * Code
 ******************************************************************************/

/*!
 * @brief Interrupt service fuction of switch.
 *
 * This function toggles the LED
 */
void KEY_1_IRQ_HANDLER(void)
{
#if (defined(FSL_FEATURE_PORT_HAS_NO_INTERRUPT) && FSL_FEATURE_PORT_HAS_NO_INTERRUPT) || \
    (!defined(FSL_FEATURE_SOC_PORT_COUNT))
    /* Clear external interrupt flag. */
    GPIO_GpioClearInterruptFlags(KEY_1_GPIO, 1U << KEY_1_GPIO_PIN);
#else
    /* Clear external interrupt flag. */
    GPIO_PortClearInterruptFlags(KEY_1_GPIO, 1U << KEY_1_GPIO_PIN);
#endif
    /* Change state of button. */
    g_Button_1_Press = true;
    SDK_ISR_EXIT_BARRIER;
}

void KEY_2_IRQ_HANDLER(void)
{
#if (defined(FSL_FEATURE_PORT_HAS_NO_INTERRUPT) && FSL_FEATURE_PORT_HAS_NO_INTERRUPT) || \
    (!defined(FSL_FEATURE_SOC_PORT_COUNT))
    /* Clear external interrupt flag. */
    GPIO_GpioClearInterruptFlags(KEY_2_GPIO, 1U << KEY_2_GPIO_PIN);
#else
    /* Clear external interrupt flag. */
    GPIO_PortClearInterruptFlags(KEY_2_GPIO, 1U << KEY_2_GPIO_PIN);
#endif
    /* Change state of button. */
    g_Button_2_Press = true;
    SDK_ISR_EXIT_BARRIER;
}

status_t CTIMER_GetPwmPeriodValue(uint32_t pwmFreqHz, uint8_t dutyCyclePercent, uint32_t timerClock_Hz)
{
    /* Calculate PWM period match value */
    g_pwmPeriod = (timerClock_Hz / pwmFreqHz) - 1U;

    /* Calculate pulse width match value */
    g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

    return kStatus_Success;
}

void motor_speed_set(uint8_t u8speed)
{
    CTIMER_GetPwmPeriodValue(20000, u8speed, g_timerClock);
    CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
}

/*!
 * @brief Application entry point.
 */
int main(void)
{
    ctimer_config_t pwm_config;
    uint32_t srcClock_Hz;
   
    /* Define the init structure for the input switch pin */
    gpio_pin_config_t sw_config = {
        kGPIO_DigitalInput,
        0,
    };

    /* Define the init structure for the output LED pin*/
    gpio_pin_config_t ain_config = {
        kGPIO_DigitalOutput,
        1,
    };

    /* Init board hardware. */
    BOARD_InitHardware();

    PRINTF("MCUX SDK version: %s\r\n", MCUXSDK_VERSION_FULL_STR);

    /* CTimer0 counter uses the AHB clock, some CTimer1 modules use the Aysnc clock */
    srcClock_Hz = CTIMER_CLK_FREQ;

    PRINTF("CTimer example to generate a PWM signal\r\n");

    CTIMER_GetDefaultConfig(&pwm_config);
    g_timerClock = srcClock_Hz / (pwm_config.prescale + 1);

    CTIMER_Init(CTIMER, &pwm_config);

    /* Get the PWM period match value and pulse width match value of 20Khz PWM signal with 20% dutycycle */
    CTIMER_GetPwmPeriodValue(20000, (uint8_t)DUTY_CYCLE, g_timerClock);
    CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
    CTIMER_StartTimer(CTIMER);

    /* Init input switch GPIO. */
    #if (defined(FSL_FEATURE_PORT_HAS_NO_INTERRUPT) && FSL_FEATURE_PORT_HAS_NO_INTERRUPT) || \
        (!defined(FSL_FEATURE_SOC_PORT_COUNT))
        GPIO_SetPinInterruptConfig(KEY_1_GPIO, KEY_1_GPIO_PIN, kGPIO_InterruptFallingEdge);
    #else
        PORT_SetPinInterruptConfig(KEY_1_GPIO, KEY_1_GPIO_PIN, kPORT_InterruptFallingEdge);
    #endif
        EnableIRQ(KEY_1_IRQ);
        GPIO_PinInit(KEY_1_GPIO, KEY_1_GPIO_PIN, &sw_config);

    /* Init input switch GPIO. */
    #if (defined(FSL_FEATURE_PORT_HAS_NO_INTERRUPT) && FSL_FEATURE_PORT_HAS_NO_INTERRUPT) || \
        (!defined(FSL_FEATURE_SOC_PORT_COUNT))
        GPIO_SetPinInterruptConfig(KEY_2_GPIO, KEY_2_GPIO_PIN, kGPIO_InterruptFallingEdge);
    #else
        PORT_SetPinInterruptConfig(KEY_2_GPIO, KEY_2_GPIO_PIN, kPORT_InterruptFallingEdge);
    #endif
        EnableIRQ(KEY_2_IRQ);
        GPIO_PinInit(KEY_2_GPIO, KEY_2_GPIO_PIN, &sw_config);

    /* Init output AIN GPIO. */
    GPIO_PinInit(AIN_1_GPIO, AIN_1_GPIO_PIN, &ain_config);
    GPIO_PinInit(AIN_2_GPIO, AIN_2_GPIO_PIN, &ain_config);

    if (xTaskCreate(hello_task, "Hello_task", configMINIMAL_STACK_SIZE + 100, NULL, hello_task_PRIORITY, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.\r\n");
        while (1)
            ;
    }
    vTaskStartScheduler();
    for (;;)
        ;
}

/*!
 * @brief Task responsible for printing of "Hello world." message.
 */
static void hello_task(void *pvParameters)
{
    for (;;)
    {
        //PRINTF("Hello world.\r\n");

        if (g_Button_1_Press)
        {
            PRINTF(" %s is pressed \r\n", KEY_1_NAME);

            g_Button_1_Press = false;

            g_motor_dir = g_motor_dir ? false : true;

            PRINTF(" motor dir is %d \r\n", g_motor_dir);

            if (g_motor_dir)
            {
                GPIO_PinWrite(AIN_1_GPIO, AIN_1_GPIO_PIN, LOGIC_LED_ON);
                GPIO_PinWrite(AIN_2_GPIO, AIN_2_GPIO_PIN, LOGIC_LED_OFF);
            }
            else
            {
                GPIO_PinWrite(AIN_1_GPIO, AIN_1_GPIO_PIN, LOGIC_LED_OFF);
                GPIO_PinWrite(AIN_2_GPIO, AIN_2_GPIO_PIN, LOGIC_LED_ON);
            }
        }

        if (g_Button_2_Press)
        {
            PRINTF(" %s is pressed \r\n", KEY_2_NAME);
           
            /* Reset state of button. */
            g_Button_2_Press = false;

            g_motor_run_flag ^= 1;
        }

        if (g_motor_speed_dir)
        {
            g_motor_speed++;

            if (g_motor_speed >= MAX_MOTOR_SPEED)
            {
                g_motor_speed_dir = false;
            }
        }
        else
        {
            g_motor_speed--;

            if (g_motor_speed <= MIN_MOTOR_SPEED)
            {
                g_motor_speed_dir = true;
            }
        }
       
        if (g_motor_run_flag)
        {
            motor_speed_set(g_motor_speed);

            PRINTF(" motor speed is %d \r\n", g_motor_speed);
        }
        else
        {
            motor_speed_set((uint8_t) 0);

            PRINTF(" motor speed is %d \r\n", 0);
        }
       
        vTaskDelay(APP_DELAY_TICK);
    }
}

最后烧录程序

企业微信截图_17699282011761.png

以上是程序调试运行结果

企业微信截图_17699283692761.png

这个是电机启动停止模式切换打印

企业微信截图_1769928428280.png

这个是方向控制状态切换的打印信息

至此已完成基础任务部分

基础任务:

1.电机启停与方向控制:通过程序实现电机的启动、停止、正转和反转。
2.电机调速控制:编写一个函数,可以通过输入参数(如0-100)来改变PWM的占空比,从而控制速度。


实物展示

fd44c5da-1fbc-4852-a4a2-18a0ad3b879b.jpg

电机正转,状态灯红色

a53eeaba-2041-442b-a948-be446ad77956.jpg

电机反转,状态灯绿色


共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]