Principio de multitarea. Desterrando a los delays.

Sin dudas. Doy fé de que el mantenimiento es muy complicado. Solo usando C con variables locales ya se ganó muchísimo a la hora de mantener el código.
De todos modos con un despachador de tareas necesitas programar bastante parecido; con código que no pare, que pase de largo en los problemas y deje banderas o algo de lo que ha pasado para la próxima; si paras a esperar a algo ya no funciona el sistema.
Otra opción es que el despachador de tareas sea una interupción del timer que corte la tarea esté donde esté, guarde una "foto" de que estaba pasando, registros, punteros etc y salte a otra tarea. Eso lo sabría hacer en ensamblador pero no lo sabría hacer en C. Supongo que hay compiladores que ya llevan esto incorporado.
 
De todos modos con un despachador de tareas necesitas programar bastante parecido; con código que no pare, que pase de largo en los problemas y deje banderas o algo de lo que ha pasado para la próxima; si paras a esperar a algo ya no funciona el sistema.
Por eso mencionaba la cola de mensajes: un mensaje marca la ocurrencia de algun suceso y luego el despachador dispara la tarea asociada a ese evento cuando le llegue el turno (hasta es posible priorizar los mensajes). Ahora no quedan flags descolgados ni cosas raras, una ISR solo manda un mensaje entendible por el desarrollador (que no es mas que una constante con nombre en un #define), previa atencion de la interrupcion y el proceso inmediato de sus datos (por ejemplo, completar una conversión y meter el resultado en un buffer)

Otra opción es que el despachador de tareas sea una interupción del timer que corte la tarea esté donde esté, guarde una "foto" de que estaba pasando, registros, punteros etc y salte a otra tarea. Eso lo sabría hacer en ensamblador pero no lo sabría hacer en C.
Eso es un despachador preemptivo, y sí... es bastante mas complejo. Dudo que haya compiladores que lo traigan, pero ya tenemos el FreeRTOS que hasta donde sé, maneja todo eso, pero no está disponible para los PICs "chicos" de las series 16 y 18.
 
....
Eso es un despachador preemptivo, y sí... es bastante mas complejo. Dudo que haya compiladores que lo traigan, pero ya tenemos el FreeRTOS que hasta donde sé, maneja todo eso, pero no está disponible para los PICs "chicos" de las series 16 y 18.

CCS tiene su RTOS y corre en los PIC de la serie 16.

El primer ejemplo que activaba leds con diferentes períodos quedaría con este aspecto:

#include <16F628A.h> #use delay(clock=4000000) #use rtos(timer=1,minor_cycle=100ms) #task(rate=1000ms,max=50ms) void tarea1() { output_toggle(pin_a0); } #task(rate=900ms,max=50ms) void tarea2() { output_toggle(pin_a1); } #task(rate=800ms,max=50ms) void tarea3() { output_toggle(pin_a2); } void main() { rtos_run(); }

Aunque jamás me puse a investigar respecto a su eficiencia consumo de recursos. Es mas, jamás lo he usado :)
 
Por lo que he leído en este archivo, el RTOS del CCS es cooperativo, no preemptivo.
Exactamente el RTOS de CCS es cooperativo.

Para esta segunda parte propongo un ejemplo simple el cual efectuá el parpado de dos leds.

El primer led tiene un periodo de 1s, este se queda encendido 100ms. Y el resto del tiempo se queda apagado.

El segundo led tiene in periodo de 1s, este se enciende dos veces, el tiempo que se queda encendido cada vez es de 100ms. Y el resto del tiempo permanece apagado.

El objetivo en efectuar un programa que mantenga a las tareas en distintos estados de modo que se puedan atender otras tareas en medio de la ejecución de una.

Se opta por tres modos de resolver este problema y comparar cantidad de recursos que se consume. Básicamente RAM y ROM.

