使用NXP MCX-N板卡新增命令控制

描述

 

  前言    

恩智浦“FRDM-MCXN947”评测活动由安富利和与非网协同举办。本篇内容由与非网用户发布,已获转载许可。原文可在与非网(eefocus)工程师社区查看。

    背景

此次任务通过串口命令行控制RGB LED,相比较与上一次任务通过单个字符控制增加了FreeRTOS-CLI组件,支持更复杂的、带参数的命令。

1. 搭建VSCode开发环境

2. 添加FreeRTOS组件,创建任务

3. 添加FreeRTOS-CLI组件,打通适配层

4. 添加FreeRTOS-CLI自定义命令,控制RGB LED

    搭建VS Code开发环境

无论是使用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。

01         配置MCUXpresso for VS Code

RGB

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即可以创建工程

02         工程结构

.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是示例工程的说明文档

03         编译、下载、调试

采用CMake+GCC编译此工程,图简便的话直接点击MCUXpresso for VS Code中的图标,如下图所示。

RGB

04         添加FreeRTOS组件

尝试过MCUXpresso IDE添加FreeRTOS组件,虽然把源码拷贝过来添加到工程里,但是port层的源文件和头文件缺失了,需要从例程拷贝复制,太麻烦了。

而VS Code中添加组件的方式特别简单,如下添加FreeRTOS组件,简直不要太爽了。

1. 鼠标右键单击工程名

2. 在弹出的菜单中选择(2)配置工程

3. 然后选择(3)管理组件

4. 在(4)处编辑框输入kernel过滤组件

5. 在(5)处选择合适的FreeRTOS类型

6. 在(6)处点击确认即可

RGB

RGB

添加FreeRTOS所做的更改体现在armgcc/config.cmake文件,如下图所示文件中增加了几处和freertos相关的配置选项。当然FreeRTOS源码不会拷贝过来,它依然存在于SDK路径中,但是需要拷贝一份FreeRTOSConfig.h过来,自行修改其中的参数。

RGB

05         新建FreeRTOS任务

新建一个最简单的FreeRTOS任务,每隔两秒钟打印一次信息。

(滑动查看)

 

int main(void)
{
  // 管脚复用和配置
  BOARD_InitBootClocks();
  BOARD_InitBootPeripherals();
  BOARD_InitBootPins();
  BOARD_InitSWD_DEBUGPins();


  // 调试串口打印日志
  BOARD_InitDebugConsole();


  PRINTF("
");
  PRINTF("
 Build: %s %s

", __DATE__, __TIME__);


  if (xTaskCreate(zygote_task, "zygote_task", ZYGOTE_TASK_STACK_SIZE, NULL, ZYGOTE_TASK_PRIORITY, NULL) !=
        pdPASS)
    {
        PRINTF("Task creation failed!.
");
        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 
", zygote_loop_cnt);


    vTaskDelay(pdMS_TO_TICKS(2000));
  }
}

 

RGB

    FreeRTOS-CLI组件

01         组件介绍

FreeRTOS-CLI是FreeRTOS官方的组件,支持注册多参数命令,命令接口可以是串口、网络套接字等。

当前使用的版本是FreeRTOS+CLI V1.0.4,适配层使用串口,注册两个多参数的命令,控制开发板上的RGB LED亮灭。

02         添加FreeRTOS-CLI组件

在源码顶层目录新建3rdparty目录并拷贝FreeRTOS_Plus_CLI组件到此,目录结构如下:

 

FreeRTOS_Plus_CLI/
    port/
        serial.c
        serial.h
    src/
         FreeRTOS_CLI.c
         FreeRTOS_CLI.h

 

我们只需要关心port/目录即可,适配UART层在这里。


03         适配层

重点在以下几个函数的适配:

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)
{
#ifndef DEBUG_CONSOLE_TRANSFER_NON_BLOCKING
  *pcRxedChar = GETCHAR();
  return pdPASS;
#else
  char data = 0;
  while (xBlockTime-- > 0) {
    if (kStatus_Success == DbgConsole_TryGetchar(&data)) {
      *pcRxedChar = data;
      return pdPASS;
    } else {
      vTaskDelay(pdMS_TO_TICKS(1));
    }
  }


  return pdFAIL;
#endif
}


signed portBASE_TYPE xSerialPutChar(xComPortHandle pxPort, signed char cOutChar,
                                    TickType_t xBlockTime)
{
  signed portBASE_TYPE ch = 0;


  ch = PUTCHAR(cOutChar);


  return ch;
}

 

04         RGB LED控制命令

为了点亮、熄灭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表示点亮

05         点亮、熄灭 LED命令的实现

(滑动查看)

 

// 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;
}

 

06         获取LED状态命令的实现

(滑动查看)

 

// 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
", 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;
}

 

07         注册命令

先定义结构体,把命令字符串和解析函数关联在一起。

(滑动查看)

 

/* 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. */
  "
ledset  :
 set r/g/b led status
example: ledset r on or ledset g off
",
  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. */
  "
ledget :
 get r/g/b led status
example: ledget r or ledget g
",
  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 
", zygote_loop_cnt);


    vTaskDelay(pdMS_TO_TICKS(2000));
  }
}

 

08         验证

发送命令ledset  r  on电量红色LED

发送命令ledset  r  off熄灭红色LED

发送命令ledget r获取红色LED点亮状态

替换r/g/b可以正确执行命令

RGB

RGB

 

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分