Funpack5-1 - 基于FRDM-MCXA346实现串口Shell控制板载RGB
该项目使用了FRDM-MCXA346,实现了Shell控制板载RGB LED的设计,它的主要功能为:实现一个带缓冲区的shell,核心是实现一个带缓冲区的shell,程序需能显示命令提示符,并循环接收用户输入,并能进行基本的命令解析。例如:实现简单的指令控制板载的LED的颜色和亮度。。
标签
Funpack活动
FRDM-MCXA346
Shell
AI辅助编程
EPTmachine
更新2026-03-20
17

Digikey联合硬禾学堂举办的Funpack第五季第一期使用的开发板时NXP的FRDM-MCXA346开发板。在官方的开发板介绍页面可以找开发板的介绍、使用说明和硬件原理图。

https://www.nxp.com.cn/design/design-center/development-boards-and-designs/FRDM-MCXA346

开发板的功能框图如下:



基于开发板实现以下功能:实现一个带缓冲区的shell,核心是实现一个带缓冲区的shell,程序需能显示命令提示符,并循环接收用户输入,并能进行基本的命令解析。例如:实现简单的指令控制板载的LED的颜色和亮度。

1、获取开发板需要的软件

NXP官方提供开发板入门页面

https://www.nxp.com.cn/document/guide/getting-started-with-frdm-mcxa346:GS-FRDM-MCXA346

从获取软件页面,安装MCUXpresso IDE和开发板对应的SDK。

FRDM_Guide_Page.png

MCUXpresso IDE中显示Installed SDKs窗口

open_install_sdks_view.png

将下载的FRDM-MCXA346 SDK导入,即可创建FRDM-MCXA346的工程

add_frdm_mcxa346_sdk.png

2、利用DeepSeek给出Shell程序参考设计

串口Shell属于常见的应用程序,在DeepSeek对话中输入程序设计的需求。完整的对话见以下链接。

https://chat.deepseek.com/share/0svi3ydgw6nwze0465

我现在有一个MCU的串口数据收发工程,需要再次基础上实现一个带缓冲区的shell,核心是实现一个能读取、解析并执行用户输入命令的简易命令行解释器,并管理好输入缓冲区。基本要求:程序需能显示命令提示符(如 ysh > $),并循环接收用户输入(\n换行符为断句符号)。管理好输入缓冲区,妥善处理字符,并能进行基本的命令解析。例如:实现简单的指令控制板载的LED的颜色和亮度。

输入上述的提示词,在返回的对话中,给出设计Shell的思路,以及各部分设计的参考代码。

  1. 概述shell设计思路
  2. 缓冲区管理(环形缓冲区、行缓冲区)
  3. 命令解析模块
  4. 命令执行模块(示例LED控制)
  5. 集成到主循环
  6. 代码示例

利用AI对话获取的参考答案实现Shell,在后续的实现过程中,整体思路可以参考,其中和硬件不相关部分的代码可以作为参考。不过给出的回答中的代码,在最后给出的参考代码之间存在不一致,使用DeepSeek进行单次对话的参考,需要在理解C语言编程的前提下,利用AI辅助编写程序片段,作为开发程序的参考。

2.1 NXP参考示例程序

导入到MCUXpresso IDE中的FRDM-MCXA346 SDK中示例程序可供参考,在示例程序的基础上添加以上从AI提供的参考代码,实现带有指令功能的Shell,控制板载的LED的颜色和亮度。

import_sdk_drivers_examples.png

以上为导入一个ctimer外设产生PWM的示例程序,用做通过PWM控制RGB的参考。

按照同样的导入方法,可以导入其他的示例程序,熟悉FRDM-MCXA346的其他外设的使用方法。

导入的示例程序中,主要关注以下几个文件夹中的代码。

  • board文件夹中是和具体硬件相关的初始化和配置代码;
  • drivers文件夹中是添加到工程中的外设驱动代码
  • source文件夹中是用户的应用程序

Project_Tree.png

实现Shell指令实现PWM控制RGB,需要确认UART和CTIMER的驱动添加工程中。

add_sdk_drivers_components.png

查看FRDM-MCXA346的原理图可知,RGB LED连接的引脚编号如图。

RGB_LED_Pins.png

