FreeRTOS大杂烩

Freertos移植
  • 其核心文件为,tasks.c、timers.c、queue.c、event_groups.c、croutine.c、list.c。源码兼顾了很多平台,但是我们可以删除一些不用的平台以及例程等,只保留我们要的。
  • 比如RVDS/ARM_CM3,这表示cotexM3架构再RVDS或Keil工具上的移植文件(比如下图中的port.c、portmacro.h)
  • 在编程过程中的大部分宏配置都在FreeRTOSConfig.h这个配置文件(调度、优先级、空闲任务等)
    在这里插入图片描述
任务切换基础

在这里插入图片描述

  • 任务间的切换,是利用tick中断实现的(关于这个时间片轮转的时间通过上面提到过的配置文件设置)

  • 任务状态有。就绪态、运行态、挂起态、阻塞态(延时、同步机制),其中每种状态下若有多个任务使用链表进行扩充
    空闲任务(pvIdleTask):(只能是就绪态或运行态)是为了帮别人收尸的(别人杀你,由别人清理。但是自杀自己是无法清理的).但是空闲任务不会直接供你使用,而是提供了一个钩子函数vApplicationIdleHook(执行的要快)让我在内部进行实现代码。

  • 首先空闲任务的目的是可以执行一些低优先级,后台的需要连续执行的函数。

  • 任务调度算法: 在FreeRTOSConfig.h文件中有宏可以配置
    (1)是否可抢占(高优先级可抢占、或者只有我自动说我不用了,你才能用)
    (2)同优先级是否可以交替执行(是否支持时间片轮转)
    (3)空闲任务是否礼让(一般空闲任务是最低优先级)

同步互斥与通信

这里其实可以把多任务看成是多线程,可知多线程是在一个进程的且文件资源共享。此时最高的办法就是使用全局变量。但是会出现资源竞争的问题,为此提出了同步机制来解决。

实现同步互斥的方法:队列、事件组、信号量、任务通知、互斥量
在这里插入图片描述

队列:

在这里插入图片描述

  • 队列像是一个传送带,而这传送带的两端可以是任务到任务、任务到中断、中断到任务直接传输信息

  • 队列结构体内部含有一个环形buffer以及Ready_Send_List、Ready_Receive_List.

    //创建队列
    QueueHandle_t xQueueCreate( UBaseType_t uxQueueLength,// Nitem
    							UBaseType_t uxItemSize ); //item_size
    //往队列写
    BaseType_t xQueueSend( QueueHandle_t xQueue, 
    						const void*pvItemToQueue,//数据的地址
    						TickType_t xTicksToWait ); //阻塞时间
    //关于往队列里面写(往尾部写、往头部写以及扩展特定的为中断服务任务使用的xxxxFrom_ISR)
    //从队列读
    BaseType_t xQueueReceive( QueueHandle_t xQueue, 
    						void * const pvBuffer, //数据地址
    					TickType_t xTicksToWait );//阻塞时间
    
邮箱:
  • FreeRTOS的邮箱概念与其它RTOS不一样。它是一个特殊的队列,因为队列的长度固定为1
  • 写邮箱:新数据覆盖旧数据,在任务中使用xQueueOverwrite(),
    在中断中使用 xQueueOverwriteFromISR() 。
    既然是覆盖,那么无论如何,数据总可以写入成功
  • 读邮箱:读数据时,数据不会被移除;在任务中使用xQueuePeek(),
    在中断中使用xQueuePeekFromISR(),
    意味着,第一次调用时会因为无数据而阻塞,一旦写入数据,以后读邮箱总能成功
队列集合:

在这里插入图片描述

  • 检测多个队列,挑出有数据的队列,进行读队列
  • 举例:有三个任务队列,鼠标任务队列queueA、按键任务队列queueB、触摸屏任务队列queueC
    *1)直接创建一个队列集
    	queue_set=xQueueCreateSet(3)
    *2)建立联系,即把单个队列依次放到队列集里面
    	xQueueAddToSet(queueA)xQueueAddToSet(queueB)
    	xQueueAddToSet(queueC)
    *3)写完队列A一次,就会写队列集一次(写队列自动会写队列集)
    	touch=>(data)=>touch_queue=>(handle)=>QueueSet
    *4)读queue_set一次,返回某一个队列(有数据的队列),并且读这个返回的队列
    
信号量:
  • 背景:关于上面的任务队列,可以用于任务与任务、任务与中断中间的数据传输,但是队列传输涉及到数据的复制等。而信号量只需要传递状态,并不需要传递具体的信息
  • 信号量:信号起通知作用、量用来表示资源的数量,
    用give给出资源,计数值+1,
    用take获得资源,计数值-1;
  • 二进制信号量:二进制信号量与计数型的唯一差别就是二值型的最大值被限定为1
    *申请二进制信号量句柄 
    	SemaphoreHandle_t xBinarySemaphore; 
    *创建二值信号量 
    	SemaphoreHandle_t xSemaphoreCreateBinary( void ); 
    *计数型信号量
    	SemaphoreHandle_t xSemaphoreCreateCounting(
    			UBaseType_t uxMaxCount,//3 			
    			UBaseType_t uxInitialCount);//0
    *1/1:
    	BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    	BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,  
    									TickType_t xTicksToWait); 
    * 二值信号量初始值为1,互斥。
    * 二值信号量初始值为0,同步。
    
