[活动专区] 【AT-START-F405测评】移植ThreadX RTOS

[复制链接]
 楼主| 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仓库
  1. git clone https://github.com/eclipse-threadx/threadx.git


以led_toggle工程为模板创建threadx工程目录
  1. 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()中的打印改成:
  1. 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:
  1. cp libraries/cmsis/cm4/device_support/startup/gcc/startup_at32f402_405.s project/at_start_f405/examples/threadx/src/
并修改:
  1. @@ -126,7 +126,7 @@
  2.    .word  SVC_Handler
  3.    .word  DebugMon_Handler
  4.    .word  0
  5. -  .word  PendSV_Handler
  6. +  .word  __tx_PendSVHandler
  7.    .word  SysTick_Handler

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

  12. -   .weak      PendSV_Handler
  13. -   .thumb_set PendSV_Handler,Default_Handler
  14. -
  15.     .weak      SysTick_Handler
  16.     .thumb_set SysTick_Handler,Default_Handler


project/at_start_f405/examples/threadx/src/at32f402_405_int.c中把SysTick_handler改成如下:
  1. void SysTick_Handler(void)
  2. {
  3. #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
  4.     _tx_execution_isr_enter();
  5. #endif
  6.     _tx_timer_interrupt();
  7. #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
  8.     _tx_execution_isr_exit();
  9. #endif
  10. }



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

  4.         /* TODO Enable the cycle count register */

  5.         /* not used */
  6.         //_tx_initialize_unused_memory = __RAM_segment_used_end__;
  7.         //_tx_thread_system_stack_ptr = _estack;

  8.         SysTick_Config(MS_TICK);

  9.         NVIC_SetPriority(SVCall_IRQn, 0xff);
  10.         NVIC_SetPriority(PendSV_IRQn, 0xff);
  11.         NVIC_SetPriority(SysTick_IRQn, 4);
  12. }
注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如下:
  1. #define MS_TICK                          (system_core_clock / 100U)

  2. extern void tx_main(void);
  3. int main(void)
  4. {
  5.         system_clock_config();

  6.         uart_print_init(115200);

  7.         tx_main();
  8. }

  9. void _tx_initialize_low_level(void)
  10. {
  11.         __disable_irq();

  12.         /* TODO Enable the cycle count register */

  13.         /* not used */
  14.         //_tx_initialize_unused_memory = __RAM_segment_used_end__;
  15.         //_tx_thread_system_stack_ptr = _estack;

  16.         SysTick_Config(MS_TICK);

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

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

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


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

  7. add_definitions(-DAT32F405RCT7_7 -DAT_START_F405_V1)

  8. file(GLOB_RECURSE SOURCES
  9.                 "project/at32f402_405_board/at32f402_405_board.c"
  10. -               "project/at_start_f405/examples/gpio/led_toggle/src/*.c"
  11. +               "project/at_start_f405/examples/threadx/src/*.c"
  12. +               "project/at_start_f405/examples/threadx/src/startup_at32f402_405.s"
  13.                 "libraries/drivers/src/*.c"
  14.                 "libraries/cmsis/cm4/device_support/system_at32f402_405.c"
  15. -               "libraries/cmsis/cm4/device_support/startup/gcc/startup_at32f402_405.s"
  16.                  )

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

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

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

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




编译&烧录
  1. cmake -B build
  2. cmake --build build
  3. 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秒钟
  1.         /* Sleep for 100 ticks.  */
  2.         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是放一起的
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

165

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部

42

主题

165

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部