FreeRTOS相关记录

freeRTOS的优势

  1. 实时性能:FreeRTOS是一个实时操作系统,具有响应快、精确的任务调度和中断处理能力。它能够满足实时应用的时间约束,确保关键任务按时执行,保持系统的可靠性和稳定性。
  2. 可移植性:FreeRTOS的内核是高度可移植的,可以方便的移植到不同的处理器架构和硬件平台上。这使得开发者可以在各种嵌入式系统上使用FreeRTOS,无论是单片机还是高性能处理器。
  3. 简单易用:FreeRTOS具有简单和直观的API,易于学习和使用。它提供了一套清晰的任务管理、同步机制和内存管理功能,开发者可以快速上手并开发应用程序。
  4. 资源效率:FreeRTOS是一个轻量级的操作系统,具有较小的内存占用和低的处理器负载。它被设计为高效利用有限的资源,适用于资源受限的嵌入式环境。
  5. 可扩展性:FreeRTOS提供了可扩展的特性和组件,如信号量、队列、定时器等,可以根据应用需求进行定制和扩展。开发者可以根据实际情况选择所需的功能,提高系统的灵活性和可定制性。
  6. 开源和活跃的社区支持:FreeRTOS是一个开源项目,拥有庞大而活跃的开发者社区。这意味着开发者可以获得免费的支持、文档、示例代码和社区贡献的扩展功能,从而更好的理解和使用FreeRTOS。

freeRTOS的不足

  1. 学习曲线:对于没有使用过实时操作系统或多线程编程经验的开发者来说,学习和理解FreeRTOS的概念、API和调试技术可能需要一定的时间和精力。
  2. 多任务管理:在复杂的应用场景中,多任务管理可能会变得复杂。开发者需要仔细规划和设计任务之间的通信、同步和资源共享,以确保正确的任务调度和数据完整性。
  3. 内存管理:虽然FreeRTOS提供了内存管理功能,但在资源受限的嵌入式系统中,内存管理可能成为挑战。开发者需要谨慎管理内存的分配和释放,避免内存泄漏和碎片化。
  4. 适应性限制:尽管FreeRTOS可移植性较好,但对于某些特定的硬件平台或处理器架构,可能需要额外的工作来适配和支持。在某些情况下,可能需要修改FreeRTOS的内核代码来满足特定的需求。
  5. 功能限制:与一些商业实时操作系统相比,FreeRTOS在某些高级功能方面可能有一定的限制。例如,与商业RTOS相比,FreeRTOS的网络协议栈和文件系统支持可能较为有限。
  6. 维护和支持: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种内存管理算法。

  1. heap_1.c:实现了pcPortMalloc ,而没有实现vPortFree(),因此适用于从来不会删除任务的应用。 因为不会释放动态分配的内存 ,所以不会产生内存碎片。
  2. heap_2.c:可以释放内存,但是不能对相邻两块小内存空间进行合并,造成内存碎片。已经被heap_4.c所替代。该方案只适合于频繁删除,创建相同任务的应用。
  3. heap_3.c:pcPortMalloc()、vPortFree() 和原先的Malloc()、free()的区别是,前者保证了线程安全的。
  4. heap_4.c:是对heap_2.c的增强,加入了对相邻小块内存的合并,避免产生内存碎片。
  5. 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);

  1. 当写入数据总数不足流触发字节数时,接收函数阻塞相应时间。
  2. 当写入数据总数达到触发字节数时,接收函数即可接收数据。
  3. 当接收函数被触发后,接收函数持续接收至缓冲区数据为空后才会再次阻塞。

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中的信号量、任务、队列、事件组等都可以使用静态的方式创建,即自己分配堆栈,不是上述采用自动分配的方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值