[Tutorial] FreeRTOS

Cosme, muy, pero muy bueno este tutorial! una lástima que no lo hayas continuado. Evidentemente en su momento no había muchas respuestas... de casualidad tendrás algo de freeRTOS con ejemplos, como lo que estabas haciendo acá?

Saludos!

Gracias.

Lamentablemente nunca llegué hacer algo práctico con el freetos, además de jugar un rato y aprender a usarlo por si en un futuro me es útil.
 
Debido a serios problemas de salud y por estar pensando con muchas vueltas mi intención de usar el So FreeRTOS se ha demorado. Actualmente estoy poniéndome al día usando la placa RaspBerry Pi B+ y un libro que me conseguí para aprender y practicar los usos de los pines GPIO del RaspBerry Pi B+ haciendo experimentos electrónicos. Cuando esté al día con eso voy a iniciar lo mismo con la LPCXpresso1769 usando la IDE de NXP. Entonces será mi punto de entrada al FreeRTOS en la LPCXpresso1769 siguiendo los 2 libros que el autor de FreeRTOS hace disponibles para comprar en su sitio de Internet para el LPC1769. Como la placa de evaluación para el controlador de motores de paso usa un miembro de la familia MK20, un ARM Cortex M4 entonces tendré que meterme con la IDe de Keil para transferir las partes que son de mi interés al LPC1769. Si Dios me lo permite eso me tendrá entretenido y fascinado con seguridad el 2016 completo y mas allá! en su momento daré reporte sobre avances relevantes aquí!
 
Hola Cosme.

Llevo ya dos semanas buscando ayuda por internet y por fin te encuentro!

Estoy utilizando el sistema FreeRTOS en un sistema empotrado y tengo un problema con las tareas.

El programa base que tengo funciona muy bien, pero más tarde modifiqué una tarea de las que tenía por otra un poco más pesada. Desde entonces esta tarea me está monopolizando el procesador y hace que el resto de tareas se ejecuten menos veces.

Hay ciertos detalles que aún no tengo claros sobre el sistema operativo, como por ejemplo, si a una tarea se le asigna un número determinado de ticks y se ejecuta en menos ticks, pasaría a ejecutarse otra tarea?

Me estoy volviendo loca y creo que el problema está por ahí, en la prioridad de las tareas.

Si me pudieses ayudar te lo agradecería muchísimo!!!

Gracias de antemano :)
 
Las tareas deberán tener el mismo nivel de prioridad para repartirse los ticks en forma equitativa. Si no me equivoco, por default un tick equivale a 1mS de ejecución.

Fijate en el mensaje #4, que tengo dos tareas con el mismo nivel de privilegio, no importa lo que ejecute c/tarea, el scheduler se encargará de asignarle el mismo tiempo de ejecución a c/tarea.

Si podés, subí algo del código y lo vemos.
 
Cosme muy buen tutorial... Estoy aprendiendo sobre FreeRTOS ya que necesito implementar un grabador digital de audio con salida a SD en una EDU-CIAA.
Mi intención es usar 3 tareas: adquisición, procesamiento y almacenado. De las cuales, la de adquisición tendra la mayor prioridad.
Cualquier sugerencia será bienvenida.
Muchas gracias por tomarte el tiempo en escribir este tuto.

Saludos desde BA.
 
Ha salido una nueva versión de la IDE de NXP que ahora también abarca los controladores de la antigua Freescale. Mi salud sigue jodiendo y mis experimentos se me bloquearon cuando alguien del Internet me atacó y me rindió mi PC inoperable! Por eso, antes de seguir como lo describí mas arriba estoy estudiando e implementando el entorno de mi taller informático aplicando las cosas que he descubierto para mi para lograr un entorno lo mas seguro posible y de aislar por si un atacante tiene éxito a pesar de todos mis esfuerzos!

