参考自https://www.w3cschool.cn/freertoschm/freertoschm-strb2u7m.html
xQueueCreate() 创建一个新的队列
queue.h
xQueueHandle xQueueCreate(
unsigned portBASE_TYPE uxQueueLength,
unsigned portBASE_TYPE uxItemSize
);
创建一个新的队列。为新的队列分配所需的储存内存,并返回一个队列处理
参数 | 含义 |
---|---|
uxQueueLength | 队列中包含最大项目数量 |
uxItemSize | 队列中每个项目所需的字节数。项目通过复制而不是引用排队,因为,所需的字节数,将复制给每个项目。队列中每个项目必须分配同样大小 |
返回值
成功:队列成功创建,并返回一个新建队列的处理
失败:创建队列失败,返回0
使用范例
struct AMessage {
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
};
void vATask( void *pvParameters ){
xQueueHandle xQueue1, xQueue2;
// 创建一个队列,包含10个unsigned long值
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
if( xQueue1 == 0 ){
// 队列不能创建,就不能使用
}
// 创建一个队列,包含10个指向AMessage 结构的指针
/// 可以通过指针传递,指针可以包含很多数据
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue2 == 0 ){
// 队列不能创建,就不能使用
}
// ... 其余代码
}
xQueueSend() 传递一个项目到队列
queue.h
portBASE_TYPE xQueueSend(
xQueueHandle xQueue,
const void * pvItemToQueue,
portTickType xTicksToWait
);
是一个调用xQueueGenericSend()的宏。能向后兼容FreeRTOS.org(没有包括xQueueSendToFront()和xQueueSendToBack() 宏的)版本。与xQueueSendToBack()等效。
传递一个项目到队列。这个项目通过复制而不是通过引用排队。这个函数不能从中断服务程序调用。参考xQueueSendFromISR(),在ISR中交错使用。
xQueueSend() 是全特点任务间通信API接口。xQueueSend() 等效于交叉API。版本不要同样的参数和同样的返回值。
参数 | 含义 |
---|---|
xQueue | 处理将项目传递给队列 |
pvItemToQueue | 指向队列中放置的项目的指针。项目的大小,由队列创建时定义,因为许多字节可以从 pvItemToQueue复制到队列的储存区域 |
xTicksToWait | 最大时间量(任务应该锁住,等待队列中的可用空间)应该已经满了。如果设置为0,调用将立即返回。时间使用滴答周期来定义,因此如果需要,常量portTICK_RATE_MS应该用来转换实时时间 |
返回值
成功:pdPASS
失败:errQUEUE_FULL
使用范例
struct AMessage
{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
} xMessage;
unsigned portLONG ulVar = 10UL;
void vATask( void *pvParameters )
{
xQueueHandle xQueue1, xQueue2;
struct AMessage *pxMessage;
// 创建一个队列,包含10个unsigned long值
xQueue1 = xQueueCreate( 10, sizeof( unsigned portLONG ) );
// 创建一个队列,包含10个指针指向AMessage 结构的指针
// 可以通过指针传递,指针可以包含很多数据
xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
// ...
if( xQueue1 != 0 )
{
// 传递一个unsigned long。等待10个时间片,分配所需的可用空间
if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
{
// 传递信息失败,继续等待下一个10个时间片。
}
}
if( xQueue2 != 0 )
{
// 传递一个指向 AMessage 结构的指针。如果队列已经满,不要锁住
pxMessage = & xMessage;
xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
}
// ... 其余代码
}
xQueueReceive() 从队列接收一个项目
portBASE_TYPE xQueueReceive(
xQueueHandle xQueue,
void *pvBuffer,
portTickType xTicksToWait
);
一个调用 xQueueGenericReceive() 函数的宏。
从队列接收一个项目。这个项目通过复制接收,因此缓冲
器必须提供足够大的空间。复制进缓冲器的字节数,在队列创建时已经定义。
这个函数一定不能在中断服务程序中使用。参考 xQueueReceiveFromISR 获得能够的选择。
xQueueReceive() 是全功能任务间通信API接口。xQueueAltReceive() 相当于API其中之一。版本需要相同的参数和相同的返回值。
参数 | 含义 |
---|---|
pxQueue | 将要接收项目的队列句柄 |
pvBuffer | 指向将要复制接收项目的缓冲器的指针 |
xTicksToWait | 任务中断并等待队列中可用空间的最大时间,应该是满的。如果设置为0,调用将立刻返回。时间在片区间中定义,如果需要,portTICK_RATE_MS常量用来转换为实际时间。如果 INCLUDE_vTaskSuspend 定义为1 ,指定的中断时间( portMAX_DELAY) 将导致任务无限期中断(没有时间溢出)。 |
返回值
成功:pdPASS
失败:errQUEUE_FULL
使用范例
struct AMessage{
portCHAR ucMessageID;
portCHAR ucData[ 20 ];
} xMessage;
xQueueHandle xQueue;
// Task to create a queue and post a value.
void vATask( void *pvParameters ){
struct AMessage *pxMessage;
// 创建一个队列,包含10个指针指向AMessage 结构的指针
// 可以通过指针传递,指针可以包含很多数据
xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
if( xQueue == 0 ){
// Failed to create the queue.
}
// ...
// 传递一个指向 AMessage 结构的指针。如果队列已经满,不要锁住
// pxMessage = & xMessage; //这句有点问题,pxMessage本身就是个AMessage结构体指针了
xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
// ... 其余代码
}
// 任务从队列接收项目
void vADifferentTask( void *pvParameters ){
struct AMessage *pxRxedMessage;
if( xQueue != 0 ){
// Receive a message on the created queue. Block for 10 ticks if a
// message is not immediately available.
if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) ){
// pcRxedMessage now points to the struct AMessage variable posted
// by vATask.
}
}
// ... Rest of task code.
}
xQueueSendFromISR() 向队列发送一个项目
queue.h
portBASE_TYPE xQueueSendFromISR(
xQueueHandle pxQueue,
const void *pvItemToQueue,
portBASE_TYPE *pxHigherPriorityTaskWoken
);
这是一个调用 xQueueGenericSendFromISR() 的宏。是为了兼容 FreeRTOS.org 以后的版本(没有包含xQueueSendToBackFromISR() 和 xQueueSendToFrontFromISR() 宏)
传递一个项到队列的后面。在终端服务程序中可以安全使用。
项在队列中是复制而不是引用,排列小项目更加灵活,特别是当从ISR调用时。在大多数情况下,使用一个指向项目的指针传进队列更加灵活
参数 | 含义 |
---|---|
xQueue | 将项目传进的队列 |
pvItemToQueue | 一个指向将在队列中放置的项目的指针。项目的大小,队列在创建时已经定义了, 将从pvItemToQueue复制许多字节到队列的存储区域 |
pxHigherPriorityTaskWoken | 如果传进队列而导致任务解锁,并且解锁的任务的优先级高于当前运行任务的优先级xQueueSendFromISR将设置 *pxHigherPriorityTaskWoken到 pdTRUE 。如果xQueueSendFromISR()设置这个值到 pdTRUE,在中断推出之前将请求任务切换。 |
返回值
成功:pdPASS
失败:errQUEUE_FULL
使用范例—>范例是缓冲IO(每次调用ISR能够更多的值)
Example usage for buffered IO (where the ISR can obtain more than one value per call):
void vBufferISR( void ){
portCHAR cIn;
portBASE_TYPE xHigherPriorityTaskWoken;
/* 没有在ISR中唤醒任务 */
xHigherPriorityTaskWoken = pdFALSE;
/* 循环直到缓冲器为空 */
do{
/*从缓冲器中获得一个字节. *
cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
/* 传递字节 */
xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
} while ( portINPUT_BYTE( BUFFER_COUNT ) );
/* 现在缓冲器为空,如果需要可以任务切换 */
if( xHigherPriorityTaskWoken ){
/* 实际宏使用了特殊接口 */
taskYIELD_FROM_ISR ();
}
}
xQueueReceiveFromISR() 从队列接收一个项目
queue.h
portBASE_TYPE xQueueReceiveFromISR(
xQueueHandle pxQueue,
void *pvBuffer,
portBASE_TYPE *pxTaskWoken
);
从队列接收一个项目。在中断程序中使用此函数是安全的。
参数 | 含义 |
---|---|
pxQueue | 发送项目的队列句柄 |
pvBuffer | 指向缓冲区的指针,将接收的项目被复制进去。 |
pxTaskWoken | 任务将锁住,等待队列中的可用空间。如果xQueueReceiveFromISR 引起一个任务解锁,pxTaskWoken 将设置为pdTRUE,否则pxTaskWoken保留不变 |
返回值
成功:pdTRUE
失败:epdFALSE
使用范例
xQueueHandle xQueue;
// Function to create a queue and post some values.
void vAFunction( void *pvParameters ){
portCHAR cValueToPost;
const portTickType xBlockTime = ( portTickType )0xff;
// Create a queue capable of containing 10 characters.
xQueue = xQueueCreate( 10, sizeof( portCHAR ) );
if( xQueue == 0 ){
// Failed to create the queue.
}
// ...
// Post some characters that will be used within an ISR. If the queue
// is full then this task will block for xBlockTime ticks.
cValueToPost = 'a';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
cValueToPost = 'b';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
// ... keep posting characters ... this task may block when the queue
// becomes full.
cValueToPost = 'c';
xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
}
// ISR that outputs all the characters received on the queue.
void vISR_Routine( void ){
portBASE_TYPE xTaskWokenByReceive = pdFALSE;
portCHAR cRxedChar;
while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) ){
// A character was received. Output the character now.
vOutputCharacter( cRxedChar );
// If removing the character from the queue woke the task that was
// posting onto the queue xTaskWokenByReceive will have been set to
// pdTRUE. No matter how many times this loop iterates only one
// task will be woken.
}
if( xTaskWokenByPost != pdFALSE ){
// We should switch context so the ISR returns to a different task.
// NOTE: How this is done depends on the port you are using. Check
// the documentation and examples for your port.
taskYIELD();
}
}
vSemaphoreCreateBinary 创建信号量
semphr. h
vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
使用已存在的队列结构来创建信号量的宏。这是二元信号量,故队列长度为1。因为实际上我们并不需要储存数据,我们只是想知道队列为空或者满,因此数据长度为0.
二元信号量与互斥锁十分相像,不过两者间有细微的差别:互斥锁包含一个优先级继承机制,而信号量没有。这种差别使得二元信号量更适合于实现同步(任务之间或任务与中断之间),互斥锁更适合于实现简单的互斥。
二元信号量并不需要在得到后立即释放,因此任务同步可以通过一个任务/中断持续释放信号量而另外一个持续获得信号量来实现。xSemaphoreGiveFromISR()页中有此种方式的实现范例。
当有另外一个具有更高优先级的任务试图获取同一个互斥锁时,已经获得互斥锁的任务的优先级会被提升。已经获得互斥锁的任务将继承试图获取同一互斥锁的任务的优先级。这意味着互斥锁必须总是要返还的,否则高优先级的任务将永远也不能获取互斥锁,而低优先级的任务将不会放弃优先级的继承。 xSemaphoreTake()页中提供了互斥锁用于互斥的范例。
互斥锁与二元信号量均赋值为xSemaphoreHandle类型,并且可以在任何此类型参数的API函数中使用。
参数 | 含义 |
---|---|
xSemaphore | 已创建的信号量句柄,需要为xSemaphoreHandle类型. |
返回值
成功:
失败:NULL
使用范例
xSemaphoreHandle xSemaphore;
void vATask( void * pvParameters ){
// 在vSemaphoreCreateBinary ()被调用前信号量不能被使用。
// 此宏直接传入变量。
vSemaphoreCreateBinary( xSemaphore );
if( xSemaphore != NULL ){
// 信号量已成功创建
// 现在信号量可以被使用
}
}
xSemaphoreTake 获取信号量
semphr. h
xSemaphoreTake(
xSemaphoreHandle xSemaphore,
portTickType xBlockTime
)
用于获取信号量的宏。信号量必须已经通过调用vSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或 xSemaphoreCreateCounting()来创建。
这个宏不能在服务中断程序中调用。如果有必要,可以调用xQueueReceiveFromISR() 来在中断服务程序中获取信号量,虽然这并不是一种正规的操作。
xSemaphoreTake() 是一个全功能任务间通讯API,
xSemaphoreAltTake() 是其等价的替代API 。这两个版本均需要同样的参数并返回同样的值。
参数 | 含义 |
---|---|
xSemaphore | 将被获得的信号量句柄,此信号量必须已经被创建 |
xBlockTime | 等待信号量可用的时钟滴答次数,可以使用portTICK_RATE_MS宏来转换为实际的时间。当为0时可以用于对信号量进行轮询(poll the semaphore)如果INCLUDE_vTaskSuspend置位“1”,则指定xBlockTime为portMAX_DELAY会导致任务阻塞时间不确定(不会超时) |
返回值
成功:pdTRUE 成功获取信号量
失败:pdFALSE 如果xBlockTime超时而信号量还未可用
使用范例
xSemaphoreHandle xSemaphore = NULL;
// A task that creates a semaphore.
void vATask( void * pvParameters )
{
// 创建一个信号量用于标识一个共享资源。
// 因为我们使用信号量来互斥,因此创建互斥锁信号量而不是二元信号量
xSemaphore = xSemaphoreCreateMutex();
}
//使用信号量的任务
void vAnotherTask( void * pvParameters )
{
// ... 完成其他事情
xSemaphoreGive 释放信号量
semphr. h
xSemaphoreGive( xSemaphoreHandle xSemaphore )
用于释放信号量的宏。这个信号量必须已经通过调用 vSemaphoreCreateBinary(), xSemaphoreCreateMutex() 或
xSemaphoreCreateCounting() 创建,并且使用 sSemaphoreTake() 获取。
这个宏不能在中断服务程序中使用。参考 xSemaphoreGiveFromISR() 获取一个在中断中使用的替代。
这个宏也不能应用于由 xSemaphoreCreateRecursiveMutex() 创建的信号量。
xSemaphoreGive() 是一个全功能任务间通讯API。xSemaphoreAltGive() 是其等价的替代API。这两个版本均需要相同的参数,并返回相同的值。
参数 | 含义 |
---|---|
xSemaphore | 即将释放的信号量的句柄,在信号量创建是返回。 |
返回值
成功:pdTRUE
失败:pdFALSE 信号量使用的是队列,因此如果队列没有位置用于发送消息就会发生一个错误——说明开始时没有正确获取信号量。
使用范例
xSemaphoreHandle xSemaphore = NULL;
void vATask( void * pvParameters ){
// 创建一个信号量用于保护共享资源。
// 因为我们使用信号量来互斥,故创建一个互斥锁信号量
// 而不是二元信号量
xSemaphore = xSemaphoreCreateMutex();
if( xSemaphore != NULL ){
if( xSemaphoreGive( xSemaphore ) != pdTRUE ){
// 可以预料这里的调用会失败,因为信号量还没有‘获取’
}
// 获取信号量——如果信号量未可用也不阻塞
if( xSemaphoreTake( xSemaphore, ( portTickType ) 0 ) ){
// 现在已经获取信号量并且可以访问共享资源
// ...
// 访问完共享资源,可以释放信号量
if( xSemaphoreGive( xSemaphore ) != pdTRUE ){
// 我们并不认为这个调用会失败,
// 因为我们必须获取信号量才可以运行到这里
}
}
}
}
xSemaphoreGiveFromISR 释放一个信号量
semphr. h
xSemaphoreGiveFromISR(
xSemaphoreHandle xSemaphore,
portBASE_TYPE *pxHigherPriorityTaskWoken
)
用于释放一个信号量的宏。释放的信号量必须已经通过vSemaphoreCreateBinary() 或 xSemaphoreCreateCounting()函数创建。
互斥信号量(通过xSemaphoreCreateMutex()函数创建)不能使用此宏。
此宏可以在中断服务程序中使用。
参数 | 含义 |
---|---|
xSemaphore | 将被释放的信号量的句柄,此句柄在信号量创建时返回。 |
pxHigherPriorityTaskWoken | 如果释放此信号量会导致一个比当前任务具有更高优先级的任务解除阻塞,xSemaphoreGiveFromISR() 函数将设置*pxHigherPriorityTaskWoken为pdTRUE。如果xSemaphoreGiveFromISR() 函数将其置为pdTRUE,则必须在离开中断前进行上下文切换。 |
返回值
成功:pdTRUE 信号量成功释放
失败:errQUEUE_FULL
使用范例
#define LONG_TIME 0xffff
#define TICKS_TO_WAIT 10
xSemaphoreHandle xSemaphore = NULL;
/* 反复运行的任务 */
void vATask( void * pvParameters )
{
/* 我们使用信号量用于同步,所以创建一个二元信号量而不是互斥信号量,必须保证中断程序不会在信号量被创建前使用它*/
vSemaphoreCreateBinary( xSemaphore );
for( ;; )
{
/* 我们希望每10个时钟滴答运行此任务一次,信号量已在任务运行前创建。
阻塞等待信号量可用。*/
if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
{
/* 可以运行 */
...
/*任务完成,返回循环的顶部重新阻塞等待。注意:如果通过此方式来使用信号量与中断服务程序同步,在这里不需要释放信号量*/
}
}
}
/* 定时器中断服务程序 */
void vTimerISR( void * pvParameters )
{
static unsigned portCHAR ucLocalTickCount = 0;
static portBASE_TYPE xHigherPriorityTaskWoken;
/* 一个时钟滴答到来 */
... 运行其他时间相关的函数。
/* 已经到了vATASK()运行的时间? */
xHigherPriorityTaskWoken = pdFALSE;
ucLocalTickCount++;
if( ucLocalTickCount >= TICKS_TO_WAIT )
{
/* 通过释放信号量来使任务解除阻塞*/
xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
/* 复位计数,10个时钟滴答后再次释放信号量 */
ucLocalTickCount = 0;
}
/* 如果xHigherPriorityTaskWoken被置为true我们必须退让。这里使用的宏是由硬件平台特定的*/
portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
xTaskCreate() 任务创建
task. h
portBASE_TYPE xTaskCreate(
pdTASK_CODE pvTaskCode,
const portCHAR * const pcName,
unsigned portSHORT usStackDepth,
void *pvParameters,
unsigned portBASE_TYPE uxPriority,
xTaskHandle *pvCreatedTask
);
创建新的任务并添加到任务队列中,准备运行
参数 | 含义 |
---|---|
pvTaskCode | 指向任务的入口函数. 任务必须执行并且永不返回 (即:无限循环). |
pcName | 描述任务的名字。主要便于调试。最大长度由configMAX_TASK_NAME_LEN.定义 |
usStackDepth | 指定任务堆栈的大小 ,堆栈能保护变量的数目- 不是字节数. 例如,如果堆栈为16位宽度,usStackDepth定义为 100, 200 字节,这些将分配给堆栈。堆栈嵌套深度(堆栈宽度)不能超多最大值——包含了size_t类型的变量 |
pvParameters | 指针用于作为一个参数传向创建的任务 |
uxPriority | 任务运行时的优先级 |
pvCreatedTask | 用于传递一个处理——引用创建的任务 |
返回值
成功:pdTRUE 如果任务成功创建并且添加到就绪列中
失败:错误代码在projdefs. H文件定义
使用范例
// 创建任务
void vTaskCode( void * pvParameters ){
for( ;; ){
// 任务代码
}
}
// 函数来创建一个任务
void vOtherFunction( void ){
static unsigned char ucParameterToPass;
xTaskHandle xHandle;
// 创建任务,存储处理。注意传递的参数为ucParameterToPass
//它在任务中不能始终存在, 所以定义为静态变量. 如果它是动态堆栈的变量,可能存在
// 没有那么长,或者至少随着时间毁灭,
// 新的时间, 尝试存储它
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
// 使用句柄来删除任务
vTaskDelete( xHandle );
}
注
如果使用FreeRTOS-MPU,建议使用 xTaskCreateRestricted()来替换xTaskCreate()。 在FreeRTOS-MPU 下使用xTaskCreate(),允许任务被创建于运行在特权模式或者用户模式。(见uxPriority 的描述)。特权模式下,可以利用任务访问整个存储器空间,用户模式下,任务只能访问自己的堆栈。在两种情况下,MPU 不会自动捕获堆栈溢出,虽然标准的FRTOS堆栈溢出检测方案依然可以使用。xTaskCreateRestricted() 允许了更大的灵活性。 【具体参数】 pvTaskCode :指向任务函数。任务必须以永不返回的形式实现(比如一个死循环)。笔者注释:这个参数即任务函数的函数名,见下文描述。 pcName : 任务函数的别名,仅仅是方便调试所用。最大的长度由configMAX_TASK_NAME_LEN定义。 usStackDepth :任务堆栈的深度,定义了堆栈可以包含的变量数——不是字节数。比如如果堆栈的宽度为16BIT,而 usStackDepth定义为100,则200 字节被分配给堆栈存储。堆栈深度乘以堆栈宽度的最大值,不能超过 size_t变量能包含的最大值。(笔者注释:堆栈到底应该定义多大,大体要考虑到:
最大级别中断嵌套需要的堆栈;
由于RTOS 生成每个任务,该任务天生需要的堆栈,FRTOS至少是16个字节,随着翻译深入会确认这个问题;
在每个任务函数中定义局部变量而产生的任务函数的变量堆栈。3者相加即为一个合适的堆栈。)
pvParameters :作为参数的指针,当任务创建时。 uxPriority :任务的优先级。包括MPU支持的系统可以选择在特权模式(系统模式)创建任务,通过设置优先级参数的portPRIVILEGE_BIT位。比如,创建一个特权任务在优先级2,则uxPriority 应当设置为( 2 | portPRIVILEGE_BIT )。 pvCreatedTask :回传一个句柄,以便创建的任务可以被关联。返回值:pdPASS,如果任务成功创建且添加到就绪列表,否则返回一个错误代码,见 projdefs.h。 范例:
vTaskDelete() 任务删除
task. h
void vTaskDelete( xTaskHandle pxTask );
INCLUDE_vTaskDelete必须定义为1,这个函数才能可用。查看配置部分获得更多信息。
从RTOS实时内核管理中移除任务。要删除的任务将从就绪,封锁,挂起,事件列表中移除,
注意:空闲任务负责释放内核分配给已删除任务的内存。因此,如果应用程序调用了vTaskDelete (),微控制器执行时间,空闲任务不假死是很重要的。内存分配给任务的代码不会自动释放,应该在任务删除之前。
参考演示程序death. c 中的例子使用 vTaskDelete ().
参数 | 含义 |
---|---|
pxTask | 处理要删除的任务。传递NULL将引起调用任务删除 |
返回值
成功:pdTRUE 如果任务成功创建并且添加到就绪列中
失败:错误代码在projdefs. H文件定义
使用范例
void vOtherFunction( void ){
xTaskHandle xHandle;
// 创建任务,存储处理
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// 使用处理来删除任务.
vTaskDelete( xHandle );
}
vTaskDelay() 任务相对延时
task. h
void vTaskDelay( portTickType xTicksToDelay );
INCLUDE_vTaskDelay必须设置为1,这个函数才为可用。参考配置获得更多信息。
延时任务为已知时间片。任务被锁住剩余的实际时间由时间片率决定。portTICK_RATE_MS常量用来用来从时间片速率(一片周期代表着分辨率)来计算实际时间。
vTaskDelay()指定一个任务希望的时间段,这个时间之后(调用vTaskDelay() )任务解锁。例如,指定周期段为100时间片,将使任务在调用vTaskDelay()100个时间片之后解锁。vTaskDelay()不提供一个控制周期性任务频率的好方法,像通过代码采取的路径,和其他任务和中断一样,在调用vTaskDelay()后 影响频率,因此任务所需的时间下一次执行。
参考
vTaskDelayUntil() ,这个交替的API函数设计了执行固定的频率。它是指定的一个绝对时间(而不是一个相对时间)后,调用任务解锁。
参数 | 含义 |
---|---|
xTicksToDelay | 时间数量,调用任务应该锁住的时间片周期 |
返回值
无
使用范例
void vTaskFunction( void * pvParameters ) {
/* 挂起500ms. */
const portTickType xDelay = 500 / portTICK_RATE_MS;
for( ;; ) {
/* 简单的每 500ms触发LED, .在每两次触发间挂起*/
vToggleLED();
vTaskDelay( xDelay );
}
}
vTaskDelayUntil() 任务绝对延时
task. h
void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement );
INCLUDE_vTaskDelayUntil 必须定义为 1以使能此函数。见configuration 章节获得更多信息。 延迟任务直到一个指定的时间结束。该函数用在周期性任务以保证任务执行频率的稳定。 与上vTaskDelay 不同的是该任务指定绝对时间,vTaskDelay指定相对时间。 (笔者注释:原文接下来两小段反复解释为什么一个是相对时间一个是绝对时间,翻译 省略,后续笔者会描述。) 必须注意,vTaskDelayUntil() 会立即返回(没有阻塞)如果它用来指定一个已经过去 的唤醒时间。所以任务用vTaskDelayUntil() 来执行周期必须重新计算它需要的唤醒时间, 如果该周期执行由于任何导致任务丢失 1个或者多个周期的理由被打断(比如该任务被挂 起) 这可以由检查pxPreviousWake 传递的变量与当前的时间封计数器来做比较检测到。在 大多数情况这不是必须的的。 常量portTICK_RATE_MS 可以用来计算时间封的实际时间间隔。 当调度器由于应用调用vTaskSuspendAll()导致挂起(调度器暂停),不能调用此函数。 具体参数 pxPreviousWakeTime :上一次唤醒的时间计数器。 xTimeIncrement :间隔周期计数器。 (笔者注释:如果有写过以定时器方式查询按键,去抖动等程序,或者看过 PIC591键盘程序的用户,对这两个参数的作用应当很容易从直观上理解。对于所有的 RTOS,都有一个时间封(心跳时间),需要靠经过设置后能提供周期性中断功能的定时器来实现,而当 RTOS发动时,将有一个计数器来计数这是第几次定时中断,这个计数器一般只会自然溢出,即达到最大值后归0。读这个计数器的值,就等于了解了从FRTOS开始运行,到当下,经历了多少时间间隔(不考虑归0),而所谓的绝对延时,就是只关心上一次的计数器时间 pxPreviousWakeTime,以及经历多长时间 xTimeIncrement后完成定时。至于计数器溢出,不会影响什么,只要这两个参数与系统的计数器的数据类型一致,又都采用自然溢出方式计算就可以。)
参数 | 含义 |
---|---|
pxPreviousWakeTime | |
xTimeIncrement |
返回值
无
使用范例
// Perform an action every 10 ticks.
void vTaskFunction( void * pvParameters )
{
portTickType xLastWakeTime;
const portTickType xFrequency = 10;
// Initialise the xLastWakeTime variable with the current time.
xLastWakeTime = xTaskGetTickCount();
for( ;; )
{
// Wait for the next cycle.
vTaskDelayUntil( &xLastWakeTime, xFrequency );
// Perform action here.
}
}
uxTaskPriorityGet() 获取某个任务的优先级
task. h
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
设置 INCLUDE_vTaskPriorityGet 为 1,此函数才能用。参考配置以获得更多信息。
获得任务的优先级。
参数 | 含义 |
---|---|
pxTask | 需要处理的任务. 当传递NULL时,将返回所调用任务的优先级 |
返回值
pxTask 的优先级
使用范例
void vAFunction( void ){
xTaskHandle xHandle;
// 创建任务,准备处理
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用这个处理来获得创建任务的优先级
// / 使用 tskIDLE_PRIORITY创建, 但是可能改
if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY ){
// 任务已经改变了其优先级
}
// ...
// / 是否高于创建任务的优先级?
if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) ){
// 此任务的优先级(使用NULL获得的)高
}
}
vTaskPrioritySet() 设置某个任务的优先级
task. h
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
设置 INCLUDE_vTaskPrioritySet 为 1,才能使用此函数。参考配置获得更多信息。
设置任务的优先级。
如果设置的优先级高于当前执行任务的优先级,则上下文切换将在此函数返回之前发生。
参数 | 含义 |
---|---|
pxTask | 需要设置优先级的任务。当传递NULL,将设置调用任务的优先级 |
uxNewPriority | 任务需要设置的优先级 |
返回值
无
使用范例
void vAFunction( void ) {
xTaskHandle xHandle;
// 创建任务,准备处理
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用其句柄来提高创建任务的优先级
vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
// ...
// 使用NULL处理,来提高优先级到同一个值
vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
}
vTaskSuspend() 挂起某个任务
task. h
void vTaskSuspend( xTaskHandle pxTaskToSuspend );
设置INCLUDE_vTaskSuspend 为1,此函数才能使用。参考配置获得更多信息。
挂起任务。当挂起一个任务时,不管优先级是多少,不需要占用任何微控制器处理器时间。
调用vTaskSuspend不会累积——即:在统一任务中调用vTaskSuspend两次,但只需调用一次vTaskResume () 来是挂起的任务就绪。
参数 | 含义 |
---|---|
pxTaskToSuspend | 处理需要挂起的任务。传递NULL将挂起调用此函数的任务。 |
返回值
无
使用范例
void vAFunction( void ){
xTaskHandle xHandle;
// 创建任务,保存句柄
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用此句柄来挂起创建的任务
vTaskSuspend( xHandle );
// ...
// 创建的任务不会在这期间运行,除非
// 其他任务调用 vTaskResume( xHandle )
//...
// 挂起自己
vTaskSuspend( NULL );
// 不能运行到这里,除非另一个任务调用vTaskResume
// 使用此任务的句柄为参数
}
vTaskResume() 恢复挂起的某个任务
task. h
void vTaskResume( xTaskHandle pxTaskToResume );
设置INCLUDE_vTaskSuspend为1,此函数才能使用。参考配置获得更多信息。
唤醒挂起的任务。
必须是调用 vTaskSuspend () 后挂起的任务,才有可能通过调用 vTaskResume ()重新运行。
参数 | 含义 |
---|---|
pxTaskToResume | 就绪的任务的句柄 |
返回值
无
使用范例
void vAFunction( void )
{
xTaskHandle xHandle;
// 创建任务,保存句柄
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
// ...
// 使用此句柄来挂起创建的任务
vTaskSuspend( xHandle );
// ...
// 创建的任务不会在此期间运行,除
// 另外一个任务调用 vTaskResume( xHandle )
//...
// 唤醒自己
vTaskResume( xHandle );
// 创建的任务将按照它在系统中的优先级
// 再次获得微处理器的处理时间
}
vTaskStartScheduler() 启动具有优先级的实时内核
task. h
void vTaskStartScheduler( void );
启动实时内核处理。在调用之后,内核已经控制执行的任务。
当 vTaskStartScheduler() 被调用时,空闲任务自动创建。
如果 vTaskStartScheduler() 成功调用,这个函数不返回,直到执行任务调用vTaskEndScheduler()。如果可供给空闲任务的RAM不足,那么函数调用失败,并立即返回。
参数 | 含义 |
---|---|
无 | 无 |
返回值
无
使用范例----演示程序main.c,创建任务和启动内核
void vAFunction( void ){
// 在启动内核前至少创建一个任务
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
// 启动具有优先级的实时内核
vTaskStartScheduler();
// 除非调用 vTaskEndScheduler () ,否则不会执行到这。
}
vTaskEndScheduler() 停止实时内核运行
task. h
void vTaskEndScheduler( void );
停止实时内核运行。所有创建的任务将自动删除,并且多任务(优先级或合作式)将停止。
当vTaskStartScheduler()调用时,执行将再次开始,像vTaskStartScheduler()仅仅返回。
在演示或PC路径的例子中,参考演示应用程序文件main.c,例子中使用vTaskEndScheduler ()。
vTaskEndScheduler ()需要在便携层定义出口函数(参考在port.c提供PC接口的vPortEndScheduler () )。执行硬件指定操作如停止内核执行。
vPortEndScheduler () 导致所有由内核分配的资源释放——但是不会释放由应用程序的任务分配的资源。
参数 | 含义 |
---|---|
无 | 无 |
返回值
无
使用范例----演示程序main.c,创建任务和启动内核
void vTaskCode( void * pvParameters ){
for( ;; ){
// 任务代码
// 有时希望结束实时内核执行
// 因此调用 ...
vTaskEndScheduler ();
}
}
void vAFunction( void ){
// 至少创建一个任务启动内核执行
xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
// 启动具有优先级的实时内核
vTaskStartScheduler();
// 当 vTaskCode () 任务调用 将仅执行到这
vTaskEndScheduler ();//当执行到这,将返回到当任务执行
}
vTaskSuspendAll() 挂起所有活动的实时内核
task. h
void vTaskSuspendAll( void );
挂起所有活动的实时内核,同时允许中断(包括内核滴答)。
任务在调用vTaskSuspendAll ()后,这个任务将继续执行,不会有任何被切换的危险,直到调用xTaskResumeAll ()函数。
API中有可能影响影响上下文切换的函数(例如,vTaskDelayUntil(), xQueueSend()等等),一定不能再调度器挂起时被调用。
参数 | 含义 |
---|---|
无 | 无 |
返回值
无
使用范例----演示程序main.c,创建任务和启动内核
void vTask1( void * pvParameters )
{
for( ;; )
{
// 任务执行到这里
// ...
// 有时 任务想执行长时间的操作,这期间,不想被切换
// 不能使用taskENTER_CRITICAL ()/taskEXIT_CRITI
// 因为操作的执行时间的长度,可能将错过中断——包括系统滴答
//
// 组织实时内核切换任务
vTaskSuspendAll ();
// 这里执行一些操作。不需要使用循环
// 因为,已经占有了整个微处理器的执行时间
// 在这期间,中断仍将起作用
// 同时,内核滴答计数仍将维持
// ...
// 操作完成。重启内核。
xTaskResumeAll ();
}
}
xTaskResumeAll() 重启实时内核
task. h
portBASE_TYPE xTaskResumeAll( void );
通过调vTaskSuspendAll ().,用重启实时内核。在调用xTaskSuspendAll ()后,内核将一直掌管被执行的任务。
参数 | 含义 |
---|---|
无 | 无 |
返回值
成功:pdTRUE 重启调度器时,发生上下文切换
失败:pdFALSE
使用范例----演示程序main.c,创建任务和启动内核
void vTask1( void * pvParameters )
{
for( ;; )
{
// 任务执行到这里
// ...
// 有时 任务想执行长时间的操作,这期间,不想被切换
// 不能使用
// taskENTER_CRITICAL ()/taskEXIT_CRITICAL ()
// 因为操作的执行时间的长度,可能将错过中断——包括系统滴答
// 组织实时内核切换任务
xTaskSuspendAll ();
// 这里执行一些操作。不需要使用循环
// 因为,已经占有了整个微处理器的执行时间
// 在这期间,中断仍将起作用
// 同时,内核滴答计数仍将维持
// ...
// 操作完成。重启内核
// 想强迫上下文切换--但是如果重启的调度器已经引起上下文切换,就没有机会。
if( !xTaskResumeAll () )
{
taskYIELD ();
}
}
}