RTXThread 基于rt-thread和RTX5的衍生版操作系统

Git链接

基于GD32F303ARM Cortex-M4的示例:
https://github.com/Yanye0xFF/RTXThread

概述

这并不是一个全新的实时操作系统,而是基于rt-thread设备驱动框架RTX5内核组合而来的衍生版系统。其中操作系统的应用层接口使用了CMSIS RTOS2 API,这是ARM公司为RTOS内核制定的一套通用接口协议,并且有很多实时系统内核的实现。
image

CMSIS-RTOS2兼容层 链接
RT-Thread https://github.com/RT-Thread-packages/CMSIS_RTOS2
FreeRTOS https://github.com/ARM-software/CMSIS-FreeRTOS
LiteOS https://support.huaweicloud.com/lib-LiteOS/zh-cn_topic_0314621635.html

RTX5的优势:

  1. 系统调度采用SVC软中断进入内核态(中断环境),因此内核函数使用主栈空间(MSP)和线程栈(PSP)空间分离。调度器相关逻辑不会关闭中断,因此可以利用Cortex-m的中断嵌套(NVIC)优先保证高实时的中断优先处理,非常适合做运动控制相关。
  2. 支持Cortex-M,Cortex-A系列核心,以及多核。由于内核函数全部使用了系统调用(SysCall),便于在此基础上拓展出用户应用和系统内核分离的微内核架构操作系统(cortex-m以固定地址的multibin实现,不带MPU的型号无法支持保护模式)。
  3. RTX5只提供实时内核的基本功能,代码量和复杂度较低,以它为基础进行二次开发比较灵活自由。
  4. 最重要的是RTX5的汽车级,工业级,医疗和铁路安全认证已经通过。
标准 说明
ISO 26262 (ASIL D) 汽车级最高安全认证
IEC 61508 (SIL 3) 工业级认证
IEC 62304 (Class C) 医疗认证
EN 50128 (SIL 4) 运输/铁路安全认证

文档链接:
https://arm-software.github.io/CMSIS_5/RTOS2/html/index.html
源码链接:
https://github.com/ARM-software/CMSIS_5/tree/develop/CMSIS/RTOS2

rt-thread的优势:

  1. 开源社区比较活跃,用的人很多,相比于rtx5,有非常多的第三方外设驱动以及内核组件。
  2. 面向对象c语言思想,linux代码风格,适合已掌握java/cpp这类高级语言的人学习和使用。
  3. 安全认证有IEC61508 SIL3,EN50128 SIL4,ISO 26262 ASIL-D 参考链接:https://www.rt-thread.org/newsDetail.html?id=3bf39c57321d895a
  4. rt-thread类似linux的POSIX接口的设备驱动框架给外设驱动兼容带来了巨大便利,我认为统一的外设驱动框架也是rt-thread有很多第三方组件的重要基础。

源码链接:
https://github.com/RT-Thread/rt-thread

RTXThread系统架构:
image

主要特性:

  1. 既有RTX5实时内核的高性能,又能享受到rt-thread的设备驱动框架以及丰富的软件生态。
  2. 任务调度,线程间通信(信号量,互斥锁,消息队列...)完全使用RTX5的实现,无任何改动。
  3. 全局内存池(Global Memory Pool)移除了RTX5的rtx_memory.c,使用rt-thread中的SMALL_MEM(mem.c 实际上是lwip项目中的动态内存实现),可以同时使用rt-thread和CMSIS RTOS2的内存分配和释放API。
  4. C库继承自rt-thread使用newlib,适配了printf,malloc,free等常见的桩函数(stub-function)。
  5. 完整适配了rt-thread设备驱动框架,支持spi,can,uart等。
  6. 支持finsh控制台,自动初始化(INIT_BOARD_EXPORT...),精简的cm_backtrace。
  7. 默认的调试输出组件使用Segger RTT,通过SWD接口输出。
  8. 使用rt-thread设备驱动框架规避了RTX中间件的版权问题(RTX的中间件RL-FlashFS, RL-USB, RL-TCPnet等需要购买正版的KEIL-MDK)。

由于携带了以上的一些外围组件,在软件跨平台和开发调试上带来很大方便,但也导致了RTXThread不是一个轻量系统,其编译后占用的ROM和RAM空间也是比较大的。
提供示例的编译选项:

  1. O2优化等级
  2. 关闭newlib --specs=nano.specs
  3. 使用浮点数printf支持-u _printf_float
  4. Segger RTT输出缓存占用4KB RAM
Invoking: GNU Arm Cross Print Size
arm-none-eabi-size --format=berkeley "RTXThread.elf"
   text	   data	    bss	    dec	    hex	filename
  70792	   1636	   7856	  80284	  1399c	RTXThread.elf
Finished building: RTXThread.siz

若启用newlib-nano属性,关闭浮点数printf支持,.text段(ROM)能减少13KB。
若关闭Segger RTT功能,.bss段(RAM)能减小4KB。