在MCU外设的配置界面查看对应的引脚可用的外设配置选项,控制RGB的引脚配置为CTIMER2的0、1、3通道的比较输出模式,可以用于PWM控制LED的亮度。

Board_Pins_Functions_Select.png

开发板配置的串口和RGB LED引脚列表如下:

Board_InitPins.png

2.2 参考AI代码添加Shell代码

board\app.h中定义用于串口和定时器的宏,管理具体硬件的硬件信息,便于在不同的硬件平台上移植程序。

#ifndef _APP_H_
#define _APP_H_

#include "board.h"

/*******************************************************************************
* Definitions
******************************************************************************/
/*${macro:start}*/
#define DEMO_LPUART LPUART2
#define DEMO_LPUART_CLK_FREQ (BOARD_DEBUG_UART_CLK_FREQ)
#define DEMO_LPUART_IRQn LPUART2_IRQn
#define DEMO_LPUART_IRQHandler LPUART2_IRQHandler

#define CTIMER CTIMER2 /* Timer 2 */
#define CTIMER_MAT0_OUT kCTIMER_Match_0 /* Match output 0 */
#define CTIMER_MAT1_OUT kCTIMER_Match_1 /* Match output 1 */
#define CTIMER_MAT3_OUT kCTIMER_Match_3 /* Match output 1 */
#define CTIMER_CLK_FREQ CLOCK_GetCTimerClkFreq(2U)

/*${macro:end}*/

/*******************************************************************************
* Prototypes
******************************************************************************/
/*${prototype:start}*/
void BOARD_InitHardware(void);

/* Array of function pointers for callback for each channel */
ctimer_callback_t ctimer_callback_table[] = {
NULL,NULL , NULL, NULL, NULL, NULL, NULL, NULL};
/*${prototype:end}*/

#endif /* _APP_H_ */

串口接收字符缓存区管理,结合AI提供的参考代码,修改串口回调函数以及其中的数据接收代码。对串口收发函数进行简单的封装uart_send_stringuart_send_byte,隐藏具体的硬件函数接口。

#define RING_SIZE 64

static uint8_t ring[RING_SIZE];
static volatile uint8_t head = 0, tail = 0;

void DEMO_LPUART_IRQHandler(void)
{
uint8_t data;

/* If new data arrived. */
if ((kLPUART_RxDataRegFullFlag)&LPUART_GetStatusFlags(DEMO_LPUART))
{
data = LPUART_ReadByte(DEMO_LPUART);

uint8_t next = (head + 1) % RING_SIZE;
if (next != tail) {
ring[head] = data;
head = next;
}
}
SDK_ISR_EXIT_BARRIER;
}

static int get_char(void) {
if (head == tail) return -1;
uint8_t c = ring[tail];
tail = (tail + 1) % RING_SIZE;
return c;
}

static void uart_send_string(const char *s) {

LPUART_WriteBlocking(DEMO_LPUART, s, strlen(s));
}

static void uart_send_byte(const char c){
LPUART_WriteBlocking(DEMO_LPUART, &c, 1);
}

static void print_prompt(void) {
uart_send_string("ysh > $ ");
}

命令行缓存区,直到遇到换行符。同时需要支持退格删除、回显等基本功能。定义行缓冲区:

#define LINE_SIZE 64

static char line[LINE_SIZE];
static uint8_t len = 0;

Shell初始化并打印输入提示符:

void shell_init(void) {
head = tail = 0;
len = 0;
print_prompt();
}

从环形缓冲区取出字符,读取到\r\n进行指令的处理和Shell换行;读取到退格字符,通过发送\b \b实现串口终端退格;读取到其他可显示字符,将字符发回,实现回显功能;处理函数如下:

void shell_process_char(void) {
int c = get_char();
if (c == -1) return;

if (c == '\r' || c == '\n') {
line[len] = '\0';
uart_send_string("\r\n");
execute_command(line);
len = 0;
print_prompt();
}
else if (c == '\b' || c == 0x7F) {
if (len > 0) {
len--;
uart_send_string("\b \b");
}
}
else if (c >= ' ' && c <= '~') {
if (len < LINE_SIZE - 1) {
line[len++] = c;
uart_send_byte(c);
}
}
}

