FreeRTOS互斥量的实验

news2024/10/5 14:40:40

互斥量又称互斥信号量(本质是信号量),是一种特殊的二值信号量,它和 信号量不同的是,它支持互斥量所有权、递归访问以及防止优先级翻转的特性, 用于实现对临界资源的独占式处理。本章要实现的功能是:创建了 3 个任务与 1 个二值信号量,任务分别是高优先级任务,中优先级任务,低优先级任务,用于 模拟产生优先级翻转。低优先级任务在获取信号量的时候,被中优先级打断,中 优先级的任务执行时间较长,因为低优先级还未释放信号量,那么高优先级任务 就无法取得信号量继续运行,此时就发生了优先级翻转,任务在运行中,使用串 口打印出相关信息。本章分为如下几部分内容: 8.1 互斥量简介 8.2 常用互斥量 API 函数 8.3 硬件设计 8.4 软件设计 8.5 实验现 象

8.1 互斥量简介

互斥信号量其实就是一个拥有优先级继承的二值信号量,在同步的应用中 (任务与任务或中断与任务之间的同步)二值信号量最适合。互斥信号量适合用于 那些需要互斥访问的应用中。在互斥访问中互斥信号量相当于一个钥匙,当任务 想要使用资源的时候就必须先获得这个钥匙,当使用完资源以后就必须归还这个 钥匙,这样其他的任务就可以拿着这个钥匙去使用资源。 互斥信号量使用和二值信号量相同的 API 操作函数,所以互斥信号量也可 以设置阻塞时间,不同于二值信号量的是互斥信号量具有优先级继承的特性。当 一个互斥信号量正在被一个低优先级的任务使用,而此时有个高优先级的任务也 尝试获取这个互斥信号量的话就会被阻塞。不过这个高优先级的任务会将低优先 级任务的优先级提升到与自己相同的优先级,这个过程就是优先级继承。优先级 继承尽可能的降低了高优先级任务处于阻塞态的时间,并且将已经出现的“优先 级翻转”的影响降到最低。

8.1.1 互斥量的优先级继承机制

在 FreeRTOS 操作系统中为了降低优先级翻转问题利用了优先级继承算法。 优先级继承算法是指,暂时提高某个占有某种资源的低优先级任务的优先级,使 之与在所有等待该资源的任务中优先级最高那个任务的优先级相等,而当这个低 优先级任务执行完毕释放该资源时,优先级重新回到初始设定值。因此,继承优 先级的任务避免了系统资源被任何中间优先级的任务抢占。 互斥量与二值信号量最大的不同是:互斥量具有优先级继承机制,而信号量 没有。也就是说,某个临界资源受到一个互斥量保护,如果这个资源正在被一个 低优先级任务使用,那么此时的互斥量是闭锁状态,也代表了没有任务能申请到 这个互斥量,如果此时一个高优先级任务想要对这个资源进行访问,去申请这个 互斥量,那么高优先级任务会因为申请不到互斥量而进入阻塞态,那么系统会将 现在持有该互斥量的任务的优先级临时提升到与高优先级任务的优先级相同,这 个优先级提升的过程叫做优先级继承。这个优先级继承机制确保高优先级任务进 入阻塞状态的时间尽可能短,以及将已经出现的“优先级翻转”危害降低到最小。 没有理解?没问题,结合过程示意图再说一遍。我们知道任务的优先级在创 建的时候就已经是设置好的,高优先级的任务可以打断低优先级的任务,抢占 CPU 的使用权。但是在很多场合中,某些资源只有一个,当低优先级任务正在占 用该资源的时候,即便高优先级任务也只能乖乖的等待低优先级任务使用完该资 源后释放资源。这里高优先级任务无法运行而低优先级任务可以运行的现象称为 “优先级翻转”。 为什么说优先级翻转在操作系统中是危害很大?因为在我们一开始创造这 个系统的时候,我们就已经设置好了任务的优先级了,越重要的任务优先级越高。 但是发生优先级翻转,对我们操作系统是致命的危害,会导致系统的高优先级任 务阻塞时间过长。 举个例子,现在有 3 个任务分别为 H 任务(High)、M 任务(Middle)、 L 任务(Low),3 个任务的优先级顺序为 H 任务>M 任务>L 任务。正常运行的 时候 H 任务可以打断 M 任务与 L 任务,M 任务可以打断 L 任务,假设系统中 有一个资源被保护了,此时该资源被 L 任务正在使用中,某一刻,H 任务需要 使用该资源,但是 L 任务还没使用完,H 任务则因为申请不到资源而进入阻塞 态,L 任务继续使用该资源,此时已经出现了“优先级翻转”现象,高优先级任 务在等着低优先级的任务执行,如果在 L 任务执行的时候刚好 M 任务被唤醒 了,由于 M 任务优先级比 L 任务优先级高,那么会打断 L 任务,抢占了 CPU 的 使用权,直到 M 任务执行完,再把 CUP 使用权归还给 L 任务,L 任务继续执 行,等到执行完毕之后释放该资源,H 任务此时才从阻塞态解除,使用该资源。 这个过程,本来是最高优先级的 H 任务,在等待了更低优先级的 L 任务与 M 任 务,其阻塞的时间是 M 任务运行时间+L 任务运行时间,这只是只有 3 个任务 的系统,假如很多个这样子的任务打断最低优先级的任务,那这个系统最高优先 级任务岂不是崩溃了,这个现象是绝对不允许出现的,高优先级的任务必须能及 时响应。所以,没有优先级继承的情况下,使用资源保护,其危害极大如下图。

