最近学习白问网韦东山老师在B站开源的freeRTOS课程,网址:韦东山直播公开课:RTOS实战项目之实现多任务系统 第1节:裸机程序框架和缺陷_哔哩哔哩_bilibili和7天物联网训练营【第2期】7天物联网智能家居实战训练营
在学习过程中按照韦老师的方法分析了下freeRTOS源码,如果有不对的地方请指证。
freeRTOS滴答时钟相关源码分析,基于cubemx生成的freeRTOS工程
xPortStartScheduler
vPortSetupTimerInterrupt
void vPortSetupTimerInterrupt( void )
{
/*
1、操作系统,是针对寄存器操作---效率高
2、首先赋值装载寄存器值 = (CPU频率/配置周期)-1
2.1、HAL_RCC_ClockConfig() cpu频率实在硬件启动时,就已经获取了
2.2、configTICK_RATE_HZ = 1000 是由cubemx配置而得
3、配置控制寄存器
3.1、开启时钟源
3.2、使能中断
3.3、使能systick
4、可以参考,M4权威指南 9.5章节----systick定时器
5、configTICK_RATE_HZ
5.1、建议不要小于1ms
5.2、建议以整数倍赋值 10ms 100ms 1000ms 便于计算管理
/* Configure SysTick to interrupt at the requested rate. */
portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;
portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}
xPortSysTickHandler
xTaskIncrementTick
void xPortSysTickHandler( void )
{
/*关闭中断,不让中断打断systick中断服务,就是进入临界段*/
vPortRaiseBASEPRI();
{
操作系统调度接口
如果调度器返回true,触发pendSV异常
if( xTaskIncrementTick() != pdFALSE )
{
触发pendSV异常
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
清除中断屏蔽,打开中断,执行pendSV异常
vPortClearBASEPRIFromISR();
}
//SysTick任务调度
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
//返回值,表示是否进行上下文切换
BaseType_t xSwitchRequired = pdFALSE;
traceTASK_INCREMENT_TICK( xTickCount );
/*内核调度器是否有挂起,pdFALSE表示内核调度器没有挂起*/
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
/* tick计数值+1 */
const TickType_t xConstTickCount = xTickCount + 1;
xTickCount = xConstTickCount;
//判断tick是否溢出越界
if( xConstTickCount == ( TickType_t ) 0U )
{
//如果溢出,要更新延时列表
taskSWITCH_DELAYED_LISTS();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/*
1、当前节拍大于时间片的锁定时间
2、就说明,有任务需要进行调度了,时间片用完了
*/
if( xConstTickCount >= xNextTaskUnblockTime )
{
for( ;; )//会一直遍历整个任务延时列表,主要目的是,找到时间片最短的任务,进行切换
{
//1、判断任务延时列表中,是否为空,也就是说,有没有任务在等待调度
if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE )
{
//如果没有任务等待,把时间片赋值为最大值,不再调度
xNextTaskUnblockTime = portMAX_DELAY;
break;
}
else
{
/*
1、从任务延时列表中,获取第一个任务控制块
1、延时列表,插入永远是把时间片最短的任务,放在第一个
2、获取任务控制块的延时时间
*/
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList );
xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) );
//再次判断,这个任务的时间片是否到达
if( xConstTickCount < xItemValue )
{
/* 没有到达,把此任务的时间片更新为当前系统的时间片 */
xNextTaskUnblockTime = xItemValue;
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 把任务从延时列表中移除 */
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
/* 把任务从事件列表中移除 */
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* 把任务添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
/* 抢占式处理 */
#if ( configUSE_PREEMPTION == 1 )
{
/*
1、判断优先级是否大于当前任务
1.1、大于则进行调度
*/
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
}
}
}
/* 时间片处理机制 */
#if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) )
{
//1、获取就绪列表长度
1.1、就绪列表指的是,当前任务优先级的列表
1.2、如果有其他任务在就绪列表中,就开始调度
if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif
#if ( configUSE_TICK_HOOK == 1 )
{
/* Guard against the tick hook being called when the pended tick
count is being unwound (when the scheduler is being unlocked). */
if( uxPendedTicks == ( UBaseType_t ) 0U )
{
vApplicationTickHook();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_TICK_HOOK */
}
//内核调度器挂起了
else
{
++uxPendedTicks;//挂起的tick+1
/* The tick hook gets called at regular intervals, even if the
scheduler is locked. */
#if ( configUSE_TICK_HOOK == 1 )
{
vApplicationTickHook();
}
#endif
}
如果是抢占模式,要开启调度
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
//返回调度器状态
return xSwitchRequired;
}