解析器将行缓冲区内容分割成命令名和参数。采用简单的空格分割,忽略行首尾空格。然后通过命令表查找匹配的命令并调用对应处理函数。

命令结构体存储指令字符以及指令函数,定义相应的命令数组用于存储多个指令。

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

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

cmd_t cmd_table[] = {
{"led", cmd_led},
{"help", cmd_help},
{NULL, NULL}
};

对串口接收的命令字符串进行处理划分指令参数并调用相应指令的处理函数。

static void execute_command(char *cmd_line) {
// 去除前导空格
while (*cmd_line == ' ') cmd_line++;
if (*cmd_line == '\0') return;

char *argv[8];
int argc = 0;
char *p = cmd_line;

//分割和提取指令参数
while (*p) {
while (*p == ' ') p++;
if (*p == '\0') break;
argv[argc++] = p;
while (*p && *p != ' ') p++;
if (*p) {
*p = '\0';
p++;
}
}

//调用指令处理函数
for (cmd_t *c = cmd_table; c->name; c++) {
if (strcmp(argv[0], c->name) == 0) {
c->handler(argc, argv);
return;
}
}

uart_send_string("Unknown command\r\n");
}

板载一个共阳RGB LED,分别由三个PWM通道控制。定义以下子命令:

  • led red on / led red off
  • led green on / led green off
  • led blue on / led blue off
  • led brightness 50 (设置亮度0-100%)
  • help

其中help指令用于显示可用的指令

void cmd_help(int argc, char *argv[]) {
uart_send_string("Available commands:\r\n");
uart_send_string(" led red on/off\r\n");
uart_send_string(" led green on/off\r\n");
uart_send_string(" led blue on/off\r\n");
uart_send_string(" led brightness <0-100>\r\n");
uart_send_string(" help\r\n");
}

由于RGB是共阳极的,PWM的占空比越大,亮度越低,在计算占空比时,对输入的占空比进行处理,和亮度保持比例关系。

    dutyCycle = 100-val;
CTIMER_UpdatePwmPulsePeriodValue(dutyCycle);

RGB LED的亮灭状态使用数组变量进行管理,在亮度发生改变时,根据RGB的是否开启,调整相应的CTIMER模块的比较寄存器中的值,实现RGB LED的亮度调节。

#define LED_OFF 0
#define LED_ON 1

enum RGB_LED{
LED_RED, //CTIMER_MATCH0
LED_GREEN, //CTIMER_MATCH1
LED_BLUE //CTIMER_MATCH3
};

static uint8_t dutyCycle = 99;
volatile uint32_t g_pwmPeriod = 0U;
volatile uint32_t g_pulsePeriod = 0U;
static uint16_t brightness = 100;


static uint8_t rgb_ledstatus[3];
static uint8_t rgb_matchreg[3]={CTIMER_MAT0_OUT,CTIMER_MAT1_OUT,CTIMER_MAT3_OUT};

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;
}
status_t CTIMER_UpdatePwmPulsePeriodValue(uint8_t dutyCyclePercent)
{
/* Calculate pulse width match value */
g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

return kStatus_Success;
}

static void update_rgb_brightness(void)
{
for(uint8_t i=0;i<3;i++)
{
if(rgb_ledstatus[i])
{
CTIMER_UpdatePwmPulsePeriod(CTIMER, rgb_matchreg[i], g_pulsePeriod);
}
}
}

led的控制采用指令 [参数1] [参数2]的方式实现控制,指令处理函数首先进行输入参数检查,并在完成指令解析后,调用CTIMER的PWM周期设置函数设置PWM的占空比,从而调节RGB LED的亮度。利用AI辅助实现常用功能的代码,在AI提供的代码基础上,添加硬件相关的函数调用。