图(1):L 任务正在使用某临界资源, H 任务被唤醒,执行 H 任务。但 L 任 务并未执行完毕,此时临界资源还未释放。 图(2):这个时刻 H 任务也要对该临界资源进行访问,但 L 任务还未释放 资源,由于保护机制,H 任务进入阻塞态,L 任务得以继续运行,此时已经发生 了优先级翻转现象。 图(3):某个时刻 M 任务被唤醒,由于 M 任务的优先级高于 L 任务, M 任 务抢占了 CPU 的使用权,M 任务开始运行,此时 L 任务尚未执行完,临界资源 还没被释放。 图(4):M 任务运行结束,归还 CPU 使用权,L 任务继续运行。 图(5):L 任务运行结束,释放临界资源,H 任务得以对资源进行访问,H 任 务开始运行。 在这过程中,H 任务的等待时间过长,这对系统来说这是很致命的,所以这 种情况不允许出现,而互斥量就是用来降低优先级翻转的产生的危害。 假如有优先级继承呢?那么,在 H 任务申请该资源的时候,由于申请不到 资源会进入阻塞态,那么系统就会把当前正在使用资源的 L 任务的优先级临时 提高到与 H 任务优先级相同,此时 M 任务被唤醒了,因为它的优先级比 H 任 务低,所以无法打断 L 任务,因为此时 L 任务的优先级被临时提升到 H,所以 当 L 任务使用完该资源了,进行释放,那么此时 H 任务优先级最高,将接着抢 占 CPU 的使用权, H 任务的阻塞时间仅仅是 L 任务的执行时间,此时的优先 级的危害降到了最低,看!这就是优先级继承的优势,具体如下图。

图(1):L 任务正在使用某临界资源,L 任务正在使用某临界资源,H 任务被 唤醒,执行 H 任务。但 L 任务并未执行完毕,此时临界资源还未释放。 图(2):某一时刻 H 任务也要对该资源进行访问,由于保护机制,H 任务进 入阻塞态。此时发生优先级继承,系统将 L 任务的优先级暂时提升到与 H 任务 优先级相同,L 任务继续执行。 图(3):在某一时刻 M 任务被唤醒,由于此时 M 任务的优先级暂时低于 L 任务,所以 M 任务仅在就绪态,而无法获得 CPU 使用权。 图(4):L 任务运行完毕,H 任务获得对资源的访问权,H 任务从阻塞态变成 运行态,此时 L 任务的优先级会变回原来的优先级。 图(5):当 H 任务运行完毕,M 任务得到 CPU 使用权,开始执行。 图(6):系统正常运行,按照设定好的优先级运行。 但是使用互斥量的时候一定需要注意:在获得互斥量后,请尽快释放互斥量, 同时需要注意的是在任务持有互斥量的这段时间,不得更改任务的优先级。 FreeRTOS 的优先级继承机制不能解决优先级反转,只能将这种情况的影响降低 到最小,硬实时系统在一开始设计时就要避免优先级反转发生。

8.1.2 互斥量应用场景

