Demostración del planifacador de protothreads

low-power-multi-threadingEn la pasada entrada Planificación de protothreads para bajo consumo hice una introducción a un sistema de protothreads más scheduler útil para bajo consumo.  En la presente entrada presento un proyecto de demostración para mostrar su funcionamiento.

Este proyecto ha sido desarrollado con PIC24, con la placa de desarrollo Explorer-16 y con el IDE MPLABx.

Todo el código y los ficheros de proyectos relacionados con esta entrada se pueden obtener en este enlace.

Tareas

La siguiente figura muestra un diagrama de flujo de datos de la aplicación:

Diagrama de flujo de datos.

Diagrama de flujo de datos.

LED DELAY TASKS: Son un conjunto de tareas que esperan a que un flag en el objeto leds asociado a cada una de ellas se active. Entonces lo desactiva y enciende un LED asociado durante medio segundo. Estas tareas ejecutan un único segmento de código reentrante. Por ello se debe indicar el flag y el LED asociado cuando se crea cada una de estas tareas

SWITCH TASK: Escanea el estado de los pulsadores de la placa cada 100mS. Si detecta algún pulsador accionado activa una bandera asociada al pulsador en el objeto leds.

RECEIVE TASK: Espera a que se haya recibido un byte por la UART. Entones espera a poder el byte recibido en la cola de recepción.

TRANSMIT TASK: Espera a que haya un byte en la cola de transmisión. Entones espera a poder enviarlo por la UART.

COMUNICATION TASK: Espera que haya datos en la cola input. Los mete en la cola output cuando el semáforo sem lo permita. Si el dato reibido es un caracter entre 1 y 4 activa un flag en el objeto leds.

TIMED TEXT TASKS: Son un conjunto de tareas que mandan un mensaje por el puerto serie periódicamente. Cada tarea tiene un periodo y mensaje distinto al de las demás.

PERIODIC END TASK: Esta tarea hace parpadear un LED, pero de una manera especial. Se crea y se destruye para probar la estabilidad del pool de tareas:

  1. Espera un semiperiodo.
  2. Cambia el estado del LED
  3. Se crea una tarea copia de sí misma.
  4. Muere.

Este es el código de la tarea:

#define _PERIOD     0.2 // Seconds

//------------------------------------------------------------------------------
// This task flashes an LED. This waits 0.2 seconds...
//                  ...to change the LED status, create a clone task and die:
PT_THREAD( periodicEnd_task( struct pt *pt, void *context ) ) {
    static uint32_t time = (uint32_t)0;
    PT_BEGIN( pt );
    PT_WAIT_UNTIL( pt, timer_isPeriodOver( &time, __sec(_PERIOD) ) );
    hal_ledToggle( 4 );
    scheduler_createNewTask( periodicEnd_task );
    PT_END( pt );
}

 Creación de hebras y arranque.

En el siguiente fragmento de código se muestra como se crean todas las hebras en el planificador. A las hebras reentrates se les adjunta su contexto. Y por último en un lazo infinito se invoca al planificador y al modo ocioso de la CPU.

Las tareas «Timed Text» se crean antes de arrancar el scheduler. Las tareas «LED DELAY» son creadas en caliente por la tarea «ledSwitches_task».


//------------------------------------------------------------------------------
/** @file main.c
  * @brief Defines the entry point for the application.
  * @date 04/01/2014.
  * @author Rafa García.                                                    */
//------------------------------------------------------------------------------

//----- Entry:
#include "timer-config.h"
#include "../scheduler/scheduler.h"

//----- Drivers:
#include "../drivers/hardware-profile/hardware-abstraction-layer.h"
#include "../drivers/timer/timer.h"

//----- Data Objects:
#include "../data-objects/all-data-objects.h"

//----- Tasks:
#include "../tasks/uart/uart-tasks.h"
#include "../tasks/timed-text/timed-text-tasks.h"
#include "../tasks/leds-switches/leds-switches-tasks.h"
#include "../tasks/periodic-end/periodic-end-tasks.h"

/** @brief  Elements quantity of the array timedTextContext.                */
#define _TIMED_TEXT_QTY (sizeof(timedTextContext)/sizeof(timedTextContext_t))

/** @brief Array that stores different contexts of a reentrant thread.      */
timedTextContext_t timedTextContext[] = {
    { " Hello every  2.4 seconds.\n", __sec(2.4)        },
    { " Hello every  9.1 seconds.\n", __sec(9.1)        },
    { " Hello every 17.5 seconds.\n", __sec(17.5)       },
    { " Hello every  1.0 minute.\n",  __sec(60.0)       },
    { " Hello every  1.2 minutes.\n", __sec(60.0*1.2)   },
};

//------------------------------------------------------------------------------
/** @brief Application.                                                     */
int main( void ) {

    // Basic hardware configuration:
    hal_cpu_init();

    // Initializes data:
    allDataObj_init();

    // Sets the timer module to measure the pass of time:
    timer_init( TIMER_FREQ );

    // Tasks creation section:
    scheduler_createNewTask( uart_receiveTask );
    scheduler_createNewTask( uart_transmitTask );
    scheduler_createNewTask( uart_comunicationTask );
    scheduler_createNewTask( ledSwitches_task );
    scheduler_createNewTask( periodicEnd_task );

    // Creates multiple reentrant tasks:
    int i; for( i = 0; i < _TIMED_TEXT_QTY; i++ ) {
        int *taskID = scheduler_createNewTask( timedText_task );
        scheduler_setContext( taskID, &timedTextContext[i] );
    }

    // While always:
    while( true ) {

        // Executes all tasks until all of them get blocked:
        scheduler_run();

        // Enters idle mode. The LED is turned off before entering...
        //      ...and turned on when exits to show the CPU activity:
        hal_ledOff( 6 );
        hal_cpu_idle();
        hal_ledOn( 6 );

    }

    // End of application:
    return 0;
}

//------------------------------------------------------------------------------

Al entrar y salir del modo ocioso se acciona sobre un LED. De este modo el LED
permanece encendido cuando la CPU está en modo activo. Observando el brillo del LED se puede ver la actividad de la CPU.
Cuando se programa esta aplicación el LED permanece apagado. Solo al incrementar la actividad en el puerto serie el LED brilla más.

 

Sistema Cooperativo y Tiempo Real

El conjunto protohebras y este planificador forman un marco de trabajo para desarrollar con poca RAM sistemas de procesos colaborativos.  Con la librería de colas que se incluye en el proyecto es fácil realizar un paso de mensajes entre procesos. El driver timer provee de los métodos para que los procesos tengan percepción del paso del tiempo.

Éste no es un planificador orientado a sistemas en tiempo real debido a que no se les puede asignar prioridades a las tareas. Pero esto no impide poder desarrollar sistemas en tiempo real con él. En sistemas alimentados por baterías la CPU está dormida casi el 100% del tiempo. Es por ello que suele ser fácil satisfacer los requisitos temporales.

Deja un comentario