【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell

目录

背景:移植RT-Thread nano,并基于  nano 添加 FinSH/shell

前提及准备工作

step1:添加rt-thread nano到裸机工程

1.1、Nano Pack 安装

方法一:Pack Installer 安装

方法二:手动安装

1.2、基础工程准备

1.3、开始移植rtt nano到裸机工程

1.4、适配 RT-Thread nano

1.5、RT-Thread Nano 配置(选配)

1.6 常见问题

step2:添加finsh到工程

2.1、添加finsh源码

2.2、实现uart驱动

2.3、修改console名称

2.4、下载验证

2.5 附录一份uart驱动供参考

2.6 常见问题


背景:移植RT-Thread nano,并基于  nano 添加 FinSH/shell

在nano上添加finsh可以有两种方法:

1、移植finsh基于device框架【这个官方文档中心有相关的文章了,链接:https://www.rt-thread.org/document/site/tutorial/nano/nano_finsh/an0033-nano-finsh/

2、移植finsh不基于device框架【本文讲解这个不基于device框架的,从头讲解,如果移植rtt nano,然后基于这个nano 再移植finsh【本文基于 rtt nano 3.1.2 /3.1.1】

前提及准备工作

  1. 安装了mdk
  2. 一个stm32 mdk裸机工程:要一份能运行的基本工程,如一个f103的可以跑led的裸机工程即可。(这里基于hal库做的mdk工程)
  3. 一个nano源码:我们直接从keil中下载nano的pack包
  4. 注意,本文基于 rtt nano 3.1.2 版本,3.1.1也行

step1:添加rt-thread nano到裸机工程

注意:step1 是参考了RTT文档中心的教程。

1.1、Nano Pack 安装

Nano Pack 可以通过 MDK 联网安装,也可以手动安装。下面开始介绍两种安装方式。

方法一:Pack Installer 安装

打开 MDK 软件,点击工具栏的 Pack Installer 图标:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第1张图片

点击右侧的 Pack,展开 Generic,可以找到 RealThread::RT-Thread,点击 Action 栏对应的 Install ,就可以在线安装 Nano Pack 了。另外,如果需要安装其他版本,则需要展开 RealThread::RT-Thread,进行选择。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第2张图片

方法二:手动安装

我们也可以从官网下载安装文件,RT-Thread Nano 离线安装包下载,下载结束后双击文件进行安装。

RT-Thread Nano 离线安装包下载路径:https://www.rt-thread.org/download/mdk/RealThread.RT-Thread.3.0.3.pack

1.2、基础工程准备

在开始创建 RT-Thread 小系统之前,我们需要准备一个能正常运行的裸机工程。作为示例,本文使用的是基于 STM32L475-Pandora 和 HAL 库的一个 LED 闪烁程序。程序的主要截图如下:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第3张图片

在我们的例程中主要做了系统初始化与LED闪烁功能,编译下载程序后,就可以看到 LED 闪烁了。读者可以根据自己的需要使用其他芯片,完成一个简单的类似裸机工程。

1.3、开始移植rtt nano到裸机工程

打开已经准备好的可以运行的裸机程序,将 RT-Thread 添加到工程。如下图,点击 Manage Run-Time Environment。、

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第4张图片

在 Manage Rum-Time Environment 里”Software Component” 栏找到 RTOS,Variant 栏选择 RT-Thread,然后勾选 kernel,点击”OK” 就添加 RT-Thread 内核到工程了。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第5张图片

现在可以在 Project 看到 RT-Thread RTOS 已经添加进来了,展开 RTOS,可以看到添加到工程的文件:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第6张图片

 

Cortex-M 芯片内核移植代码:

context_rvds.s
cpuport.c

kernel文件

clock.c
components.c
device.c
idle.c
ipc.c
irq.c
kservice.c
mem.c
object.c
scheduler.c
thread.c
timer.c

 应用代码及配置文件:

board.c
rtconfig.h

1.4、适配 RT-Thread nano

RT-Thread 会用到了异常处理函数 HardFault_Handler() 和悬挂处理函数 PendSV_Handler(),以及 Systick 中断服务函数 SysTick_Handler(),所以用户代码需要保证这几个函数没有被使用,若编译提示函数重复定义,请删除自己定义的函数。

RT-Thread Nano 在 board.c 中默认完成了 systick 的配置,用户可以修改宏 RT_TICK_PER_SECOND 的值配置每秒 systick 数。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第7张图片

RT-Thread Nano 默认是使用数组作为 heap。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第8张图片

替换例程中的 delay 函数:

1). 包含 RT-Thread 的相关头文件

2). 删除之前在裸机工程中做的系统配置(如hal初始化、时钟初始化等),这是因为RT-Thread在系统启动时已经配置完成,否则会重复配置。

3). 将 delay() 函数替换成 rt_thread_mdelay(),如 rt_thread_mdelay(500) 将延时 500ms。

下面是完成修改的代码:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第9张图片

编译程序之后下载到芯片就可以看到基于 RT-Thread 的程序运行起来了,LED 正常闪烁。

注意事项:当添加 RT-Thread 之后,裸机中的 main() 函数会自动变成 RT-Thread 系统中 main 线程 的入口函数。由于线程不能一直独占 CPU,所以此时在 main() 中使用 while(1) 时,需要有让出 CPU 的动作,比如使用 rt_thread_mdelay() 系列的函数让出 CPU。另外3.0.3版本中还没有 rt_thread_mdelay() 接口,可以使用 rt_thread_delay()

1.5、RT-Thread Nano 配置(选配)

用户可以根据自己的需要通过修改 rtconfig.h 文件里面的宏定义配置相应功能。

