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.