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);
        }        
    }
}

ChibiOS para el IDE Arduino

Un sistema operativo en tiempo real es una pieza bastante complicado de software, incluso con una pequeña RTOS - debido a la forma en que se mete con varios detalles de bajo nivel de ejecución del código, tales como pilas y las interrupciones. Es por lo tanto no es poca cosa cuando todo se puede hacer como una biblioteca de complemento estándar para el IDE de Arduino.
Pero eso es exactamente lo que ha sido hecho por Bill Greiman con ChibiOS , en forma de una biblioteca llamada "ChibiOS_AVR" (también hay una versión ARM para la debida y Teensy).
Así que vamos a continuar donde lo dejé ayer e instalar esta cosa para su uso con JeeNodes, eh?
  • descargar una copia de ChibiOS20130208.zip de esta página en Google Code
  • descomprimirá y el interior encontrará una carpeta llamada ChibiOS_AVR
  • moverlo dentro de la bibliotecas carpeta en su carpeta de bocetos IDE (al lado de JeeLib, etc)
  • También puede ser que desee mover ChibiOS_ARM y SdFat junto a él, para su uso posterior
  • otras cosas en ese archivo ZIP son un archivo README y la documentación HTML
  • eso es todo, ahora relanzar el IDE de Arduino para que sea reconocer las nuevas bibliotecas
Eso es realmente todo lo que hay que hacer. La carpeta ChibiOS_AVR también contiene una docena de ejemplos, cada uno de los cuales es digno de mirar en y probar. Tenga en cuenta que no hay ningún LED en un JeeNode estándar, y que el LED azul del USB JeeNode SMD y JeeNode está en el pin 9 y tiene una polaridad inversa ( "0" a su vez en "1" apagarlo ).
Nota: Estoy usando esto con Arduino IDE 1.5.2, pero también debería funcionar con 1.0.x IDE
Las cosas simples son todavía relativamente simple con un RTOS, pero estar preparados para hacer frente a toda una serie de nuevos conceptos y técnicas cuando realmente comienza a sumergirse en un montón de maneras de hacer las tareas e interrumpe el trabajo en conjunto -. Exclusiones mutuas, semáforos, eventos, colas, buzones ...
Por suerte, ChibiOS viene con una gran cantidad de documentación, incluyendo algunos generales guías y de cómo-a . La documentación AVR-específica se puede encontrar aquí(al igual que en ese archivo ZIP que acaba de descargar).
No estoy seguro de que este es el mejor lugar para ello, pero he puesto el ejemplo de ayer en JeeLib por ahora.
Me gustaría ir a la RTOS y ChibiOS un poco más en las próximas semanas, aunque sólo sea para ver cómo la comunicación inalámbrica y los modos de suspensión de baja potencia se pueden montar en ese país.
Solo un dato por ahora: la latencia de cambio de contexto de ChibiOS en una ATmega328 @ 16 MHz parece ser alrededor de 15 mu s. O, para decirlo de otra manera: se puede cambiar entre múltiples tareas durante sesenta mil veces por segundo. Trago.
TUTORIAL

1 El muestreo usando el ADC

Fig.1 - Potenciómetro diagrama.
Un análogo al convertidor digital de es un dispositivo que convierte una cantidad física continua (por lo general de tensión) a un número digital que representa la amplitud de la cantidad.
En esta demostración, estamos muestreando voltaje a través de unpotenciómetro para establecer su posición. Un potenciómetro es un mando de tres pines que proporciona una resistencia variable entre WA y WB pasadores (Fig.1). Cantidad de resistencia entre A y B es constante, pero girando el eje del potenciómetro, cambiamos la cantidad de resistencia a cada lado de W.
Conexión de A a 3 V, B a GND (o viceversa) y la tensión de muestreo entre W y GND que podría medir la posición del limpiaparabrisas: según divisor de voltaje , el voltaje muestreado sería proporcional a la cantidad de resistencia entre W y el pasador conectado a GND.

2 ADC en ChibiOS

STM32 tiene más de un ADC. Como siempre, tuvimos que permitirá subsistema ADC en halconf.h y controlador adecuado en mcuconf.h .
Cada ADC tiene más de un canal. Podríamos tomar muestras de estos canales que eligen una secuencia personalizada. Con el fin de hacer que teníamos que configurar algún registro ADC aunqueADCConversionGroup . Por otra parte, podríamos configurar la frecuencia de muestreo y el modo. Nuestro grupo de conversión es:
/ *
 * Grupo de conversión ADC.
 * Modo: tampón circular, 10 muestras de 1 canal, SW activa.
 * Canales: IN0.
 * / 