互斥量的使用比较单一,因为它是信号量的一种,并且它是以锁的形式存在。 在初始化的时候,互斥量处于开锁的状态,而被任务持有的时候则立刻转为闭锁 的状态。互斥量更适合于: ●可能会引起优先级翻转的情况。 递归互斥量更适用于: ●任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持 有而造成死锁的问题。 多任务环境下往往存在多个任务竞争同一临界资源的应用场景,互斥量可被 用于对临界资源的保护从而实现独占式访问。另外,互斥量可以降低信号量存在 的优先级翻转问题带来的影响。 比如有两个任务需要对串口进行发送数据,其硬件资源只有一个,那么两个 任务肯定不能同时发送啦,不然导致数据错误,那么,就可以用互斥量对串口资 源进行保护,当一个任务正在使用串口的时候,另一个任务则无法使用串口,等 到任务使用串口完毕之后,另外一个任务才能获得串口的使用权。 另外需要注意的是互斥量不能在中断服务函数中使用,因为其特有的优先级 继承机制只在任务起作用,在中断的上下文环境毫无意

8.2 常用互斥量 API 函数

8.2.1 互斥量创建函数 xSemaphoreCreateMutex(

xSemaphoreCreateMutex()用于创建一个互斥量,并返回一个互斥量句柄。 该句柄的原型是一个 void 型的指针,在使用之前必须先由用户定义一个互斥量 句柄。要想使用该函数必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1, 即开启动态内存分配,其实该 宏在 FreeRTOS.h 中默认定义为 1,即所有 FreeRTOS 的对象在创建的时候都默 认使用动态内存分配方案,同时还需在 FreeRTOSConfig.h 中把 configUSE_MUTEXES 宏定义打开,表示使用互斥量。

8.2.2 递归互斥量创建函数 xSemaphoreCreateRecursiveMutex()

xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量,不是递归的 互斥量由函数 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic() 创建(我们只讲解动态创建),且只能被同一个任务获取一次,如果同一个任务 想再次获取则会失败。递归信号量则相反,它可以被同一个任务获取很多次,获 取多少次就需要释放多少次。递归信号量与互斥量一样,都实现了优先级继承机 制,可以降低优先级反转的危害。 要想使用该函数必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 和 configUSE_RECURSIVE_MUTEXES 均定 义为 1。宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 即表示开启动态内 存分配,其实该宏在 FreeRTOS.h 中默认定义为 1,即所有 FreeRTOS 的对象在 创建的时候都默认使用动态内存分配方案。该函数的具体说明如下:

8.2.3 互斥量删除函数 vSemaphoreDelete()

互斥量的本质是信号量,直接调用 vSemaphoreDelete()函数进行删除即可。

8.2.4 互斥量获取函数 xSemaphoreTake()

我们知道,当互斥量处于开锁的状态,任务才能获取互斥量成功,当任务持 有了某个互斥量的时候,其它任务就无法获取这个互斥量,需要等到持有互斥量 的任务进行释放后,其他任务才能获取成功,任务通过互斥量获取函数来获取互 斥量的所有权。任务对互斥量的所有权是独占的,任意时刻互斥量只能被一个任 务持有,如果互斥量处于开锁状态,那么获取该互斥量的任务将成功获得该互斥 量,并拥有互斥量的使用权;如果互斥量处于闭锁状态,获取该互斥量的任务将 无法获得互斥量,任务将被挂起,在任务被挂起之前,会进行优先级继承,如果 当前任务优先级比持有互斥量的任务优先级高,那么将会临时提升持有互斥量任 务的优先级。互斥量的获取函数是一个宏定义,实际调用的函数就是 xQueueGenericReceive(),

8.2.5 递归互斥量获取函数 xSemaphoreTakeRecursive()

xSemaphoreTakeRecursive()是一个用于获取递归互斥量的宏,与互斥量的 获取函数一样,xSemaphoreTakeRecursive()也是一个宏定义,它最终使用现有 的队列机制,实际执行的函数是 xQueueTakeMutexRecursive()。互斥量之前必 须由 xSemaphoreCreateRecursiveMutex()这个函数创建。要注意的是该函数不 能用于获取由函数 xSemaphoreCreateMutex()创建的互斥量。要想使用该函数必 须在头文件 FreeRTOSConfig.h 中把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。该函数的具体说明如下。

8.2.6 互斥量释放函数 xSemaphoreGive()

任务想要访问某个资源的时候,需要先获取互斥量,然后进行资源访问,在 任务使用完该资源的时候,必须要及时归还互斥量,这样别的任务才能对资源进 行访问。在前面的讲解中,我们知道,当互斥量有效的时候,任务才能获取互斥 量,那么,是什么函数使得信号量变得有效呢?FreeRTOS 给我们提供了互斥量 释放函数 xSemaphoreGive(),任务可以调用 xSemaphoreGive()函数进行释放互 斥量,表示我已经用完了,别人可以申请使用,互斥量的释放函数与信号量的释 放函数一致,都是调用 xSemaphoreGive()函数,但是要注意的是,互斥量的释 放只能在任务中,不允许在中断中释放互斥量

8.2.7 递归互斥量释放函数 xSemaphoreGiveRecursive()

xSemaphoreGiveRecursive()是一个用于释放递归互斥量的宏。

8.3整体代码

优先级翻转代码

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "semphr.h"


//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define LowPriority_TASK_PRIO        3
//任务堆栈大小    
#define LowPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t LowPriority_Task_Handle;
//任务函数
void LowPriority_Task(void *pvParameters);

//任务优先级
#define MidPriority_TASK_PRIO        4
//任务堆栈大小    
#define MidPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t MidPriority_Task_Handle;
//任务函数
void MidPriority_Task(void *pvParameters);

//任务优先级
#define HighPriority_TASK_PRIO        5
//任务堆栈大小    
#define HighPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t HighPriority_Task_Handle;
//任务函数
void HighPriority_Task(void *pvParameters);


SemaphoreHandle_t BinarySem_Handle =NULL;


/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS优先级翻转实验\r\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;
    taskENTER_CRITICAL();           //进入临界区
     
    /* 创建 BinarySem */
    BinarySem_Handle = xSemaphoreCreateBinary();
    xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量    
    
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
                
    //创建低优先级任务
    xTaskCreate((TaskFunction_t )LowPriority_Task,     
                (const char*    )"LowPriority_Task",   
                (uint16_t       )LowPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LowPriority_TASK_PRIO,
                (TaskHandle_t*  )&LowPriority_Task_Handle);

    //创建中优先级任务
    xTaskCreate((TaskFunction_t )MidPriority_Task,     
                (const char*    )"MidPriority_Task",   
                (uint16_t       )MidPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )MidPriority_TASK_PRIO,
                (TaskHandle_t*  )&MidPriority_Task_Handle);
                
    //创建高优先级任务
    xTaskCreate((TaskFunction_t )HighPriority_Task,     
                (const char*    )"HighPriority_Task",   
                (uint16_t       )HighPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )HighPriority_TASK_PRIO,
                (TaskHandle_t*  )&HighPriority_Task_Handle);
                
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//低优先级任务函数
void LowPriority_Task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
    static uint32_t i=0;
    
    while(1)
    {
        printf("LowPriority_Task 获取信号量\n");
        //获取二值信号量 xSemaphore,没获取到则一直等待
        xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */
                              portMAX_DELAY); /* 等待时间 */
        if( xReturn == pdTRUE )
            printf("LowPriority_Task Running\n\n");

        for(i=0;i<2000000;i++)//模拟低优先级任务占用信号量
        {
            taskYIELD();//发起任务调度
        }

        printf("LowPriority_Task 释放信号量!\r\n");
        xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量
        LED2=!LED2;
        vTaskDelay(500);
    }
}

