[活动专区]

【AT-START-F405测评】移植ThreadX RTOS

[复制链接]
550|4
手机看帖
扫描二维码
随时随地手机跟帖
xhackerustc|  楼主 | 2024-5-12 13:16 | 显示全部楼层 |阅读模式
本帖最后由 xhackerustc 于 2024-5-12 18:03 编辑

#申请原创#@21小跑堂
ThreadX RTOS可是大名鼎鼎,微软已经把它给转到Eclipse开源组织下去了,所以现在叫Eclipse ThreadX RTOS。关于ThreadX牛X之处,网上搜索一大堆,这里不赘述了。个人感觉它比FreeRTOS好,ThreadX的代码风格本人很喜欢。今天就在AT32F405上移植下。


移植方**或者原则
不改变ThreadX的代码结构,不改变ThreadX自身的代码,除sample外不拷贝ThreadX的代码,不向ThreadX代码添加AT32F405的BSP,也不向AT32F405 BSP添加ThreadX中间件,要利用好AT32F405 BSP的模板。好处显而易见,代码尽可能复用而不重复,ThreadX直接利用clone下来的git仓库,能随时和上游同步而不用再次拷贝,且ThreadX和AT32F405 BSP独立。

要做到这一点,就要充分利用ThreadX本身的代码构建工具,当前ThreadX本身是用CMake管理构建的,这也是笔者第一篇测评贴为啥选择CMake做工程模板的原因。


移植步骤
clone ThreadX RTOS仓库
git clone https://github.com/eclipse-threadx/threadx.git


以led_toggle工程为模板创建threadx工程目录
cp -a project/at_start_f405/examples/gpio/led_toggle/  project/at_start_f405/examples/threadx


ThreadX例子程序
拷贝ports/linux/gnu/example_build/sample_threadx.c到threadx工程目录project/at_start_f405/examples/threadx/src,并把main()函数改名为tx_main(),后续会在bsp的main()函数中调用tx_main()。为增加点标识度,把thread_0_entry()中的打印改成:
printf("**** ThreadX AT32F405 Demonstration **** (c) 1996-2020 Microsoft Corporation\n\n");


中断向量的处理
最新的threadx对于cortex-m4 ports已经不使用SVC中断,但有两个中断用到了:PendSV和SysTick,而且PendSV的中断处理handler微软已经做好了,所以需要修改中断向量,在AT32F405 BSP中中断向量是在startup_at32f402_405.s中定义的,为区分其余裸机工程笔者是copy一份startup_at32f402_f405.s:
cp libraries/cmsis/cm4/device_support/startup/gcc/startup_at32f402_405.s project/at_start_f405/examples/threadx/src/
并修改:
@@ -126,7 +126,7 @@
   .word  SVC_Handler
   .word  DebugMon_Handler
   .word  0
-  .word  PendSV_Handler
+  .word  __tx_PendSVHandler
   .word  SysTick_Handler

   /* External Interrupts */
@@ -263,9 +263,6 @@
    .weak      DebugMon_Handler
    .thumb_set DebugMon_Handler,Default_Handler

-   .weak      PendSV_Handler
-   .thumb_set PendSV_Handler,Default_Handler
-
    .weak      SysTick_Handler
    .thumb_set SysTick_Handler,Default_Handler


project/at_start_f405/examples/threadx/src/at32f402_405_int.c中把SysTick_handler改成如下:
void SysTick_Handler(void)
{
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    _tx_execution_isr_enter();
#endif
    _tx_timer_interrupt();
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    _tx_execution_isr_exit();
#endif
}



_tx_initialize_low_level()函数的实现
仔细阅读ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S,发现我们还需要实现_tx_initialize_low_level()函数,但没必要用汇编写,完全可以用c语言,只需几行c代码就够了,笔者把它放在了main.c中,其具体实现是(基本是汇编的一对一重新实现):
void _tx_initialize_low_level(void)
{
        __disable_irq();

        /* TODO Enable the cycle count register */

        /* not used */
        //_tx_initialize_unused_memory = __RAM_segment_used_end__;
        //_tx_thread_system_stack_ptr = _estack;

        SysTick_Config(MS_TICK);

        NVIC_SetPriority(SVCall_IRQn, 0xff);
        NVIC_SetPriority(PendSV_IRQn, 0xff);
        NVIC_SetPriority(SysTick_IRQn, 4);
}
注1:ThreadX官方example里初始化了DWT,但DWT现在没用上,所以笔者放TODO里
注2:ThreadX官方example里初始化_tx_initialize_unused_memory和_tx_thread_system_stack_ptr两个全局变量,笔者也用c实现了但注释掉了,因阅读ThreadX源码发现这两个变量目前没用上。
注3:如上所述最新ThreadX已经不用SVC中断,但为和汇编保持一致,还是设置了SVC的中断优先级。由此可见微软对ThreadX这部分代码没清理干净。

完整的main.c如下:
#define MS_TICK                          (system_core_clock / 100U)

extern void tx_main(void);
int main(void)
{
        system_clock_config();

        uart_print_init(115200);

        tx_main();
}

