顿悟了,一下子就顿悟了,其实就是看到 keli 调试的时候有很多的汇编代码,运行的那么快,然后就突然对 FreeRTOS 很有感觉了;就是运行的太快,即使不停的切换我们也没有感觉。
在单核MCU上,多任务并不是真正的并行执行,而是通过快速的任务切换实现的"时间分片"。
对MCU硬件特性来说单核MCU依然是一条指令一条指令按顺序执行;而RTOS通过系统定时器定期中断,保存/恢复任务状态;对用户感知来说由于切换频率极高(通常1ms),用户感觉多个任务在"同时"运行
时间轴: |----1ms----|----1ms----|----1ms----|----1ms----|----1ms----|
任务执行: [ 任务A执行 ] [ 任务B执行 ] [ 任务C执行 ] [ 任务A执行 ] [ 任务B执行 ]
中断: ↑SysTick ↑SysTick ↑SysTick ↑SysTick ↑SysTick
状态: [保存A] [保存B] [保存C] [保存A] [保存B]
[加载B] [加载C] [加载A] [加载B] [加载C]
// 典型的SysTick配置
#define configTICK_RATE_HZ 1000 // 1000Hz = 1ms时间片
#define configCPU_CLOCK_HZ 100000000 // 100MHz CPU
// SysTick初始化
SysTick_Config(configCPU_CLOCK_HZ / configTICK_RATE_HZ); // 每1ms触发一次
特性 | 说明 |
|---|---|
时间片长度 | 通常1ms,可配置 |
中断频率 | 1000Hz(1ms间隔) |
切换开销 | 几十个CPU周期 |
用户感知 | 无感知,任务似乎并行运行 |
/**
* @brief SysTick中断处理函数
* @note 每1ms触发一次,是任务调度的起点
*/
void xPortSysTickHandler(void)
{
uint32_t ulPreviousMask;
ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
{
/* 系统节拍计数器递增 */
if( xTaskIncrementTick() != pdFALSE )
{
/* 需要进行任务切换,触发PendSV中断 */
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
}
}
portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
}
/**
* @brief PendSV中断处理程序(ARM Cortex-M系列汇编代码)
*/
xPortPendSVHandler:
/* 1. 保存当前任务的CPU寄存器到任务堆栈 */
mrs r0, psp ; 获取当前任务堆栈指针(PSP)
ldr r3, =pxCurrentTCB ; 获取当前TCB地址
ldr r2, [r3] ; 加载当前TCB指针
stmdb r0!, {r4-r11} ; 保存寄存器R4-R11到堆栈
str r0, [r2] ; 保存新的堆栈指针到TCB
/* 2. 调用C语言任务调度器 */
push {r3, r14} ; 保护寄存器
cpsid i ; 禁用中断
bl vTaskSwitchContext ; 调用任务调度器
cpsie i ; 启用中断
pop {r2, r3} ; r2=新TCB地址,r3=返回地址
/* 3. 恢复新任务的上下文 */
ldr r1, [r2] ; 获取新任务TCB
ldr r0, [r1] ; 获取新任务的堆栈指针
ldmia r0!, {r4-r11} ; 从堆栈恢复寄存器R4-R11
msr psp, r0 ; 设置新的堆栈指针
bx r3 ; 返回,继续执行新任务
/**
* @brief 任务切换上下文函数
*/
void vTaskSwitchContext( void )
{
if( uxSchedulerSuspended != ( UBaseType_t ) 0U )
{
/* 调度器被暂停,不允许任务切换 */
xYieldPendings[ 0 ] = pdTRUE;
}
else
{
/* 记录任务运行时间统计 */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
ulTotalRunTime[ 0 ] = portGET_RUN_TIME_COUNTER_VALUE();
if( ulTotalRunTime[ 0 ] > ulTaskSwitchedInTime[ 0 ] )
{
pxCurrentTCB->ulRunTimeCounter +=
( ulTotalRunTime[ 0 ] - ulTaskSwitchedInTime[ 0 ] );
}
ulTaskSwitchedInTime[ 0 ] = ulTotalRunTime[ 0 ];
}
#endif
/* 检查栈溢出 */
taskCHECK_FOR_STACK_OVERFLOW();
/* 选择优先级最高的就绪任务 */
taskSELECT_HIGHEST_PRIORITY_TASK();
/* 平台特定的任务切换后处理 */
portTASK_SWITCH_HOOK( pxCurrentTCB );
}
}
算法类型 | 时间复杂度 | 适用平台 | 优缺点 |
|---|---|---|---|
通用算法 | O(n) | 所有平台 | 兼容性好,但速度较慢 |
硬件优化 | O(1) | 支持CLZ指令的ARM | 速度极快,硬件加速 |
/**
* @brief 通用任务选择算法(线性搜索)
*/
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
do { \
UBaseType_t uxTopPriority = uxTopReadyPriority; \
\
/* 找到最高优先级且有就绪任务的队列 */ \
while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) != pdFALSE ) \
{ \
configASSERT( uxTopPriority ); \
--uxTopPriority; \
} \
\
/* 从该优先级的任务列表中选择下一个任务(时间片轮转) */ \
listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \
uxTopReadyPriority = uxTopPriority; \
} while( 0 )
/**
* @brief 硬件优化的任务选择算法
*/
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
/* 位图操作宏 */
#define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) \
( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) )
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) \
( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
/* 使用CLZ指令快速找到最高优先级 */
#define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) \
uxTopPriority = ( 31UL - ( ( uint32_t ) __CLZ( ( uxReadyPriorities ) ) ) )
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
/**
* @brief CLZ指令示例
* CLZ = Count Leading Zeros(计算前导零个数)
*/
// 示例:假设uxReadyPriorities = 0x00000110
// (二进制: 00000000 00000000 00000001 00010000)
// CLZ(0x00000110) = 28 (前面有28个0)
// 最高优先级 = 31 - 28 = 3
uint32_t uxReadyPriorities = 0x00000110; // 优先级0和优先级8的任务就绪
uint32_t uxTopPriority = 31 - __CLZ(uxReadyPriorities); // 结果:uxTopPriority = 8
/**
* @brief 任务控制块结构定义(简化版)
*/
typedefstruct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /* 栈顶指针 - 必须是第一个成员 */
ListItem_t xStateListItem; /* 状态列表项 */
ListItem_t xEventListItem; /* 事件列表项 */
UBaseType_t uxPriority; /* 任务优先级 */
StackType_t *pxStack; /* 栈底指针 */
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /* 任务名称 */
#if ( configGENERATE_RUN_TIME_STATS == 1 )
configRUN_TIME_COUNTER_TYPE ulRunTimeCounter; /* 运行时间计数器 */
#endif
#if ( configUSE_TASK_NOTIFICATIONS == 1 )
volatileuint32_t ulNotifiedValue[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
volatileuint8_t ucNotifyState[ configTASK_NOTIFICATION_ARRAY_ENTRIES ];
#endif
} tskTCB;
typedef tskTCB TCB_t;
/**
* @brief 任务状态枚举
*/
typedef enum
{
eRunning = 0, /* 运行状态 - 任务正在CPU上执行 */
eReady, /* 就绪状态 - 任务可以运行,等待调度 */
eBlocked, /* 阻塞状态 - 任务等待事件或延时 */
eSuspended, /* 挂起状态 - 任务被明确挂起 */
eDeleted, /* 删除状态 - 任务已删除 */
eInvalid /* 无效状态 */
} eTaskState;
/**
* @brief 任务列表定义
*/
/* 就绪任务列表 - 按优先级分组 */
PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];
/* 延时任务列表 */
PRIVILEGED_DATA static List_t xDelayedTaskList1;
PRIVILEGED_DATA static List_t xDelayedTaskList2;
PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList;
PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList;
/* 挂起任务列表 */
#if ( INCLUDE_vTaskSuspend == 1 )
PRIVILEGED_DATA static List_t xSuspendedTaskList;
#endif
/* 当前TCB指针 */
#if ( configNUMBER_OF_CORES == 1 )
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL;
#else
PRIVILEGED_DATA TCB_t * volatile pxCurrentTCBs[ configNUMBER_OF_CORES ] = { NULL };
#endif