体验预览

  1. 系统启动
00> RTXThread Operating System
00> Powered by RT-Thread and RTX5
00> Version info:
00> RT-Thread 4.1.1
00> CMSIS RTOS2 API 20010003
00> RTX Kernel 50050004
00> app version:12, fcpu:120000000Hz
00> msh >
  1. 目前支持的shell命令
  < help
00> help
00> RTXThread shell commands:
00> reboot           - reboot system
00> date             - get date and time or set (local timezone) [year month day hour min sec]
00> dac              - dac function
00> pwm_get          - pwm_get <pwm_dev> <channel>
00> pwm_set          - pwm_set <pwm_dev> <channel> <period> <pulse>
00> pwm_disable      - pwm_disable <pwm_dev> <channel>
00> pwm_enable       - pwm_enable <pwm_dev> <channel>
00> list             - list objects
00> list_device      - list device in system
00> show_mstack      - show main stack usage
00> list_isr         - list interrupt handlers
00> list_timer       - list timer in system
00> list_thread      - list thread
00> version          - show RTXThread version information
00> clear            - clear the terminal screen
00> free             - Show the memory usage in the system.
00> ps               - List threads in the system.
00> help             - RTXThread shell help.
00> poke             - write memory
00> peek             - read memory
00> gpio             - test gpio
00> test_div0        - divided by zero
00> test_busfault    - exception bus fault
  1. 列出所有的线程
  < ps
00> ps
00> thread       pri  status      sp     stack size stack base max used left tick  error
00> ------------ ---  ------- ---------- ---------- ----------  ------  ---------- ---
00> tshell        16  running 0x20003768 0x00000400 0x200033c0    39%            4 OK
00> tidle0         1  ready   0x200027e8 0x00000200 0x20002628    00%            0 OK
00> main          24  suspend 0x200031f8 0x00000800 0x20002acc    31%         2235 OK
00> timer         40  suspend 0x200029c0 0x00000200 0x20002828    08%           -1 OK
00> sys_evt       23  suspend 0x20004180 0x00000400 0x20003dfc    05%           -1 OK
00> msh >
  1. 列出当前注册到驱动框架的外设
  < list_device
00> list_device
00> device               type         ref count
00> ------------ -------------------- ----------
00> spi-flash    SPI Device           0       
00> g-sensor     SPI Device           0       
00> spi0         SPI Bus              0       
00> rtc          RTC                  1       
00> gpio         Pin Device           0       
00> dac0         DAC Device           0       
00> can0         CAN Device           0       
00> adc0         ADC Device           0 
  1. 异常测试:尝试写入未定义地址
int test_busfault(int argc, char **argv) {
    float a = 100.0f;
    float b = 2.0f;
    float c = a / b;
    printf("a:%f\n", c);

    int *ptr = (int *)0x9f0000000;
    *ptr = 100;
    return 0;
}
MSH_CMD_EXPORT(test_busfault, exception bus fault);

  < test_busfault
00> test_busfault
00> a:50.000000
00> hard fault on thread: "tshell"
00> thread       pri  status      sp     stack size stack base max used left tick  error
00> ------------ ---  ------- ---------- ---------- ----------  ------  ---------- ---
00> tshell        16  running 0x20003660 0x00000400 0x200032b8    83%            4 OK
00> tidle0         1  ready   0x200026e0 0x00000200 0x20002520    00%            0 OK
00> main          24  suspend 0x200030f0 0x00000800 0x200029c4    31%         3615 OK
00> timer         40  suspend 0x200028b8 0x00000200 0x20002720    08%           -1 OK
00> sys_evt       23  suspend 0x20004078 0x00000400 0x20003cf4    05%           -1 OK
00> 
00> =================== Registers information ====================
00>   R0 : 00000000  R1 : 22400000  R2 : 00000064  R3 : 0000000e
00>   R4 : 0000000d  R5 : 200031fe  R6 : 08007de5  R7 : 20003658
00>   R8 : 20003658  R9 : 00000000  R10: 0801222c  R11: 00000000
00>   R12: 0000000c  LR : 08007df1  PC : 08005f40  PSR: 61010000
00> ==============================================================
00> 
00> FPU is active!
00> bus fault:
00> SCB_CFSR_BFSR:0x04, Bus fault is caused by imprecise data access violation
00> System will reboot after 1 second.
  1. 异常测试:制造除0异常(由于我未使能UsageFault,异常将由中断号靠前Hard Fault接管)
int test_div0(int argc, char **argv) {
    int a = 100;
    int b = 0;
    int c = a / b;
    printf("c:%d\n", c);

    return 0;
}
MSH_CMD_EXPORT(test_div0, divided by zero);

  < test_div0
