miércoles, 24 de febrero de 2016

RTOS tutorial basico.

En esta sección se ofrece un tutorial sobre cómo escribir aplicaciones que utilizan un RTOS en microcontroladores de memoria limitada. Si usted está buscando un turorial FreeRTOS específica o un tutorial más completo sobre el uso de un RTOS en un sistema embebido, entonces los libros FreeRTOS será un recurso más valioso.Esta parte del sitio web presenta cuatro contrastantes soluciones de diseño a una aplicación en tiempo real incrustado hipotético. La idoneidad de cada solución se juzga para ordenadores integrados con diferentes capacidades de memoria RAM, ROM y procesamiento. Además se evalúa la simplicidad y facilidad de mantenimiento correspondiente de cada diseño. Esta no es la intención de presentar una lista exhaustiva de los posibles diseños, sino una guía para las formas en que el kernel en tiempo real FreeRTOS puede ser utilizado.
Cabe señalar que esta sección fue escrito hace varios años - cuando FreeRTOS se utilizó principalmente en muy pequeñas microcontroladores. Desde ese momento se ha vuelto más común el uso de FreeRTOS en microcontroladores mucho más grandes que no están tan restringidos en la ROM y RAM que proporcionan.
La aplicación se ejecutará en una placa computadora incorporada que debe controlar una planta manteniendo al mismo tiempo las dos interfaces de usuario locales y remotos.
Se representa anteriormente, el sistema consta de:
  1. Un ordenador incorporado dentro de un terminal de control.
  2. Dos sensores en red de bus de campo.
  3. La planta siendo controlado (podría ser cualquier cosa, motor, calentador, etc.). Esto está conectado en la misma red de bus de campo.
  4. Un teclado de matriz que se analizará con la finalidad general de IO.
  5. Dos indicadores LED.
  6. Una pantalla LCD.
  7. Un servidor web incorporado al que un equipo de monitorización remota puede adjuntar.
  8. Una interfaz RS232 a una utilidad de configuración que se ejecuta en un PDA.

Requisitos de software de primer nivel

Aquí estamos interesados ​​en los requerimientos de secuencia y temporización, en lugar de los requerimientos funcionales exactos.

Control de la planta

Cada ciclo de control se realice la siguiente secuencia:
  1. Transmitir una trama en el bus de campo para solicitar los datos de los sensores conectados en red.
  2. Espere a recibir los datos de ambos sensores.
  3. Ejecutar el algoritmo de control.
  4. Transmitir una orden a la planta.
La función de control del ordenador incorporado transmitirá una solicitud cada 10 ms exactamente, y el comando resultante se transmitirá dentro de 5 ms de esta solicitud. El algoritmo de control depende de la sincronización exacta, por lo que es de suma importancia que se cumplan estos requisitos de tiempo. El tiempo de t de control pudiera dar o no convergencia al sistema de control base.

CONTROL tx 10ms Rx 5ms

Interfaz del operador local

El teclado y la pantalla LCD pueden ser utilizados por el operador para seleccionar, ver y modificar los datos del sistema. La interfaz de operador funcionará mientras la planta está siendo controlado.
Para asegurar que no se pierdan las pulsaciones de teclas del teclado deberá ser escaneado por lo menos cada 15 ms. La pantalla LCD se actualizará en un plazo de 50 ms de un ser tecla pulsada.
Nótese que durante el diseño del sistema solo se estan indicando tipo receta de cocina requerimientos de tiempo e importancia de dichos esquemas dentro del sistema.

PANTALLA t actualización: 50ms
TECLADO t escaneo: 15ms

LED

El LED se utiliza para indicar el estado del sistema. Un LED verde intermitente indicará que el sistema está funcionando como se esperaba. Un LED rojo intermitente indicará una condición de falla.
El LED correcta se encenderá y se apagará una vez cada segundo. Esta tasa de encendido se mantiene entre 50 ms.

LED On-Off: 50ms

Interfaz RS232 PDA

