电子网首页 > 开源与设计

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

2026-02-07 01:10 | 来源:电子世界报

目前完成的是进阶任务,除了通过开发板按键实现电机启停、正反转、转速调节外,新增串口信号解析功能,支持上位机/串口工具发送指令远程控制电机,兼顾本地按键交互和远程串口控制两种模式。串口解析采用环形缓冲区+中断接收+滑动窗口匹配架构,保障通信稳定性,实现过程如下,视频在文章最底部。

一、硬件原理

1.1 TB6612FNG 驱动芯片控制逻辑

TB6612FNG是一款专为直流电机设计的双通道H桥驱动芯片,相比传统L298N,它具有功耗低、集成度高、自带续流二极管等优势,其核心控制逻辑如下:

AIN1 电平

AIN2 电平

电机状态

正转

反转

/

/

刹车/停止

此外,芯片的PWMA(对应A通道)引脚接收PWM波输入,通过调整PWM占空比可线性调节电机转速:占空比越高,电机转速越快;占空比为0时,电机停止。

1.2 硬件连接设计

本方案中MCUTB6612FNG、外设的核心连接关系:

• PWM输出:MCUFlexPWM模块输出通道(BOARD_PWM_BASEADDR)连接TB6612FNGPWMA引脚,用于输出调速PWM波;

• 方向控制:MCU的两个GPIO引脚(BOARD_Direct1/Direct2)分别连接TB6612FNGAIN1/AIN2,通过电平翻转控制电机转向;

• 按键输入:SW2/SW3两个按键分别接MCUGPIO中断引脚(下降沿触发),用于本地交互控制;

• 状态指示:MCUGPIO引脚控制LED,用于指示PWM运行状态(电机启停);

• 串口通信:MCULPUART外设(BOARD_UART_BASEADDR)通过TX/RX引脚连接电脑/串口工具,用于接收远程控制指令,采用中断接收模式避免主循环阻塞。

二、软件实现

2.1 开发环境

• 开发工具:Keil5

• 底层驱动:NXP MCUX SDK(适配目标MCU,包含PWMGPIO、中断、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;  // 生产索引(中断接收数据、写入缓冲区的位置)
// -----------------------------------------------------------------------------


2PWM/ 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%后再按,重置为停止
            }
        }
    }
}


3LPUART串口配置与中断接收模块

该模块是串口远程控制的基础,采用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轮询,平衡实时性与资源占用
    }
}




推荐技术

返回顶部