本篇博客,我们详细介绍另一个重要的应用,信号量。
目录
一、信号量的简介
1.0 举例理解
1.1 FreeRTOS中的应用
1.2 队列与信号量的对比
二、二值信号量
2.1 二值信号量的概念
2.2 二值信号量的API函数接口
2.2.1 使用二值信号量的过程
2.2.2 创建二值信号量函数
2.2.3 释放二值信号量函数
2.2.4 获取二值信号量函数
2.3 二值信号量实验
四、计数型信号量
4.1 计数型信号量的概念
4.2 计数型信号量的API函数接口
4.2.1 使用计数信号量的过程
4.2.2 创建计数信号量函数
4.2.3 释放计数信号量和获取计数信号量
4.2.4 获取信号量当前计数值大小
4.3 计数型信号量实验
六、优先级翻转简介
6.1 概念
6.2 示意图理解
6.3 优先级翻转实验
八、互斥信号量
8.1 互斥信号量的概念
8.2 互斥信号量的特点
8.3 互斥信号量的API函数接口
8.3.1 使用互斥信号量的过程
8.3.2 创建互斥信号量函数
8.3.3 释放互斥信号量和获取互斥信号量函数
8.4互斥信号量实验
一、信号量的简介
1.0 举例理解
通过上一节的学习,我们知道多个任务如果同时访问共享资源的话,会引起数据的破坏,于是,引入了 消息队列,而在本节,又引入了一个新的概念,信号量,信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问,这与Linux中的线程同步问题有异曲同工之妙。信号量描述的是一种状态,信号量的值代表的是当前可用资源数目,举一个生活中的例子进行理解。
1.1 FreeRTOS中的应用
多个任务对信号量进行操作,当计数值大于0,代表有信号量资源,当释放信号量,信号量计数值(资源数)加一,当获取信号量,信号量计数值(资源数)减一。
信号量的计数值都有限制:限定最大值。如果最大值被限定为1,那么它就是二值信号量;如果最大值不是1,它就是计数型信号量。
1.2 队列与信号量的对比
二、二值信号量
2.1 二值信号量的概念
二值信号量的本质是一个队列长度为 1 的队列 ,该队列就只有空和满两种情况,这就是二值。 二值信号量通常用于互斥访问或任务同步, 与互斥信号量比较类似,但是二值信号量有可能会导致优先级翻转的问题 ,所以二值信号量更适合用于同步!如下图所示:
多个任务同时获取信号量,只会有其中的一个任务获取成功,因为获取信号量成功后,信号量计数值会从1减为0,其他任务再获取就会阻塞住,只有当信号量被之前那个任务释放以后,信号量加为1,其他任务才有可能获取信号量成功! 如此,便实现了任务的同步,这里的同步指的是多个任务协调执行!这就好想是只有一个停车位的停车场,只有当前面的人把车开走,后面的人才有可能把车停进去,否则一直在等待,也就是阻塞状态。
2.2 二值信号量的API函数接口
2.2.1 使用二值信号量的过程
使用二值信号量的过程:创建二值信号量 ——>释放二值信号量 ——>获取二值信号量
注意:创建的二值信号量一开始信号量值是为0的,因此,任务在使用信号量前必须先进行释放!!!
2.2.2 创建二值信号量函数
#include"semphr.h" //包含头⽂件
SemaphoreHandle_t xSemaphoreCreateBinary( void );
通过前面的学习,我们知道,底层还是用队列实现的,因此,它底层调用的是下面的这个函数:
xQueueGenericCreate(1 ,semSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_BINARY_SEMAPHORE );
第一个参数为队列长度为1,第二个参数为队列项是下面的宏定义:
#define semSEMAPHORE_QUEUE_ITEM_LENGTH ((uint8_t) 0U) 代表队列项长度为0
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回二值信号量的句柄 |
从上面我们可以知道,利用 xSemaphoreCreateBinary( void );函数可以创建信号量值为0的信号量,创建成功返回对应的信号量句柄,因此,在最开始我们需要提前定义好信号量句柄!
2.2.3 释放二值信号量函数
BaseType_t xSemaphoreGive( xSemaphore );
通过前面的学习,我们知道,底层还是用队列实现的,因此,它底层调用的是下面的这个函数:
xQueueGenericSend((QueueHandle_t) (xSemaphore,NULL,semGIVE_BLOCK_TIME,queueSEND_TO_BACK )
第三个参数为阻塞时间,宏定义如下:
#define semGIVE_BLOCK_TIME ( ( TickType_t ) 0U )也就是0
我们知道,释放信号量不会发生阻塞,因此没有阻塞延时这个参数!
形参 | 描述 |
xSemaphore | 要释放的信号量句柄 |
返回值 | 描述 |
pdPASS | 释放信号量成功 |
errQUEUE_FULL | 释放信号量失败 |
从上面我们可以知道,释放信号量,不会发生阻塞,只需要传入对应的信号量句柄即可!
2.2.4 获取二值信号量函数
BaseType_t xSemaphoreTake( xSemaphore, xBlockTime );
我们知道,获取信号量可能会发生阻塞,因此它有阻塞延时这个参数!
从上面我们可以知道,获取信号量,可能会发生阻塞,因此需要传入对应的信号量句柄和阻塞时间!
2.3 二值信号量实验
创建任务文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "semphr.h"
/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);
/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK1_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK1_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t task1_handler; //定义任务句柄(结构体指针)
void task1(void* args);
/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK2_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK2_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t task2_handler; //定义任务句柄(结构体指针)
void task2(void* args);
QueueHandle_t semphore_handle; //创建二值信号量
/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{
taskENTER_CRITICAL(); /*进入临界区*/
xTaskCreate( (TaskFunction_t) task1,
(char *) "task1",
( configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void *) NULL,
(UBaseType_t) TASK1_PRIO ,
(TaskHandle_t *) &task1_handler );
xTaskCreate( (TaskFunction_t) task2,
(char *) "task2",
( configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void *) NULL,
(UBaseType_t) TASK2_PRIO ,
(TaskHandle_t *) &task2_handler );
vTaskDelete(NULL); //删除开始任务自身,传参NULL
taskEXIT_CRITICAL(); /*退出临界区*/
//临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}
/********任务1的任务函数,无返回值且是死循环***********/
/*****任务1:释放二值信号量*******/
void task1(void* args)
{
uint8_t key=0;
BaseType_t xReturn;
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES )
{
xReturn = xSemaphoreGive(semphore_handle); //释放二值信号量
if(xReturn!=pdPASS)
{
printf("释放二值信号量失败\n");
}
}
vTaskDelay(10); //FreeRTOS自带的延时函数,延时10毫秒
}
}
/****如果按键未按下,信号量不会释放,任务2获取信号量就会失败,进入阻塞态不会因为它的优先级高执行,必须等任务1完成****/
/********任务2的任务函数,无返回值且是死循环***********/
/***任务2:获取二值信号量*******/
void task2(void* args)
{
uint32_t i=0;
BaseType_t xReturn;
while(1)
{
xReturn= xSemaphoreTake(semphore_handle,portMAX_DELAY); //获取信号量,一直阻塞等待
if(xReturn==pdTRUE)
{
printf("获取二值信号量成功!:%d\n",++i);
}
}
}
//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
semphore_handle=xSemaphoreCreateBinary(); //创建二值信号量
if(semphore_handle!=NULL)
{
printf("二值信号量创建成功\n");
}
/***开始任务的创建***/
xTaskCreate( (TaskFunction_t) start_task,
(char *) "start_task",
( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void *) NULL,
(UBaseType_t) START_TASK_PRIO ,
(TaskHandle_t *) &start_task_handler );
vTaskStartScheduler(); //开启任务调度器
}
主函数任务调度文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
extern TaskHandle_t Start_Handle;
int main(void)
{
//硬件初始化
My_UsartInit();
//调用入口函数
freertos_demo();
}
四、计数型信号量
4.1 计数型信号量的概念
计数型信号量相当于队列长度大于1 的队列(信号量的上限值),因此计数型信号量能够容纳多个资源,这在计数型信号量被创建的时候确定的。
4.2 计数型信号量的API函数接口
4.2.1 使用计数信号量的过程
使用计数型信号量的过程:创建计数型信号量 ——>释放信号量 ——>获取信号量
4.2.2 创建计数信号量函数
下面的函数用于创建一个计数型信号
xSemaphoreCreateCounting(uxMaxCount, uxInitialCount );
从上面我们可以知道,此函数可以创建计数信号量,指定信号量的最大值和信号量的初始值,创建成功返回对应的信号量句柄,因此,在最开始我们需要提前定义好信号量句柄!
4.2.3 释放计数信号量和获取计数信号量
计数型信号量的释放和获取与二值信号量相同 !
4.2.4 获取信号量当前计数值大小
下面这个函数用于获取信号量当前计数值大小
uxSemaphoreGetCount( xSemaphore );
4.3 计数型信号量实验
创建任务文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "semphr.h"
/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);
/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK1_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK1_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t task1_handler; //定义任务句柄(结构体指针)
void task1(void* args);
/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define TASK2_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define TASK2_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t task2_handler; //定义任务句柄(结构体指针)
void task2(void* args);
QueueHandle_t count_semphore_handle; //创建二值信号量
/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{
taskENTER_CRITICAL(); /*进入临界区*/
xTaskCreate( (TaskFunction_t) task1,
(char *) "task1",
( configSTACK_DEPTH_TYPE) TASK1_STACK_SIZE,
(void *) NULL,
(UBaseType_t) TASK1_PRIO ,
(TaskHandle_t *) &task1_handler );
xTaskCreate( (TaskFunction_t) task2,
(char *) "task2",
( configSTACK_DEPTH_TYPE) TASK2_STACK_SIZE,
(void *) NULL,
(UBaseType_t) TASK2_PRIO ,
(TaskHandle_t *) &task2_handler );
vTaskDelete(NULL); //删除开始任务自身,传参NULL
taskEXIT_CRITICAL(); /*退出临界区*/
//临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}
/********任务1的任务函数,无返回值且是死循环***********/
/*****任务1:释放计数型信号量*******/
void task1(void* args)
{
uint8_t key=0;
while(1)
{
key=KEY_Scan(0);
if(key==KEY0_PRES )
{
xSemaphoreGive(count_semphore_handle); //释放计数型信号量
}
vTaskDelay(10); //FreeRTOS自带的延时函数,延时10毫秒
}
}
/****如果按键未按下,信号量不会释放,任务2获取信号量就会失败,进入阻塞态不会因为它的优先级高执行,必须等任务1完成****/
/********任务2的任务函数,无返回值且是死循环***********/
/***任务2:获取计数型信号量*******/
void task2(void* args)
{
BaseType_t xReturn;
while(1)
{
xReturn= xSemaphoreTake(count_semphore_handle,portMAX_DELAY); //获取计数型信号量,一直阻塞等待
if(xReturn==pdTRUE)
{
printf("信号量的计数值为:%d\n",(int )uxSemaphoreGetCount(count_semphore_handle));
}
vTaskDelay(1000); //延时1秒
}
}
//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
count_semphore_handle=xSemaphoreCreateCounting( 100, 0 ); //创建计数型信号量
if(count_semphore_handle!=NULL)
{
printf("计数型信号量创建成功\n");
}
/***开始任务的创建***/
xTaskCreate( (TaskFunction_t) start_task,
(char *) "start_task",
( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void *) NULL,
(UBaseType_t) START_TASK_PRIO ,
(TaskHandle_t *) &start_task_handler );
vTaskStartScheduler(); //开启任务调度器
}
主函数调用入口函数文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
extern TaskHandle_t Start_Handle;
int main(void)
{
//硬件初始化
My_UsartInit();
//调用入口函数
freertos_demo();
}
六、优先级翻转简介
6.1 概念
优先级翻转:高优先级的任务反而慢执行,低优先级的任务反而优先执行。优先级翻转在抢占式内核中是非常常见的,但是在实时操作系统中是不允许出现优先级翻转的,因为优先级翻转会破坏任务的预期顺序,可能会导致未知的严重后果。在使用二值信号量的时候,经常会遇到优先级翻转的问题。
6.2 示意图理解
6.3 优先级翻转实验
创建任务文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "semphr.h"
/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);
/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define low_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define low_task_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t low_task_handler; //定义任务句柄(结构体指针)
void low_task(void* args);
/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define middle_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define middle_task_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t middle_task_handler; //定义任务句柄(结构体指针)
void middle_task(void* args);
/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define high_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define high_task_PRIO 4 //定义任务优先级,0-31根据任务需求
TaskHandle_t high_task_handler; //定义任务句柄(结构体指针)
void high_task(void* args);
QueueHandle_t semphore_handle; //创建二值信号量句柄
/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{
taskENTER_CRITICAL(); /*进入临界区*/
xTaskCreate( (TaskFunction_t) low_task,
(char *) "low_task",
( configSTACK_DEPTH_TYPE) low_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) low_task_PRIO ,
(TaskHandle_t *) &low_task_handler );
xTaskCreate( (TaskFunction_t) middle_task,
(char *) "middle_task",
( configSTACK_DEPTH_TYPE) middle_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) middle_task_PRIO ,
(TaskHandle_t *) &middle_task_handler );
xTaskCreate( (TaskFunction_t) high_task,
(char *) "high_task",
( configSTACK_DEPTH_TYPE) high_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) high_task_PRIO ,
(TaskHandle_t *) &high_task_handler );
vTaskDelete(NULL); //删除开始任务自身,传参NULL
taskEXIT_CRITICAL(); /*退出临界区*/
//临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}
/********任务1的任务函数,无返回值且是死循环***********/
/*****任务1:低优先级任务*******/
void low_task(void* args)
{
while(1)
{
printf("low_task任务获取信号量!\n");
xSemaphoreTake(semphore_handle,portMAX_DELAY );
printf("low_task任务正在运行!\n");
My_Delay_s(3);
printf("low_task任务释放信号量!\n");
xSemaphoreGive(semphore_handle); //释放信号量
vTaskDelay(1000); //FreeRTOS自带的延时函数,延时10毫秒
}
}
/****如果按键未按下,信号量不会释放,任务2获取信号量就会失败,进入阻塞态不会因为它的优先级高执行,必须等任务1完成****/
/********任务2的任务函数,无返回值且是死循环***********/
/***任务2:中优先级任务*******/
void middle_task(void* args)
{
while(1)
{
printf("middle_task任务正在运行!\n");
vTaskDelay(1000);
}
}
/********任务3的任务函数,无返回值且是死循环***********/
/***任务3:高优先级任务*******/
void high_task(void* args)
{
while(1)
{
printf("high_task任务获取信号量!\n");
xSemaphoreTake(semphore_handle,portMAX_DELAY );
printf("high_task任务正在运行!\n");
My_Delay_s(1);
printf("high_task任务释放信号量!\n");
xSemaphoreGive(semphore_handle); //释放信号量
vTaskDelay(1000); //FreeRTOS自带的延时函数,延时10毫秒
}
}
//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
semphore_handle=xSemaphoreCreateBinary(); //创建二值信号量
if(semphore_handle!=NULL)
{
printf("二值信号量创建成功\n");
}
xSemaphoreGive(semphore_handle); //释放信号量
/***开始任务的创建***/
xTaskCreate( (TaskFunction_t) start_task,
(char *) "start_task",
( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void *) NULL,
(UBaseType_t) START_TASK_PRIO ,
(TaskHandle_t *) &start_task_handler );
vTaskStartScheduler(); //开启任务调度器
}
主函数调用文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
extern TaskHandle_t Start_Handle;
int main(void)
{
//硬件初始化
My_UsartInit();
//调用入口函数
freertos_demo();
}
八、互斥信号量
8.1 互斥信号量的概念
互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中二值信号量最适合。互斥信号量适合用于那些需要互斥访问的应用中! (优先级继承就是用来解决优先级翻转问题的!)
优先级继承:当一个互斥信号量正在被一个低优先级的任务持有时, 如果此时有个高优先级的任务也尝试获取这个互斥信号量,那么这个高优先级的任务就会被阻塞。不过这个高优先级的任务会将低优先级任务的优先级提升到与自己相同的优先级。
高优先级任务必须等到中优先级任务M执行完毕,然后低优先级任务也执行完毕释放信号量,才能轮到高优先级任务获取信号量成功,然后才能运行!
修改如下:
因为修改了低优先级任务的优先级,所以,此时中优先级任务不会再抢占低优先级任务执行,只要低优先级执行完毕,然后释放信号量,高优先级任务就可以执行,节省了中优先级任务执行的时间!此时任务H的阻塞时间仅仅是任务L 的执行时间,将优先级翻转的危害降到了最低
8.2 互斥信号量的特点
优先级继承并不能完全的消除优先级翻转的问题,它只是尽可能的降低优先级翻转带来的影响
注意:互斥信号量不能用于中断服务函数中,原因如下:
(1) 互斥信号量有任务优先级继承的机制, 但是中断不是任务,没有任务优先级, 所以互斥信号量只能用与任务中,不能用于中断服务函数。
(2) 中断服务函数中不能因为要等待互斥信号量而设置阻塞时间进入阻塞态。(中断是一瞬间的事,非常紧急,不可进行阻塞)
8.3 互斥信号量的API函数接口
8.3.1 使用互斥信号量的过程
使用互斥信号量:首先将宏configUSE_MUTEXES置1。
使用流程:创建互斥信号量 ——>(take)获取信号量 ——>(give)释放信号量。
注意:创建互斥信号量时,函数内部会主动释放一次信号量!所以,一开始创建的互信信号量的计数值为1,它是有资源的。
8.3.2 创建互斥信号量函数
函数 | 描述 |
xSemaphoreCreateMutex() | 使用动态方法创建互斥信号量。 |
xSemaphoreCreateMutexStatic() | 使用静态方法创建互斥信号量。 |
#define xSemaphoreCreateMutex() xQueueCreateMutex( queueQUEUE_TYPE_MUTEX )
返回值 | 描述 |
NULL | 创建失败 |
其他值 | 创建成功返回互斥信号量的句柄 |
从上面我们可以知道,此函数可以创建互斥信号量,创建成功返回对应的信号量句柄,因此,在最开始我们需要提前定义好互斥信号量句柄!
8.3.3 释放互斥信号量和获取互斥信号量函数
互斥信号量的释放和获取函数与二值信号量相同 !只不过互斥信号量不支持中断中调用!
8.4互斥信号量实验
创建任务文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
#include "mydelay.h"
#include "mykey.h"
#include "semphr.h"
/**********************START_TASK任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define START_TASK_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define START_TASK_PRIO 1 //定义任务优先级,0-31根据任务需求
TaskHandle_t start_task_handler; //定义任务句柄(结构体指针)
void start_task(void* args);
/**********************TASK1任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define low_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define low_task_PRIO 2 //定义任务优先级,0-31根据任务需求
TaskHandle_t low_task_handler; //定义任务句柄(结构体指针)
void low_task(void* args);
/**********************TASK2任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define middle_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define middle_task_PRIO 3 //定义任务优先级,0-31根据任务需求
TaskHandle_t middle_task_handler; //定义任务句柄(结构体指针)
void middle_task(void* args);
/**********************TASK3任务配置******************************/
/***********包括任务堆栈大小、任务优先级、任务句柄、创建任务***********/
#define high_task_STACK_SIZE 128 //定义堆栈大小为128字(1字等于4字节)
#define high_task_PRIO 4 //定义任务优先级,0-31根据任务需求
TaskHandle_t high_task_handler; //定义任务句柄(结构体指针)
void high_task(void* args);
QueueHandle_t mutex_semphore_handle; //创建互斥信号量句柄
/*********开始任务用来创建一个任务,只创建一次,不能是死循环,创建完这个任务后删除开始任务本身***********/
void start_task(void* args)
{
taskENTER_CRITICAL(); /*进入临界区*/
xTaskCreate( (TaskFunction_t) low_task,
(char *) "low_task",
( configSTACK_DEPTH_TYPE) low_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) low_task_PRIO ,
(TaskHandle_t *) &low_task_handler );
xTaskCreate( (TaskFunction_t) middle_task,
(char *) "middle_task",
( configSTACK_DEPTH_TYPE) middle_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) middle_task_PRIO ,
(TaskHandle_t *) &middle_task_handler );
xTaskCreate( (TaskFunction_t) high_task,
(char *) "high_task",
( configSTACK_DEPTH_TYPE) high_task_STACK_SIZE,
(void *) NULL,
(UBaseType_t) high_task_PRIO ,
(TaskHandle_t *) &high_task_handler );
vTaskDelete(NULL); //删除开始任务自身,传参NULL
taskEXIT_CRITICAL(); /*退出临界区*/
//临界区内不会进行任务的调度切换,出了临界区才会进行任务调度,抢占式
}
/********任务1的任务函数,无返回值且是死循环***********/
/*****任务1:低优先级任务*******/
void low_task(void* args)
{
while(1)
{
printf("low_task任务获取信号量!\n");
xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY );
printf("low_task任务正在运行!\n");
My_Delay_s(3);
printf("low_task任务释放信号量!\n");
xSemaphoreGive(mutex_semphore_handle); //释放信号量
vTaskDelay(1000); //FreeRTOS自带的延时函数,延时10毫秒
}
}
/****如果按键未按下,信号量不会释放,任务2获取信号量就会失败,进入阻塞态不会因为它的优先级高执行,必须等任务1完成****/
/********任务2的任务函数,无返回值且是死循环***********/
/***任务2:中优先级任务*******/
void middle_task(void* args)
{
while(1)
{
printf("middle_task任务正在运行!\n");
vTaskDelay(1000);
}
}
/********任务3的任务函数,无返回值且是死循环***********/
/***任务3:高优先级任务*******/
void high_task(void* args)
{
while(1)
{
printf("high_task任务获取信号量!\n");
xSemaphoreTake(mutex_semphore_handle,portMAX_DELAY );
printf("high_task任务正在运行!\n");
My_Delay_s(1);
printf("high_task任务释放信号量!\n");
xSemaphoreGive(mutex_semphore_handle); //释放信号量
vTaskDelay(1000); //FreeRTOS自带的延时函数,延时10毫秒
}
}
//FreeRTO入口例程函数,无参数,无返回值
void freertos_demo(void)
{
mutex_semphore_handle= xSemaphoreCreateMutex(); //创建互斥信号量,并且主动释放一次信号量
if(mutex_semphore_handle!=NULL)
{
printf("互斥信号量创建成功\n");
}
/***开始任务的创建***/
xTaskCreate( (TaskFunction_t) start_task,
(char *) "start_task",
( configSTACK_DEPTH_TYPE) START_TASK_STACK_SIZE,
(void *) NULL,
(UBaseType_t) START_TASK_PRIO ,
(TaskHandle_t *) &start_task_handler );
vTaskStartScheduler(); //开启任务调度器
}
主函数调用任务文件
#include "stm32f4xx.h" // Device header
#include "stdio.h"
#include "myled.h"
#include "myusart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "dynamic.h"
extern TaskHandle_t Start_Handle;
int main(void)
{
//硬件初始化
My_UsartInit();
//调用入口函数
freertos_demo();
}
至此,已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见!