FreeRTOS和Ucos中不同优先级的任务运行时采用的是抢占式调度,同一优先级下有多个任务需要运行时,则对同优先级的任务进行时间片轮转调度。将抢占式调度和时间片轮转调度联合使用就是混合式调度。
在 RTOS 中,最小的时间单位为一个Tick,即 SysTick 的中断周期, 同优先级轮转调度时Ucos可以指定每个任务运行多少个 Tick,但是FreeRTOS的任务运行时间片是固定的一个Tick,不可调整。在Ucos中若优先级0下有两个任务A和B,任务时间片设置为10ms,SysTick 的中断周期为1ms。则系统运行结果 是A运行10ms后切换B运行10ms然后再切换为任务A运行如此反复。在FreeRTOS中则是A运行1ms切换任务B运行1ms再切换任务A运行。
为了方便在同一优先级下面挂载与删除任务,RTOS的任务控制块当中(Task Control Block TCB)中引入了双向链表结构。FreeRTOS和Ucos任务控制块结构体如下,本例程中模仿FreeRTOS中链表的用法来实现双向循环链表。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35/*FreeRTOS*/ typedef struct tskTaskControlBlock { volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ #if ( portUSING_MPU_WRAPPERS == 1 ) xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ #endif ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ StackType_t *pxStack; /*< Points to the start of the stack. */ ... ... ... } /*Ucos*/ struct os_tcb { CPU_STK *StkPtr; /* Pointer to current top of stack */ void *ExtPtr; /* Pointer to user definable data for TCB extension */ CPU_STK *StkLimitPtr; /* Pointer used to set stack 'watermark' limit */ OS_TCB *NextPtr; /* Pointer to next TCB in the TCB list */ OS_TCB *PrevPtr; /* Pointer to previous TCB in the TCB list */ OS_TCB *TickNextPtr; OS_TCB *TickPrevPtr; ... ... ... }
一、双向链表的实现
网上关于双向循环链表的教程有很多,这里不再叙述链表的实现过程直接贴代码。推荐B站上的两个视频教程,一个是关于C语言指针的《强烈推荐】4小时彻底掌握C指针 - 顶尖程序员图文讲解 - UP主翻译校对 (已完结)》,另一个是关于数据结构的《【强烈推荐】深入浅出数据结构 - 顶尖程序员图文讲解 - UP主翻译校对 (已完结)》,讲师是一个印度小哥,讲解的很不错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127typedef struct STRUCT_NODE { struct STRUCT_NODE* next; struct STRUCT_NODE* prev; void* pOwner; /* Pointer to a TCB that contains the node */ }Node,*NodePtr; typedef struct STRUCT_LIST { UINT16 nodeCount; NodePtr pHeadNode; }List,*ListPtr; /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void NodeInit(NodePtr pNode,void* pOwner) { pNode->next = pNode; pNode->prev = pNode; pNode->pOwner = pOwner; } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void ListInit(ListPtr pList) { pList->pHeadNode = NULL; pList->nodeCount = 0; } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void ListInsertAtHead(ListPtr pList,NodePtr pNode) { if(pList->pHeadNode == NULL) { pList->pHeadNode = pNode; pList->nodeCount++; return ; } pList->pHeadNode->prev->next = pNode; pNode->next = pList->pHeadNode; pNode->prev = pList->pHeadNode->prev; pList->pHeadNode->prev = pNode; pList->pHeadNode = pNode; pList->nodeCount++; } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void ListInsertAtTail(ListPtr pList,NodePtr pNode) { if(pList->pHeadNode == NULL) { pList->pHeadNode = pNode; pList->nodeCount++; return ; } pList->pHeadNode->prev->next = pNode; pNode->next = pList->pHeadNode; pNode->prev = pList->pHeadNode->prev; pList->pHeadNode->prev = pNode; pList->nodeCount++; } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ NodePtr ListPopHead(ListPtr pList) { NodePtr pTmpNode = NULL; if(pList->pHeadNode == NULL) { return NULL; } pTmpNode = pList->pHeadNode; pList->pHeadNode = pTmpNode->next; pList->pHeadNode->prev = pTmpNode->prev; pTmpNode->prev->next = pList->pHeadNode; pList->nodeCount--; return pTmpNode; } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void ListRemoveNode(ListPtr pList, NodePtr pNode) { pNode->prev->next = pNode->next; pNode->next->prev = pNode->prev; NodeInit(pNode,pNode->pOwner); pList->nodeCount--; }
二、同优先级轮转调度设计
任务控制块中增加链表节点变量和时间片设置变量,当设置的任务运行时间片为0时,就采用系统缺省值代替0。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69typedef struct STRUCT_TASK_TCB { UINT32 *stackPtr; /*Points to the location of the last item placed on the tasks stack*/ Node taskTcbNode; UINT8 priority; /*The priority of the task. 0 is the highest priority*/ UINT8 timeSliceSet; UINT8 timeSlice; }TaskTcb,*TaskTcbPtr; List taskTcbPtrPrioTbl[OS_TASK_MAX_PRIORITIES + 1]; /********************************************************************************************** * @brief TaskCreate * * @param None * @retval None **********************************************************************************************/ void TaskCreate(TaskTcb *pTcb, FunPtr pTaskFun, UINT32* pTaskStack, UINT32 stackSize, UINT8 taskPrio, UINT8 timeSlice) { UINT32 *pStack; UINT32 cnt; for(cnt = 0; cnt < stackSize*sizeof(UINT32);cnt++)/* Fill the stack with a known value to assist debugging. */ { *((UINT8*)pTaskStack + cnt) = OS_STACK_FILL_BYTE; } pStack = &pTaskStack[stackSize];/* Get top address of stack, because grows from high memory to low*/ pStack = (UINT32 *)((UINT32)(pStack) & 0xFFFFFFF8u);/* AAPCS(ARM application procedure all standard) Align the stack to 8bytes */ /*Registers stacked as if auto-saved on exception*/ *(--pStack) = (UINT32)0x01000000uL; /*xPSR bit24 thumb state bit*/ *(--pStack) = (UINT32)pTaskFun; /*Entry Point(PC)*/ *(--pStack) = (UINT32)0x12121212uL; /*R14 (LR)*/ *(--pStack) = (UINT32)0x12121212uL; /* R12*/ *(--pStack) = (UINT32)0x03030303uL; /* R3*/ *(--pStack) = (UINT32)0x02020202uL; /* R2*/ *(--pStack) = (UINT32)0x01010101uL; /* R1*/ *(--pStack) = (UINT32)0x00000000u; /* R0*/ /*Remaining registers saved on process stack*/ *(--pStack) = (UINT32)0x11111111uL; /* R11*/ *(--pStack) = (UINT32)0x10101010uL; /* R10*/ *(--pStack) = (UINT32)0x09090909uL; /* R9*/ *(--pStack) = (UINT32)0x08080808uL; /* R8*/ *(--pStack) = (UINT32)0x07070707uL; /* R7*/ *(--pStack) = (UINT32)0x06060606uL; /* R6*/ *(--pStack) = (UINT32)0x05050505uL; /* R5*/ *(--pStack) = (UINT32)0x04040404uL; /* R4*/ pTcb->stackPtr = pStack; pTcb->priority = taskPrio; if(timeSlice != 0) pTcb->timeSliceSet = timeSlice; else pTcb->timeSliceSet = OS_TASK_DEFAULT_TIME_SLICE; pTcb->timeSlice = pTcb->timeSliceSet; NodeInit(&pTcb->taskTcbNode, pTcb); ListInsertAtTail(&taskTcbPtrPrioTbl[taskPrio], &pTcb->taskTcbNode); BitMapSetBit(&taskPrioBitMap, taskPrio); }
将taskTcbPtrPrioTbl由上一章的任务控制块指针数组修改为双向链表数组,并修改相应的函数。在OsTaskStart函数中自动获取最高优先级任务的控制块指针并启动任务运行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51/********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void TaskScheduleInit(void) { UINT8 i = 0; for(i = 0; i < OS_TASK_MAX_PRIORITIES + 1; i++) { ListInit(&taskTcbPtrPrioTbl[i]); } BitMapInit(&taskPrioBitMap); } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void OsTaskStart(void) { UINT8 highestPrio = BitMapFirstGet(&taskPrioBitMap); pOsHighRdyTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[highestPrio].pHeadNode->pOwner); OSStartHighRdy(); } /********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void TaskSchedule(void) { UINT8 prio = BitMapFirstGet(&taskPrioBitMap); if((TaskTcbPtr)(taskTcbPtrPrioTbl[prio].pHeadNode->pOwner) != pOsCurTcb) { pOsHighRdyTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[prio].pHeadNode->pOwner); TaskSwitch(); } }
每进入一次SysTick中断就将时间片减1,只有同优先级任务数量大于1个时才进行轮转调度,并更新下一任务运行时的时间片长度。时间片运行结束的任务放入队尾,等待下一周期的轮转调用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27/********************************************************************************************** * @brief * * @param None * @retval None **********************************************************************************************/ void SysTick_Handler(void) { NodePtr pHeadNode; TaskTcbPtr pTaskTcb; if(pOsCurTcb->timeSlice) pOsCurTcb->timeSlice--; if(taskTcbPtrPrioTbl[pOsCurTcb->priority].nodeCount > 1) { if(pOsCurTcb->timeSlice == 0) { pHeadNode = ListPopHead(&taskTcbPtrPrioTbl[pOsCurTcb->priority]); ListInsertAtTail(&taskTcbPtrPrioTbl[pOsCurTcb->priority],pHeadNode); pTaskTcb = (TaskTcbPtr)(taskTcbPtrPrioTbl[pOsCurTcb->priority].pHeadNode->pOwner); pTaskTcb->timeSlice = pTaskTcb->timeSliceSet; } } TaskSchedule(); }
三、仿真验证
在main函数当中创建两个任务,优先级设置为0,StartTask任务时间片设置为2,SecondTask任务时间片设置为3。仿真查看两个两个任务是否交替运行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15int main() { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); LED_GPIO_Config(); SysTickRateInit(); TaskScheduleInit(); TaskCreate(&StartTaskTcb, StartTask, StartTaskStack, START_TASK_SIZE,0, 2); TaskCreate(&SecondTaskTcb, SecondTask, SecondTaskStack, SECOND_TASK_SIZE,0,3); OsTaskStart(); return 0; }
当前StartTask任务的时间片运行结束即将切换到SecondTask运行。
当前SecondTask任务的时间片运行结束即将切换到StartTask运行。
最后
以上就是有魅力砖头最近收集整理的关于基于STM32的RTOS简易分析-混合式调度一、双向链表的实现二、同优先级轮转调度设计三、仿真验证的全部内容,更多相关基于STM32内容请搜索靠谱客的其他文章。
发表评论 取消回复