互斥量:
  • 上面的队列和二值信号量和队列都可以实现互斥的功能。但是有个问题。
    关于信号量的互斥,只要是个人都可以give和take。如果现在ab互斥,c来捣乱。就出现了很大问题
    为此出现了互斥量
  • 互斥量的核心:谁上锁,就只能谁解锁,但是FreeRTOS的互斥锁并没有代码实现(其实linux也是如此),只是靠程序员的自觉了。(这样导致谁上锁谁释放只是一种约定,好在RT-Thead中实现了)
  • 互斥量的重点:优先级的反转(优先级继承)、扩展递归锁
  • 互斥量函数:互斥量不能在ISR中使用。
    创建互斥量
    	SemaphoreHandle_t xSemaphoreCreateMutex( void ); 
    释放
    	BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );
    获得
    	BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore,
    									TickType_t xTicksToWait );
    删除
    	void vSemaphoreDelete( SemaphoreHandle_t xSemaphore ); 
    
事件组:

在这里插入图片描述

  • 解释:上述bit0用来表示串口是否就绪,bit1用来表示按键是否被按下。
    (值为1表示事件发生,值为0表示事件没发生)
    一个或多个任务、ISR都可以去写这些位;一个或多个任务、ISR都可以去读这些位
    可以等待某一位、某些位中的任意一个,也可以等待多位
  • 操作:或"、"与
    唤醒谁?
    *1队列、信号量:事件发生时,只会唤醒一个任务
    *2事件组:事件发生时,会唤醒所有符号条件的任务,简单地说它有“广播”的作用
    是否清除事件?
    *1队列、信号量:是消耗型的资源,队列的数据被读走就没了;信号量被获取后就减少了
    *2事件组:被唤醒的任务有两个选择,可以让事件保留不动,也可以清除事件
  • 事件组操作函数:
    要先创建,得到一个句柄;
    创建
    	EventGroupHandle_t xEventGroupCreate( void ); 
    设置事件 
    	EventBits_t xEventGroupSetBits( EventGroupHandle_t
    										 xEventGroup,//事件组
    						const EventBits_t uxBitsToSet ); //设置哪些位
    等待事件
    	EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, 
    							const EventBits_t uxBitsToWaitFor, //等待哪些位
    							const BaseType_t xClearOnExit,//是否要清除事件 
    							const BaseType_t xWaitForAllBits,//等待1位还是all位 
    							TickType_t xTicksToWait );//等待事件
    删除
    	void vEventGroupDelete( EventGroupHandle_t xEventGroup ); 
    
任务通知:

在这里插入图片描述
使用队列、信号量、事件组等。并不知道对方是谁,
但是使用任务通知时,可以明确指定:通知哪个任务

  • 优势:
    (1)可以明确指定通知哪个任务
    (2)节省内存,TCB中内置通知结构体成员,无须另外创建
    通知成员为:通知值ulNotifiedValue(计数值、位、任意数值)
    通知状态ucNotifyState(无等待通知、在等待通知、接受通知待处理)
  • 缺点:
    (1)不能发送给ISR,ISR并没有TCB即内部无通知功能
    (2)数据只能该任务独享
    (3)无法缓冲数据
    (4)无法广播
    (5)若发送受阻,发送方无法进入阻塞状态等待
  • 任务通知函数操作:
    发出通知xTaskNotifyGive 与 vTaskNotifyGiveFromISR
    取出通知ulTaskNotifyTake
定时器使用:

在这里插入图片描述

  • 定时器三要素:超时时间、函数(回调函数)、单独触发还是周期触发
  • 操作函数:
    //创建 
    TimerHandle_t xTimerCreate( const char * const pcTimerName, 
    						const TickType_t xTimerPeriodInTicks, 
    								const UBaseType_t uxAutoReload, 
    										void * const pvTimerID, 
    					TimerCallbackFunction_t pxCallbackFunction ); 
    					
    //回调函数
    void ATimerCallback( TimerHandle_t xTimer ); 
    typedef void (* TimerCallbackFunction_t)( TimerHandle_t xTimer ); 
    
    //启动/停止/复位(启动定时器设置为运行态,停止定时器设置为睡眠态,复位即是冬眠转运行态)
    BaseType_t xTimerStart( TimerHandle_t xTimer, 
    					TickType_t xTicksToWait ); 
    BaseType_t xTimerStop( TimerHandle_t xTimer, 
    					TickType_t xTicksToWait );
    BaseType_t xTimerReset( TimerHandle_t xTimer,
     					TickType_t xTicksToWait ); 
     					
    //修改定时器周期:
    BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, 
    								TickType_t xNewPeriod, 
    							TickType_t xTicksToWait ); 
    							
    //删除定时器
    BaseType_t xTimerDelete( TimerHandle_t xTimer, 
    						TickType_t xTicksToWait ); 
    
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栋哥爱做饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值