FreeRTOS基础(十二):信号量

          本篇博客,我们详细介绍另一个重要的应用,信号量。

目录

一、信号量的简介

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);    //删除开始任务自身,传参NULLtaskEXIT_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);    //删除开始任务自身,传参NULLtaskEXIT_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);    //删除开始任务自身,传参NULLtaskEXIT_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);    //删除开始任务自身,传参NULLtaskEXIT_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();}

至此,已经讲解完毕!初次学习,循序渐进,一步步掌握即可!以上就是全部内容!请务必掌握,创作不易,欢迎大家点赞加关注评论,您的支持是我前进最大的动力!下期再见! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/24870.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PDF转图片工具

背景: 今天有个朋友找我:“我有个文件需要更改,但是文档是PDF的,需要你帮我改下内容,你是搞软件的,这个对你应该是轻车熟路了吧,帮我弄弄吧”,听到这话我本想反驳,我是开…

IT闲谈-IMD是什么,有什么优势

目录 一、引言二、IDM是什么?三、IDM的优势1. 高速下载2. 稳定性强3. 强大的任务管理4. 视频下载5. 浏览器整合 四、应用场景1. 商务办公2. 教育学习3. 娱乐休闲 总结 一、引言 在数字化时代,下载管理器已成为我们日常工作和生活中不可或缺的工具。而在…

王学岗鸿蒙开发(北向)——————(四、五、六)ArkUi声明式组件

