FreeRTOS库函数 API Reference(三)内核控制

目录

内核控制(Kernel Control)

1.taskYIELD() 任务切换

2.taskDISABLE_INTERRUPTS() 关闭中断

3.taskENABLE_INTERRUPTS() 打开中断

4.taskENTER_CRITICAL()进入临界区 taskEXIT_CRITICAL()退出临界区

5.taskENTER_CRITICAL_FROM_ISR()进入临界区 和 taskEXIT_CRITICAL_FROM_ISR()退出临界区

6.vTaskStartScheduler(启动RTOS任务调度)

7.vTaskEndScheduler(停止RTOS内核滴答)(只在x86 Real Mode PC端口上实现)

8.vTaskSuspendAll(挂起任务调度器)

9.xTaskResumeAll(恢复暂停后的任务调度器)

10.vTaskStepTick(设置系统节拍值)


英文原文:

FreeRTOS - Task Control Functions and Macros for the Free Open Source RTOS FreeRTOShttps://www.freertos.org/a00020.html

内核控制(Kernel Control)

1.taskYIELD() 任务切换

taskYIELD();

taskYIELD()用于请求切换到另一个任务的上下文。但是,如果没有其他任务比调用taskYIELD()的任务具有更高或相同的优先级,那么RTOS调度器将简单地选择调用taskYIELD()的任务再次运行。

如果configUSE_PREEMPTION设置为1,那么RTOS调度器将总是运行能够运行的最高优先级的任务,所以调用taskYIELD()将永远不会导致切换到更高优先级的任务。

2.taskDISABLE_INTERRUPTS() 关闭中断

taskDISABLE_INTERRUPTS();

如果正在使用的端口支持configMAX_SYSCALL_INTERRUPT_PRIORITY(或configMAX_API_CALL_INTERRUPT_PRIORITY)常数,那么taskDISABLE_INTERRUPTS将禁用所有中断,或者掩码(禁用)中断直到configMAX_SYSCALL_INTERRUPT_PRIORITY设置为止。检查正在使用的端口的taskDISABLE_INTERRUPTS的实现。

如果正在使用的端口不支持configMAX_SYSCALL_INTERRUPT_PRIORITY常数,那么taskDISABLE_INTERRUPTS()将全局禁用所有可屏蔽中断。

通常这个宏不会被直接调用,taskENTER_CRITICAL()和taskEXIT_CRITICAL()应该在它的位置使用。

3.taskENABLE_INTERRUPTS() 打开中断

taskENABLE_INTERRUPTS();

宏启用微控制器中断。通常这个宏不会被直接调用,taskENTER_CRITICAL()和taskEXIT_CRITICAL()应该在它的位置使用。

4.taskENTER_CRITICAL()进入临界区 taskEXIT_CRITICAL()退出临界区

void taskENTER_CRITICAL( void );
void taskEXIT_CRITICAL( void );

通过调用taskENTER_CRITICAL()进入临界区,然后通过调用taskEXIT_CRITICAL()退出临界区。

宏 taskENTER_CRITICAL() 和宏 taskEXIT_CRITICAL() 提供了一个基本的关键段实现,它通过简单地禁用中断来工作,可以是全局的,也可以是特定的中断优先级。参见vTaskSuspendAll() RTOS API函数获取在不禁用中断的情况下创建临界区的信息。

如果正在使用的FreeRTOS端口没有使用configMAX_SYSCALL_INTERRUPT_PRIORITY内核配置常量(也称为configMAX_API_CALL_INTERRUPT_PRIORITY),那么调用taskENTER_CRITICAL()将使中断全局禁用。

如果正在使用的FreeRTOS端口使用了configMAX_SYSCALL_INTERRUPT_PRIORITY内核配置常量,那么调用taskENTER_CRITICAL()将使中断处于或低于中断优先级,中断优先级由禁用configMAX_SYSCALL_INTERRUPT_PRIORITY设置,所有高优先级中断启用。

抢占式上下文切换只发生在中断内部,所以当中断被禁用时不会发生。因此,调用taskENTER_CRITICAL()的任务保证在临界区退出之前一直保持在Running状态,除非任务显式地试图阻塞或放弃(它不应该在临界区内部这样做)。

