1. SVC介绍
#define vPortSVCHandler SVC_Handler
SVC(Supervisor Call) ,也称为SVC模式或SVC异常。在ARM架构中,SVC是一种处理器异常,它允许应用程序代码请求操作系统(OS)内核服务。当SVC异常被触发时,处理器会切换到SVC模式,并执行一个特定的异常处理函数(通常是SVC异常处理程序),该函数会处理来自用户模式的请求。
SVC通常用于执行系统调用、上下文切换、线程调度等任务。它是同步的,意味着在SVC异常处理期间,处理器不会响应其他中断或异常。
操作系统不让用户程序直接访问硬件,而是通过提供一些系统服务函数,用户程序使用SVC发出对系统服务函数的呼叫请求,以这种方式调用他们来间接访问硬件(提出要求—得到满足)。因此,当用户程序想要控制特定的硬件时,他就会产生一个SVC异常,然后操作系统提供的SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务
(用户程序—OS—硬件),通过SVC程序,让用户程序变得与硬件无关,因此在开发应用程序时无需了解硬件的操作细节,方便应用程序跨平台移植,
2. SVC使用
该例子介绍了 在函数prvStartFirstTask中调用svc 0,通过vPortSVCHandler处理,然后运行系统的第一个任务-IdleTask
2.1 SVC
在RTOS中,通常会定义一系列的服务号(SVC number),用于区分不同的SVC调用。例如,服务号0可能用于启动第一个任务,服务号1可能用于其他系统调用等。
/* Call SVC to start the first task. */
svc 0
具体调用如下:
vTaskStartScheduler();
---xPortStartScheduler();
------prvStartFirstTask();//开始第一个任务
__asm void prvStartFirstTask( void )
{
/* *INDENT-OFF* */
PRESERVE8
/* Use the NVIC offset register to locate the stack. */
ldr r0, =0xE000ED08 //0xE000ED08 ; 向量表偏移量寄存器的地址
ldr r0, [ r0 ]
ldr r0, [ r0 ]
/* Set the msp back to the start of the stack. */
msr msp, r0
/* Globally enable interrupts. */
cpsie i
cpsie f
dsb
isb
/* Call SVC to start the first task. */
svc 0
nop
nop
/* *INDENT-ON* */
}
0xE000ED08 ; 向量表偏移量寄存器的地址,VTOR寄存器存放的是中断向量表的起始地址
svc 0之后的动作将会交给vPortSVCHandler函数处理
2.2 vPortSVCHandler 函数
vPortSVCHandler 函数是SVC异常的处理函数。
__asm void vPortSVCHandler( void )
{
/* *INDENT-OFF* */
PRESERVE8
ldr r3, = pxCurrentTCB /* Restore the context. */
ldr r1, [ r3 ] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0 !, { r4 - r11 } /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
msr psp, r0 /* Restore the task stack pointer. */
isb
mov r0, # 0
msr basepri, r0
orr r14, # 0xd
bx r14
/* *INDENT-ON* */
}
pxCurrentTCB指向当前执行的任务,函数prvAddNewTaskToReadyList将任务加到任务队列中去,
prvAddNewTaskToReadyList函数被xTaskCreate调用,而函数vTaskStartScheduler执行时便会创建系统的第一个任务—idle task,
typedef tskTCB TCB_t;
portDONT_DISCARD PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;
static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )
{
taskENTER_CRITICAL();
{
uxCurrentNumberOfTasks++;
if( pxCurrentTCB == NULL )
{
pxCurrentTCB = pxNewTCB;