普通组件 1,注意,如上图,build只能有一个根节点 2,Entry表示程序的入口 Component表示自定义的组件 Preview表示可以预览 3,图片存放的地方 4, Image组件最好只给宽度,给了高度又给宽度容易失真。 build() {Row() {/…

normalizing flows vs 直方图规定化

normalizing flows名字的由来 The base density P ( z ) P(z) P(z) is usually defined as a multivariate standard normal (i.e., with mean zero and identity covariance). Hence, the effect of each subsequent inverse layer is to gradually move or “flow” the da…

第十二章:净世山的终极考验

虽然击败了黑袍人,但四人并未有丝毫的松懈。他们深知,净世山的考验远不止如此。果然,随着黑袍人的倒下,整个山顶开始剧烈震动,仿佛有什么东西即将苏醒。“小心,这山顶似乎有变!”赵无极大声提醒…

Java——JVM

前言 JVM.即Java虚拟机.用来解释执行Java字节码. 一、JVM中的内存区域划分 JVM其实也是一个进程,进程运行过程中,要从操作系统这里申请一些资源(内存就是其中的典型资源) 这些内存空间,就支撑了后续Java程序的执行. JVM从系统中申请了一大块内存,这一大块内存给Java程序使…

18、关于优化中央企业资产评估管理有关事项的通知

一、加强重大资产评估项目管理 (一)中央企业应当对资产评估项目实施分类管理,综合考虑评估目的、评估标的资产规模、评估标的特点等因素,合理确定本集团重大资产评估项目划分标准,原则上,企业对外并购股权项目应纳入重大资产评估项目。中央企业应当研究制定重大资产评估…

WebSocket首次使用踩坑记录

背景 IOT服务,后台收到信息推送至前端进行实时日志打印。 实现步骤 Springboot版本:2.5.15 1、增加依赖,我的是jdk1.8编写时发现需要用到javax.websocket但是我默认没有的,故此添加第二个依赖,引入javax.websocket。…

折腾日记:如何在Mac上连接Esp32

个人博客地址 最近购买了一块Esp32单片机,在Mac环境上进行开发,并且成功点亮LED灯和连上屏幕,为什么会上手选择Esp32开发板,主要考虑它自带Wi-Fi和蓝牙,单价也不高,就算后面不玩了,也能转成物联…

深度学习:如何静悄悄地改变我们的日常生活

深度学习 深度学习:如何静悄悄地改变我们的日常生活一、消费电子产品智能手机与个人助理娱乐与社交媒体 二、医疗健康三、汽车与交通四、公共安全五、总结 深度学习:如何静悄悄地改变我们的日常生活 在近年来,深度学习技术因其强大的数据处理…

LibreOffice电子表格如何实现快速筛选并将结果放到新的工作表

如果是在excel或者wps中,可能大家都习惯了自动筛选,然后复制到新的工作表或者删除掉复制内容的办法。但是在LibreOffice中,经测试,大数据表的删除或者复制是非常慢的。这也是很多人放弃LibreOffice的原因之一。那么我们如何快速筛…

Flutter 使用ffigen生成ffmpeg的dart接口

Flutter视频渲染系列 第一章 Android使用Texture渲染视频 第二章 Windows使用Texture渲染视频 第三章 Linux使用Texture渲染视频 第四章 全平台FFICustomPainter渲染视频 第五章 Windows使用Native窗口渲染视频 第六章 桌面端使用texture_rgba_renderer渲染视频 第七章 使用ff…

代码随想录算法训练营day46 | 完全背包、518. 零钱兑换 II、377. 组合总和 Ⅳ

完全背包 完全背包相对于01背包来说物品有无限个 代码的不同主要体现在遍历顺序上, 完全背包的背包重量不需要倒序遍历,因为物品有无限个,可以被无限添加;并且因为背包重量正序遍历,后续的值依赖于前面的值&#xff…

【C语言】一节课拿捏---动态内存分配

谢谢观看!希望以下内容帮助到了你,对你起到作用的话,可以一键三连加关注!你们的支持是我更新地动力。 因作者水平有限,有错误还请指出,多多包涵,谢谢! 目录 一、 为什么要有动态内存…

PgSQL技术内幕 - psql与服务端连接与交互机制

PgSQL技术内幕 - 客户端psql与服务端连接与交互机制 简单来说,PgSQL的psql客户端向服务端发起连接请求,服务端接收到请求后,fork出一个子进程,之后由该子进程和客户端进行交互,处理客户端的SQL等,并将结果返…

【Python】一文向您详细介绍 __str__ 的作用和用法

【Python】一文向您详细介绍 str 的作用和用法 下滑即可查看博客内容 🌈 欢迎莅临我的个人主页 👈这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地!🎇 🎓 博主简介:985高校的普通本硕&…

【CS.AI】AI引领编程新时代:深度探索GitHub Copilot

文章目录 引言0. TOP TAKEAWAYS 重要要点1. Copilot的基本功能2. 技术原理3. 优势与局限优势局限 4. 使用体验4.1 初次使用4.2 在 JetBrains 全家桶中使用 GitHub Copilot1. 安装插件2. 配置插件3. 使用 GitHub Copilot 4.3 日常开发4.4 体验与反馈 5. 对开发者生态系统的影响5…

梯度下降: 02. 批量梯度下降BGD,随机梯度下降SGD,小批量梯度下降MBGD

简介 本文从原理上介绍了三种梯度下降的方法,相同点,异同点,优缺点。 内容包含了数学公式的推导与说明 1. 梯度下降的3种方法 梯度下降分三类,原理基本相同,操作方式略有区别 批量梯度下降BGD(BatchGradient Descent):使用全量数据进行特征抽取,模型训练小批量梯度下降…

VueRouter3学习笔记

文章目录 1,入门案例2,一些细节高亮效果非当前路由会被销毁 3,嵌套路由4, 传递查询参数5,命名路由6,传递路径参数7,路径参数转props8,查询参数转props9,replace模式10&am…

【C++】深入理解decltype和decltype(auto)

深入理解decltype和decltype(auto) 一、decltype语法介绍二、decltype的推导规则1. expr不加括号2. expr加上括号 三、关于decltype的CV属性推导四、 decltype(auto) 的使用 一、decltype语法介绍 decltype关键字是C11新标准引入的关键字,它…