La interfaz RS232 PDA será capaz de ver y acceder a los mismos datos que la interfaz de operador local, y aplicar las mismas restricciones de tiempo - ignorando cualquier tiempos de transmisión de datos.

TXRX RS232 (BAUDIOS): 9600bps

Interfaz de TCP / IP

El servidor web incorporado deberá atender las peticiones HTTP dentro de un segundo. 

Solución # 1 SIN RTOS ¿Por qué utilizar un kernel RTOS?


<<< | >>>
Véase también el artículo FAQ " ¿Por qué utilizar un RTOS? ".

Sinopsis

Muchas aplicaciones pueden ser producidos sin el uso de un núcleo RTOS y esta página describe un enfoque que podría ser tomada.
A pesar de que la aplicación en este caso es probable que sea demasiado complejo para este tipo de enfoque de la página se incluye para destacar tanto los posibles problemas y proporcionar un contraste con los siguientes diseños de software basada RTOS.

Implementación

Esta solución utiliza un enfoque de bucle infinito tradicional, por lo que cada componente de la aplicación es representada por una función que se ejecuta hasta su finalización.
Lo ideal sería que un temporizador de hardware se utiliza para programar la función de control de la planta momento crítico. Sin embargo, tener que esperar a la llegada de datos y el complejo cálculo realizado hacen la función de control inadecuado para la ejecución dentro de una rutina de servicio de interrupción.

Concepto de la Operación

La frecuencia y el orden en el que los componentes se denominan en el bucle infinito se pueden modificar para introducir algo de priorización. Un par de tales alternativas de secuenciación se proporcionan en el ejemplo siguiente.

Configuración del planificador

El planificador RTOS no se utiliza.

Evaluación

tamaño pequeño código.
No depende de código fuente terceros.
Sin memoria RAM RTOS, ROM o procesamiento de arriba.
Difícil atender a los requisitos de tiempo complejas.
No escala bien sin un gran aumento de la complejidad.
Timing difícil de evaluar o mantener debido a las interdependencias entre las diferentes funciones.

Conclusión

El enfoque de bucle simple es muy bueno para pequeñas aplicaciones y aplicaciones con requisitos de tiempo flexibles - pero puede llegar a ser complejos, difíciles de analizar y difícil de mantener si se amplía a los sistemas más grandes.

Ejemplo

Este ejemplo es una aplicación parcial de la aplicación hipotética introducido previamente.

La función de control de la planta

La función de control puede ser representado por el siguiente pseudo código:
PlantControlCycle (void)
{
    TransmitRequest ();
    WaitForFirstSensorResponse ();

    (datos si ya ha recibido el primer sensor)
    { 
        WaitForSecondSensorResponse ();
        
        (datos si ya ha recibido segundo sensor)
        {
            PerformControlAlgorithm ();
            TransmitResults ();
        }
    }
}

Las funciones de interfaz humana

Esto incluye el teclado, pantalla LCD, comunicaciones RS232 y el servidor web incorporado.
El siguiente pseudo código representa una estructura de bucle infinito simple para el control de estas interfaces.
int main (void)
{
    Inicializar ();
    
    para( ;; )
    {
        ScanKeypad ();
        UpdateLCD ();
        ProcessRS232Characters ();
        ProcessHTTPRequests ();   
    }

    // Nunca debe llegar hasta aquí.
    return 0;
}
Esto supone dos cosas: En primer lugar, las comunicaciones IO es amortiguada por las rutinas de servicio de interrupción de modo periféricos no requieren votación. En segundo lugar, la función de llamadas individuales dentro del bucle ejecutar lo suficientemente rápido como para que se cumplan todos los requisitos máximos de tiempo.

La programación de la función de control de la planta

La longitud de la función de control significa que no puede simplemente ser llamado desde una interrupción de 10 ms temporizador.
Añadiéndolo al bucle infinito requeriría la introducción de algún tipo de control temporal. Por ejemplo ... :
// Bandera utiliza para marcar el momento en que una
// Ciclo de control debe comenzar (exclusión mutua
// Cuestiones que se ignoran para este ejemplo).
int TimerExpired;