El primer paso es instalar usando la herramienta "VirtualBox" una máquina virtual e instalar en ella Ubuntu 16.04.1 en la versión de 64 bits. Resultó que VirtualBox solo ofrece la alternativa de os con 64 bits cuando en mi os del PC, Windows 10 Pro, desactivo los servicios del Windows 10 "Hyper-V". Al momento estoy recapitulando lo estudiado previamente sobre Linux y Ubuntu para configurar Ubuntu en la máquina virtual de forma apropiada! Voy a asignar 128 GBytes de disco dura a la máquina virtual y ahora estoy recapitulando de que tamaño las diversas particiones del disco duro mejor.

Después intentare a ver si puedo activar la máquina virtual ya configurada reactivando "Hyper-V" en el os Windows 10 Pro, pues eso es requisito para poder activar Docker que uso para explorar la materia de contenedores en el entorno del PC. También instalaré Docker en Ubuntu en mi máquina virtual!
 
Lo que pasa es que Hyper-V es un gestor de maquinas virtual de Windows, si activas dicha función toma la aceleración de hardware lo que hace imposible al VirtualBox usar esos recursos, por ello piden que la elimines.

Por cierto, podrías intentar usar la maquina virtual de Windows directamente, usas el Administrador de Hyper-V
 
Última edición:
Hola amigo ante todo saludos y gracias por su tiempo. Hoy tengo una pregunta que he tratado de resolver con el buscado y debo admitir que hay respuesta del tema pero aun no me quedan claras.
Mi problema: tengo un módem sim900 conectado a un pic16f877a por el modo rs232 (C7y C6). Dicho módem esta Programado para que los SMS o cualquiera actividad del módem lo bajen directo al puerto del pic tan pronto el módem lo procesa.
Mi programa lo apaño con una interrupción(int_rda), pero ademas mi programa maneja otra interrupción la cual llevo para manejar tiempos, puesto que la misma se dispara cada segundo cargando un contador y con esto genero retardo en las actividades.
Pero estoy notando que no hay efectividad en la recesión de datos proveniente del módem, ya que en ocasiones si lo detecta y en otras no. Esto me da sospechas que las interrupciónes se esta bloqueando una con otra y llega el momento en que queda colgada, por eso estuve investigando y existen un cierto manejo de prioridades a nivel de interrupción, el cual noto que esta mas enfocado para los pic18 y para los pic16f toda las interrupciónes son de prioridad alta.
Para esto CCS tiene #priority que da mayor prioridad a una interrupción,
pero #priority a mi no me compila (CCS 4.032) me manda un error de que esa palabra reservada no la entiende el compilado.

En mi busque pude leer un comentario donde una persona maneja las prioridades de interrupciónes con banderas pero no lo explico, y esa es mi pregunta como se puede manejar la prioridades de interrupciónesa nivel de software con banderas, o si tienen alguna otra forma de manejar esta prioridades les agradezco que me lo comenten para probar.
 
Las interrupciones disparan el mismo vector, es responsabilidad del usuario revisar las banderas de cada una, otro problema que podrías tener sería que una bandera se dispare mientras sirves otra interrupción posterior y luego no sea verificada antes de dar por completa la rutina.

Recuerda que tienes banderas en INTCON y PIR1

En primera deshabilitas las interrupciones globales al entrar en la rutina de interrupción, las banderas son establecidas sin importar el bit de habilitación, esto te evitará entrar a la rutina nuevamente, después verifica todas las banderas que empleas y controla las subrutinas adecuadas a ellas, al final verificas nuevamente si aún hay banderas activas, en caso de no encontrar ninguna ya rehabilitas las interrupciones globales. La prioridad la estableces por el orden al verificar las banderas.
 
Hola nelsont. ccs picC gestiona automáticamente los registros intcon, pir1 y los de mas, recuerda que en la gama 16 de Pic una interrupción no puede detener otra que se esté ejecutando solo espera a que termine la interrupción previa para recién ejecutarse.
Respecto de #priority, este solo define que interrupción se atenderá primero si ubiera más de una en espera.
El problema que comentas suena más a inadecuada forma de gestión de las interrupciones. Sería bueno que subas tu código para que se pueda ver por dónde va el problema.
 
