内容介绍
内容介绍
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开发板实物图如下:

2.2 外设资源
本项目使用的核心外设及引脚分配(参考NXP SDK及板卡手册):
外设类型 | 外设模块 | 功能描述 | 引脚/接口说明 |
|---|---|---|---|
串口 | LPUART2 | Shell通信(调试串口) | 通过MCU-Link USB转UART,波特率115200 |
GPIO | GPIO3 18/19/21 | RGB LED颜色控制 | 板载RGB LED(D1),对应PIO引脚见SDK |
3. 需求分析
3.1 功能需求
- 命令提示符:启动后显示
sh > $,命令执行后刷新提示符; - 输入管理:
- 以
\n或者\r为命令结束符,支持最长128字节的命令输入; - 使用环形缓冲区管理输入,防止溢出;
- 以
- 命令解析:
- 支持命令+参数格式(如
led red on); - 内置
help命令(显示帮助信息)和led命令(LED控制);
- 支持命令+参数格式(如
- 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提供的以下例程进行开发:
frdmmcxa346_hello_world:LPUART初始化和调试串口配置;frdmmcxa346_lpuart_interrupt:LPUART中断接收模式;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
查看更多
猜你喜欢
Funpack5-1 - 用FRDM-MCXA346实现的串口shell该项目使用了FRDM-MCXA346,实现了串口接收并控制LED灯的设计,它的主要功能为:通过串口给FRDM-MCXA346添加了个shell,可以使用命令控制LED灯的效果。
aramy
15
Funpack5-1 - 基于NXP FRDM-MCXA346实现RGB LED Shell控制该项目使用了FRDM-MCXA346,实现了RGB LED Shell控制的设计,它的主要功能为:NXP FRDM-MCXA346实现RGB LED Shell控制。
冲向天空的猪
5
Funpack5-1 - 基于NXP FRDM-MCXA346 开发板实现RGB串口shell该项目使用了NXP FRDM-MCXA346 开发板,实现了串口shell的设计,它的主要功能为:通过串口指令控制板载RGB led灯。
亚历鸽斯
6