Primer modo. Este el modo con el que se inició al principio de este post.
C:
/*****************************************************************************/
#include <16F84a.h>
/*****************************************************************************/
#FUSES PUT                      //Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
/*****************************************************************************/
#use delay(crystal=20000000)
/*****************************************************************************/
#USE TIMER(TIMER=0,TICK=10ms,BITS=16,NOISR)
/*****************************************************************************/
void tarea1()
{
   static unsigned int8 estado_tarea1;
   switch(estado_tarea1)
   {
      case 0: output_high(pin_a0);estado_tarea1++;break;
      case 1:output_low(pin_a0);estado_tarea1++;break;
      case 9: estado_tarea1=0;break;
      default: estado_tarea1++;
   }
}
/*****************************************************************************/
void tarea2()
{
   static unsigned int8 estado_tarea2;
   switch(estado_tarea2)
   {
      case 0:output_high(pin_a1);estado_tarea2++; break;
      case 1:output_low(pin_a1);estado_tarea2++;break;
      case 2:output_high(pin_a1);estado_tarea2++;break;
      case 3:output_low(pin_a1);estado_tarea2++; break;
      case 9:estado_tarea2=0;break;
      default:estado_tarea2++;
   }
}
/*****************************************************************************/
void main()
{
   unsigned int16 tick,tick_tarea1=0,tick_tarea2=0;
   while(true)                            
   {
      tick=get_ticks();  
      if((tick-tick_tarea1)>TICKS_PER_SECOND/10)
      {
         tick_tarea1=tick;
         tarea1();
      }
      if((tick-tick_tarea2)>TICKS_PER_SECOND/10)
      {
         tick_tarea2=tick;
         tarea2();
      }
   }
}
/*****************************************************************************/
1.png

Este segundo método tiene como base la propuesta de Dr. Zoidberg usando punteros a funciones y estructuras, lamentablemente el modo en que logre implementarlo ocupa mucho espacio tanto en la RAM como en la ROM. Seguramente PICs de mayor cantidada de memoria no habria mucho problema y probablemete exista un mejor método para lograrlo pero por el momento no lo encontré.

C:
#include <16f84a.h>
#fuses nowdt,noprotect,put
#use delay(crystal=20M)
/*****************************************************************************/
#include "setjmp.h"
#use timer(timer=0,tick=10ms,bits=16,noisr)
typedef void(*punteroFuncion)();
typedef struct {
                  punteroFuncion pf;
                  unsigned int16 periodo;
                  unsigned int16 tickCont;
                  jmp_buf despachador;
                  jmp_buf tarea;
                  char bandera;
               }TAREA;
unsigned int8 i;
/*****************************************************************************/
void tarea1();
void tarea2();
void tarea3();
TAREA tareas[]={
               {tarea1,TICKS_PER_SECOND/10,0}, //crea la tarea1,aprox 100ms,0->la tarea inicia en aprox 100ms
               {tarea2,TICKS_PER_SECOND/10,0}//crea la tarea2,aprox 100ms,0->la tarea inicia en aprox 300ms
               };
