SysTick初始化
![](https://i-blog.csdnimg.cn/blog_migrate/d7ea450f44a783ede1017ae98d66be88.png)
//系统节拍初始化 port.c
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 便于计算管理
*/
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 );
}
SysTick中断服务函数
![](https://i-blog.csdnimg.cn/blog_migrate/3edf2ae5fb3317a2fbd620855a13383f.png)
//SysTick任务调度
BaseType_t xTaskIncrementTick( void )
{
TCB_t * pxTCB;
TickType_t xItemValue;
// 返回值,表示是否进行上下文切换
BaseType_t xSwitchRequired = pdFALSE;
//uxSchedulerSuspended 表示内核调度器是否挂起,,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; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */
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 /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */
}
else//内核调度器挂起了
{
//挂起的tick+1
++uxPendedTicks;
}
//如果是抢占模式,要开启调度
#if ( configUSE_PREEMPTION == 1 )
{
if( xYieldPending != pdFALSE )
{
xSwitchRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* configUSE_PREEMPTION */
//返回调度器状态
return xSwitchRequired;
}
SysTick任务调度
![](https://i-blog.csdnimg.cn/blog_migrate/3ea83c312031cb9e18d384b1ef731499.png)
//系统节拍中断服务函数
void xPortSysTickHandler( void )
{
/*
1、配置中断屏蔽寄存器
2、不让中断打断,systick中断服务
3、其实就是进入临街段
*/
vPortRaiseBASEPRI();
{
/*
操作系统调度接口
如果调度器返回true,触发pendSV异常
*/
if( xTaskIncrementTick() != pdFALSE )
{
/* 触发pendSV */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
//清楚可屏蔽中断,其实就打开全部中断-----》pendSV就是执行
vPortClearBASEPRIFromISR();
}