Siguiendo con la idea de bloquear temporalmente a una tarea, nos encontramos con otra función similar a vTaskDelay(), llamada vTaskDelayUntil().

La diferencia más importante entre estas dos funciones es que vTaskDelay() cuenta los “ticks” en forma relativa, desde que fue llamda la función hasta alcanzar la cantidad de “ticks” necesarios para salir del estado bloqueado. En cambio vTaskDelayUntil() cuenta los “ticks” en forma absoluta, es decir cuando la tarea abandona el estado de bloqueo y pasa a un estado preparado por un lado almacena el nº de “ticks” actual en una variable y por el otro la cantidad de “ticks” necesarios para salir del estado bloqueado, entonces cuando se llega a una cierta cantidad de “ticks” comparandolo con los “ticks” iniciales, la diferencia debería dar la cantidad de “ticks” necesarios.

Si bien suena confuso y pareciera que ambas funciones hacen exactamente lo mismo (la segunda de una manera más rebuscada por trabajar en forma absoluta), el creador del FreeRTOS afirma que usando la segunda función se obtienen tiempos más ajustados que usando la primera y recomienda su uso cuando se requieran tareas periódicas. Esto se debe a que corrige la diferencia de tiempo que se produce cuando la tarea pasa de un estado bloqueado a uno preparado.

Prototipo de vTaskDelayUntil():

PHP:
void vTaskDelayUntil( portTickType * pxPreviousWakeTime, portTickType xTimeIncrement );

  • pxPreviousWakeTime: almacena en una variable por referencia, el valor del “tick” actual donde la tarea pasa de un estado bloqueado a un estado preparado. Esta variable solo la inicializaremos al principio y luego es el propio kernel que se encarga de actualizarla.
  • xTimeIncrement: el incremento de “ticks” que necesitamos implementar (igual que en vTaskDelay()).

Ejemplo 5:

Implementar el ejemplo 4 usando vTaskDelayUntil().

PHP:
void vTaskFunction( void *pvParameters )
{
  char *pcTaskName;
  portTickType xLastWakeTime;

  /* The string to print out is passed in via the parameter. Cast this to a
  character pointer. */
  pcTaskName = ( char * ) pvParameters;

  /* The xLastWakeTime variable needs to be initialized with the current tick
  count. Note that this is the only time the variable is written to explicitly.
  After this xLastWakeTime is updated automatically internally within
  vTaskDelayUntil(). */
  xLastWakeTime = xTaskGetTickCount();

  /* As per most tasks, this task is implemented in an infinite loop. */
  for( ;; )
   {
    /* Print out the name of this task. */
    vPrintString( pcTaskName );
    /* This task should execute exactly every 250 milliseconds. As per
    the vTaskDelay() function, time is measured in ticks, and the
    portTICK_RATE_MS constant is used to convert milliseconds into ticks.
    xLastWakeTime is automatically updated within vTaskDelayUntil() so is not
    explicitly updated by the task. */
    vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );
   }
}

La salida y el comportamiento temporal es exactamente el mismo que en el ejemplo 4.

Código en Cortex-M3:

PHP:
#include <LPC17xx.H>

#include "FreeRTOS.h"
#include "task.h"

#include "defines.c"

//------- Variables globales para las interrupciones -------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,flag_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones -------------//

#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"

void vTask( void *pvParameters )
{      
    portTickType xLastWakeTime;
  
    // xTaskGetTickCount() devuelve la cuenta de ticks actuales -> Referencia inicial
    xLastWakeTime = xTaskGetTickCount();
      
    while(1)
    {
        enviar_string_uart0((u8 *)(pvParameters));//Imprimir Tarea!
      
        vTaskDelayUntil( &xLastWakeTime, ( 250 / portTICK_RATE_MS ) );    //Fija la cantidad de ticks exactos a partir de la refencia inicial, xLastWakeTime se va actualizando =>     Ideal para tareas periodicas
    }
}

