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

共1条 1/1 1 跳转至

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

工程师
2026-02-01 18:00:22     打赏

上期过程贴我们已经实现了基础任务


本期成果贴我们将实现

进阶任务:

1.基于电位器的模拟调速系统:使用一个旋转电位器作为输入,实时无级地控制电机的速度和方向。

想要实现此任务,我们需要通过adc采集电位器输出的模拟量,并同步转换为电机pwm信号的占空比

我们参考的例程代码如图所示

企业微信截图_17699353338698.png

首先我们在原有工程的基础上添加ADC有关引脚配置

企业微信截图_17699353782534.png

添加完引脚配置记得更新源代码

企业微信截图_17699354224090.png

更新完源代码之后,我们来修改源码

主要修改四个文件

企业微信截图_17699355171145.png

我们逐个观察

企业微信截图_17699355807210.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)

#define DEMO_LPADC_BASE                  ADC0
#define DEMO_LPADC_USER_CHANNEL          0U
#define DEMO_LPADC_USER_CMDID            1U                          /* CMD1 */
#define DEMO_LPADC_VREF_SOURCE           kLPADC_ReferenceVoltageAlt3 /* VDDA */
#define DEMO_LPADC_DO_OFFSET_CALIBRATION true
#define DEMO_LPADC_USE_HIGH_RESOLUTION   true
/*${macro:end}*/

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*${prototype:start}*/
void BOARD_InitHardware(void);
/*${prototype:end}*/

#endif /* _APP_H_ */

企业微信截图_1769935633348.png

/*
 * 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);
   
    CLOCK_SetClockDiv(kCLOCK_DivADC0, 1u);
    CLOCK_AttachClk(kFRO12M_to_ADC0);

    BOARD_InitPins();
    BOARD_InitBootClocks();
    BOARD_InitDebugConsole();
}
/*${function:end}*/

企业微信截图_17699356748708.png

/*
 * 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_LPADC_CONVERSION_COMPLETE_TIMEOUT 0
#define CONFIG_LPADC_CALIBRATION_READY_TIMEOUT 0
#define CONFIG_LPADC_GAIN_CAL_READY_TIMEOUT 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"

#include "fsl_lpadc.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;

#if (defined(DEMO_LPADC_USE_HIGH_RESOLUTION) && DEMO_LPADC_USE_HIGH_RESOLUTION)
const uint32_t g_LpadcFullRange   = 65536U;
const uint32_t g_LpadcResultShift = 0U;
#else
const uint32_t g_LpadcFullRange   = 4096U;
const uint32_t g_LpadcResultShift = 3U;
#endif /* DEMO_LPADC_USE_HIGH_RESOLUTION */

lpadc_conv_result_t g_LpadcResultConfigStruct;

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

    lpadc_config_t mLpadcConfigStruct;
    lpadc_conv_trigger_config_t mLpadcTriggerConfigStruct;
    lpadc_conv_command_config_t mLpadcCommandConfigStruct;
   
    /* 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);

        LPADC_GetDefaultConfig(&mLpadcConfigStruct);
    /* Set to highest power level here, users need to properly match ADC clock and power level according 
     * to application requirements. For specific correspondence, please refer to the data sheet. */
#if defined(FSL_FEATURE_LPADC_HAS_CFG_PWRSEL) && (FSL_FEATURE_LPADC_HAS_CFG_PWRSEL == 1U)
    mLpadcConfigStruct.powerLevelMode = kLPADC_PowerLevelAlt4;
#endif /* FSL_FEATURE_LPADC_HAS_CFG_PWRSEL */
    mLpadcConfigStruct.enableAnalogPreliminary = true;
#if defined(DEMO_LPADC_VREF_SOURCE)
    mLpadcConfigStruct.referenceVoltageSource = DEMO_LPADC_VREF_SOURCE;
#endif /* DEMO_LPADC_VREF_SOURCE */
    LPADC_Init(DEMO_LPADC_BASE, &mLpadcConfigStruct);

    /* Request LPADC calibration. */
#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE) && FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE
    LPADC_SetOffsetCalibrationMode(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_CALIBRATION_MODE);
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CALOFSMODE */

#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CALOFS) && FSL_FEATURE_LPADC_HAS_CTRL_CALOFS
#if defined(DEMO_LPADC_DO_OFFSET_CALIBRATION) && DEMO_LPADC_DO_OFFSET_CALIBRATION
    LPADC_DoOffsetCalibration(DEMO_LPADC_BASE); /* Request offset calibration, automatic update OFSTRIM register. */
#else                                           /* Update OFSTRIM register manually. */

#if defined(FSL_FEATURE_LPADC_HAS_OFSTRIM) && FSL_FEATURE_LPADC_HAS_OFSTRIM
#if defined(FSL_FEATURE_LPADC_OFSTRIM_COUNT) && (FSL_FEATURE_LPADC_OFSTRIM_COUNT == 2U)
    LPADC_SetOffsetValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