//中优先级任务函数
void MidPriority_Task(void *pvParameters)
{
    while(1)
    {
        printf("MidPriority_Task Running\n");
        vTaskDelay(500);
    }
}

//高优先级任务函数
void HighPriority_Task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
    
    while(1)
    {
        printf("HighPriority_Task 获取信号量\n");
        //获取二值信号量 xSemaphore,没获取到则一直等待
        xReturn = xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */
                              portMAX_DELAY); /* 等待时间 */
        if(pdTRUE == xReturn)
            printf("HighPriority_Task Running\n");
        LED2=!LED2;
        xReturn = xSemaphoreGive( BinarySem_Handle );//给出二值信号量
        vTaskDelay(500);
    }
}

使用互斥量解决优先级翻转代码

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "key.h"
#include "semphr.h"


//任务优先级
#define START_TASK_PRIO        1
//任务堆栈大小    
#define START_STK_SIZE         128  
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);

//任务优先级
#define LED1_TASK_PRIO        2
//任务堆栈大小    
#define LED1_STK_SIZE         50  
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);

//任务优先级
#define LowPriority_TASK_PRIO        3
//任务堆栈大小    
#define LowPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t LowPriority_Task_Handle;
//任务函数
void LowPriority_Task(void *pvParameters);

//任务优先级
#define MidPriority_TASK_PRIO        4
//任务堆栈大小    
#define MidPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t MidPriority_Task_Handle;
//任务函数
void MidPriority_Task(void *pvParameters);