RT-Thread Nano 默认未开启宏 RT_USING_HEAP,故只支持静态方式创建任务及信号量。若要通过动态方式创建对象则需要在 rtconfig.h 文件里开启 RT_USING_HEAP 宏定义。

MDK 的配置向导 configuration Wizard 可以很方便的对工程进行配置,Value 一栏可以选中对应功能及修改相关值,等同于直接修改配置文件 rtconfig.h。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第10张图片

1.6 常见问题

Q1:如何升级 pack?

A:Pack 升级步骤基本如同软件包,展开 RealThread::RT-Thread 后,选择比较新的 Nano 版本,点击 Install 进行安装。如下图所示,点击红色框中的 Install 进行升级,即可将 3.1.1 版本升级到 3.1.2。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第11张图片

step2:添加finsh到工程

2.1、添加finsh源码

点击Manage Run-Environment:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第12张图片

勾选device drivers与shell,这将自动把FinSH组件的源码到工程,并自动添加 #define RTE_USING_DEVICE#define RTE_USING_FINSH 宏:

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第13张图片

2.2、实现uart驱动

实现uart驱动,主要实现初始化与读写接口,并借助了device注册接口,将uart注册到系统中,使其更方便的对接shell。需要实现的函数原型如下:

rt_err_t  (*init)   (rt_device_t dev);
rt_err_t  (*open)   (rt_device_t dev, rt_uint16_t oflag);
rt_size_t (*read)   (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
rt_size_t (*write)  (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);

//外加一个注册的函数,注册一个 rt_device 的实例,方便对接 FinSH 组件。

需要添加的代码如下所示:

rt_err_t uart_init(rt_device_t dev)
{
    ...
}

rt_err_t uart_open(rt_device_t dev, rt_uint16_t oflag)
{
    return	uart_init(dev);
}

rt_size_t uart_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    ...                   
}

rt_size_t uart_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    ...
}               

struct rt_device uart_dev;
static int uart_register(void)     
{
    uart_dev.init  = uart_init;
    uart_dev.open  = uart_open;
    uart_dev.read  = uart_read;
    uart_dev.write = uart_write;
  
    rt_device_register(&uart_dev,"uart1",0);
    return 0;
}
INIT_BOARD_EXPORT(uart_register);
  • uart_init():初始化串口,包括硬件引脚的初始化与串口传输模式及波特率等的设置。
  • uart_open():打开串口,使用该接口仅仅是为了对接 FinSH,没有实际意义,直接返回串口初始化即可。
  • uart_read():读入字符,长度size。
  • uart_write():输出字符,长度size。注意输出打印时,在遇到 \n 时,就在输出 \n 之前先输出一个 \r
  • uart_register():注册函数,将uart设备注册到系统中,务必使用INIT_BOARD_EXPORT()初始化该函数。

注意:RT-Thread 系统中已有的打印均以 “\n” 结尾,而并非 “\r\n”,所以在字符输出时,需要在输出“\n”之前输出“\r”完成回车与换行,否则系统打印出来的信息将只有换行。

2.3、修改console名称

打开rtconfig.h文件:将console的名称修改为刚注册到系统中的uart名称,这样FinSH就对接到了UART端口上。如上面注册名为“uart1”,则在rtconfig.h中修改 RT_CONSOLE_DEVICE_NAME 的定义为 uart1:

#define RT_CONSOLE_DEVICE_NAME      "uart1"

2.4、下载验证

下载到开发板进行验证,移植成功

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第14张图片

2.5 附录一份uart驱动供参考

static UART_HandleTypeDef UartHandle;
rt_err_t uart_init(rt_device_t dev)
{
    UartHandle.Instance = USART1;
    UartHandle.Init.BaudRate   = 115200;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = UART_MODE_TX_RX;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = UART_PARITY_NONE;

    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        while (1);
    }

    return 0;
}

rt_err_t uart_open(rt_device_t dev, rt_uint16_t oflag)
{
    return  uart_init(dev);
}

rt_size_t uart_read(rt_device_t dev,
                    rt_off_t    pos,
                    void       *buffer,
                    rt_size_t   size)
{
    while (1)
    {
        if (HAL_UART_Receive(&UartHandle, buffer, size, 20) == 0)
        {
            return size;
        }
    }
}

rt_size_t uart_write(rt_device_t dev,
                     rt_off_t    pos,
                     const void *buffer,
                     rt_size_t   size)
{
    rt_size_t i = 0;
    char a = '\r';
    const char *val = 0;    

    val = (const char *)(buffer);
    
    __HAL_UNLOCK(&UartHandle);

    for (i = 0; i < size; i++)
    {
        if (*(val + i) == '\n')
        {
            HAL_UART_Transmit(&UartHandle, (uint8_t *)&a, 1, 20);
        }
        HAL_UART_Transmit(&UartHandle, (uint8_t *)(val + i), 1, 20);
    }
    return 1;
}

struct rt_device uart_dev;
static int uart_register(void)
{
    uart_dev.init  = uart_init;
    uart_dev.open  = uart_open;
    uart_dev.read  = uart_read;
    uart_dev.write = uart_write;

    rt_device_register(&uart_dev, "uart1", 0);
    return 0;
}
INIT_BOARD_EXPORT(uart_register);

2.6 常见问题

Q1:输出打印正常,却没有打印 msh >,也不能输入。

【超详细教程】移植RT-Thread nano,并基于 nano 添加 FinSH/shell_第15张图片

A:这是由于FinSH 没打开,所以只有打印功能,需要在rtconfig.h中打开 #define RTE_USING_FINSH 宏定义。

你可能感兴趣的:(RT-Thread)