下载 FreeRTOS
 

出色的 RTOS & 嵌入式软件

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

解决方案 #2
完全抢占式系统


<<< | >>>

概要

这是一种传统的抢先式多任务处理解决方案。 该方案充分利用了 RTOS 服务,不考虑由此产生的内存和处理器开销,而是 将所需的功能简单地 划分为多个自主任务。


实现

为系统的每个部分创建一个单独的任务, 这些部分能够独立存在, 或者具有特定的定时要求。


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

任务将进入阻塞态,直到有事件指示需要处理。 事件可以是外部事件(例如 按下某个键) ,也可以内部事件(如定时器过期)。 这种事件驱动的方法意味着不会浪费 CPU 时间 来轮询尚未发生的事件。

根据任务的时间要求为任务分配优先级。 定时要求越严格, 优先级越高(并非所有优先级分配评估都如此简单)。


操作理念

能够执行的最高优先级任务(而非处于阻塞状态)是 RTOS 保证获得处理器时间的任务。 如果有更高优先级的任务可用,内核将立即挂起正在执行的任务。

这种调度自动执行,应用程序源代码内没有明确的知识、结构或命令。 但是, 应用程序设计者有责任确保为任务分配适当的优先级。

没有任务能够执行时,将执行空闲任务。 空闲任务可以选择将处理器置于节能模式。


调度器配置

调度器配置为抢占式操作。 内核滴答频率应设置为 提供所需时间粒度的最慢值。


评估

简单、分段、灵活、可维护的设计,几乎没有相关性。
根据任务的优先级,处理器利用率可在任务之间自动切换, 而无需在应用程序源代码中进行显式操作。
事件驱动结构确保不会浪费 CPU 时间轮询尚未发生的事件。 仅在 有任务需要完成时才执行。
如果空闲任务将处理器置于节能(休眠)模式中,则可以降低功耗, 但也可能会浪费,因为滴答中断有时会不必要地唤醒处理器。
内核功能将使用处理资源。 其程度将取决于所选内核的 滴答频率。
此解决方案需要大量任务务,每个任务都需要自己的堆栈, 而且很多任务需要一个队列来接收事件, 因此,该解决方案占用大量 RAM。
在相同优先级的任务之间频繁切换上下文将浪费处理器周期。


结论

如果 RAM 和处理能力足够,这不失为一种很好的解决方案。 需要仔细斟酌将应用程序划分为多个任务 以及分配给每个任务的优先级。


示例

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


设备控制任务

此任务用于实现所有控制功能, 具有严格的定时要求, 因此在系统中具有最高优先级:
#define CYCLE_RATE_MS       10
#define MAX_COMMS_DELAY     2

void PlantControlTask( void *pvParameters )
{
TickType_t xLastWakeTime;
DataType Data1, Data2;

    InitialiseTheQueue();

    // A
    xLastWakeTime = xTaskGetTickCount();

    // B
    for( ;; )
    {
        // C
        vTaskDelayUntil( &xLastWakeTime, CYCLE_RATE_MS );
        
        // Request data from the sensors.
        TransmitRequest();
        
        // D
        if( xQueueReceive( xFieldBusQueue, &Data1, MAX_COMMS_DELAY ) )
        {
            // E
            if( xQueueReceive( xFieldBusQueue, &Data2, MAX_COMMS_DELAY ) )
            {
                PerformControlAlgorithm();
                TransmitResults();                
            }
        } 
    }
    
    // Will never get here!
}
请参阅上述代码片段内的标签:
  1. xLastWakeTime 已初始化。 此变量与 vTaskDelayUntil() API 函数一起使用, 以控制控制函数的执行频率。

  2. 此函数作为自主任务执行,因此永远不能退出。

  3. vTaskDelayUntil() 指示内核,此任务 应在 xLastWakeTime 存储的时间之后正好 10 毫秒开始执行。 在达到该时间之前,控制任务处于阻塞态。 由于这是系统中优先级最高的任务, 因此可以保证在正确的时间再次开始执行。 该任务会抢占 正在运行的任何优先级较低的任务。

  4. 从联网传感器请求的数据与接收的数据之间存在有限时间。 中断服务程序将到达现场总线上的数据放置在 xFieldBusQueue 中, 因此控制任务可以在队列中处于阻塞状态,以等待有可用数据。 如前所述,因为该任务是系统中的最高优先级任务, 所以可保证在数据可用时立即继续执行。

  5. 与“D”一样,等待来自第二个传感器的数据。
XQueueReceive() 的返回值为 0,表示在指定的阻塞期间内没有数据到达。 这是 任务必须处理的错误条件。 为简单起见,已省略此错误条件和其他错误处理功能。


嵌入式 Web 服务器任务

嵌入式 Web 服务器任务可由以下伪代码表示。 此任务仅在数据可用时使用处理器时间, 但需要可变且相对较长的时间才能完成。 因此,该任务的优先级较低, 以防止对设备控制、RS232 或键盘扫描任务的定时产生不利影响。
void WebServerTask( void *pvParameters )
{
DataTypeA Data;

    for( ;; )
    {
        // Block until data arrives.  xEthernetQueue is filled by the
        // Ethernet interrupt service routine.
        if( xQueueReceive( xEthernetQueue, &Data, MAX_DELAY ) )
        {
            ProcessHTTPData( Data );
        }        
    }
}


RS232 接口

此任务在结构上与嵌入式 Web 服务器任务非常相似。 该任务被赋予中等优先级, 以确保不会对设备控制任务的定时产生不利影响。
void RS232Task( void *pvParameters )
{
DataTypeB Data;

    for( ;; )
    {
        // Block until data arrives.  xRS232Queue is filled by the
        // RS232 interrupt service routine.
        if( xQueueReceive( xRS232Queue, &Data, MAX_DELAY ) )
        {
            ProcessSerialCharacters( Data );
        }        
    }
}


键盘扫描任务

这是一项简单的周期性任务。 由于其定时要求类似于 RS232 任务,因此被赋予中等优先级。

周期时间设置得比指定的限制快得多。 这是为了说明这样一个事实, 即该任务可能不会在请求时立即获得处理器时间, 并且执行时,可能会被设备控制任务抢占。

#define DELAY_PERIOD 4

void KeyScanTask( void *pvParmeters )
{
char Key;
TickType_t xLastWakeTime;

    xLastWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        // Wait for the next cycle.
        vTaskDelayUntil( &xLastWakeTime, DELAY_PERIOD );
        
        // Scan the keyboard.
        if( KeyPressed( &Key ) )
        {
            UpdateDisplay( Key );
        }
    }
}
如果根据整个系统的定时,此任务成为最低优先级任务,则可以完全删除对 vTaskDelayUntil() 的调用。 然后,只要所有较高优先级任务处于阻塞态,键盘扫描功能就会连续执行, 有效取代空闲任务。


LED 任务

这是所有任务中最简单的任务。
#define DELAY_PERIOD 1000

void LEDTask( void *pvParmeters )
{
TickType_t xLastWakeTime;

    xLastWakeTime = xTaskGetTickCount();

    for( ;; )
    {
        // Wait for the next cycle.
        vTaskDelayUntil( &xLastWakeTime, DELAY_PERIOD );

        // Flash the appropriate LED.
        if( SystemIsHealthy() )
        {
            FlashLED( GREEN );
        }
        else
        {
            FlashLED( RED );
        }        
    }
}


下一篇> > > 解决方案# 3 : 减少 RAM 使用







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