//任务优先级
#define HighPriority_TASK_PRIO        5
//任务堆栈大小    
#define HighPriority_STK_SIZE         512  
//任务句柄
TaskHandle_t HighPriority_Task_Handle;
//任务函数
void HighPriority_Task(void *pvParameters);


SemaphoreHandle_t MuxSem_Handle =NULL;


/*******************************************************************************
* 函 数 名         : main
* 函数功能           : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
int main()
{
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4
    LED_Init();
    KEY_Init();
    USART1_Init(115200);
    printf("FreeRTOS互斥量实验\r\n");
    //创建开始任务
    xTaskCreate((TaskFunction_t )start_task,            //任务函数
                (const char*    )"start_task",          //任务名称
                (uint16_t       )START_STK_SIZE,        //任务堆栈大小
                (void*          )NULL,                  //传递给任务函数的参数
                (UBaseType_t    )START_TASK_PRIO,       //任务优先级
                (TaskHandle_t*  )&StartTask_Handler);   //任务句柄              
    vTaskStartScheduler();          //开启任务调度
}

//开始任务任务函数
void start_task(void *pvParameters)
{
    taskENTER_CRITICAL();           //进入临界区
     
    /* 创建MuxSem */
    MuxSem_Handle = xSemaphoreCreateMutex();    
    
    //创建LED1任务
    xTaskCreate((TaskFunction_t )led1_task,     
                (const char*    )"led1_task",   
                (uint16_t       )LED1_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LED1_TASK_PRIO,
                (TaskHandle_t*  )&LED1Task_Handler); 
                
    //创建低优先级任务
    xTaskCreate((TaskFunction_t )LowPriority_Task,     
                (const char*    )"LowPriority_Task",   
                (uint16_t       )LowPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )LowPriority_TASK_PRIO,
                (TaskHandle_t*  )&LowPriority_Task_Handle);

    //创建中优先级任务
    xTaskCreate((TaskFunction_t )MidPriority_Task,     
                (const char*    )"MidPriority_Task",   
                (uint16_t       )MidPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )MidPriority_TASK_PRIO,
                (TaskHandle_t*  )&MidPriority_Task_Handle);
                
    //创建高优先级任务
    xTaskCreate((TaskFunction_t )HighPriority_Task,     
                (const char*    )"HighPriority_Task",   
                (uint16_t       )HighPriority_STK_SIZE, 
                (void*          )NULL,
                (UBaseType_t    )HighPriority_TASK_PRIO,
                (TaskHandle_t*  )&HighPriority_Task_Handle);
                
    vTaskDelete(StartTask_Handler); //删除开始任务
    taskEXIT_CRITICAL();            //退出临界区
} 

//LED1任务函数
void led1_task(void *pvParameters)
{
    while(1)
    {
        LED1=0;
        vTaskDelay(200);
        LED1=1;
        vTaskDelay(800);
    }
}

//低优先级任务函数
void LowPriority_Task(void *pvParameters)
{
    BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为pdPASS */
    static uint32_t i=0;
    
    while(1)
    {
        printf("LowPriority_Task 获取互斥量\n");
        //获取互斥量 MuxSem,没获取到则一直等待
        xReturn = xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */
                              portMAX_DELAY); /* 等待时间 */
        if( xReturn == pdTRUE )
            printf("LowPriority_Task Running\n\n");

        for(i=0;i<2000000;i++)//模拟低优先级任务占用互斥量
        {
            taskYIELD();//发起任务调度
        }

        printf("LowPriority_Task 释放互斥量!\r\n");
        xReturn = xSemaphoreGive( MuxSem_Handle );//给出互斥量
        LED2=!LED2;
        vTaskDelay(500);
    }
}

