目前完成的是进阶任务,除了通过开发板按键实现电机启停、正反转、转速调节外,新增串口信号解析功能,支持上位机/串口工具发送指令远程控制电机,兼顾本地按键交互和远程串口控制两种模式。串口解析采用环形缓冲区+中断接收+滑动窗口匹配架构,保障通信稳定性,实现过程如下,视频在文章最底部。
一、硬件原理
1.1 TB6612FNG 驱动芯片控制逻辑
TB6612FNG是一款专为直流电机设计的双通道H桥驱动芯片,相比传统L298N,它具有功耗低、集成度高、自带续流二极管等优势,其核心控制逻辑如下:
AIN1 电平 | AIN2 电平 | 电机状态 |
高 | 低 | 正转 |
低 | 高 | 反转 |
高/低 | 高/低 | 刹车/停止 |
此外,芯片的PWMA(对应A通道)引脚接收PWM波输入,通过调整PWM占空比可线性调节电机转速:占空比越高,电机转速越快;占空比为0时,电机停止。
1.2 硬件连接设计
本方案中MCU与TB6612FNG、外设的核心连接关系:
• PWM输出:MCU的FlexPWM模块输出通道(BOARD_PWM_BASEADDR)连接TB6612FNG的PWMA引脚,用于输出调速PWM波;
• 方向控制:MCU的两个GPIO引脚(BOARD_Direct1/Direct2)分别连接TB6612FNG的AIN1/AIN2,通过电平翻转控制电机转向;
• 按键输入:SW2/SW3两个按键分别接MCU的GPIO中断引脚(下降沿触发),用于本地交互控制;
• 状态指示:MCU的GPIO引脚控制LED,用于指示PWM运行状态(电机启停);
• 串口通信:MCU的LPUART外设(BOARD_UART_BASEADDR)通过TX/RX引脚连接电脑/串口工具,用于接收远程控制指令,采用中断接收模式避免主循环阻塞。
二、软件实现
2.1 开发环境
• 开发工具:Keil5
• 底层驱动:NXP MCUX SDK(适配目标MCU,包含PWM、GPIO、中断、LPUART等底层驱动)
• 核心功能:PWM初始化与占空比更新、GPIO中断(按键)、按键长短按识别、LPUART串口接收(环形缓冲区)与指令解析(滑动窗口匹配)、电机状态双模式控制(按键/串口)。
2.2 代码架构与核心模块解析
完整代码基于模块化设计,核心分为PWM初始化、GPIO与中断配置、按键交互逻辑、LPUART串口配置与指令解析(核心新增)、主循环五大模块,其中串口解析模块采用“环形缓冲区+中断接收+滑动窗口解析”架构,保障远程控制的稳定性与可靠性,以下是关键代码解析:
(1)全局变量定义
全局变量是整个程序的状态基石,既要管理电机核心状态,也要为串口指令解析提供环形缓冲区及相关索引支持,具体代码及对应功能说明如下:
#include "fsl_common.h"
#include "fsl_gpio.h"
#include "fsl_pwm.h"
#include "fsl_lpuart.h"
#include "board.h"
// 硬件引脚宏定义(适配MCXA153开发板),统一定义便于后续修改维护,避免硬编码
#define PWM_BASEADDR BOARD_PWM_BASEADDR
#define PWM_CHANNEL BOARD_PWM_CHANNEL
#define AIN1_GPIO BOARD_Direct1_GPIO
#define AIN1_PIN BOARD_Direct1_PIN
#define AIN2_GPIO BOARD_Direct2_GPIO
#define AIN2_PIN BOARD_Direct2_PIN
#define KEY2_GPIO BOARD_SW2_GPIO
#define KEY2_PIN BOARD_SW2_PIN
#define KEY3_GPIO BOARD_SW3_GPIO
#define KEY3_PIN BOARD_SW3_PIN
#define LED_GPIO BOARD_LED_GPIO
#define LED_PIN BOARD_LED_PIN
#define LPUART_BASEADDR BOARD_UART_BASEADDR // 串口外设改为LPUART,适配中断接收+环形缓冲区架构
// 电机状态枚举,用枚举统一管理电机三种核心状态(停止/正转/反转),相比单纯布尔值更清晰,避免逻辑混乱
typedef enum {
MOTOR_STOP = 0, // 停止
MOTOR_FORWARD, // 正转
MOTOR_REVERSE // 反转
} Motor_State;
// 全局状态变量,用于跨函数传递电机状态和参数
Motor_State motor_state = MOTOR_STOP; // 初始状态:停止,确保上电后电机处于安全状态
uint8_t pwm_duty = 0; // PWM占空比(0-100),初始为0,对应电机停止
uint32_t key_press_time = 0; // 按键长按计时,用于识别按键长短按,实现单按键多功能
// -------------------------- 串口解析核心定义 --------------------------
/* LPUART 环形缓冲区大小:256字节,兼顾缓存能力与MCXA153内存占用 */
#define DEMO_RING_BUFFER_SIZE 256
/* 命令数据长度:固定5字节帧格式,对应指令帧【0x5A 状态 方向 占空比 0xA5】 */
#define CMD_DATA_SIZE 5
/* 命令头尾标识(帧边界):用于精准识别指令帧,避免乱码误触发,提升解析可靠性 */
#define CMD_HEADER 0x5A // 帧头标识
#define CMD_TAIL 0xA5 // 帧尾标识
/* 环形缓冲区核心变量:解决串口数据“生产-消费”异步问题,防止数据丢失 */
uint8_t demoRingBuffer[DEMO_RING_BUFFER_SIZE]; // 环形数据缓存区,存储中断接收的串口数据
volatile uint16_t txIndex = 0; // 消费索引(主循环读取/解析数据的位置)
volatile uint16_t rxIndex = 0; // 生产索引(中断接收数据、写入缓冲区的位置)
// -----------------------------------------------------------------------------(2)PWM/ GPIO/ 按键交互模块
该模块是电机本地控制的核心,包含PWM调速、电机状态控制、GPIO与按键中断配置,复用原有成熟逻辑,确保本地控制稳定,同时与串口模块解耦,具体代码及功能说明如下:
// ========== PWM初始化与占空比更新 ==========
// 功能:初始化FlexPWM模块,生成适配TB6612的PWM波,同时提供占空比更新接口,实现电机转速调节
void PWM_Init(void)
{
pwm_config_t pwm_config;
uint32_t pwm_freq = 10000; // 10kHz适配TB6612,该频率下电机运行平稳、噪音小,是TB6612的最优工作频率
uint32_t bus_clk = CLOCK_GetFreq(kCLOCK_BusClk); // 获取总线时钟频率,用于计算PWM周期值
PWM_GetDefaultConfig(&pwm_config); // 调用SDK封装函数,获取PWM默认配置,简化初始化流程,无需手动配置寄存器
pwm_config.periodMode = kPWM_EdgeAligned; // 配置为边沿对齐模式,常规调速场景首选,调速精度高、逻辑简单
PWM_Init(PWM_BASEADDR, PWM_CHANNEL, &pwm_config); // 初始化PWM外设,绑定PWM基地址和通道,应用配置参数
// 设置PWM周期,公式:周期值 = 总线时钟 / 目标频率,确保PWM频率稳定在10kHz
PWM_SetPeriodValue(PWM_BASEADDR, PWM_CHANNEL, bus_clk / pwm_freq);
PWM_SetDutyCycle(PWM_BASEADDR, PWM_CHANNEL, 0); // 初始占空比设为0,上电后电机不转动,保障安全
PWM_StartTimer(PWM_BASEADDR, PWM_CHANNEL); // 启动PWM定时器,开始输出PWM波
}
// 功能:更新PWM占空比,同时联动LED状态指示,直观反馈电机运行状态
void PWM_UpdateDuty(uint8_t duty)
{
pwm_duty = (duty > 100) ? 100 : duty; // 防溢出保护,确保占空比始终在0-100范围内,避免无效参数导致电机异常
PWM_SetDutyCycle(PWM_BASEADDR, PWM_CHANNEL, pwm_duty); // 更新PWM占空比,实现转速调节
// LED状态同步:占空比>0则亮(电机运行),否则灭(停止),通过LED直观判断电机工作状态
GPIO_WritePinOutput(LED_GPIO, LED_PIN, (pwm_duty > 0) ? 1 : 0);
}
// ========== 电机状态控制 ==========
// 功能:根据传入的电机状态,控制TB6612的AIN1/AIN2引脚电平,实现电机正转、反转、停止,联动PWM占空比
// 适配串口解析的指令参数,可直接被串口指令执行函数调用
void Motor_Control(Motor_State state, uint8_t duty)
{
motor_state = state; // 更新全局电机状态,确保其他函数能获取当前电机状态
switch(state)
{
case MOTOR_STOP:
// 停止状态:AIN1/AIN2均置低,对应TB6612刹车停止逻辑,同时占空比置0,双重保障电机停止
GPIO_WritePinOutput(AIN1_GPIO, AIN1_PIN, 0);
GPIO_WritePinOutput(AIN2_GPIO, AIN2_PIN, 0);
PWM_UpdateDuty(0);
break;
case MOTOR_FORWARD:
// 正转状态:AIN1=1、AIN2=0,严格匹配TB6612正转控制逻辑,同步更新占空比
GPIO_WritePinOutput(AIN1_GPIO, AIN1_PIN, 1);
GPIO_WritePinOutput(AIN2_GPIO, AIN2_PIN, 0);
PWM_UpdateDuty(duty);
break;
case MOTOR_REVERSE:
// 反转状态:AIN1=0、AIN2=1,严格匹配TB6612反转控制逻辑,同步更新占空比
GPIO_WritePinOutput(AIN1_GPIO, AIN1_PIN, 0);
GPIO_WritePinOutput(AIN2_GPIO, AIN2_PIN, 1);
PWM_UpdateDuty(duty);
break;
}
}
// ========== 按键中断(本地控制,保留原有逻辑) ==========
// 功能:初始化GPIO引脚(方向控制、LED、按键),配置按键中断,为本地按键交互提供硬件基础
void GPIO_Init_All(void)
{
gpio_pin_config_t gpio_config;
// 方向控制引脚(AIN1/AIN2)初始化:推挽输出,初始低电平
gpio_config.pinDirection = kGPIO_DigitalOutput;
gpio_config.outputLogic = 0;
GPIO_PinInit(AIN1_GPIO, AIN1_PIN, &gpio_config);
GPIO_PinInit(AIN2_GPIO, AIN2_PIN, &gpio_config);
// LED引脚初始化:推挽输出,初始灭
GPIO_PinInit(LED_GPIO, LED_PIN, &gpio_config);
// 按键引脚初始化:上拉输入,用于按键检测
gpio_config.pinDirection = kGPIO_DigitalInput;
gpio_config.outputLogic = 1; // 上拉,避免外部干扰导致误触发
GPIO_PinInit(KEY2_GPIO, KEY2_PIN, &gpio_config);
GPIO_PinInit(KEY3_GPIO, KEY3_PIN, &gpio_config);
// 按键中断配置(SW2/SW3下降沿触发):按键按下时产生中断,响应及时,避免主循环扫描占用资源
GPIO_SetPinInterruptConfig(KEY2_GPIO, KEY2_PIN, kGPIO_InterruptFallingEdge);
GPIO_SetPinInterruptConfig(KEY3_GPIO, KEY3_PIN, kGPIO_InterruptFallingEdge);
GPIO_EnablePinInterrupt(KEY2_GPIO, KEY2_PIN); // 使能SW2按键中断
GPIO_EnablePinInterrupt(KEY3_GPIO, KEY3_PIN); // 使能SW3按键中断
EnableIRQ(BOARD_SW2_IRQn); // 使能SW2中断NVIC通道,确保中断能被CPU响应
EnableIRQ(BOARD_SW3_IRQn); // 使能SW3中断NVIC通道
}
// SW2中断:短按切换转向,长按停止(同原有逻辑),通过长按计时实现单按键多功能,最大化利用开发板按键资源
void BOARD_SW2_IRQHandler(void)
{
// 清除中断标志(必须),避免持续触发中断
GPIO_ClearPinInterruptFlags(KEY2_GPIO, KEY2_PIN);
// 按键消抖(10ms),排除机械抖动导致的误触发
SDK_DelayAtLeastUs(10000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
if(GPIO_ReadPinInput(KEY2_GPIO, KEY2_PIN) != 0) return; // 误触发,直接返回
// 长短按识别:计时200ms,区分短按和长按
key_press_time = 0;
while(GPIO_ReadPinInput(KEY2_GPIO, KEY2_PIN) == 0)
{
SDK_DelayAtLeastUs(1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
key_press_time++;
if(key_press_time >= 200) break; // 长按阈值:200ms
}
// 长按(停止)/短按(切换转向)
if(key_press_time >= 200)
{
Motor_Control(MOTOR_STOP, 0);
}
else
{
if(motor_state == MOTOR_FORWARD)
{
Motor_Control(MOTOR_REVERSE, pwm_duty);
}
else if(motor_state == MOTOR_REVERSE)
{
Motor_Control(MOTOR_FORWARD, pwm_duty);
}
else // 停止状态下短按:默认正转,初始转速50%
{
Motor_Control(MOTOR_FORWARD, 50);
}
}
}
// SW3中断:短按调速,长按启停(同原有逻辑),与SW2配合,实现电机本地全功能控制
void BOARD_SW3_IRQHandler(void)
{
GPIO_ClearPinInterruptFlags(KEY3_GPIO, KEY3_PIN);
SDK_DelayAtLeastUs(10000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
if(GPIO_ReadPinInput(KEY3_GPIO, KEY3_PIN) != 0) return;
key_press_time = 0;
while(GPIO_ReadPinInput(KEY3_GPIO, KEY3_PIN) == 0)
{
SDK_DelayAtLeastUs(1000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY);
key_press_time++;
if(key_press_time >= 200) break;
}
// 长按:启停切换;短按:转速±10%
if(key_press_time >= 200)
{
if(motor_state == MOTOR_STOP)
{
Motor_Control(MOTOR_FORWARD, 50); // 启停默认正转,转速50%
}
else
{
Motor_Control(MOTOR_STOP, 0);
}
}
else
{
if(motor_state != MOTOR_STOP) // 仅运行时调速
{
if(pwm_duty + 10 <= 100)
{
Motor_Control(motor_state, pwm_duty + 10);
}
else
{
Motor_Control(MOTOR_STOP, 0); // 转速到100%后再按,重置为停止
}
}
}
}(3)LPUART串口配置与中断接收模块
该模块是串口远程控制的基础,采用LPUART外设,基于“中断接收+环形缓冲区”设计,负责初始化串口参数、实时接收串口数据并写入环形缓冲区,解决数据异步接收丢失问题,确保指令接收的完整性和实时性,具体代码及功能说明如下:
/**
* @brief LPUART串口初始化(115200波特率,8N1,接收中断使能)
* 功能:配置串口核心参数,初始化LPUART外设,使能接收中断,为串口指令接收和解析做好准备
* 适配MCXA153开发板,兼容环形缓冲区+中断接收架构
*/
void LPUART_Init_Config(void)
{
lpuart_config_t lpuart_config;
// 1. 获取LPUART默认配置,调用SDK封装函数,简化初始化流程,无需手动配置寄存器
LPUART_GetDefaultConfig(&lpuart_config);
// 2. 配置核心参数:115200波特率(串口工具通用速率,兼容性强),8位数据位,无校验,1位停止位(标准8N1格式)
lpuart_config.baudRate_Bps = 115200;
lpuart_config.parityMode = kLPUART_ParityDisabled;
lpuart_config.stopBitCount = kLPUART_OneStopBit;
lpuart_config.enableTx = true; // 开启发送(用于指令执行结果回显,便于调试确认)
lpuart_config.enableRx = true; // 必须开启接收,否则无法接收串口指令
// 3. 初始化LPUART外设,绑定LPUART基地址,应用配置参数,传入总线时钟频率用于波特率计算
LPUART_Init(LPUART_BASEADDR, &lpuart_config, CLOCK_GetFreq(kCLOCK_BusClk));
// 4. 使能LPUART接收中断(关键:串口指令通过中断接收,不阻塞主循环,保证实时性)
// 仅使能“接收数据寄存器满”中断,即接收到1字节数据就触发中断,立即写入缓冲区
LPUART_EnableInterrupts(LPUART_BASEADDR, kLPUART_RxDataRegFullInterruptEnable);
// 5. 使能LPUART中断NVIC通道,设置中断优先级(可选),确保中断能被CPU及时响应
EnableIRQ(BOARD_UART_IRQn);
NVIC_SetPriority(BOARD_UART_IRQn, 1); // 优先级低于按键中断,确保本地控制优先
}
/**
* @brief LPUART接收中断服务函数(核心:数据“生产”,写入环形缓冲区)
* 功能:响应串口接收中断,读取接收数据,检查缓冲区空间后写入环形缓冲区,更新生产索引,防止数据溢出和丢失
*/
void BOARD_UART_IRQHandler(void)
{
uint8_t data;
// 临时变量存储当前索引,避免中断执行过程中索引被篡改,提升稳定性
uint16_t tmprxIndex = rxIndex;
uint16_t tmptxIndex = txIndex;
/* 检查中断源:仅处理“接收数据寄存器满”中断,避免响应其他无关中断(如发送中断) */
if ((kLPUART_RxDataRegFullFlag) & LPUART_GetStatusFlags(LPUART_BASEADDR))
{
data = LPUART_ReadByte(LPUART_BASEADDR); // 读取接收到的1字节数据,清空接收寄存器
/* 检查环形缓冲区是否已满(避免溢出):判断下一个写入位置是否与消费索引重叠 */
if (((tmprxIndex + 1) % DEMO_RING_BUFFER_SIZE) != tmptxIndex)
{
demoRingBuffer[rxIndex] = data; // 缓冲区未满,将数据存入当前写入位置
rxIndex++; // 更新写入索引(生产索引)
rxIndex %= DEMO_RING_BUFFER_SIZE; // 环形取模,当索引达到缓冲区末尾时,自动回到头部,实现“环形”循环
}
// 缓冲区已满时,自动丢弃当前数据,避免覆盖已存储的未解析数据
}
SDK_ISR_EXIT_BARRIER; // SDK中断退出屏障,确保中断执行完成,提升程序稳定性
}(4)串口指令解析模块
该模块是串口远程控制的核心,采用“滑动窗口匹配”解析环形缓冲区中的数据,严格按照固定5字节帧格式(0x5A+状态+方向+占空比+0xA5)识别有效指令,提取参数后执行电机控制,包含完善的容错处理,具体代码及功能说明如下:
/**
* @brief 指令执行函数(核心:解析提取的指令参数,执行对应的电机控制操作)
* @param cmdData 完整的5字节指令帧(已通过帧头帧尾校验)
* 功能:拆解指令帧中的3个有效参数(状态、方向、占空比),执行PWM启停、电机转向、转速调节,同时回显执行结果
*/
void processCommand(uint8_t *cmdData)
{
uint8_t status = cmdData[1]; // 启停状态:1=启动PWM,0=停止PWM(对应指令帧第2字节)
uint8_t direction = cmdData[2]; // 电机方向:1=正转,2=反转(对应指令帧第3字节)
uint8_t pwm = cmdData[3]; // PWM占空比:1-100(对应指令帧第4字节,直接设定电机转速)
/* 1. PWM启停控制 + 电机方向控制 + 占空比更新,联动执行 */
if (status == 1)
{
// 启动PWM:根据方向参数控制电机转向,设置对应占空比,点亮LED指示运行状态
if (direction == 1)
{
Motor_Control(MOTOR_FORWARD, pwm); // 正转
// 指令执行结果回显,便于上位机/串口工具确认(十六进制+中文说明)
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"指令执行:启动PWM+正转+", 18);
}
else if (direction == 2)
{
Motor_Control(MOTOR_REVERSE, pwm); // 反转
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"指令执行:启动PWM+反转+", 18);
}
else
{
// 方向参数无效,默认正转,同时回显提示
Motor_Control(MOTOR_FORWARD, pwm);
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"指令执行:启动PWM+方向参数无效(默认正转)+", 28);
}
// 回显占空比
LPUART_WriteByte(LPUART_BASEADDR, pwm/10 + '0');
LPUART_WriteByte(LPUART_BASEADDR, pwm%10 + '0');
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"%占空比rn", 7);
}
else if (status == 0)
{
// 停止PWM:控制电机停止,熄灭LED,回显执行结果
Motor_Control(MOTOR_STOP, 0);
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"指令执行:停止PWM+电机停止rn", 22);
}
else
{
// 启停状态参数无效,丢弃指令,回显错误提示
LPUART_WriteBlocking(LPUART_BASEADDR, (uint8_t*)"指令错误:启停状态参数无效(仅支持0=停止/1=启动)rn", 44);
}
}
/**
* @brief 主循环串口指令解析函数(核心:数据“消费”,滑动窗口匹配有效指令)
* 功能:在主循环中轮询环形缓冲区,计算有效数据长度,通过滑动窗口遍历匹配指令帧头帧尾,提取有效指令并执行
* 包含容错处理,防止无效数据堆积,保障解析稳定性
*/
void LPUART_Parse_Command(void)
{
uint16_t currentRx = rxIndex; // 当前生产索引(最新写入数据位置)
uint16_t currentTx = txIndex; // 当前消费索引(已解析数据位置)
// 计算缓冲区中未消费的有效数据长度(处理索引跨缓冲区尾部的情况,避免计算错误)
uint16_t available = (currentRx >= currentTx) ?
(currentRx - currentTx) :
(DEMO_RING_BUFFER_SIZE - currentTx + currentRx);
/* 仅当缓冲区中有效数据≥5字节时,才进行解析(满足固定指令帧长度要求) */
if (available >= CMD_DATA_SIZE)
{
bool cmdFound = false; // 标记是否找到有效指令
/* 滑动窗口遍历缓冲区:从当前消费索引开始,以1字节为步长,搜索符合格式的指令帧 */
for (uint16_t offset = 0; offset <= available - CMD_DATA_SIZE; offset++)
{
// 计算当前滑动窗口的起始位置(环形取模,适配索引跨边界情况)
uint16_t pos = (currentTx + offset) % DEMO_RING_BUFFER_SIZE;
/* 校验帧头、帧尾是否匹配:核心校验逻辑,确保指令帧的唯一性和完整性 */
if (demoRingBuffer[pos] == CMD_HEADER &&
demoRingBuffer[(pos + 4) % DEMO_RING_BUFFER_SIZE] == CMD_TAIL)
{
/* 匹配成功,提取完整5字节指令帧,存入临时数组 */
uint8_t cmdData[CMD_DATA_SIZE];
for (uint8_t j = 0; j < CMD_DATA_SIZE; j++)
{
// 环形取模,确保从缓冲区中正确提取每一字节指令
cmdData[j] = demoRingBuffer[(pos + j) % DEMO_RING_BUFFER_SIZE];
}
/* 调用指令执行函数,解析参数并控制电机 */
processCommand(cmdData);
/* 消费已解析的5字节数据:更新消费索引,标记该段数据已处理,避免重复解析 */
txIndex = (pos + CMD_DATA_SIZE) % DEMO_RING_BUFFER_SIZE;
cmdFound = true; // 标记找到有效指令
break; // 找到一条有效指令后退出循环,优先处理当前指令,避免重复解析
}
}
/* 容错处理:未找到有效指令,但缓冲区数据超过5字节,丢弃首字节,防止无效数据长期堆积 */
if (!cmdFound && available > CMD_DATA_SIZE)
{
txIndex = (txIndex + 1) % DEMO_RING_BUFFER_SIZE;
}
}
}
/* 典型指令示例(十六进制,适配固定5字节帧格式) */
// 5A 01 01 50 A5 → 启动PWM+正转+50%占空比
// 5A 01 02 80 A5 → 启动PWM+反转+80%占空比
// 5A 00 00 00 A5 → 停止PWM(方向/占空比参数无效,不影响停止功能)
// 5A 01 01 10 A5 → 启动PWM+正转+10%占空比(5)主函数
主函数是程序的入口,负责整合所有模块,完成初始化和初始状态设置,主循环中仅执行串口指令解析函数,所有交互逻辑(按键、串口)由中断驱动,确保程序高效、实时,具体代码及功能说明如下:
int main(void)
{
// 1. 板级初始化(时钟、引脚):SDK标配初始化函数,自动完成时钟配置、引脚默认配置,无需手动编写
BOARD_InitBootPins();
BOARD_InitBootClocks();
// 2. 外设初始化(顺序不影响,按需初始化):初始化所有核心外设,为程序运行提供硬件支持
GPIO_Init_All(); // GPIO+按键中断初始化,完成本地按键交互的硬件和中断配置
PWM_Init(); // PWM调速初始化,生成适配TB6612的PWM波,初始占空比为0
LPUART_Init_Config(); // LPUART串口初始化,配置环形缓冲区+中断接收,准备接收远程指令
// 3. 初始状态:电机停止,上电后默认将电机设置为停止状态,避免意外转动,保障硬件安全
Motor_Control(MOTOR_STOP, 0);
// 4. 主循环:仅执行串口指令解析函数,无其他阻塞逻辑,降低CPU占用
// 按键交互由GPIO中断驱动,串口数据接收由LPUART中断驱动,解析在主循环轮询执行,兼顾实时性与稳定性
while(1)
{
LPUART_Parse_Command(); // 串口指令解析(数据“消费”),轮询检测缓冲区,解析有效指令
SDK_DelayAtLeastUs(10000, SDK_DEVICE_MAXIMUM_CPU_CLOCK_FREQUENCY); // 10ms轮询,平衡实时性与资源占用
}
}