1. 什么是任务?
任务可以理解为进程/线程,创建一个任务,就会在内存开辟一个空间。
比如: 玩游戏、陪女朋友,都可以视为任务 Windows 系统中的 MarkText 、谷歌浏览器、记事本,都是任务。
任务通常都含有 while(1) 死循环。
2. 任务创建与删除相关函数
任务动态创建与静态创建的区别: 动态创建任务的堆栈由系统分配,而静态创建任务的堆栈由用户自己传递。 通常情况下使用动态方式创建任务。
参数说明:
1. pvTaskCode:指向任务函数的指针,任务必须实现为永不返回(即连续循环);
2. pcName:任务的名字,主要是用来调试,默认情况下最大长度是16;
3. pvParameters:指定的任务栈的大小;
4. uxPriority:任务优先级,数值越大,优先级越大;
5. pxCreatedTask:用于返回已创建任务的句柄可以被引用。
删除任务:
void vTaskDelete(TaskHandle_t xTaskToDelete);
只需将待删除的任务句柄传入该函数,即可将该任务删除。 当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。
实操创建任务:
示例代码:
void StartTaskLED1(void const * argument)
{
/* USER CODE BEGIN StartTaskLED1 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
osDelay(500);
}
/* USER CODE END StartTaskLED1 */
}
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 */
}
3、任务调度
什么是任务调度?
调度器就是使用相关的调度算法来决定当前需要执行的哪个任务。
FreeRTOS中开启任务调度的函数是 vTaskStartScheduler() ,但在 CubeMX 中被封装为 osKernelStart() 。
FreeRTOS的任务调度规则是怎样的?
FreeRTOS 是一个实时操作系统,
它所奉行的调度规则:
1. 高优先级抢占低优先级任务,系统永远执行最高优先级的任务(即抢占式调度)
2. 同等优先级的任务轮转调度(即时间片调度)
还有一种调度规则是协程式调度,但官方已明确表示不更新,主要是用在小容量的芯片上,用得 也不多。
抢占式调度运行过程:
总结: 1. 高优先级任务,优先执行;
2. 高优先级任务不停止,低优先级任务无法执行;
3. 被抢占的任务将会进入就绪态
时间片调度运行过程:
1. 同等优先级任务,轮流执行,时间片流转;
2. 一个时间片大小,取决为滴答定时器中断周期;
3. 注意没有用完的时间片不会再使用,下次任务 Task3 得到执行,还是按照一个时间片的时钟 节拍运行
任务的状态
FreeRTOS中任务共存在4种状态:
Running 运行态
当任务处于实际运行状态称之为运行态,即CPU的使用权被这个任务占用(同一时间仅一个任务 处于运行态)。
Ready 就绪态
处于就绪态的任务是指那些能够运行(没有被阻塞和挂起),但是当前没有运行的任务,因为同 优先级或更高优先级的任务正在运行。
Blocked 阻塞态
如果一个任务因延时,或等待信号量、消息队列、事件标志组等而处于的状态被称之为阻塞态。
Suspended 挂起态
类似暂停,通过调用函数 vTaskSuspend() 对指定任务进行挂起,挂起后这个任务将不被执行, 只有调用函数 xTaskResume() 才可以将这个任务从挂起态恢复。
总结:
1. 仅就绪态可转变成运行态
2. 其他状态的任务想运行,必须先转变成就绪态
实验需求 创建 4 个任务:taskLED1,taskLED2,taskKEY1,taskKEY2,
任务要求如下:
taskLED1:间隔 500ms 闪烁 LED1;
taskLED2:间隔 1000ms 闪烁 LED2;
taskKEY1:如果 taskLED1 存在,则按下 KEY1 后删除 taskLED1 ,否则创建 taskLED1 ; taskKEY2:如果 taskLED2 正常运行,则按下 KEY2 后挂起 taskLED2 ,否则恢复 taskLED2
代码:
void StartTaskLED1(void const * argument)
{
/* USER CODE BEGIN StartTaskLED1 */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
osDelay(500);
}
/* USER CODE END StartTaskLED1 */
}
/* USER CODE BEGIN Header_StartTaskLED2 */
/**
* @brief Function implementing the taskLED2 thread.
* @param argument: Not used
* @retval None
*/
/* 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 */
}
/* USER CODE BEGIN Header_StartTaskKey1 */
/**
* @brief Function implementing the taskKey1 thread.
* @param argument: Not used
* @retval None
*/
/* 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);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
{
printf("key1按下\r\n");
if(taskLED1Handle==NULL)
{
printf("task1不存在,准备创建任务1\r\n");
osThreadDef(taskLED1, StartTaskLED1, osPriorityNormal, 0, 128);
taskLED1Handle = osThreadCreate(osThread(taskLED1), NULL);
if(taskLED1Handle!=NULL)
{
printf("任务一创建完成\r\n");
}
}
else
{
printf("删除任务1\r\n");
osThreadTerminate(taskLED1Handle);
taskLED1Handle = NULL;
}
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET);
}
}
osDelay(1);
}
/* USER CODE END StartTaskKey1 */
}
/* USER CODE BEGIN Header_StartTaskKey2 */
/**
* @brief Function implementing the taskKey2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTaskKey2 */
void StartTaskKey2(void const * argument)
{
/* USER CODE BEGIN StartTaskKey2 */
/* Infinite loop */
static int flag=0;
for(;;)
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET)
{
osDelay(20);
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET)
{
printf("key2按下\r\n");
if(flag==0)
{
osThreadSuspend(taskLED2Handle);
printf("task2已经暂停\r\n");
flag=1;
}
else
{
osThreadResume(taskLED2Handle);
printf("task2已经恢复\r\n");
flag=0;
}
}
while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET);
}
osDelay(1);
}
/* USER CODE END StartTaskKey2 */
}