//中优先级任务函数
void MidPriority_Task(void *pvParameters)
{
    while(1)
    {
        printf("MidPriority_Task Running\n");
        vTaskDelay(500);
    }
}

//高优先级任务函数
void HighPriority_Task(void *pvParameters)
{
    BaseType_t xReturn = pdTRUE;
    
    while(1)
    {
        printf("HighPriority_Task 获取互斥量\n");
        //获取互斥量 MuxSem,没获取到则一直等待
        xReturn = xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */
                              portMAX_DELAY); /* 等待时间 */
        if(pdTRUE == xReturn)
            printf("HighPriority_Task Running\n");
        LED2=!LED2;
        xReturn = xSemaphoreGive( MuxSem_Handle );//给出互斥量
        vTaskDelay(500);
    }
}

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

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

相关文章

算法训练营 day24 回溯算法 回溯算法理论基础 组合

算法训练营 day24 回溯算法 回溯算法理论基础 组合 回溯算法理论基础 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 虽然回溯法很难&#xff0c;很不好理解&#xff0c;但是回溯法并不是什么高效的算法。 因为回溯的本质是穷举&#xff0c;穷举所有可能&am…

钉钉微应用 - - - - 如何本地开发调试?

钉钉微应用 - 本地开发调试1. 安装DingTalk-Design-CLI2. 初始化代码模版3. 启动开发、调试功能4. 遇到的问题4.1 对应企业没有xxx域名微应用&#xff1f;&#xff1f;4.2 history、location的表现异常&#xff1f;&#xff1f;4.3 本地已经存在了H5微应用&#xff0c;也想使用…

软件体系结构与架构技术知识点万字总结

文章目录页面技术一、Spring框架1. 三层体系架构2. Spring的核心3. Spring 的Bean中主要的装配方式&#xff08;1&#xff09;基于XML的装配&#xff1a;&#xff08;2&#xff09;基于Annotation的装配&#xff1a;&#xff08;3&#xff09;自动装配&#xff1a;4. Spring框架…

Linux 中启用 SSH 密码登录

Linux 中启用 SSH 密码登录 文章目录Linux 中启用 SSH 密码登录1、更改配置文件2、设置登录密码3、完成1、更改配置文件 首先使用 管理员 权限打开/etc/ssh/sshd_config文件。 sudo vi /etc/ssh/sshd_config找到 PasswordAuthentication 选项&#xff0c;耐心查找。 当然&am…

23种设计模式之十一种行为型模式

23种设计模式之十一种行为型模式1. 设计模式概述1.1 什么是设计模式1.2 设计模式的好处2. 设计原则分类3. 详解3.1 单一职责原则3.2 开闭原则3.3 里氏代换原则3.4 依赖倒转原则3.5 接口隔离原则3.6 合成复用原则3.7 迪米特法则4. Awakening1. 设计模式概述 我们的软件开发技术也…

车道线检测源码详解

源码链接见文末 1.车道数据与标签下载 数据下载地址:因为数据的规模比较大,源码中只包含了部分数据,能够供运行代码使用 CULane:https://xingangpan.github.io/projects/CULane.html tusimple:https://github.com/TuSimple/tusimple-benchmark/issues/3 2.项目环境配置 …

数据存储:MySQL之多表连接方式

在我们走出新手村&#xff0c;开始编写系统时&#xff0c;总会遇到各种复杂的场景需要多个数据库表的联查&#xff0c;这时我们就需要掌握多表查询有几种方式&#xff0c;以便我们在各种复杂的应用场景使用适宜的连接方式。 用于测试的表&#xff1a; student表 grade表 syst…

车载以太网 - SomeIP测试专栏 - 详细解析 - 02

对于介绍SomeIP协议&#xff0c;我还是想从最基础的协议解析来&#xff0c;所以今天还是先将SomeIP协议详解给大家列举一下&#xff0c;也方便大家在工作中如果不记得哪些信息随时可以查看学习&#xff0c;也算是留给我自己的笔记吧&#xff0c;毕竟确实容易忘记。 SomeIP数据&…

【GD32F427开发板试用】基于移植BP神经网络辨认花的种类

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;卢瑟 前言 很荣幸参与这次GD32的开发板试用活动(白嫖活动)&#xff0c;由于在家条件比较有限&#xff0c;就移植了之前做过的项目。利用神经网…

