ThreadX分析(一)

简介

项目地址:https://github.com/azure-rtos/threadx

  • ThreadX被设计的像C库一样,只有被用到的组件才会编译进最终的App中,这样可以保证最终生成的应用软件只包含自己所需的组件,拥有更小的应用体积(一般2KB~15KB左右)。
  • ThreadX易于裁剪,即适用于微控制器也适用与像CISC/RISC/DSP等强大的处理器上。
  • ThreadX除了提供内核之外还提供了usbx、levelx、netxduo、filex、guix等强大的组件。
  • ThreadX使用picokernel结构,相较于其他微内核结构拥有更高的效率
  • ThreadX使用C语言和少量的汇编语言编写,易于移植,可以在一周内完成移植工作
  • Simple picokernel architecture(简单的picokernel 架构)
  • Automatic scaling (small footprint)(自动缩放,占用空间小)
  • Deterministic processing()
  • Fast real-time performance(快速实时的性能)
  • Preemptive and cooperative scheduling(抢占式协同调度)
  • Flexible thread priority support(支持动态优先级)
  • Dynamic system object creation(动态对象创建)
  • Unlimited number of system objects(不限制系统对象数量)
  • Optimized interrupt handling(优化的中断处理)
  • Preemption-threshold™
  • Priority inheritance(优先级继承)
  • Event-chaining™
  • Fast software timers(高效的软件定时器)
  • Run-time memory management(Run-time内存管理)
  • Run-time performance monitoring(Run-time性能监视)
  • Run-time stack analysis(Run-time堆分析)
  • Built-in system trace(内嵌系统追踪调试)
  • Vast processor support(支持大量的处理器)
  • Vast development tool support(支持很多开发工具)
  • Completely endian neutral

组件间的依赖关系

在这里插入图片描述

其中

  • usbx:USB库
  • levelx: 提供了NAND和NOR闪存磨损均衡操作方式
  • netxduo: 网络库
  • filex: 文件库
  • guix: GUI库
  • threadx: os

构建编译

这将编译生成一个库文件,可以将这个库文件包含在自己的项目中

$ mkdir build; cd build
$ cmake -Bbuild -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_linux.cmake -GNinja ../
$ cmake --build .

注意:除了编译threadx之外,要编译其他组件的时候都要参考组件间的关系图来完成编译依赖

Threadx概览

Threadx目录结构
threadx
├── CONTRIBUTING.md 
├── LICENSE.txt
├── LICENSED-HARDWARE.txt
├── README.md
├── SECURITY.md
├── CMakeLists.txt 
├── cmake				//保存了相应平台的cmake设置,会在目录的CmakeList.txt中调用
├── common				//threadx的源码(for 单核CPU)
├── common_modules		//支持动态应用加载相关代码
├── common_smp			//threadx的源码(for 多核CPU)
├── docs
├── ports				//与平台对应的代码(for 单核CPU),移植时需要这个里面的代码
├── ports_module		//动态应用加载平台相关代码
├── ports_smp			//与平台对应的代码(for 多核CPU)
├── samples				//例程
└── utility				//包含了低功耗相关代码和FreeRTOS适配层

调度器定时器初始化相关操作在threadx的***tx_initialize_low_level***汇编文件

下面是一些重要的文件

文件名描述
tx_api.hthreadx的API文件
tx_port.h包含对应平台的数据定义和结构体定义
demo_threadx.cthreadx的demo文件
tx.a (tx.lib)threadx c源码编译获得的c库,可以在项目中直接使用
使用thread的步骤
  1. 包含tx_api.h

  2. 创建一个main函数,在main函数中必须调用***tx_kernel_enter***来启动threadx,不涉及到threadx内核相关的初始化工作应该在此函数之前初始化。

    注意:tx_kernel_enter函数不会返回。

  3. 在main.c中创建***tx_application_define*** 函数,在这里创建系统初始资源包括threads, queues, memory pools, event flags groups, mutexes, 和semaphores。

  4. 编译应用程序并连接***tx.lib***,最终生成可执行程序。

#include "tx_api.h"
unsigned long my_thread_counter = 0;
TX_THREAD my_thread;
main( )
{
    /* Enter the ThreadX kernel. */
    tx_kernel_enter( );
}
void tx_application_define(void *first_unused_memory)
{
    /* Create my_thread! */
    tx_thread_create(&my_thread, "My Thread",
    my_thread_entry, 0x1234, first_unused_memory, 1024,
    3, 3, TX_NO_TIME_SLICE, TX_AUTO_START);
}
void my_thread_entry(ULONG thread_input)
{
    /* Enter into a forever loop. */
    while(1)
    {
        /* Increment thread counter. */
        my_thread_counter++;
        /* Sleep for 1 tick. */
        tx_thread_sleep(1);
    }
}
Threadx配置
最小代码配置
TX_DISABLE_ERROR_CHECKING
TX_DISABLE_PREEMPTION_THRESHOLD
TX_DISABLE_NOTIFY_CALLBACKS
TX_DISABLE_REDUNDANT_CLEARING
TX_DISABLE_STACK_FILLING
TX_NOT_INTERRUPTABLE
TX_TIMER_PROCESS_IN_ISR
最快运行速度配置

