“从0开始的FreeRTOS”系列教程第四讲
作者:satori
这一次我们来进行基于FreeRTOS的任务管理实验。
在开讲之前,推荐一下Zou Changjun翻译的FreeRTOS实时内核使用指南(官方网站上的英文原名是Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide),在后面讲解API时我们力求精简易懂,所以不会说太多详细的内容,具体可以参见该文档和官方API手册(文末有本文档的下载链接)。
回顾一下上次我们介绍的有关FreeRTOS的进程的知识:
主要有:进程的概念,进程的调度机制
对于FreeRTOS而言,不同优先级的进程之间采用优先级调度算法,对于同优先级的进程之间采用时间片轮转调度算法+FCFS算法。
本次实验我们主要的实验内容为
任务的创建
同优先级进程之间的时间片轮转调度算法
不同优先级进程之间的优先级调度算法
任务的删除
任务的挂起(延时函数)
首先我们先来学习一下和FreeRTOS的任务创建有关的API:
对于初学者而言,比较需要关心的参数有以下几个:pvTaskCode,pcName和uxPriority
pvTaskCode是任务函数名
pcName是用户起的函数名
uxPriority是任务优先级
在cude建立的工程中,我们不会直接使用freertos的API,而是会使用CMSIS-RTOS的API:
#define osThreadDef (
name, //进程名
priority, //进程优先级
instances, //进程函数
stacksz //栈大小
)
其中priority一般使用的是CMISIS-RTOS自己定义的七个优先级(详见上一讲介绍的结构体)
osThreadId osThreadCreate (
const osThreadDef_t * thread_def, //传递在osThreadDef中输入的参数
void * argument //参数
)
这里我们通过一个实例来学习进程的创建:
实验1:
使用CMSIS-RTOS创建一个进程:
仿照第二讲中的方式新建一个工程
并启用串口1(后续的教程中串口会非常常用)
参数设置如下
将默认的任务的名字改成task_1
创建工程,在freertos.c上加入
#include "stm32f1xx_hal.h"
在usart.c下加入以下代码,就可以在32中使用printf功能了
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
* @brief Retargets the C library printf function to the USART.
* @param None
* @retval None
*/
PUTCHAR_PROTOTYPE
{
/* Place your implementation of fputc here */
/* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF); //具体根据自己启用的串口修改这个函数
return ch;
}
在freertos.c中添加如下代码
/* USER CODE BEGIN FunctionPrototypes */
void delay(int x);
/* USER CODE END FunctionPrototypes */
/* task1_handler function */
void task1_handler(void const * argument)
{
/* USER CODE BEGIN task1_handler */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5);
printf("this is task_1 running\r\n");
delay(500);
}
/* USER CODE END task1_handler */
}
/* USER CODE BEGIN Application */
void delay(int x)
{
for(int i=0;i<x;i++)
{
for(int j=0;j<1000;j++);
}
}
/* USER CODE END Application */
编译后烧录
可以发现板子上的绿灯闪烁+串口发送数据
然后我们注释掉所有和task_1有关的代码,来尝试不借助cubemx自己生成一个系统任务
(实际上关于cubemx,我的个人意见是,创建工程时这是一个非常方便的工具,但是带来的后续开发的麻烦也很多,比如要修改外设配置的时候,要经历cube中修改------>重新生成工程---->继续写代码这样一个过程,如果你只是要做修改串口波特率这类很简单的参数修改的话,何苦非要经过上面这个繁琐的过程而非直接在代码中修改呢?)
需要添加的内容如下
/声明任务句柄
osThreadId task_2Handle;
//声明任务函数
void task2_handler(void const * argument);
//利用osTreadDef和osThreadCreate两个函数定义任务参数+创建任务
osThreadDef(task_2, //任务名
task2_handler, //任务函数
osPriorityNormal, //任务优先级
0, // 子进程数
128); //任务栈
task_2Handle = osThreadCreate(osThread(task_2), //任务名要和上面的函数中一致
NULL);
/关于两个函数更具体的讲解可以看官方API手册
http://www.keil.com/pack/doc/CMSIS_Dev/RTOS/html/group__CMSIS__RTOS__ThreadMgmt.html#gac59b5713cb083702dce759c73fd90dff
最后添加任务函数
void task2_handler(void const * argument)
{
/* USER CODE BEGIN task1_handler */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
printf("this is task_2 running\r\n");
delay(500);
}
/* USER CODE END task1_handler */
}
编译下载,查看实验效果
红灯闪烁,串口发送数据如下
实验2:
时间片轮转调度实验
取消task_1有关代码的注释,让task_1和task_2同时开始运行,观察实验结果——
1.红灯和绿灯同时闪烁
2.串口发送数据如下
可以发现串口发送的数据存在错位的情况,思考一下这是为什么?
实验3:
优先级调度实验
在创建task2的函数中修改task2的优先级
osThreadDef(task_2, task2_handler, osPriorityAboveNormal, 0, 128);
task_2Handle = osThreadCreate(osThread(task_2), NULL);
编译下载后发现只有红灯闪烁
串口发送数据如下
这个现象的原理是只有在同优先级的任务间才存在时间片轮转调度,当task2优先级高于task1时,系统会始终执行task2
再修改代码如下
void task2_handler(void const * argument)
{
/* USER CODE BEGIN task1_handler */
/* Infinite loop */
for(;;)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
printf("this is task_2 running\r\n");
osDelay(500);
}
/* USER CODE END task1_handler */
}
会发现任务交替运行
这就体现出了osDelay和普通的延时之间的区别
osDelay的原理是将一个任务修改到最低优先级(IDLE优先级),使任意一个就绪进程都可以抢占cpu
通过以上三个实验,我们学习了任务创建,时间片轮转调度和优先级调度的实践
实际上在rtos中有相当丰富的进程管理函数,包括获取进程id,设置进程优先级,设置进程优先级等(可以用来实现动态优先级算法
这里就不多加介绍了,有兴趣的可以自己去了解
最后附上推荐教材(官方文档的中文翻译版)
《FreeRTOS实时内核使用指南》:
链接:https://pan.baidu.com/s/1qouYjnNSsa0u6KxI-0jMRQ
提取码:al12
原文地址:https://www.cnblogs.com/sasasatori/p/12231964.html