Static  const ADCConversionGroup my_conversion_group = {
  FALSO,                             / * NO CIRCULAR * / 
  MY_NUM_CH,                         / * NUMB DE CH * / 
  NULL ,                              / * NO ADC RETROLLAMADA * / 
  NULL ,                              / * NO ADC ERROR RETROLLAMADA * / 
  0 ,                                 / * CR1 * / 
  ADC_CR2_SWSTART,                   / * CR2 * / 
  0 ,                                 / * * SMPR1 / 
  ADC_SMPR2_SMP_AN0 (ADC_SAMPLE_3),   / * * SMPR2 / 
  ADC_SQR1_NUM_CH (MY_NUM_CH),        / * SQR1 * / 
  0 ,                                 / * * SQR2 / 
  ADC_SQR3_SQ1_N (ADC_CHANNEL_IN0)    / * SQR3 * / 
};
Estamos muestreando de forma no circular, de un solo canal ( ADC_CHANNEL_IN0 ). La frecuencia de muestreo es igual a tres ciclos de reloj principal ( ADC_SAMPLE_3 ), (Para más información ver para los registros de ADC en Manual de referencia).
Hemos desactivado el dos de devolución de llamada de ADC. La primera de ellas se produce durante el muestreo dos veces (en el medio y en el final del grupo de conversión); el segundo se produce en caso de error ADC.
En principal tuvimos para configurar nuestra pasador. En hilo de ADC que empezamos, el muestreo de un tampón de n elementos y hacer que significa en los datos con el fin de eliminar el ruido.
estática msg_t Thd2 ( void * arg)  {
   unsigned II;
  ( Vacío ) arg;
  chRegSetThreadName ( "controlador Led" );
   / *
   * Activa el conductor ADC1.
   * / 
  AdcStart (y ADCD1, NULL );
   mientras que (TRUE) {
    adcConvert (y ADCD1, y my_conversion_group, sample_buff, MY_SAMPLING_NUMBER); . / * Hacer media de los valores muestreados * / 
    media = 0 ;
     para (ii = 0 ; ii <MY_NUM_CH * MY_SAMPLING_NUMBER; ii ++) {
    
      media + = sample_buff [ii];
    }
    decir / = MY_NUM_CH * MY_SAMPLING_NUMBER;
    lastvalue = ( float ) significa * 3 / 4096 ;
    flag = true;
  }
  volver  0 ;
}
Tenga en cuenta que podemos utilizar el ADC en modo continuo utilizando otro API: adcStartConversion () .Recuerde siempre que teníamos a la dimensión muestras de tampón correctamente para evitar un desbordamiento:
estática  adcsample_t sample_buff [MY_NUM_CH * MY_SAMPLING_NUMBER];

3 de atenuación mediante PWM

La figura 2 -Relation entre el ciclo de trabajo y el valor medio.
La modulación por ancho de pulso es una técnica utilizada para codificar un mensaje en una señal pulsante. Es un tipo de modulación. Aunque esta técnica de modulación puede utilizarse para codificar la información para su transmisión, su uso principal es permitir el control de la potencia suministrada a los aparatos eléctricos, especialmente a las cargas inerciales tales como motores.
Por otra parte, podemos usar PWM a desvanecerse la intensidad del LED.Esto es posible gracias principio de que el valor medio de PWM es proporcional a su ciclo de trabajo .

4 PWM en ChibiOS

Simplificando, PWM es una onda cuadrada que se caracteriza por un período fijo y un ciclo de trabajo variable.La mejor manera de darse cuenta de que está utilizando un contador de tiempo. La fijación de la frecuencia del temporizador, (de ahí período), podríamos definir periodo PWM y ciclo de trabajo como múltiplo del temporizador cuántica. Tenga en cuenta que la frecuencia del temporizador debe ser varios órdenes de magnitud mayor que la frecuencia de PWM con el fin de tener una buena resolución ciclo de trabajo.
En este punto, debe quedar claro que la frecuencia del campo de PWMConfig se denomina temporizador relacionado y que hay un temporizador para cada PWM. . Dado que cada temporizador como algo más que un canal que podría generar más de PWM con el mismo periodo pero diferente ciclo de trabajo
del controlador PWM tiene devoluciones de llamada: una es común a cada canal y se produce cada periodo, otros son de canal en función y se producen cuando los interruptores de salida PWM, por lo tanto, depende de ciclo de trabajo. Esta es la PWMConfig verde usado desvaneciendo LED de intensidad:
estática PWMConfig pwmcfg = {
   1000000 ,                                     / * 1 MHz de frecuencia de reloj PWM. * / 
  1000 ,                                        / * Período Inicial de PWM de 1 ms. * /
  pwmpcb,
  {
   {PWM_OUTPUT_ACTIVE_HIGH, pwmc1cb},
   {PWM_OUTPUT_DISABLED, NULL },
   {PWM_OUTPUT_DISABLED, NULL },
   {PWM_OUTPUT_DISABLED, NULL }
  },
  0 ,
   0 
};

