头图

在FreeRTOS中,使用三个宏重写了中断,实现任务调度。

#define vPortSVCHandler     SVC_Handler
#define xPortPendSVHandler  PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

在使用STM32 HAL库时,需要在stm32f1xx_it.c文件中找到被重写的函数体,加上__weak,防止重定义。

__weak void SysTick_Handler(void){/*...*/}
__weak void PendSV_Handler(void){/*...*/}
__weak void SVC_Handler(void){/*...*/}

SVC_Handler

FreeRTOS中,TCB(任务控制块)是用来管理任务的关键结构体,它包含了管理和调度任务所需的所有信息。

typedef struct tskTaskControlBlock 
{
    volatile StackType_t * pxTopOfStack;
    ListItem_t xStateListItem;
    ListItem_t xEventListItem;
    UBaseType_t uxPriority;
    StackType_t * pxStack; 
    char pcTaskName[ configMAX_TASK_NAME_LEN ]; 
} tskTCB;

.

在《Cortex™-M3 Devices Generic User Guide》中关于SP和LR的描述。

Stack Pointer

The Stack Pointer (SP) is register R13. In Thread mode, bit[1] of the CONTROL register indicates the stack pointer to use:

  • 0 = Main Stack Pointer (MSP). This is the reset value.
  • 1 = Process Stack Pointer (PSP).

On reset, the processor loads the MSP with the value from address 0x00000000.

Link Register

The Link Register (LR) is register R14. It stores the return information for subroutines, function calls, and exceptions.

On reset, the processor sets the LR value to 0xFFFFFFFF.

.
SVC_Handler具体的汇编程序如下。

SVC_Handler
        ; 加载pxCurrentTCB地址到R3
        LDR      r3,[pc,#28] ; LDR R3, =pxCurrentTCB
        
        ; 加载当前任务的堆栈地址到R0
        LDR      r1,[r3,#0]
        LDR      r0,[r1,#0]
        
        ; 从R0指向的堆栈中恢复R4到R11的值,并更新堆栈指针R0
        LDM      r0!,{r4-r11}
        
        ; 将当前线程的堆栈指针更改为R0中的值。
        MSR      PSP,r0
        
        ; 指令同步屏障,确保之前的所有指令完成执行
        ISB
        
        ; BASEPRI 寄存器中的值存储的是最低的允许运行中断号
        ; MSR 指令将通用寄存器的内容移动到指定的特殊寄存器中
        ; 清除基础优先级寄存器BASEPRI,允许所有优先级的中断
        MOV      r0,#0
        MSR      BASEPRI,r0
        
        ; 通过设置LR[3:0]的值为0X0D,设置堆栈指针为PSP
        ORR      lr,lr,#0xd
        
        BX       lr

.

SysTick_Handler

SysTick_Handler是一个定时中断服务函数,默认为1ms触发一次。在FreeRTOS中,它被用作触发PendSV中断,实际的任务切换在PendSV_Handler函数中执行。

void xPortSysTickHandler( void )
{
    portDISABLE_INTERRUPTS();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* A context switch is required.*/
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portENABLE_INTERRUPTS();
}

.

PendSV_Handler

.
PendSV_Handler具体的汇编程序如下。

PendSV_Handler
        ; MSR 指令将特殊寄存器的内容移动到指定的通用寄存器中
        MRS      r0,PSP
        ; 指令同步屏障,确保之前的所有指令完成执行
        ISB      
        
        LDR      r3,[pc,#52] ; [0x8002020] = 0x2000024c
        LDR      r2,[r3,#0]
        
        STMDB    r0!,{r4-r11}
        STR      r0,[r2,#0]
        
        PUSH.W   {r3,lr}
        
        MOV      r0,#0x50
        MSR      BASEPRI,r0
        
        BL       vTaskSwitchContext
        MOV      r0,#0
        MSR      BASEPRI,r0
        POP      {r3,lr}
        
        LDR      r1,[r3,#0]
        LDR      r0,[r1,#0]
        LDM      r0!,{r4-r11}
        MSR      PSP,r0
        
        ; 指令同步屏障,确保之前的所有指令完成执行
        ISB      
        BX       lr

.

#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList )                                           \
    {                                                                                          \
        List_t * const pxConstList = ( pxList );                                               \
        /* Increment the index to the next item and return the item, ensuring */               \
        /* we don't return the marker used at the end of the list.  */                         \
        ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                           \
        if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \
        {                                                                                      \
            ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext;                       \
        }                                                                                      \
        ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner;                                         \
    }

#define taskSELECT_HIGHEST_PRIORITY_TASK()                                \
{                                                                         \
    UBaseType_t uxTopPriority = uxTopReadyPriority;                       \
                                                                          \
    /* Find the highest priority queue that contains ready tasks. */      \
    while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \
    {                                                                     \
        configASSERT( uxTopPriority );                                    \
        --uxTopPriority;                                                  \
    }                                                                     \
                                                                          \
    /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \
     * the  same priority get an equal share of the processor time. */                    \
    listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
    uxTopReadyPriority = uxTopPriority;                                                   \
}

void vTaskSwitchContext( void )
{
    if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
    {
        /* The scheduler is currently suspended - do not allow a context
         * switch. */
        xYieldPending = pdTRUE;
    }
    else
    {
        xYieldPending = pdFALSE;
        /* Select a new task to run using either the generic C or port
         * optimised asm code. */
        taskSELECT_HIGHEST_PRIORITY_TASK();
    }
}

.


step1nto
1 声望1 粉丝