一、RTX5 配置文件RTX_Config.h
使用RTX5开发项目之前,一定要先把RTX5系统配置文件RTX_Config.h弄明白。在学习RTX5的配置文件后,我认为RTX5的系统配置文件比FreeRTOS与ucosIII的系统配置文件都要简单。
RTX_Congig.h包含以下内容:
System Configuration(非常重要)
Thread Configuration(非常重要)
Timer Configuration(简单)
Event Flags Configuration(简单)
Mutex Configuration(简单)
Semaphore Configuration(简单)
Memory Pool Configuration(简单)
Event Record Configuration(跟RTX5内核没关系,调试工具)
1、 System Configuration
用于配置内存池,滴答定时器的频率,中断回调函数ISR的事件Buffer,时间片轮转调度。
Global Dynamic memory size(内存池的大小):内存池的大小默认值是32768个字节,相当于32K的RAM内存。每一款单片机都有RAM内存,只是大小或数量不一样而已。
Kernel Tick Frequency[Hz](内核滴答时钟的频率):提供时间基准给osDelay()与osDelayUntil(),还有超时计算。Kernel Tick Frequency频率越高就更占用CPU的资源,一般项目直接使用1000Hz即可。
Round-Robin Thread switching(时间片轮转调度):RTX5也提供时间片轮转调度的功能。有了这个功能后,系统就允许有两个以上的线程使用同一个优先级。为同一个优先级下的线程划分时间片进行调度,时间片的长度可以通过Round-Robin Timeout设置(5等于5个Tick)。时间片轮转调度适用于不要求任务实时响应的情况。
ISR FIFO Queue(中断回调请求FIFO队列):RTX5能缓存Cortex-M内核的中断回调函数ISR的请求与参数。
Object Memory usage counters(对象内存使用计数器):在系统运行的过程中,计算线程,消息队列等对象的生存情况。对于需要安全性的应用程序来说,这个功能是必需的。勾选这个选项后,通过DEBUG模式下的RTX RTOS调试窗口可以看到Object Memory Usage Counter选项。
2、 Thread Configuration
Object specific Memory allocation(对象特定的内存分配):为每一个RTX5的对象创建一个专用的固定大小的内存块,可以有效防止内存碎片化,并简化内存不足情况的处理。并且,在专用的内存里创建对象与删除对象的时间都是完全确定的。默认的配置下,不勾选Object Specific Memory allocation。此时,当我们创建RTX5的对象时,会在全局内存池里申请一段内存。有线程对象,有消息队列对象,有信号量对象等等,按申请内存的时间顺序一个个放入Global Memory Pool(全局内存池)里。这种情况下,内存肯定会有一点乱。
Number of user Threads(用户线程的数量):定义同一时间上最多可以运行多少个用户线程。超过数量的线程不会被创建。
Total Stack size[bytes] for user Threads with user-provided Stack size(提供给用户线程的总堆栈大小):默认值0,表示为不设置的意思。
Default Thread Stack size(线程的默认堆栈大小):创建线程时不指定堆栈大小,就会使用Default Thread Stack Size的值来创建线程。
Idle Thread Stack size(空闲线程的堆栈大小):看空闲线程的设计需要多大的内存,默认的空闲线程只需256KB。
Idle Thread TrustZone Module Identifier(空闲线程信任区域模块标识符)。
Stack overrun checking (堆栈溢出检查):一定要勾选,堆栈真的很容易溢出。
Stack usage watermark(堆栈使用水印):使用水印模式初始化线程堆栈以分析堆栈使用情况。启用此选项会显着增加线程创建的执行时间。我觉得没必要勾选这一项,在DEBUG模式下,使用Keil的RTX RTOS也能看到堆栈的使用情况。
3、 Timer Configuration
Object specific Memory allocation(对象特定的内存分配):请参考前面。
Timer Thread Priority(软件定时器的优先级):软件定时器支持6种优先级:
Low;Below Normal;Normal;Above Normal;High;Real Time。优先级依次由低到高。
Timer Thread Stack size[bytes]:设置每一个软件定时器的堆栈大小。
Timer Thread TrustZone Module Identifier:特定的Cortex-M内核才有。
Timer Callback Queue entries(定时器回调消息队列):设置定时器并发回调函数的数量,设置0时,关闭此功能。
Event Flags Configuration、Mutex Configuration、Semaphore Configuration、Memory Pool Configuration:剩下的消息队列设置,互斥量设置,信号量设置,内存池设置,消息队列设置,都只有一个Object specific Memory allocation。跟线程的Object specific Memory allocation一样。
二、时间延时
相对延时osDelay(uin32_t ticks)
绝对延时osDelayUntil(uint32_t ticks)
无论哪一种时间延迟方式都难以避免线程被延时执行。当各个线程的优先级不一样时,RTX5执行可剥夺型线程管理。某个线程的延时时间到达时,很有可能有其他更高优先级的线程处于就绪态,或者产生ISR中断回调处理,此时线程就会被推迟执行。线程被推迟的时间肯定会波动的,比如某一次延时时间达到时,系统上并没有优先级更高的线程正处于就绪态,或者也没有ISR中断回调需要执行,这个理想的条件下,线程不会被推迟执行。所以,要计算每一次线程被推迟的时间比较困难。
两种延时方式也许看不出区别,但实际上两者是不同的。绝对延时OsDelayUntil()需要与另一个API函数osKernelGetTickCount()配合使用。相对延时osDelay()非常简单,在需要延时的地方直接调用它即可。相对延时osDelay()在系统负荷较重时有可能会少一个节拍,RTX5官方手册也有说明。使用绝对延时osDelayUntil()也很难避免被推迟,但它总会和预期的“匹配值”同步(osKernelGetTickCount()获取),因此,一般推荐使用“绝对延时”来实现长时间运行的周期性延时。比如,线程A在CPU上电时就被创建,创建之后一直以5ms的周期运行,永不停止。
调用osDelay()时,当前线程进入WAIT_DELAY(BLOCKED)状态,调度器将运行下一个已经处于就绪态(READY)的线程。当计时时间到达,调用osDelay()的线程从WAIT_DELAY(BLOCKED)状态切换到就绪态(READY),如果没有其他更高优先级的线程,那么当前线程就直接进入运行态(RUNNING)。
osDelayUntil()基于一个绝对时间来延时,这个绝对时间来自osKernelGetTickCount()。调用osDelayUntil()使线程进入阻塞态(BLOCKED)并立刻进行上下文切换,当计时时间达到时,线程从阻塞态(BLOCKED)切换到准备态(READY),如果当前没有更高优先级的其他线程的话,那么线程直接进入运行态(RUNNING)。