下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

内核
最新资讯
简化任何设备的身份验证云连接。
利用 CoAP 设计节能型云连接 IoT 解决方案。
11.0.0 版 FreeRTOS 内核简介:
FreeRTOS 路线图和代码贡献流程。
使用 FreeRTOS 实现 OPC-UA over TSN。


解决方案 #4 减少处理器开销


<<< | >>>

注意:自 FreeRTOS V4.0.0 推出以来,这些页面一直没有更新。 V4.0.0 引入了协程的概念, 可为本教程所介绍的设计提供另一种新颖解决方案。 更多信息,请参阅任务和协程文档 。

概要

解决方案 #2 演示了如何通过充分利用 RTOS 功能来生成干净的应用程序。 解决方案 #3 演示了 如何将其应用于 RAM 资源有限的嵌入式计算机。 解决方案 #4 进行了进一步的修改, 目的是减少 RTOS 处理开销。

通过配置内核进行协作调度,创建混合调度算法(既不是完全抢占式,也不是完全协作式), 然后从事件中断服务程序内执行上下文切换。

实现


解决方案 #4 函数任务和优先级

同样,关键设备控制功能通过高优先级任务实现, 但使用协作式调度器需要对其实现进行修改。 之前, 使用 vTaskDelayUntil() API 函数维护定时。 使用抢占式调度器时, 为控制任务分配最高优先级可确保在指定的时间开始执行。 而现在使用的是协作式调度器, 只有在应用程序源代码明确请求时才会发生任务切换, 因此保证的定时功能丢失。

解决方案 #4 使用外设定时器的中断,确保以控制任务所需的确切频率 请求上下文切换。 调度器确保每个请求的上下文切换 都会切换到可运行的最高优先级任务。

键盘扫描函数也在由定时器中断触发的任务中执行, 因此同样需要定期处理器时间。 可以很容易地评估这项任务的定时; 控制函数最坏的情况处理时间由错误情况给出, 如果没有来自网络传感器的数据,则控制函数将超时。 键盘扫描函数的 执行时间基本固定。 因此,我们可以确定,以这种方式链接其功能永远不会导致控制周期频率的抖动, 或者更糟糕的是, 不会导致错过控制周期。

RS232 任务将由 RS232 中断服务程序调度。

LED 功能的灵活定时要求意味着,可以将嵌入式 Web 服务器任务加入到 空闲任务钩子。 如果还不行的话,也可以移到高优先级任务。


操作理念

仅在显式请求上下文切换时,协作调度器才会执行上下文切换。 这很大程度上 降低了 RTOS 产生的处理器开销[除了空闲任务不能再将处理器置于节能模式之外?]。 空闲任务(包括嵌入式 Web 服务器功能)将在 没有任何不必要的内核中断的情况下执行。

来自 RS232 或定时器外设的中断将导致上下文切换, 并且仅在必要时进行切换。 这样, RS232 任务的优先级仍然高于空闲任务,并且自身仍可被设备控制任务抢占, 以维持优先级较高的系统功能。


调度器配置

调度器配置为协作式操作。 内核滴答仅用于维护实时滴答值。


评估

只创建两个应用程序任务,因此使用的 RAM 少于解决方案 #2。
RTOS 上下文切换开销降至最低,尽管空闲任务可能会使用更多的 CPU 周期, 因为这些任务无法再使用节能模式。
仅使用 RTOS 功能的子集。 这需要在应用程序源代码层面更多地考虑定时和执行环境, 但仍然可以显著简化设计 (与解决方案 #1 相比)。
依赖处理器外围设备, 不可移植。
仍需考虑解决方案 #1 中确定的模块之间的分析和相互依赖性问题, 尽管程度要低得多。
如果应用程序过大,设计可能无法扩展


结论

RTOS 内核的功能只需很少的开销,即使在处理器和内存限制无法实现完全抢占式解决方案的系统上, 也可以实现简化的设计。


示例

此示例是先前介绍的假设应用程序的部分实现。 使用 FreeRTOS API。


高优先级任务

高优先级任务由周期性中断服务程序“给定”的信号量触发:
void vTimerInterrupt( void )
{
    // 'Give' the semaphore.  This will wake the high priority task.
    xSemaphoreGiveFromISR( xTimingSemaphore );
    
    // The high priority task will now be able to execute but as
    // the cooperative scheduler is being used it will not start
    // to execute until we explicitly cause a context switch.
    taskYIELD();    
}
请注意,用于从 ISR 内强制上下文切换的语法因移植而异。 不要直接复制此示例,而是检查您使用的移植文档。

高优先级任务包含设备控制和键盘功能。 首先调用 PlantControlCycle(), 以确保定时一致。

void HighPriorityTaskTask( void *pvParameters )
{
    // Start by obtaining the semaphore.
    xSemaphoreTake( xSemaphore, DONT_BLOCK );  

    for( ;; )
    {
        // Another call to take the semaphore will now fail until
        // the timer interrupt has called xSemaphoreGiveFromISR().
        // We use a very long block time as the timing is controlled
        // by the frequency of the timer.
        if( xSemaphoreTake( xSemaphore, VERY_LONG_TIME ) == pdTRUE )
        {
            // We unblocked because the semaphore became available.
            // It must be time to execute the control algorithm.
            PlantControlCycle();
            
            // Followed by the keyscan.
            if( KeyPressed( &Key ) )
            {
                UpdateDisplay( Key );
            }
        }
        
        // Now we go back and block again until the next timer interrupt.
    }
}


RS232 任务

RS232 任务只在等待数据到达的队列上阻塞。 RS232 中断服务程序必须 将数据发布到队列中(使任务处于就绪态),然后强制执行上下文切换。 这项机制就如同 上面给出的定时器中断伪码。

因此, RS232 任务可以由以下伪代码表示:

void vRS232Task( void *pvParameters )
{
DataType Data;

    for( ;; )
    {
       if( cQueueReceive( xRS232Queue, &Data, MAX_DELAY ) )
        {
            ProcessRS232Data( Data );
        }        
    }
}


嵌入式 Web 服务器和 LED 功能

其他的系统功能放置在空闲任务钩子中。 这只是由空闲任务 每个循环调用的函数。
void IdleTaskHook( void )
{
static TickType_t LastFlashTime = 0;

    ProcessHTTPRequests();
    
    // Check the tick count value to see if it is time to flash the LED
    // again.
    if( ( xTaskGetTickCount() - LastFlashTime ) > FLASH_RATE )
    {
        UpdateLED();
        
        // Remember the time now so we know when the next flash is due.
        LastFlashTime = xTaskGetTickCount();
    } 
}




Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.