#define portYIELD() \
{ \
/* Set a PendSV to request a context switch. */ \
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \
\
/* Barriers are normally not required but do ensure the code is completely \
within the specified behaviour for the architecture. */ \
__dsb( portSY_FULL_READ_WRITE ); \
__isb( portSY_FULL_READ_WRITE ); \
}
#define portNVIC_INT_CTRL_REG ( * ( ( volatile uint32_t * ) 0xe000ed04 ) )
#define portNVIC_PENDSVSET_BIT ( 1UL << 28UL )
void vPortEnterCritical( void )
{
portDISABLE_INTERRUPTS();
uxCriticalNesting++;
/* This is not the interrupt safe version of the enter critical function so
assert() if it is being called from an interrupt context. Only API
functions that end in "FromISR" can be used in an interrupt. Only assert if
the critical nesting count is 1 to protect against recursive calls if the
assert function also uses a critical section. */
if( uxCriticalNesting == 1 )
{
configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 );
}
}
static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
msr basepri, ulNewBASEPRI
dsb
isb
}
}
这是一段汇编代码,其实主要就是将configMAX_SYSCALL_INTERRUPT_PRIORITY这个宏赋值给basepri寄存器,我们先来看看basepri寄存器是干什么的,在CM3权威指南中有如下说明:
该寄存器被称为中断可屏蔽器,作用是用于屏蔽优先级低于某个数值的中断,从而保证对时间要求严格的任务能够按照预期来完成任务。并且上表中的所有寄存器是CM3的特殊功能寄存器,只能由指令MSR\MRS访问,所以代码中的msr指令可以理解为专门为这些特殊功能寄存器设置的赋值命令。那么如何正确的赋值才能达到我们预期的屏蔽效果呢?这是一个比较复杂的问题,下面一步一步来分析。
要解决这个问题,就必须要从中断优先级分组说起,CM3中划分为两种优先级,一种是抢占优先级,另一种是子优先级(或响应优先级)。CM3支持高达256级可编程优先级,相应地由一个8位的寄存器用于表达这256级的优先级。但是CM最多支持128级抢占优先级,而不是256级,这是因为内核在出厂时厂家就规定了表达抢占优先级的位只能在[7:1]这个区间内,因此抢占优先级最高为128级,相应优先级最高为256级,这是CM3中的优先级定义。下图是优先级分组表。
但是单片机在设计的时候并不会将这256个优先级全部使用,因为如果全部使用的话芯片架构将会变得很复杂,因此,stm32仅仅拿出了这8位中的高4位来表达它的优先级,低4位则无效。因此,stm32最高支持16级的优先级。那么如何来确定优先级的高低呢?实际上这同使用库函数编程的时候判断方法是相同的,因stm32为例,假定分组位置为3,全部设置为抢占优先级,如果往寄存器中写入0001 xxxx表达的优先级要比往寄存器中写入0011 xxxx表达的优先级高。知道了这些以外还需要明确一点的是,设置BASEPRI寄存器屏蔽优先级的方法和设置中断优先级的方法是相同的,因为这里只有4位来表示中断优先级,因此要屏蔽优先级不高于5的中断(注意CM3内核中始终认为优先级数字为0的那一个是最高优先级,优先级数字越大优先级越低),只需要以下指令即可:
到这里们就知道了CM3中断优先级分组和设置中断优先级以及屏蔽中断优先级的原则了,那么回到代码中继续分析宏configMAX_SYSCALL_INTERRUPT_PRIORITY
其定义如下:
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configPRIO_BITS __NVIC_PRIO_BITS
#define __NVIC_PRIO_BITS 4
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configPRIO_BITS __NVIC_PRIO_BITS
#define __NVIC_PRIO_BITS 4
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
portNVIC_SYSPRI2_REG |= portNVIC_PENDSV_PRI;
portNVIC_SYSPRI2_REG |= portNVIC_SYSTICK_PRI;
portNVIC_SYSPRI2_REG定义如下:
#define portNVIC_SYSPRI2_REG ( * ( ( volatile uint32_t * ) 0xe000ed20 ) )
#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
void vPortExitCritical( void )
{
configASSERT( uxCriticalNesting );
uxCriticalNesting--;
if( uxCriticalNesting == 0 )
{
portENABLE_INTERRUPTS();
}
}
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
{
__asm
{
/* Barrier instructions are not used as this function is only used to
lower the BASEPRI value. */
msr basepri, ulBASEPRI
}
}
static portFORCE_INLINE uint32_t ulPortRaiseBASEPRI( void )
{
uint32_t ulReturn, ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
mrs ulReturn, basepri
msr basepri, ulNewBASEPRI
dsb
isb
}
return ulReturn;
}
void vTaskSuspendAll( void )
{
++uxSchedulerSuspended;
}
BaseType_t xTaskIncrementTick( void )
{
···
traceTASK_INCREMENT_TICK( xTickCount );
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* Minor optimisation. The tick count cannot change in this
block. */
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
if( xConstTickCount == ( TickType_t ) 0U )
{
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
···
}
···
}
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
switch. */
xYieldPending = pdTRUE;
}
else
{
xYieldPending = pdFALSE;
traceTASK_SWITCHED_OUT();
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime );
#else
ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif
if( ulTotalRunTime > ulTaskSwitchedInTime )
{
pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
ulTaskSwitchedInTime = ulTotalRunTime;
}
#endif /* configGENERATE_RUN_TIME_STATS */
/* Check for stack overflow, if configured. */
taskCHECK_FOR_STACK_OVERFLOW();
/* Select a new task to run using either the generic C or port
optimised asm code. */
taskSELECT_HIGHEST_PRIORITY_TASK();
traceTASK_SWITCHED_IN();
#if ( configUSE_NEWLIB_REENTRANT == 1 )
{
/* Switch Newlib's _impure_ptr variable to point to the _reent
structure specific to this task. */
_impure_ptr = &( pxCurrentTCB->xNewLib_reent );
}
#endif /* configUSE_NEWLIB_REENTRANT */
}
}
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;
/* If uxSchedulerSuspended is zero then this function does not match a
previous call to vTaskSuspendAll(). */
configASSERT( uxSchedulerSuspended );
taskENTER_CRITICAL();
/*************** 第一部分 *****************/
{
--uxSchedulerSuspended;----(1)
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )----(2)
{
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )----(3)
{
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )----(4)
{
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) );----(5)
( void ) uxListRemove( &( pxTCB->xEventListItem ) );----(6)
( void ) uxListRemove( &( pxTCB->xStateListItem ) );----(7)
prvAddTaskToReadyList( pxTCB );----(8)
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )----(9)
{
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
if( pxTCB != NULL )----(10)
{
prvResetNextTaskUnblockTime();
}
/*************** 第二部分 *****************/
{
UBaseType_t uxPendedCounts = uxPendedTicks; /* Non-volatile copy. */
if( uxPendedCounts > ( UBaseType_t ) 0U )----(11)
{
do
{
if( xTaskIncrementTick() != pdFALSE )----(12)
{
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
--uxPendedCounts;----(13)
} while( uxPendedCounts > ( UBaseType_t ) 0U );----(14)
uxPendedTicks = 0;----(15)
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
if( xYieldPending != pdFALSE )----(16)
{
#if( configUSE_PREEMPTION != 0 )
{
xAlreadyYielded = pdTRUE;
}
#endif
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
taskEXIT_CRITICAL();
return xAlreadyYielded;
}
到这里我们就将FreeRTOS的一些内核控制函数讲述完了,FreeRTOS内核控制函数相对来说比较难,而且不好理解,关系错综复杂。