Funpack5-1:基于 NXP FRDM-MCXA346 实现串口 Shell
该项目使用了NXP FRDM-MCXA346,实现了串口 Shell的设计,它的主要功能为:LED 控制,串口命令行交互。
标签
嵌入式系统
Funpack活动
开发板
topgear
更新2026-03-16
4

1. 项目描述

本项目基于NXP FRDM-MCXA346开发板,实现一个带输入缓冲区的简易命令行解释器(Shell)。核心功能包括:

  • 显示自定义命令提示符(sh > $);
  • 循环接收用户输入(以\n换行符为命令结束标志);
  • 管理环形输入缓冲区,防止数据溢出;
  • 解析并执行用户命令,实现板载RGB LED的颜色控制(红/绿/蓝开关)。




2. 硬件资源

2.1 板卡介绍

FRDM-MCXA346是NXP推出的低成本、可扩展开发平台,核心参数如下:

  • 主控芯片:MCXA346(Arm Cortex-M33内核,最高180MHz主频,1MB Flash,256KB RAM);
  • 调试器:板载MCU-Link OB(基于LPC55S16,支持CMSIS-DAP调试和USB转UART桥接);
  • 扩展接口:Arduino UNO R3、mikroBUS、Pmod插座,支持电机控制、传感器等扩展板;
  • 电源:通过USB Type-C(J15)或外部5-9V供电。

FRDM-MCXA346开发板实物图如下:

FRDM-MCXA346开发板

2.2 外设资源

本项目使用的核心外设及引脚分配(参考NXP SDK及板卡手册):

外设类型

外设模块

功能描述

引脚/接口说明

串口

LPUART2

Shell通信(调试串口)

通过MCU-Link USB转UART,波特率115200

GPIO

GPIO3 18/19/21

RGB LED颜色控制

板载RGB LED(D1),对应PIO引脚见SDK board.h




3. 需求分析

3.1 功能需求

  1. 命令提示符:启动后显示sh > $,命令执行后刷新提示符;
  2. 输入管理
    • \n或者\r为命令结束符,支持最长128字节的命令输入;
    • 使用环形缓冲区管理输入,防止溢出;
  3. 命令解析
    • 支持命令+参数格式(如led red on);
    • 内置help命令(显示帮助信息)和led命令(LED控制);
  4. LED控制
    • 颜色控制:led <red/green/blue> <on/off>

3.2 非功能需求

  • 响应时间:命令解析与执行时间<100ms;
  • 健壮性:处理无效命令、参数错误等异常场景;
  • 可扩展性:预留命令表接口,方便添加新命令。




4. 系统框图

系统采用分层架构,从输入到输出的数据流如下:




5. 开发环境

  • IDE:MCUXpresso IDE v25.6.136(Eclipse-based,支持SDK导入和调试);
  • SDK:MCUXpresso SDK for FRDM-MCXA346 v25.12.00 (915 2025-12-19)(包含LPUART、GPIO 等驱动);
  • 编译器:GCC for Arm Embedded(随IDE安装);
  • 串口工具:MobaXterm/MCUXpresso内置串口终端(配置:115200-8-N-1)。




6. 开发过程

6.1 参考例程

基于NXP SDK提供的以下例程进行开发:

  1. frdmmcxa346_hello_world:LPUART初始化和调试串口配置;
  2. frdmmcxa346_lpuart_interrupt:LPUART中断接收模式;
  3. frdmmcxa346_led_blinky_peripheral:GPIO控制LED开关;

6.2 代码架构与核心模块

项目代码分为4个核心模块,结构如下:

project/
├── circular_buffer.h/c # 环形缓冲区模块
├── uart_shell.h/c # LPUART驱动模块
├── cmd_parser.h/c # 命令解析模块
├── led_ctrl.h/c # LED控制模块
└── main.c # 主循环