int main()
{                  
    static const char *pcTextForTask1 = "Tarea 1 esta corriendo\r\n";
    static const char *pcTextForTask2 = "Tarea 2 esta corriendo\r\n";
    portBASE_TYPE ret;
  
    configurar_pll(CLK_XTAL,25,2,3,(DIVISOR_PERIFERICOS_1<<PCLK_TIMER0),0);    // Cristal de 12MHz => M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz
          
    configurar_uart0(UART_9600); //9600bps
  
    habilitar_interrupciones();
  
    enviar_string_uart0((u8 *)("Creando Tarea1\r\n"));  
    ret=xTaskCreate(vTask,(const signed char *)("Task 1"),configMINIMAL_STACK_SIZE,(void *)pcTextForTask1,1,NULL );
    if(ret==pdTRUE)
        enviar_string_uart0((u8 *)("Se creo T1\r\n"));
    else
        enviar_string_uart0((u8 *)("No se creo T1\r\n"));
  
    enviar_string_uart0((u8 *)("Creando Tarea2\r\n"));
    ret=xTaskCreate( vTask, (const signed char *)("Task 2"),configMINIMAL_STACK_SIZE,(void *)pcTextForTask2, 2, NULL );
    if(ret==pdTRUE)
        enviar_string_uart0((u8 *)("Se creo T2\r\n"));
    else
        enviar_string_uart0((u8 *)("No se creo T2\r\n"));
  
    enviar_string_uart0((u8 *)("Llamando al Scheduler\r\n"));
    vTaskStartScheduler();

    while(1);
  
}

Ejemplo 6:

Se pide:

  • Crear dos tareas con prioridad “1” y que simplemente impriman un String. Estas tareas nunca deberán ser bloqueadas en forma temporal.
  • Crear una tarea con prioridad “2” que imprimirá un String y será bloqueada temporalmente usando vTaskDelayUntil() para que sea periódica.

Prototipo de tareas continuas:

PHP:
void vContinuousProcessingTask( void *pvParameters )
{
  char *pcTaskName;

  /* The string to print out is passed in via the parameter. Cast this to a
  character pointer. */
  pcTaskName = ( char * ) pvParameters;

  /* As per most tasks, this task is implemented in an infinite loop. */
  for( ;; )
   {
    /* Print out the name of this task. This task just does this repeatedly
    without ever blocking or delaying. */
    vPrintString( pcTaskName );
   }
}

Prototipo de la tarea periódica:

PHP:
void vPeriodicTask( void *pvParameters )
{
  portTickType xLastWakeTime;

  /* The xLastWakeTime variable needs to be initialized with the current tick
  count. Note that this is the only time the variable is explicitly written to.
  After this xLastWakeTime is managed automatically by the vTaskDelayUntil()
  API function. */
  xLastWakeTime = xTaskGetTickCount();

  /* As per most tasks, this task is implemented in an infinite loop. */
  for( ;; )
   {
   /* Print out the name of this task. */
   vPrintString( "Periodic task is running\r\n" );
   /* The task should execute every 10 milliseconds exactly. */
   vTaskDelayUntil( &xLastWakeTime, ( 10 / portTICK_RATE_MS ) );
   }
}

Salida en DOS:

Ver el archivo adjunto 97560

La salida puede ser confusa, ya que da la sensación que las tareas continuas cambian cuando se ejecuta la tarea periódica, esto no es así y se va a ver en el comportamiento temporal y en la salida Cortex-M3.

Comportamiento temporal:

Ver el archivo adjunto 97561

Viendo el comportamiento temporal y la salida en DOS obtenida, es evidente que el "tick" base y el periodo de la tarea 3 son iguales, algo que no va a suceder en el ejemplo con Cortex-M3.

Código en Cortex-M3:

PHP:
#include <LPC17xx.H>

#include "FreeRTOS.h"
#include "task.h"

#include "defines.c"

