本帖最后由 6552918 于 2023-8-1 11:27 编辑
#申请原创# #技术资源# @21小跑堂
rt-thread OS是优秀的国产RTOS,但完成版的rt-thread耗费资源比较大,而且要使用全部功能需要厂家支持有完整额BSP才能很好的体验,才能体现出完整版的优势。针对LKS32MC07X厂家是没有BSP的,想体验rt-thread可以使用nano版本,nano版本使用MDK进行移植还是比较容易的,我看有其他同学已经有相关的移植帖子了,但由于LKS的库不是很健全,有些地方需要重点注意一下,所以还是要介绍一下移植过程,但本帖在内核移植上不是重点,重点是结合UART+DMA实现shell,实现和MCU的命令交互。
这里还先是简单介绍一下内核的移植过程,仅供参考。
Nano Pack 可以通过在 Keil MDK IDE 内进行安装,也可以手动安装。下面开始介绍两种安装方式。
方法一:在内安装
打开 MDK 软件,点击工具栏的 Pack Installer 图标:
点击右侧的 Pack,展开 Generic,可以找到 RealThread::RT-Thread,点击 Action 栏对应的 Install ,就可以在线安装 Nano Pack 了。另外,如果需要安装其他版本,则需要展开 RealThread::RT-Thread,进行选择,箭头所指代表已经安装的版本。
方法二:手动安装
我们也可以从官网下载安装文件,RT-Thread Nano rId10(https://www.rt-thread.org/download/mdk/RealThread.RT-Thread.3.1.5.pack),下载结束后双击文件进行安装:
移植前需要准备一个能够正常运行的工程,我使用的是上篇帖子我做的UART+DMA的工程
添加到工程
打开已经准备好的可以运行的裸机程序,将 RT-Thread 添加到工程。如下图,点击 Manage Run-Time Environment。
点击OK,现在可以在 Project 看到 RT-Thread RTOS 已经添加进来了,展开 RTOS,可以看到添加到工程的文件:
board.c
rtconfig.h
这两个文件是系统的配置文件,修改相关的配置都在这两个文件内修改实现
中断与异常处理
RT-Thread 会接管异常处理函数 HardFault_Handler() 和悬挂处理函数 PendSV_Handler(),这两个函数已由 RT-Thread 实现,所以需要删除工程里中断服务例程文件中的这两个函数,避免在编译时产生重复定义。如果此时对工程进行编译,没有出现函数重复定义的错误,则不用做修改。LKS32MC07X的库文件中并没有这两个函数,因此无需处理。
系统时钟配置
需要在 board.c 中实现 系统时钟配置(为 MCU、外设提供工作时钟)与 os tick 的配置(为操作系统提供心跳 / 节拍)。
在board.c中添加芯片头文件
添加OS Tick Configuration
但在使用内核函数SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);发现两个问题,一个是SystemCoreClock咩有定义,和SysTick_Config函数没有定义
SystemCoreClock没定义需要在lks32mc07x_sys.c和lks32mc07x_sys.h内添加相应的定义即可
SysTick_Config函数没有定义改如何解决呢?首先我们发现SysTick_Config这个函数依赖__Vendor_SysTickConfig这个宏,这个宏在core_cm0.h内有定义,但又依赖__CHECK_DEVICE_DEFINES这个宏,这个宏是没有定义的。
于是我们在编译器设置内定义__CHECK_DEVICE_DEFINES这个宏
定义完成,并将警告忽略(看着别扭的可以再想办法解决掉,因为core_cm0.h文件是只读的,我不想改变只读特性),这样SysTick_Config函数就可以正常使用了。
在board.c内添加系统滴答定时器中断函数,实现系统时钟
关掉interrupt.c文件内的void SysTick_Handler(void)函数定义
内存堆初始化
系统内存堆的初始化在 board.c 中的 rt_hw_board_init() 函数中完成,内存堆功能是否使用取决于宏 RT_USING_HEAP 是否开启,RT-Thread Nano 默认开启内存堆功能,需要关闭此宏,这样可以保持一个较小的体积。
至此,和内核相关的设置就配置完成,接下来我们要处理一些初始化函数,因为在rt-threadnano中的main函数是个线程,原来一些底层初始化再放在这个函数内就不太合适了,需要将初始化函数使用rt-thread的自动初始化功能放到板级初始化过程中。
通过进行串口收发测试,发送正常,间隔在500ms左右,说明程序已经运行起来了
接下来是在 RT-Thread Nano 上添加控制台与 FinSH
本篇文档分为两部分:
第一部分是添加 UART 控制台(实现打印):用来向控制台对接的终端输出打印信息;该部分只需要实现两个函数,串口初始化和系统输出函数,即可完成 UART 控制台打印功能。
第二部分是移植 FinSH 组件(实现命令输入),用以在控制台输入命令调试系统;该部分的实现基于第一部分,只需要添加 FinSH 组件源码并再对接一个系统输入函数即可实现。
下面将对这两部分进行说明。
在 Nano 上添加 UART 控制台(实现打印)
在 RT-Thread Nano 上添加 UART 控制台打印功能后,就可以在代码中使用 RT-Thread 提供的打印函数 rt_kprintf() 进行信息打印,从而获取自定义的打印信息,方便定位代码 bug 或者获取系统当前运行状态等。实现控制台打印(需要确认 rtconfig.h 中已使能 RT_USING_CONSOLE 宏定义),需要完成基本的硬件初始化,以及对接一个系统输出字符的函数,本小节将详细说明。
实现串口初始化位置(注:此部分为 3.1.5 版本中 #error TODO 2 的部分:#error "TODO 2: Enable the hardware uart and config baudrate.")
使用串口对接控制台的打印,首先需要初始化串口,如引脚、波特率等。 初始化的串口函数有以下两种调用方式,二选一:
方法一:默认使用宏 INIT_BOARD_EXPORT() 进行自动初始化,不需要显式调用,如下所示。
方法二:可以使用显式调用:相关初始化函数,需要在 board.c 中的 rt_hw_board_init() 函数中调用。
实现 rt_hw_console_output(注:此部分为 3.1.5 版本中 #error TODO 3 的部分:#error "TODO 3: Output the string 'str' through the uart.")
实现 finsh 组件输出一个字符,即在该函数中实现 uart 输出字符:
/* 实现 2:输出一个字符,系统函数,函数名不可更改 */
void rt_hw_console_output(const char *str);
添加完输出代码后,编译正常,但程序运缺出现问题
经过各种折腾和测试,都要搞放弃了,最后测试发现和初始化函数内的全局IRQ开关有关
将相关操作关闭就正常了
接下来在 Nano 上添加 FinSH 组件(实现命令输入)
RT-Thread FinSH 是 RT-Thread 的命令行组件(shell),提供一套供用户在命令行调用的操作接口,主要用于调试或查看系统信息。
在 RT-Thread Nano 上添加 FinSH 组件,实现 FinSH 功能的步骤主要如下:
添加 FinSH 源码到工程。
实现函数对接。
然后在 rtconfig.h 中打开 finsh 相关选项,如下图:
实现 rt_hw_console_getchar(注:此部分为 3.1.5 版本中 #error TODO 4 的部分:#error "TODO 4: Read a char from the uart and assign it to 'ch'.")
要实现 FinSH 组件功能:既可以打印也能输入命令进行调试,控制台已经实现了打印功能,现在还需要在 finsh_port.c 中对接控制台输入函数,实现字符输入:
/* 实现 3:finsh 获取一个字符,系统函数,函数名不可更改 */
char rt_hw_console_getchar(void);
rt_hw_console_getchar():控制台获取一个字符,即在该函数中实现 uart 获取字符,可以使用查询方式获取(注意不要死等,在未获取到字符时,需要让出 CPU),
这里我使用的是一种非常高效的方式-》信号量,获取字符的函数不会阻塞式等待,不会影响其他程序的响应
先关操作如下:
至此,移植rt-threadnano和使用UART+DMA实现shell的过程就完成了,其中有很多细节需要注意。
Demo30_rt-thread(UART+DMA).zip
(1.76 MB)
|