本帖最后由 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。
|