// Rutina de servicio de interrupción del temporizador. Esta
// Está configurado para ejecutar cada 10 ms.
TimerInterrupt vacío (vacío)
{    
    TimerExpired = true;
}


// Principal () todavía contiene el bucle infinito - 
// Dentro de la cual una llamada al control de la planta
// Función se ha añadido.
int main (void)
{
    Inicializar ();
    
    para( ;; )
    {
        // Girar hasta que es hora para la siguiente
        // Ciclo.
        si (TimerExpired)
        {
            PlantControlCycle ();
            TimerExpired = false;

            ScanKeypad ();
            UpdateLCD ();

            // Los LEDs podrían utilizar una cuenta de
            // El número de interrupciones, o una
            // Diferente temporizador.
            ProcessLEDs ();

            // Comms tampones deben ser grandes
            // Suficiente para mantener el valor de 10 ms
            // Datos.
            ProcessRS232Characters ();
            ProcessHTTPRequests ();   
        }

        // El procesador se puede poner a dormir
        // Aquí siempre es despertado por cualquier
        // interrumpir.
    }

    // Nunca debe llegar hasta aquí.
    return 0;
}
... Pero esto no es una solución aceptable:
  • Un retraso o fallo en los resultados de bus de campo en un aumento del tiempo de ejecución de la función de control de la planta. Los requisitos de tiempo de las funciones de la interfaz lo más probable es ser violada.
  • La ejecución de todas las funciones de cada ciclo también podría resultar en una violación de la sincronización del ciclo de control.
  • Fluctuación en el tiempo de ejecución puede causar ciclos se puede perder. Por ejemplo, el tiempo de ejecución de ProcessHTTPRequests () podría ser insignificante cuando no se han recibido solicitudes HTTP, pero bastante largo cuando se servía una página.
  • No es muy fácil de mantener - que depende de cada función que se ejecuta dentro del tiempo máximo.
  • Los tampones de comunicación solamente se limpian una vez por ciclo que requiere su longitud a ser más grandes de lo que sería necesario.


estructuras alternativas

