【STM32之FreeRTOS(三)】任务的调度与状态

【STM32之FreeRTOS(三)】任务的调度与状态

一、什么是任务调度?

任务调度就是调度器使用相关的调度算法来决定当前需要执行的哪个任务。

FreeRTOS中开启任务调度的函数是 vTaskStartScheduler() ,但在 CubeMX 中被封装为osKernelStart()

二、FreeRTOS的任务调度规则

FreeRTOS 是一个实时操作系统,它所奉行的调度规则:

  • 1.高优先级抢占低优先级任务,系统永远执行最高优先级的任务(即抢占式调度)

  • 抢占式调度——比如,陪女朋友 > 玩游戏

  • 2. 同等优先级的任务轮转调度(即时间片调度)

  • 时间片调度——比如,三个好朋友找你打球,陪第一个打1s,再陪等二个打1s,接着陪第三个打1s,依次类推循环

还有一种调度规则是协程式调度,但官方已明确表示不更新,主要是用在小容量的芯片上,用得也不多。


1.抢占式调度运行过程

当优先级更高的任务开始运行后,会去打断运行中的低优先级的任务;

当某个任务进入阻塞态后,会根据优先级去执行其他任务;

在这里插入图片描述

Task 1:玩游戏

Task 2:老妈喊你吃饭

Task 3:女朋友喊你看电视

阻塞:比如女朋友肚子疼上厕所

总结:

  1. 高优先级任务,优先执行;
  2. 高优先级任务不停止,低优先级任务无法执行;
  3. 被抢占的任务将会进入就绪态

2.时间片调度运行过程

在这里插入图片描述

Task 1:陪A打1s篮球

Task 2:陪B打1s兵乓球

Task 3:陪C打1s桌球

阻塞:A/B/C肚子痛上厕所(阻塞的时间0.5s就不管了,直接去和下一个人打球)

总结:

  1. 同等优先级任务,轮流执行,时间片流转;
  2. 一个时间片大小,取决为滴答定时器中断周期;
  3. 注意没有用完的时间片不会再使用,下次任务 Task3 得到执行,还是按照一个时间片的时钟节拍运行

三、任务的状态

FreeRTOS中任务共存在4种状态:

  • Running 运行态

当任务处于实际运行状态称之为运行态,即CPU的使用权被这个任务占用(同一时间仅一个任务 处于运行态)。

比如,汽车已经在公路上跑起来了

  • Ready 就绪态

处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同优先级或更高优先级的任务正在运行。

比如,汽车加满了油,引擎发动了,就等着一脚油门出发;分时复用,同一个时间只能跑一辆车

  • Blocked 阻塞态

如果一个任务因延时,或等待信号量、消息队列、事件标志组等而处于的状态被称之为阻塞态。

比如,汽车跑着跑着遇到了红灯(调用delay延时);此时进入阻塞,就可以根据优先级安排另一辆汽车出发

  • Suspended 挂起态

类似暂停,通过调用函数vTaskSuspend() 对指定任务进行挂起,挂起后这个任务将不被执行, 只有调用函数 xTaskResume() 才可以将这个任务从挂起态恢复。

比如,车故障了,我们调用vTaskSuspend() 把该辆汽车拿去修理厂维修,暂时就不跑了;等人家调用xTaskResume()告诉我们说修好了才能上路

在这里插入图片描述

总结:

  1. 就绪态可转变成运行态
  2. 其他状态的任务想运行,必须先转变成就绪态

四、任务综合小实验

1.实验需求

创建 4 个任务:taskLED1,taskLED2,taskKEY1,taskKEY2

开发板采用STM32F103C8T6

任务要求如下:

  • taskLED1:间隔 500ms 闪烁 LED1;
  • taskLED2:间隔 1000ms 闪烁 LED2;
  • taskKEY1:如果 taskLED1 存在,则按下 KEY1 后删除 taskLED1 ,否则创建 taskLED1 ;
  • taskKEY2:如果 taskLED2 正常运行,则按下 KEY2 后挂起 taskLED2 ,否则恢复 taskLED2;

2.CubeMX配置

复制一份之前的工程模板(muban_freertos),打开它的CubeMX,再进行下面的操作

2.1 配置KEY1,KEY2引脚

根据自己的开发板原理图来,我这里是PA0和PA1

在这里插入图片描述

在这里插入图片描述

2.2 打开串口配置

在这里插入图片描述

2.3 创建任务

