恩智浦“FRDM-MCXN947”评测活动由安富利和与非网协同举办。本篇内容由与非网用户发布,已获转载许可。原文可在与非网(eefocus)工程师社区查看。
此次任务通过串口命令行控制RGB LED,相比较与上一次任务通过单个字符控制增加了FreeRTOS-CLI组件,支持更复杂的、带参数的命令。
1. 搭建VSCode开发环境
2. 添加FreeRTOS组件,创建任务
3. 添加FreeRTOS-CLI组件,打通适配层
4. 添加FreeRTOS-CLI自定义命令,控制RGB LED
无论是使用MCUXpresso IDE还是VS Code开发环境,都必须要:
1. 安装MCUXpresso IDE,因为IDE里有NXP支持的GCC工具链
2. 下载mcux_sdk_frdm_mcxn947 SDK
3. VS Code安装插件MCUXPresso for VS Code
4. VS Code配置插件MCUXpresso for VS Code
前几个步骤都好说,这里简短演示下VS Code配置插件MCUXpresso for VS Code。
1. 在VS Code侧边栏单击MCUXpresso图标展开右侧视图
2. 单击(2)处展开右侧视图。这里建议单击Import Example from Repository,因为它比上面的Import Repository有更多的配置选型,可以直接从这里创建示例工程
3. 单击(3)处选择本地的SDK路径,例如这里选择已经下载并解压缩的mcux_sdk_frdm_mcxn947
4. 单击(4)处选择MCUXpresso IDE的GCC工具链
5. 单击(5)处选择开发板,一个SDK可以支持同类型的几个开发板,根据需要选择对应的开发板
6. 单击(6)处选择示例工程模版,也可以输入关键词搜索
7. 编辑(7)处输入框输入新建的工程名字
8. 单击(8)处选择工程保存路径
9. 最后点击Create即可以创建工程
.vscode/包含一些配置选项、调试启动文件
repo/是一个链接文件,执行SDK所在文件夹
app/包含应用代码
armgcc包含CMakeLists.txt目录程序以及一些bat、shell编译脚本,如果新增了源文件和头文件,需要修改此处的CMakeLists.txt文件
board/包含管脚、时钟、外设初始化代码,是MCUXpresso Config Tools自动生成的文件夹
iar/是IAR IDE工程文件和链接脚本
mdk/是MDK IDE工程文件和链接脚本
后缀名为*.mex是MCUXpresso Config Tools的输入文件
readme.md是示例工程的说明文档
采用CMake+GCC编译此工程,图简便的话直接点击MCUXpresso for VS Code中的图标,如下图所示。
尝试过MCUXpresso IDE添加FreeRTOS组件,虽然把源码拷贝过来添加到工程里,但是port层的源文件和头文件缺失了,需要从例程拷贝复制,太麻烦了。
而VS Code中添加组件的方式特别简单,如下添加FreeRTOS组件,简直不要太爽了。
1. 鼠标右键单击工程名
2. 在弹出的菜单中选择(2)配置工程
3. 然后选择(3)管理组件
4. 在(4)处编辑框输入kernel过滤组件
5. 在(5)处选择合适的FreeRTOS类型
6. 在(6)处点击确认即可
添加FreeRTOS所做的更改体现在armgcc/config.cmake文件,如下图所示文件中增加了几处和freertos相关的配置选项。当然FreeRTOS源码不会拷贝过来,它依然存在于SDK路径中,但是需要拷贝一份FreeRTOSConfig.h过来,自行修改其中的参数。
新建一个最简单的FreeRTOS任务,每隔两秒钟打印一次信息。
(滑动查看)
int main(void)
{
// 管脚复用和配置
BOARD_InitBootClocks();
BOARD_InitBootPeripherals();
BOARD_InitBootPins();
BOARD_InitSWD_DEBUGPins();
// 调试串口打印日志
BOARD_InitDebugConsole();
PRINTF("\r\n");
PRINTF("\r\n Build: %s %s\r\n\r\n", __DATE__, __TIME__);
if (xTaskCreate(zygote_task, "zygote_task", ZYGOTE_TASK_STACK_SIZE, NULL, ZYGOTE_TASK_PRIORITY, NULL) !=
pdPASS)
{
PRINTF("Task creation failed!.\r\n");
while (1)
;
}
vTaskStartScheduler();
for (;;)
;
}
static void zygote_task(void *pvParameters)
{
uint32_t zygote_loop_cnt = 0;
for (;;) {
zygote_loop_cnt++;
PRINTF("zygote loop cnt: %u \r\n", zygote_loop_cnt);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
FreeRTOS-CLI是FreeRTOS官方的组件,支持注册多参数命令,命令接口可以是串口、网络套接字等。
当前使用的版本是FreeRTOS+CLI V1.0.4,适配层使用串口,注册两个多参数的命令,控制开发板上的RGB LED亮灭。
在源码顶层目录新建3rdparty目录并拷贝FreeRTOS_Plus_CLI组件到此,目录结构如下:
FreeRTOS_Plus_CLI/
port/
serial.c
serial.h
src/
FreeRTOS_CLI.c
FreeRTOS_CLI.h
我们只需要关心port/目录即可,适配UART层在这里。
重点在以下几个函数的适配:
xSerialPortInitMinimal()
xSerialPortInit()
vSerialPutString()
xSerialGetChar()
xSerialPutChar()
因为管脚初始化已经由MCUXpresso Config Tools图形化配置完成,通过Debug UART进行输入输出,所以前两个串口初始化函数可以留空,重点在于xSerialGetChar()和xSerialPutChar()的实现,这里简单实现一下,通过Debug UART进行输入输出即可。
(滑动查看)
signed portBASE_TYPE xSerialGetChar(xComPortHandle pxPort,
signed char *pcRxedChar,
TickType_t xBlockTime)
{
*pcRxedChar = GETCHAR();
return pdPASS;
char data = 0;
while (xBlockTime-- > 0) {
if (kStatus_Success == DbgConsole_TryGetchar(&data)) {
*pcRxedChar = data;
return pdPASS;
} else {
vTaskDelay(pdMS_TO_TICKS(1));
}
}
return pdFAIL;
}
signed portBASE_TYPE xSerialPutChar(xComPortHandle pxPort, signed char cOutChar,
TickType_t xBlockTime)
{
signed portBASE_TYPE ch = 0;
ch = PUTCHAR(cOutChar);
return ch;
}
为了点亮、熄灭RGB LED,需要实现如下这样的命令:
ledset r on点亮红色LED,同理ledset g/b on点亮绿色、蓝色LED
ledset r off熄灭红色LED,同理ledset g/b off熄灭绿色、蓝色LED
ledget r获取红色LED状态,如LEDR:OFF表示熄灭,LEDR:ON表示点亮
(滑动查看)
// TODO: ledset r/g/b on/off
// 作用:设置灯的状态
// 命令: ledset
// 参数1:编号,这里以 r/g/b 缩写分别表示 "RED/GREE/BLUE" 三个灯
// 参数2:开关,这里以字符串 on/off 分别表示 "开灯/关灯"
/**
* @brief
*
* @param pcWriteBuffer
* @param xWriteBufferLen
* @param pcCommandString
* @return BaseType_t
*/
static BaseType_t prvLedSetCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
configASSERT(pcWriteBuffer);
/* param1: r/g/b */
const char *paramLedId = NULL;
BaseType_t paramLedIdLength = 0;
led_id_e mLedId = LED_ID_INVALID;
/* param2: on/off */
const char *paramLedStatus = NULL;
BaseType_t paramLedStatusLength = 0;
led_status_e mLedStatus;
// 首先清除输出缓冲区旧的内容
memset(pcWriteBuffer, 0, xWriteBufferLen);
// TODO: 根据两个参数打印返回的字符串
paramLedId = FreeRTOS_CLIGetParameter(pcCommandString, 1, ¶mLedIdLength);
paramLedStatus = FreeRTOS_CLIGetParameter(pcCommandString, 2, ¶mLedStatusLength);
if (strncmp("r", paramLedId, 1) == 0) {
mLedId = LED_ID_RED;
} else if (strncmp("g", paramLedId, 1) == 0) {
mLedId = LED_ID_GREEN;
} else if (strncmp("b", paramLedId, 1) == 0) {
mLedId = LED_ID_BLUE;
} else {
mLedId = LED_ID_INVALID;
}
if (strncmp("on", paramLedStatus, 2) == 0) {
mLedStatus = LED_ON;
} else if (strncmp("off", paramLedStatus, 3) == 0) {
mLedStatus = LED_OFF;
}
led_set_status(mLedId, mLedStatus);
/* There is no more data to return after this single string, so return pdFALSE. */
return pdFALSE;
}
// TODO: ledget r/g/b
// 作用:获取灯的状态
// 命令: ledget
// 参数1:编号
/**
* @brief
*
* @param pcWriteBuffer
* @param xWriteBufferLen
* @param pcCommandString
* @return BaseType_t
*/
static BaseType_t prvLedGetCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
configASSERT(pcWriteBuffer);
/* param1: r/g/b */
const char *paramLedId = NULL;
BaseType_t paramLedIdLength = 0;
led_id_e mLedId = LED_ID_INVALID;
led_status_e mLedStatus;
// 首先清除输出缓冲区旧的内容
memset(pcWriteBuffer, 0, xWriteBufferLen);
paramLedId = FreeRTOS_CLIGetParameter(pcCommandString, 1, ¶mLedIdLength);
if (strncmp("r", paramLedId, 1) == 0) {
mLedId = LED_ID_RED;
} else if (strncmp("g", paramLedId, 1) == 0) {
mLedId = LED_ID_GREEN;
} else if (strncmp("b", paramLedId, 1) == 0) {
mLedId = LED_ID_BLUE;
} else {
mLedId = LED_ID_INVALID;
}
/* 获取灯的状态 */
mLedStatus = led_get_status(mLedId);
/* 输出灯的状态,输出到 pcWriteBuffer 缓冲区中 */
sprintf(pcWriteBuffer, "%s: %s\r\n", led_helper_id_to_string(mLedId), led_helper_status_to_string(mLedStatus));
/* There is no more data to return after this single string, so return pdFALSE. */
return pdFALSE;
}
先定义结构体,把命令字符串和解析函数关联在一起。
(滑动查看)
/* Structure that defines the "ledset" command line command. This generates
a table that gives information on each task in the system. */
static const CLI_Command_Definition_t xLedSet =
{
"ledset", /* The command string to type. */
"\r\nledset
, :\r\n set r/g/b led status\r\nexample: ledset r on or ledset g off\r\n" prvLedSetCommand, /* The function to run. */
2 /* 2 parameters are expected. */
};
/* Structure that defines the "ledget" command line command. This generates
a table that gives information on each task in the system. */
static const CLI_Command_Definition_t xLedGet =
{
"ledget", /* The command string to type. */
"\r\nledget
:\r\n get r/g/b led status\r\nexample: ledget r or ledget g\r\n" ,prvLedGetCommand, /* The function to run. */
1 /* 1 parameters are expected. */
};
再在合适的时机注册命令,如下所示:
(滑动查看)
void vRegisterBspCliCommands(void)
{
/* Register all the command line commands defined immediately above. */
FreeRTOS_CLIRegisterCommand( &xLedSet );
FreeRTOS_CLIRegisterCommand( &xLedGet );
}
static void zygote_task(void *pvParameters)
{
uint32_t zygote_loop_cnt = 0;
/* FreeRTOS-CLI 任务创建 */
vUARTCommandConsoleStart();
extern void vRegisterSampleCLICommands(void);
vRegisterSampleCLICommands();
vRegisterBspCliCommands();
for (;;) {
zygote_loop_cnt++;
PRINTF("zygote loop cnt: %u \r\n", zygote_loop_cnt);
vTaskDelay(pdMS_TO_TICKS(2000));
}
}
发送命令ledset r on电量红色LED
发送命令ledset r off熄灭红色LED
发送命令ledget r获取红色LED点亮状态
替换r/g/b可以正确执行命令
您可以点击文末“阅读原文”,前往与非网(eefocus)工程师社区查看原始文章。
用户测评(三):使用NXP MCX-N板卡搭建环境及点灯
用户测评(二):体验NXP MCX-N板卡的NPU功能