对taskENTER_CRITICAL()和taskEXIT_CRITICAL()的调用被设计成嵌套。因此,只有在对taskENTER_CRITICAL()的每次调用都执行一次对taskEXIT_CRITICAL()的调用时,一个临界区才会被退出。

临界段必须保持非常短,否则它们将对中断响应时间产生不利影响。每个对taskENTER_CRITICAL()的调用必须与对taskEXIT_CRITICAL()的调用紧密配对。

FreeRTOS API函数不能从临界区调用。

taskENTER_CRITICAL()和taskEXIT_CRITICAL()不能从中断服务例程(ISR)中调用——参见taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()中的中断安全等量。

使用示例:

/* 利用临界段的函数 */
void vDemoFunction( void )
{
    /* 进入临界区。在本例中,这个函数本身是在临界区中调用的,因此进入这个临界区将导致嵌套深度为2 */
    taskENTER_CRITICAL();

    /* 执行此处临界区所保护的操作 */

    /* 退出临界区。在这个例子中,这个函数本身是从一个临界区调用的,所以这个对taskEXIT_CRITICAL()的调用会将嵌套计数减少1,但不会导致中断被启用 */
    taskEXIT_CRITICAL();
}

/* 在临界区中调用vDemoFunction()的任务 */
void vTask1( void * pvParameters )
{
    for( ;; )
    {
        /* 在这里执行一些功能 */

        /* 调用taskENTER_CRITICAL()来创建一个临界区 */
        taskENTER_CRITICAL();


        /* 执行这里需要临界部分的代码。 */


        /* 对taskENTER_CRITICAL()的调用可以嵌套,因此调用包含自己对taskENTER_CRITICAL()和taskEXIT_CRITICAL()的调用的函数是安全的。 */
        vDemoFunction();

        /* 需要临界段的操作完成,因此退出临界段。在调用taskEXIT_CRITICAL()之后,嵌套深度将为零,因此中断将被重新启用。 */
        taskEXIT_CRITICAL();
    }
}

5.taskENTER_CRITICAL_FROM_ISR()进入临界区 和 taskEXIT_CRITICAL_FROM_ISR()退出临界区

UBaseType_t taskENTER_CRITICAL_FROM_ISR( void );
void taskEXIT_CRITICAL_FROM_ISR( UBaseType_t uxSavedInterruptStatus );

可用于中断服务例程(ISR)的taskENTER_CRITICAL()和taskEXIT_CRITICAL()的版本。

在ISR中,通过调用taskENTER_CRITICAL_FROM_ISR()进入临界区,然后通过调用taskEXIT_CRITICAL_FROM_ISR()退出临界区。

宏taskENTER_CRITICAL_FROM_ISR()和宏taskEXIT_CRITICAL_FROM_ISR提供了一个基本的关键段实现,它通过简单地禁用中断来工作,无论是全局的,还是特定的中断优先级级别。

如果正在使用的FreeRTOS端口支持中断嵌套,那么调用taskENTER_CRITICAL_FROM_ISR()将在由configMAX_SYSCALL_INTERRUPT_PRIORITY(或configMAX_API_CALL_INTERRUPT_PRIORITY)内核配置常量设置的中断优先级及以下禁用中断,并保持所有其他中断优先级启用。如果正在使用的FreeRTOS端口不支持中断嵌套,那么taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()将不起作用。

对taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()的调用被设计为嵌套,但是宏如何使用的语义与taskENTER_CRITICAL()和taskEXIT_CRITICAL()的等价函数不同。

临界段必须保持非常短,否则会对嵌套的高优先级中断的响应时间产生不利影响。每个对taskENTER_CRITICAL_FROM_ISR()的调用必须与对taskEXIT_CRITICAL_FROM_ISR()的调用紧密配对。

FreeRTOS API函数不能从临界区调用。

参数:

uxSavedInterruptStatus

