上期过程贴我们已经实现了基础任务
本期成果贴我们将实现
进阶任务:
1.基于电位器的模拟调速系统:使用一个旋转电位器作为输入,实时无级地控制电机的速度和方向。
想要实现此任务,我们需要通过adc采集电位器输出的模拟量,并同步转换为电机pwm信号的占空比
我们参考的例程代码如图所示

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

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

更新完源代码之后,我们来修改源码
主要修改四个文件

我们逐个观察

/*
* 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_ */
/*
* 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}*/
/* * 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);
}
}代码已粘贴完毕
下面来说一下硬件连接及程序流程

上图是硬件连接示意图
软件按流程方面
通过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) │ ▼ 回到循环开始
至此代码流程展示完毕
结果展示
电机运行

电机停止

至此展示完毕
心得体会:
通过本次活动是我了解了如何使用单片机去控制电机的方法
并且掌握了如何使用ADC采集模拟量数据
在代码编写及调试过程中其实并不是一帆风顺的,这里略过了一些坑
主要体现在需要在
void BOARD_InitHardware(void)函数中打开需要使用到的外设时钟,否则会导致程序segment fault,这里需要着重注意

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

视频链接:https://www.bilibili.com/video/BV1Ss6bBFE81/
我要赚赏金
