FreeRTOS互斥量 基于STM32
文章目录一、互斥量基本概念二、互斥量的优先级继承机制三、互斥量应用场景四、互斥量运作机制五、互斥量函数接口讲解1.互斥量创建函数 xSemaphoreCreateMutex()2.递归xSemaphoreCreateRecursiveMutex()3.互斥量删除函数 vSemaphoreDelete()4.互斥量获取函数 xSemaphoreTake()5.递归互斥量获取函数 xSemaphoreTakeRecursive()6.互斥量释放函数 xSemaphoreGive()7.递归互斥量释放函数 xSemaphoreGiveRecursive()六、互斥量实验1.拟优先级翻转实验2.互斥量实验七、实验现象一、互斥量基本概念互斥量又称互斥信号量本质是信号量是一种特殊的二值信号量它和信号量不 同的是它支持互斥量所有权、递归访问以及防止优先级翻转的特性用于实现对临界资 源的独占式处理。任意时刻互斥量的状态只有两种开锁或闭锁。当互斥量被任务持有时 该互斥量处于闭锁状态这个任务获得互斥量的所有权。当该任务释放这个互斥量时该 互斥量处于开锁状态任务失去该互斥量的所有权。当一个任务持有互斥量时其他任务 将不能再对该互斥量进行开锁或持有。持有该互斥量的任务也能够再次获得这个锁而不被 挂起这就是递归访问也就是递归互斥量的特性这个特性与一般的信号量有很大的不 同在信号量中由于已经不存在可用的信号量任务递归获取信号量时会发生主动挂起 任务最终形成死锁。 如果想要用于实现同步任务之间或者任务与中断之间二值信号量或许是更好的 选择虽然互斥量也可以用于任务与任务、任务与中断的同步但是互斥量更多的是用于 保护资源的互锁。 用于互锁的互斥量可以充当保护资源的令牌当一个任务希望访问某个资源时它必 须先获取令牌。当任务使用完资源后必须还回令牌以便其它任务可以访问该资源。是 不是很熟悉在我们的二值信号量里面也是一样的用于保护临界资源保证多任务的访 问井然有序。当任务获取到信号量的时候才能开始使用被保护的资源使用完就释放信号 量下一个任务才能获取到信号量从而可用使用被保护的资源。但是信号量会导致的另一 个潜在问题那就是任务优先级翻转具体会在下文讲解。而 FreeRTOS 提供的互斥量 可以通过优先级继承算法可以降低优先级翻转问题产生的影响所以用于临界资源的 保护一般建议使用互斥量。二、互斥量的优先级继承机制在 FreeRTOS 操作系统中为了降低优先级翻转问题利用了优先级继承算法。优先级继 承算法是指暂时提高某个占有某种资源的低优先级任务的优先级使之与在所有等待该 资源的任务中优先级最高那个任务的优先级相等而当这个低优先级任务执行完毕释放该 资源时优先级重新回到初始设定值。因此继承优先级的任务避免了系统资源被任何中 间优先级的任务抢占。 互斥量与二值信号量最大的不同是互斥量具有优先级继承机制而信号量没有。也 就是说某个临界资源受到一个互斥量保护如果这个资源正在被一个低优先级任务使用 那么此时的互斥量是闭锁状态也代表了没有任务能申请到这个互斥量如果此时一个高 优先级任务想要对这个资源进行访问去申请这个互斥量那么高优先级任务会因为申请 不到互斥量而进入阻塞态那么系统会将现在持有该互斥量的任务的优先级临时提升到与高优先级任务的优先级相同这个优先级提升的过程叫做优先级继承。这个优先级继承机 制确保高优先级任务进入阻塞状态的时间尽可能短以及将已经出现的“优先级翻转”危 害降低到最小。 没有理解没问题结合过程示意图再说一遍。我们知道任务的优先级在创建的时候 就已经是设置好的高优先级的任务可以打断低优先级的任务抢占 CPU 的使用权。但是 在很多场合中某些资源只有一个当低优先级任务正在占用该资源的时候即便高优先 级任务也只能乖乖的等待低优先级任务使用完该资源后释放资源。这里高优先级任务无法 运行而低优先级任务可以运行的现象称为“优先级翻转”。 为什么说优先级翻转在操作系统中是危害很大因为在我们一开始创造这个系统的时 候我们就已经设置好了任务的优先级了越重要的任务优先级越高。但是发生优先级翻 转对我们操作系统是致命的危害会导致系统的高优先级任务阻塞时间过长。举个例子现在有 3 个任务分别为 H 任务High、M 任务Middle、L 任务 Low3 个任务的优先级顺序为 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优先级翻转图解图1(1) L 任务正在使用某临界资源 H 任务被唤醒执行 H 任务。但 L 任务并 未执行完毕此时临界资源还未释放。图1(2)这个时刻 H 任务也要对该临界资源进行访问但 L 任务还未释放资源 由于保护机制H 任务进入阻塞态L 任务得以继续运行此时已经发生了优先级翻转现 象。图 1(3)某个时刻 M 任务被唤醒由于 M 任务的优先级高于 L 任务 M 任务抢 占了 CPU 的使用权M任务开始运行此时 L 任务尚未执行完临界资源还没被释放。图 1(4)M 任务运行结束归还 CPU 使用权L 任务继续运行。图 1(5)L任务运行结束释放临界资源H 任务得以对资源进行访问H 任务开 始运行。在这过程中H 任务的等待时间过长这对系统来说这是很致命的所以这种情况不 允许出现而互斥量就是用来降低优先级翻转的产生的危害。 假如有优先级继承呢那么在 H 任务申请该资源的时候由于申请不到资源会进入 阻塞态那么系统就会把当前正在使用资源的 L 任务的优先级临时提高到与 H 任务优先级 相同此时 M 任务被唤醒了因为它的优先级比 H 任务低所以无法打断 L 任务因为 此时 L 任务的优先级被临时提升到 H所以当 L 任务使用完该资源了进行释放那么此 时 H 任务优先级最高将接着抢占 CPU 的使用权 H 任务的阻塞时间仅仅是 L 任务的执 行时间此时的优先级的危害降到了最低看这就是优先级继承的优势图2优先级继承图2(1)L 任务正在使用某临界资源L 任务正在使用某临界资源 H 任务被唤醒 执行 H 任务。但 L 任务并未执行完毕此时临界资源还未释放。图 2(2)某一时刻 H 任务也要对该资源进行访问由于保护机制H 任务进入阻 塞态。此时发生优先级继承系统将 L 任务的优先级暂时提升到与 H 任务优先级相同L 任务继续执行。图 2(3)在某一时刻 M 任务被唤醒由于此时 M 任务的优先级暂时低于 L 任务 所以 M 任务仅在就绪态而无法获得 CPU 使用权。图 2(4)L任务运行完毕H 任务获得对资源的访问权H 任务从阻塞态变成运行 态此时 L 任务的优先级会变回原来的优先级。图 2(5)当 H 任务运行完毕M任务得到 CPU 使用权开始执行。图 2(6)系统正常运行按照设定好的优先级运行。 但是使用互斥量的时候一定需要注意在获得互斥量后请尽快释放互斥量同时需 要注意的是在任务持有互斥量的这段时间不得更改任务的优先级。FreeRTOS 的优先级继 承机制不能解决优先级反转只能将这种情况的影响降低到最小硬实时系统在一开始设 计时就要避免优先级反转发生。三、互斥量应用场景互斥量的使用比较单一因为它是信号量的一种并且它是以锁的形式存在。在初始 化的时候互斥量处于开锁的状态而被任务持有的时候则立刻转为闭锁的状态。互斥量 更适合于可能会引起优先级翻转的情况。递归互斥量更适用于任务可能会多次获取互斥量的情况下。这样可以避免同一任务多次递归持有而造 成死锁的问题。多任务环境下往往存在多个任务竞争同一临界资源的应用场景互斥量可被用于对临 界资源的保护从而实现独占式访问。另外互斥量可以降低信号量存在的优先级翻转问题 带来的影响。 比如有两个任务需要对串口进行发送数据其硬件资源只有一个那么两个任务肯定 不能同时发送啦不然导致数据错误那么就可以用互斥量对串口资源进行保护当一 个任务正在使用串口的时候另一个任务则无法使用串口等到任务使用串口完毕之后 另外一个任务才能获得串口的使用权。 另外需要注意的是互斥量不能在中断服务函数中使用因为其特有的优先级继承机制 只在任务起作用在中断的上下文环境毫无意义。四、互斥量运作机制多任务环境下会存在多个任务访问同一临界资源的场景该资源会被任务独占处理。 其他任务在资源被占用的情况下不允许对该临界资源进行访问这个时候就需要用到 FreeRTOS 的互斥量来进行资源保护那么互斥量是怎样来避免这种冲突 用互斥量处理不同任务对临界资源的同步访问时任务想要获得互斥量才能进行资源 访问如果一旦有任务成功获得了互斥量则互斥量立即变为闭锁状态此时其他任务会 因为获取不到互斥量而不能访问这个资源任务会根据用户自定义的等待时间进行等待 直到互斥量被持有的任务释放后其他任务才能获取互斥量从而得以访问该临界资源此 时互斥量再次上锁如此一来就可以确保每个时刻只有一个任务正在访问这个临界资源 保证了临界资源操作的安全性。图3互斥量运作机制五、互斥量函数接口讲解1.互斥量创建函数 xSemaphoreCreateMutex()xSemaphoreCreateMutex()用于创建一个互斥量并返回一个互斥量句柄。该句柄的原 型是一个 void 型的指针在使用之前必须先由用户定义一个互斥量句柄。要想使用该函数 必须在 FreeRTOSConfig.h 中把宏 configSUPPORT_DYNAMIC_ALLOCATION 定义为 1 即开启动态内存分配其实该宏在 FreeRTOS.h 中默认定义为 1即所有 FreeRTOS 的对象 在创建的时候都默认使用动态内存分配方案同时还需在 FreeRTOSConfig.h 中把 configUSE_MUTEXES 宏定义打开表示使用互斥量。xSemaphoreCreateMutex()函数使用实例SemaphoreHandle_t MuxSem_Handle; void vATask( void * pvParameters ) { /* 创建一个互斥量 */ MuxSem_Handle xSemaphoreCreateMutex(); if (MuxSem_Handle! NULL ) { /* 互斥量创建成功 */ } }2.递归xSemaphoreCreateRecursiveMutex()用于创建一个递归互斥量不是递归的互斥量由函 数 xSemaphoreCreateMutex() 或 xSemaphoreCreateMutexStatic()创建我们只讲解动态创 建且只能被同一个任务获取一次如果同一个任务想再次获取则会失败。递归信号量 则相反它可以被同一个任务获取很多次获取多少次就需要释放多少次。递归信号量与 互斥量一样都实现了优先级继承机制可以降低优先级反转的危害。xSemaphoreCreateRecursiveMutex()函数说明xSemaphoreCreateRecursiveMutex()函数使用实例SemaphoreHandle_t xMutex; void vATask( void * pvParameters ) { /* 创建一个递归互斥量 */ xMutex xSemaphoreCreateRecursiveMutex(); if ( xMutex ! NULL ) { /* 递归互斥量创建成功 */ } }3.互斥量删除函数 vSemaphoreDelete()互斥量的本质是信号量直接调用 vSemaphoreDelete()函数进行删除即可。4.互斥量获取函数 xSemaphoreTake()我们知道当互斥量处于开锁的状态任务才能获取互斥量成功当任务持有了某个 互斥量的时候其它任务就无法获取这个互斥量需要等到持有互斥量的任务进行释放后 其他任务才能获取成功任务通过互斥量获取函数来获取互斥量的所有权。任务对互斥量 的所有权是独占的任意时刻互斥量只能被一个任务持有如果互斥量处于开锁状态那 么获取该互斥量的任务将成功获得该互斥量并拥有互斥量的使用权如果互斥量处于闭 锁状态获取该互斥量的任务将无法获得互斥量任务将被挂起在任务被挂起之前会 进行优先级继承如果当前任务优先级比持有互斥量的任务优先级高那么将会临时提升 持有互斥量任务的优先级。互斥量的获取函数是一个宏定义实际调用的函数就是 xQueueGenericReceive()xSemaphoreTake()函数使用实例static void LowPriority_Task(void* parameter) { static uint32_t i; BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { printf(LowPriority_Task 获取互斥量\n); //获取互斥量 MuxSem,没获取到则一直等待 xReturn xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */ portMAX_DELAY); /* 等待时间 */ if(pdTRUE xReturn) printf(LowPriority_Task Runing\n\n); for(i0;i2000000;i)//模拟低优先级任务占用互斥量 { taskYIELD();//发起任务调度 } printf(LowPriority_Task 释放互斥量!\r\n); xReturn xSemaphoreGive( MuxSem_Handle );//给出互斥量 LED1_TOGGLE; vTaskDelay(1000); } }5.递归互斥量获取函数 xSemaphoreTakeRecursive()xSemaphoreTakeRecursive()是一个用于获取递归互斥量的宏与互斥量的获取函数一 样xSemaphoreTakeRecursive()也是一个宏定义它最终使用现有的队列机制实际执行 的函数是 xQueueTakeMutexRecursive() 。 互 斥 量 之 前 必 须 由 xSemaphoreCreateRecursiveMutex()这个函数创建。要注意的是该函数不能用于获取由函数 xSemaphoreCreateMutex()创建的互斥量。要想使用该函数必须在头文件 FreeRTOSConfig.h 中把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。xSemaphoreTakeRecursive()函数说明递归互斥量可以在一个任务中多次获取当第一次获取递归互斥量时队列结构体成 员指针 pxMutexHolder 指向获取递归互斥量的任务控制块当任务再次尝试获取这个递归 互斥量时如果任务就是拥有递归互斥量所有权的任务那么只需要将记录获取递归次数 的 成 员 变 量 u.uxRecursiveCallCount 加 1 即 可 不 需 要 再 操 作 队 列 。xSemaphoreTakeRecursive()函数的使用实例emaphoreHandle_t xMutex NULL; /* 创建信号量的任务 */ void vATask( void * pvParameters ) { /* 创建一个递归互斥量保护共享资源 */ xMutex xSemaphoreCreateRecursiveMutex(); } /* 使用互斥量 */ void vAnotherTask( void * pvParameters ) { /* ... 做其他的事情 */ if ( xMutex ! NULL ) { /* 尝试获取递归信号量。 如果信号量不可用则等待 10 个 ticks */ if(xSemaphoreTakeRecursive(xMutex,( TickType_t)10)pdTRUE ) { /* 获取到递归信号量可以访问共享资源 */ /* ... 其他功能代码 */ /* 重复获取递归信号量 */ xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); /* 释放递归信号量获取了多少次就要释放多少次 */ xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); /* 现在递归互斥量可以被其他任务获取 */ } else { /* 没能成功获取互斥量所以不能安全的访问共享资源 */ } } }6.互斥量释放函数 xSemaphoreGive()任务想要访问某个资源的时候需要先获取互斥量然后进行资源访问在任务使用 完该资源的时候必须要及时归还互斥量这样别的任务才能对资源进行访问。在前面的 讲解中我们知道当互斥量有效的时候任务才能获取互斥量那么是什么函数使得 信号量变得有效呢FreeRTOS 给我们提供了互斥量释放函数 xSemaphoreGive()任务可以 调用 xSemaphoreGive()函数进行释放互斥量表示我已经用完了别人可以申请使用互 斥量的释放函数与信号量的释放函数一致都是调用 xSemaphoreGive()函数但是要注意 的是互斥量的释放只能在任务中不允许在中断中释放互斥量。 使用该函数接口时只有已持有互斥量所有权的任务才能释放它当任务调用 xSemaphoreGive()函数时会将互斥量变为开锁状态等待获取该互斥量的任务将被唤醒。 如果任务的优先级被互斥量的优先级翻转机制临时提升那么当互斥量被释放后任务的优先级将恢复为原本设定的优先级xSemaphoreGive()使用实例SemaphoreHandle_t xSemaphore NULL; void vATask( void * pvParameters ) { /* 创建一个互斥量用于保护共享资源 */ xSemaphore xSemaphoreCreateMutex(); if ( xSemaphore ! NULL ) { if ( xSemaphoreGive( xSemaphore ) ! pdTRUE ) { /* 如果要释放一个互斥量必须先有第一次的获取*/ } /* 获取互斥量不等待 */ if ( xSemaphoreTake( xSemaphore, ( TickType_t ) 0 ) ) { /* 获取到互斥量可以访问共享资源 */ /* ... 访问共享资源代码 */ /* 共享资源访问完毕释放互斥量 */ if ( xSemaphoreGive( xSemaphore ) ! pdTRUE ) { /* 互斥量释放失败这可不是我们希望的 */ } } } }7.递归互斥量释放函数 xSemaphoreGiveRecursive()xSemaphoreGiveRecursive()是一个用于释放递归互斥量的宏。要想使用该函数必须在 头文件 FreeRTOSConfig.h 把宏 configUSE_RECURSIVE_MUTEXES 定义为 1。互斥量和递归互斥量的最大区别在于一个递归互斥量可以被已经获取这个递归互斥量 的 任务 重复 获取 而不 会形 成死 锁。这 个递 归调 用功能 是通 过队 列结 构体成 员 u.uxRecursiveCallCount 实现的这个变量用于存储递归调用的次数每次获取递归互斥量 后这个变量加 1在释放递归互斥量后这个变量减 1。只有这个变量减到 0即释放和 获取的次数相等时互斥量才能变成有效状态然后才允许使用 xQueueGenericSend()函数 释放一个递归互斥量。SemaphoreHandle_t xMutex NULL; void vATask( void * pvParameters ) { /* 创建一个递归互斥量用于保护共享资源 */ xMutex xSemaphoreCreateRecursiveMutex(); } void vAnotherTask( void * pvParameters ) { /* 其他功能代码 */ if ( xMutex ! NULL ) { /* 尝试获取递归互斥量 如果不可用则等待 10 个 ticks */ if(xSemaphoreTakeRecursive(xMutex,( TickType_t ) 10 ) pdTRUE) { /* 获取到递归信号量可以访问共享资源 */ /* ... 其他功能代码 */ /* 重复获取递归互斥量 */ xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); xSemaphoreTakeRecursive( xMutex, ( TickType_t ) 10 ); /* 释放递归互斥量获取了多少次就要释放多少次 */ xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); xSemaphoreGiveRecursive( xMutex ); /* 现在递归互斥量可以被其他任务获取 */ } else { /* 没能成功获取互斥量所以不能安全的访问共享资源 */ } } }六、互斥量实验1.拟优先级翻转实验模拟优先级翻转实验是在 FreeRTOS 中创建了三个任务与一个二值信号量任务分别 是高优先级任务中优先级任务低优先级任务用于模拟产生优先级翻转。低优先级任 务在获取信号量的时候被中优先级打断中优先级的任务执行时间较长因为低优先级 还未释放信号量那么高优先级任务就无法取得信号量继续运行此时就发生了优先级翻 转任务在运行中使用串口打印出相关信息/* FreeRTOS头文件 */ #include FreeRTOS.h #include task.h #include queue.h #include semphr.h /* 开发板硬件bsp头文件 */ #include bsp_led.h #include bsp_usart.h #include bsp_key.h /**************************** 任务句柄 ********************************/ /* * 任务句柄是一个指针用于指向一个任务当任务创建好之后它就具有了一个任务句柄 * 以后我们要想操作这个任务都需要通过这个任务句柄如果是自身的任务操作自己那么 * 这个句柄可以为NULL。 */ static TaskHandle_t AppTaskCreate_Handle NULL;/* 创建任务句柄 */ static TaskHandle_t LowPriority_Task_Handle NULL;/* LowPriority_Task任务句柄 */ static TaskHandle_t MidPriority_Task_Handle NULL;/* MidPriority_Task任务句柄 */ static TaskHandle_t HighPriority_Task_Handle NULL;/* HighPriority_Task任务句柄 */ /********************************** 内核对象句柄 *********************************/ /* * 信号量消息队列事件标志组软件定时器这些都属于内核的对象要想使用这些内核 * 对象必须先创建创建成功之后会返回一个相应的句柄。实际上就是一个指针后续我 * 们就可以通过这个句柄操作这些内核对象。 * * 内核对象说白了就是一种全局的数据结构通过这些数据结构我们可以实现任务间的通信 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数 * 来完成的 * */ SemaphoreHandle_t BinarySem_Handle NULL; /******************************* 全局变量声明 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些全局变量。 */ /******************************* 宏定义 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些宏定义。 */ /* ************************************************************************* * 函数声明 ************************************************************************* */ static void AppTaskCreate(void);/* 用于创建任务 */ static void LowPriority_Task(void* pvParameters);/* LowPriority_Task任务实现 */ static void MidPriority_Task(void* pvParameters);/* MidPriority_Task任务实现 */ static void HighPriority_Task(void* pvParameters);/* MidPriority_Task任务实现 */ static void BSP_Init(void);/* 用于初始化板载相关资源 */ /***************************************************************** * brief 主函数 * param 无 * retval 无 * note 第一步开发板硬件初始化 第二步创建APP应用任务 第三步启动FreeRTOS开始多任务调度 ****************************************************************/ int main(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ /* 开发板硬件初始化 */ BSP_Init(); printf(这是一个FreeRTOS优先级翻转实验\n); /* 创建AppTaskCreate任务 */ xReturn xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */ (const char* )AppTaskCreate,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )1, /* 任务的优先级 */ (TaskHandle_t* )AppTaskCreate_Handle);/* 任务控制块指针 */ /* 启动任务调度 */ if(pdPASS xReturn) vTaskStartScheduler(); /* 启动任务开启调度 */ else return -1; while(1); /* 正常不会执行到这里 */ } /*********************************************************************** * 函数名 AppTaskCreate * 功能说明 为了方便管理所有的任务创建函数都放在这个函数里面 * 参数 无 * 返回值 无 **********************************************************************/ static void AppTaskCreate(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ taskENTER_CRITICAL(); //进入临界区 /* 创建Test_Queue */ BinarySem_Handle xSemaphoreCreateBinary(); if(NULL ! BinarySem_Handle) printf(BinarySem_Handle二值信号量创建成功!\r\n); xReturn xSemaphoreGive( BinarySem_Handle );//给出二值信号量 // if( xReturn pdTRUE ) // printf(释放信号量!\r\n); /* 创建LowPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )LowPriority_Task, /* 任务入口函数 */ (const char* )LowPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )2, /* 任务的优先级 */ (TaskHandle_t* )LowPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建LowPriority_Task任务成功!\r\n); /* 创建MidPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )MidPriority_Task, /* 任务入口函数 */ (const char* )MidPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )3, /* 任务的优先级 */ (TaskHandle_t* )MidPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建MidPriority_Task任务成功!\n); /* 创建HighPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )HighPriority_Task, /* 任务入口函数 */ (const char* )HighPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )4, /* 任务的优先级 */ (TaskHandle_t* )HighPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建HighPriority_Task任务成功!\n\n); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL(); //退出临界区 } /********************************************************************** * 函数名 LowPriority_Task * 功能说明 LowPriority_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void LowPriority_Task(void* parameter) { static uint32_t i; BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { printf(LowPriority_Task 获取信号量\n); //获取二值信号量 xSemaphore,没获取到则一直等待 xReturn xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */ portMAX_DELAY); /* 等待时间 */ if( xReturn pdTRUE ) printf(LowPriority_Task Runing\n\n); for(i0;i2000000;i)//模拟低优先级任务占用信号量 { taskYIELD();//发起任务调度 } printf(LowPriority_Task 释放信号量!\r\n); xReturn xSemaphoreGive( BinarySem_Handle );//给出二值信号量 // if( xReturn pdTRUE ) // ; /* 什么都不做 */ LED1_TOGGLE; vTaskDelay(500); } } /********************************************************************** * 函数名 MidPriority_Task * 功能说明 MidPriority_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void MidPriority_Task(void* parameter) { while (1) { printf(MidPriority_Task Runing\n); vTaskDelay(500); } } /********************************************************************** * 函数名 HighPriority_Task * 功能说明 HighPriority_Task 任务主体 * 参数 * 返回值 无 ********************************************************************/ static void HighPriority_Task(void* parameter) { BaseType_t xReturn pdTRUE;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { printf(HighPriority_Task 获取信号量\n); //获取二值信号量 xSemaphore,没获取到则一直等待 xReturn xSemaphoreTake(BinarySem_Handle,/* 二值信号量句柄 */ portMAX_DELAY); /* 等待时间 */ if(pdTRUE xReturn) printf(HighPriority_Task Runing\n); LED1_TOGGLE; xReturn xSemaphoreGive( BinarySem_Handle );//互斥量释放函数 给出二值信号量 // if( xReturn pdTRUE ) //printf(HighPriority_Task 释放信号量!\r\n); vTaskDelay(500); } } /*********************************************************************** * 函数名 BSP_Init * 功能说明 板级外设初始化所有板子上的初始化均可放在这个函数里面 * 参数 * 返回值 无 *********************************************************************/ static void BSP_Init(void) { /* * STM32中断优先级分组为4即4bit都用来表示抢占优先级范围为0~15 * 优先级分组只需要分组一次即可以后如果有其他的任务需要用到中断 * 都统一用这个优先级分组千万不要再分组切忌。 */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* LED 初始化 */ LED_GPIO_Config(); /* 串口初始化 */ USART_Config(); /* 按键初始化 */ Key_GPIO_Config(); } /********************************END OF FILE****************************/2.互斥量实验互斥量实验是基于优先级翻转实验进行修改的目的是为了测试互斥量的优先级继承 机制是否有效/* FreeRTOS头文件 */ #include FreeRTOS.h #include task.h #include queue.h #include semphr.h /* 开发板硬件bsp头文件 */ #include bsp_led.h #include bsp_usart.h #include bsp_key.h /**************************** 任务句柄 ********************************/ /* * 任务句柄是一个指针用于指向一个任务当任务创建好之后它就具有了一个任务句柄 * 以后我们要想操作这个任务都需要通过这个任务句柄如果是自身的任务操作自己那么 * 这个句柄可以为NULL。 */ static TaskHandle_t AppTaskCreate_Handle NULL;/* 创建任务句柄 */ static TaskHandle_t LowPriority_Task_Handle NULL;/* LowPriority_Task任务句柄 */ static TaskHandle_t MidPriority_Task_Handle NULL;/* MidPriority_Task任务句柄 */ static TaskHandle_t HighPriority_Task_Handle NULL;/* HighPriority_Task任务句柄 */ /********************************** 内核对象句柄 *********************************/ /* * 信号量消息队列事件标志组软件定时器这些都属于内核的对象要想使用这些内核 * 对象必须先创建创建成功之后会返回一个相应的句柄。实际上就是一个指针后续我 * 们就可以通过这个句柄操作这些内核对象。 * * 内核对象说白了就是一种全局的数据结构通过这些数据结构我们可以实现任务间的通信 * 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数 * 来完成的 * */ SemaphoreHandle_t MuxSem_Handle NULL; /******************************* 全局变量声明 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些全局变量。 */ /******************************* 宏定义 ************************************/ /* * 当我们在写应用程序的时候可能需要用到一些宏定义。 */ /* ************************************************************************* * 函数声明 ************************************************************************* */ static void AppTaskCreate(void);/* 用于创建任务 */ static void LowPriority_Task(void* pvParameters);/* LowPriority_Task任务实现 */ static void MidPriority_Task(void* pvParameters);/* MidPriority_Task任务实现 */ static void HighPriority_Task(void* pvParameters);/* MidPriority_Task任务实现 */ static void BSP_Init(void);/* 用于初始化板载相关资源 */ /***************************************************************** * brief 主函数 * param 无 * retval 无 * note 第一步开发板硬件初始化 第二步创建APP应用任务 第三步启动FreeRTOS开始多任务调度 ****************************************************************/ int main(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ /* 开发板硬件初始化 */ BSP_Init(); printf(这是一个FreeRTOS互斥量实验\n); /* 创建AppTaskCreate任务 */ xReturn xTaskCreate((TaskFunction_t )AppTaskCreate, /* 任务入口函数 */ (const char* )AppTaskCreate,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )1, /* 任务的优先级 */ (TaskHandle_t* )AppTaskCreate_Handle);/* 任务控制块指针 */ /* 启动任务调度 */ if(pdPASS xReturn) vTaskStartScheduler(); /* 启动任务开启调度 */ else return -1; while(1); /* 正常不会执行到这里 */ } /*********************************************************************** * 函数名 AppTaskCreate * 功能说明 为了方便管理所有的任务创建函数都放在这个函数里面 * 参数 无 * 返回值 无 **********************************************************************/ static void AppTaskCreate(void) { BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ taskENTER_CRITICAL(); //进入临界区 /* 创建MuxSem */ MuxSem_Handle xSemaphoreCreateMutex(); if(NULL ! MuxSem_Handle) printf(MuxSem_Handle互斥量创建成功!\r\n); xReturn xSemaphoreGive( MuxSem_Handle );//给出互斥量 // if( xReturn pdTRUE ) // printf(释放信号量!\r\n); /* 创建LowPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )LowPriority_Task, /* 任务入口函数 */ (const char* )LowPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL, /* 任务入口函数参数 */ (UBaseType_t )2, /* 任务的优先级 */ (TaskHandle_t* )LowPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建LowPriority_Task任务成功!\r\n); /* 创建MidPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )MidPriority_Task, /* 任务入口函数 */ (const char* )MidPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )3, /* 任务的优先级 */ (TaskHandle_t* )MidPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建MidPriority_Task任务成功!\n); /* 创建HighPriority_Task任务 */ xReturn xTaskCreate((TaskFunction_t )HighPriority_Task, /* 任务入口函数 */ (const char* )HighPriority_Task,/* 任务名字 */ (uint16_t )512, /* 任务栈大小 */ (void* )NULL,/* 任务入口函数参数 */ (UBaseType_t )4, /* 任务的优先级 */ (TaskHandle_t* )HighPriority_Task_Handle);/* 任务控制块指针 */ if(pdPASS xReturn) printf(创建HighPriority_Task任务成功!\n\n); vTaskDelete(AppTaskCreate_Handle); //删除AppTaskCreate任务 taskEXIT_CRITICAL(); //退出临界区 } /********************************************************************** * 函数名 LowPriority_Task * 功能说明 LowPriority_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void LowPriority_Task(void* parameter) { static uint32_t i; BaseType_t xReturn pdPASS;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { printf(LowPriority_Task 获取互斥量\n); //获取互斥量 MuxSem,没获取到则一直等待 xReturn xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */ portMAX_DELAY); /* 等待时间 */ if(pdTRUE xReturn) printf(LowPriority_Task Runing\n\n); for(i0;i2000000;i)//模拟低优先级任务占用互斥量 { taskYIELD();//发起任务调度 } printf(LowPriority_Task 释放互斥量!\r\n); xReturn xSemaphoreGive( MuxSem_Handle );//给出互斥量 LED1_TOGGLE; vTaskDelay(1000); } } /********************************************************************** * 函数名 MidPriority_Task * 功能说明 MidPriority_Task任务主体 * 参数 * 返回值 无 ********************************************************************/ static void MidPriority_Task(void* parameter) { while (1) { printf(MidPriority_Task Runing\n); vTaskDelay(1000); } } /********************************************************************** * 函数名 HighPriority_Task * 功能说明 HighPriority_Task 任务主体 * 参数 * 返回值 无 ********************************************************************/ static void HighPriority_Task(void* parameter) { BaseType_t xReturn pdTRUE;/* 定义一个创建信息返回值默认为pdPASS */ while (1) { printf(HighPriority_Task 获取互斥量\n); //获取互斥量 MuxSem,没获取到则一直等待 xReturn xSemaphoreTake(MuxSem_Handle,/* 互斥量句柄 */ portMAX_DELAY); /* 等待时间 */ if(pdTRUE xReturn) printf(HighPriority_Task Runing\n); LED1_TOGGLE; printf(HighPriority_Task 释放互斥量!\r\n); xReturn xSemaphoreGive( MuxSem_Handle );//给出互斥量 vTaskDelay(1000); } } /*********************************************************************** * 函数名 BSP_Init * 功能说明 板级外设初始化所有板子上的初始化均可放在这个函数里面 * 参数 * 返回值 无 *********************************************************************/ static void BSP_Init(void) { /* * STM32中断优先级分组为4即4bit都用来表示抢占优先级范围为0~15 * 优先级分组只需要分组一次即可以后如果有其他的任务需要用到中断 * 都统一用这个优先级分组千万不要再分组切忌。 */ NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 ); /* LED 初始化 */ LED_GPIO_Config(); /* 串口初始化 */ USART_Config(); /* 按键初始化 */ Key_GPIO_Config(); } /********************************END OF FILE****************************/七、实验现象优先级反转互斥量