这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » “2025Let'sdo第4期”基于单片机的直流电机控制-成果帖

共1条 1/1 1 跳转至

“2025Let'sdo第4期”基于单片机的直流电机控制-成果帖

工程师
2026-01-28 20:30:06     打赏

本次使用的是核心板上的SW2和SW3分别作为方向和占空比的控制,生成后的工程已经有了20khzpwm的配置以及两个按键引脚的配置,所以直接写一个非常简单的按键逻辑即可

/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2020, 2025 NXP
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/*******************************************************************************
 * Includes
 ******************************************************************************/

#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_ctimer.h"
#include "pin_mux.h"
#include "fsl_port.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#ifndef CTIMER_MAT_PWM_PERIOD_CHANNEL
#define CTIMER_MAT_PWM_PERIOD_CHANNEL kCTIMER_Match_3
#endif
#define PWM_FREQUENCY_HZ   20000U
#define DUTY_STEP_PERCENT  10U

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
typedef enum _motor_direction
{
    kMotorDirectionStop = 0U,
    kMotorDirectionForward,
    kMotorDirectionReverse
} motor_direction_t;

static void Motor_SetDirection(motor_direction_t direction);
static status_t Motor_UpdateDutyCycle(uint8_t dutyCyclePercent);
static void Buttons_Init(void);
static bool Button_LevelHigh(GPIO_Type *gpio, uint32_t pin);

/*******************************************************************************
 * Variables
 ******************************************************************************/
volatile uint32_t g_pwmPeriod   = 0U;
volatile uint32_t g_pulsePeriod = 0U;
static uint8_t s_currentDutyCycle           = DUTY_CYCLE;
static motor_direction_t s_currentDirection = kMotorDirectionStop;
static bool s_sw2IdleLevel                  = true;
static bool s_sw3IdleLevel                  = true;

/*******************************************************************************
 * Code
 ******************************************************************************/
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;
}

static void Motor_SetDirection(motor_direction_t direction)
{
    s_currentDirection = direction;
    switch (direction)
    {
        case kMotorDirectionForward:
            GPIO_PinWrite(BOARD_INITPINS_AIN1_GPIO, BOARD_INITPINS_AIN1_PIN, 1U);
            GPIO_PinWrite(BOARD_INITPINS_AIN2_GPIO, BOARD_INITPINS_AIN2_PIN, 0U);
            break;
        case kMotorDirectionReverse:
            GPIO_PinWrite(BOARD_INITPINS_AIN1_GPIO, BOARD_INITPINS_AIN1_PIN, 0U);
            GPIO_PinWrite(BOARD_INITPINS_AIN2_GPIO, BOARD_INITPINS_AIN2_PIN, 1U);
            break;
        case kMotorDirectionStop:
        default:
            GPIO_PinWrite(BOARD_INITPINS_AIN1_GPIO, BOARD_INITPINS_AIN1_PIN, 0U);
            GPIO_PinWrite(BOARD_INITPINS_AIN2_GPIO, BOARD_INITPINS_AIN2_PIN, 0U);
            break;
    }
}

static status_t Motor_UpdateDutyCycle(uint8_t dutyCyclePercent)
{
    status_t status =
        CTIMER_UpdatePwmDutycycle(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, dutyCyclePercent);
    if (status == kStatus_Success)
    {
        /* Keep cached values in sync for debugging */
        g_pulsePeriod      = (g_pwmPeriod + 1U) * (100U - dutyCyclePercent) / 100U;
        s_currentDutyCycle = dutyCyclePercent;
    }
    return status;
}

static void Buttons_Init(void)
{
    /* Enable GPIO clock for SW3 (GPIO1) in case it is not already enabled */
    CLOCK_EnableClock(kCLOCK_GateGPIO1);

    /* Ensure GPIO and PORT clocks are enabled for both switch ports */
    CLOCK_EnableClock(kCLOCK_GatePORT1);
    CLOCK_EnableClock(kCLOCK_GatePORT3);
    CLOCK_EnableClock(kCLOCK_GateGPIO1);
    CLOCK_EnableClock(kCLOCK_GateGPIO3);

    const port_pin_config_t switch_config = {.pullSelect = kPORT_PullUp,
                                             .pullValueSelect = kPORT_LowPullResistor,
                                             .slewRate = kPORT_FastSlewRate,
                                             .passiveFilterEnable = kPORT_PassiveFilterDisable,
                                             .openDrainEnable = kPORT_OpenDrainDisable,
                                             .driveStrength = kPORT_LowDriveStrength,
                                             .driveStrength1 = kPORT_NormalDriveStrength,
                                             .mux = kPORT_MuxAsGpio,
                                             .inputBuffer = kPORT_InputBufferEnable,
                                             .invertInput = kPORT_InputNormal,
                                             .lockRegister = kPORT_UnlockRegister};

    PORT_SetPinConfig(PORT3, BOARD_SW2_GPIO_PIN, &switch_config);
    PORT_SetPinConfig(PORT1, BOARD_SW3_GPIO_PIN, &switch_config);

    gpio_pin_config_t sw_config = {.pinDirection = kGPIO_DigitalInput, .outputLogic = 0U};
    GPIO_PinInit(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN, &sw_config);
    GPIO_PinInit(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN, &sw_config);

    s_sw2IdleLevel = Button_LevelHigh(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN);
    s_sw3IdleLevel = Button_LevelHigh(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN);
}

