freeRTOS相关
freeRTOS的优势
- 实时性能:FreeRTOS是一个实时操作系统,具有响应快、精确的任务调度和中断处理能力。它能够满足实时应用的时间约束,确保关键任务按时执行,保持系统的可靠性和稳定性。
- 可移植性:FreeRTOS的内核是高度可移植的,可以方便的移植到不同的处理器架构和硬件平台上。这使得开发者可以在各种嵌入式系统上使用FreeRTOS,无论是单片机还是高性能处理器。
- 简单易用:FreeRTOS具有简单和直观的API,易于学习和使用。它提供了一套清晰的任务管理、同步机制和内存管理功能,开发者可以快速上手并开发应用程序。
- 资源效率:FreeRTOS是一个轻量级的操作系统,具有较小的内存占用和低的处理器负载。它被设计为高效利用有限的资源,适用于资源受限的嵌入式环境。
- 可扩展性:FreeRTOS提供了可扩展的特性和组件,如信号量、队列、定时器等,可以根据应用需求进行定制和扩展。开发者可以根据实际情况选择所需的功能,提高系统的灵活性和可定制性。
- 开源和活跃的社区支持:FreeRTOS是一个开源项目,拥有庞大而活跃的开发者社区。这意味着开发者可以获得免费的支持、文档、示例代码和社区贡献的扩展功能,从而更好的理解和使用FreeRTOS。
freeRTOS的不足
- 学习曲线:对于没有使用过实时操作系统或多线程编程经验的开发者来说,学习和理解FreeRTOS的概念、API和调试技术可能需要一定的时间和精力。
- 多任务管理:在复杂的应用场景中,多任务管理可能会变得复杂。开发者需要仔细规划和设计任务之间的通信、同步和资源共享,以确保正确的任务调度和数据完整性。
- 内存管理:虽然FreeRTOS提供了内存管理功能,但在资源受限的嵌入式系统中,内存管理可能成为挑战。开发者需要谨慎管理内存的分配和释放,避免内存泄漏和碎片化。
- 适应性限制:尽管FreeRTOS可移植性较好,但对于某些特定的硬件平台或处理器架构,可能需要额外的工作来适配和支持。在某些情况下,可能需要修改FreeRTOS的内核代码来满足特定的需求。
- 功能限制:与一些商业实时操作系统相比,FreeRTOS在某些高级功能方面可能有一定的限制。例如,与商业RTOS相比,FreeRTOS的网络协议栈和文件系统支持可能较为有限。
- 维护和支持:FreeRTOS是一个开源项目,虽然有庞大的社区支持,但对于一些特定问题或需求,可能需要依赖社区贡献的扩展或自行解决。商业RTOS通常提供更全面的技术支持和保障。
临界段:
临界段就是执行不想被中断的代码段,比如创建任务的时候。
临界段其本质就是关闭FreeRtos可控优先级中断,即低于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY优先级的中断。
taskENTER_CRITICAL(); //进入临界区
taskEXIT_CRITICAL(); //退出临界区
任务创建:
任务创建分为动态创建和静态创建。
动态创建堆栈由系统分配;静态创建堆栈由自己传递。
//动态创建
xTaskCreate( vTask1,/*指向任务函数的指针*/
"Task 1",/*任务的文本名字,只会在调试中用到*/
512,/*栈深度 大多数小型微控制器会使用的值会比此值小得多*/
NULL,/*没有任务参数*/
7,/*此任务运行在优先级7上. */
&pvCreatedTask_vTask1 );/*任务句柄*/
//静态创建
xTaskCreateStatic
消息队列:
消息队列是常用于任务间通信的数据结构,且读写队列均支持超时机制。
Queue1 = xQueueCreate(10,sizeof(uint8_t)); //创建消息队列
for(int i = 0 ; i < 8;i++)
{
//以下三种都是消息队列发送,其中xQueueSendToBack与xQueueSend是一样的,都是同一函数,只是define成不同名字
//xQueueSendToFront为后入先出,其他为先入先出
//主要是入队方式的不同
xQueueSendToBack(Queue1,&data[i],10); //发送消息队列 超等待10
xQueueSend(Queue1,&data[i],10);
xQueueSendToFront(Queue1,&data[i],10);
}
for(int i = 0 ; i < 8;i++)
{
xQueueReceive(Queue1,&data[i],100); //读取后删除队列中消息 超等待100
}
信号量:
信号量可以简单分为四类:二值信号量、计数信号量、互斥信号量、递归互斥信号量。
1.二值信号量
二值信号量可用于临界资源访问,也可以用于同步功能。其相当于长度为1的队列。
xSemaphoreHandle binaryHandle = NULL; //二值信号量
vSemaphoreCreateBinary(binaryHandle); //创建二值信号量
获取成功后,BinFlag 为pdTRUE,失败为pdFALSE
portBASE_TYPE BinFlag = xSemaphoreTake(binaryHandle,10); //超时10
xSemaphoreGive(binaryHandle); //释放二值信号量
二值信号量在中断中的使用:
static portBASE_TYPE xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(binaryHandle,&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//xHigherPriorityTaskWoken == pdTRUE才会切换
//portYIELD_FROM_ISR原型为:
#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired != pdFALSE ) portYIELD()
#define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x )
2.计数信号量
计数信号量可用于事件计数,也可以用于资源管理(比如停车位)。其相当于长度大于1的队列。
xSemaphoreHandle CounctHandle = NULL; //计数信号量
CounctHandle = xSemaphoreCreateCounting(5,5);//最大计数值 初始值表示还有几个信号量可以获取
获取成功后,BinFlag 为pdTRUE,失败为pdFALSE
///每获取一次,可用二值信号量减1,释放则加1
portBASE_TYPE BinFlag = xSemaphoreTake(CounctHandle,0); //0为不阻塞
xSemaphoreGive(CounctHandle); //释放二值信号量
3.互斥信号量
互斥信号量其实是一个拥有优先级继承的二值信号量,比如一个低优先级任务获取了互斥信号量,正在使用时,高优先级任务再来获取此互斥信号量,此时高优先级任务被阻塞,相当于低优先级任务的优先级被提高到等同高优先级任务。
//使用互斥量后不释放将会在下次获取时,一直阻塞。
//本任务内获取互斥信号量后,只能在本任务内释放。
xSemaphoreHandle MutexHandle = NULL; //互斥量
MutexHandle = xSemaphoreCreateMutex(); //创建互斥量
xSemaphoreTake(MutexHandle,portMAX_DELAY); //无限超时等待,没有信号量就阻塞
xSemaphoreGive(MutexHandle); //使用完互斥信号量后要释放
4.递归互斥信号量
递归互斥信号量就是互斥信号量的扩展,在本任务内可以多次获取递归互斥信号量,但是在本任务内获取多少次,则必须在本任务内释放多少次,否则就会无限阻塞。
//使用互斥量后不释放将会在下次获取时,一直阻塞。
//本任务内获取递归互斥信号量后,只能在本任务内释放。
xSemaphoreHandle numMutexHandle = NULL; //递归互斥量
numMutexHandle = xSemaphoreCreateRecursiveMutex();//创建递归互斥量
xSemaphoreTakeRecursive(numMutexHandle,portMAX_DELAY);//获取递归互斥量,portMAX_DELAY表示一直阻塞
xSemaphoreTakeRecursive(numMutexHandle,portMAX_DELAY);//获取递归互斥量,portMAX_DELAY表示一直阻塞
xSemaphoreTakeRecursive(numMutexHandle,portMAX_DELAY);//获取递归互斥量,portMAX_DELAY表示一直阻塞
xSemaphoreGiveRecursive(numMutexHandle);//释放互斥量
xSemaphoreGiveRecursive(numMutexHandle);//释放互斥量
xSemaphoreGiveRecursive(numMutexHandle);//释放互斥量
事件标志组:
当 configUSE_16_BIT_TICKS为0的时候,TickType_t是个32位的数据类型,因此EventBits_t也是个32位的数据类型。EventBits_t类型的变量可以存储24个事件位,另外的那高8位有其他用。事件位0存放在这个变量的bit0上,变量的bit1就是事件位1,以此类推。对于STM32来说,一个事件标志组最多可以存储24个事件位。
EventGroupHandle_t EventGroupHandle = NULL;//事件组
EventGroupHandle = xEventGroupCreate(); // 创建事件标志组
//清除指定bit位事件
//xEventGroup:事件组句柄
//uxBitsToClear:要清除的位,比如0x09,同时清除bit3和bit0
//返回清除前的事件值
EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )
//xEventGroupGetBits就是xEventGroupClearBits( xEventGroup, 0 ),即不清除事件位并获取事件值
xEventGroupGetBits( xEventGroup )//获取事件值
// 定义事件位
#define BIT_0 (1<<0)
#define BIT_1 (1<<1)
xEventGroupSetBits(EventGroupHandle,BIT_0); // 设置事件标志组 BIT_0 置1
xEventGroupSetBits(EventGroupHandle,BIT_1); // 设置事件标志组 BIT_1 置1
//等待事件,返回值为事件值
EventBits_t EventBitsVal = xEventGroupWaitBits(
EventGroupHandle, // 要等待的事件标志组
(BIT_0|BIT_1), // 等待的事件位
pdTRUE, // 清零BIT_0|BIT_1,pdFALSE则在退出时不清零
pdFALSE, // 只要事件位其一得到就退出,pdTRUE则需要都满足才会退出
portMAX_DELAY ); // 死等
任务通知:
任务通知可以提高运行速度,并且减少RAM的使用。
一个任务通知只能有一个接收任务,接收任务可以因为接收任务通知而进入阻塞态,发送任务则不会因为任务通知发送失败而阻塞。
//任务通知
//通知任务句柄:pvCreatedTask_vTask1
//通知值:number
//通知的方式:eSetValueWithOverwrite
xTaskNotify( pvCreatedTask_vTask1, number, eSetValueWithOverwrite );
eNoAction = 0, /* Notify the task without updating its notify value. */
eSetBits, /* 更新指定的bit */
eIncrement, /* 通知值加一 */
eSetValueWithOverwrite, /* 覆盖写入的方式更新通知值*/
eSetValueWithoutOverwrite /* 不覆盖写通知值 */
//等待获取通知值
//等待阻塞realnumber的时间
//0x00:表示使用通知前不清除任务的通知值位
//portMAX_DELAY:表示函数xTaskNotifyWait()退出前将任务通知值设置为0
uint32_t ulNotifiedValue = 0;
uint32_t realnumber = 1000;
if( pdPASS == xTaskNotifyWait( 0x00, portMAX_DELAY , &ulNotifiedValue, realnumber ) )
{
//成功获取通知
//利用通知值修改阻塞时间
realnumber = ulNotifiedValue;
}
//获取任务通知
//xClearCountOnExit:为pdFALSE时,退出函数任务通知值减一,为pdTRUE时,退出函数任务通知值清零。类似二值信号量
//xTicksToWait:阻塞时间
ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait )
内存管理:
FreeRTOS将内核与内存管理分开实现,所以用户可以自己选择不同的分配策略去适配自己的需求。对于v9.0版本分别提供了heap_1.c、heap_2.c、heap_3.c、heap_4.c、heap_5.c,这5种内存管理算法。
- heap_1.c:实现了pcPortMalloc ,而没有实现vPortFree(),因此适用于从来不会删除任务的应用。 因为不会释放动态分配的内存 ,所以不会产生内存碎片。
- heap_2.c:可以释放内存,但是不能对相邻两块小内存空间进行合并,造成内存碎片。已经被heap_4.c所替代。该方案只适合于频繁删除,创建相同任务的应用。
- heap_3.c:pcPortMalloc()、vPortFree() 和原先的Malloc()、free()的区别是,前者保证了线程安全的。
- heap_4.c:是对heap_2.c的增强,加入了对相邻小块内存的合并,避免产生内存碎片。
- heap_5.c:对Heap_4,进行再次的增强,将不相邻的代码内存空间进行链接使用,变成更大的内存空间。
任务大小确认:
#define INCLUDE_uxTaskGetStackHighWaterMark 1 //定义置1 可查看堆栈剩余多少
//pvCreatedTask_vTask1:任务句柄
//返回任务剩余堆栈大小
unsigned portBASE_TYPE num = uxTaskGetStackHighWaterMark(pvCreatedTask_vTask1);
流缓冲区
freeRTOS流缓冲区是任务和任务间、中断和任务之间的通信,还可在双核间传递消息。流缓冲区的实现,假设只有一个任务或中断写入,并且仅有一个任务或中断从缓冲区读取,此时写入和读取都是安全的;但是有多个不同的写入或者读取是不安全的。若有多个不同的写入或者读取,在调用相应的API时,需要进入临界区,并将相应的阻塞时间设置为0。
//创建句柄
StreamBufferHandle_t xStreamBuffer = NULL;
xStreamBuffer = xStreamBufferCreate(
100,//流缓冲区最大字节数
10);//流触发字节数 -> 当缓冲区数据大于等于此设置参数时 流接收函数可接收到数据
//返回发送长度
//xStreamBuffer句柄
//sendBuff发送的数据buff
//sizeof(uint8_t) * 8发送数据长度
//portMAX_DELAY阻塞时间
length = xStreamBufferSend(xStreamBuffer,sendBuff,sizeof(uint8_t) * 8,portMAX_DELAY);
//返回接收长度
//xStreamBuffer句柄
//receiveBuff接收数据的buff
//sizeof(uint8_t) * 24单次接收最多数据
//portMAX_DELAY阻塞时间
length = xStreamBufferReceive(xStreamBuffer,receiveBuff,sizeof(uint8_t) * 24,portMAX_DELAY);
- 当写入数据总数不足流触发字节数时,接收函数阻塞相应时间。
- 当写入数据总数达到触发字节数时,接收函数即可接收数据。
- 当接收函数被触发后,接收函数持续接收至缓冲区数据为空后才会再次阻塞。
API杂记:
在删除任务后,任务所占用内存由空闲任务回收。
空闲任务在启动调度器时自动创建。
空闲任务的钩子函数绝不能被阻塞,阻塞有可能导致其他任务没有进入运行态。
#define configUSE_IDLE_HOOK 1 /* 空闲任务的钩子函数,如果配置为1,这个钩子函数需要用户自己编写 */
//空闲任务的钩子函数定义不可更改
void vApplicationIdleHook(void) //空闲任务的钩子函数
{
}
//删除任务
//pvCreatedTask_vTask1任务句柄
vTaskDelete(pvCreatedTask_vTask1);
//获取任务优先级
//xTask:任务句柄,本任务时为NULL
//返回优先级
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
//修改优先级
//xTask:任务句柄,本任务时为NULL
//uxNewPriority :新的优先级
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
//挂起任务
//pvCreatedTask_vTask1任务句柄
vTaskSuspend(pvCreatedTask_vTask1);
//恢复任务
//pvCreatedTask_vTask1任务句柄
vTaskResume(pvCreatedTask_vTask1);
//挂起所有任务
void vTaskSuspendAll( void )
//恢复所有任务
//返回pdFALSE失败 pdTRUE成功
BaseType_t xTaskResumeAll( void )
FreeRTOS中的信号量、任务、队列、事件组等都可以使用静态的方式创建,即自己分配堆栈,不是上述采用自动分配的方式。