(1)环形缓冲区模块(circular_buffer.h/c

实现128字节的环形缓冲区,用于存储LPUART接收的字符,核心函数:

/****************** circular_buffer.h ******************/

#ifndef CIRCULAR_BUFFER_H
#define CIRCULAR_BUFFER_H

#include <stdint.h>
#include <stdbool.h>

#define CB_SIZE 128 // 缓冲区大小

typedef struct {
uint8_t buffer[CB_SIZE];
uint16_t head; // 写入位置
uint16_t tail; // 读取位置
} circular_buf_t;

void cb_init(circular_buf_t *cb);
bool cb_push(circular_buf_t *cb, uint8_t data);
bool cb_peek_line(circular_buf_t *cb, uint8_t *line, uint16_t max_len);
void cb_consume_line(circular_buf_t *cb);

#endif /* CIRCULAR_BUFFER_H */


/****************** circular_buffer.c ******************/

#include "circular_buffer.h"
#include <string.h>

void cb_init(circular_buf_t *cb) {
cb->head = 0;
cb->tail = 0;
memset(cb->buffer, 0, CB_SIZE);
}

bool cb_push(circular_buf_t *cb, uint8_t data) {
uint16_t next_head = (cb->head + 1) % CB_SIZE;
if (next_head == cb->tail) return false; // 缓冲区满
cb->buffer[cb->head] = data;
cb->head = next_head;
return true;
}

bool cb_peek_line(circular_buf_t *cb, uint8_t *line, uint16_t max_len) {
uint16_t temp_tail = cb->tail;
uint16_t len = 0;
while (temp_tail != cb->head && len < max_len - 1) {
uint8_t data = cb->buffer[temp_tail];
line[len++] = data;
temp_tail = (temp_tail + 1) % CB_SIZE;
if (data == '\n' || data == '\r') {
line[len] = '\0';
return true;
}
}
return false;
}

void cb_consume_line(circular_buf_t *cb) {
while (cb->tail != cb->head) {
uint8_t data = cb->buffer[cb->tail];
cb->tail = (cb->tail + 1) % CB_SIZE;
if (data == '\n') break;
}
}

(2)LPUART驱动模块(uart_shell.h/c

初始化LPUART2(115200波特率),开启接收中断,在中断中将字符存入环形缓冲区:

/****************** uart_shell.h ******************/

#ifndef UART_SHELL_H
#define UART_SHELL_H

#include <stdint.h>

void uart_shell_init(void);
void uart_send(const char *str);

#endif /* UART_SHELL_H */


/****************** uart_shell.c ******************/

#include "uart_shell.h"
#include "circular_buffer.h"
#include "fsl_lpuart.h"
#include "fsl_clock.h"
#include "board.h"

#define LPUART_INSTANCE LPUART2
#define LPUART_BAUDRATE 115200

extern circular_buf_t rx_buf;

void uart_shell_init(void) {
// lpuart_config_t config;
// CLOCK_SetClockDiv(kCLOCK_DivLPUART2, 1u);
// CLOCK_AttachClk(kFRO12M_to_LPUART2);
//
// LPUART_GetDefaultConfig(&config);
// config.baudRate_Bps = LPUART_BAUDRATE;
// config.enableTx = true;
// config.enableRx = true;
// LPUART_Init(LPUART_INSTANCE, &config, CLOCK_GetLpuartClkFreq(kCLOCK_Lpuart2));

/* Enable RX interrupt. */
LPUART_EnableInterrupts(LPUART_INSTANCE, kLPUART_RxDataRegFullInterruptEnable);
EnableIRQ(LPUART2_IRQn);
}

void uart_send(const char *str) {
while (*str) LPUART_WriteBlocking(LPUART_INSTANCE, (const uint8_t *)str++, 1);
}

void LPUART2_IRQHandler(void) {
uint8_t data;
if (kLPUART_RxDataRegFullFlag & LPUART_GetStatusFlags(LPUART_INSTANCE)) {
LPUART_ReadBlocking(LPUART_INSTANCE, &data, 1);
LPUART_WriteBlocking(LPUART_INSTANCE, &data, 1);
cb_push(&rx_buf, data);
}
}

(3)命令解析模块(cmd_parser.h/c

定义命令表,支持命令匹配和参数解析:

/****************** cmd_parser.h ******************/

#ifndef CMD_PARSER_H
#define CMD_PARSER_H

void cmd_parse_and_execute(char *line);

#endif /* CMD_PARSER_H */


/****************** cmd_parser.c ******************/

#include "cmd_parser.h"
#include "uart_shell.h"
#include "led_ctrl.h"
#include <string.h>
#include <stdio.h>

typedef void (*cmd_func_t)(int argc, char *argv[]);
typedef struct {
const char *name;
cmd_func_t func;
const char *help;
} cmd_t;

static void cmd_help(int argc, char *argv[]);
static void cmd_led(int argc, char *argv[]);

static cmd_t cmd_table[] = {
{"help", cmd_help, "help - Show available commands"},
{"led", cmd_led, "led <red/green/blue> <on/off>"},
};

static void cmd_help(int argc, char *argv[]) {
uart_send("Available commands:\r\n");
for (int i = 0; i < sizeof(cmd_table)/sizeof(cmd_table[0]); i++) {
uart_send(" ");
uart_send(cmd_table[i].help);
uart_send("\r\n");
}
}

static void cmd_led(int argc, char *argv[]) {
if (argc < 3) {
uart_send("Usage: ");
uart_send(cmd_table[1].help);
uart_send("\r\n");
return;
}

led_color_t color;
if (strcmp(argv[1], "red") == 0) color = LED_RED;
else if (strcmp(argv[1], "green") == 0) color = LED_GREEN;
else if (strcmp(argv[1], "blue") == 0) color = LED_BLUE;
else {
uart_send("Error: Invalid color (red/green/blue)\r\n");
return;
}

bool on = (strcmp(argv[2], "on") == 0);
if (strcmp(argv[2], "on") != 0 && strcmp(argv[2], "off") != 0) {
uart_send("Error: Invalid state (on/off)\r\n");
return;
}
led_set_color(color, on);
char msg[32];
snprintf(msg, sizeof(msg), "LED %s %s\r\n", argv[1], argv[2]);
uart_send(msg);
}

void cmd_parse_and_execute(char *line) {
char *argv[10];
int argc = 0;
char *token = strtok(line, " \r\n");
while (token && argc < 10) {
argv[argc++] = token;
token = strtok(NULL, " \r\n");
}

if (argc == 0) return;
bool found = false;
for (int i = 0; i < sizeof(cmd_table)/sizeof(cmd_table[0]); i++) {
if (strcmp(argv[0], cmd_table[i].name) == 0) {
cmd_table[i].func(argc, argv);
found = true;
break;
}
}
if (!found) uart_send("Unknown command. Type 'help'.\r\n");
}

(4)LED控制模块(led_ctrl.h/c

初始化RGB LED的GPIO(颜色控制)和PWM(亮度控制),核心函数:

/****************** led_ctrl.h ******************/

#ifndef LED_CTRL_H
#define LED_CTRL_H

#include <stdint.h>
#include <stdbool.h>

typedef enum { LED_RED, LED_GREEN, LED_BLUE } led_color_t;

void led_init(void);
void led_set_color(led_color_t color, bool on);

#endif /* LED_CTRL_H */


/****************** led_ctrl.c ******************/

#include "led_ctrl.h"
#include "fsl_gpio.h"
#include "fsl_clock.h"
#include "fsl_port.h"
#include "board.h"
#include "pin_mux.h"

void led_init(void)
{
BOARD_InitLEDsPins();
LED_RED_INIT(LOGIC_LED_OFF);
LED_GREEN_INIT(LOGIC_LED_OFF);
LED_BLUE_INIT(LOGIC_LED_OFF);
}

void led_set_color(led_color_t color, bool on)
{
uint8_t logic_on_off_stat = on ? LOGIC_LED_ON : LOGIC_LED_OFF;

switch (color) {
case LED_RED:
LED_RED_INIT(logic_on_off_stat);
break;
case LED_GREEN:
LED_GREEN_INIT(logic_on_off_stat);
break;
case LED_BLUE:
LED_BLUE_INIT(logic_on_off_stat);
break;
default:
break;
}
}

6.3 主循环逻辑

主循环负责检查缓冲区、解析命令和执行功能,流程如下:

主循环代码(main.c):

/*
* Copyright (c) 2013 - 2015, Freescale Semiconductor, Inc.
* Copyright 2016-2017, 2024 NXP
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/

#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"

#include "circular_buffer.h"
#include "uart_shell.h"
#include "cmd_parser.h"
#include "led_ctrl.h"


#define SHELL_PROMPT "sh > $ "

circular_buf_t rx_buf;

/*!
* @brief Main function
*/
int main(void)
{
char ch;

/* Init board hardware. */
BOARD_InitHardware();

cb_init(&rx_buf);
uart_shell_init();
led_init();

uart_send("Welcome to debug console!\r\nType 'help' for commands.\r\n");
uart_send(SHELL_PROMPT);

while (1) {
uint8_t line[CB_SIZE];
if (cb_peek_line(&rx_buf, line, sizeof(line))) {
cmd_parse_and_execute((char *)line);
cb_consume_line(&rx_buf);
uart_send(SHELL_PROMPT);
}
}
}




7. 功能展示

通过串口终端(如MobaXterm)连接开发板,测试以下功能:

(1)启动与帮助

Welcome to SH!
Type 'help' for commands.
sh > $ help
help - Show available commands
led <red/green/blue> <on/off>
sh > $

(2)LED颜色控制

sh > $ led red on
LED red on
sh > $ led green on
LED green on
sh > $ led blue off
LED blue off

(3)异常处理

sh > $ led yellow on
Invalid color, use red/green/blue




总结

本项目基于FRDM-MCXA346实现了一个轻量级Shell,通过环形缓冲区管理输入、命令表扩展功能,完成了LED的颜色和亮度控制。后续可扩展更多命令(如GPIO读取、ADC采样等),或增加历史命令、Tab补全等功能。

附件下载
frdmmcxa346_uart_shell.zip
项目代码
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号