FreeRTOS任务挂起和恢复
任务挂起和恢复
1. 任务挂起和恢复 API 函数
vTaskSuspend()
函数:将任务置于挂起状态
1
2
3
void vTaskSuspend(TaskHandle_t xTaskToSuspend)
参 数:xTaskToSuspend 要挂起的任务的任务句柄
返 回 值:无
vTaskResume()
函数:将任务从挂起状态恢复到就绪态
1
2
3
void vTaskResume(TaskHandle_t xTaskToResume)
参 数:xTaskToResume 要恢复的任务的任务句柄
返 回 值:无
2. 任务挂起和恢复函数源码分析
- 任务挂起函数源码分析
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
void vTaskSuspend(TaskHandle_t xTaskToSuspend){
TCB_t *pxTCB;
/* 进入临界段 */
taskENTER_CRITICAL();
{
/* 获取任务控制块,若为NULL则挂起自身 */
pxTCB = prvGetTCBFromHandle(xTaskToSuspend);
/* 将任务从就绪列表中移除 */
if(uxListRemove(&(pxTCB->xStateListItem)) == (UBaseType_t)0){
taskRESET_READY_PRIORITY(pxTCB->uxPriority);
}
else{
mtCOVERAGE_TEST_MARKER();
}
/* 查看任务是否在等待某个事件,如是则将其从事件列表中移除 */
if(listLIST_ITEM_CONTAINER(&(pxTCB->xEventListItem))!=NULL){
(void) uxListRemove(&(pxTCB->xEventListItem));
}
else{
mtCOVERAGE_TEST_MARKER();
}
/* 将任务添加到挂起任务列表表尾 */
vListInsertEnd(&xSuspendedTaskList, &(pxTCB->xStateListItem));
}
/* 退出临界段 */
taskEXIT_CRITICAL();
if(xSchedulerRunning != pdFALSE){ //判断调度器是否开启
/* 重新计算还要多长时间执行下一个任务 */
taskENTER_CRITICAL();
{
prvResetNextTaskUnblockTime();
}
taskEXIT_CRITICAL();
}
else{
mtCOVERAGE_TEST_MARKER();
}
if(pxTCB == pxCurrentTCB){
if(xSchedulerRunning != pdFALSE){
/* 若刚挂起的是正在运行的任务,且任务调度器运行正常,则强制进行一次任务切换 */
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else{
/* 若任务调度器没有开启,则读取当前任务挂起列表的长度,判断所有任务是否都被挂起*/
if(listCURRENT_LIST_LENGTH(&xSuspendedTaskList) == uxCurrentNumberOfTasks){
/* 若所有任务都被挂起,把当前的任务控制块赋值为NULL */
pxCurrentTCB = NULL;
}
else{
/* 若还有没被挂起的任务,则获取下一个要运行的任务 */
vTaskSwitchContext();
}
}
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
- 任务恢复函数源码分析
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
void vTaskResume(TaskHandle_t xTaskToResume){
/* 获取要恢复的任务控制块 */
TCB_t * const pxTCB = (TCB_t *) xTaskToResume;
configASSERT( xTaskToResume );
/* 任务控制块不能为NULL和当前任务 */
if(( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB )){
/* 进入临界段 */
taskENTER_CRITICAL();
{
/* 判断任务是否被挂起 */
if(prvTaskIsTaskSuspended(pxTCB) != pdFALSE){
/* 从挂起列表中移除 */
(void) uxListRemove(&( pxTCB->xStateListItem));
/* 添加到就绪列表中 */
prvAddTaskToReadyList( pxTCB );
/* 要恢复的任务优先级高于当前正在运行的任务优先级 */
if(pxTCB->uxPriority >= pxCurrentTCB->uxPriority){
/* 完成一次任务切换 */
taskYIELD_IF_USING_PREEMPTION();
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/* 退出临界段 */
taskEXIT_CRITICAL();
}
else{
mtCOVERAGE_TEST_MARKER();
}
}
3. 任务挂起和恢复函数实例
使用 STM32CubeMX
可以非常方便的将 FreeRTOS
移植到工程中。本实验就是利用 STM32CubeMX
移植 FreeRTOS
,并创建三个任务:
Led_Task:D2 指示灯闪烁 Usart_Task:每隔 1s 向串口输出字符串 Key_Task:按下 K_UP,挂起 Led_Task 任务; 按下 K_DOWN,恢复 Led_Task 任务
3.1 STM32CubeMX 设置
RCC 设置外接 HSE,时钟设置为 72M
PC1 设置为 GPIO 推挽输出模式、上拉、高速、默认输出电平为高电平
PA0 设置为 GPIO 输入模式、下拉模式;PE2/PE3/PE4 设置为 GPIO 输入模式、上拉模式
USART1 选择为异步通讯方式,波特率设置为 115200Bits/s,传输数据长度为 8Bit,无奇偶校验,1 位停止位
激活 FreeRTOS,添加任务,设置任务名称、优先级、堆栈大小、函数名称等参数
使用
FreeRTOS
操作系统,一定要将 HAL 库的Timebase Source
从SysTick
改为其他定时器,选好定时器后,系统会自动配置TIM
输入工程名,选择路径(不要有中文),选择
MDK-ARM V5
;勾选Generated periphera initialization as a pair of ‘.c/.h’ files per IP
;点击GENERATE CODE
,生成工程代码
3.2 MDK-ARM 软件编程
- 创建按键驱动文件 key.c 和 key.h,参考按键输入例程
- 添加 Led_Task、Usart_Task 和 Key_Task 任务函数代码
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
void Led_Task(void const * argument){
/* USER CODE BEGIN Led_Task */
/* Infinite loop */
for(;;){
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);
osDelay(500); //1ms时基
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);
osDelay(500); //1ms时基
}
/* USER CODE END Led_Task */
}
void Usart_Task(void const * argument){
/* USER CODE BEGIN Usart_Task */
/* Infinite loop */
for(;;){
printf("UsartTask is Runing!\r\n");
osDelay(1000);
}
/* USER CODE END Usart_Task */
}
void KeyTask(void const * argument){
/* USER CODE BEGIN KeyTask */
uint8_t key = 0;
/* Infinite loop */
for(;;){
key = KEY_Scan(0);
switch(key){
case KEY_UP_PRES:
vTaskSuspend(Led_TaskHandle);
printf("Led_Task被挂起!\r\n");
key = 0;
break;
case KEY_DOWN_PRES:
vTaskResume(Led_TaskHandle);
printf("Led_Task被恢复!\r\n");
key = 0;
break;
}
osDelay(10);
}
/* USER CODE END KeyTask */
}
3.3 下载验证
编译无误下载到开发板后,D2 指示灯闪烁表示程序正常运行。打开串口调试助手,可以看到串口每隔 1s 输出相应字符;按下 K_UP 按键,Led_Task 被挂起,D2 指示灯停止闪烁,按下 K_DOWN 按键,Led_Task 任务恢复,D2 指示灯开始闪烁
以上转载自博主“安迪西嵌入式”的FreeRTOS专栏,仅作学习记录,如有侵权,请联系删除。