打印
[应用相关]

FreeRTOS及其应用,万字长文,基础入门

[复制链接]
手机看帖
扫描二维码
随时随地手机跟帖
61
7.6 信号量使用注意点

1、建议合理使用信号量进行事件同步处理,减少对定时器的依赖。

2、使用前合理设定超时时间和依赖关系,避免多个任务互相等待对方释放的信号量而死锁。


使用特权

评论回复
62
我只会加减乘除|  楼主 | 2022-5-1 19:16 | 只看该作者
八、 事件8.1 事件的概念

信号量用于单个任务与任务或任务与中断之间的同步,但有些任务可能与多个任务由关联,此时信号量实现就比较麻烦,可以使用事件机制。

事件是一种实现任务间通信的机制,多任务环境下,任务、中断之间往往需要同步操作,一个事件发生会告知等待中的任务,即形成一个任务与任务、中断与任务间的同步。事件可以提供一对多、多对多的同步操作。一对多同步模型:一个任务等待多个事件的触发,这种情况是比较常见的。

任务可以通过设置事件位来实现事件的触发和等待操作。FreeRTOS 的事件仅用于同步,不提供数据传输功能。


使用特权

评论回复
63
我只会加减乘除|  楼主 | 2022-5-1 19:16 | 只看该作者
8.2 事件的应用

在某些场合,可能需要多个事件发生了才能进行下一步操作。各个事件可分别发送或一起操作事件标志组,而任务可以等待多个事件,任务仅对感兴趣的事件进行关注。当有感兴趣的事件发生时并且符合感兴趣的条件,任务将被唤醒并进行后续的处理动作。

其机制类似一个全局变量,子任务使用特殊的接口函数对指定的位进行写1或者清零,主任务阻塞等待该变量满足设定的规则,则返回运行。

例如项目中的喂狗机制,多个任务,只要有一个任务发生异常,则主任务停止喂狗,等待被重启。

不使用事件机制,则3个任务定时向主master task发送消息,表明自身任务运行正常;同时master task定时查询,是否收到3个任务的消息,如果全都收到表示正常,清除进入下一个定时检查周期;如果其中一个未收到则表示对应任务异常,故意停止喂狗等待被重启。

使用事件机制,则相对容易,3个任务定时设置对应的标志位,master task只需要等待指定的事件位,超时就表示异常;不需要自身定时查询,也省去了定时发消息。当然缺点是master task只能阻塞等待事件不能执行其他业务逻辑。


使用特权

评论回复
64
我只会加减乘除|  楼主 | 2022-5-1 19:17 | 只看该作者
8.3 事件接口

xEventGroupCreate()用于创建一个事件组,vEventGroupDelete()删除事件对象控制块来释放系统资源。

事件组置位,任务中使用 xEventGroupSetBits(),中断中使用xEventGroupSetBitsFromISR();

xEventGroup 事件句柄。uxBitsToSet 指定事件中的事件标志位。如设置 uxBitsToSet 为 0x09 则位 3和位 0 都需要被置位。返回调用 xEventGroupSetBits() 时事件组中的值。

事件组清除位,任务中使用xEventGroupClearBits(),中断中使用 xEventGroupClearBitsFromISR(),都是用于清除事件组指定的位,如果在获取事件的时候没有将对应的标志位清除,那么就需要用这个函数来进行显式清除。

xEventGroup 事件句柄。uxBitsToClear 指定事件组中的哪个位需要清除。如设置 uxBitsToSet 为 0x09则位 3和位 0 都需要被清除。

读取事件标志,任务中使用 xEventGroupGetBits(),中断中使用xEventGroupGetBitsFromISR()。

重点是等待事件函数 xEventGroupWaitBits(),获取任务感兴趣的事件且支持等待超时机制,当且仅当任务等待的事件发生时,任务才能获取到事件信息。否则任务将保持阻塞状态以等待事件发生。当其它任务或中断服务程序往其等待的事件设置对应的标志位,该任务将自动由阻塞态转为就绪态。

EventGroupWaitBits()用于获取事件组中的一个或多个事件发生标志,当要读取的事件标志位没有被置位时,任务将进入阻塞等待状态。要想使用该函数必 须 把FreeRTOS/source/event_groups.c 这个 C 文件添加到工程中。

1. EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup,  
2.                                  const EventBits_t uxBitsToWaitFor,  
3.                                  const BaseType_t xClearOnExit,  
4.                                  const BaseType_t xWaitForAllBits,  
5.                                  TickType_t xTicksToWait )  