/*****************************************************************************/
void despachadorTareas(TAREA *tareas,unsigned int8 tCant)
{
   punteroFuncion pf1;
   unsigned int16 tick;

   for(i=0;i<tCant;i++)
   {
      (tareas+i)->tarea[0]=0;
      (tareas+i)->tarea[1]=0;
      (tareas+i)->bandera=0;
   }
   while(true)
   {
      tick=get_ticks();
      for(i=0;i<tCant;i++)
      {
         if((tick-(tareas+i)->tickCont)>(tareas+i)->periodo)
         {
            (tareas+i)->tickCont=tick;//actualiza los ticks          
           
            if((tareas+i)->bandera==0)
            {
               if(setjmp((tareas+i)->despachador)==0)
               {
                  pf1=(tareas+i)->pf;
                  pf1();            
               }
            }
            else
            {
               (tareas+i)->bandera=0;
               longjmp((tareas+i)->tarea,1);
             
            }
         }
      }
   }
}
/*****************************************************************************/
#inline
void yield(TAREA *tareas)
{
    if(setjmp((tareas+i)->tarea)==0)
    {
      (tareas+i)->bandera=1;
      longjmp((tareas+i)->despachador,1);
    }
}
/*****************************************************************************/
void tarea1()
{
   static unsigned int8 s;
   output_high(pin_a0);
   yield(&tareas);
   output_low(pin_a0);
   for(s=0;s<8;yield(&tareas),s++);
}
/*****************************************************************************/
void tarea2()
{
   static unsigned int8 s;
   output_high(pin_a1);
   yield(&tareas);
   output_low(pin_a1);
   yield(&tareas);
   output_high(pin_a1);
   yield(&tareas);
   output_low(pin_a1);
   for(s=0;s<6;yield(&tareas),s++);
}
/*****************************************************************************/
void main()
{
   despachadorTareas(tareas,2);     //llama al despachador de tareas, 2 es la
                                    //cantidad de tareas creadas
}
2.png

Este último es usando el RTOS de CCS.

C:
#include <16F84a.h>
/*****************************************************************************/
#FUSES PUT                      //Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
/*****************************************************************************/
#use delay(crystal=20000000)
/*****************************************************************************/
#use rtos(timer=0, minor_cycle=10ms)
#task(rate=100ms)
void tarea1();
#task(rate=100ms)
void tarea2();
/*****************************************************************************/
void tarea1()
{
   static unsigned int8 n;
   output_high(pin_a0);
   rtos_yield();
   output_low(pin_a0);
   for(n=0;n<8;rtos_yield(),n++);
}
/*****************************************************************************/
void tarea2()
{
   static unsigned int8 n;
   output_high(pin_a1);
   rtos_yield();
   output_low(pin_a1);
   rtos_yield();
   output_high(pin_a1);
   rtos_yield();
   output_low(pin_a1);
   for(n=0;n<6;rtos_yield(),n++);
}
/*****************************************************************************/
void main()
{
   rtos_run();
}
/*****************************************************************************/
3.png

La implementación de múltiples tareas es totalmente posible en los micro controladores, solo es cuestión de elegir las herramientas que mejor se ajunten a nuestros requerimientos.
 
Bue.....pero el primer ejemplo y el segundo son completamente diferentes en cuanto al mecanismo de despacho.
En la segundo, la inicializacion estatica de las estructuras es bastante dudosa, y la presencia del yield complica la comparacion (pero esta muy bien hecho (y)) y el codigo.

Separando el proceso en mas funciones vinculadas por variables globales reduce mucho el lio de codigo y elimina el yield.

Yo tengo armado para Arduino la primer version que subi, el codigo me ocupa 962 bytes y las variables solo 32 bytes.
 
Perdón, pero, ¿por qué hay tantas llamadas a rtos_yield()?

Según el manual, se debe usar cuando se quiere ceder el control a rtos_run(). Por ejemplo, al final de la tarea. Pero en este caso, ¿son necesarias 9 llamadas en cada tarea?
 
Perdón, pero, ¿por qué hay tantas llamadas a rtos_yield()?
Según el manual, se debe usar cuando se quiere ceder el control a rtos_run(). Por ejemplo, al final de la tarea. Pero en este caso, ¿son necesarias 9 llamadas en cada tarea?
yield es una forma no-muy-eficiente de devolver el control al despachador cuando no tenés nada mas que hacer y podés esperar a que te toque el turno de ejecución nuevamente.
El problema con los nuevos ejemplos es que requieren dos timers diferentes: uno para encender el LED y otro para apagarlo en un tiempo diferente y menor que el primero, de esa forma el primero define el período de encendido y el segundo la duración del encendido. YO hubiera hecho algo diferente: una función para encender el LED y otra para apagarlo, cada una con su timer y tratar de sincronizarlas con una variable global. En el caso del rtos esto es muy fácil con el rtos_wait o con el rtos_await. En el caso del despachador es mas complejo por que no tenemos una API como la de rtos, aunque con el yield de Saint_ y un poco de imaginación podría funcionar.
 