void cmd_led(int argc, char *argv[]) {
if (argc < 2) {
uart_send_string("Usage: led <red|green|blue|brightness> ...\r\n");
return;
}

if (strcmp(argv[1], "red") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT0_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_RED]=LED_ON;
uart_send_string("Red ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT0_OUT, 0);
rgb_ledstatus[RGB_RED]=LED_OFF;
uart_send_string("Red OFF\r\n");
}
}
else if (strcmp(argv[1], "green") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT1_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_GREEN]=LED_ON;
uart_send_string("Green ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT1_OUT, 0);
rgb_ledstatus[RGB_GREEN]=LED_OFF;
uart_send_string("Grenn OFF\r\n");
}
}
else if (strcmp(argv[1], "blue") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT3_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_BLUE]=LED_ON;
uart_send_string("Blue ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT3_OUT, 0);
rgb_ledstatus[RGB_BLUE]=LED_OFF;
uart_send_string("Blue OFF\r\n");
}
}
else if (strcmp(argv[1], "brightness") == 0) {
if (argc < 3) return;
int val = atoi(argv[2]);
if (val < 0) val = 0;
if (val > 100) val = 100;
dutyCycle = 100-val;
CTIMER_UpdatePwmPulsePeriodValue(dutyCycle);
update_rgb_brightness();
uart_send_string("Brightness set\r\n");
}
else {
uart_send_string("Invalid subcommand\r\n");
}
}

完成以上功能模块,结合实例程序的代码,对外设进行初始化,调用shell初始化代码和shell字符串处理代码,实现Shell指令控制RGB LED亮灭和亮度调节功能。

int main(void)
{
lpuart_config_t uart_config;
ctimer_config_t config;
uint32_t srcClock_Hz;
uint32_t timerClock;

/* Init hardware*/
BOARD_InitHardware();

srcClock_Hz = CTIMER_CLK_FREQ;

CTIMER_GetDefaultConfig(&config);
timerClock = srcClock_Hz / (config.prescale + 1);

CTIMER_Init(CTIMER, &config);

/* Get the PWM period match value and pulse width match value of 2Khz PWM signal */
CTIMER_GetPwmPeriodValue(2000, dutyCycle, timerClock);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT0_OUT, g_pwmPeriod, 0, false);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT1_OUT, g_pwmPeriod, 0, false);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT3_OUT, g_pwmPeriod, 0, false);
CTIMER_StartTimer(CTIMER);


LPUART_GetDefaultConfig(&uart_config);
uart_config.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
uart_config.enableTx = true;
uart_config.enableRx = true;

LPUART_Init(DEMO_LPUART, &uart_config, DEMO_LPUART_CLK_FREQ);
shell_init();
/* Enable RX interrupt. */
LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_RxDataRegFullInterruptEnable);
EnableIRQ(DEMO_LPUART_IRQn);

while (1)
{
/* Send data only when LPUART TX register is empty and ring buffer has data to send out. */
while (kLPUART_TxDataRegEmptyFlag & LPUART_GetStatusFlags(DEMO_LPUART))
{
shell_process_char();
}
}
}

程序编译并下载到开发板,即可实现Shell指令控制RGB LED亮灭和调节亮度的功能。

3、利用RTThread实现类似的功能

上面利用DeepSeek提供的参考代码实现的简单的Shell控制功能,利用AI给出的答案,可以快速搭建出可用的代码片段。常见的RTOS中同样带有shell控制功能,提供更为丰富的功能,借助其提供的接口函数和说明文档,可以实现相同的功能。

3.1 获取RTThread代码以及软件

从Github上拉取RTThrea的代码仓库

https://github.com/RT-Thread/rt-thread.git

同时下载相应的env windows工具,用于配置RTThread代码。

https://github.com/RT-Thread/env-windows/releases/download/v2.0.0/env-windows-v2.0.0.7z

env windows工具解压到指定的位置,并在设置界面中将env工具集成到右键快捷菜单,便于在RTThread代码中打开env工具,对代码进行配置。

env_register.png

bsp\nxp\mcx\mcxa\frdm-mcxa346中打开env工具,输入menuconfig指令,进入代码的配置面板。

menuconfig_board_driver.png

确认开启串口并保存退出。运行scons --target=mdk5指令更新Keil MDK5工程。打开文件夹中名为project的Keil5 MDK工程。编译并下载后,通过串口控制程序运行Shell指令。

mcxa346_finsh_shell1.png

3.2 修改RTThread工程

原有的RTThread工程已经支持Finsh Shell功能,需要添加指令控制RGB的指令,在代码中引入MCXA346 CTIMER模块的相关头文件。

