延时函数
- vTaskDelay():相对延时,指每次延时都是从执行函数vTaskDelay()开始,直到延时指定的时间结束。
- vTaskDelayUntil():绝对延时,整个任务的运行周期看成一个整体,适用于需要按照一定频率运行的任务。
(1)为任务主体,也就是任务真正要做的工作。
(2)是任务函数中调用vTaskDelayUntil()对任务进行延时。
(3)其他任务在运行(高优先级)
#if ( INCLUDE_vTaskDelay == 1 )
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/* A delay time of zero just forces a reschedule. */
if( xTicksToDelay > ( TickType_t ) 0U )//入口参数大于0
{
configASSERT( uxSchedulerSuspended == 0 );
vTaskSuspendAll();//挂起任务调度器
{
traceTASK_DELAY();
/* A task that is removed from the event list while the
scheduler is suspended will not get placed in the ready
list or removed from the blocked list until the scheduler
is resumed.
This task cannot be in an event list as it is the currently
executing task. */
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );//任务添加到阻塞列表中
}
xAlreadyYielded = xTaskResumeAll();//恢复任务调度器
}
else
{
mtCOVERAGE_TEST_MARKER();
}
/* Force a reschedule if xTaskResumeAll has not already done so, we may
have put ourselves to sleep. */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
#endif /* INCLUDE_vTaskDelay */
/*-----------------------------------------------------------*/
消息队列
队列是任务到任务、任务到中断、中断到任务数据交流的一种机制(消息传递)。
全局变量的弊端:数据无保护,导致数据不安全,当多个任务同时对该变量操作时,数据易受损。
/*写队列*/
xQueueSend()
{
//进入临界区(关中断)
写队列实际操作
//退出临界区(开中断)
}
/*读队列*/
xQueueReceive()
{
//进入临界区(关中断)
读队列实际操作
//退出临界区(开中断)
}
FreeRTOS基于队列,实现了多种功能,包括队列集、互斥信号量、计数型信号量、二值信号量、递归互斥信号量。
队列中可以存储数量有限、大小固定的数据。队列中的每一个数据叫做“队列项目”,队列能够存储队列项目的最大数量称为队列的长度。
创建队列时,就要指定队列长度以及队列项目的大小。
队列通常采用“先进先出”(FIFO)的数据缓冲机制,先入队的数据会先从队列中被读取,FreeRTOS中也可以配置“后进先出”LIFO。
FreeRTOS采用实际值传递(ucos采用地址传递),即将数据拷贝到队列中进行传递,也可以传递指针,传递较大的数据的时候采用指针传递。
队列不属于某个任务,任何任务和中断都可以向队列发送/读取消息。
当任务向一个队列发送消息时,可以指定一个阻塞时间,当队列已满无法入队。
- 若阻塞时间为0:直接返回不会等待;
- 若阻塞时间为0~port_MAX_DELAY :等待设定的阻塞时间,若在该时间内还无法入队,超时后直接返回不再等待;
- 若阻塞时间为port_MAX_DELAY :死等,一直等到可以入队为止。出队阻塞与入队阻塞类似;
入队阻塞
队列满了,此时写不进去数据。
将该任务的状态列表项挂载在pxDelayedTaskList阻塞列表上。
将该任务的事件列表项挂载在xTasksWaitingToSend等待发送列表。
出队阻塞
队列为空,此时读取不了数据。
将该任务的状态列表项挂载在pxDelayedTaskList阻塞列表
将该任务的事件列表项挂载在xTasksWaitingToReceive等待接收列表
问题:当多个任务写入消息给一个“满队列”时,这些任务都会进入阻塞状态,也就是说有多个任务 在等待同一 个队列的空间。那当队列中有空间时,哪个任务会进入就绪态?
- 优先级最高
- 如果大家的优先级相同,那么等待时间最久的任务就会进入就绪态
队列操作基本过程
- 创建队列
- 往队列写入第一个消息
- 往队列写入第二个消息
- 从队列读取第一个消息
队列结构体
当用于队列使用时:
- 队列结构体存储区
- 队列项存储区(消息)
队列相关API函数
动态和静态创建队列之间的区别:队列所需的内存空间由FreeRTOS从FreeRTOS管理的堆中分配,而静态创建需要用户自行分配内存。
#define xQueueCreate ( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), (queueQUEUE_TYPE_BASE ))
- 形参uxQueueLength:队列长度
- 形参uxItemSize :队列项大小
- 返回值NULL:创建失败,其他值:创建成功,返回句柄。
FreeRTOS基于队列实现了多种功能,每一种功能对应一种队列类型,队列类型的queue.h文件中有定义
往队列写入消息
以上写入函数调用的都是同一个函数xQueueGenericSend(),只是指定了不同的写入位置。
覆写没有阻塞时间
覆写方式写入队列,只有在队列的队列长度为1时,才能够使用。
- xQueueReceive():从队列头部读取消息,并删除消息。
- xQueuePeek():从队列头部读取消息。
信号量的简介
信号量是一种解决同步问题的机制,可以实现对共享资源的有序访问。
- 判断信号量是否有资源
- 信号量有资源,获取信号量成功
- 信号量没有资源,获取信号量失败,可以选择等待(任务阻塞)释放信号量
信号量资源数:计数值
释放信号量:计数值++
获取信号量:计数值–
当计数值>0,代表有信号量资源
信号量,用于传递状态
如果信号量最大值限定为1,就是二值信号量。
如果信号量最大值>1,就是计数型信号量。
队列与信号量区别
- 队列可以容纳多个数据;信号量仅存放数值,无法存放其它数据。
- 创建队列时有两部分内存:队列结构体+队列项存储空间;创建信号量,只需分配信号量结构体。
- 写入队列:队列满时,可阻塞;释放信号量:不可阻塞,计数值++,当计数值为最大时,反悔失败。
- 读取队列:当队列为空时,可阻塞;获取信号量:计数值–,当没有资源时,可阻塞。
二值信号量
二值信号量的本质是一个队列长度为1的队列,该队列就只有空和满两种情况。
- 创建二值信号量
- 释放二值信号量
- 获取二值信号量