//------- Variables globales para las interrupciones -------------//
u8 flag_timer0=0,flag_timer1=0,flag_timer2=0,flag_timer3=0,flag_ext1=0,flag_ext2=0,flag_uart0_rx=0,flag_uart0_tx=1,dato_uart0,flag_rtc=0,flag_adc=0;
//------- Variables globales para las interrupciones -------------//

#include "interrupciones.c"
#include "configuracion_PLL.c"
#include "perifericos.c"
#include "funciones_uart.c"

void vTask_continua( void *pvParameters )
{
    while(1)
    {
        enviar_string_uart0((u8 *)(pvParameters));//Imprimir Tarea!      
    }
}

void vTask_periodica( void *pvParameters )
{      
    portTickType xLastWakeTime;
  
    /* xTaskGetTickCount() devuelve la cuenta de ticks actuales -> Referencia inicial */
    xLastWakeTime = xTaskGetTickCount();
  
    while(1)
    {
        enviar_string_uart0((u8 *)(pvParameters));//Imprimir Tarea!      
      
        vTaskDelayUntil( &xLastWakeTime, ( 100 / portTICK_RATE_MS ) );    //Fija la cantidad de ticks exactos a partir de la refencia inicial, xLastWakeTime se va actualizando =>     Ideal para tareas periodicas
    }
}

int main()
{                  
    static const char *texto_task_continua_1 = "T1\r\n";
    static const char *texto_task_continua_2 = "T2\r\n";
    static const char *texto_task_periodica = "Corriendo tarea periodica\r\n";
    portBASE_TYPE ret;
  
    configurar_pll(CLK_XTAL,25,2,3,(DIVISOR_PERIFERICOS_1<<PCLK_TIMER0),0);    // Cristal de 12MHz => M=25 N=2 => FCCO => CPU-CLK=100MHz y P-CLK=25MHz
          
    configurar_uart0(UART_115200); //115200bps
  
    habilitar_interrupciones();
  
    enviar_string_uart0((u8 *)("Creando Tarea1\r\n"));
    ret=xTaskCreate(vTask_continua,(const signed char *)("Tarea1"),configMINIMAL_STACK_SIZE,(void *)texto_task_continua_1,1,NULL );
    if(ret==pdTRUE)
        enviar_string_uart0((u8 *)("Se creo T1 continua\r\n"));
    else
        enviar_string_uart0((u8 *)("No se creo T1 continua\r\n"));
  
    enviar_string_uart0((u8 *)("Creando Tarea2\r\n"));  
    ret=xTaskCreate(vTask_continua,(const signed char *)("Tarea2"),configMINIMAL_STACK_SIZE,(void *)texto_task_continua_2,1,NULL );
    if(ret==pdTRUE)
        enviar_string_uart0((u8 *)("Se creo T2 continua\r\n"));
    else
        enviar_string_uart0((u8 *)("No se creo T2 continua\r\n"));
  
    enviar_string_uart0((u8 *)("Creando Tarea3\r\n"));  
    ret=xTaskCreate(vTask_periodica, (const signed char *)("Tarea3"),configMINIMAL_STACK_SIZE,(void *)texto_task_periodica, 2, NULL );
        if(ret==pdTRUE)
        enviar_string_uart0((u8 *)("Se creo T3 periodica\r\n"));
    else
        enviar_string_uart0((u8 *)("No se creo T3 periodica\r\n"));
  
    enviar_string_uart0((u8 *)("Llamando al Scheduler\r\n"));
    vTaskStartScheduler();

    while(1);
  
}

Salida obtenida del puerto serie:

Ver el archivo adjunto 97562

Subo ambos ejemplos y como en el ej. 6 al haber 2 tareas continuas (tal como se vió en los primeros ejemplos), tuve que modificar el "tick" base y hacer que el baud-rate del puerto serie sea el más veloz posible.

no se que me salio mal pero no sale de la tarea 1, el ejemplo lo segui al pie de la letra adaptado a un stm32f103, no se si sera por que estoy imprimiento en terminal por semihosting, voy a probarlo redireccionando al puerto serali
1586894009327.png
 
Arriba