Dos factores pueden ser identificados que limitan la idoneidad de la estructura de bucle sencillo descrito hasta ahora.
  1. La duración de cada llamada a la funciónPermitiendo que cada función para ejecutar en su totalidad lleva demasiado tiempo. Esto se puede prevenir mediante el fraccionamiento de cada función en un número de estados.Sólo un estado se ejecuta cada llamada. El uso de la función de control como un ejemplo:
    // Define los estados para la función del ciclo de control.
    typdef enumeración eCONTROL_STATES
    {
        ESTART, // Iniciar nuevo ciclo. 
        eWait1, // Espera a que la primera respuesta del sensor. 
        eWait2   // esperar a la segunda respuesta del sensor.
    eControlStates};
    
    PlantControlCycle (void)
    {
    eControlState raíces estática = ESTART;
    
        interruptor (raíces)
        {
            ESTART caso:
                TransmitRequest ();
                Estate = eWait1;
                romper;
                
            eWait1 caso;
                (datos si ya ha recibido el primer sensor)
                {
                    Estate = eWait2;
                }
                // ¿Cómo están los tiempos de espera para ser manipulados?
                romper;
                
            eWait2 caso;
                (datos si ya ha recibido el primer sensor)
                {
                    PerformControlAlgorithm ();
                    TransmitResults ();
                    
                    Estate = ESTART;
                }
                // ¿Cómo están los tiempos de espera para ser manipulados?
                romper;           
        }
    }
    
    Esta función ahora es estructuralmente más compleja, e introduce nuevos problemas de agenda. El código en sí será más difícil de entender cuando se agregan los estados adicionales - por ejemplo, para manejar las condiciones de tiempo de espera y de error.
  2. La granularidad del temporizadorUn temporizador de intervalo más corto dará más flexibilidad.
    La implementación de la función de control como una máquina de estados (una al hacerlo haciendo cada llamada más corta) puede permitir que se llama a partir de una interrupción del temporizador. El intervalo del temporizador tendrá que ser lo suficientemente corto como para asegurar la función se vuelve a llamar a una frecuencia que cumpla con los requisitos de tiempo. Esta opción está plagado de problemas de tiempo y mantenimiento.
    Alternativamente, la solución bucle infinito podría modificarse para llamar a diferentes funciones en cada bucle - con la función de control de alta prioridad se llama con más frecuencia:
    int main (void)
    {
    int contador = -1;
    
        Inicializar ();
        
        // Cada función se implementa como un estado 
        // Máquina de modo está garantizado para ejecutar 
        // Rápidamente - pero debe ser llamado con frecuencia.
        
        // Nota la frecuencia del temporizador se ha planteado.
        
        para( ;; )
        {
            si (TimerExpired)
            {
                Contador ++;
                
                interruptor (Contador)
                {
                    caso 0: ControlCycle ();
                              ScanKeypad ();
                              romper;
                              
                    Caso 1: UpdateLCD ();
                              romper;
    
                    Caso 2: ControlCycle ();
                              ProcessRS232Characters ();
                              romper;
    
                    Caso 3: ProcessHTTPRequests ();
                              
                              // Volver a empezar
                              Contador = -1;                          
                              romper;
                              
                }
                
                TimerExpired = false;
            }
        }
    
        // Nunca debe llegar hasta aquí.
        return 0;
    }
    
    Más información se puede introducir por medio de contadores de eventos, por lo que la funcionalidad de prioridad más bajo sólo se llama si se ha producido un evento que requiere servicio:
        para( ;; )
        {
            si (TimerExpired)
            {
                Contador ++;
                
                // Procesar el ciclo de control cada otro bucle.
                interruptor (Contador)
                {
                    caso 0: ControlCycle ();
                              romper;
                              
                    Caso 1: contador = -1;
                              romper;
                }
    
                // Proceso de sólo una de las otras funciones. único proceso
                // Una función si hay algo que hacer. EventStatus ()
                // Comprueba si hay eventos desde la última iteración.
                interruptor (EventStatus ())
                {
                    EVENT_KEY caso: ScanKeypad ();
                                        UpdateLCD ();
                                        romper;
                               
                    EVENT_232 caso: ProcessRS232Characters ();
                                        romper;
                                
                    EVENT_TCP caso: ProcessHTTPRequests ();
                                        romper;
                }
                
                TimerExpired = false;
            }
        }
    
    Proceso de sucesos en esta forma reducirá los ciclos de CPU desperdiciados pero el diseño sigue exhibirán fluctuación en la frecuencia con la que se ejecuta el ciclo de control.

Solución # 2 CON RTOS Un sistema completamente Preferente


Sinopsis

Esta es una solución multitarea preferente tradicional. Se hace un uso completo de los servicios RTOS sin tener en cuenta a la memoria y el procesador resultante por encima. Hay una partición simplista de la funcionalidad requerida para una serie de tareas autónomas.

Implementación

Una tarea separada se crea para cada parte del sistema que puede ser identificado como siendo capaz de existir de forma aislada, o que tienen un requisito de tiempo particular.

Solución # 2 funciones tareas y prioridades
Las tareas se bloqueará hasta que un evento indica que se requiere un procesamiento. Eventos o bien puede ser externo (como pulsación de una tecla), o interno (por ejemplo, un temporizador expira). Este enfoque orientado a eventos significa que no hay tiempo de CPU se desperdicia votación para eventos que no han ocurrido.
Las prioridades se asignan a tareas de acuerdo a sus requerimientos de tiempo. El más estricto el requisito de tiempo mayor será la prioridad (no todas las evaluaciones de asignación de prioridad son que simplista).

Concepto de la Operación

La tarea de mayor prioridad que es capaz de ejecutar (no está bloqueado) es la tarea garantizada por el RTOS para obtener el tiempo de procesador. El núcleo se suspenda inmediatamente la ejecución de una tarea debería estar disponible una tarea de mayor prioridad.
Esta programación se realiza automáticamente, sin el conocimiento explícito, el diseño o los comandos dentro del código fuente de la aplicación. Sin embargo, es responsabilidad de los diseñadores de aplicaciones para garantizar que las tareas se asignan una prioridad adecuada.
Cuando ninguna tarea es capaz de ejecutar la tarea ociosa ejecutará. La tarea de inicio cuenta con la opción de colocar el procesador en modo ahorro de energía.