taskLED1,taskLED2可根据上一章的内容直接创建,这里不过多重复

创建taskKEY1

在这里插入图片描述

创建taskKEY2

在这里插入图片描述

2.4 串口打印测试

生成工程代码后,打开工程编写串口代码

/* USER CODE BEGIN 0 */
#include "stdio.h"
int fputc(int ch,FILE *f)
{	
	unsigned char temp[1] = {ch};
	HAL_UART_Transmit(&huart1,temp,1,0xffff);
	return ch;
}
/* USER CODE END 0 */

在这里插入图片描述

打开库设置,并勾选✔

在这里插入图片描述

在这里插入图片描述

可以在main.c里测试一下,串口打印是否成功

使用打印函数printf()

在这里插入图片描述

引入头文件

#include “stdio.h”

在这里插入图片描述

串口接线

STM32F103C8T6串口调试模块
PA9RX
PA10TX
3.3V/5V3.3V/5V
GNDGND

编译后烧录,测试成功

在这里插入图片描述

2.5 编写任务函数业务逻辑代码

在freertos.c下去编写代码

FreeRTOS帮我们创建好了4个任务

在这里插入图片描述

实现TaskLED1和TaskLED2

在这里插入图片描述

/* USER CODE END Header_StartTaskLED1 */
void StartTaskLED1(void const * argument)
{
  /* USER CODE BEGIN StartTaskLED1 */
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
    osDelay(500); //ms
  }
  /* USER CODE END StartTaskLED1 */
}

/* USER CODE END Header_StartTaskLED2 */
void StartTaskLED2(void const * argument)
{
  /* USER CODE BEGIN StartTaskLED2 */
  /* Infinite loop */
  for(;;)
  {
		HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
    osDelay(1000);
  }
  /* USER CODE END StartTaskLED2 */
}

实现TaskKEY1

在这里插入图片描述

/* USER CODE END Header_StartTaskKEY1 */
void StartTaskKEY1(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY1 */
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
			//软件实现简单防抖
			osDelay(20);   //20ms
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET){
				printf("KEY1被按下!\r\n");
				if(taskLED1Handle == NULL){
					printf("任务1!不存在\r\n");
					printf("开始创建任务1!\r\n");
					osThreadDef(taskLED1, StartTaskLED1, osPriorityNormal, 0, 128);
					taskLED1Handle = osThreadCreate(osThread(taskLED1), NULL);		
					if(taskLED1Handle!= NULL){
						printf("任务1创建完成!\r\n");
					}
				}
				else{
					printf("开始删除任务1!\r\n");
					osThreadTerminate(taskLED1Handle);
					//手动将句柄置为NULL
					taskLED1Handle = NULL;
					printf("任务1删除成功!\r\n");
				}
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET);
		}
    osDelay(10);

  }
  /* USER CODE END StartTaskKEY1 */

实现TaskKEY2

在这里插入图片描述

/* USER CODE END Header_StartTaskKEY2 */
void StartTaskKEY2(void const * argument)
{
  /* USER CODE BEGIN StartTaskKEY2 */
	static int flag = 0;
  /* Infinite loop */
  for(;;)
  {
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
			//软件实现简单防抖
			osDelay(20);   //20ms
			if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET){
				printf("KEY2被按下!\n");
				if(flag == 0){
					//任务2挂起
					osThreadSuspend(taskLED2Handle);
					printf("任务2已挂起(暂停)!\n");
					flag = 1;
				}
				else{
					osThreadResume(taskLED2Handle);
					printf("任务2恢复!\n");
					flag = 0;
				}
			}
			while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1) == GPIO_PIN_RESET);
		}
    osDelay(10);
  }
  /* USER CODE END StartTaskKEY2 */
}

到此完成业务代码编写,可以进行编译烧录了

3.效果演示

编译烧录重启后,蓝灯0.5s翻转一次,黄灯1s翻转一次

当按下KEY1,蓝灯任务被删除,蓝灯状态停滞,同时串口有删除任务信息

在这里插入图片描述

当再次按下KEY1,蓝灯任务再次被创建,任务执行,蓝灯0.5s翻转一次,同时串口有创建任务信息

在这里插入图片描述

当按下KEY2,黄灯任务被挂起(暂停),黄灯状态停滞,同时串口有挂起(暂停)任务信息

在这里插入图片描述

当按下KEY2,黄灯任务恢复,黄灯1s翻转一次,同时串口有恢复任务信息

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

索子也敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值