0、ThreadX启动流程
tx_kernel_enter
_tx_initialize_low_level
_tx_initialize_high_level
tx_application_define
_tx_thread_schedule
#define tx_kernel_enter _tx_initialize_kernel_enter
- tx_kernel_enter,进入ThreadX内核,即启动Thread内核
- _tx_initialize_low_level,初始化中断向量,SysTick定时器, 设置中断优先级等,一般不使用ThreadX定义的中断,直接修改启动文件定义的中断向量
- _tx_initialize_high_level,调度器初始化,初始化信号量、事件标志、消息队列等,根据宏选择是否将软件定时器线程化。
- tx_application_define,应用程序回调函数,用户可以在里面创建任务,各种通信机制。
- _tx_thread_schedule,启动ThreadX调度器
1、任务优先级
任务优先级,数值越小优先级越高
2、ThreadX专用任务创建函数
void tx_application_define(void *first_unused_memory)
- 该函数需要用户自己定义,ThreadX启动会自动调用该函数,用户可以在该函数中创建自己的任务。
- first_unused_memory,未使用的地址空间,方便用户创建自己的动态内存管理。ThreadX提供了给用户创建内存池、内存块等进行动态内存分配的功能。
3、ThreadX系统没有定义空闲线程
- 如需要空闲线程需要用户自己定义
ThreadX内核没有空闲任务,那么没有任务执行的时候它都在干什么:
ports/cortex_m4/gnu/src/tx_thread_schedule.S文件中有定义:
@ /* The following is the idle wait processing... in this case, no threads are ready for execution and the
@ system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts
@ are disabled to allow use of WFI for waiting for a thread to arrive. */
@
__tx_ts_wait:
CPSID i @ Disable interrupts
LDR r1, [r2] @ Pickup the next thread to execute pointer
STR r1, [r0] @ Store it in the current pointer
CBNZ r1, __tx_ts_ready @ If non-NULL, a new thread is ready!
没有任务时ThreadX都在这里执行死循环。
4、ThreadX内核GCC编译构建中__RAM_segment_used_end__的作用
- __RAM_segment_used_end__的含义就是当前已经使用RAM区的末尾地址。
- 其值会通过void tx_application_define(void *first_unused_memory)函数的first_unused_memory参数传递给用户使用。
5、使用GCC编译构建需要在链接脚本中定义__RAM_segment_used_end__
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
__RAM_segment_used_end__ = .;
} >RAM
6、修改系统时钟和OS Tick
在Threadx移植文件下ports/cortex_m4/gnu/src/tx_initialize_low_level.s中定义:
SYSTEM_CLOCK = 180000000
SYSTICK_CYCLES = ((SYSTEM_CLOCK / 1000) -1)
- SYSTEM_CLOCK, 系统时钟180MHZ
- SYSTICK_CYCLES 后面的1000表示OS Tick时钟为1000HZ,也就是OS Tick是1ms
7、临界段保护—中断锁
#define TX_INTERRUPT_SAVE_AREA unsigned int interrupt_save;
#define TX_DISABLE interrupt_save = __disable_interrupts();
#define TX_RESTORE __restore_interrupts(interrupt_save);
使用方法:
TX_INTERRUPT_SAVE_AREA
TX_DISABLE
xxx 临界段
TX_RESTORE
8、临界段保护——任务锁
一般RTOS任务锁实现有两种方法:
- 调度器加锁,给调度器加锁的话,就无法实现任务切换,高优先级任务也就无法抢占低优先级任务的执行,同时高优先级任务也是无法向低优先级任务切换的uCos、RT-Thread等就是用这种方式实现。
- 关闭给OS提供Tick的定时器,没有心跳系统就不能进行调度工作。
但ThreadX有更好的实现方式:抢占阈值。抢占阈值是ThreadX独有的高级功能。
- 抢占阈值允许任务指定禁止抢占的优先级上限。优先级高于上限的任务仍可以执行抢占,但不允许优先级低于上限的任务执行抢占
- 可以将抢占阈值设置为0来禁止所有任务抢占
比如一个任务的优先级是 5,我们希望执行某代码期间禁止优先级 0-4 的任务抢占:
TX_THREAD my_thread;
UINT my_old_threshold;
UINT status;
status = tx_thread_preemption_change(&my_thread,
0, &my_old_threshold);
用户可以在此处执行关键代码。
status = tx_thread_preemption_change(&my_thread,
5, &my_old_threshold);
9、ThreadX 动态加载
ThreadX支持动态加载App执行,类似安卓App
ThreadX动态APP玩法调用主程序的API也很方便,这样APP就可以仅写应用代码即可,驱动之类的都在主程序实现:
https://www.armbbs.cn/forum.php?mod=viewthread&tid=112170&highlight=threadx
10、绝对延时和相对延时
相对延时:
tx_thread_sleep(100);
通过相对延时延时实现绝对延时:
static void TaskFunc(ULONG thread_input)
{
UINT delay, nextTime;
const UINT frequency = 200;
/* 获取 frequency 个时钟节拍后的时间 */
nextTime= tx_time_get() + frequency;
while(1)
{
LedToggle();
delay = nextTime - tx_time_get();
nextTime += frequency ;
if(delay <= frequency)
{
tx_thread_sleep(delay);
}
}
}