Configuración del planificador

El programador está configurado para una operación preventiva. La frecuencia del núcleo garrapata debe fijarse en el valor más lento que proporciona la granularidad de tiempo requerido.

Evaluación

, Segmentados, de diseño simple de mantener flexibles, con pocas interdependencias.
La utilización del procesador se conecta automáticamente a partir de una tarea a otra en función de las necesidades más urgentes sin ninguna acción explícita requerida dentro del código fuente de la aplicación.
La estructura orientada a eventos asegura que ningún tiempo de CPU se desperdicia votación para eventos que no han ocurrido. Procesamiento sólo se realiza cuando hay trabajo que necesitaba ser hecho.
El consumo de energía puede reducirse si la tarea ociosa coloca el procesador en modo ahorro de energía (sueño), pero también puede ser desperdiciado como la interrupción de la garrapata a veces se despierta el procesador innecesariamente.
La funcionalidad del núcleo utilizará los recursos de procesamiento. El alcance de esta dependerá de la frecuencia kernel garrapata elegido.
Esta solución requiere una gran cantidad de tareas, cada una de las cuales requieren su propia pila, y muchos de los cuales requiere una cola en la que se pueden recibir eventos. Por lo tanto, esta solución utiliza una gran cantidad de memoria RAM.
contexto frecuente el cambio entre las tareas de la misma prioridad será un desperdicio de ciclos de procesador.

Conclusión

Esto puede ser una buena solución proporciona la RAM y capacidad de procesamiento disponible.La partición de la aplicación en tareas y la prioridad asignada a cada tarea requiere una cuidadosa consideración.

Ejemplo

Este ejemplo es una aplicación parcial de la aplicación hipotética introducido previamente. se utiliza la API FreeRTOS.

Control de tareas planta

Esta tarea implementa toda la funcionalidad de control. Tiene requerimientos críticos de tiempo y por lo tanto se le da la más alta prioridad en el sistema:
#define CYCLE_RATE_MS 10
#define MAX_COMMS_DELAY 2

PlantControlTask ​​anular (pvParameters void *)
{
TickType_t xLastWakeTime;
Tipo de datos datos1, datos2;

    InitialiseTheQueue ();

    // UN
    xLastWakeTime = xTaskGetTickCount ();

    // B
    para( ;; )
    {
        // C
        vTaskDelayUntil (y xLastWakeTime, CYCLE_RATE_MS);
        
        // Solicitar datos de los sensores.
        TransmitRequest ();
        
        // D
        si (xQueueReceive (xFieldBusQueue, y Datos1, MAX_COMMS_DELAY))
        {
            // E
            si (xQueueReceive (xFieldBusQueue, y Datos2, MAX_COMMS_DELAY))
            {
                PerformControlAlgorithm ();
                TransmitResults ();                
            }
        } 
    }
    
    // Nunca se tiene aquí!
}
Haciendo referencia a las etiquetas dentro del fragmento de código anterior:
  1. xLastWakeTime es inicializado. Esta variable se utiliza con el vTaskDelayUntil () la función de API para controlar la frecuencia a la que realiza la función de control.
  2. Esta función se ejecuta como una tarea autónoma por lo que nunca debe salir.
  3. vTaskDelayUntil () le dice al núcleo que esta tarea debe comenzar a ejecutar exactamente 10 ms después de la hora almacenada en xLastWakeTime . Hasta que se alcanza esta vez la tarea de control bloqueará. Como esta es la tarea de mayor prioridad dentro del sistema se garantiza que comience a ejecutar de nuevo exactamente en el momento correcto. Será adelantarse a cualquier tarea de menor prioridad que le pasa a estar en ejecución.
  4. Hay un tiempo finito entre los datos que se solicitan de los sensores conectados en red y que están recibiendo datos. Los datos que llegan en el bus de campo se coloca en elxFieldBusQueue por una rutina de servicio de interrupción, la tarea de control, por tanto, puede hacer una llamada de bloqueo en la cola para esperar a que los datos estén disponibles. Al igual que antes, ya que es la tarea de mayor prioridad en el sistema está garantizado para continuar con la ejecución inmediata de datos está disponible.
  5. Como 'D', a la espera de los datos del segundo sensor.