#include "board.h"
#include "fsl_ctimer.h"

复制MCUXpresso IDE中的裸机工程中的RGB控制相关的代码。由于使用RTThread工程中提供的串口数据发送函数,将原有代码中的uart_send_string替换为rt_kprintf

在工程使用指令注册宏,将RGB LED控制的指令函数添加到Shell指令列表中。

MSH_CMD_EXPORT(rgb_ctrl, rgb_led contorl command);

对原有的代码进行接口代码修改后,添加RGB LED控制指令的代码如下。

#define LED_OFF 0
#define LED_ON 1

#define CTIMER CTIMER2 /* Timer 2 */
#define CTIMER_MAT0_OUT kCTIMER_Match_0 /* Match output 0 */
#define CTIMER_MAT1_OUT kCTIMER_Match_1 /* Match output 1 */
#define CTIMER_MAT3_OUT kCTIMER_Match_3 /* Match output 1 */
#define CTIMER_CLK_FREQ CLOCK_GetCTimerClkFreq(2U)

#define CTIMER_MAT_PWM_PERIOD_CHANNEL kCTIMER_Match_2

enum RGB_LED{
RGB_RED,
RGB_GREEN,
RGB_BLUE
};

static uint8_t rgb_ledstatus[3];
static uint8_t rgb_matchreg[3]={CTIMER_MAT0_OUT,CTIMER_MAT1_OUT,CTIMER_MAT3_OUT};


static uint8_t dutyCycle = 99;
volatile uint32_t g_pwmPeriod = 0U;
volatile uint32_t g_pulsePeriod = 0U;

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;
}
status_t CTIMER_UpdatePwmPulsePeriodValue(uint8_t dutyCyclePercent)
{
/* Calculate pulse width match value */
g_pulsePeriod = (g_pwmPeriod + 1U) * (100 - dutyCyclePercent) / 100;

return kStatus_Success;
}

static void update_rgb_brightness(void)
{
for(uint8_t i=0;i<3;i++)
{
if(rgb_ledstatus[i])
{
CTIMER_UpdatePwmPulsePeriod(CTIMER, rgb_matchreg[i], g_pulsePeriod);
}
}
}

void ctimer_pwm_init(void)
{
ctimer_config_t config;
uint32_t srcClock_Hz;
uint32_t timerClock;

srcClock_Hz = CTIMER_CLK_FREQ;
CTIMER_GetDefaultConfig(&config);
timerClock = srcClock_Hz / (config.prescale + 1);

CTIMER_Init(CTIMER, &config);

/* Get the PWM period match value and pulse width match value of 2Khz PWM signal */
CTIMER_GetPwmPeriodValue(2000, dutyCycle, timerClock);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT0_OUT, g_pwmPeriod, 0, false);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT1_OUT, g_pwmPeriod, 0, false);
CTIMER_SetupPwmPeriod(CTIMER, CTIMER_MAT_PWM_PERIOD_CHANNEL, CTIMER_MAT3_OUT, g_pwmPeriod, 0, false);
CTIMER_StartTimer(CTIMER);
}

void rgb_ctrl(int argc, char *argv[]) {
if (argc < 2) {
rt_kprintf("Usage: rgb_ctrl <red|green|blue|brightness> ...\r\n");
return;
}

if (strcmp(argv[1], "red") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT0_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_RED]=LED_ON;
rt_kprintf("Red ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT0_OUT, 0);
rgb_ledstatus[RGB_RED]=LED_OFF;
rt_kprintf("Red OFF\r\n");
}
}
else if (strcmp(argv[1], "green") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT1_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_GREEN]=LED_ON;
rt_kprintf("Green ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT1_OUT, 0);
rgb_ledstatus[RGB_GREEN]=LED_OFF;
rt_kprintf("Green OFF\r\n");
}
}
else if (strcmp(argv[1], "blue") == 0) {
if (argc < 3) return;
if (strcmp(argv[2], "on") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT3_OUT, g_pulsePeriod);
rgb_ledstatus[RGB_BLUE]=LED_ON;
rt_kprintf("Blue ON\r\n");
} else if (strcmp(argv[2], "off") == 0) {
CTIMER_UpdatePwmPulsePeriod(CTIMER, CTIMER_MAT3_OUT, 0);
rgb_ledstatus[RGB_BLUE]=LED_OFF;
rt_kprintf("Blue OFF\r\n");
}
}
else if (strcmp(argv[1], "brightness") == 0) {
if (argc < 3) return;
int val = atoi(argv[2]);
if (val < 0) val = 0;
if (val > 100) val = 100;
dutyCycle = 100-val;
CTIMER_UpdatePwmPulsePeriodValue(dutyCycle);
update_rgb_brightness();
rt_kprintf("Brightness set\r\n");
}
else if (strcmp(argv[1], "help") == 0) {
rt_kprintf("RGB LED commands:\r\n");
rt_kprintf(" rgb_ctrl red on/off\r\n");
rt_kprintf(" rgb_ctrl green on/off\r\n");
rt_kprintf(" rgb_ctrl blue on/off\r\n");
rt_kprintf(" rgb_ctrl brightness <0-100>\r\n");
rt_kprintf(" rgb_ctrl help\r\n");
}
else {
rt_kprintf("Invalid subcommand\r\n");
}
}

