ThreadX RTOS快速入门
ThreadX 是由 Express Logic 开发的一款硬实时操作系统,是唯一同时通过汽车电子、工业控制和医疗设备等高安全认证的RTOS,起初是收费RTOS,在2019年微软收购后,改为MIT开源协议,商用免费,是目前最强开源RTOS,具有完整的全家桶,如GUIX、NetX、NetX Duo、FileX、USBX。
2019年微软收购Express Logic, 2023年,微软把ThreadX捐给Eclipse基金会,ThreadX经过3次改名:
- ThreadX (2019年之前)
- Azure RTOS (2019~2023年)
- Eclipse ThreadX (2023年之后)
在叫Azure RTOS时,有大量示例代码,开箱即用,改为Eclipse ThreadX后,采用简洁模式,没什么示例代码了,想用就要自己移植,相比由Linux基金会托管的Zephyr RTOS就有比较完整的支持平台和示例代码,微软选择Eclipse基金会而不选择Linux基金会运营ThreadX,真的感到惋惜。
我们使用树莓派 pico rp2040进行测试,由于threadx没有适配这款设备,我们进行简单的移植,开发平台是ubuntu 22.04, gcc交叉编译器。
1. 安装开发环境
$ sudo apt install build-essential flex bc gawk texinfo file liblz4-dev ssh git libssl-dev libncurses-dev cmake $ sudo apt install gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib pkg-config libusb-1.0-0-dev
2. 设置Pico开发环境
下载树莓派官方pico相关代码:
$ mkdir -p ~/pico $ cd ~/pico $ git clone --recurse-submodules https://github.com/raspberrypi/pico-sdk.git $ git clone https://github.com/raspberrypi/picotool.git $ git clone https://github.com/raspberrypi/pico-examples.git
设置环境变量:
$ export PICO_SDK_PATH=$HOME/pico/pico-sdk
编译安装picotool:
$ cd ~/pico/picotool/ $ mkdir build $ cd build/ $ cmake .. $ make -j4 $ sudo make install
3. 移植ThreadX
创建文件夹和下载ThreadX源码:
$ mkdir -p ~/pico/pico-threadx $ cd ~/pico/pico-threadx $ mkdir -p samples/blink $ git clone https://github.com/eclipse-threadx/threadx.git
目录如下:
$ ls ~/pico/ pico-examples pico-sdk pico-threadx picotool
代码git id:
$ cd ~/pico/pico-threadx/threadx $ git branch -vv * master 7ad78c40 [origin/master] Merge pull request #387 from netomi/patch-1
设置pico-threadx相关文件:
$ cp ~/pico/pico-examples/CMakeLists.txt ~/pico/pico-threadx $ cp ~/pico/pico-examples/pico_sdk_import.cmake ~/pico/pico-threadx $ cp ~/pico/pico-examples/i2c/CMakeLists.txt ~/pico/pico-threadx/samples $ cp ~/pico/pico-examples/blink/CMakeLists.txt ~/pico/pico-threadx/samples/blink $ cp ~/pico/pico-threadx/threadx/ports_module/cortex_m0+/gnu/example_build/sample_threadx/tx_initialize_low_level.S ~/pico/pico-threadx/samples/blink $ cp ~/pico/pico-threadx/threadx/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.c ~/pico/pico-threadx/samples/blink
最终目录如下:
$ cd ~/pico/pico-threadx $ tree -L 3 . . ├── CMakeLists.txt ├── pico_sdk_import.cmake ├── samples │ ├── blink │ │ ├── CMakeLists.txt │ │ ├── sample_threadx.c │ │ └── tx_initialize_low_level.S │ └── CMakeLists.txt └── threadx └── ...
上面3个CMakeLists.txt都需要进行一些修改。
由于官方不同版本的代码可能有所不同,这里不使用diff,直接贴出修改后的最终文件:
1. ~/pico/pico-threadx/CMakeLists.txt
cmake_minimum_required(VERSION 3.12) # Pull in SDK (must be before project) include(pico_sdk_import.cmake) project(pico_examples C CXX ASM) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 17) set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR}) # Initialize the SDK pico_sdk_init() function(add_subdirectory_exclude_platforms NAME) if (ARGN) if (PICO_PLATFORM IN_LIST ARGN) message("Skipping ${NAME} example which is unsupported on this platform") return() endif() foreach(PATTERN IN LISTS ARGN) string(REGEX MATCH "${PATTERN}" MATCHED "${PICO_PLATFORM}") if (MATCHED) message("Skipping ${NAME} example which is unsupported on this platform") return() endif() endforeach() endif() add_subdirectory(${NAME}) endfunction() set(THREADX_ARCH "cortex_m0") set(THREADX_TOOLCHAIN "gnu") add_subdirectory(threadx) add_subdirectory(samples)
2. ~/pico/pico-threadx/samples/CMakeLists.txt
add_subdirectory_exclude_platforms(blink)
3. ~/pico/pico-threadx/samples/blink/CMakeLists.txt
project(blink) add_executable(${PROJECT_NAME} tx_initialize_low_level.S sample_threadx.c ) # pull in common dependencies target_link_libraries(${PROJECT_NAME} pico_stdlib cmsis_core threadx) pico_enable_stdio_usb(${PROJECT_NAME} 1) # create map/bin/hex file etc. pico_add_extra_outputs(${PROJECT_NAME})
4. ~/pico/pico-threadx/samples/blink/tx_initialize_low_level.S
@ .global _tx_thread_system_stack_ptr .global _tx_initialize_unused_memory @ .global __RAM_segment_used_end__ .global _tx_thread_context_save .global _tx_thread_context_restore .global _tx_timer_interrupt .global ram_vector_table @ @ SYSTEM_CLOCK = 125000000 SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) .text .align 4 .syntax unified @VOID _tx_initialize_low_level(VOID) @{ .global _tx_initialize_low_level .thumb_func _tx_initialize_low_level: @ @ /* Disable interrupts during ThreadX initialization. */ @ CPSID i @ @ /* Set base of available memory to end of non-initialised RAM area. */ @ @ LDR r0, =_tx_initialize_unused_memory @ Build address of unused memory pointer @ LDR r1, =__RAM_segment_used_end__ @ Build first free address @ ADDS r1, r1, #4 @ @ STR r1, [r0] @ Setup first unused memory pointer @ @ /* Enable the cycle count register. */ @ /* Not all M0 have DWT, uncomment if you have a DWT and want to use it. */ @ LDR r0, =0xE0001000 @ Build address of DWT register @ LDR r1, [r0] @ Pickup the current value @ MOVS r2, #1 @ ORRS r1, r1, r2 @ Set the CYCCNTENA bit @ STR r1, [r0] @ Enable the cycle count register @ @ /* Setup Vector Table Offset Register. */ @ LDR r0, =0xE000E000 @ Build address of NVIC registers LDR r2, =0xD08 @ Offset to vector base register ADD r0, r0, r2 @ Build vector base register LDR r1, =ram_vector_table @ Pickup address of vector table STR r1, [r0] @ Set vector table address @ @ /* Set system stack pointer from vector value. */ @ LDR r0, =_tx_thread_system_stack_ptr @ Build address of system stack pointer LDR r1, =ram_vector_table @ Pickup address of vector table LDR r1, [r1] @ Pickup reset stack pointer STR r1, [r0] @ Save system stack pointer @ @ /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ @ LDR r0, =0xE000E000 @ Build address of NVIC registers LDR r1, =SYSTICK_CYCLES STR r1, [r0, #0x14] @ Setup SysTick Reload Value LDR r1, =0x7 @ Build SysTick Control Enable Value STR r1, [r0, #0x10] @ Setup SysTick Control @ @ /* Configure handler priorities. */ @ LDR r1, =0x00000000 @ Rsrv, UsgF, BusF, MemM LDR r0, =0xE000E000 @ Build address of NVIC registers LDR r2, =0xD18 @ ADD r0, r0, r2 @ STR r1, [r0] @ Setup System Handlers 4-7 Priority Registers LDR r1, =0xFF000000 @ SVCl, Rsrv, Rsrv, Rsrv LDR r0, =0xE000E000 @ Build address of NVIC registers LDR r2, =0xD1C @ ADD r0, r0, r2 @ STR r1, [r0] @ Setup System Handlers 8-11 Priority Registers @ Note: SVC must be lowest priority, which is 0xFF LDR r1, =0x40FF0000 @ SysT, PnSV, Rsrv, DbgM LDR r0, =0xE000E000 @ Build address of NVIC registers LDR r2, =0xD20 @ ADD r0, r0, r2 @ STR r1, [r0] @ Setup System Handlers 12-15 Priority Registers @ Note: PnSV must be lowest priority, which is 0xFF @ @ /* Return to caller. */ @ BX lr @} @ @ /* System Tick timer interrupt handler */ .global __tx_SysTickHandler .global SysTick_Handler .thumb_func __tx_SysTickHandler: .thumb_func SysTick_Handler: @ VOID SysTick_Handler (VOID) @ { @ PUSH {r0, lr} #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY BL _tx_execution_isr_enter @ Call the ISR enter function #endif BL _tx_timer_interrupt #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY BL _tx_execution_isr_exit @ Call the ISR exit function #endif POP {r0, r1} MOV lr, r1 BX lr @ }
5. ~/pico/pico-threadx/samples/blink/sample_threadx.c (文件太大, 给出diff)
--- threadx/ports_module/cortex_m0+/gnu/example_build/sample_threadx/sample_threadx.c +++ samples/blink/sample_threadx.c @@ -2,7 +2,11 @@ threads of different priorities, using a message queue, semaphore, mutex, event flags group, byte pool, and block pool. */ +#include <stdio.h> #include "tx_api.h" +#include "pico/stdlib.h" +#include "hardware/gpio.h" + #define DEMO_STACK_SIZE 1024 #define DEMO_BYTE_POOL_SIZE 9120 @@ -57,7 +61,7 @@ void thread_6_and_7_entry(ULONG threa int main() { - + stdio_init_all(); /* Enter the ThreadX kernel. */ tx_kernel_enter(); } @@ -179,7 +183,9 @@ void thread_0_entry(ULONG thread_inpu { UINT status; - + gpio_init(PICO_DEFAULT_LED_PIN); + gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT); + gpio_put(PICO_DEFAULT_LED_PIN, 0); /* This thread simply sits in while-forever-sleep loop. */ while(1) @@ -188,8 +194,15 @@ UINT status; /* Increment the thread counter. */ thread_0_counter++; + if (thread_0_counter % 2 == 0) { + gpio_put(PICO_DEFAULT_LED_PIN, 1); + } else { + gpio_put(PICO_DEFAULT_LED_PIN, 0); + } + + printf("hello world: %d.\n", thread_0_counter++); /* Sleep for 10 ticks. */ - tx_thread_sleep(10); + tx_thread_sleep(100); /* Set event flag 0 to wakeup thread 5. */ status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR);
直接复制替换掉原有内容,或修改diff代码即可。
后续,如果想要添加新的示例代码,则可以在blink同一级:
- ’pico-threadx/samples/CMakeLists.txt‘
- 'pico-threadx/samples/blink/*'
添加相应的文件和配置即可,另外,比如netx等移植时,则跟threadx处于相同一级目录即可。
4. 编译验证
编译:
$ cd ~/pico/pico-threadx $ mkdir build $ cd build $ cmake .. $ make -j4
其中blink文件夹下会生成blink.uf2:
$ ls ~/pico/pico-threadx/build/samples/blink ... blink.uf2 ...
直接把blink.uf2复制到raspberry pi pico的内存中,就会看到板载LED闪烁和串口打印数据了。

浙公网安备 33010602011771号