Un valor de retorno de 0 desde xQueueReceive () indica que no hay datos llegaron dentro del plazo de bloque especificado. Esta es una condición de error la tarea debe manejar. Esta y otras funciones de gestión de errores se ha omitido por simplicidad.

Embedded Web Server Tarea

La tarea del servidor web incorporado puede ser representado por el siguiente pseudo código.Esto sólo se utiliza el tiempo de procesador cuando hay datos disponibles, pero tomará un tiempo variable y relativamente largo para completar. Por lo tanto, se le da una prioridad baja para evitar que afectar de forma adversa la sincronización de las tareas de control de planta, RS232 o escaneo teclado.
WebServerTask vacío (pvParameters void *)
{
Datos DataTypeA;

    para( ;; )
    {
        // Bloque de datos hasta que llega. xEthernetQueue es llenado por el
        // Ethernet rutina de servicio de interrupción.
        si (xQueueReceive (xEthernetQueue, y bases de datos, max_delay))
        {
            ProcessHTTPData (datos);
        }        
    }
}

Interfaz RS232

Esto es muy similar en estructura a la tarea del servidor Web incorporado. Se da una prioridad media para asegurarse de que no afecta negativamente en la planificación de la tarea de control de la planta.
RS232Task anular (pvParameters void *)
{
Datos DataTypeB;

    para( ;; )
    {
        // Bloque de datos hasta que llega. xRS232Queue es llenado por el
        // Rutina de servicio de interrupción RS232.
        si (xQueueReceive (xRS232Queue, y bases de datos, max_delay))
        {
            ProcessSerialCharacters (datos);
        }        
    }
}

Teclado exploración de la tarea

Esta es una tarea cíclica simple. Se da una prioridad media como sus requisitos de tiempo son similares a la tarea RS232.
El tiempo de ciclo se ajusta mucho más rápido que el límite especificado. Esto es para tener en cuenta el hecho de que no puede obtener el tiempo de procesador de inmediato a petición - y una vez que la ejecución puede conseguir se anticipó por la tarea de control de la planta.
DELAY_PERIOD #define 4

KeyScanTask anular (pvParmeters void *)
{
charla clave;
TickType_t xLastWakeTime;

    xLastWakeTime = xTaskGetTickCount ();

    para( ;; )
    {
        // Espera a que el próximo ciclo.
        vTaskDelayUntil (y xLastWakeTime, DELAY_PERIOD);
        
        // La ejecución del teclado.
        si (KeyPressed (& Key))
        {
            UpdateDisplay (Key);
        }
    }
}
Si la temporización global del sistema eran tales que este se podría hacer la tarea de prioridad más bajo entonces la llamada a vTaskDelayUntil () podría ser eliminado por completo. La función de exploración clave sería luego ejecutar continuamente siempre que todas las tareas de mayor prioridad se bloquearon - tomar efectivamente el lugar de la tarea vacía.

Tarea LED

Esta es la más simple de todas las tareas.
DELAY_PERIOD #define 1000

LEDTask vacío (pvParmeters void *)
{
TickType_t xLastWakeTime;

    xLastWakeTime = xTaskGetTickCount ();

    para( ;; )
    {
        // Espera a que el próximo ciclo.
        vTaskDelayUntil (y xLastWakeTime, DELAY_PERIOD);

        // Flash del LED apropiado.
        si (SystemIsHealthy ())
        {
            FlashLED (VERDE);
        }
        más
        {
            FlashLED (RED);
        }        
    }
}

No hay comentarios:

Publicar un comentario