MSH_CMD_EXPORT(rgb_ctrl, rgb_led contorl command);

int main(void)
{
rt_kprintf("FRDM-MCXA346\r\n");

/* Configure button pin as input with pull-up */
rt_pin_mode(BUTTON_PIN, PIN_MODE_INPUT_PULLUP);

/* Attach interrupt to button pin */
rt_pin_attach_irq(BUTTON_PIN, PIN_IRQ_MODE_FALLING, button_irq_callback, RT_NULL);
rt_pin_irq_enable(BUTTON_PIN, PIN_IRQ_ENABLE);

ctimer_pwm_init();

while (1)
{
rt_thread_mdelay(500);
}
}

在硬件初始化部分需要修改控制RGB引脚的时钟配置和引脚初始化,bsp\nxp\mcx\mcxa\frdm-mcxa346\board\board.c中添加时钟初始化代码。

add_ctimer2_clock_code.png

bsp\nxp\mcx\mcxa\frdm-mcxa346\board\MCUX_Config\board\pin_mux.c/h中的BOARD_InitPins函数中添加CTIMER2复位和修改相关引脚初始化的代码。

ctimer2_init_code_change.png

利用Git版本管理工具,可以清晰的看出修改的部分。编译并下载工程到开发板,在Shell串口中输入help指令,输出结果中显示可用的指令列表。

RGB_LED_Shell_CMD.png

运行rgb_ctrl指令即可控制RGB LED的亮灭和亮度调节。

msh_rgb_contrl.png

4、总结

FRDM-MCXA346开发板提供的SDK示例程序和开发板配置工具可以帮助开发者快速上手开发,利用DeepSeek的AI辅助功能得到的Shell控制程序,给出一个Shell模块设计的大致框架,结合FRDM-MCXA346提供的示例程序,在硬件上进行适配和代码修改,可以很快实现一个Shell模块。 通过对CTIMER PWM示例程序的学习,熟悉如何产生PWM输出,并调整PWM的占空比,实现RGB相关的控制函数和接口设计,结合Shell工程实现控制台指令控制RGB的亮灭和亮度调节。 尝试在RTThread适配的FRDM-MCXA346工程中添加Shell指令控制RGB的亮灭和亮度,修改串口的输出传输指令和硬件初始化函数后,即完成相关功能的移植。 通过本次活动,完成FRDM-MCXA346上实现Shell控制RGB的指令,借助AI和示例程序结合,可以快速完成一个基本Shell控制台的框架,通过调试和修改,完成最终的RGB LED亮灭和亮度调节,并移植RGB控制模块到RTThread中使用,在实现过程中,借助AI和示例参考,加快功能的验证和代码开发,提高开发的效率,利用Git版本管理工具,可以清晰地看到代码的修改。借助不同功能的工具,让开发思路更为清晰。

附件下载
baremetal_project_uart_shell_rgb.zip
使用MCUXpresso创建的裸机工程
rtthread-frdm-mcxa346.zip
使用RTThread实现的工程,需要搭配RTThread代码仓库运行
团队介绍
嵌入式开发工程师
团队成员
EPTmachine
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号