​ 在最小代码量的基础上添加下面两个配置

TX_REACTIVATE_INLINE
TX_INLINE_THREAD_RESUME_SUSPEND
全局time源配置

​ 在***tx_port.h***中配置全局时钟,其定义了多少个tick中断表示一秒(例如:10ms产生一次中断则这个值设置成100)在threadx组件中有使用这个变量FileX, NetX, GUIX, USBX,等。

注意:需要与系统心跳时间相匹配,否则会使计算出来的时间错误

TX_TIMER_TICKS_PER_SECOND	100
详细配置描述

TX_BYTE_POOL_ENABLE_PERFORMANCE_INFO

如果定义了这个宏,则使能了byte内存池的性能收集功能。默认未定义此宏

TX_DISABLE_ERROR_CHECKING

如果定义了这个宏,则会跳过给所有的基本参数检查,这大概能提高30%左右的性能以及减少image大小,默认未定义此宏

注意:部分函数的返回值也会变成无效的如果定义了此宏

TX_DISABLE_NOTIFY_CALLBACKS

如果定义了这个宏,则会导致各个模块中的notify callbacks失效,这样可以稍微减少代码大小以及提高性能。默认未定义此宏

TX_DISABLE_PREEMPTION_THRESHOLD

如果定义了这个宏,则会取消抢占阈值调度功能,会稍微减少代码大小并且提高性能。默认未定义

TX_DISABLE_REDUNDANT_CLEARING

如果定义了这个宏,则会取消Threadx对全局未赋值变量以及结构体的清零功能。只有在编译器初始化代码中有做这个清零操作的时候才能取消这个功能。定义这个宏会稍微减少代码大小并且提高系统初始化时的性能。默认这个宏未定义

TX_DISABLE_STACK_FILLING

如果定义了这个宏,禁止在创建时将0xEF值放在每个线程堆栈的每个字节中。默认情况下,没有定义此选项。

TX_ENABLE_EVENT_TRACE

定义后,ThreadX启用事件收集代码来创建TraceX跟踪缓冲区。

TX_ENABLE_STACK_CHECKING

定义后,启用ThreadX运行时堆栈检查,包括分析使用了多少堆栈,以及检查堆栈区域前后的数据模式“栅栏”。如果检测到堆栈错误,则调用已注册的应用程序堆栈错误处理程序。这个选项确实会略微增加开销和代码大小。查看***tx_thread_stack_error_notify***API函数以获得更多信息。默认情况下,没有定义此选项。

TX_EVENT_FLAGS_ENABLE_PERFORMANCE_INFO

定义后,启用收集事件标志组的性能信息。默认情况下,没有定义此选项。

TX_INLINE_THREAD_RESUME_SUSPEND

定义后,ThreadX通过内联代码改进了***tx_thread_resume*** 和***tx_thread_suspend*** API调用。这增加了代码大小,但提高了这两个API调用的性能。

TX_MAX_PRIORITIES

定义ThreadX的优先级级别。取值范围为32 ~ 1024(含1024),必须能被32整除。增加支持的优先级级别的数量会使每组32个优先级的RAM使用增加128字节。然而,这对性能的影响微乎其微。缺省情况下,该值被设置为32个优先级。

TX_MINIMUM_STACK

定义最小堆栈大小(以字节为单位)。它用于在创建线程时进行错误检查。默认值是特定于平台的,可以在***tx_port.h***中找到。

TX_MISRA_ENABLE

在定义时,ThreadX利用了符合MISRA C的约定。详情请参阅***ThreadX_MISRA_Compliance.pdf***。

TX_MUTEX_ENABLE_PERFORMANCE_INFO

定义后,启用对mutex(互斥锁)的性能信息的收集。默认情况下,没有定义此选项。

TX_NO_TIMER

在定义时,ThreadX计时器逻辑是完全禁用的。这在没有使用ThreadX计时器特性(线程睡眠、API超时、时间片和应用程序计时器)的情况下非常有用。如果指定了TX_NO_TIMER ,则还必须定义选项TX_TIMER_PROCESS_IN_ISR

TX_NOT_INTERRUPTABLE

在定义时,ThreadX不会试图最小化中断锁定时间。这将导致更快的执行,但会略微增加中断锁定时间。

TX_QUEUE_ENABLE_PERFORMANCE_INFO

定义后,启用收集队列上的性能信息。默认情况下,没有定义此选项。

TX_REACTIVATE_INLINE

定义后,内联执行ThreadX计时器的重新激活,而不是使用函数调用。这会提高性能,但会略微增加代码大小。默认情况下,没有定义此选项。

TX_SEMAPHORE_ENABLE_PERFORMANCE_INFO

定义后,启用对信号量的性能信息的收集。默认情况下,没有定义此选项。