Mi pregunta era porque la solución de Eduardo en #43 parece mínima, elegante y correcta, aún sin llamar a rtos_yield().

Bueno, lo que dice Saint_: hay modos distintos.
 
Acá les traigo una versión de este minikernel cooperativo para Arduino.
El ejemplo es el primero que trajo Saint_ y este es el video:

Este es el código:
C:
#include "CompuIIBoard.h"
#include "dispatcher.h"

#define TICK_PERIOD_MS  10

byte LEDs_stat = 0;

void tarea1() {
  LEDs_stat ^= 0x01;
  writeLEDs( LEDs_stat );
}

void tarea2() {
  LEDs_stat ^= 0x04;
  writeLEDs( LEDs_stat );
}

void tarea3() {
  LEDs_stat ^= 0x10;
  writeLEDs( LEDs_stat );
}

void setup() {
  // put your setup code here, to run once:
  // Acá iniciamos el timer que cuenta los ticks
  initBoardCompuII();
  writeLEDs( LEDs_stat );
  use_timer1( TICK_PERIOD_MS );
}

void loop() {
  // put your main code here, to run repeatedly:
  // No tiene mucho caso por que el dispatcher es un loop infinito, pero bue...
  // para que quede según el estandard Arduino
  TASK tareas[] = {
      createTask( tarea1, 10 ),
      createTask( tarea2, 30 ),
      createTask( tarea3, 100 )
      };

  runDispatcher( tareas, 3 );
}

Y les dejo todos los archivos.
Aún debo verificar la configuración del Timer 1, pero hasta donde pude medir el tiempo funciona bien. Si hay alguna modificación lo subo de nuevo.

El Timer 1 del Atmega328 corre en modo CTC y la frecuencia del cristal está dividida correctamente por 10^6 como debe ser. La ISR del timer 1 se dispara por comparación y solo incrementa el tick_timer del sistema (es un unsigned int así que pueden poner tiempos muuuy largos a cada tarea). También se incluye un macro llamado createTask que permite reducir la cantidad de valores al inicializar el arreglo de tareas solo al nombre de la tarea y a la cantidad de ticks a la cual se ejecuta.
La autoconfiguración del preescaler está tomada de Google Code Archive - Long-term storage for Google Code Project Hosting. y por lo tanto toda la biblioteca tiene licencia GPL3 y no se aplica la Creative Commons del foro.
 

Adjuntos

  • test-dispatcher.zip
    2.9 KB · Visitas: 3
Esperando que se me termine de arreglar la rodilla, estaba medio sin hacer nada y me dije:
"Voy a arreglar el despachador para que funcione con cualquier Timer del Arduino (0, 1 0 2) ya que estaba fijo solo para el Timer 1".
Bueno...nada muy evolucionado. Solo un poco de copiar y pegar lo del Timer 1, arreglar algunos registros y poner una parva de directivas de compilación condicional para minimizar la cantidad de código generado.
El unico nombre de función "visible" que ha cambiado es use_timer1 que ahora se llama use_timer.
Tampoco hace falta contar cuantas tareas ponemos en la lista, ya que hay un macro endTaskList() que pone en la lista los valores necesarios para que el despachador se avive donde termina la lista.
Por ultimo, y culpa del IDE de Arduino, hay una definición del timer en el archivo dispatcher.h. Esto no es mas que un macro que puede valer:
#define USE_TIMER0 ó
#define USE_TIMER1 ó
#define USE_TIMER2
tienen que poner el que corresponda para seleccionar el timer que quieren usar y el compilador se encarga de arreglar todo lo demás para que el despachador funcione con el timer elegido.

