NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植

简介

前置文章:选读:NXP freescale 开发环境搭建:https://blog.csdn.net/jiladahe1997/article/details/105966658

本文使用的芯片 : NXP MK66FX1
本文使用的开发环境MDK KEIL + RT-thread + RT-thread FINSH

在搭建好开发环境之后,准备使用一款RTOS来做作为基础框架,rtos在嵌入式系统的作用类似于nodejs的express,JavaScript的vue、react一样,是嵌入式开发的底层框架,可以极大的帮助开发者进行开发。

常见的RTOS有,freeRTOS、RT-thread、腾讯的tiny-os,阿里的AliOS等,综合比较之下,我选择了RT-thread,主要原因是中文文档上手快,自带巨好用的finsh控制台。 但是比较是小公司背景,虽然历史悠久,但是N年以后是否还能和阿里腾讯或者亚马逊的freeRTOS抗衡,还很难说。

打个广告: 推荐我自己开发的嵌入式软件的镜像站:https://embeddedproxy.cn/,上面有MDK Keil的安装包、Keil的STM32 PACK、STM32CubeMX、micropython等一系列嵌入式相关软件的国内加速镜像,每日和官方进行一次同步,欢迎试用。




移植步骤目录

  • 简介
  • 移植步骤
    • 1.添加rt-thread到编译链中
    • 2.重新设置汇编编译器(非必须
    • 3.使用 NXP Config Tool 开启串口并生成代码(非必须
    • 4.移动串口初始化函数到最开始
    • 5.移植 重写部分函数
    • 6.验证一下:

移植步骤

1.添加rt-thread到编译链中

请参照官方的移植步骤:https://www.rt-thread.org/document/site/tutorial/nano/nano-port-keil/an0039-nano-port-keil/

Note:记得在 rtconfig.h 文件最上面中加上这几行. 官方文档写的比较隐晦。

简单讲一下这几行的作用:
RT_USING_FINSH : (必须) 开启FINSH线程。
FINSH_USING_DESCRIPTION :(可选) 使FINSH线程在按TAB输出帮助时,可以输出函数描述
FINSH_USING_SYMTAB: (可选)使FINSH线程在按TAB输出帮助时,会自动换行

#define RT_USING_FINSH
#define FINSH_USING_DESCRIPTION
#define FINSH_USING_SYMTAB

NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第1张图片



2.重新设置汇编编译器(非必须

这一步是因为前置文章中生成的nxp工程默认使用 Arm Clang6 进行编译,but rt-thread只能用 Arm Clang5 进行编译。

在按照上一步官方的步骤添加完rt-thread会报以下错误,如果你没有报这个错,说明你的工程使用的是 Arm Clang5 进行编译,无需关注本步骤
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第2张图片
首先将全局的汇编编译器设置为Arm Clang 5
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第3张图片
进行上述步骤之后,会报下面的错:

NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第4张图片
这是因为,上面有说到,NXP工程默认的汇编编译器是 Arm Clang6 ,这是有原因的,因为NXP的startup文件只支持Arm Clang6。 现在为了适配rt-thread,将编译器改成了Arm Clang5,当然会报错。 所以还需要 再单独的把startup文件的编译器设置为 Arm Clang 6.



Note :注意这个勾,默认是灰色的,代表不生效;点击一次后勾会取消掉;再点击一次后,才会变成黑色的勾
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第5张图片



3.使用 NXP Config Tool 开启串口并生成代码(非必须

手动添加BOARD_InitPeripherals函数 这一步就是在config tool上点点点,操作一下生成代码,类似于STM32CubeMX
1.先配置引脚
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第6张图片
2.然后开启外设 peripheral,最后生成代码即可,记得关掉 TDR 和TC中断
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第7张图片

Note : 记得关掉 TDR 和TC中断!发送中断一般是发送数据时才开启,发送完就关闭,这里一上来默认就给你打开,注意踩坑

Note:生成外设peripheral 后并不会自动调用初始化外设的代码 peripheral ,这一点很坑,记得手动加上
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第8张图片





4.移动串口初始化函数到最开始

由于RT-thread在main函数之前,就会执行打印操作,所以上面图中的一系列操作不能放在main函数中执行,而是要在main函数之前,在RT-thread执行打印操作之前就进行。

所以需要调用RT-thread提供的 INIT_BOARD_EXPORT 函数,操作一下。
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第9张图片

从Keil5.27升级到Keil5.29后,下面问题已消失
Note:由于 INIT_BOARD_EXPORT 用了C语言中比较高深的宏定义技巧,必须要在链接属性里面加点东西,才能保证括号里的函数不被 linker链接器优化掉,加上这一句 --keep=(.rti_fn.) 这是因为INIT_BOARD_EXPORT 会自动给调用的函数加上 .rti_fn. arrtibute属性,具体原理请看这篇文章https://www.rt-thread.org/qa/thread-5679-1-1.html
有时候生成的工程会自动加上一行,自动加了就不用手动加了。

NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第10张图片







5.移植 重写部分函数

由于硬件平台不同,rt-thread中集成的两个功能:console控制台输出finsh 是无法使用的。根据rt-thread版本的不同,你可能会遇到两种情况:

  1. 这两个功能使用空函数实现,能够编译成功,但是实际函数没有作用。就像下面的样子:
RT_WEAK void rt_hw_console_output(const char *str)
{
     
    /* empty console output */
}
  1. 这两个功能的函数直接未实现,会编译失败。
    Undefined symbol rt_hw_console_output

我们需要做的就是根据我们使用的MCU和电路板,以及使用的串口,完成这两个函数。

官方实现和具体内容可以参见这篇文章:https://www.rt-thread.org/document/site/tutorial/nano/finsh-port/an0045-finsh-port/

我认为官方的方法过于繁琐了,下面我介绍一下我个人的移植方法。

rt_hw_console_output 函数,这个函数往串口输出数据。直接copy官方的写法:但是官方用的STM32的库,我这里需要简单的替换成NXP的库,直接替换一下 HAL_UART_Transmit -> UART_WriteBlocking

#include "peripherals.h"
void rt_hw_console_output(const char *str)
{
     
    rt_size_t i = 0, size = 0;
    char a = '\r';

    _//_HAL_UNLOCK(&UartHandle);

    size = rt_strlen(str);
    for (i = 0; i < size; i++)
    {
     
        if (*(str + i) == '\n')
        {
     
            //HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 1);
           UART_WriteBlocking(UART4_PERIPHERAL, (uint8_t *)&a, 1);
        }
        //HAL_UART_Transmit(&UartHandle, (uint8_t *)(str + i), 1, 1);
         UART_WriteBlocking(UART4_PERIPHERAL, (uint8_t *)(str + i), 1);
    }
}

rt_hw_console_getchar 函数,这个函数在一个finsh线程中轮询获取一个字符,然后finsh线程会自动保存并解析获取到的字符。

finsh的原理如下:
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第11张图片
官方的移植方法考虑到了finsh处理速度慢于串口的情况,所以用缓冲区实现的比较麻烦,实际上可以使用消息队列更简单的实现,主要原理就是利用消息队列在串口中断和finsh线程之间传递收到的字符。

首先开启消息队列:
NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第12张图片
然后写几行代码:

/* 使用消息队列 */
struct rt_messagequeue mq;
static rt_uint8_t msg_pool[512];

char rt_hw_console_getchar(void)
{
     
    char ch = 0;
	rt_mq_recv(&mq, &ch, sizeof(ch), RT_WAITING_FOREVER);
    return ch;   
}

/* 串口中断 */
void UART4_SERIAL_RX_TX_IRQHANDLER(void) {
     
	uint32_t flag = UART_GetStatusFlags(UART4_PERIPHERAL);
	if ((kUART_RxDataRegFullFlag | kUART_RxOverrunFlag) & flag)
    {
     
        uint8_t rx_data = UART_ReadByte(UART4_PERIPHERAL);
        rt_mq_send(&mq, &rx_data ,1);
    }
}

/* 初始化消息队列 */
void initMQ(){
     
    rt_err_t result;
    result = rt_mq_init(&mq,"mqt",&msg_pool[0],1,sizeof(msg_pool),RT_IPC_FLAG_FIFO);       
    if (result != RT_EOK)
    {
     
        rt_kprintf("finsh shell init message queue failed.\n");
    }
}

/* 将消息队列初始化添加到RT-thread启动 */
INIT_APP_EXPORT(initMQ);



6.验证一下:

NXP freescale 平台的 RTOS RT-thread 以及 finsh控制台 移植_第13张图片

你可能感兴趣的:(嵌入式,嵌入式,编译器)