void _tx_initialize_low_level(void)
{
        __disable_irq();

        /* TODO Enable the cycle count register */

        /* not used */
        //_tx_initialize_unused_memory = __RAM_segment_used_end__;
        //_tx_thread_system_stack_ptr = _estack;

        SysTick_Config(MS_TICK);

        NVIC_SetPriority(SVCall_IRQn, 0xff);
        NVIC_SetPriority(PendSV_IRQn, 0xff);
        NVIC_SetPriority(SysTick_IRQn, 4);
}
注:貌似ThreadX默认TICK都是一秒100次,这里修改了MS_TICK和AT32F405 BSP不一样

修改CMakeLists.txt
除改动工程应用源码目录使其指向examples/threadx目录外,最主要的改动是把threadx源码加入编译:
add_subdirectory(${THREADX_SRC_DIR}/threadx threadx)
由此可以看到,非常简单的一行就能完美利用ThreadX自身的CMakeLists.txt,ThreadX自身的构造我们都不用管,微软已经做好了。

并让最后链接azrtos::threadx库
target_link_libraries(${PROJECT_NAME}.elf nosys azrtos::threadx)
注:azrtos::threadx是由ThreadX的CMakeLists.txt定义,应用只需链接之即可。


完整CMakeLists.txt改动如下:
@@ -30,18 +30,23 @@ include_directories(libraries/cmsis/cm4/core_support
                    libraries/cmsis/dsp/include/
                    libraries/drivers/inc/
                    project/at32f402_405_board/
-                   project/at_start_f405/examples/gpio/led_toggle/inc)
+                   project/at_start_f405/examples/threadx/inc)

add_definitions(-DAT32F405RCT7_7 -DAT_START_F405_V1)

file(GLOB_RECURSE SOURCES
                "project/at32f402_405_board/at32f402_405_board.c"
-               "project/at_start_f405/examples/gpio/led_toggle/src/*.c"
+               "project/at_start_f405/examples/threadx/src/*.c"
+               "project/at_start_f405/examples/threadx/src/startup_at32f402_405.s"
                "libraries/drivers/src/*.c"
                "libraries/cmsis/cm4/device_support/system_at32f402_405.c"
-               "libraries/cmsis/cm4/device_support/startup/gcc/startup_at32f402_405.s"
                 )

+set(THREADX_SRC_DIR ${CMAKE_SOURCE_DIR}/../../kernel/threadx/)
+set(THREADX_ARCH "cortex_m4")
+set(THREADX_TOOLCHAIN "gnu")
+add_subdirectory(${THREADX_SRC_DIR}/threadx threadx)
+
set(LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/libraries/cmsis/cm4/device_support/startup/gcc/linker/AT32F405xC_FLASH.ld)
add_link_options(
                 -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
@@ -52,7 +57,7 @@ add_link_options(-T ${LINKER_SCRIPT})

add_executable(${PROJECT_NAME}.elf ${SOURCES} ${LINKER_SCRIPT})

-# target_link_libraries(${PROJECT_NAME}.elf printfloat)
+target_link_libraries(${PROJECT_NAME}.elf nosys azrtos::threadx)

set(HEX_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.hex)
set(BIN_FILE ${PROJECT_BINARY_DIR}/${PROJECT_NAME}.bin)




编译&烧录
cmake -B build
cmake --build build
pyocd load -e sector -t at32f405rct7_7 build/at32f405.bin


运行视频
https://www.bilibili.com/video/BV1tZ421j7t2/

总结
本文充分利用ThreadX自身的cmake构造工程,杜绝源码重复拷贝,最小化源码改动,在AT32F405平台成功移植了ThreadX RTOS。

使用特权

评论回复
xhackerustc|  楼主 | 2024-5-12 13:25 | 显示全部楼层
ThreadX那个sample,是睡眠10个ticks就打印,太快了,笔者改成了100个ticks即1秒钟
        /* Sleep for 100 ticks.  */
        tx_thread_sleep(100);

使用特权

评论回复
gouguoccc| | 2024-5-12 13:28 | 显示全部楼层
和RTthread相比咋样呢?

使用特权

评论回复
xhackerustc|  楼主 | 2024-5-12 13:36 | 显示全部楼层
ThreadX cortex-m4的sample里汇编写的_tx_initialize_low_level()还设置了VTOR寄存器,但笔者示例工程里直接用AT32F405 BSP的code,在SystemInit()函数里已经做过了,所以c语言版本这个动作就忽略掉了

使用特权

评论回复
xhackerustc|  楼主 | 2024-5-12 13:49 | 显示全部楼层
gouguoccc 发表于 2024-5-12 13:28
和RTthread相比咋样呢?

两者设计实现路子不太一样,RT-Thread除与芯片硬件无关的比如文件系统、调度、网络、内存管理、usb协议栈等等外,想尽可能实现芯片的bsp驱动,所以RT-Thread主线里有bsp目录,占了源码的大头。而ThreadX只实现cpu、中断控制器和timer相关ports,不做芯片的bsp驱动,固件默认是芯片厂商自己去做的。这一点上ThreadX和FreeRTOS有点像。

另外RT-Thread有三个版本,分别是标准版、nano版和smart版,ThreadX只一个版本

ThreadX把文件系统、USB协议栈、网络这些分别放独立源码仓库里,FileX、USBX、NETX,RT-Thread是放一起的

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

11

主题

41

帖子

0

粉丝