【Java开发常用软件整理】

该博客整理了一些JAVA程序员常用的软件开发类软件、系统类软件&#xff0c;可以作为JAVA程序员配置Java开发基础环境的参考手册。 目录开发类软件Java8 安装IntelliJ IDEA 安装Maven 安装Git 安装MYSQL 安装系统类软件开发类软件 开发软件安装包下载地址&#xff1a;开发软件安…

ASP.NET Core Web应用程序项目部署流程

目录 一、准备ASP.NET Core应用程序部署文件 二、环境配置 三、测试 ASP.NET Core Web 应用程序 四、部署后访问失败问题 以下部署流程都是基于Windows服务器环境下进行的。 一、准备ASP.NET Core应用程序部署文件 使用 Visual Studio 开发工具创建 ASP.NET Core 的Web应…

盘点最近线程池的几个面试重要考点

有点惊叹最近的面试题&#xff0c;因为从之前的基础的面试题&#xff0c;到之后的一些涉及到分布式和微服务的面试题&#xff0c;再到现在的线程池的一些面试题&#xff0c;反正不同的面试官&#xff0c;就有不同的针对方向&#xff0c;可能现在的面试官比较想考验你的多方面的…

Hive(2):Apache Hive 安装部署

1 元数据相关名词 1.1 Metadata Metadata即元数据。元数据包含用Hive创建的database、table、表的位置、类型、属性&#xff0c;字段顺序类型等元信息。元数据存储在关系型数据库中。如hive内置的Derby、或者第三方如MySQL等。 1.2 Metastore Metastore即元数据服务。Metast…

Python中tqdm进度条的详细介绍(安装程序与耗时的迭代)

平时在做一些测试时候&#xff0c;是没有进度条出现的&#xff0c;这跟大家pip安装程序不一样(有安装进度条)&#xff0c;比如做遍历的时候&#xff1a;for i in range(10):time.sleep(0.5)print(i)只是每过0.5秒就进行打印输出&#xff0c;在这个等待过程是没有任何提示的&…

SAP ADM100-2.1 SAP系统启停过程

一、SAP系统开启过程 在SAP系统使用过程中维护硬件和修改SAP系统配置文件后重启SAP系统是有必要的。开启SAP系统是每个SAP系统管理员应该熟悉的初始过程。 每个SAP系统包含一个数据库和至少一个实例,JAVA栈SAP系统还有一个CS中央服务实例,ABAP栈SAP系统含有一个ABAP CS中央服…

springboot2.5集成log4j2报错

报错信息&#xff1a; SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. 修改依赖 把依…

【01Studio MaixPy AI K210】22.ESP8266与服务器连接的问题及处理

目录 前提 问题 处理 前提 WiFi的账号密码输入正确 ESP8266的主题与服务器的主题一致 代码的服务器网址正确 ESP8266正确连接核心板的引脚 ESP8266返回“OK” simple.py文件以发送到开发板 WiFi已打开 问题 1.连接上WiFi&#xff0c;已输出IP信息&#xff0c;但是仍然…

48.Isaac教程--GMapping应用程序

GMapping应用程序 GMapping 是一个使用 OpenSlam 软件库的地图生成工具。 该应用程序允许您创建地图以在其他应用程序中使用。 GMapping 应用程序使用 Carter 参考机器人的 LIDAR 功能。 注意 建图是一项计算密集型和存储密集型活动&#xff0c;可能需要微调才能生成合适的…

Hive(1):Apache Hive入门

1 Apache Hive概述 1.1 什么是Hive Apache Hive是一款建立在Hadoop之上的开源数据仓库系统&#xff0c;可以将存储在Hadoop文件中的结构化、半结构化数据文件映射为一张数据库表&#xff0c;基于表提供了一种类似SQL的查询模型&#xff0c;称为Hive查询语言&#xff08;HQL&a…

记OPNsense防火墙的安装过程 - 安全

前些天在网上看到防火墙软件OPNsense&#xff0c;对其有了兴趣&#xff0c;以前写过一个其前面的一个软件M0n0wall( 关于m0n0wall的安装及配置 )&#xff0c;当时也是非常有名的防火墙&#xff0c;现在有了OPNsense&#xff0c;这个老防火墙已经停止更新了。 下面对OPNsense防火…