最全认证 RTOS——azure_threadX 移植教程
ThreadX 内核内容介绍
Note本文图片过多,未转移自有服务器,仍使用公共图床,载图可能会很慢
threadx 已捐赠给 eclipse 基金会,目前的开源代码在这里。
我使用的通常是 st 公司的 fork,实际上的支持并没有好多少,该有的坑也还是有
ThreadX 内核包含三个内容:ThreadX 操作系统内核,Modules 模组、trace 调试监测。在 ThreadX 源码中,这些内容很多是混杂在一起的。所以简单写一个文档记录一下。即是移植教程,也记录库结构。
本文档所有的图片,都在图片相关描述文字的上方。这是 threadX 源码的所有内容其中显示出了系统内核的三个版本
common
普通内核common_module
具有模组加载功能的内核,硬件驱动与软件逻辑可完全分离common_smp
具有多核 MCU 支持的内核
内核移植目标是使用这些组件
- ThreadX 操作系统内核
- Modules 模组
- Trace 调试监控
纯内核移植
纯内核是我说的——相对于modules
版内核和smp
版内核,实际上与 freertos、bios 这类普通 RTOS 功能无异,只是移植 threadX 系其他组件更方便,命名也更一致,例如 NetX、usbX、FileX 都可以非常简单加入系统。
哪些是纯内核源码?
在 threadX 操作系统源码中,有如图框出的两个文件夹,这两个文件夹实际上就是我们移植操作系统内核需要的所有源码了
首先关注到 common 文件夹,这些实际上都是需要的源码,也就是我们的工程需要的文件,但仍然有一些值得注意的地方。
在 stc 文件夹下,有一些 tx_trace_xx 命名的源码文件,这些是 trace 调试监测使用的源码
以这个开启监控的函数为例,如果没有使用 TX_ENABLE_EVENT_TRACE 宏定义进行开启监控功能,那么这个函数是不会做任何事情的
在 inc 文件夹下,也有一个 tx_trace.h 的文件。
没有开启 TX_ENABLE_EVENT_TRACE 宏定义,也是几乎不做任何事情。
回到源码文件夹,再关注到 ports 文件夹
找到自己对应的内核
找到自己对应的编译器,源码和头文件都是工程所需,另外要注意的是排除可能存在的这个文件 tx_misra.S,可能已经有 tx_misra.c 定义了
简单介绍一下ports/内核名/编译器名/inc
中的tx_port.h文件,这里是调整 threadX 的唯一接口,类似TX_ENABLE_EVENT_TRACE
这样控制操作系统的功能开启与否的宏定义,可以写在编译器整体 define 处。
但是关注到第79行,他给了一个另外的.h 接口供我们定义。我们可以将TX_INCLUDE_USER_DEFINE_FILE
宏定义写在编译器整体 define 中然后通过自定义一个tx_user.h文件来控制操作系统宏定义。
这个tx_user.h文件有一个示例,在操作系统源码common\inc
下有名为tx_user_sample.h
或类似的文件作为示例,但是tx_user.h这个文件名需要自己创建或改名他的示例文件。
在 common\src 下有这样一个 tx_thread_initialize.c,其中有一个变量可以在运行时查看配置状况。这个变量同时在 tx_thread.h 中被 extern 声明,所以使用 threadX 操作系统时可以随地查看这个变量且不可重复声明。
应该如何开启?
虽然我们使用 ac6编译器,仍然可以先关注一下 gnu 文件夹,因为其中有一个简单的创建任务的示例
在这里!
简单分析示例
- 这部分是头文件,仅仅包含了 tx_api.h
- 这部分是 main 入口
- 这部分是一个固定需要自己定义的函数,这个函数名在_tx_initialize_kernel_enter()开启 thread 内核时会进行调用,实际上就是内核开启后给我们用户程序的接口,。
简单分析一下这个用户接口函数的话,其实就是将 threadX 的几个主要 api 演示了一遍,申请内存空间,使用这片内存空间创建任务,创建消息队列,创建消息量等等常见常用的 API,具体如何使用可以自行看源码。
结合整个示例,我们知道使用 threadX 内核的最基本操作:
- 包含
tx_api.h
- 定义
void tx_application_define(void *first_unused_memory)
这个函数,并在这个函数调用后再创建任务(此时 threadX 内核已经开启) - 主程序中使用
tx_kernel_enter();
函数开启 threadX 内核
关注到与示例同文件夹下的这个文件
其中定义了一些中断函数,用于接管芯片的中断处理,使 threadX 操作系统运行起来,注意修改SYSTEM_CLOCK
和SYSTICK_CYCLES
,在如图示例中,600000
代表芯片主频时钟是6M,100代表
操作系统的时钟基准是10ms,也就是tx_thread_sleep(1);
实际上是释放内核10ms
其实这个.S 文件仍然有需要修改的地方,似乎是某些中断没有定义,最好是使用 stm32cubeMX 自动生成一个,然后粘到自己工程里,如果操作系统没有正常运行,最大的可能是这个 tx_initialize_low_level.s 有问题
编译器整体 Define 添加这样一条TX_INCLUDE_USER_DEFINE_FILE
带模组管理器的内核移植
首先确认一个概念,模组管理器和模组是什么?
模组管理器具有 threadX 内核,具有驱动硬件的能力,可以从某些存储介质内加载模组的可执行二进制内容(例如从片内 flash 的0x8100000这个地址开始加载模组;模组不具有驱动硬件的能力,没有 threadX 内核,无法单独工作,但是作为一个单独的工程存在,可以独立编译发布。
也就是模组管理器和模组是两个工程,同时他们需要移植的源码是不同的。
至于有什么必要做这样的事情,不在这里讨论。
哪些是带模组管理器的源码?
可以先不管那么多,将这两个文件夹都粘进自己的工程再说在 threadX 操作系统源码中,有如图框出的三个文件夹,这三个文件夹内有移植带模组管理器的内核的所有源码
由于默认已经看过纯内核移植的内容了,认为对基础内核以及 trace 调试追踪有一定了解。所以这些内容不再赘述,仅讨论与纯内核不同的点。
关注到common_modules
文件夹
作为模组管理器的内核,需要的是框起来的两个源码文件夹,而 module_lib 是给模组移植的源码。
可以先不管那么多,将这两个文件夹的所有内容都包含进自己的工程再说,后续再做调整
关注到ports_modules
文件夹
找到自己对应的架构
找到自己对应的编译器
需要的是框起来的两个源码文件夹,而 module_lib 是给模组移植的源码。
不管那么多,将这两个文件夹的所有内容都包含进自己的工程再说,后续再做调整
然后从例程找到或者使用 stm32cubeMX 自动生成一个tx_initialize_low_level.S,由于 stm32cubeMX 不支持生成 modules 工程,事实上也不适合,能顺利使用 modules 需要大量自己的适配和操作。
在如图这个工程中,推荐从例程中寻找。
在纯内核移植中已经介绍过tx_initialize_low_level.S了,不再赘述。
回到这个文件夹下,找到名为txm_module_user_sample.h的文件,复制一份改名为txm_module_user.h,当编译器整体 define 设置有TXM_MODULE_INCLUDE_USER_DEFINE_FILE时,在模组管理器工程和模组工程中都会调用名为txm_module_user.h的头文件(必须是相同的),以控制操作系统功能
txm_module_user.h
中大致是这样的内容
记得tx_user.h
也需要创建,在纯内核中有说明
应该如何开启
实际上与纯内核的开启方法一样。但注意这种内核使用时,标准的做法是将业务逻辑都做成模组进行加载,但是模组工程的建立以及二者的适配也挺麻烦的,目前还没完全搞清楚,所以也没有做成文档。
但就把它当作普通内核使用也是没有问题的。