Les dejo el mismo ejemplo de antes con los pequeños cambios y un .zip con todos los archivos.
C:
#include "CompuIIBoard.h"
#include "dispatcher.h"

#define TICK_PERIOD_MS  10

byte LEDs_stat = 0;

void tarea1() {
  LEDs_stat ^= 0x01;
  writeLEDs( LEDs_stat );
}

void tarea2() {
  LEDs_stat ^= 0x04;
  writeLEDs( LEDs_stat );
}

void tarea3() {
  LEDs_stat ^= 0x10;
  writeLEDs( LEDs_stat );
}

void setup() {
  // put your setup code here, to run once:
  // Acá iniciamos el timer que cuenta los ticks
  initBoardCompuII();
  writeLEDs( LEDs_stat );
  use_timer( TICK_PERIOD_MS );
}

void loop() {
  // put your main code here, to run repeatedly:
  // No tiene mucho caso por que el dispatcher es un loop infinito, pero bue...
  // para que quede según el estandard Arduino
  TASK tareas[] = {
      createTask( tarea1, 5 ),
      createTask( tarea2, 15 ),
      createTask( tarea3, 50 ),
      endTaskList()
      };

  runDispatcher( tareas );
}

y este es el dispatcher.h
C:
#ifndef DISPATCHER_H
#define DISPATCHER_H

#include <Arduino.h>

#define USE_TIMER0

#define createTask( fptr, tickCnt )   { fptr, tickCnt, 0 }
#define endTaskList()                 { NULL, 0, 0 }

typedef void (*taskPtr)();

typedef struct {
    taskPtr ftask;
    unsigned int ticks;
    unsigned int ticksCnt;
} TASK;

void use_timer( int tickPeriod );
void runDispatcher( TASK tasks[] );
unsigned int get_ticks();

#endif
 

Adjuntos

  • test-dispatcher-multi.zip
    3.3 KB · Visitas: 5
Para los que siempre preguntan como se hace, acá les dejo la imitación del "Auto Fantástico" hecho con la simple función barreLED() y el kernel(cito) multitarea. Esto mismo se puede hacer muy estúpidamente con un lazo for y un delay(), pero no queda tiempo de CPU para hacer nada mas. De esta otra forma pueden controlar las luces, las puertas o lo que se les ocurra y es tanto o mas simple que el otro.

C:
#include "CompuIIBoard.h"
#include "dispatcher.h"

#define TICK_PERIOD_MS  300
#define UP    1
#define DOWN  0

byte LEDs_stat = 0;

void barreLED() {
  static byte i = 0, up = UP;  // = 0

  LEDs_stat = _BV( i );
  writeLEDs( LEDs_stat );
  if( i >= 7 ) up = DOWN;
  else if( i <= 0 ) up = UP;
  if( up == UP ) i++;
  else i--;
}

void setup() {
  // put your setup code here, to run once:
  // Acá iniciamos el timer que cuenta los ticks
  initBoardCompuII();
  writeLEDs( LEDs_stat );
  use_timer( TICK_PERIOD_MS );
}

void loop() {
  // put your main code here, to run repeatedly:
  // No tiene mucho caso por que el dispatcher es un loop infinito, pero bue...
  // para que quede según el estandard Arduino
  TASK tareas[] = {
      createTask( barreLED, 5 ),
      endTaskList()
      };

  runDispatcher( tareas );
}

Y un video de como queda:



WARNING!!!
En el ejemplo el tiempo de barrido no coincide con la realidad por que estaba configurado el Timer2 que no puede interrumpir cada 300ms y la autoconfiguración lo pone al máximo tiempo que dá que no coincide con lo programado. OJO!!! Para esto hay que usar el Timer1 o en su defecto poner un menor timer-tick y una mayor cantidad en la cuenta de ticks de la tarea. Algo como esto:

C:
#include "CompuIIBoard.h"
#include "dispatcher.h"

#define TICK_PERIOD_MS  1 //OJO que va con el Timer2!!
#define UP    1
#define DOWN  0

byte LEDs_stat = 0;

void barreLED() {
  static byte i = 0, up = UP;  // = 0

  LEDs_stat = _BV( i );
  writeLEDs( LEDs_stat );
  if( i >= 7 ) up = DOWN;
  else if( i <= 0 ) up = UP;
  if( up == UP ) i++;
  else i--;
}

void setup() {
  // put your setup code here, to run once:
  // Acá iniciamos el timer que cuenta los ticks
  initBoardCompuII();
  writeLEDs( LEDs_stat );
  use_timer( TICK_PERIOD_MS );
}

void loop() {
  // put your main code here, to run repeatedly:
  // No tiene mucho caso por que el dispatcher es un loop infinito, pero bue...
  // para que quede según el estandard Arduino
  TASK tareas[] = {
      createTask( barreLED, 150 ),
      endTaskList()
      };

  runDispatcher( tareas );
}

Mejor que lo hagan y prueben ustedes. Luego, si tengo ganas, le pongo un warning al compilador para que les avise de esta situación.

WARNING_2!!!
También les subo una nueva versión del dispatcher.cpp que asegura la lectura atómica de la variable tickCounter (en la función get_ticks() )que es actualizada por las interrupciones del timer elegido. ES IMPORTANTE QUE SIEMPRE USEN ESTA NUEVA VERSIOOOOONNNNN!!!!!
 

Adjuntos

  • knight-2000.zip
    619 bytes · Visitas: 4
  • dispatcher.cpp.zip
    1.4 KB · Visitas: 6
Última edición:
Dr. Zoidberg, un muy buen trabajo el que hiciste con el dispatcher.ccp que implementaste y al más puro estilo de avr-gcc.

También hice ligeros cambios al despachador de tareas en ccs PicC para simplificar un poquito más la creación de tareas.
el siguiente ejemplo es el mismo que subiste (el de la imitación del auto fantástico). Los leds están conectados a puerto B del pic16f628a

main.c
C:
/*****************************************************************************/
#include <main.h>
/*****************************************************************************/
void barrerLed()
{
   const unsigned int8 secuencia[]={1,2,4,8,16,32,64,128,64,32,16,8,4,2};
   static unsigned int8 n;
   output_b(secuencia[n]);
   if(++n>13){n=0;}
}
/*****************************************************************************/
void main()
{
   TAREA tareas[]={
                  crearTarea(barrerLed,TICKS_PER_SECOND/10)//crea la tarea1,aprox 100ms
                  };
   despachadorDeTareas(tareas);
}
/*****************************************************************************/
main.h
C:
/*****************************************************************************/
#include <16F628.h>
/*****************************************************************************/
#FUSES PUT                      //Power Up Timer
#FUSES NOMCLR                   //Master Clear pin used for I/O
#FUSES BROWNOUT                 //Reset when brownout detected
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES NOPROTECT                //Code not protected from reading
/*****************************************************************************/
#use delay(internal=4MHz)
/*****************************************************************************/
//!#define temporizador 0
//!#define  tick_ms 10ms
//!#define  nBits 8
#include "tareas.c"
/*****************************************************************************/
tareas.c
C:
/******************************************************************************
Parámetros configurables:
*******************************************************************************
#define temporizador, este puede tomar 3 valores 0,1,2.

Ejemplo:
#define temporizador   0   //usa el temporizador 0 para el contero de los ticks
#define temporizador   1   //usa el temporizador 1 para el contero de los ticks
#define temporizador   2   //usa el temporizador 2 para el contero de los ticks

Si no es declarado, usa el temporizador 1.
*******************************************************************************
#define tick_ms, define el tiempo transcurrido en cada tick

Ejemplo:
#define   tick_ms      1   //1 milisegundo por cada tick
#define   tick_ms      15   //15 milisegundos por cada tick

Si no se declara, usa 1 milisegundo como tiempo para cada tick
*******************************************************************************
#define nBits, define la cantidad de bits de retorno para la función get_ticks();
sus valores pueden ser 8,16,32.

Ejemplo:
#define nBits   8   //usa 8 bits (0-255) para la función get_ticks();
#define nBits   16   //usa 8 bits (0-65535) para la función get_ticks();
#define nBits   32   //usa 8 bits (0-4294967295) para la función get_ticks();

Si no se define usa 16 bits para la función get_ticks();
/*****************************************************************************/
#ifndef  temporizador
   #define  temporizador 1