taskEXIT_CRITICAL_FROM_ISR()使用uxSavedInterruptStatus作为唯一参数。用作uxSavedInterruptStatus参数的值必须是匹配调用taskENTER_CRITICAL_FROM_ISR()返回的值。

taskENTER_CRITICAL_FROM_ISR()不带任何参数。

返回值:

taskENTER_CRITICAL_FROM_ISR()返回宏被调用前的中断掩码状态。taskENTER_CRITICAL_FROM_ISR()返回的值必须作为匹配调用taskEXIT_CRITICAL_FROM_ISR()的uxSavedInterruptStatus参数。

taskEXIT_CRITICAL_FROM_ISR()不返回值。

使用示例:

/* 从ISR调用的函数。 */
void vDemoFunction( void )
{
    UBaseType_t uxSavedInterruptStatus;

    /* 进入临界区。
       在本例中,这个函数本身是在临界区中调用的,因此进入这个临界区将导致嵌套深度为2。
       将taskENTER_CRITICAL_FROM_ISR()返回的值保存到本地堆栈变量中,
       这样它就可以传递给taskEXIT_CRITICAL_FROM_ISR()。 */
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();

    /* 执行此处临界区所保护的操作。 */

    /* 退出临界区。
       在这个例子中,这个函数本身是在临界区被调用的,所以在uxSavedInterruptStatus中
       存储一个值之前中断就已经被禁用了,因此将uxSavedInterruptStatus传递给        
       taskEXIT_CRITICAL_FROM_ISR()不会导致中断被重新启用。 */
    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}

/* 在中断服务例程中调用vDemoFunction()的任务。 */
void vDemoISR( void )
{
    UBaseType_t uxSavedInterruptStatus;

    /* 调用taskENTER_CRITICAL_FROM_ISR()创建一个临界区,将返回值保存到本地堆栈变量中。 */
    uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();


    /* 执行这里需要临界部分的代码。 */


    /* 对taskENTER_CRITICAL_FROM_ISR()的调用可以嵌套,因此调用包含对
       taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()
       的自己的调用的函数是安全的。 */
    vDemoFunction();

    /* 需要临界段的操作完成,因此退出临界段。假设在进入该ISR时启用了中断,
       保存在uxSavedInterruptStatus中的值将导致中断被重新启用。*/
    taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}

6.vTaskStartScheduler(启动RTOS任务调度)

void vTaskStartScheduler( void );

启动RTOS调度程序。调用RTOS之后,内核可以控制执行哪些任务以及何时执行。

空闲任务和计时器守护任务(可选)在RTOS调度器启动时自动创建。

vTaskStartScheduler()只会在没有足够的RTOS堆可用来创建空闲或计时器守护进程任务时返回。

所有的RTOS演示应用程序项目都包含使用vTaskStartScheduler()的例子,通常在main.c中的main()函数中。

使用示例:

 void vAFunction( void )
 {
     // 任务可以在启动RTOS调度器之前或之后创建
     xTaskCreate( vTaskCode,
                  "NAME",
                  STACK_SIZE,
                  NULL,
                  tskIDLE_PRIORITY,
                  NULL );

     // 启动实时调度程序。
     vTaskStartScheduler();

     // 除非内存不足,否则不会到达这里。
 }

7.vTaskEndScheduler(停止RTOS内核滴答)(只在x86 Real Mode PC端口上实现

void vTaskEndScheduler( void );

注意:这只在x86 Real Mode PC端口上实现。

停止RTOS内核滴答。所有创建的任务将被自动删除和多任务(抢占或合作)将停止。然后从调用vTaskStartScheduler()的地方继续执行,就像vTaskStartScheduler()刚刚返回一样。

请参阅演示应用程序文件主。例如,使用vTaskEndScheduler()。

vTaskEndScheduler()要求在可移植层中定义一个出口函数(参见port中的vPortEndScheduler())。c表示PC端口)。这将执行特定于硬件的操作,例如停止RTOS内核滴答。

vTaskEndScheduler()将导致RTOS内核分配的所有资源被释放——但不会释放应用程序任务分配的资源。

