FreeRTOS基础(十二):信号量

news2024/11/28 18:44:52

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

目录

一、信号量的简介

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();
	 
}



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

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

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

相关文章

[linux] makefilegdb理解

目录 Linux项目自动化构建工具-make/Makefile 背景 理解 依赖关系 依赖方法 原理 Linux调试器-gdb使用 背景 开始使用 Linux项目自动化构建工具-make/Makefile 背景 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力 一个工…

Spring Boot通过自定义注解和Redis+Lua脚本实现接口限流

😄 19年之后由于某些原因断更了三年,23年重新扬帆起航,推出更多优质博文,希望大家多多支持~ 🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Mi…

关于python中的列表和元组

变量就是一块内存空间,用来表示/存储数据 如果表示的数据较少,直接定义几个变量就行了 但是也有的时候,要表示的数据就比较多,如果只是通过定义一个变量来表示一个数据的话,这样的工作效率太低, 所以在p…

C++基础教程

目录 一.简介 二.基本语法 三. 面向对象 四.总结 一.简介 C 是一种高级语言,它是由 Bjarne Stroustrup 于 1979 年在贝尔实验室开始设计开发的。C 进一步扩充和完善了 C 语言,是一种面向对象的程序设计语言。C 可运行于多种平台上,如 Win…

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

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

【C++题解】1265. 爱因斯坦的数学题

问题:1265. 爱因斯坦的数学题 类型:简单循环 题目描述: 爱因斯坦出了一道这样的数学题:有一条长阶梯,若每步跨 2 阶,则最最后剩一阶,若每步跨 3 阶,则最后剩 2 阶,若每…

webapi跨越问题

由于浏览器存在同源策略,为了防止 钓鱼问题,浏览器直接请求才不会有跨越的问题 浏览器要求JavaScript或Cookie只能访问同域下的内容 浏览器也是一个应用程序,有很多限制,不能访问和使用电脑信息(获取cpu、硬盘等&#…

JWT 从入门到精通

什么是 JWT JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案 JSON Web Token Introduction - jwt.ioLearn about JSON Web Tokens, what are they, how they work, when and why you should use them.https://jwt.io/introduction 一、常见会…

Kimichat使用案例010:快速识别出图片中的表格保存到Excel

文章目录 一、介绍二、图片信息三、输入内容四、输出内容五、markdown提示词六、markdown输出一、介绍 如果有一张图片格式的表格,想要快速复制到Excel表格中,那么一般要借助于OCR工具。之前试过不少在线OCR工具,识别效果差强人意。其实,kimichat就可以非常好的完成这个任务…

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

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

Scanpy(3)单细胞数据分析常规流程

单细胞数据分析常规流程 面对高效快速的要求上,使用R分析数据越来越困难,转战Python分析,我们通过scanpy官网去学习如何分析单细胞下游常规分析。 数据3k PBMC来自健康的志愿者,可从10x Genomics免费获得。在linux系统上,可以取消注释并运行以下操作来下载和解压缩数据。…

【Python机器学习】PCA——特征提取(2)

上一篇写过了用单一最近邻分类器训练后的精度只有0.22. 现在用PCA。想要度量人脸的相似度,计算原始像素空间中的距离是一种相当糟糕的方法。用像素表示来比较两张图像时,我们比较的是每个像素的灰度值与另一张图像对应位置的像素灰度值。这种表示与人们…

IDEA下项目发送到Gitee

一、首先在Gitee创建一个仓库(什么都不选,这是最简单的方式,否则需要 pull push等一些操作,我嫌麻烦) 二、按图点击(创建存储区,选择你要上传的项目) 三、按图点击后正常文件名会变绿…

信息安全与密码技术概述

1. 信息安全的法律法规 2016年11月7日,中华人民共和国第十二届全国人民代表大会常务委员会第二十四次会议通过《中华人民共和国网络安全法》,自2017年6月1日起施行。 2019年10月26日,中华人民共和国第十三届全国人民代表大会常务委员会第十四…

Ubuntu虚拟机使用纯命令行对根分区进行扩展

Ubuntu虚拟机使用纯命令行对根分区进行扩展 前排提示 因为Ubuntu再安装时,根分区是没有使用LVM进行磁盘管理的,所以如果想扩展根分区,我们不得不使用另外一种暴力的方法。简单来说就是利用fdisk删除原来的根分区再基于原来的起始块号重新建…

PHP超详细安装及应用

目录 所需安装包如下 一、PHP安装 依赖包安装 安装扩展工具(先将PHP所需的软件包全部拖进centos根目录下) 安装libmcrypt 安装mhash 安装mcrypt 安装PHP 二、设置LAMP组件环境(要保证mysql、http都安装完成了) Php.ini的建…

MySQL常用的库操作、表操作、INSERT、DELETE

库操作 查询数据库: show databases; 创建数据库: create database chat; 删除数据库: drop database chat; 选择数据库: use chat; 表操作 查询表: show tables&am…

数据库同步软件PanguSync常见错误解决方法

​​​​​​在部署PanguSync数据库同步软件的过程中,常常会遇见一些错误提示,某些老铁可能会一脸懵逼,本文对一些常见的错误信息进行了总结,并提供了解决方法。 1.")"附近有语法错误 该问题是由于源表未设置主键&…

中国现代书法第一人颜廷利:全球知名哲学家思想家教育家

在人生的旅途中,如果一个人的所有追求仅仅是为了满足自己的需求和欲望,而非出于善意、行善或造福他人,那么无论其遭受了多少苦难,这样的生活是难以赢得他人的同情或怜悯的。这是东方哲学家、科学家颜廷利教授在其著作《升命学说》…

如何借助ChatGPT写文献综述?从文献搜索到综述生成,顶级高效指令值得收藏

欢迎关注EssayBot,为大家带来最酷最有效的智能AI学术科研写作攻略。关于使用ChatGPT等AI工具的相关问题可以添加作者七哥沟通 在学术研究的过程中,文献综述是学术论文的核心组成部分,其主要目的在于对特定问题的先行研究成果进行总结和梳理&a…