#endif
/*****************************************************************************/
#ifndef  tick_ms
   #define  tick_ms 1ms
#endif
/*****************************************************************************/
#ifndef  nBits
   #define  nBits 16
   #define  tipoVariable unsigned int16
#else
   #if nBits==8
      #define  tipoVariable unsigned int8
   #elif nBits==16
      #define  tipoVariable unsigned int16
   #elif nBits==32
      #define  tipoVariable unsigned int32
   #else
      #error  nBits, solo puede ser 8,16 o 32
   #endif
#endif
/*****************************************************************************/
#use timer(timer=temporizador,tick=tick_ms,bits=nBits,noisr)
/*****************************************************************************/
typedef void(*punteroFuncion)();
typedef struct {
                  punteroFuncion pf;
                  tipoVariable periodo;
                  tipoVariable tickCont;
               }TAREA;
/*****************************************************************************/
#define crearTarea(pf,periodo) {pf,periodo,0}
#define despachadorDeTareas(tareas) despachador(tareas,sizeof(tareas)/(2+2*nBits/8))
/*****************************************************************************/
void despachador(TAREA tareas[],tCant)
{
   tipoVariable tick;
   unsigned int8 i;
   while(true)
   {
      tick=get_ticks();
      for(i=0;i<tCant;i++)
      {
         if((tick-tareas[i].tickCont)>tareas[i].periodo)
         {
            tareas[i].tickCont=tick;
            tareas[i].pf();
         }
      }
   }
}
/*****************************************************************************/
 

Adjuntos

  • ejemplo.rar
    46.9 KB · Visitas: 9
Me gusta tu propuesta (y)(y)(y)
Solo que dejaría a get_ticks que siempre retorne un valor del mismo sizeof. Total, es una variable que se actualiza en una ISR , así que puede ser de 16 bits sin problemas.... es una idea.

Dr. Zoidberg, un muy buen trabajo el que hiciste con el dispatcher.ccp que implementaste y al más puro estilo de avr-gcc.
Naaaa......es puro C... solo usé recursos del avr-gcc para la ISR y para la lectura atómica de tickCounter.
PD: No quise usar un arreglo de constantes para no chupar tanta memoria de datos, pero va muy bien si la tenés disponible :excelente:

Ya que se establicen ambos despachadores, veremos si podemos integrarlos en un solo paquete para cualquier micro PIC o ATMEL.
Por ahora, estoy pensando como agregar una función delay() que use el sistema de despacho y no los busy-wait del avr-gcc o del ccs.
 
Última edición:
Solo que dejaría a get_ticks que siempre retorne un valor del mismo sizeof. Total, es una variable que se actualiza en una ISR , así que puede ser de 16 bits sin problemas.... es una idea.
Si creo que no habría ningún problema.
Ya que se establicen ambos despachadores, veremos si podemos integrarlos en un solo paquete para cualquier micro PIC o ATMEL.
Sería muy interesante, pero habría que definir algunos detalles, por ejemplo:

Para que familias de PIC y AVR, para que compiladores (CCS, MikroC, AVRGCC, arduino).

