所有现代微处理器和微控制器都包含某种中断机制。 这些机制是提供许多应用程序所需的响应能力所必需的。 当然,使用RTOS时,响应性和可预测性是主要目标,但同时又彼此相对。 使用中断会破坏实时操作系统的完整性。 在上一篇文章(第4和第6条)中提到了此问题及其解决方案。 在本文中,我们将研究Nucleus SE中使用的中断处理策略。 在所有情况下,中断都不是由Nucleus SE控制的:当中断按照优先级和向量发生时,会以通常的方式进行处理。 它们的执行时间只是从主应用程序和调度程序的代码中的可用时间中“窃取”。 显然,所有的中断处理程序都应该简单,简短和快速。

定期和受控中断
Nucleus SE提供了两种处理中断的方法:“本机”或“常规”(本机),其中中断没有什么特别的,并且在某种程度上与操作系统的交互作用有限(至少在使用优先级调度程序时);托管,您可以在其中从中断处理程序访问大量的API调用。
使用I / O宏,Nucleus SE中断处理程序可以在标准或托管模式下使用。
员工中断
Nucleus SE Staff中断是标准的中断处理程序,可以视为“不受管理”。 通常在中断可能发生的频率很高且需要处理而对计算资源的使用较少的情况下使用它们。 这种处理程序很可能是用C编写的,因为许多现代的嵌入式编译器都支持使用interrupt关键字来开发中断处理程序。 仅存储编译器认为必要的上下文信息。 这将导致对标准中断处理程序可以执行的操作的重大限制,我们很快就会看到。
要在Nucleus SE中创建常规中断处理程序,只需编写常规中断处理程序就足够了,包括在开头调用
宏NUSE_NISR_Enter() ,在末尾调用
NUSE_NISR_Exit() 。 这些宏在
nuse_types文件中定义
。 h并将全局变量
NUSE_Task_State设置为
NUSE_NISR_CONTEXT 。
引导式中断
如果您需要更大的灵活性来处理中断处理程序,则可以使用Nucleus SE管理的中断。 与标准中断的主要区别在于保持上下文。 受控中断不会使编译器在堆栈上保存多个寄存器,而是在输入处保存任务的整个上下文(在其自己的上下文块中)。 然后,从输出的上下文块中还原当前任务的上下文。 这提供了通过中断处理程序代码来更改当前任务的能力,这在使用优先级调度程序时是可能的。 上一篇文章(
#10 )中提供了Nucleus SE中上下文保存和恢复的完整说明。
显然,与标准中断发生的情况相比,上下文的完全保留与使用多个寄存器的堆栈中的存储相比,增加了计算资源的使用。 必须为此付出额外的灵活性,这就是提供中断处理方法选择的原因。
使用
nuse_types.h中描述的
NUSE_MANAGED_ISR()宏来构建托管中断。 该宏创建一个包含以下操作的函数:
- 维持任务的内容;
- 将NUSE_Task_State分配给 NUSE_MISR_CONTEXT ;
- 用户提供的中断处理程序功能代码;
- 将NUSE_Task_State恢复到以前的状态;
- 恢复任务的上下文。
宏具有两个参数:用作生成的处理程序的函数名称的中断名称,以及包含中断处理程序的用户逻辑的函数的名称。
来自中断处理程序的API调用
可以从标准或托管中断处理程序中调用的API函数集取决于所使用的调度程序。 一般而言,使用优先级调度程序提供了许多选项,用于通过API函数调用来访问调度程序,这在使用标准中断处理程序时很困难。
使用优先级调度程序时,对标准中断处理程序的API调用使用优先级调度程序时,允许从标准中断处理程序中进行有限数量的API函数调用。 此限制是Nucleus SE API灵活性的结果:许多调用可能导致任务准备就绪,并且标准中断处理程序可能不会调用调度程序(因为未保存任务上下文)。 关闭任务锁可提供更大的灵活性。
始终允许以下API调用:
NUSE_Task_Current() NUSE_Task_Check_Stack() NUSE_Task_Information() NUSE_Task_Count() NUSE_Partition_Pool_Information() NUSE_Partition_Pool_Count() NUSE_Mailbox_Information() NUSE_Mailbox_Count() NUSE_Queue_Information() NUSE_Queue_Count() NUSE_Pipe_Information() NUSE_Pipe_Count() NUSE_Semaphore_Information() NUSE_Semaphore_Count() NUSE_Event_Group_Information() NUSE_Event_Group_Count() NUSE_Signals_Send() NUSE_Timer_Control() NUSE_Timer_Get_Remaining() NUSE_Timer_Reset() NUSE_Timer_Information() NUSE_Timer_Count() NUSE_Clock_Set() NUSE_Clock_Retrieve() NUSE_Release_Information()
但是,只有
NUSE_Signals_Send()对它们有用,因为它提供了一种方便的方法来向任务指示需要采取某些措施。
如果禁用了锁定,也就是说,许多API调用无法将任务置于就绪状态,那么其他API调用将变为可用:
NUSE_Partition_Allocate() NUSE_Partition_Deallocate() NUSE_Mailbox_Send() NUSE_Mailbox_Receive() NUSE_Mailbox_Reset() NUSE_Queue_Send() NUSE_Queue_Receive() NUSE_Queue_Jam() NUSE_Queue_Reset() NUSE_Pipe_Send() NUSE_Pipe_Receive() NUSE_Pipe_Jam() NUSE_Pipe_Reset() NUSE_Semaphore_Obtain() NUSE_Semaphore_Release() NUSE_Semaphore_Reset() NUSE_Event_Group_Set() NUSE_Event_Group_Retrieve()
某些API调用始终是标准中断处理程序无法访问的,因为它们不可避免地需要调度程序的工作:
NUSE_Task_Suspend() NUSE_Task_Resume() NUSE_Task_Sleep() NUSE_Task_Relinquish() NUSE_Task_Reset() NUSE_Signals_Receive()
使用优先级调度程序以外的任何调度程序时,对托管中断处理程序或标准中断处理程序的API调用使用“运行至完成”,“循环调度”或“时间片”调度程序时,可以从中断处理程序中调用更多API函数。 如果使用优先级调度程序,则托管中断处理程序具有一组相似的功能。 这是因为允许调用,这可能导致安排另一个任务。
NUSE_Reschedule()代码提供了此功能,该代码在中断处理程序中检测调用上下文并抑制上下文更改(允许它在中断处理程序的末尾发生)。 在上一篇文章(
第9篇)中对调度程序的工作进行了全面分析。
关键要求是中断处理程序内部的API调用不应导致当前任务的挂起,例如,等待释放资源。
换句话说,必须使用
NUSE_NO_SUSPEND暂停
选项进行此类调用。
考虑到这一点,可以使用以下API调用:
NUSE_Task_Current() NUSE_Task_Check_Stack() NUSE_Task_Information() NUSE_Task_Count() NUSE_Task_Suspend() NUSE_Task_Resume() NUSE_Task_Reset() NUSE_Partition_Allocate() NUSE_Partition_Deallocate() NUSE_Partition_Pool_Information() NUSE_Partition_Pool_Count() NUSE_Mailbox_Send() NUSE_Mailbox_Receive() NUSE_Mailbox_Reset() NUSE_Mailbox_Information() NUSE_Mailbox_Count() NUSE_Queue_Send() NUSE_Queue_Receive() NUSE_Queue_Jam() NUSE_Queue_Reset() NUSE_Queue_Information() NUSE_Queue_Count() NUSE_Pipe_Send() NUSE_Pipe_Receive() NUSE_Pipe_Jam() NUSE_Pipe_Reset() NUSE_Pipe_Information() NUSE_Pipe_Count() NUSE_Semaphore_Obtain() NUSE_Semaphore_Release() NUSE_Semaphore_Reset() NUSE_Semaphore_Information() NUSE_Semaphore_Count() NUSE_Event_Group_Set() NUSE_Event_Group_Retrieve() NUSE_Event_Group_Information() NUSE_Event_Group_Count() NUSE_Signals_Send() NUSE_Timer_Control() NUSE_Timer_Get_Remaining() NUSE_Timer_Reset() NUSE_Timer_Information() NUSE_Timer_Count() NUSE_Clock_Set() NUSE_Clock_Retrieve() NUSE_Release_Information()
某些呼叫始终被禁止,因为它们与当前任务直接相关:
NUSE_Task_Relinquish() NUSE_Signals_Receive() NUSE_Task_Sleep()
实时时钟中断处理程序
实时时钟(RTC)中断处理程序是Nucleus SE中唯一完整的中断处理程序。 除了提供Nucleus SE中时间管理的所有必要功能外,它还用作编写托管中断处理程序的示例。
RTC中断处理程序操作
先前的文章之一列出了RTC中断处理程序提供的功能,其中涉及Nucleus SE(
#27 )中的系统时间这一广泛主题。 所描述的功能是可选的,具体取决于应用程序的配置。
以下是完整的RTC中断处理程序代码。 #if NUSE_TIMER_NUMBER != 0 { U8 timer; for (timer=0; timer<NUSE_TIMER_NUMBER; timer++) { if (NUSE_Timer_Status[timer]) { if (--NUSE_Timer_Value[timer] == 0) { NUSE_Timer_Expirations_Counter[timer]++; #if NUSE_TIMER_EXPIRATION_ROUTINE_SUPPORT || NUSE_INCLUDE_EVERYTHING if (NUSE_Timer_Expiration_Routine_Address[timer] != NULL) { ((PF1)NUSE_Timer_Expiration_Routine_Address[timer]) NUSE_Timer_Expiration_Routine_Parameter[timer]); } #endif /* reschedule? */ if (NUSE_Timer_Reschedule_Time[timer] != 0) { /* yes: set up time */ NUSE_Timer_Value[timer] = NUSE_Timer_Reschedule_Time[timer]; } else { /* no: disable */ NUSE_Timer_Status[timer] = FALSE; } } } } } #endif #if NUSE_SYSTEM_TIME_SUPPORT || NUSE_INCLUDE_EVERYTHING NUSE_Tick_Clock++; #endif #if NUSE_TASK_SLEEP || NUSE_INCLUDE_EVERYTHING { U8 task; for (task=0; task<NUSE_TASK_NUMBER; task++) { if (NUSE_Task_Timeout_Counter[task] != 0) { NUSE_Task_Timeout_Counter[task]--; if (NUSE_Task_Timeout_Counter[task] == 0) { NUSE_Wake_Task(task); } } } } #endif #if NUSE_SCHEDULER_TYPE == NUSE_TIME_SLICE_SCHEDULER if (--NUSE_Time_Slice_Ticks == 0) { NUSE_Reschedule(); } #endif
接下来,我们看一下RTC中断处理程序的四个主要功能区域。
计时器如果配置了应用程序计时器,则中断处理程序将进入循环以通过将其计数器减1来处理每个计时器。如果计时器结束计数(即计数器达到0),则可以执行两个操作:
- 如果配置了计时器完成处理程序并且计时器具有正确的(非NULL )函数指针(在NUSE_Timer_Expiration_Routine_Address []中 ),则通过从NUSE_Timer_Expiration_Routine_Parameter []中获取一个参数来执行该处理程序;
- 如果将计时器配置为在完成后初始化(即NUSE_Timer_Reschedule_Time []具有非零值),则计时器将使用该值重新加载。
上一篇文章(#28)中详细描述了应用程序计时器。
系统时钟如果配置了系统计时器,则
NUSE_Tick_Cloc k的值仅增加1。有关更多信息,请参见第28条。
暂停任务(任务睡眠)如果启用了对暂停任务的支持(即,配置了
NUSE_Task_Sleep() API调用),则会检查每个任务的超时计数器(
NUSE_Task_Timeout_Counter []中的值),如果不等于零,则减少1。如果达到零,则恢复对应的任务。
时间片调度如果使用了时间片调度程序,则调度程序计数器(
NUSE_Time_Slice_Ticks )会减少。 如果达到零,则调用调度程序。 对
NUSE_Reschedule()的调用负责重置计数器。
控制中断
必须解释为什么RTC中断处理程序是可控的,因为在某些情况下,用户可能决定将其重写为标准中断,以减少对计算资源的使用。 例如,如果仅使用一个系统时间功能(即,没有应用程序计时器,没有任务挂起,也没有时间片调度程序),则定期中断将起作用。 在以下情况下需要引导中断:
- 如果使用了计时器,并且配置了完成计时器,因为这些处理器可以(从中断上下文中)进行API调用,这将导致新的调度。 它们具有与从中断处理程序进行的API调用相同的限制(请参见本文前面的内容)。
- 如果使用优先级调度程序,则完成任务挂起可能需要唤醒具有更高优先级的任务;
- 如果使用了时间片调度程序,则会从RTC中断处理程序中调用它,因此,需要一个受控中断。
兼容Nucleus RTOS
由于Nucleus SE中断的实现与Nucleus RTOS完全不同,因此您不应期望在这方面具有兼容性。 Nucleus RTOS具有标准/低级/高级中断方案,这有点类似于Nucleus SE中的标准/受控中断方案。
底层和高层中断处理程序
低级中断处理程序低级中断服务例程(LISR)的执行方式与常规处理程序相同,包括使用当前堆栈。 Nucleus RTOS会一直保持上下文,直到调用低级中断处理程序为止,并在处理程序完成后恢复上下文。 因此,低级中断处理程序可以用C编写,并且可以用C调用其他处理程序。但是,低级处理程序仅可使用少数Nucleus RTOS服务。 如果中断处理需要其他Nucleus RTOS服务,则需要激活一个高级中断处理程序。 Nucleus RTOS支持使用多个低级中断处理程序。
高级中断处理程序高级别中断服务例程(HISR)是动态创建和删除的。 每个高级处理器都有自己的堆栈空间和自己的控制单元。 内存由应用程序分配。 并且,当然,必须先创建一个高级中断处理程序,然后才能激活它。
由于高级中断处理程序具有自己的堆栈和控制单元,因此如果尝试访问当前正在使用的Nucleus RTOS数据结构,则可以将其暂时阻止。
高级中断处理程序可以使用三个优先级。 如果在具有较低优先级的处理程序的工作期间激活了具有较高优先级的较高级别的处理程序,则将在任务完成时执行具有较低优先级的处理程序。 具有相同优先级的高级中断处理程序将按照其激活顺序执行。 必须先完成所有激活的高级中断处理程序,然后才能继续以正常模式安排任务。
Nucleus RTOS API实用程序要求中断
Nucleus RTOS有几个API调用来支持中断。 它们都没有在Nucleus SE中实现。
对于标准中断,API调用提供以下功能:
- 控制(激活/停用)中断(本地和全局);
- 设置中断向量。
对于低级中断:
对于高级中断:
- 创建/消除高级中断;
- 高级别中断激活;
- 获取应用程序中(目前)的高级中断数;
- 获取指向所有高级中断控制单元的指针;
- 获取指向当前高级中断控制单元的指针;
- 获取高级中断信息。
全局中断控制无论任务如何,此调用都会激活或取消激活中断。 因此,此调用取消激活的中断将保持不变,直到通过重用此调用将其激活为止。
服务电话原型:
INT NU_Control_Interrupts (INT new_level);
参数:
new_level-系统的新中断级别。 它始终可以取值
NU_DISABLE_INTERRUPTS (取消激活所有中断)和
NU_ENABLE_INTERRUPTS (激活所有中断)。 根据体系结构,其他值可能可用。
返回值:
该服务调用返回先前级别的已激活中断。
本地中断控制通过此服务调用,您可以根据任务激活或取消激活中断。 该调用将状态寄存器更改为指定的值。 下次更改上下文时,状态寄存器将返回上一次对
NU_Control_Interrupts()的调用所指定的值。
服务电话原型:
INT NU_Local_Control_Interrupts (INT new_level);
参数:
new_level-当前任务的新中断级别。 它始终可以取值
NU_DISABLE_INTERRUPTS (取消激活所有中断)和
NU_ENABLE_INTERRUPTS (激活所有中断)。 根据体系结构,其他值可能可用。
返回值:
该服务调用返回先前级别的已激活中断。
设置中断向量该开销将替换由中断处理程序控制的向量所指定的中断向量。
服务电话原型:
VOID *NU_Setup_Vector (INT vector, VOID *new);
参数:
vector-要为其注册中断的中断向量;
new是为向量编写的中断处理程序。
返回值:
该实用程序调用返回指向先前为中断向量注册的中断处理程序的指针。
低级中断记录此开销使用中断向量调用低级中断处理程序的功能。 系统上下文将在调用指定的低级中断处理程序之前自动保存,并在中断处理程序完成后恢复。
服务电话原型:
STATUS NU_Register_LISR (INT vector, VOID (*lisr_entry) (INT), VOID (**old_lisr) (INT);
参数:
vector-要为其注册中断的中断向量;
lisr_entry-将为向量注册的函数,值
NU_NULL将清除向量;
old_lisr是先前为指定向量注册的函数。
返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_VECTOR-无效向量;
NU_NOT_Rector-由于尚未在l
isr_entry中指定取消注册,因此此向量尚未注册。
NO_MORE_LISRS-已达到注册的低级中断处理程序的最大数量。
创建一个高级中断处理程序该实用程序调用将创建一个高级中断处理程序。
服务电话原型:
STATUS NU_Create_HISR (NU_HISR *hisr, CHAR *name, VOID (*hisr_entry) (VOID), OPTION priority, VOID *stack_pointer, UNSIGNED stack_size);
参数:
hisr-指向用户为高级中断处理程序提供的控制块的指针;
name-指向以零结尾的高级中断处理程序的7个字符的名称的指针;
hisr_entry-高级中断处理程序函数的入口;
优先级 -高级中断处理程序(0-2)具有三个优先级; 优先级0为最高;
stack_pointer-指向高级中断处理程序的堆栈区域的指针;
stack_size-高级中断处理程序的堆栈中的字节数。
返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_HISR-指向高级中断处理程序(
NULL )或控制单元的控制单元的空指针;
NU_INVALID_ENTRY-指向高级中断处理程序(
NULL )入口点的空指针;
NU_INVALID_PRIORITY-高级中断处理程序的优先级不正确;
NU_INVALID_MEMORY-无效的堆栈指针;
NU_INVALID_SIZE-堆栈大小太小。
删除高级中断处理程序该实用程序调用将删除先前创建的高级中断处理程序。
服务电话原型:
STATUS NU_Delete_HISR (NU_HISR *hisr);
参数:
hisr是指向用户提供的高级中断处理程序的控制块的指针。
返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_HISR-指向高级中断处理程序的
无效指针。
激活高级中断处理程序该实用程序调用将激活高级中断处理程序。 如果当前正在执行指定的高级中断处理程序,则在处理程序停止工作之前,不会执行激活请求。 一个高级中断处理程序针对每个激活请求运行一次。
服务电话原型:
STATUS NU_Activate_HISR (NU_HISR *hisr);
参数:
hisr是指向高级中断处理程序的控制块的指针。
返回值:
NU_SUCCESS-调用成功完成;
NU_INVALID_HISR-指向高级中断处理程序的控制单元的无效指针。
获取系统中高级中断处理程序的数量该实用程序调用返回已安装的高级中断处理程序的数量。 所有创建的高级中断处理程序都被视为已安装。 远程高级中断处理程序不被视为已安装。
服务电话原型:
UNSIGNED NU_Established_HISRs(VOID);
参数:
缺席。
返回值:
该实用程序调用返回系统中已安装的高级中断处理程序的数量。
获取指向高级中断处理程序的控制块的指针该服务调用形成了指向系统中安装的所有高级中断处理程序的指针的顺序列表。
服务电话原型:
UNSIGNED NU_HISR_Pointers(NU_HISR **pointer_list, UNSIGNED maximum_pointers);
参数:
pointer_list-指针数组
NU_HISR的指针; 该数组将填充指向系统中安装的高级中断处理程序的指针;
maximum_pointers-可以放置在数组中的
NU_HISR指针的最大数量; 它通常等于
pointer_list数组的大小。
返回值:
该实用程序调用返回系统中活动的高级中断处理程序的数量。
获取指向当前高级中断处理程序的指针该实用程序调用返回一个指向当前正在执行的高级中断处理程序的指针。
服务电话原型:
NU_HISR *NU_Current_HISR_Pointer(VOID);
参数:
缺席。
返回值:
该服务调用返回一个指向当前正在执行的高级中断处理程序的控制单元的指针。 如果非高级中断处理程序调用此函数,
则返回
NU_NULL 。
获取有关高级中断处理程序的信息该实用程序调用返回有关指定的高级中断处理程序的各种信息。
服务电话原型:
STATUS NU_HISR_Information(NU_HISR *hisr, char *name, UNSIGNED *scheduled_count, DATA_ELEMENT *priority, VOID **stack_base, UNSIGNED *stack_size, UNSIGNED *minimum_stack);
参数:
hisr-指向高级中断处理程序的指针;
name-指向高级中断处理程序名称的8个字符区域的指针,包括终止的零;
schedule_count-指向该高级中断处理程序被调度的总次数的变量的指针;
优先级 -指向用于存储高级中断处理程序的优先级的变量的指针;
stack_base-指向存储原始堆栈指针的指针; 这与创建高级中断处理程序时传递的指针相同;
stack_size-指向用于存储高级中断处理程序的总堆栈大小的变量的指针;
minimum_stack – , .
:
NU_SUCCESS – ;
NU_INVALID_HISR – .
API
APINucleus RTOS:
NU_Activate_HISR() NU_Local_Control_Interrupts() NU_Current_HISR_Pointer() NU_Current_Task_Pointer() NU_Retrieve_Clock()
APINucleus RTOS, -, Nucleus RTOS,
NU_NO_SUSPEND .
Nucleus SE.
: , . — Mentor Embedded ( Mentor Graphics). , . .
, e-mail: colin_walls@mentor.com.