static bool Button_LevelHigh(GPIO_Type *gpio, uint32_t pin)
{
    return (GPIO_PinRead(gpio, pin) != 0U);
}

/*!
 * @brief Main function
 */
int main(void)
{
    ctimer_config_t config;
    uint32_t srcClock_Hz;
    uint32_t timerClock;

    /* Init 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(&config);
    timerClock = srcClock_Hz / (config.prescale + 1);

    CTIMER_Init(CTIMER, &config);

    /* Get the PWM period match value and pulse width match value of PWM_FREQUENCY_HZ PWM signal with DUTY_CYCLE */
    CTIMER_GetPwmPeriodValue(PWM_FREQUENCY_HZ, (uint8_t)DUTY_CYCLE, timerClock);
    CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT_OUT, g_pwmPeriod, g_pulsePeriod, false);
    CTIMER_StartTimer(CTIMER);
    Motor_SetDirection(s_currentDirection);
    Buttons_Init();
    PRINTF("SW2: cycle DIR (STOP->FWD->REV), SW3: duty +10%% (wrap 0-100%%)\r\n");
    PRINTF("DIR: STOP, DUTY: %u%%\r\n", s_currentDutyCycle);

    bool prevSw2 = Button_LevelHigh(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN);
    bool prevSw3 = Button_LevelHigh(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN);

    while (1)
    {
        bool sw2 = Button_LevelHigh(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN);
        bool sw3 = Button_LevelHigh(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN);

        if (sw2 != prevSw2)
        {
            SDK_DelayAtLeastUs(10000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
            sw2 = Button_LevelHigh(BOARD_SW2_GPIO, BOARD_SW2_GPIO_PIN);
            if (sw2 != prevSw2)
            {
                prevSw2 = sw2;
                if (sw2 != s_sw2IdleLevel)
                {
                    if (s_currentDirection == kMotorDirectionStop)
                    {
                        s_currentDirection = kMotorDirectionForward;
                    }
                    else if (s_currentDirection == kMotorDirectionForward)
                    {
                        s_currentDirection = kMotorDirectionReverse;
                    }
                    else
                    {
                        s_currentDirection = kMotorDirectionStop;
                    }
                    Motor_SetDirection(s_currentDirection);
                    const char *dirStr = (s_currentDirection == kMotorDirectionForward)
                                             ? "FWD"
                                             : ((s_currentDirection == kMotorDirectionReverse) ? "REV" : "STOP");
                    PRINTF("DIR: %s, DUTY: %u%%\r\n", dirStr, s_currentDutyCycle);
                }
            }
        }

        if (sw3 != prevSw3)
        {
            SDK_DelayAtLeastUs(10000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
            sw3 = Button_LevelHigh(BOARD_SW3_GPIO, BOARD_SW3_GPIO_PIN);
            if (sw3 != prevSw3)
            {
                prevSw3 = sw3;
                if (sw3 != s_sw3IdleLevel)
                {
                    uint8_t nextDuty = (uint8_t)((s_currentDutyCycle + DUTY_STEP_PERCENT) % 110U);
                    if (nextDuty > 100U)
                    {
                        nextDuty = 0U;
                    }
                    if (Motor_UpdateDutyCycle(nextDuty) == kStatus_Success)
                    {
                        PRINTF("DUTY: %u%%, DIR: %s\r\n", s_currentDutyCycle,
                               (s_currentDirection == kMotorDirectionForward) ? "FWD" : "REV");
                    }
                }
            }
        }

        prevSw2 = sw2;
        prevSw3 = sw3;
    }
}

可以在示波器清晰的看到PWM的波形image.png


共1条 1/1 1 跳转至

回复

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