Otro detalle es si se pretende hacer una alternativa simple a los RTOS existentes (RTOS CCS, OSA para MikroC y FreeRtos para Avr) o tratar de competir.

Desde mi punto de vista seria generar la alternativa simple.

En cuanto al despachador de tareas, en CCS en imprescindible usar la constante TICS_PER_SECOND ya que la directiva #use timer(…) no logra hacer un cálculo muy preciso del tiempo, por ejemplo si se elige un tiempo de 1ms y dependiendo de la velocidad del oscilador pude que ese tiempo sea al final 1.2ms. Para corregir ese erros es que se genera la constante TICS_PER_SECOND que guarda el valor real de tics que suceden en un segundo.

Aunque para solucionar esto y tratar de uniformar el método de creación de tareas y su respectivo tiempo de despacho podría elegirse un tiempo fijo por ejemplo 1ms y las tareas seria despachadas en un múltiplo de este tiempo.
Por ahora, estoy pensando como agregar una función delay() que use el sistema de despacho y no los busy-wait del avr-gcc o del ccs.
Seria genial, obviamente lo complicado es retornar a la instrucción siguiente de donde se llamó a la función.
 
Desde mi punto de vista seria generar la alternativa simple.
Totalmente de acuerdo. Los RTOS existentes, en especial los preemptivos, requieren conocer algunas técnicas para semaforización y para evitar dead-locks que si bien no son complicadas, por lo general están totalmente fuera del alcance del común de los programadores (léase: los que hacen copy & paste sin entender nada de lo que sucede).
Creo que acá la idea es un conjunto de primitivas para terminar ya con los famosos delay() y el consumo inútil de tiempo de CPU en las tareas que requieren una ejecución periódica y "aparentemente" concurrente.
En cuanto al despachador de tareas, en CCS en imprescindible usar la constante TICS_PER_SECOND ya que la directiva #use timer(…) no logra hacer un cálculo muy preciso del tiempo, por ejemplo si se elige un tiempo de 1ms y dependiendo de la velocidad del oscilador pude que ese tiempo sea al final 1.2ms. Para corregir ese erros es que se genera la constante TICS_PER_SECOND que guarda el valor real de tics que suceden en un segundo.
Ese es un problema que ocurre en cualquier microprocesador/microcontrolador. Si la frecuencia de reloj no es un múltiplo exacto del tick del timer, la constante te va a dar en punto flotante... pero no la podés usar así por que el timer y el kernel cuentan en enteros, y tarde o temprano vas a perder presición en el intervalo del tick: la única diferencia es el error final que vas a tener.
De todas maneras yo no me preocuparía mucho por esto por que el despacho es en una suerte de very-soft-real-time y sobre todo depende de la carga de tiempo que usa cada tarea en la secuencia que se despacha, así que muuuucha presición no vas a tener.
Seria genial, obviamente lo complicado es retornar a la instrucción siguiente de donde se llamó a la función.
Ya tengo algo hecho, pero estoy teniendo problemas por intentar minimizar el consumo de memoria...
 
Última edición:
Seria genial, obviamente lo complicado es retornar a la instrucción siguiente de donde se llamó a la función.
Dando vueltas por la web (léase "webiando") encontré uno de los pocos videos de multitarea en Arduino que vale la pena:
Y lo mas valioso es la referencia a la "biblioteca" protothreads que es una EXCELENTE herramienta para hacer multitarea cooperativo en cualquier plataforma que tenga disponible un compilador de lenguaje C.
En el link está el sitio de donde descargarla, pero les recomiendo leer como está hecha esta obra de arte por que es increíble como usa una "trampa" que tienen los compiladores de C y casi usa cero recursos extra.

Yo la he probado en Arduino y en la PC usando Code::Blocks con compilador gcc en 64 bits...y anda a la perfección y sin cambios.

Se las recomiendo muy seriamente por que es una herramienta MUY buena....y vean el video por que es bastante demostrativo.
 
Atrás
Arriba