1.正点原子FreeRTOS——内存管理
2.FreeRTOS互斥信号量
3.Freertos(4)----信号量
4.FreeRTOS 从入门到精通10--资源管理(互斥量与信号量)
5.FreeRTOS系列教程(四):如何使用信号量
6.FreeRTOS:什么叫二值信号量
正点原子FreeRTOS——内存管理
FreeRTOS内存管理简介
在使用FreeRTOS创建任务、信号s信队列、量源信号量等对象时,号量一般提供两种方法:动态方法创建和静态方法创建。信号s信动态方法创建自动从FreeRTOS管理的量源内存堆中申请创建对象所需的内存,并在对象删除后,号量织梦响应式博客网站附源码将这块内存释放回FreeRTOS管理的信号s信内存堆。静态方法创建则需要自行提供内存空间,量源这些任务、号量队列被删除后,信号s信这些内存空间也没有其他用途。量源动态方式更灵活,号量但FreeRTOS提供多种动态内存管理算法,信号s信可根据不同嵌入式系统需求选择。量源
FreeRTOS内存管理算法
正点原子的号量例程中,均使用heap_4内存管理算法。heap_1算法只实现了pvPortMalloc,没有实现vPortFree,只能申请内存,无法释放内存。如果工程中创建的任务、队列、信号量等无需删除,可以使用heap_1算法。heap_1实现最简单,管理的内存堆是一个数组,申请内存时从数组中分出合适大小的内存。heap_2使用最适应算法,并支持释放内存,但无法将相邻空闲内存块合并成一个大的空闲内存块,会产生内存碎片。heap_4使用首次适应算法,支持内存申请、释放,并能将空闲且相邻的内存进行合并,减少内存碎片。heap_5在heap_4的基础上实现了管理多个非连续内存区域的能力,适用于内存地址不连续的场景。
FreeRTOS内存管理相关API
FreeRTOS内存管理实验旨在学习如何使用FreeRTOS内存管理,观察申请和释放过程中内存大小的变化情况。实验包括创建task1、按键扫描、KEY0申请内存、KEY1释放内存,并打印剩余内存信息。heap4.c讲解内存的结构体,包含指针和无符号整数两个成员。内存堆初始化prvHeapInit()通过宏configTOTAL_HEAP_SIZE获取FreeRTOS管理的内存总大小,获取给FreeRTOS管理的数组首地址,进行对齐操作,并初始化管理的数组。合并空闲内存prvInsertBlockIntoFreeList()用于将空闲内存插入空闲列表,影视站群源码合并相邻空闲内存,并将空闲地址由低到高排列。申请内存pvPortMalloc()和释放内存vPortFree()提供了内存的申请和释放功能,分别用于动态分配和释放内存。
FreeRTOS互斥信号量
互斥信号量是实时系统中用于实现资源互斥访问的重要工具。当需要确保多个任务在同时使用共享资源时,互斥信号量可以保证只有一个任务可以访问该资源。这有效避免了优先级翻转现象,确保了系统的稳定性和任务的预期执行顺序。
优先级翻转是一种在可剥夺内核中常见的情况,其后果可能会严重破坏任务的执行顺序。在互斥信号量的应用中,任务获取信号量的过程涉及到优先级的提升,这在一定程度上减轻了优先级翻转的影响。但请注意,优先级继承并不能完全消除优先级翻转,它只是降低这种现象的出现概率。
互斥信号量的API函数包含创建、释放和获取三个主要部分。创建互斥信号量通过一个宏执行,最终调用xQueueCreateMutex()函数完成。释放互斥信号量时,调用xSemaphoreGive()函数,并在内部的prvCopyDataToQueue()函数中处理优先级继承问题。获取互斥信号量的过程同样涉及优先级处理,通过xSemaphoreTake()函数实现,并且在调用xQueueGenericReceive()时处理优先级继承。
在实际应用中,互斥信号量可以避免优先级翻转问题。以STMCubeMX为例,可以将FreeRTOS移植到工程中,创建具有不同优先级的任务和互斥信号量。通过这种方式,可以有效地解决优先级翻转问题,确保系统稳定运行。
实例中,使用STMCubeMX进行配置,设置不同优先级的任务和互斥信号量,然后在MDK-ARM软件中进行编程,最终将代码下载到开发板上进行验证。通过串口调试助手观察调试信息,可以看到互斥信号量成功地解决了优先级翻转问题,确保了任务的执行顺序和系统的稳定性。
Freertos(4)----信号量
Freertos中的二值信号量是一种用于任务间或任务与中断间同步的基本工具。它与互斥信号量类似,但不具备优先级继承机制。二值信号量的特点在于其队列仅有一项,意味着队列要么为空,要么已满,任务只需判断队列状态,无需关注具体消息内容。星盘排盘源码
以温湿度传感器为例,如果采集数据和刷新屏幕的周期不同步,可能会浪费CPU资源。通过使用二值信号量,传感器数据采集完成后才会触发屏幕刷新,确保数据的准确性并节省CPU资源。在操作中,任务会根据信号量队列状态进入阻塞或非阻塞状态。
Freertos通过在发送信号量时立即返回,避免了发送端和接收端的同步问题。创建二值信号量时,API与创建队列类似,只是设置消息数量为1,大小为0,类型为二值信号量队列。
计数信号量则更注重资源管理,允许多个任务访问,但限制任务总数。当超过限制时,后续任务会阻塞,直到有任务释放资源。这种机制就像多个人上厕所的比喻,确保了资源访问的有序性。
互斥信号量则提供了互斥和优先级继承特性,确保临界资源的独占访问,避免优先级翻转问题。在源码中,创建、释放和获取互斥信号量的过程同样体现了简化设计的理念。
递归互斥信号量允许任务多次获取并释放,但必须是成对操作,且同样具有优先级继承机制。递归互斥信号量的API和源码实现同样遵循这一原则。
FreeRTOS 从入门到精通--资源管理(互斥量与信号量)
资源管理(Resource Management)在嵌入式开发领域至关重要,本文旨在深入探讨资源管理中的两个关键概念:数据同步机制与信号量(Semaphore),以及资源保护机制与互斥量(Mutex)。同时,本文将介绍优先级倒置(Priority inversion)这一现象,并讨论如何通过互斥量和关键区(Critical Section)解决资源保护问题。
信号量是一种同步机制,通过其获取(taking a semaphore)和给予(giving a semaphore)操作,可以在任务间实现数据同步和资源保护。二进制信号量(binary semaphore)最多只能通知解锁一个任务,适用于数据产生频率较低的场合;计数信号量(counting semaphore)则可以通知解锁多个任务,适用于数据产生频率较高的场景。
在中断函数中,信号量操作的版本应确保在中断环境下的正确性。信号量的常用函数包括xSemaphoreCreateBinary(创建二进制信号量)、xSemaphoreTake(获取信号量)和xSemaphoreGive(给予信号量)。二进制信号量适用于数据产生频率较低的场景,而计数信号量则适用于数据产生频率较高的简单的内核源码场景。
资源保护机制中,信号量可以起到部分作用,但其缺点在于可能引发优先级倒置(Priority inversion)。在优先级倒置现象中,低优先级任务持有资源,高优先级任务等待资源释放,造成资源使用上的不正常中断。为解决优先级倒置问题,FreeRTOS引入了互斥量(Mutex)。互斥量是信号量的变种,通过设置FreeRTOSConfig.h中的configUSE_MUTEXES为1来启用互斥量。互斥量操作的函数包括xSemaphoreCreateMutex(创建互斥量)。
互斥量引入了优先级继承(priority inheritance)特性,当任务获取互斥量时,其优先级临时提升至与想要获取互斥量的任务中最高优先级相同,从而显著降低优先级倒置发生的概率。通过互斥量,任务间可以更安全地访问共享资源,避免资源冲突。
关键区(Critical Section)是资源保护的另一种方法。在FreeRTOS中,通过taskENTER_CRITICAL()和taskEXIT_CRITICAL()宏定义,可以关闭所有中断(包括内核切换的中断),从而实现资源的独占访问。这种方法较为直接,但通常被视为保护资源的一种较激进的方式。
本文系列的理论篇到此结束。如果您对资源管理、信号量、互斥量和优先级倒置有更多想法、感受或建议,欢迎在评论区分享。期待实战篇的深入探讨,与您共同学习和成长!
FreeRTOS系列教程(四):如何使用信号量
大家好,我是旭辉君,一个智能硬件领域深度探索的技术博主。
在上篇文章中,我们理解了在FreeRTOS中如何使用消息队列进行任务间的数据传递,链接如下:
本文我们就一起来探索信号量的使用。所谓信号量,可以简单的理解为就是一个状态标志,我们可以用这个状态标志来进行任务间的同步,有序访问,或者互斥访问。从这些对于信号量不同的应用,常用的信号量可以分为:
本文我们将重点讲述二值信号量与计数信号量的使用。互斥信号量放在下一篇文章讲解。通过本文,我们将会知道:
接下来让我们一起,进入信号量的图片制作影视源码探索之旅!
如前文所述,信号量可以提供任务间数据的同步机制。我们假设有两个任务TaskA和TaskB,其中TaskB等待TaskA产生的数据并进行处理,按照之前我们在裸机编程时候的思路,一般都是设置一个全局变量,然后在while1中轮流执行这两个任务,若TaskA产生的数据让这个全局变量发生改变,TaskB在轮询到之后就能处理这些数据,但是,如果TaskA里面的数据久久不发生改变,那么一直轮询TaskB就是无效的,CPU做了许多的无用功。
所以应该怎么优化呢?
假若在TaskA数据发生不改变的时候,TaskB进入阻塞态不执行,当TaskA数据发生改变的时候才去执行TaskB,这样就不会浪费CPU的资源。为此,FreeRTOS引入了信号量(Semaphore)概念,通过信号量的同步机制可以使任务在数据还没到达的时候进入阻塞状态,在数据到达之后才得以执行,提高系统资源利用率。
二进制信号量只有两个状态,只能用于两个任务间的同步;计数信号量中信号量的数目可以自定义设定为多个,可用于多个任务间的同步。
创建信号量时, 系统会为创建的信号量对象分配内存, 二值信号量的最大可用信号量个数为 1。创建成功后,任何任务都可以从创建的二值信号量资源中获取这个二值信号量,获取成功则任务继续运行, 否则任务会根据用户指定的阻塞超时时间来等待其它任务或者中断释放信号量。在等待这段时间,系统将任务变成阻塞态, 任务将被挂到该信号量的阻塞等待列表中。下图为任务获取信号量时的示意图:获取信号量无效时任务进入阻塞,其他任务释放信号量后,信号量有效,该任务恢复为就绪态。
相比于二值信号量,计数信号量允许多个任务获取同一个信号量,这多个任务的数目可以由我们设定。比如我们设定,某个资源只能有 3 个任务访问,那么第 4 个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务 1)释放掉该资源的时候,第 4 个任务才能获取到信号量从而进行资源的访问,其运作的机制具体见下图:
观察信号量控制块结构体以及信号量创建函数的源码,我们就会惊奇的发现:FreeRTOS 的信号量控制块结构体与消息队列结构体是一模一样的!信 号 量 的 创 造 实 际 调 用 的 函 数 xQueueGenericCreate()也与消息队列一样!只是参数或者其代表的意义有一些差异。
所以我们可以理解为:信号量就是一种特殊的消息队列!由于我们只关注信号状态,不关注消息内容,这个队列就没有设置消息存储空间。
其中,xSemaphoreCreateBinary()是一个宏定义,展开后调用xQueueGenericCreate(),也就是上一篇文章我们创建队列时候使用的函数,只是传递的参数不同。
与二值信号量一样,xSemaphoreCreateCounting()展开后也是调用xQueueGenericCreate(),创建的计数信号量只有消息队列控制块结构体存储空间而没有消息存储空间 。
删除信号量函数vSemaphoreDelete()是一个宏定义,其调用的是vQueueDelete()函数。删除信号量过程其实就是删除消息队列过程, 因为信号量其实就是特殊的,无法存储消息的消息队列。
xSemaphoreGive()是一个用于释放信号量的宏, 真正的实现该过程是调用消息队列通用发送函数xQueueGenericSend()。释放信号量实际上是一次入队操作,并且阻塞时间为0,也就是释放信号量时,如果信号量计数值已满,就返回信号量释放错误。
xSemaphoreTake()是一个用于获取信号量的宏, 真正的实现该过程是调用消息队列通用接收函数xQueueGenericReceive()。信号量获取实际上就是一次消息出队操作,所以我们也可以按照消息队列的接收机制来理解信号量的获取:当有任务试图获取信号量的时候,当且仅当信号量有效,也就是队列中存在可用信号量的时候,任务才能获取到信号量。
如果信号量无效,在用户指定的阻塞超时时间中,该任务将保持阻塞状态以等待信号量有效。在阻塞超时等待的时间内,如果有其它任务或中断释放了有效的信号量,该任务将自动由阻塞态转移为就绪态。如果任务等待的时间超过了指定的阻塞时间,即使信号量中还是没有可用信号量,任务也会自动从阻塞态转移为就绪态。
创建三个任务,task_example_1,task_example_2和task_example_3。其中task_example_1用于计时,每3s让task_example_2释放二值信号量,task_example_3用于信号量获取,在获取不到信号量的时候一直死等。主体代码如下:
下图为运行后的串口输出结果:可以看到,cnt计数的时候,每3s释放一个二值信号量,然后立即就能被获取到,实现了Task2与Task3两个任务间的同步。
创建计数信号量xSemaphoreCreateCounting(5,3),其中参数5表示最大可容纳五个状态,任务每获取一个信号量,信号量计数减一,每释放一个信号量,信号量计数加一。初始值为3,表示初始里面已经有了三个信号量。同样三个任务,task_example_3每ms获取一个信号量, task_example_2每2s释放一个信号量,task_example_1用于每1s的计时显示。程序主体代码如下:
运行程序后串口输出如下:可以看到,初始计数信号量有3个,随着不断获取,计数信号量为空,之后就获取失败,只有计数信号量释放后才能继续获取。
本文主要探索了二值信号量与计数信号量的原理及其使用方法,包括信号量的原理,信号量的运行机制,信号量与消息队列的比较,信号量的相关API函数,以及信号量的使用实验等。通过本文,不知道大家对第一节的几个问题,有没有自己的答案。有疑问的同学,欢迎评论区留言交流。原创不易,大家的点赞和关注是对我持续更新最大的鼓励,谢谢!也为坚持看到系列文章此处的你点赞!
想要文中工程源码的同学,可以关注我的微信公众号:硬件电子与嵌入式小栈,留言:freertos源码 即可获取。同时我还整理了一些学习FreeRTOS实用的书籍资料,公众号留言:freertos资料 即可获取。公众号里也会不定期更新干货文章哦。
FreeRTOS:什么叫二值信号量
FreeRTOS中的二值信号量是一种特殊的信号量,仅能保持两个状态,即0或1。其功能类似于一个拥有单一队列项的队列,队列为空表示值为0,队列满则值为1。二值信号量提供了发送(Send)和接收(Receive)两种操作。
当执行Send操作时,若信号量值为0,表示队列为空,此时操作成功并将信号量值设为1。若信号量值为1,则发送操作可能失败或被阻塞,取决于信号量设置和系统调度策略。
在接收(Receive)操作中,若信号量值为1,表示队列已满,操作成功且信号量值设为0。若信号量值为0,接收操作可能被阻塞,直至其他任务或中断执行Send操作,将信号量值设为1。
通过发送和接收操作,二值信号量实现任务间或任务与中断间的同步。当任务等待特定事件时,尝试接收信号量并在值为0时被阻塞。当另一任务或中断检测到事件后,发送信号量唤醒等待的任务,使其继续执行。
因此,二值信号量在FreeRTOS中作为高效同步机制,简化了任务间或任务与中断间的通信与协作。
FreeRTOS-如何使用二值信号量实现任务间的同步
FreeRTOS中的二值信号量作为一种重要工具,用于管理共享资源和任务间的同步。想象一下,酒店的客房数量就像一个信号量,满表示不可用,空表示可用。同理,任务间的同步就像间谍接收到信号后行动,需要精确的信号控制。二值信号量仅具备两种状态,有效和无效,从而实现高效的任务协调。
本质上,二值信号量是单元素队列,其API允许设置阻塞时间。当任务试图获取信号量时,若信号量无效,任务会进入阻塞状态,直到信号量变为有效。优先级高的任务会优先获得信号,激活执行。信号传递包括释放和获取,如在中断服务函数中释放信号,任务随后获取并执行。
下面是一个简单的示例:定义一个二值信号量,创建并由一个任务根据按键释放,另一任务负责获取并检查释放状态。这便实现了任务间的同步。对于任务与中断的交互,需使用中断级API,而非任务级API。
尽管本文以基础实例为主,但FreeRTOS系列文章已涵盖多个主题,如移植、时间管理、系统时钟节拍和任务基础。后续将深入探讨更复杂的应用场景,敬请关注。
正点原子FreeRTOS——信号量
FreeRTOS中的信号量机制
信号量是FreeRTOS中一种关键的同步工具,它用于管理和控制对共享资源的访问顺序。信号量有多种类型,如二值信号量和计数型信号量,各自具有不同的特性和应用场景。 二值信号量,本质上是一个队列长度为1的特殊信号量,用于互斥访问或任务同步。当信号量计数值为1时,表示资源被占用;释放后计数值减1,可用于同步任务。然而,二值信号量可能导致优先级翻转,因此在同步场景中更常见。使用时,包括创建(动态或预定义)、释放和获取等步骤。 计数型信号量则可以容纳多个资源,创建时确定其最大值。这种信号量在资源数量较多时更为适用,使用过程涉及创建、释放和获取信号量。计数型信号量实验中,通过任务协作测试信号量的释放和获取功能。 优先级翻转是抢占式内核中的一个潜在问题,特别是当高优先级任务被低优先级持有信号量时。互斥信号量,即带优先级继承的二值信号量,通过暂时提升阻塞任务优先级,来降低优先级翻转的风险。但优先级继承并不能完全避免问题,只是减轻影响。互斥信号量不能在中断服务函数中使用,需要正确设置configUSE_MUTEXES并遵循特定的获取和释放流程。 在优先级翻转实验中,通过互斥信号量的使用,试图解决优先级翻转带来的问题。实验设计涉及将信号量功能替换为互斥信号量,以观察其对抢占式内核的改进效果。FreeRTOS递归互斥信号量
递归互斥信号量是一种特殊的互斥信号量,不同于普通互斥信号量,已经获取递归互斥信号量的任务可以再次获取此信号量,即任务可以嵌套使用,次数不限。递归互斥信号量同样需解决优先级继承问题,获取的次数必须释放相同的次数,且不能在中断服务函数中使用。
递归互斥信号量的实现通过宏调用xQueueCreateMutex()函数创建,此函数源码细节参考互斥信号量章节3.1的介绍。释放递归互斥信号量使用宏调用xSemaphoreGiveRecursive()函数,调用xQueueGiveMutexRecursive()完成释放过程。获取递归互斥信号量使用宏调用xSemaphoreTakeRecursive()函数,执行xQueueTakeMutexRecursive()实现获取。
实例展示了递归互斥信号量的应用。通过STMCubeMX将FreeRTOS移植至工程,创建优先级高低不同的三个任务与一个递归互斥信号量。在MDK-ARM软件中编程,编译无误后下载至开发板,使用串口调试助手观察调试信息。
如需获取FreeRTOS递归互斥信号量实例的完整工程源代码,请关注公众号并在公众号内发送指定消息。
FreeRTOS简介
FreeRTOS,一个专为小型嵌入式系统设计的迷你操作系统内核,它的存在旨在提供基础的系统功能。它的核心特性包括任务管理、精准的时间管理、信号量机制、消息队列服务以及内存和记录功能,这些使得它在资源有限的小型系统中展现出强大的适应性。[1] 由于实时操作系统对系统资源,特别是RAM的需求,像μC/OS-II、embOS和salvo这样的RTOS能够在小容量RAM的单片机上运行,而FreeRTOS就是其中之一。相比于商业的μC/OS-II和embOS,FreeRTOS的一大亮点是其开源的性质,用户可以自由获取和使用源代码。此外,它还具有高度的可移植性和可裁剪性,开发者可以根据项目需求灵活定制和移植到各种类型的单片机上。目前,FreeRTOS的最新版本为7.4.0,这表明其持续更新和优化,以满足不断变化的嵌入式系统需求。扩展资料
在嵌入式领域中,嵌入式实时操作系统正得到越来越广泛的应用。采用嵌入式实时操作系统(RTOS)可以更合理、更有效地利用CPU的资源,简化应用软件的设计,缩短系统开发时间,更好地保证系统的实时性和可靠性。