#elif defined(FSL_FEATURE_LPADC_OFSTRIM_COUNT) && (FSL_FEATURE_LPADC_OFSTRIM_COUNT == 1U)
    LPADC_SetOffsetValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE);
#endif /* FSL_FEATURE_LPADC_OFSTRIM_COUNT */

#else  /* For other OFSTRIM register type. */
    if (DEMO_LPADC_OFFSET_CALIBRATION_MODE == kLPADC_OffsetCalibration12bitMode)
    {
        LPADC_SetOffset12BitValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
    }
    else
    {
        LPADC_SetOffset16BitValue(DEMO_LPADC_BASE, DEMO_LPADC_OFFSET_VALUE_A, DEMO_LPADC_OFFSET_VALUE_B);
    }
#endif /* FSL_FEATURE_LPADC_HAS_OFSTRIM */

#endif /* DEMO_LPADC_DO_OFFSET_CALIBRATION */
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CALOFS */

#if defined(FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ) && FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ
    /* Request auto calibration (including gain error calibration and linearity error calibration). */
    LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
#endif /* FSL_FEATURE_LPADC_HAS_CTRL_CAL_REQ */

#if (defined(FSL_FEATURE_LPADC_HAS_CFG_CALOFS) && FSL_FEATURE_LPADC_HAS_CFG_CALOFS)
    /* Do auto calibration. */
    LPADC_DoAutoCalibration(DEMO_LPADC_BASE);
#endif /* FSL_FEATURE_LPADC_HAS_CFG_CALOFS */

    /* Set conversion CMD configuration. */
    LPADC_GetDefaultConvCommandConfig(&mLpadcCommandConfigStruct);
    mLpadcCommandConfigStruct.channelNumber = DEMO_LPADC_USER_CHANNEL;
#if defined(DEMO_LPADC_USE_HIGH_RESOLUTION) && DEMO_LPADC_USE_HIGH_RESOLUTION
    mLpadcCommandConfigStruct.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
#endif /* DEMO_LPADC_USE_HIGH_RESOLUTION */
    LPADC_SetConvCommandConfig(DEMO_LPADC_BASE, DEMO_LPADC_USER_CMDID, &mLpadcCommandConfigStruct);

    /* Set trigger configuration. */
    LPADC_GetDefaultConvTriggerConfig(&mLpadcTriggerConfigStruct);
    mLpadcTriggerConfigStruct.targetCommandId       = DEMO_LPADC_USER_CMDID;
    mLpadcTriggerConfigStruct.enableHardwareTrigger = false;
    LPADC_SetConvTriggerConfig(DEMO_LPADC_BASE, 0U, &mLpadcTriggerConfigStruct); /* Configurate the trigger0. */

    PRINTF("ADC Full Range: %d\r\n", g_LpadcFullRange);
#if defined(FSL_FEATURE_LPADC_HAS_CMDL_CSCALE) && FSL_FEATURE_LPADC_HAS_CMDL_CSCALE
    if (kLPADC_SampleFullScale == mLpadcCommandConfigStruct.sampleScaleMode)
    {
        PRINTF("Full channel scale (Factor of 1).\r\n");
    }
    else if (kLPADC_SamplePartScale == mLpadcCommandConfigStruct.sampleScaleMode)
    {
        PRINTF("Divided input voltage signal. (Factor of 30/64).\r\n");
    }
#endif

    /* 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)
{
    uint16_t adc_result = 0U;

    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;
        }

        LPADC_DoSoftwareTrigger(DEMO_LPADC_BASE, 1U); /* 1U is trigger0 mask. */
#if (defined(FSL_FEATURE_LPADC_FIFO_COUNT) && (FSL_FEATURE_LPADC_FIFO_COUNT == 2U))
        while (!LPADC_GetConvResult(DEMO_LPADC_BASE, &g_LpadcResultConfigStruct, 0U))
#else
        while (!LPADC_GetConvResult(DEMO_LPADC_BASE, &g_LpadcResultConfigStruct))
#endif /* FSL_FEATURE_LPADC_FIFO_COUNT */
        {
        }
        adc_result = ((g_LpadcResultConfigStruct.convValue) >> g_LpadcResultShift);
        PRINTF("adc_result value: %d\r\n", adc_result);

        g_motor_speed = (( adc_result - 1000 ) / 600) % 100;

        PRINTF("g_motor_speed value: %d\r\n", g_motor_speed);

        // 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);
    }
}

代码已粘贴完毕

下面来说一下硬件连接及程序流程

企业微信截图_17699370415385.png

上图是硬件连接示意图


软件按流程方面

通过SW2按下触发中断进而实现电机方向控制

通过SW3按下触发中断进而实现电机启动停止控制


使用ADC读取电位器数值,并同步转换成电机实际转速

进而通过设置ctimer输出pwm波占空比调整 电机驱动模块 PWMA 脚的波形实现电机转速控制

具体程序流程图

