主页

方案 #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 任务将继续占先空闲任务,并且它也可以被设备控制任务占先 - 保持系统的优先级机制。

 

调度配置

调度配置为协作方式,内核节拍只用于保持实时节拍值。

 

评价

只创建两个系统任务,所以使用的内存比方案 #2 少很多。
RTOS 进程开销减少到最小。
只使用了 RTOS 的部分功能,这需要在源代码级仔细考虑定时和运行环境,但是仍然极大的简化了设计(比较方案 #1)。
依赖处理器的外设,不可移植。
模块之间的问题分析和相互依赖和方案 #1 一样变得需要再次考虑 - 尽管范围小多了。
当系统变得很大时设计可能不足。

 

结论

RTOS 内核的特性可以只需要很小的开销,即使因为处理器和内存的限制不能使用完整的占先式方案也可以简化设计。


例子

这个例子是假想系统的一部分。使用了 FreeRTOS.org 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 portTickType 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();
    } 
}


翻译:   邵子扬
EMail:   shaoziyang@126.com
Blog:    http://blog.ednchina.com/shaoziyang
2008年10月

 

Site Meter