freeRTOS源码解析3--port.c 3

接着上一篇继续解析。

2、port.c源码解析

2.6 vPortEndScheduler

这个函数的作用我还不清楚,暂时先按函数名字面意思来理解,就是用于结束调度器的。

1 void vPortEndScheduler( void )
2 {
3     /* Not implemented in ports where there is nothing to return to.
4      * Artificially force an assert. */
5     /* 在没有可返回的ports中未实现。人工强制断言。 */
6     configASSERT( uxCriticalNesting == 1000UL );
7 }

2.7 vPortEnterCritical

 1 #define portDISABLE_INTERRUPTS()                  vPortRaiseBASEPRI()
 2 #define portNVIC_INT_CTRL_REG     ( *( ( volatile uint32_t * ) 0xe000ed04 ) )
 3 /* Masks off all bits but the VECTACTIVE bits in the ICSR register. */
 4 /* 屏蔽所有bit位,除了ICSR寄存器里的VECTACTIVE的位 */
 5 #define portVECTACTIVE_MASK                   ( 0xFFUL )
 6 
 7 void vPortEnterCritical( void )
 8 {
 9     // 这个宏就是vPortRaiseBASEPRI,在前面的文章中已解析过了。
10     portDISABLE_INTERRUPTS();
11     // 在启动调度器的时候,该值初始化为0。
12     uxCriticalNesting++;
13 
14     /* This is not the interrupt safe version of the enter critical function so
15      * assert() if it is being called from an interrupt context.  Only API
16      * functions that end in "FromISR" can be used in an interrupt.  Only assert if
17      * the critical nesting count is 1 to protect against recursive calls if the
18      * assert function also uses a critical section. */
19     if( uxCriticalNesting == 1 )
20     {
21         // portNVIC_INT_CTRL_REG这个寄存器前面的文章也已描述过了。
22         // VECTACTIVE(bit0~9) & 0xFF非0的话,表明当前有中断正在处理。
23         // 这个函数不应该在中断中被调用,所以非0则assert。
24         configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
25     }
26 }
vPortEnterCritical

该函数用于进入临界区,freeRTOS进入临界区的方法很简单,就是屏蔽所有会调用系统调用的中断,并用一个变量uxCriticalNesting记录递归调用。

2.8 vPortExitCritical

 1 #define portENABLE_INTERRUPTS()                   vPortSetBASEPRI( 0 )
 2 
 3 void vPortExitCritical( void )
 4 {
 5     // 只有进入临界区(uxCriticalNesting必定大于0)了,才需要退出。
 6     configASSERT( uxCriticalNesting );
 7     uxCriticalNesting--;
 8 
 9     // 如果此时uxCriticalNesting非0,说明vPortEnterCritical存在递归调用,
10     // 还有其他任务或者内核在临界区中,此时不能开中断。
11     if( uxCriticalNesting == 0 )
12     {
13         portENABLE_INTERRUPTS();
14     }
15 }
vPortExitCritical

该函数用于退出临界区。

2.9 xPortPendSVHandler

下面的解析,去除了FPU相关的代码,下图为任务栈的图,当前任务保存现场之后和下一个就绪任务的现场部分应该是一样的。

 

 1 __asm void xPortPendSVHandler( void )
 2 {
 3     extern uxCriticalNesting;
 4     extern pxCurrentTCB;
 5     extern vTaskSwitchContext;
 6 
 7 /* *INDENT-OFF* */
 8     PRESERVE8
 9 
10     mrs r0, psp               // r0 = psp,psp是任务用的栈指针
11     isb
12     /* Get the location of the current TCB. */
13     ldr r3, =pxCurrentTCB
14     ldr r2, [ r3 ]            // r2 = pxCurrentTCB->pxTopOfStack
15 
16     /* Save the core registers. */
17     stmdb r0!, {r4-r11, r14}  // 将当前任务的r14,r4~r11入栈
18 
19     /* Save the new top of stack into the first member of the TCB. */
20     str r0, [ r2 ]            // 将r0的值写入pxCurrentTCB->pxTopOfStack
21 
22     stmdb sp!, {r0, r3}       // 因为在中断中,所以这里的sp值的是msp,保存r0和r3的值
23     mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
24     msr basepri, r0           // 屏蔽中断
25     dsb
26     isb
27     bl vTaskSwitchContext     // 找到下一个要运行的任务,并将pxCurrentTCB指向该任务的TCB
28     mov r0, #0
29     msr basepri, r0           // 启动中断
30     ldmia sp!, {r0, r3}       // 恢复r0和r3的值
31 
32     /* The first item in pxCurrentTCB is the task top of stack. */
33     ldr r1, [ r3 ]
34     ldr r0, [ r1 ]
35 
36     /* Pop the core registers. */
37     ldmia r0!, {r4-r11, r14}  // 将pxCurrentTCB的任务的现场恢复
38 
39     msr psp, r0               // 将pxCurrentTCB的任务的栈赋给psp
40     isb
41 
42     bx r14                    // 触发中断返回序列
43 /* *INDENT-ON* */
44 }
xPortPendSVHandler