flowchart TD

    %% ===== Main Init =====
    A([程序启动]) --> B[初始化硬件 BOARD_InitHardware]
    B --> C[初始化 CTimer → 配置 PWM → 启动 PWM]
    C --> D[初始化 LPADC(配置/校准/触发器)]
    D --> E[初始化按键 GPIO(含中断)]
    E --> F[初始化电机方向 GPIO(AIN1/AIN2)]
    F --> G[创建 FreeRTOS 任务 hello_task]
    G --> H[启动调度器 vTaskStartScheduler]

    %% ===== Interrupts =====
    subgraph INT1 [KEY1 中断:切换方向]
        I1[KEY1 中断触发] --> I2[清除中断标志位]
        I2 --> I3[g_Button_1_Press = true]
    end

    subgraph INT2 [KEY2 中断:启停电机]
        J1[KEY2 中断触发] --> J2[清除中断标志位]
        J2 --> J3[g_Button_2_Press = true]
    end

    %% ===== Task Loop =====
    subgraph TASK [hello_task 主循环]
        T1[[循环开始]] --> T2{KEY1 是否按下?}
        T2 -- 是 --> T3[切换 g_motor_dir<br>更新 AIN1/AIN2 输出]
        T2 -- 否 --> T4[继续]

        T3 --> T4

        T4 --> T5{KEY2 是否按下?}
        T5 -- 是 --> T6[切换 g_motor_run_flag]
        T5 -- 否 --> T7[继续]

        T6 --> T7

        T7 --> T8[触发 ADC 转换]
        T8 --> T9[等待 ADC 完成]
        T9 --> T10[读取 ADC → adc_result]
        T10 --> T11[计算速度 g_motor_speed = ((adc-1000)/600)%100]

        T11 --> T12{电机运行? g_motor_run_flag}
        T12 -- 是 --> T13[设置 PWM 占空比 = g_motor_speed]
        T12 -- 否 --> T14[设置 PWM = 0(停机)]

        T13 --> T15[打印调试信息]
        T14 --> T15

        T15 --> T16[vTaskDelay(APP_DELAY_TICK)]
        T16 --> T1
    end
┌──────────────────────────────────────────┐
│                程序启动                  │
└──────────────────────────────────────────┘
                │
                ▼
     初始化板级硬件 BOARD_InitHardware()
                │
                ▼
     初始化 CTimer → 配置 PWM → 启动 PWM
                │
                ▼
     初始化 LPADC(配置、校准、触发器)
                │
                ▼
     初始化按键 GPIO(含中断)
                │
                ▼
     初始化电机方向 GPIO(AIN1 / AIN2)
                │
                ▼
     创建 FreeRTOS 任务 hello_task
                │
                ▼
     启动调度器 vTaskStartScheduler()
┌──────────────────────────────────────────┐
│              hello_task 循环             │
└──────────────────────────────────────────┘
                │
                ▼
     是否检测到 KEY1 按下?
                │
        ┌──────┴────────┐
        │                 │
       是                 否
        │                 │
        ▼                 │
 切换电机方向 g_motor_dir │
 更新 AIN1/AIN2 输出      │
        │                 │
        ▼                 │
                │
                ▼
     是否检测到 KEY2 按下?
                │
        ┌──────┴────────┐
        │                 │
       是                 否
        │                 │
        ▼                 │
 切换电机运行标志 g_motor_run_flag
                │
                ▼
     触发 ADC 转换(软件触发)
                │
                ▼
     等待 ADC 转换完成
                │
                ▼
     读取 ADC 结果 → adc_result
                │
                ▼
     计算电机速度:
     g_motor_speed = ((adc_result - 1000) / 600) % 100
                │
                ▼
     g_motor_run_flag 是否为真?
                │
        ┌──────┴────────┐
        │                 │
       是                 否
        │                 │
        ▼                 ▼
 设置 PWM 占空比为 g_motor_speed   设置 PWM 为 0(停机)
                │
                ▼
     打印调试信息
                │
                ▼
     vTaskDelay(APP_DELAY_TICK)
                │
                ▼
     回到循环开始

至此代码流程展示完毕

结果展示

电机运行

企业微信截图_17699392972945.png

电机停止

企业微信截图_17699393463583.png


至此展示完毕


心得体会:

    通过本次活动是我了解了如何使用单片机去控制电机的方法

    并且掌握了如何使用ADC采集模拟量数据

    在代码编写及调试过程中其实并不是一帆风顺的,这里略过了一些坑

    主要体现在需要在

void BOARD_InitHardware(void)

    函数中打开需要使用到的外设时钟,否则会导致程序segment fault,这里需要着重注意

    企业微信截图_17699397953826.png

    另外重中之重就是一定要通过git管理项目代码,这样可以保证前一版代码功能是正常的。

来一张全景图

853d5aa9-5ecb-4181-acee-197585e67be0.jpg

视频链接:https://www.bilibili.com/video/BV1Ss6bBFE81/





共1条 1/1 1 跳转至

回复

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