参数

xEventGroup 事件句柄。

  uxBitsToWaitFor 一个按位或的值,指定需要等待事件组中的哪些位置1。如需要等待 bits 0 and/or bit 1 and/or bit 2则 uxBitsToWaitFor 配置为 0x07(0111b)。

xClearOnExit pdTRUE:xEventGroupWaitBits() 等待到满足任务唤醒的事件时,系统将清除由形参 uxBitsToWaitFor 指定的事件标志位。pdFALSE:不会清除由形参 uxBitsToWaitFor 指定的事件标志位。

xWaitForAllBits pdTRUE :当形参 uxBitsToWaitFor 指定的位都置位的时候,xEventGroupWaitBits()才满足任务唤醒的条件,这也是“逻辑与”等待事件,并且在没有超时的情况下返回对应的事件标志位的值。pdFALSE:当形参 uxBitsToWaitFor 指定的位有其中任意一个置位的时候,这也是常说的“逻辑或”等待事件,在没有超时的情况下 函数返回对应的事件标志位的值。xTicksToWait 最大超时时间,单位为系统节拍周期

返回值

返回事件中的哪些事件标志位被置位,返回值很可能并不是用户指定的事件位,需要对返回值进行 判断再处理 。

其应用类似某个全局变量,等待事件的任务在设定的时间内,监控该变量某些位的值;该值由其他任务或中断修改。


使用特权

评论回复
65
我只会加减乘除|  楼主 | 2022-5-1 19:18 | 只看该作者
九、 任务通知

FreeRTOS 从 V8.2.0 版本开始提供任务通知这个功能,可以在一定场合下替代 FreeRTOS 的信号量,队列、事件组等,但是使用也有局限性。将宏定义 configUSE_TASK_NOTIFICATIONS 设置为 1才能开启开功能。但该功能并不常用。


使用特权

评论回复
66
我只会加减乘除|  楼主 | 2022-5-1 19:18 | 只看该作者
十、 内存管理10.1 内存管理的概念

FreeRTOS 内存管理模块管理用于系统中内存资源,它是操作系统的核心模块之一。主要包括内存的初始化、分配以及释放。一般不同的平台移植代码,内存的动态申请和释放接口需要替换。嵌入式实时操作系统中,一般不支持标准C库中的 malloc()和 free(),其内存有限,随着内存不断被分配和释放,整个系统内存区域会产生越来越多的碎片。

FreeRTOS提供了 5 种内存管理算法,源文件在Source\portable\MemMang 路径下,使用的时候选择其中一个。heap_1.c、heap_2.c 和 heap_4.c 这三种内存管理方案,内存堆实际上是一个很大的 数 组ucHeap。

heap_1.c内存管理方案简单,它只能申请内存而不能进行内存释放。有些嵌入式系统并不会经常动态申请与释放内存,一般都是在系统启动后就一直使用下去,永不删除,适合这种方式。

heap_2.c 方案支持释放申请的内存,但是它不能把相邻的两个小的内存块合成一个大的内存块,对于每次申请内存大小都比较固定的;但每次申请并不是固定内存大小的则会造成内存碎片。如下图,随着不断的申请释放,空闲空间会变成很多小片段。

heap_3.c 方案只是封装了标准 C 库中的 malloc()和 free()函数,由编译器提供,需要通过编译器或者启动文件设置堆空间。

heap_4.c 方案是在heap_2.c 基础上,对内存碎片进行了改进,能把相邻的空闲的内存块合并成一个更大的块,这样可以减少内存碎片。

heap_5.c 方案在实现动态内存分配时与 heap4.c 方案一样,采用最佳匹配算法和合并算法,并且允许内存堆跨越多个非连续的内存区,也就是允许在不连续的内存堆中实现内存分配,比如做图形显示,可能芯片内部的 RAM 不足,额外扩展SDRAM,那这种内存管理方案则比较合适。

一般物联网平台使用的是heap_4.c。


使用特权

评论回复
67
我只会加减乘除|  楼主 | 2022-5-1 19:19 | 只看该作者
10.2 内存管理接口

不管其内部的管理如何实现的,对上层应用层的接口都是一样的。

1. void *pvPortMalloc( size_t xSize ); //内存申请函数   
2. void vPortFree( void *pv );          //内存释放函数   
3. void vPortInitialiseBlocks( void ); //初始化内存堆函数   
4. size_t xPortGetFreeHeapSize( void );    //获取当前未分配的内存堆大小   
5. size_t xPortGetMinimumEverFreeHeapSize( void ); //获取未分配的内存堆历史最小值  