使用示例:

 void vTaskCode( void * pvParameters )
 {
     for( ;; )
     {
         // 任务代码

         // 在某些时候,我们希望结束实时内核处理
         // 所以调用 ...
         vTaskEndScheduler ();
     }
 }

 void vAFunction( void )
 {
     // 在启动RTOS内核之前创建至少一个任务。
     xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );

     // 使用抢占启动实时内核。
     vTaskStartScheduler();

     // 只有当vTaskCode()任务调用vTaskEndScheduler()时才会到达这里。当我们回到这里时,我们又回到了单任务执行。
 }

8.vTaskSuspendAll(挂起任务调度器)

void vTaskSuspendAll( void );

挂起任务调度器。挂起调度器可以防止发生上下文切换,但会启用中断。如果一个中断请求在调度器挂起时进行上下文切换,那么该请求将保持挂起,只有在调度器恢复(未挂起)时才执行。

在之前调用vTaskSuspendAll()之后,调用xTaskResumeAll()将调度器从暂停状态转换出来。

对vTaskSuspendAll()的调用可以嵌套。在调度器离开suspend状态并重新进入Active状态之前,对xTaskResumeAll()的调用必须与之前对vTaskSuspendAll()的调用相同的次数

xTaskResumeAll()只能从正在执行的任务中调用,因此在调度器处于初始化状态(在调度器启动之前)时不能调用。

当调度程序挂起时,其他FreeRTOS API函数不能被调用。

有可能导致上下文切换的API函数(例如vTaskDelayUntil(), xQueueSend()等)一定不能在调度程序挂起时调用。

使用示例:

/* 一个暂停然后恢复调度程序的函数。 */
void vDemoFunction( void )
{
    /* 这个函数暂停调度程序。当从vtas1调用它时,调度程序已经挂起,
       因此这个调用创建了一个嵌套深度为2的嵌套深度。*/
    vTaskSuspendAll();
        
    /* 在这里执行一个操作。 */
        
    /* 由于对vTaskSuspendAll()的调用是嵌套的,
       因此在这里恢复调度器不会导致调度器重新进入活动状态。 */
    xTaskResumeAll();
}


void vTask1( void * pvParameters )
{
    for( ;; )
    {
        /* 在这里执行一些操作。 */
            
        /* 在某些情况下,任务希望执行一个操作,但在此期间它不希望被交换出去,
           或者它希望访问从另一个任务(但不是从中断)访问的数据。它不能使用                    
           taskENTER_CRITICAL()/taskEXIT_CRITICAL(),因为操作的长度可能
           会导致中断被错过。 */
            

        /* 防止调度器执行上下文切换。 */
        vTaskSuspendAll();
            

        /* 请在此处执行操作。不需要使用临界段,因为任务拥有除中断服务例程使用的所有处理时间。*/           
            
            
        /* 对vTaskSuspendAll()的调用可以嵌套,因此调用一个(非API)函数是安全的,
           该函数也包含对vTaskSuspendAll()的调用。当调度器挂起时,不应该调用API函数。 */
        vDemoFunction();

            
        /* 操作完成。将调度程序设置回Active状态。 */
        if( xTaskResumeAll() == pdTRUE )
        {
            /* 在xTaskResumeAll()中发生了上下文切换。 */
        }
        else
        {
            /* 在xTaskResumeAll()中没有发生上下文切换。 */
        }
    }
}

9.xTaskResumeAll(恢复暂停后的任务调度器)

BaseType_t xTaskResumeAll( void );

通过调用vTaskSuspendAll()恢复调度器暂停后的调度器。

xTaskResumeAll()只恢复调度器。它不会取消之前通过调用vTaskSuspend()挂起的任务。

返回值:

如果恢复调度程序导致上下文切换,则返回pdTRUE,否则返回pdFALSE。

使用示例:

void vTask1( void * pvParameters )
 {
     for( ;; )
     {
         /* Task code goes here. */

         /* ... */

         /* 在某些情况下,任务希望执行一个长时间的操作,而在此期间它不希望被换出。
            它不能使用taskENTER_CRITICAL ()/taskEXIT_CRITICAL(),
            因为操作的长度可能会导致中断被错过——包括节拍。

            防止RTOS内核换出任务。 */
         vTaskSuspendAll();

         /* 请在此处执行操作。不需要使用临界区,因为我们拥有所有的微控制器处理时间。
            在此期间,中断仍将运行,实时RTOS内核计时计数将保持不变。 */

         /* ... */

         /* 操作完成。重启RTOS内核。我们希望强制进行上下文切换——但是如果恢复调度器
            已经导致了上下文切换,那就没有意义了。 */
         if( !xTaskResumeAll () )
         {
              taskYIELD ();
         }
     }
 }

10.vTaskStepTick(设置系统节拍值)

void vTaskStepTick( TickType_t xTicksToJump );

如果RTOS配置为使用无tick的空闲功能,那么tick中断将被停止,并且微控制器处于低功耗状态,每当空闲任务是唯一能够执行的任务时。在退出低功耗状态时,必须纠正滴答计数值,以反映停止时所经过的时间。

如果一个FreeRTOS端口包含一个默认的portSUPPRESS_TICKS_AND_SLEEP()实现,那么vTaskStepTick()将在内部使用,以确保维护正确的tick计数值。vTaskStepTick()是一个公共API函数,它允许重写默认的portSUPPRESS_TICKS_AND_SLEEP()实现,并在所使用的端口不提供默认值时提供portSUPPRESS_TICKS_AND_SLEEP()。

configUSE_TICKLESS_IDLE配置常量必须设置为1,以便vTaskStepTick()可用。

参数:

xTicksToJump  自滴答中断停止以来已经过的RTOS滴答数。对于正确的操作,参数必须小于或等于portSUPPRESS_TICKS_AND_SLEEP()参数。

使用示例:

该示例显示了对多个函数的调用。只有vTaskStepTick()是FreeRTOS API的一部分。其他功能特定于所用硬件上可用的时钟和节能模式,因此必须由应用程序编写人员提供。

/* 首先定义portSUPPRESS_TICKS_AND_SLEEP()。该参数是到下一次内核需要执行之前的时间,单位为滴答。 */
#define portSUPPRESS_TICKS_AND_SLEEP( xIdleTime ) vApplicationSleep( xIdleTime )

/* 定义portSUPPRESS_TICKS_AND_SLEEP()调用的函数。 */
void vApplicationSleep( TickType_t xExpectedIdleTime )
{
unsigned long ulLowPowerTimeBeforeSleep, ulLowPowerTimeAfterSleep;

    /* 从时间源读取当前时间,当微控制器处于低功耗状态时,该时间源将保持工作状态。 */
    ulLowPowerTimeBeforeSleep = ulGetExternalTime();

    /* 停止正在生成滴答中断的计时器。 */
    prvStopTickInterruptTimer();

    /* 配置一个中断,以便在内核下一次需要执行时将微控制器从低功耗状态中取出。
       当微控制器处于低功耗状态时,中断必须从一个保持运行的源产生。 */
    vSetWakeTimeInterrupt( xExpectedIdleTime );

    /* 进入低功耗状态 */
    prvSleep();

    /* 确定微控制器实际处于低功耗状态的时间,如果微控制器是由一个非vSetWakeTimeInterrupt()
       调用配置的中断而退出低功耗模式,那么这个时间将小于xExpectedIdleTime。注意,调度程序
       在调用portSUPPRESS_TICKS_AND_SLEEP()之前被挂起,并在portSUPPRESS_TICKS_AND_SLEEP()
       返回时恢复。因此,在此函数完成之前,不会执行其他任务。 */
    ulLowPowerTimeAfterSleep = ulGetExternalTime();

    /* 修正内核滴答计数,以反映微控制器在低功耗状态下所花费的时间。 */
    vTaskStepTick( ulLowPowerTimeAfterSleep - ulLowPowerTimeBeforeSleep );

    /* 重新启动正在生成滴答中断的计时器。 */
    prvStartTickInterruptTimer();
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

QxNL

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

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

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

打赏作者

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

抵扣说明:

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

余额充值