TX_THREAD_ENABLE_PERFORMANCE_INFO

定义后,启用收集线程上的性能信息。默认情况下,没有定义此选项。

TX_TIMER_ENABLE_PERFORMANCE_INFO

定义后,启用对计时器收集性能信息。默认情况下,没有定义此选项。

TX_TIMER_PROCESS_IN_ISR

定义后,消除了ThreadX的内部系统计时器线程。这将提高计时器事件的性能,并减少RAM需求,因为不再需要计时器堆栈和控制块。但是,使用此选项将所有计时器过期处理移动到计时器ISR级别。默认情况下,没有定义此选项。

TX_TIMER_THREAD_PRIORITY

定义内部ThreadX系统计时器线程的优先级。默认值为优先级0—ThreadX中的最高优先级。默认值定义在***tx_port.h***中。

TX_TIMER_THREAD_STACK_SIZE

定义内部ThreadX系统计时器线程的堆栈大小(以字节为单位)。该线程处理所有线程睡眠请求和所有服务调用超时。此外,所有应用程序计时器回调例程都是从这个上下文中调用的。默认值是特定于平台的,可以在***tx_port.h***中找到。

获取tx version

可以使用**_tx_version_id这个变量在程序中获取tx version,在*tx_port.h***中定义了这个变量

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是代码实现: ```c #include "stm32f10x.h" #include "os.h" #define STACK_SIZE 256 OS_TCB tcb1; OS_TCB tcb2; OS_TCB tcb3; CPU_STK stack1[STACK_SIZE]; CPU_STK stack2[STACK_SIZE]; CPU_STK stack3[STACK_SIZE]; OS_FLAG_GRP* flag_group; void thread1(void* pdata); void thread2(void* pdata); void thread3(void* pdata); int main() { OS_ERR err; OSInit(&err); flag_group = OSFlagCreate(0, &err); OSTaskCreate(&tcb1, "Thread 1", thread1, NULL, 25, &stack1[0], STACK_SIZE / 10, STACK_SIZE, 0, 0, NULL, OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, &err); OSTaskCreate(&tcb2, "Thread 2", thread2, NULL, 24, &stack2[0], STACK_SIZE / 10, STACK_SIZE, 0, 0, NULL, OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, &err); OSTaskCreateStatic(&tcb3, "Thread Flag3", thread3, NULL, 26, &stack3[0], STACK_SIZE / 10, STACK_SIZE, 0, 0, NULL, OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR, &err); OSStart(&err); } void thread1(void* pdata) { OS_ERR err; while (1) { OSTimeDlyHMSM(0, 0, 0, 500, OS_OPT_TIME_HMSM_STRICT, &err); GPIO_SetBits(GPIOB, GPIO_Pin_0); OSTimeDlyHMSM(0, 0, 0, 500, OS_OPT_TIME_HMSM_STRICT, &err); GPIO_ResetBits(GPIOB, GPIO_Pin_0); OSSemPost(&sem1, OS_OPT_POST_1, &err); } } void thread2(void* pdata) { OS_ERR err; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); while (1) { OSFlagPend(flag_group, 0x01, OS_OPT_PEND_FLAG_SET_ALL + OS_OPT_PEND_FLAG_CONSUME, 0, &err); GPIO_SetBits(GPIOB, GPIO_Pin_1); OSTimeDlyHMSM(0, 0, 0, 500, OS_OPT_TIME_HMSM_STRICT, &err); GPIO_ResetBits(GPIOB, GPIO_Pin_1); OSTaskDel(OS_PRIO_SELF, &err); } } void thread3(void* pdata) { OS_ERR err; while (1) { GPIO_SetBits(GPIOB, GPIO_Pin_2); OSTimeDlyHMSM(0, 0, 0, 5, OS_OPT_TIME_HMSM_STRICT, &err); GPIO_ResetBits(GPIOB, GPIO_Pin_2); OSTimeDlyHMSM(0, 0, 0, 5, OS_OPT_TIME_HMSM_STRICT, &err); OSFlagPost(flag_group, 0x01, OS_OPT_POST_FLAG_SET, &err); } } ``` 在该代码中,我们使用了STM32F10x控制器来实现线程的设计。thread1使用了一个死循环,等待thread2退出后开始运行,并循环地每隔一段时间运行一次。thread2使用了一个较高的优先级,抢占低优先级的thread1,并在执行完一段程序后自动被系统脱离。thread3实现了一个周期为10个OSTicks的方波,同时具有比thread1更高的优先级。我们还使用了OSFlag和OSSemaphore来协调线程之间的同步与互斥。 在keil软件仿真中,我们可以设置虚拟逻辑分析仪来获取线程的状态。需要使用CMSIS-DAP工具来连接STM32控制器,并在仿真之前将STM32控制器与CMSIS-DAP连接。在仿真时,可以通过“Debug”菜单中的“Start/Stop Logic Analyzer”选项来启动虚拟逻辑分析仪。通过该工具,我们可以获取每个线程的运行状态并进行分析

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值