一般主要是使用内存申请和释放两个接口,用法和注意事项同malloc/free一样,成对使用。内存释放后尽量将指针设为NULL。


使用特权

评论回复
68
我只会加减乘除|  楼主 | 2022-5-1 19:20 | 只看该作者
十一、 通用接口

一些常用接口进行说明。

11.1 临界段

进入和退出临界段的宏在 task.h 中定义,进入和退出临界段的宏分中断保护版本和非中断版本,但最终都是通过开/关中断来实现。主要用于对全局变量的控制,系统使用非常多,但实际项目中没使用,因为全局变量的异常访问时小概率问题,只是测试没发现,理论上是存在问题的。

1. /* 在中断场合*/  {   
2.     uint32_t ulReturn;   
3.     
4.     ulReturn = taskENTER_CRITICAL_FROM_ISR(); /* 进入临界段,临界段可以嵌套 */   
5.     
6.     /* 临界段代码 */      
7.        
8.     taskEXIT_CRITICAL_FROM_ISR( ulReturn );  }   /* 退出临界段 */
1.  /* 在非中断场合 */  {   
2.      
3.     taskENTER_CRITICAL();     /* 进入临界段 */
4.
5.     /* 临界段代码 */   
6.     
7.     taskEXIT_CRITICAL();  }   /* 退出临界段*/  


使用特权

评论回复
69
我只会加减乘除|  楼主 | 2022-5-1 19:21 | 只看该作者
11.2 任务阻塞延时

vTaskDelay ()阻塞延时,任务调用该延时函数后会被剥离 CPU 使用权,进入阻塞状态,直到延时结束。但是该函数不能用在中断服务和定时回调函数。延时单位是tick。


使用特权

评论回复
70
我只会加减乘除|  楼主 | 2022-5-1 19:22 | 只看该作者
11.3 获取系统时钟计数值
1. TickType_t xTaskGetTickCount( void )  
2. TickType_t xTaskGetTickCountFromISR( void )  
注意该接口分任务版和中断版,该接口获取的是tick计数值,需要结合系统时钟频率转换成时间。

使用特权

评论回复
71
我只会加减乘除|  楼主 | 2022-5-1 19:22 | 只看该作者
11.4 中断回调函数

和其它平台不同,中断回调中释放中断标记即可,freeRTOS中,中断触发后,可能某些阻塞的任务获取了相关信号,需要立刻执行,因此中断服务发送消息后,需要主动查询阻塞任务的情况,执行任务切换动作。

1. static uint32_t ulExampleInterruptHandler( void )  
2. {  
3.     BaseType_t xHigherPriorityTaskWoken;  
4.
5.     xQueueSendToBackFromISR (xQueueRx,&cChar,&xHigherPriorityTaskWoken);  
6.     portYIELD_FROM_ISR(xHigherPriorityTaskWoken);  
7. }  


使用特权

评论回复
72
Uriah| | 2022-10-7 07:07 | 只看该作者

I2C1工作时钟源选择;I2C1模块工作时钟的开启使能。

使用特权

评论回复
73
Bblythe| | 2022-10-7 10:06 | 只看该作者

CPU借助于APB总线访问相关寄存器达到对I2C1工作模块的控制

使用特权

评论回复
74
Pulitzer| | 2022-10-7 13:05 | 只看该作者

控制模块的时钟仍然由外设时钟PCLK提供

使用特权

评论回复
75
Uriah| | 2023-6-1 07:07 | 只看该作者

128以上的某些地址为特殊寄存器使用,不能给程序用

使用特权

评论回复
76
帛灿灿| | 2023-6-1 09:03 | 只看该作者

超出 120 个字节则必须用 idata 显式的指定为间接寻址

使用特权

评论回复
77
Bblythe| | 2023-6-1 10:06 | 只看该作者

只要内存占用量不超过 256.0 就可以用 small 模式编译

使用特权

评论回复
78
童雨竹| | 2023-6-1 12:02 | 只看该作者

small 模式下未指存储类型的变量默认为data型

使用特权

评论回复
79
Pulitzer| | 2023-6-1 13:05 | 只看该作者

超过变量128后必须使用compact模式编译

使用特权

评论回复
80
公羊子丹| | 2023-6-1 14:08 | 只看该作者

访问时采用不同的指令,所以并不会占用 RAM 空间

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则