该函数是PendSV中断的处理函数,是freeRTOS的核心函数之一,主要用于执行上下文切换,并将CPU让给下一个就绪任务。

2.10 xPortSysTickHandler

 1 void xPortSysTickHandler( void )
 2 {
 3     /* The SysTick runs at the lowest interrupt priority, so when this interrupt
 4      * executes all interrupts must be unmasked.  There is therefore no need to
 5      * save and then restore the interrupt mask value as its value is already
 6      * known - therefore the slightly faster vPortRaiseBASEPRI() function is used
 7      * in place of portSET_INTERRUPT_MASK_FROM_ISR(). */
 8     // 屏蔽中断。
 9     vPortRaiseBASEPRI();
10     {
11         /* Increment the RTOS tick. */
12         // 滴答时钟的处理函数,如果返回pdTRUE的话,则表示要执行一次调度。
13         if( xTaskIncrementTick() != pdFALSE )
14         {
15             /* A context switch is required.  Context switching is performed in
16              * the PendSV interrupt.  Pend the PendSV interrupt. */
17             // 触发PendSV中断,该中断处理函数,在上面已经解析过了。
18             portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
19         }
20     }
21 
22     // 开启所有中断
23     vPortClearBASEPRIFromISR();
24 }
xPortSysTickHandler

这是系统滴答时钟的中断处理函数,它每隔一定时间就会执行一次。

2.11 vPortSetupTimerInterrupt

 1     __weak void vPortSetupTimerInterrupt( void )
 2     {
 3         /* Calculate the constants required to configure the tick interrupt. */
 4         #if ( configUSE_TICKLESS_IDLE == 1 )
 5             {
 6                 ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ );
 7                 xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick;
 8                 ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ );
 9             }
10         #endif /* configUSE_TICKLESS_IDLE */
11 
12         /* Stop and clear the SysTick. */
13         portNVIC_SYSTICK_CTRL_REG = 0UL;
14         portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;
15 
16         /* Configure SysTick to interrupt at the requested rate. */
17         portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
18         portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
19     }
vPortSetupTimerInterrupt

这是配置滴答时钟的函数,根据手册配置即可,这里是配置成1ms中断。

2.12 vPortValidateInterruptPriority

 1 void vPortValidateInterruptPriority( void )
 2 {
 3     uint32_t ulCurrentInterrupt;
 4     uint8_t ucCurrentPriority;
 5 
 6     /* Obtain the number of the currently executing interrupt. */
 7     ulCurrentInterrupt = vPortGetIPSR();
 8 
 9     /* Is the interrupt number a user defined interrupt? */
10     if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER )
11     {
12         /* Look up the interrupt's priority. */
13         ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ];
14         configASSERT( ucCurrentPriority >= ucMaxSysCallPriority );
15     }
16 
17     configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue );
18 }
vPortValidateInterruptPriority

这是用于检查在中断中调用带有FromISR后缀的系统调用时,该中断优先级是否属于configMAX_SYSCALL_INTERRUPT_PRIORITY下的,这是因为freeRTOS屏蔽中断时只会屏蔽中断优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY的中断,而更高优先级的中断不会被屏蔽,所以如果更高优先级的中断中调用带有FromISR后缀的系统调用时,freeRTOS就可能无法正常运行。

具体解析这里就不做了,去看里面用到的寄存器的描述就懂了。

 

至此port.c的函数基本都过了一遍,现在已经了解了freeRTOS是如何进行任务切换的以及进入退出临界区的方式等等,到现在freeRTOS的基础函数都解析过了,它的调度、事件、队列等的处理都会用到里面的一个或几个函数或宏,后面开始freeRTOS的第二个核心:任务处理/调度。

posted @ 2022-10-02 15:06  freeManX1807  阅读(1132)  评论(0编辑  收藏  举报