00> test_div0
00> hard fault on thread: "tshell"
00> thread       pri  status      sp     stack size stack base max used left tick  error
00> ------------ ---  ------- ---------- ---------- ----------  ------  ---------- ---
00> tshell        16  running 0x20003660 0x00000400 0x200032b8    10%            4 OK
00> tidle0         1  ready   0x200026e0 0x00000200 0x20002520    00%            0 OK
00> main          24  suspend 0x200030f0 0x00000800 0x200029c4    31%         4485 OK
00> timer         40  suspend 0x200028b8 0x00000200 0x20002720    08%           -1 OK
00> sys_evt       23  suspend 0x20004078 0x00000400 0x20003cf4    05%           -1 OK
00> 
00> =================== Registers information ====================
00>   R0 : 00000001  R1 : 20003658  R2 : 00000000  R3 : 0000000a
00>   R4 : 00000009  R5 : 200031fe  R6 : 08007e05  R7 : 20003658
00>   R8 : 20003658  R9 : 00000000  R10: 0801222c  R11: 00000000
00>   R12: 00000000  LR : 08005f41  PC : 08007e04  PSR: 61000000
00> ==============================================================
00> 
00> usage fault:
00> SCB_CFSR_UFSR:0x01, Usage fault is caused by attempts to execute an undefined instruction
00> System will reboot after 1 second.

系统启动流程

RTXThread的启动流程和rt-thread基本相同,但是最终运行的是RTX5内核。

裸机阶段

+ startup_gd32f30x_hd.S

RTXThread/src/libcpu/startup_gd32f30x_hd.S是整个系统的汇编入口,MCU上电后进入Reset_Handler子程序,在经过copy data sectionzero bss section之后,进入SystemInit函数,在这里进行平台相关时钟配置。由于SystemInit各个单片机的实现都不同,需要根据自己选用的芯片型号,从官方库中提取。
image

image

+ components.c

在时钟配置完成后,跳转到C语言入口entry函数,该函数位于rtthread/components.c,entry函数直接跳转到了rtthread_startup
image

  1. 首先执行板级初始化,SystemCoreClockUpdate函数主要是将SystemInit配置的时钟再次读取出来更新到SystemCoreClock全局变量,方便用户验证是否配置成功以及后期使用。
  2. 设置中断控制器,在Cortex-M处理器中,RTX5使用SysTick_IRQn,PendSV,SVC三个中断,需要保证SysTick_IRQnPendSV都位于最低优先级,SVC要求比最低优先级高一个等级(SVC也可以是最高优先级),即SVC > PendSV = SysTick_IRQn,如果启用了抢占优先级(中断嵌套)需要保证SysTick/PendSV的抢占优先级低于SVC。这里我使用了非抢占中断模式,4bit全部用于表示响应优先级。
    在此配置情况下:
    os_systick.c SysTick_IRQn默认为最低优先级15,
    image
    rtx_core_cm.h PendSV默认为15,SVC默认为0。
    image

如果NVIC控制器配置成4bit全表示抢占优先级,则这三个内核中断的优先级如下:

SysTick_IRQn PendSV SVC
15 15 14

image
3. 执行INIT_BOARD_EXPORT导出的初始化函数,这属于rt-thread组件自动初始化的一部分。(这里就不详细展开了)需要注意的是INIT_BOARD使用裸机栈,OS环境此时还未建立。
4. 配置堆内存,rt_system_heap_init将bss段后面的空余RAM作为系统堆区,注意:堆内存分配器使用的互斥锁对象的控制块内存(control block)需要静态分配,因为此时内存分配器还未初始化。
5. 调用osKernelInitialize做一些内核初始化,主要是设置isr_queue大小,时间片调度间隔,通用内存初始化(osRtxMemoryInit,RTXThread中这一步什么都不做,因此不要使用可变大小对象池),固定对象大小的内存池的初始化(osRtxMemoryPoolInit,RTXThread中可正常使用),在RTXThread系统中,osRtxMemory通用堆内存被上一步的rt_system_heap接管。

RTX_Config.h配置文件中,以下宏需要定义为0,定义为其他值不起作用。

#define OS_THREAD_OBJ_MEM           0
#define OS_THREAD_USER_STACK_SIZE   0
#define OS_MEMPOOL_OBJ_MEM          0
#define OS_MEMPOOL_DATA_SIZE        0
#define OS_MSGQUEUE_OBJ_MEM         0
#define OS_MSGQUEUE_DATA_SIZE       0
#define OS_DYNAMIC_MEM_SIZE         0

image

  1. rt_application_init建立main线程,最后通过osKernelStart开启调度器。
    image

main线程阶段

用户程序的入口是user_main,位于application/main.c文件中,application包中提供了一个默认的消息循环实现(类似Android中的Message Handler),用于线程间异步分发事件使用,通过system_event_loop_create进行初始化。
image

posted @ 2023-01-08 20:30  Yanye  阅读(1176)  评论(0编辑  收藏  举报