Tenga en cuenta que la frecuencia del temporizador es de 1 MHz y que PWM periodo inicial es de 1000 es decir temporizador cuántica 1ms. Pwmpcb es el período de devolución de llamada y pwmc1cb es la devolución de llamada del canal 1; Por otra parte, el primer canal está activo y la salida es alta durante un período de tiempo proporcional al ciclo de trabajo, en lugar de otros canales están desactivados.
Como todos los pilotos, PWM debe iniciarse antes de usarlo. PWM API permite iniciar la elección de los canales de ciclo de trabajo, así como el período de PWM cambio, canal desactivado y dejar de conducir. Con el fin de LED se desvanecen apenas comenzamos el conductor antes de lazo de hilo y permitir que continuamente cambiando de canal ciclo de trabajo (en nuestro caso elegimos el ciclo de trabajo proporcional a durar valor muestreado).
estática msg_t Thd1  ( void * arg)  { 
  ( vacío ) arg;
  chRegSetThreadName ( "controlador Led" );
  pwmStart (y PWMD1, y pwmcfg); mientras que (TRUE) {
  
    porcentaje = lastvalue * 10.000 / 3 ;
     si (porcentaje < 100 ) {
      porcentaje = 0 ;
      pwmDisableChannel (y PWMD1, 0 );
      palClearPad (GPIOA, GPIOA_LED_GREEN);
    }
    otra cosa  si (porcentaje> 9900 ) {
      porcentaje = 10000 ;
      pwmDisableChannel (y PWMD1, 0 );
      palSetPad (GPIOA, GPIOA_LED_GREEN);
    }
    otra cosa {
      pwmEnableChannel (y PWMD1, 0 , PWM_PERCENTAGE_TO_WIDTH (y PWMD1, porcentaje));
    }
    chThdSleepMilliseconds ( 1 );
  }
  volver RDY_OK;
}

LED verde no está conectado directamente a nuestra salida del temporizador y utilizamos las devoluciones de llamada para hacer una PWM de software. Cuando el ciclo de trabajo es del 0% o 100% entre uno pwmpcb ypwmc1cb devoluciones de llamada no podría ocurrir: que explica los controles sobre los bordes del ciclo de trabajo.
estática  vacío  pwmpcb (PWMDriver * pwmp)  { 
  ( vacío ) pwmp;
   si (porcentaje)
    palSetPad (GPIOA, GPIOA_LED_GREEN);
} Static void pwmc1cb (PWMDriver * pwmp) {   ( vacío ) pwmp;
   

  palClearPad (GPIOA, GPIOA_LED_GREEN);
}
Recuerde que antes de usar PWM para permitir subsistema en halconf.h y asignar temporizador en mcuconf.h.

5 La rueda de color

La conducción de un LED RGB utilizamos tres canales de la misma PWM. Rueda de colores evitar que más de dos uniones se afinan juntos: esto porque encender el tercer cambio de color de unión hacia la saturación perdedora blanco.
mientras que (TRUE) {
  tmp = lastvalue * 90 ;
  ii = tmp / 90 ;
  jj = tmp% 90 ;
   si (== ii 0 ) {
    porcentaje [ 0 ] = 10.000 * sin (jj * 3,14 / 180 );
    porcentaje [ 1 ] = 0 ;
    porcentaje [ 2 ] = 10.000 * cos (jj * 3,14 / 180 );
  }
  otra cosa  si (ii == 1 ) {
    porcentaje [ 0 ] = 10.000 * cos (jj * 3,14 / 180 );
    porcentaje [ 1 ] = 10.000 * sin (jj * 3,14 / 180 );
    porcentaje [ 2 ] = 0 ;
  }
  otra cosa  si (ii == 2 ) {
    porcentaje [ 0 ] = 0 ;
    porcentaje [ 1 ] = 10.000 * cos (jj * 3,14 / 180 );
    porcentaje [ 2 ] = 10.000 * sin (jj * 3,14 / 180 ); 
  ...
  
}

6 Nuestro circuito

El circuito que hicimos para conducir el LED RGB es la siguiente:
Fig.3 - El muestreo y el circuito de regulación de luz.

7 Proyecto utilizado en este tutorial

Este código ha sido probado utilizando ChibiOS / RT 3.0.x.