赞
踩
因为 main() 不再是一个线程,所以在到达 main 之前 RTX 不会干扰系统启动。一旦执行到达main(),建议你按照如下顺序来初始化硬件并启动内核:
RTX 实现了一个低延迟的抢占式调度器。
RTX 的主要部分是在 handler 模式下执行的,例如:
为了降低ISR(中断服务函数)执行的延迟,这些系统中断被配置为使用可用的最低优先级组。
RTX调度器结合了优先级和基于循环的上下文切换。
上图中的例子包含四个线程(1、2、3和4)。线程1和线程2具有相同的优先级,线程3具有更高的优先级,线程4具有最高的优先级。只要线程3和线程4被阻塞,调度器就会在线程1和线程2之间按时间片进行切换(循环)。 注:可以配置轮循调度的时间片,RTX_Config.h 文件中的 Round-Robin Timeout 参数定义了在线程切换之前,线程将执行多长时间。默认值是5。取值范围为[1-1000]。
在 Time == 2 期间,线程2通过一个 RTOS-call (在SVC中断服务函数中执行) 解除了线程3的阻塞。调度程序立即切换到线程3,因为线程3具有最高的优先级。
在 Time == 4 期间,发生了一个中断(ISR)并抢占SysTick_Handler。RTX不会向中断服务执行添加任何延迟。ISR的中断服务函数里使用一个RTOS-call来解除线程4的阻塞。PendSV 的 flag 被设置为延迟上下文切换,而不是立即切换到线程4。PendSV_Handler在SysTick_Handler返回后立即执行,并执行到线程4的延迟上下文切换。
在 Time == 5期间,当最高优先级的线程4被 阻塞RTOS-call(blocking RTOS-call) 再次阻塞时,立即切换回线程3。
在 Time == 5期间,线程3也使用了一个 阻塞RTOS-call。因此,调度器将切换回线程2
RTX5的对象(objects)有:线程、互斥、信号量、计时器、消息队列、线程和事件标志,以及内存池。
它们需要专用的RAM内存。可以用osObjectNew()创建对象,用osObjectDelete()删除对象。对象的相关内存需要在其生存期内可用。
RTX5提供了三种不同的内存分配方法:(可以在同一个应用程序中混合使用所有内存分配方法。)
全局内存池(Global Memory Pool)
特定对象内存池(Object-specific Memory Pools)
静态对象内存(Static Object Memory)
Memory type 内存类型 | Requirements 要求 |
---|---|
Control Block 控制块 | 4字节对齐。 大小定义: osRtxThreadCbSize, osRtxTimerCbSize, osRtxEventFlagsCbSize, osRtxMutexCbSize, osRtxSemaphoreCbSize, osRtxMemoryPoolCbSize, osRtxMessageQueueCbSize. |
Thread Stack 线程堆栈 | 8字节对齐。 大小是特定于应用程序的,即堆栈变量和帧的数量 |
Memory Pool 内存池 | 4字节对齐。 使用osRtxMemoryPoolMemSize计算大小 |
Message Queue 消息队列 | 4字节对齐。 使用osRtxMessageQueueMemSize计算大小 |
RTX Object | Linker Section |
---|---|
Thread | .bss.os.thread.cb |
Timer | .bss.os.timer.cb |
Event Flags | .bss.os.evflags.cb |
Mutex | .bss.os.mutex.cb |
Semaphore | .bss.os.semaphore.cb |
Memory Pool | .bss.os.mempool.cb |
Message Queue | .bss.os.msgqueue.cb |
// 定义静态分配的对象为线程1
__attribute__((section(".bss.os.thread.cb")))
osRtxThread_t worker_thread_tcb_1;
系统线程osRtxIdleThread可以用来将系统切换到低功耗模式。进入低功耗模式的最简单形式是执行将处理器置于休眠模式(等待事件发生)的wfe函数。
Code Example:
#include "RTE_Components.h"
#include CMSIS_device_header /* Device definitions */
void osRtxIdleThread (void) {
/* 空闲任务(idle demon) 属于系统线程, 在没有其他线程运行时运行 */
/* 即将运行 */
for (;;) {
__WFE(); /* 进入睡眠模式 */
}
}
注意:__WFE() 并非在每个Cortex-M实现中都可用。查询技术手册是否可用__WFE() 。
RTX使用通用的OS Tick API来配置和控制它的周期性 Kernel Tick。
若要用其他计时器替代滴答计时器,只需要实现一个自定义版本的OS Tick API。
注意:
提供的OS Tick实现必须确保使用的计时器中断使用与服务中断相同的(低)优先级组,即RTX使用的中断不能相互抢占。有关详细信息,请参阅调度程序部分。
RTX5提供了对 tick-less运行的扩展,这对于广泛使用低功耗模式的应用程序非常有用,因为SysTick计时器也被禁用了。为了在这种省电模式中提供时间刻度,可以使用wake-up计时器产生计时器间隔。CMSIS-RTOS2函数 osKernelSuspend 和 osKernelResume 控制 tick-less运行。
利用这个功能可以让RTX5的线程管理器停止周期性的内核滴答中断。当所有活跃线程都被挂起时,系统进入power-down模式,并计算它可以在这种power-down模式下保持多长时间。在power-down模式下,处理器和外围设备可以关闭。只有一个wake-up计时器必须保持通电状态,因为这个计时器负责在power-down后唤醒系统。
Tick-less运行 由osRtxIdleThread线程控制。唤醒时间值(wake-up timeout value)是在系统进入power-down模式之前设置的。函数osKernelSuspend计算RTX Timer Ticks基准下的唤醒时间;此值用于设置在系统power-down模式下运行的wake-up计时器。
一旦系统恢复操作(通过唤醒时间超时或其他中断),RTX5线程调度器将使用osKernelResume函数启动。参数sleep_time指定系统处于关机模式的时间(在RTX Timer Ticks基准下)。
Code Example:示例代码
CMSIS-RTOS2 API的每个实现都可以带来自己的附加特性。RTX5增加了几个函数用于 idle more,用于错误通知,用于特殊的系统定时器功能。它还使用宏来控制块和内存大小。
如果您需要在您的应用程序代码中的一些RTX特定函数,#include头文件rtx_os.h
超时值(Timeout Value)是几个osXxx函数的参数,允许有时间解析请求。超时值为0意味着RTOS不等待,函数立即返回,即使在没有资源可用的情况下也是如此。超时值为osWaitForever意味着RTOS无限等待,直到资源可用为止。或者使用不推荐的osThreadResume强制线程恢复。
超时值指定在时间延迟过期之前计时器滴答的数量。该值是一个上限,取决于自最后一个计时器滴答响以来实际经过的时间。
示例:
可以从线程和中断服务例程(ISR)中调用以下CMSIS-RTOS2函数:
如果那些不能在ISR中调用的函数在ISR里被调用了,他们会验证中断状态并返回状态码osErrorISR。在某些实现中,可以使用HARD_FAULT向量捕获此条件。
SVC(Supervisor Calls)管理程序调用,是针对软件和操作系统的异常,用于生成系统功能调用。它们有时被称为软件中断。例如,一个操作系统可能通过SVC提供对硬件的访问,而不是允许用户程序直接访问硬件。因此,当用户程序想要使用某些硬件时,它会使用SVC指令生成异常。操作系统中的软件异常处理程序执行并向用户应用程序提供请求的服务。这样,对硬件的访问就在操作系统的控制之下,操作系统可以防止用户应用程序直接访问硬件,从而提供更健壮的系统。
SVCs还可以使软件更加便携,因为用户应用程序不需要知道底层硬件的编程细节。用户程序只需要知道应用程序编程接口(API)的函数ID和参数;实际的硬件级编程是由设备驱动程序处理的。
SVCs在Arm Cortex-M内核的特权处理模式下运行。SVC函数接受参数并可以返回值。函数的使用方法与其他函数相同;但是,它们是通过SVC指令间接执行的。当执行SVC指令时,控制器切换到特权处理程序模式。
在此模式下不禁用中断。为了保护SVC函数不受中断的影响,您需要在代码中包含disable/enable内部函数_disable_irq()和_enable_irq()。
您可以使用SVC函数来访问受保护的外围设备,例如,配置NVIC和中断。如果您以非特权(受保护)模式运行线程,并且需要从线程内部更改中断,那么这是必需的。
要在你的Keil RTX5项目中实现SVC功能,你需要:
uint32_t svc_atomic_inc32 (uint32_t *mem) {
// A protected function to increment a counter.
uint32_t val;
__disable_irq();
val = *mem;
(*mem) = val + 1U;
__enable_irq();
return (val);
}
void * const osRtxUserSVC[1+USER_SVC_COUNT] = {
(void *)USER_SVC_COUNT,
(void *)svc_atomic_inc32,
};
#define USER_SVC_COUNT 1 // Number of user SVC functions
__STATIC_FORCEINLINE uint32_t atomic_inc32 (uint32_t *mem) {
register uint32_t val;
__ASM volatile (
"svc 1" : "=l" (val) : "l" (mem) : "cc", "memory"
);
return (val);
}
mdk5编译器v5版本:
uint32_t atomic_inc32 (uint32_t *mem) __svc(1);
注意:
1. SVC函数0保留给Keil RTX5内核。
2. 在给SVC函数编号时,不要留空白。它们必须占据从1开始的连续数字范围。
3. SVC功能仍然可以被中断。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。