目录
背景:移植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 常见问题
在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】
注意:step1 是参考了RTT文档中心的教程。
Nano Pack 可以通过 MDK 联网安装,也可以手动安装。下面开始介绍两种安装方式。
打开 MDK 软件,点击工具栏的 Pack Installer 图标:
点击右侧的 Pack,展开 Generic,可以找到 RealThread::RT-Thread,点击 Action 栏对应的 Install ,就可以在线安装 Nano Pack 了。另外,如果需要安装其他版本,则需要展开 RealThread::RT-Thread,进行选择。
我们也可以从官网下载安装文件,RT-Thread Nano 离线安装包下载,下载结束后双击文件进行安装。
RT-Thread Nano 离线安装包下载路径:https://www.rt-thread.org/download/mdk/RealThread.RT-Thread.3.0.3.pack
在开始创建 RT-Thread 小系统之前,我们需要准备一个能正常运行的裸机工程。作为示例,本文使用的是基于 STM32L475-Pandora 和 HAL 库的一个 LED 闪烁程序。程序的主要截图如下:
在我们的例程中主要做了系统初始化与LED闪烁功能,编译下载程序后,就可以看到 LED 闪烁了。读者可以根据自己的需要使用其他芯片,完成一个简单的类似裸机工程。
打开已经准备好的可以运行的裸机程序,将 RT-Thread 添加到工程。如下图,点击 Manage Run-Time Environment。、
在 Manage Rum-Time Environment 里”Software Component” 栏找到 RTOS,Variant 栏选择 RT-Thread,然后勾选 kernel,点击”OK” 就添加 RT-Thread 内核到工程了。
现在可以在 Project 看到 RT-Thread RTOS 已经添加进来了,展开 RTOS,可以看到添加到工程的文件:
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
RT-Thread 会用到了异常处理函数 HardFault_Handler()
和悬挂处理函数 PendSV_Handler()
,以及 Systick 中断服务函数 SysTick_Handler()
,所以用户代码需要保证这几个函数没有被使用,若编译提示函数重复定义,请删除自己定义的函数。
RT-Thread Nano 在 board.c 中默认完成了 systick 的配置,用户可以修改宏 RT_TICK_PER_SECOND 的值配置每秒 systick 数。
RT-Thread Nano 默认是使用数组作为 heap。
替换例程中的 delay
函数:
1). 包含 RT-Thread 的相关头文件
。
2). 删除之前在裸机工程中做的系统配置(如hal初始化、时钟初始化等),这是因为RT-Thread在系统启动时已经配置完成,否则会重复配置。
3). 将 delay()
函数替换成 rt_thread_mdelay()
,如 rt_thread_mdelay(500)
将延时 500ms。
下面是完成修改的代码:
编译程序之后下载到芯片就可以看到基于 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()
。
用户可以根据自己的需要通过修改 rtconfig.h 文件里面的宏定义配置相应功能。
RT-Thread Nano 默认未开启宏 RT_USING_HEAP,故只支持静态方式创建任务及信号量。若要通过动态方式创建对象则需要在 rtconfig.h 文件里开启 RT_USING_HEAP 宏定义。
MDK 的配置向导 configuration Wizard 可以很方便的对工程进行配置,Value 一栏可以选中对应功能及修改相关值,等同于直接修改配置文件 rtconfig.h。
Q1:如何升级 pack?
A:Pack 升级步骤基本如同软件包,展开 RealThread::RT-Thread 后,选择比较新的 Nano 版本,点击 Install 进行安装。如下图所示,点击红色框中的 Install 进行升级,即可将 3.1.1 版本升级到 3.1.2。
点击Manage Run-Environment:
勾选device drivers与shell,这将自动把FinSH组件的源码到工程,并自动添加 #define RTE_USING_DEVICE
与#define RTE_USING_FINSH
宏:
实现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);
\n
时,就在输出 \n
之前先输出一个 \r
。注意:RT-Thread 系统中已有的打印均以 “\n” 结尾,而并非 “\r\n”,所以在字符输出时,需要在输出“\n”之前输出“\r”完成回车与换行,否则系统打印出来的信息将只有换行。
打开rtconfig.h文件:将console的名称修改为刚注册到系统中的uart名称,这样FinSH就对接到了UART端口上。如上面注册名为“uart1”,则在rtconfig.h中修改 RT_CONSOLE_DEVICE_NAME 的定义为 uart1:
#define RT_CONSOLE_DEVICE_NAME "uart1"
下载到开发板进行验证,移植成功
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);
Q1:输出打印正常,却没有打印 msh >
,也不能输入。
A:这是由于FinSH 没打开,所以只有打印功能,需要在rtconfig.h中打开 #define RTE_USING_FINSH
宏定义。