TODOPIC

Microcontroladores PIC => * PROYECTOS * => Mensaje iniciado por: jgpeiro06 en 24 de Marzo de 2009, 12:59:49

Título: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 24 de Marzo de 2009, 12:59:49
Hola!, me gustaría enseñaros a todos los foreros un gadjet con microcontroladores. Se trata de un emulador de un reloj Casio F91W con un basado en un PIC24 de microchip.
El sistema pretende emular a la el modelo F91W, imitando las especificaciones y el funcionamiento del  reloj Casio. El emulador dispone de 3 botones, un display y un altavoz que permiten al usuario interactuar de la misma manera que con el F91W, aunque con una interfaz totalmente distinta. Este gatjet no tiene ni pantalla ni botones, para visualizar la hora se utiliza un mecanismo similar al de los relojes "propeller clock" o "giroplay" y para detectar las teclas pulsadas se utilizan LEDs como sensores. El altavoz es piezo-eléctrico, igual que en el Casio.

El proyecto tiene unos 5 meses y la mayor parte del tiempo la he dedicado a escribir el software, con el objetivo de iniciarme con los RTOS. En proyectos anteriores me encontré con la dificultad de mantener y ampliar el código cuando el proyecto iba creciendo. Por ejemplo: Si queremos añadir una nueva funcionalidad al sistema será fácil si nuestro sistema aun es pequeño, pero será cada vez mas y mas difícil a medida que el sistema vaya creciendo. Esto no ocurre si diseñamos el sistema basado en un RTOS. En este caso, el sistema se divide en subsistemas y al RTOS se le indican las interconexiones entre ellos. Si queremos añadir una nueva funcionalidad será tan fácil como escribirla e indicar al RTOS su relación con las demás.

El sistema esta dividido en 8 tareas concurrentes comunicadas entre si con colas "queues". Cada tarea incluye objetos y librerías para efectuar su función. Las tareas que acceden a un mismo recurso poseen mecanismos de sincronización como mutex. Las tareas se pueden agrupar en cuatro grupos diferentes según su rol en el sistema: productoras, procesadoras, consumidoras y control. Cada grupo de taras posee prioridades distintas, teniendo la menor las productoras ... y la mayor la de control.

Para utilizar el gadjet el usuario debe agitar continuamente la mano para leer la información. El aparato tiene una columna de 24 LEDs y genera en el aire una imagen estable de 128*24 pixels. Incorpora un acelerómetro para sincronizar el movimiento de la mano con el barrido de la imagen y así mantenerla lo más estable posible en el aire.
Los mismos 24 LEDs se pueden poner en modo inverso y así ser usados como sensores. De este modo se emulan los 3 botones (A B y C)del Casio.

El sistema también posee un botón, un botón físico real, que no existe en el reloj Casio. Este botón permite “despertar” al reloj durante un tiempo para que el usuario interactúe con él. Además el botón comparte pinIO con el altavoz y, aunque sus funciones son totalmente incompatibles, el sistema sincroniza perfectamente el pin IO de tal manera que las dos funciones se ejecuten en “paralelo” y NUNCA se interfieran entre si.
 
Funcionamiento
Después de un reset, la función main configura todas las 8 tareas y arranca el RTOS.   En este punto solo hay dos tareas en funcionamiento: TaskButton y TaskWakeUp.
Cuando el usuario pulsa el botón, la tarea TaskButton envía un mensaje a la tarea TaskWakeUp y esta ultima despierta todas las demás tareas. Pasados 20 segundos, las vuelve a suspender.

Mientras todas las tareas están despiertas, el usuario puede interactuar con el reloj visualizando en pantalla la misma información que en el reloj original y navegando por los mismos menús.
Cuando pulsa una tecla la tarea GetLeds envía un mensaje a la tarea TaskWatch con la tecla pulsada.
La tarea TaskWatch le pasara a la función WatchProcces la tecla pulsada para actualizar el estado del reloj y después enviara a la tarea TaskGraphics todas las primitivas que componen la pantalla del reloj en el modo actual( texto, líneas, símbolos…).
La tarea TaskGraphics pintara todas las primitivas en una matriz de 128*24 y cuando termine le pasara esta matriz a la tarea TaskPutLeds.
La tarea TaskPutLeds recibirá el mensaje y esperara a que la tarea TaskAcce le indique el momento exacto para mostrar el mensaje en el aire.
La tarea TaskAcce mide constantemente la aceleración y envía un mensaje a la tarea TaskPutLeds cuando la aceleración alcanza un máximo o un mínimo.

Las tareas TasButton y TaskSpeaker comparten un recurso(un pinIO del PIC), y para evitar que ambas accedan al recurso al mismo tiempo están sincronizadas mediante un mutex. Las tareas GetLeds y PutLeds también comparten recursos(los 24 LEDs y el Mosfet) y también están sincronizadas con un mutex.
Diagrama de la aplicación
     (http://farm4.static.flickr.com/3599/3382685114_9590b408c3_o.jpg)

Aquí dejo algunas fotos:
   Este es el aspecto del circuito montado. En la parte inferior se acopla un portapilas con dos pilas tipo AAA. Tambien se puede ver como el conector ICSP tiene un formato miniUSB.
 (http://farm4.static.flickr.com/3421/3381866813_05ac1b1da7_o.jpg)


La imagen muestra la disposición de todos los elementos que se mostraran según el estado del reloj.
 (http://farm4.static.flickr.com/3632/3381867383_e50f8d00b8_o.jpg)

Esta  imagen muestra como se usan los LEDs cuando se detecta una pulsación.
 (http://farm4.static.flickr.com/3663/3382685584_550de9307f_o.jpg)
Este grafico muestra un diagrama de bloques de la tarea TaskAcce y su equivalente en C. Los bloques Avg, Diff y AND, RSFF están modelados como objetos.
 (http://farm4.static.flickr.com/3654/3381867957_c00b5b3142.jpg)

Aquí dejo algunos links
   Enlace a la guía de usuario del reloj de Casio. Aunque no es el F91W, es el modelo mas parecido del que conseguí la guía ya que no en contre justo la del F91W.
http://ftp.casio.co.jp/pub/world_manual/wat/en/qw2428.pdf
Enlace a una pagina que explica como usar un LED como sensor de Luz.
http://en.wikipedia.org/wiki/LED_as_light_sensor
   Enlace a la página del RTOS utilizado
http://www.freertos.org/   

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Nocturno en 24 de Marzo de 2009, 13:56:54
Plas, plas, plas, a tus pies, jgpeiro.

¿Publicarás el código?
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: septiembre_negro en 24 de Marzo de 2009, 14:46:03
Pues no queda más que aplaudir  :-/ :-/ :-/ ase mucho que no miraba un desarrollo tan interesante
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 24 de Marzo de 2009, 17:01:30
Aquí añado un video que muestra como se usa el aparato....

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: aitopes en 24 de Marzo de 2009, 17:01:56
Buenisimo!
Aunque no logro ver las imagenes :(
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 24 de Marzo de 2009, 20:05:55
Citar
¿Publicarás el código?
Claro!! y lo explicare para todos lo mejor que sepa....

El código esta dividido en 3 partes principales:
        El main. Este se encarga de definir la aplicación en si (tareas y su interconexión) y de arrancar el RTOS llamando a la función vTaskStartScheduler.
        Las tareas. Hay 4 tipos de tareas según su rol:
                Productoras: Cogen datos del exterior del sistema como un pinIO o el ADC y envían un mensaje con el dato leído.
                Procesadoras: Reciben un mensaje, procesan la información y reenvían los resultados en otro mensaje.
                Consumidoras: Reciben un mensaje y lo mandan al exterior del sistema como a un LED o a algún periférico externo.
                Control: Supervisan el funcionamiento del sistema “escuchando” la información que se pasan las tareas.
        Las utilidades. Aquí se agrupan todas las funciones de utilidad general necesarias para las tareas, como por ejemplo la clase Watch.c o las funciones graficas GLCD.c.

Voy a empezar describiendo la tarea TaskButton. Es una tarea de tipo productor, y realiza una función muy sencilla en el sistema: informar a quien lo necesite de que el usuario ha pulsado el botón.
(http://farm4.static.flickr.com/3396/3420061661_2d35f54693_o.jpg)

Tiene 2 funciones vStartTaskButton() y vTaskButton(). La primera es llamada por el main y se encarga de indicar al RTOS los recursos que tiene esta tarea (un mutex llamado xMutexIO y un queue llamado xQueueKey ). La segunda función es la que ejecutara el RTOS cuando este arranque.

Código: C
  1. #ifndef TASKBUTTON_C
  2. #define TASKBUTTON_C
  3.  
  4. #include ".\TaskButton.h"
  5.  
  6. int startTaskButton( xTaskButtonResources *pxResources ){
  7.                 if( pxResources->xMutexIO == NULL ){
  8.                         if( NULL == ( pxResources->xMutexIO = xSemaphoreCreateMutex() ) )
  9.                                 return -1;
  10.                         vQueueAddToRegistry( pxResources->xMutexIO, ( signed portCHAR * )"xMutexIO" );
  11.                 }              
  12.                
  13.                 if( pxResources->xQueueKey == NULL ){
  14.                         if( NULL == ( pxResources->xQueueKey = xQueueCreate( 5, sizeof( xMessageButtonKey ) ) ) )
  15.                                 return -1;
  16.                         vQueueAddToRegistry( pxResources->xQueueKey, ( signed portCHAR * )"xQueueButtonKey" );
  17.                 }
  18.                
  19.                 if( pdPASS != xTaskCreate( vTaskButton, ( signed portCHAR * ) "Button", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  20.                         return -1;
  21.                        
  22.                 return 0;
  23. }
  24.  
  25. void vTaskButton( void *pvParameters ){
  26.  
  27.         xTaskButtonResources *pxResources;
  28.         pxResources = (xTaskButtonResources*)pvParameters;     
  29.         xMessageButtonKey xMessage;
  30.  
  31.         while(1){
  32.                 vTaskDelay( TASK_BUTTON_DELAY );
  33.                 if( pdTRUE == xSemaphoreTake( pxResources->xMutexIO, portMAX_DELAY /*TASK_BUTTON_MUTEX_BLOCKTIME*/ ) ){
  34.                         TRISAbits.TRISA1 = 1;
  35.  
  36.                         if( !BTN ){
  37.                                 while( !BTN )    // Esperamos a que el usuario suelte el boton, esto impide mandar 100 mensajes repetidos si se pulsa el boton durante X segundos
  38.                                         vTaskDelay( TASK_BUTTON_DELAY );
  39.  
  40.                                 TRISAbits.TRISA1 = 1;
  41.                                 xSemaphoreGive( pxResources->xMutexIO );
  42.  
  43.                                 xMessage.key = PRESSED;
  44.                                 xMessage.time = xTaskGetTickCount();
  45.                                 xQueueSend( pxResources->xQueueKey, ( void * )&xMessage, portMAX_DELAY );
  46.                         }
  47.  
  48.                         TRISAbits.TRISA1 = 1;
  49.                         xSemaphoreGive( pxResources->xMutexIO );
  50.                 }
  51.  
  52.         }
  53.  
  54. }
  55.  
  56.  
  57. #endif  // #ifndef TASKBUTTON_C

Una vez arrancado el RTOS el PC empezara a correr por la función vTaskButton y esta realiza lo siguiente en el bucle infinito while(1):
Primero comprueba si el recurso compartido esta disponible para su uso, de lo contrario esperara un tiempo y vuelve a comprobar. Hay que recordar que el botón y el altavoz están conectados al mismo pin y que hay que asegurar que NUNCA se lea del botón mientras se escribe en el altavoz. Para esto las dos tareas implicadas, vTaskButton y vTaskSpeaker comparten un mutex y se mantienen sincronizadas.
Cuando la tarea vTaskButton "se adueña" del mutex sabe que la tarea vTaskSpeaker no podrá tocar el pinIO hasta que vuelva a "liberar" el mutex.
Ahora, en posesión del mutex, si podemos configurar el pin como entrada, leer su valor desconfigurarlo y liberar el mutex para que otra tarea pueda usarlo.
Si el valor leído es de “botón pulsado” se comunica a otras tareas enviando un mensaje.

¿Sencillo?, yo creo que si. ¿Eficiente?, no mucho...¿Robusto? 100%.
Las funciones xSemaphoreTake, vTaskDelay, xSemaphoreGive… son las herramientas que proporciona el RTOS y se encuentran perfectamente documentadas en su pagina web.

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: MLO__ en 24 de Marzo de 2009, 22:46:58
Sencillamente espectacular .... felicitaciones
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 25 de Marzo de 2009, 16:43:37
Tarea TaskAcce.
(http://farm4.static.flickr.com/3659/3420061679_1ff806002e_o.jpg)

Esta tarea tiene como objetivo sincronizar el mensaje de la tarea TaskPutLeds con el movimiento de la mano y ofrecer así una imagen lo mas estable posible.
Debe leer continuamente el acelerómetro (a través del ADC) e indicar al sistema dos eventos clave del movimiento de la mano: cuando la mano esta a la izq. del todo y cuando esta a la drcha. del todo.
Con esta información se puede calcular el momento en el que se debe refrescar la imagen para que se superponga con la anterior.

Lo primero de todo es definir la relación que existe entre el movimiento en zig-zag de la mano y la aceleración medida.

 (http://farm4.static.flickr.com/3617/3385872720_ba3b4eeec9_o.jpg)

El esta figura se muestran los eventos que queremos detectar. En t = 1.54 la mano esta a la izquierda del todo, su velocidad es 0 y su aceleración es mínima. En t = 4.68 la mano esta a la derecha del todo, su velocidad es 0 y su aceleración es máxima.
Puesto que solo se puede leer la aceleración, se debe calcular cuando esta en su punto máximo o mínimo, y esto se cumple cuando su derivada (la velocidad) es 0.

Diagrama de bloques del algoritmo.
(http://farm4.static.flickr.com/3654/3381867957_938f960a6b_o.jpg)

En el diagrama de bloques esto esta implementado con el Diff y el Avg, aunque este ultimo hace un promedio con un buffer de 8 muestras.

A partir de aquí los bloques son similares tanto para detectar el máximo como para el mínimo, así que solo describiré el máximo.

Lo primero que hay que hacer es definir un margen ( TRIGER_LEVEL ) por debajo del cual consideraremos que la señal es 0, y otro margen (ARM_LEVEL) que nos dirá si la aceleración es máxima o mínima.
También hay que evitar que se envie más de un mensaje por evento, y para eso se ha introducido un biestable RS. Cuando se envía un mensaje(simbolizado con una banderita levantada) se resetea el biestable, y este no vuelve a ponerse a uno hasta que la velocidad es inferior a -ARM_LEVEL.

En esta imagen se muestra el uso de los márgenes y el biestable en la detección.
 (http://farm4.static.flickr.com/3597/3385060321_ecbddac70f_o.jpg)


Aquí se muestra el código de la aplicación. Tiene, como la tarea anterior, 2 funciones vStartTaskAcce y vTaskAcce.
La primera función es llamada por el main durante la configuración de aplicación (antes de arrancar el RTOS). Recibe como parámetro una estructura que contiene manejadores (handler) de todos los recursos de la tarea, devuelve un -1 si ha habido error al inicializar crear algún recurso o 0 si todo ha sido correcto. Es responsabilidad del main leer el valor devuelto y actuar en consecuencia( en este caso el main aborta el programa ) .
   
La segunda es la que se encarga de detectar el máximo o mínimo con el algoritmo descrito antes y enviar un mensaje.


Código: C
  1. #ifndef TASKACCE_C
  2. #define TASKACCE_C
  3.  
  4. #include ".\TaskAcce.h"
  5.  
  6. int startAcceTask( xTaskAcceResources *pxResources )
  7. {
  8.         if(  NULL == ( pxResources->xQueue = xQueueCreate( 5, sizeof( xMessageAccMove ) ) ) )   // Create Queue and check it.
  9.                 return -1;
  10.         vQueueAddToRegistry( pxResources->xQueue, ( signed portCHAR * )"xQueueAcce" );
  11.        
  12.         if( pdPASS != xTaskCreate( vAccelerometerTask, ( signed portCHAR * ) "Accelero", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  13.                 return -1;
  14.                
  15.         return 0;
  16. }      
  17.  
  18. void vAccelerometerTask( void *pvParameters )
  19. {
  20.         xTaskAcceResources *pxAcceR;                    // Task parameteres handler.
  21.         xMessageAccMove xMessage;                               // Queue message.
  22.         int input;
  23.         Differentiator dffrnttr1;                               // Differentiator block object.
  24.         RSFF rsff1, rsff2;                                              // Two RS flip-flops block objects.
  25.         Average avrg1;                                                  // Average block object.
  26.         ANDGate andgt1, andgt2;                                 // Two input logic AND gate block objetc.
  27.        
  28.         DifferentiatorConstruct( &dffrnttr1 );
  29.         DifferentiatorSetAt( &dffrnttr1, DERIVATOR_DT );
  30.  
  31.         RSFFConstruct( &rsff1 );
  32.         RSFFConstruct( &rsff2 );
  33.  
  34.         AverageConstruct( &avrg1 );
  35.         AverageSetBufferSize( &avrg1 , AVERAGE_LEVEL );
  36.  
  37.         ANDGateConstruct( &andgt1 );
  38.         ANDGateConstruct( &andgt2 );
  39.  
  40.         ACCS = 1;                                                               // Enable ACCELEROMETER
  41.        
  42.         pxAcceR = (xTaskAcceResources*)pvParameters;   
  43.        
  44.         vTaskSuspend( pxAcceR->xHandle );
  45.  
  46.         while( 1 ){
  47.        
  48.                 IFS0bits.AD1IF = 0;                                     // Convert ADC
  49.                 AD1CON1bits.ASAM = 1;          
  50.  
  51.                 vTaskDelay( 1 );                                        // Task Delay
  52.                
  53.                 while(!IFS0bits.AD1IF);
  54.                 AD1CON1bits.ASAM = 0;
  55.        
  56.                 input = ReadADC10(2);                           // Read converted value
  57.  
  58.                 DifferentiatorProcessV( &dffrnttr1, input );
  59.  
  60.                 AverageProcessV( &avrg1, dffrnttr1.out );
  61.  
  62.                 RSFFProcessV( &rsff1, andgt1.out, (avrg1.out>ARM_LEVEL) );
  63.                 ANDGateProcessV( &andgt1, rsff1.Q, (avrg1.out<-FIRE_LEVEL) );
  64.                 if( andgt1.out == HIGH ){
  65.                         xMessage.direction = 1;
  66.                         xMessage.time = xTaskGetTickCount();
  67.                         xQueueSend( pxAcceR->xQueue, ( void * )&xMessage, ( portTickType ) 0 );
  68.                 }
  69.                                
  70.                 RSFFProcessV( &rsff2, andgt2.out, (avrg1.out<-ARM_LEVEL) );
  71.                 ANDGateProcessV( &andgt2, rsff2.Q, (avrg1.out>FIRE_LEVEL) );
  72.                 if( andgt2.out == HIGH ){
  73.                         xMessage.direction = -1;
  74.                         xMessage.time = xTaskGetTickCount();
  75.                         xQueueSend( pxAcceR->xQueue, ( void * )&xMessage, ( portTickType ) 0 );
  76.                 }
  77.         }
  78. }
  79.  
  80. #endif  //#ifndef TASKACCE_C


El código se podría haber implementado de muchas maneras, pero se ha intentado acercar a lo que seria la programación orientada a objetos(POO).
En lugar de utilizar una función que se llame diferencia, se han creado dos archivos: differenciator.c y differenciator.h
Differenciator.h- Contiene los atributos ( una estructura ) y la declaración de los métodos del objeto( declaraciones de las funciones).
Differenciator.c- Contiene las definiciones de los métodos del objeto.

Para utilizar un objeto tipo diferentiator podríamos utilizar un programa de este tipo:
Código: C
  1. #include <stdio>
  2.         #include "differentiator.h"
  3.         Main(){
  4.                 Differentiator diff1;
  5.                 DifferentiatorConstructor( &diff1 );
  6.                 while(i++){
  7.                         DifferentiatorProcces( &diff1, i );
  8.                         printf(%d”, diff1.out );
  9.                 }
  10.         }
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: vtasco en 26 de Marzo de 2009, 02:31:06
tremendo proyecto!!

se irá de impresión, hace rato que quiero ver un ejemplo de freertos, para ver cómo se usa.

gracias!
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Nocturno en 26 de Marzo de 2009, 02:47:09
¿Ese diagrama de bloques está generado mediante alguna herramienta de FreeRTOS?
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 26 de Marzo de 2009, 05:51:57
Citar
¿Ese diagrama de bloques está generado mediante alguna herramienta de FreeRTOS?
No, los diagramas los hago con el paint, que quizás no sea el programa más adecuado, pero es suficiente para lo que necesito.

La única herramienta que hay relacionada es un programa "tracer" que viene con el RTOS. Un RTOS puede enviar al exterior información de su estado y de los eventos que van sucediendo en cada momento, el "tracer" recibe esta info y la muestra en unas gráficas. El MPLAB incorpora el "RTOS Viewer", que permite visualizar el estado interno del RTOS (estado de las tareas creadas,numero de queues creados... ) en cada breakpoint del programa.

El FreeRTOS solo son unas librerías que añades a tu proyecto, 4 .c y unos 6 .h.
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 27 de Marzo de 2009, 07:11:41
La tarea TaskWatch
(http://farm4.static.flickr.com/3593/3420869788_d9a098102b_o.jpg)

Esta es la tarea central del emulador. Se encarga de mantener el reloj y sus menús actualizados en pantalla.
Recibe la tecla pulsada de la tarea TaskGetLeds, actualiza el estado del reloj y envía una serie de primitivas que representan la pantalla a la tarea TaskGraphics.
Esta dividida, como las dos anteriores, en dos funciones vStartTaskWatch y vTaskWatch.
El código de la tarea en si es sencillo y toda la implementación del reloj se encuentra en una clase llamada Watch. La clase Watch se apoya sobre otra clase llamada Menu,

Código: C
  1. #ifndef TASKWATCH_C
  2. #define TASKWATCH_C
  3.  
  4. #include "TaskWatch.h"
  5.  
  6.  
  7. // Casio Watch - FE10-1A Menu Emulation
  8.  
  9. int startTaskWatch( xTaskWatchResources *pxResources ){
  10.        
  11.         if( pxResources->xQueueSound == NULL ){
  12.                 if( NULL == ( pxResources->xQueueSound = xQueueCreate( 5, sizeof( xMessageWatchSound ) ) ) )
  13.                         return -1;
  14.                 vQueueAddToRegistry( pxResources->xQueueSound, ( signed portCHAR * )"xQueueWatchSound" );
  15.         }
  16.  
  17.         if( pxResources->xQueueKey == NULL ){
  18.                 if( NULL == ( pxResources->xQueueKey = xQueueCreate( 5, sizeof( xMessageWatchKey ) ) ) )
  19.                         return -1;
  20.                 vQueueAddToRegistry( pxResources->xQueueKey, ( signed portCHAR * )"xQueueWatchKey" );
  21.         }
  22.  
  23.         if( pxResources->xQueuePrimitive == NULL ){
  24.                 if( NULL == ( pxResources->xQueuePrimitive = xQueueCreate( 10, sizeof( xMessageWatchPrimitive ) ) ) )
  25.                         return -1;
  26.                 vQueueAddToRegistry( pxResources->xQueuePrimitive, ( signed portCHAR * )"xQueueWatchPrimitive" );
  27.         }
  28.  
  29.        
  30.         if( pdPASS != xTaskCreate( vTaskWatch, ( signed portCHAR * ) "Watch", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  31.                 return -1;
  32.        
  33.         return 0;              
  34. }
  35.  
  36.  
  37. void vTaskWatch( void *pvParameters ){
  38.  
  39.         xTaskWatchResources *pxResources;
  40.         pxResources = (xTaskWatchResources*)pvParameters;      
  41.         xMessageWatchKey xMessageKey;
  42.         xMessageWatchSound xMessageSound;
  43.         xMessageWatchPrimitive xMessagePrimitive;
  44.         char text_message[20];  // make sure _sprint no write out of array.
  45.        
  46.         Watch watch1;
  47.         int tmp=-1;
  48.         int i;
  49.         int refresh2 = 0;
  50.        
  51.         WatchConstruct( &watch1 );
  52.        
  53.         vTaskSuspend(NULL);
  54.        
  55.         Qglcd_textArial( 4, 0, "MLS09", 1 );
  56.         Qglcd_update();
  57.         vTaskDelay(8000);      
  58.                
  59.         while(1){
  60.                 vTaskDelay( 250 );
  61.                
  62.  
  63.                 if( watch1.SIG && watch1.timedate.f.min == 0 && watch1.timedate.f.sec == 0 ){
  64.                         xMessageSound.reps = 0x5050;
  65.                         xMessageSound.time = xTaskGetTickCount();
  66.                         xQueueSend( pxResources->xQueueSound, ( void * )&xMessageSound, portMAX_DELAY );
  67.                         vTaskDelay( 2000 );
  68.                 }
  69.                
  70.                 WatchProcessClock( &watch1 );   // Update clock from RTCC
  71.                
  72.                 while( xQueueReceive( pxResources->xQueueKey, &xMessageKey, 0 ) ){
  73.                        
  74.                         if( 1 == watch1.menu1.state%5 && xMessageKey.key == 3 ){
  75.                                 xMessageSound.reps = 0x5050;
  76.                                 xMessageSound.time = xTaskGetTickCount();
  77.                                 xQueueSend( pxResources->xQueueSound, ( void * )&xMessageSound, portMAX_DELAY );
  78.                                 xMessageSound.reps = 0x5050;
  79.                                 xMessageSound.time = xTaskGetTickCount();
  80.                                 xQueueSend( pxResources->xQueueSound, ( void * )&xMessageSound, portMAX_DELAY );
  81.  
  82.                         }      
  83.                
  84.                         WatchProcessKey( &watch1, xMessageKey.key );    // insert value to internal FSM
  85.                        
  86.                         // MenuPrint( Menu *this );
  87.                         // If you have change a menu redraw.
  88.                         if( tmp != watch1.menu1.state%5 ){
  89.                                 tmp = watch1.menu1.state%5;
  90.                                
  91.                                 glcd_fillScreen( 0 );
  92.                                 Qglcd_textArial( 15, 0, MenuGetNodeName( &watch1.menu1 ), 1 );
  93.                                 Qglcd_update();
  94.                        
  95.                                 // Delay 4000 Ticks. If Queue received, resume task.
  96.                                 if( xQueuePeek( pxResources->xQueueKey, &xMessageKey, 4000 ) )
  97.                                         continue;
  98.                        
  99.                         // MenuFunctionsPrint( Menu *this );
  100.                         // If press "unusable" function key, show "usable" keys.
  101.                         }else if( watch1.menu1.tree[watch1.menu1.state].connections[xMessageKey.key].pfunction == NULL ){
  102.                                
  103.                                 glcd_fillScreen( 0 );
  104.                                 Qglcd_pixel( 6, 2, 1 );
  105.                                 Qglcd_pixel( 6, 4, 1 );
  106.                                 Qglcd_pixel( 6, 6, 1 );
  107.                                 Qglcd_pixel( 6, 10, 1 );
  108.                                 Qglcd_pixel( 6, 12, 1 );
  109.                                 Qglcd_pixel( 6, 14, 1 );
  110.                                 Qglcd_pixel( 6, 18, 1 );
  111.                                 Qglcd_pixel( 6, 20, 1 );
  112.                                 Qglcd_pixel( 6, 22, 1 );
  113.                                
  114.                                 Qglcd_line( 64, 2, 64, 7, 1 );
  115.                                 Qglcd_line( 64, 10, 64, 15, 1 );
  116.                                 Qglcd_line( 64, 18, 64, 23, 1 );
  117.                                
  118.                                 Qglcd_text57( 10, 3, MenuGetNodeFunctionName( &watch1.menu1, 0 ), 1, 1 );
  119.                                
  120.                                 Qglcd_text57( 10, 10, MenuGetNodeChildName( &watch1.menu1, 1 ), 1, 1 );
  121.                                
  122.                                 Qglcd_text57( 10, 16, MenuGetNodeFunctionName( &watch1.menu1, 2 ), 1, 1 );
  123.                                 Qglcd_text57( 70, 3, MenuGetNodeFunctionName( &watch1.menu1, 3 ), 1, 1 );
  124.                                 Qglcd_text57( 70, 10, MenuGetNodeFunctionName( &watch1.menu1, 4 ), 1, 1 );
  125.                                 Qglcd_text57( 70, 16, MenuGetNodeFunctionName( &watch1.menu1, 5 ), 1, 1 );     
  126.                                 Qglcd_update();
  127.  
  128.                                 // Delay 4000 Ticks. If Queue received, resume task.
  129.                                 if( xQueuePeek( pxResources->xQueueKey, &xMessageKey, 4000 ) )
  130.                                         continue; // Implicit because his position.
  131.                         }
  132.                 }
  133.                
  134. // Show watch status...WatchPrint( Watch *this );
  135.                 glcd_fillScreen( 0 );
  136.                 switch( watch1.menu1.state ){
  137.                         case 0:
  138.                         case 5+0:
  139. // FORMATO:
  140. //      LUNES   P       23:59           ALM
  141. //      31-12           23:59 59        SIG
  142.                                 Qglcd_text57( 9, 16, wday_names[ watch1.timedate.f.wday  ], 1, 1 );
  143.                                
  144.                                 _sprintf( text_message, "%02x-%02x", watch1.timedate.f.mday, watch1.timedate.f.mon  );
  145.                                 Qglcd_text57( 9, 7, text_message, 1, 1 )
  146.  
  147.                                 if( watch1.PM24 ){
  148.                                         // ...Conver hour to 12 hour format...
  149.                                         int hour = DECtoBCD( BCDtoDEC(watch1.timedate.f.hour)%12 );
  150.                                         _sprintf( text_message, "%2x:%02x", hour, watch1.timedate.f.min  );
  151.                                 }else{
  152.                                         _sprintf( text_message, "%02x:%02x", watch1.timedate.f.hour, watch1.timedate.f.min  );
  153.                                        
  154.                                 }
  155.                                 Qglcd_text57( 38, 7, text_message, 2, 1 );
  156.  
  157.                                
  158.                                 _sprintf( text_message, "%02x", watch1.timedate.f.sec  );
  159.                                 Qglcd_text57( 93, 7, text_message, 1, 1 );
  160.  
  161.                                 if( watch1.PM24 ){      Qglcd_text57( 32, 14, "P", 1, 1 );}
  162.                                 if( watch1.ALM ){       Qglcd_text57( 108, 16, "ALM", 1, 1 );}
  163.                                 if( watch1.SIG ){       Qglcd_text57( 108, 7, "SIG", 1, 1 );}
  164.                                
  165.                                 Qglcd_update();
  166.                                 vTaskDelay(2000);
  167.                                 break;
  168.                                
  169.                         case 1:
  170.                         case 5+1:
  171. // FORMATO:
  172. //      ALARM           23:59           ALM
  173. //                              23:59           SIG
  174.                                 Qglcd_text57( 9, 16, "ALAR", 1, 1 );
  175.                                
  176.                                 if( watch1.PM24 ){
  177.                                         // ...Conver hour to 12 hour format...
  178.                                         _sprintf( text_message, "%02x:%02x", watch1.alarm.f.hour, watch1.alarm.f.min  );
  179.                                 }else{
  180.                                         _sprintf( text_message, "%02x:%02x", watch1.alarm.f.hour, watch1.alarm.f.min  );
  181.                                        
  182.                                 }
  183.                                 Qglcd_text57( 38, 7, text_message, 2, 1 );
  184.                                
  185.                                 if( watch1.PM24 ){      Qglcd_text57( 32, 14, "P", 1, 1 );}
  186.                                 if( watch1.ALM ){       Qglcd_text57( 108, 16, "ALM", 1, 1 );}
  187.                                 if( watch1.SIG ){       Qglcd_text57( 108, 7, "SIG", 1, 1 );}
  188.                                
  189.                                 Qglcd_update();
  190.                                 vTaskDelay(2000);
  191.                                 break;
  192.                         case 2:
  193.                         case 5+2:
  194. // FORMATO:
  195. //      ALARM           __:__           ALM
  196. //                              __:__           SIG
  197.                                 Qglcd_text57( 9, 16, "ALAR", 1, 1 );
  198.                                
  199.                                 if( watch1.PM24 ){
  200.                                         // ...Conver hour to 12 hour format...
  201.                                         _sprintf( text_message, "%02x:%02x", watch1.alarm.f.hour, watch1.alarm.f.min  );
  202.                                 }else{
  203.                                         _sprintf( text_message, "%02x:%02x", watch1.alarm.f.hour, watch1.alarm.f.min  );
  204.                                        
  205.                                 }
  206.                                 Qglcd_text57( 38, 7, text_message, 2, 1 );
  207.                                
  208.                                 if( watch1.PM24 ){      Qglcd_text57( 32, 14, "P", 1, 1 );}
  209.                                 if( watch1.ALM ){       Qglcd_text57( 108, 16, "ALM", 1, 1 );}
  210.                                 if( watch1.SIG ){       Qglcd_text57( 108, 7, "SIG", 1, 1 );}
  211.                                
  212.                                 if( (xTaskGetTickCount()/500)%2 ){
  213.                                         switch( watch1.ChangeAlarmCounter ){
  214.                                                 case 0: Qglcd_line( 40, 5, 60, 5, 1 ); break;
  215.                                                 case 1: Qglcd_line( 70, 5, 90, 5, 1 ); break;
  216.                                                 default: break;
  217.                                         }
  218.                                 }
  219.                                
  220.                                 Qglcd_update();
  221.                                 vTaskDelay(2000);
  222.                                 break;
  223.                         case 3:
  224.                         case 5+3:      
  225. // FORMATO:            
  226. //      STOP            23:59   SPL    
  227. //                              23:59 59
  228.                                 Qglcd_text57( 9, 16, "STOP", 1, 1 );
  229.                        
  230.                                 _sprintf( text_message, "%02d:%02d", (int)(watch1.out/3600)%24, (int)(watch1.out/60)%24  );
  231.                                 Qglcd_text57( 38, 7, text_message, 2, 1 )
  232.                        
  233.                                 _sprintf( text_message, "%02d", (int)(watch1.out)%60  );
  234.                                 Qglcd_text57( 93, 7, text_message, 1, 1 );
  235.                                        
  236.                                 if( watch1.SPL ){       Qglcd_text57( 92, 14, "SPL", 1, 1 );}
  237.                                
  238.                                 Qglcd_update();
  239.                                 vTaskDelay(2000);
  240.                                 break;
  241.                         case 4:
  242.                         case 5+4:
  243. // FORMATO:
  244. //      _____   P       __:__          
  245. //      __-__           __:__ __       
  246.                                 Qglcd_text57( 9, 16, wday_names[ watch1.timedate.f.wday  ], 1, 1 );
  247.                                
  248.                                 _sprintf( text_message, "%02x-%02x", watch1.timedate.f.mday, watch1.timedate.f.mon  );
  249.                                 Qglcd_text57( 9, 7, text_message, 1, 1 )
  250.  
  251.                                 if( watch1.PM24 ){
  252.                                         // ...Conver hour to 12 hour format...
  253.                                         int hour = DECtoBCD( BCDtoDEC(watch1.timedate.f.hour)%12 );
  254.                                         _sprintf( text_message, "%02x:%02x", hour, watch1.timedate.f.min  );
  255.                                 }else{
  256.                                         _sprintf( text_message, "%02x:%02x", watch1.timedate.f.hour, watch1.timedate.f.min  );
  257.                                        
  258.                                 }
  259.                                 Qglcd_text57( 38, 7, text_message, 2, 1 );
  260.  
  261.                                
  262.                                 _sprintf( text_message, "%02x", watch1.timedate.f.sec  );
  263.                                 Qglcd_text57( 93, 7, text_message, 1, 1 );
  264.  
  265.                                 if( watch1.PM24 ){      Qglcd_text57( 32, 14, "P", 1, 1 );}
  266.                
  267.                                 if( (xTaskGetTickCount()/500)%2 ){
  268.                                         switch( watch1.ChangeTimeCounter ){
  269.                                                 case 0: Qglcd_line( 93, 5, 103, 5, 1 ); break;
  270.                                                 case 1: Qglcd_line( 40, 5, 60, 5, 1 ); break;
  271.                                                 case 2: Qglcd_line( 70, 5, 90, 5, 1 ); break;
  272.                                                 case 3: Qglcd_line( 24, 5, 34, 5, 1 ); break;
  273.                                                 case 4: Qglcd_line( 9, 5, 19, 5, 1 ); break;
  274.                                                 case 5: Qglcd_line( 9, 15, 34, 15, 1 ); break;
  275.                                                 default: break;
  276.                                         }
  277.                                 }              
  278.                                
  279.                                 Qglcd_update();
  280.                                 vTaskDelay(2000);
  281.                                 break;
  282.                         default:
  283.                                 break;
  284.                 }
  285.         }
  286. }
  287. #endif  // #ifndef TASKWATCH_H


Esta tarea es mas larga, pero su código es más sencillo de entender, ya que la mayor parte del código solo trata de enviar las primitivas a la tarea TaskWatch.
Todas las funciones Qglcd_... están definidas en el archivo TaskWatch.h más o menos como:

Código: C
  1. #define Qglcd_pixel( x, y, c )                          \
  2.         xMessagePrimitive.function = __glcd_pixel;      \
  3.         xMessagePrimitive.intA = x;                             \
  4.         xMessagePrimitive.intB = y;                             \
  5.         xMessagePrimitive.intC = c;                             \
  6.         xMessagePrimitive.time = xTaskGetTickCount();   \
  7.         xQueueSend( pxResources->xQueuePrimitive, ( void * )&xMessagePrimitive, portMAX_DELAY );




La clase Menu

La case Menu esta compuesta de dos archivos:
   Menu.h que declara los métodos y atributos del objeto.
   Menu.c que  define los métodos del objeto.
Un objeto del tipo menú implementa una maquina de estados finitos(FSM) de Mealy. Esta declarado de la siguiente manera:

Código: C
  1. typedef struct connection{
  2.         int  newnode;
  3.         char functname[5];
  4.         void (*pfunction)(void*);
  5. }connection;
  6.  
  7. typedef struct{
  8.         char name[5];
  9.         connection connections[6];
  10. }node;
  11.  
  12. typedef struct{
  13.         node *tree;
  14.         int state;
  15.         int size;
  16. }Menu;

Y tiene los siguientes métodos:

Código: C
  1. Menu *MenuConstruct( Menu *this, node *tree, int size );
  2. void MenuDestruct( Menu *this );
  3. void MenuProcessV( Menu *this, int input, void * object );
  4. char *MenuGetNodeName( Menu *this );
  5. char *MenuGetNodeChildName( Menu *this, int input );
  6. char *MenuGetNodeFunctionName( Menu *this, int input );

En la función  MenuConstructor se asigna al puntero tree la dirección de una estructura que contendrá la organización del menú. Esta es la usada para emular los menús del F91W:

(http://farm4.static.flickr.com/3601/3388912695_98969bfbf8_o.jpg)

Como el reloj tiene 6 posibles teclas (A,B,C cortos y A,B,C largos) cada estado de la maquina tiene 6 posibles acciones. Cada acción puede llamar a una cambiar de estado y/o llamar a una función apuntada por el parámetro pfunction.


Después de “construir” un objeto menú (MenuConstructor) solo debemos llamar a la función MenuProcessV. Esta acepta 3 parámetros:
   Menu *this: El menú que se pretende procesar.
int input: La tecla pulsada.
void * object: El parámetro(normalmente un objeto) que se le pasara a la función apuntada por pfunction.

Un objeto menú tiene en cada uno de sus nodos un nombre. Para obtener información sobre los nombres la clase tiene 3 métodos:
MenuGetNodeName: Devuelve el nombre del nodo actual.
MenuGetNodeChildName: Devuelve el nombre de un nodo inferior.
MenuGetNodeFunctionName, Devuelve el nombre de la función a la que apunta pfunction.

La tarea TaskWatch llama a la función MenuGetNodeName cada vez que el reloj cambia de menú y su texto es representado en formato Arial(24 pixels de alto).



La clase Watch

Código: C
  1. typedef struct Wacth{
  2.     Menu menu1;
  3.     rtccTimeDate timedate;
  4.     rtccTime alarm;
  5.     rtccTime stopwatch;
  6.     rtccTime splittime;
  7.     long start, stop, split, out;
  8.     int ChangeAlarmCounter, ChangeTimeCounter;
  9.     int PM24;
  10.     int ALM;
  11.     int SIG;
  12.     int RUN;
  13.     int SPL;
  14. }Watch;
Código: C
  1.  
La clase Watch encapsula todo el funcionamiento interno del reloj. Almacena el valor de la hora, la alarma y el cronometro, así como variables de estado. Si por ejemplo ponemos el reloj en modo 24H se pone a uno el campo de Watch.PM24 = 1 y esto será usado en la representación del reloj.

Aunque el gatjet pretende ser un emulador fiel al reloj Casio, este se encuentra en modo “desarrollo” y se ha añadido una pequeña funcionalidad de ayuda a la depuración.
Como el Casio original, este gadjet tiene 6 teclas distintas A,B,C cortos y A,B,C largos. Si estando en un menú, introducimos una tecla que no hace nada, se muestra una pantalla de información con todas las funciones asociadas a cada una de las teclas.

Las funciones de hora, alarma y cronometro basan su precisión en el periférico Real Time Clock Calendar (RTCC) que incorpora el microcontrolador. El cronometro tiene opciones de Star, Stop y Split, pero estas solo tienen precisión de 1 segundo(el Casio tiene una precisión de 0.01 segundos).

Los métodos mostrados a continuación permiten al usuario realizar las operaciones de cronometro definidas en documento USER’S GUIDE 2428 de Casio:

Código: C
  1. void Inicio( Watch *this );
  2. void Parada( Watch *this );
  3. void Fraccion( Watch *this );
  4. void Libera( Watch *this );
  5. void Borrado( Watch *this );
  6. void ClearSplit( Watch *this );
  7. void StartStop( Watch *this );
  8.  
  9. /*             
  10. Función        Medición de tiempo transcurrido.
  11. Tecla           C               C               C               C               A
  12. Acción         Inicio          Parada          Reinicio        Parada          Borrado
  13.  
  14. Función        Medición de tiempo fraccionado.
  15. Tecla           C               A               A               C               A
  16. Acción         Inicio          Fracción       Libera          Parada          Borrado
  17.  
  18. Función        Tiempos fraccionados y/o tiempos del primero y del segundo en llegar.
  19. Tecla           C               A               C               A               A
  20. Acción         Inicio          Fracción       Parada          Libera          Borrado
  21. */


Algunos links:
Maquina de estados finitos:   http://en.wikipedia.org/wiki/Finite_state_machine#Software_applications
Guía de usuario del reloj de Casio:   http://ftp.casio.co.jp/pub/world_manual/wat/en/qw2428.pdf
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 28 de Marzo de 2009, 10:57:33
La tarea TaskGetLeds.
(http://farm4.static.flickr.com/3648/3420869758_bcc0d29f2d_o.jpg)

Responsabilidades:
   Debe informar al sistema cunado el usuario pulse una tecla, indicando la tecla pulsada.
Estructura de la tarea:
   Esta dividida en dos funciones, vStartTaskGetLeds y vTaskLeds. La primera crea los recursos usados. La segunda comprueba continuamente las teclas e informa y envía un mensaje.
Funcionamiento:
   Lo primero que hace esta tarea es intentar obtener un mutex. Esta tarea comparte los 24 LEDs con TaskPutLeds y por eso ambas deben estar sincronizadas. Cuando obtiene el mutex llama a la función get_leds y esta devuelve un valor con el siguiente significado:
      -1: No hay ninguna tecla pulsada.
      0: La tecla A esta pulsada.
      1: La tecla B esta pulsada.
      2: La tecla C esta pulsada.
Si no hay ninguna tecla pulsada se libera el mutex y vuelve al principio.
Si hay una tecla pulsada se espera hasta que se suelte y se envía en un mensaje uno de estos códigos:
      0: La tecla A esta pulsada menos de 1.5 segundos.
      1: La tecla B esta pulsada menos de 1.5 segundos.
      2: La tecla C esta pulsada menos de 1.5 segundos.
3: La tecla A esta pulsada más de 1.5 segundos.
      4: La tecla B esta pulsada más de 1.5 segundos.
      5: La tecla C esta pulsada más de 1.5 segundos.

La librería LEDs.c
Incorpora funciones para el control de los LEDs. Permite controlar individualmente el estado de cada LED y su polaridad.

Código: C
  1. unsigned long long order_LEDS( unsigned long value );
  2.         void put_ordered_LEDS( unsigned long long out );
  3.         void put_LEDS( unsigned long value );
  4.         void set_COLS_DIR( unsigned char COLS );        // to be placed in mosfet.h
  5.         void set_LEDS_DIR( unsigned long value );
  6.         unsigned long get_LEDS( void );

La función get_LEDS necesita que los LEDS puedan ser polarizados en inversa, para esto el PCB incorpora dos mosfet que pueden dar una salida de GND o VCC.
Los LEDS están divididos en 2 grupos, pares e impares. Mientras unos se polarizan como receptores los otros pueden estar como emisores y medir asi la luz reflejada.

(http://farm4.static.flickr.com/3417/3389631638_ca874794ce_o.jpg)


PIN SWAP para un ruteado más sencillo en Altium Designer.
Esta es una de las opciones que me han permitido reducir notablemente el tiempo de ruteado y el número de vias de la placa. Se trata de indicar al programa que pines tienen funciones intercambiables y él mismo los intercambia para que el número de cruces sea mínimo.

Aquí se puede ver el antes y el después:
(http://farm4.static.flickr.com/3184/3389630692_97cfdcffd5.jpg)
(http://farm4.static.flickr.com/3424/3388819575_b08cb5dbbb.jpg)


Reordenar antes de mostrar.
Puesto que no hay ningún orden entre los LEDs y los pins que ocupan, es necesario reordenar todos los datos que quieran ser mostrados. La función ordenar debe ser optima ya que es llamada muchas veces durante el funcionamiento del programa. Una primera implementación en C es suficientemente rápida y portable, pero decidí implementar una segunda versión en ensamblador para mejorar la eficiencia.

   Solución C:
Código: C
  1. const unsigned long long LEDSNETLIST[24] =      // 0x1ULL << bitnumber+port*16
  2. {      
  3.         0x1ULL << (10+16),      0x1ULL << (9+32),       0x1ULL << (8+32),       0x1ULL << (7+32),      
  4.         0x1ULL << (6+32),       0x1ULL << (9+16),       0x1ULL << (8+16),       0x1ULL << (7+16),
  5.         0x1ULL << (6+16),       0x1ULL << (5+16),       0x1ULL << (15+16),      0x1ULL << (5+32),
  6.         0x1ULL << (4+32),       0x1ULL << (3+32),       0x1ULL << (9+0),        0x1ULL << (8+0),
  7.         0x1ULL << (3+0),        0x1ULL << (2+0),        0x1ULL << (2+32),       0x1ULL << (1+32),      
  8.         0x1ULL << (0+0),        0x1ULL << (0+32),       0x1ULL << (3+16),       0x1ULL << (2+16)
  9. };
  10.  
  11. unsigned long long order_LEDS( unsigned long value ){
  12.         unsigned long long out = 0;
  13.         unsigned char i = 0;
  14.        
  15.         if( value )     // If value == 0, skip loop.
  16.                 for( i = 0 ; i < 24 ; i++, value >>= 1 )
  17.                         if( value & 0x1 )
  18.                                 out |= LEDSNETLIST[i];
  19.         return out;    
  20. }

      Solución ASM:

Código: C
  1. unsigned long long order_LEDS_ASM( unsigned long value ){
  2.         unsigned long long out = 0;
  3.        
  4.         if( value )                                     // If value == 0, skip loop.
  5.         {
  6.                 asm("mov        w14,w0");               // w3:w0 = &out(U:H:M:L)
  7.                 asm("add        w0, #2, w1");
  8.                 asm("add        w0, #4, w2");
  9.                 asm("add        w0, #6, w3");
  10.        
  11.                 asm("add        w0, #8, w4");           // w5:w4 = &value(H:L)
  12.                 asm("add        w0, #10, w5");
  13.        
  14.  
  15.                 asm("BTSC       [w4], #0");             asm("BSET       [w1], #10");
  16.                 asm("BTSC       [w4], #1");             asm("BSET       [w2], #9");
  17.                 asm("BTSC       [w4], #2");             asm("BSET       [w2], #8");
  18.                 asm("BTSC       [w4], #3");             asm("BSET       [w2], #7");
  19.  
  20. //...LEDs 4 to 19
  21.  
  22.                 asm("BTSC       [w5], #4");             asm("BSET       [w0], #0");
  23.                 asm("BTSC       [w5], #5");             asm("BSET       [w2], #0");
  24.                 asm("BTSC       [w5], #6");             asm("BSET       [w1], #3");
  25.                 asm("BTSC       [w5], #7");             asm("BSET       [w1], #2");
  26.         }      
  27.         return out;    
  28. }
Aquí dejo algún link:
Led como sensor:
http://en.wikipedia.org/wiki/LEDs_as_Photodiode_Light_Sensors

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Nocturno en 28 de Marzo de 2009, 13:28:25
¡Coño!, ¿qué Altium te reordena los pines que son asignables dinámicamente?  :shock:
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 28 de Marzo de 2009, 14:13:58
Citar
¡Coño!, ¿qué Altium te reordena los pines que son asignables dinámicamente?

Sip, asi es. Echale un ojo a este link.
http://www.altium.com/files/AltiumDesigner6/LearningGuides/AP0138%20Pin%20and%20Part%20Swapping%20with%20Dynamic%20Net%20Assignment.PDF
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Nocturno en 28 de Marzo de 2009, 14:47:24
¡Magnífico!, con la de veces que uso arrays de componentes y me tengo que comer el coco en establecer el mejor orden posible. Incluso para poner un conector de expansión podrá venir bien esta función.

No paro de aprender con este hilo, muchas gracias jgpeiro.
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 29 de Marzo de 2009, 17:02:45
Citar
Incluso para poner un conector de expansión podrá venir bien esta función.
Si, si, yo lo he comentado para los LEDs, pero es cierto que es más útil para conectores.


La tarea TaskGraphics

(http://farm4.static.flickr.com/3567/3396371646_72205151fb_o.jpg)

Responsabilidades:
   Debe dibujar en un buffer todas las primitivas. Cuando recibe el mensaje Qglcd_update envía el buffer a la tarea TaskPulLeds.
Recursos:
   Dispone de un queue de entrada llamado xQueuePrimitive para recibir las primitivas que debe dibujar y otro queue llamado xQueueDisp para enviar el buffer con toda la    imagen.
Estructura de la tarea:
   Esta dividida en dos funciones, vStartTaskGraphics y vTaskGraphics. La primera crea los recursos y la segunda dibuja y envía el buffer.
Funcionamiento:
   No tiene ningún misterio, cada vez que recibe un mensaje con una primitiva llama a la función correspondiente de la librería Graphics.c

Código: C
  1. #ifndef TASKGRAPHICS_C
  2. #define TASKGRAPHICS_C
  3.  
  4. #include ".\TaskGraphics.h"
  5.  
  6. unsigned long mensajeBuffer[2][128];
  7.  
  8. int startTaskGraphics( xTaskGraphicsResources *pxResources ){
  9.                        
  10.         if( pxResources->xQueuePrimitive == NULL ){
  11.                 if( NULL == ( pxResources->xQueuePrimitive = xQueueCreate( 10, sizeof( xMessageGraphicsPrimitive ) ) ) )
  12.                         return -1;
  13.                 vQueueAddToRegistry( pxResources->xQueuePrimitive, ( signed portCHAR * )"xQueueGraphicsPrimitive" );
  14.         }
  15. //      if( pxResources->xQueueDisp == NULL ){
  16.                 if( NULL == ( pxResources->xQueueDisp = xQueueCreate( 1, sizeof( xMessageGraphicsDisp ) ) ) )
  17.                         return -1;
  18.                 vQueueAddToRegistry( pxResources->xQueueDisp, ( signed portCHAR * )"xQueueGraphicsDisp" );
  19. //      }
  20.        
  21.         if( pdPASS != xTaskCreate( vTaskGraphics, ( signed portCHAR * ) "Graphics", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  22.                 return -1;
  23.                
  24.         return 0;
  25. }
  26.  
  27.  
  28. void vTaskGraphics( void *pxParameters ){
  29.        
  30.         unsigned long *pmensaje;
  31.         xTaskGraphicsResources *pxResources;            // Task parameteres handler.
  32.         xMessageGraphicsPrimitive xMessagePrimitive;
  33.         xMessageGraphicsDisp xMessageDisp;
  34.        
  35.         pxResources = (xTaskGraphicsResources*)pxParameters;   
  36.  
  37.         vTaskSuspend( NULL );
  38.  
  39.         if( pmensaje == &mensajeBuffer[0][0] )  pmensaje = &mensajeBuffer[1][0];
  40.         else                                                                    pmensaje = &mensajeBuffer[0][0];
  41.         glcd_init( 0, pmensaje );
  42.        
  43.         while( 1 ){
  44.                
  45.                 vTaskDelay( 10 );
  46.                
  47.                 xQueueReceive( pxResources->xQueuePrimitive, &xMessagePrimitive, portMAX_DELAY );
  48.        
  49.                 switch( xMessagePrimitive.function ){
  50.                         case __glcd_init : glcd_init( xMessagePrimitive.intA, xMessagePrimitive.pulongA );                                                              break;
  51.                         case __glcd_pixel : glcd_pixel( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.intC);                                                  break;
  52.                         case __glcd_line : glcd_line( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.intC,xMessagePrimitive.intD,xMessagePrimitive.intE);              break;
  53.                         case __glcd_rect : glcd_rect( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.intC,xMessagePrimitive.intD,xMessagePrimitive.intE,xMessagePrimitive.intF);break;
  54.                         case __glcd_bar : glcd_bar( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.intC,xMessagePrimitive.intD,xMessagePrimitive.intE,xMessagePrimitive.intF);break;
  55.                         case __glcd_circle : glcd_circle( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.intC,xMessagePrimitive.intD,xMessagePrimitive.intE);          break;
  56.                         case __glcd_text57 : glcd_text57( xMessagePrimitive.intA, xMessagePrimitive.intB, xMessagePrimitive.acharA/*xMessagePrimitive.pcharA*/, xMessagePrimitive.intC, xMessagePrimitive.intD);break;
  57.                         case __glcd_textArial : glcd_textArial( xMessagePrimitive.intA,xMessagePrimitive.intB,xMessagePrimitive.acharA/*xMessagePrimitive.pcharA*/,xMessagePrimitive.intC);                     break;
  58.                         case __glcd_fillScreen : glcd_fillScreen( xMessagePrimitive.intA );                                                                             break;
  59.                         case __glcd_update :
  60.                                 glcd_rect(0,0,127,23,0,1);
  61.                                 xMessageDisp.time = xTaskGetTickCount();
  62.                                 xMessageDisp.buffer = pmensaje;
  63.                                 if( pdTRUE == xQueueSend( pxResources->xQueueDisp, ( void * )&xMessageDisp, portMAX_DELAY ) ){
  64.                                         //while( uxQueueMessagesWaiting( pxResources->xQueueDisp ) )
  65.                                         //      vTaskDelay(100);
  66.                                        
  67.                                         if( pmensaje == &mensajeBuffer[0][0] )  pmensaje = &mensajeBuffer[1][0];
  68.                                         else                                                                    pmensaje = &mensajeBuffer[0][0];
  69.  
  70.                                         glcd_init( 0, pmensaje );
  71.                                 }
  72.                                
  73.                                
  74.                         default:        break;
  75.                 }
  76.         }                      
  77. }
  78.                
  79.  
  80. #endif  //#ifndef TASKACCE_C

La librería Graphics.c
Es la librería que proporciona el compilador CCS para dibujar primitivas 2D básicas como líneas, pixeles, círculos…
Tiene algunas modificaciones:
La función glcd_init acepta un puntero a un array tipo unsigned long, todas las primitivas serán dibujadas en este array.
La función glcd_pixel utiliza una tabla (rotate table) para pintar los píxeles en el buffer.
La función glcd_textArial pinta texto en formato Arial. Las fuentes de este texto han sido generadas con el programa BitFontCreator Pro.


Doble buffer compartido con la tarea TaskPutLeds
La tarea TaskGraphics declara dos buffers del mismo tamaño que la pantalla, para poder representar la información antigua mientras se genera la nueva.
Cuando se recibe el mensaje Qglcd_update, se envía a la tarea TaskPutLeds el buffer pintado y se empieza a dibujar sobre el otro buffer.
Puesto que los buffers son grandes, no se hace copia en el mensaje, solo se envía un puntero. Esto es algo con lo que hay que tener cuidado, y en este caso el tamaño del queue (solo admite 1 mensaje) impide que se produzcan errores.


Paso de punteros en los mensajes
Cuando se pasa un puntero en un mensaje se debe estar seguro que el dato al que apunta “exista” cuando el mensaje es recibido.
En la tarea TaskWatch las funciones Qglcd_text5x7 y Qglcd_Arial pasaban un puntero a la cadena que debía ser dibujada y de vez en cuando se dibujaban caracteres sin sentido… Cuando se trabaja con concurrencia hay que tener en cuenta que todas las tareas se ejecutan en “paralelo” y que mientras una esta leyendo un dato la otra lo puede borrar. Esto se soluciono copiando toda la cadena (hasta 5 caracteres mas el carácter nulo) en el mensaje enviado.


Algunos links:
   BitFontCreator Pro:
   http://www.iseasoft.com/bfc.htm
   Librerías graficas, incluidas en el compilador CCS:
   http://www.ccsinfo.com/
   Computación concurrente:
   http://es.wikipedia.org/wiki/Concurrente

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: J1M en 30 de Marzo de 2009, 03:55:30
Me quito el sombrero José Antonio! Gracias por iluminarnos con tus proyectos. Enhorabuena por la magistral forma en que dedicas tu tiempo libre! :P
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 30 de Marzo de 2009, 17:42:25
La tarea TaskSpeaker

(http://farm4.static.flickr.com/3652/3399050503_204003fc28.jpg)

Responsabilidades:
   Debe generar un sonido utilizando el piezo-altavoz cada vez que reciba un mensaje.
Tipo:
   Tarea consumidora.
Estructura de la tarea:
   Esta dividida en dos funciones, vStartTaskSpeaker y vTaskSpeaker. La primera crea los recursos y la segunda recibe el mensaje y generara el sonido.
Funcionamiento:
   Esta tarea esta inactiva mientras no se reciba ningún mensaje. Una vez recibido un mensaje lo primero que hace esta tarea es intentar obtener un mutex. Esta tarea    comparte un pinIO con la tarea TaskButton y ambas deben estar sincronizadas.
   Cuando se obtiene el mutex se configura el pinIO como salida y se genera el sonido indicado en el mensaje.

Código: C
  1. #ifndef TASKSPEAKER_C
  2. #define TASKSPEAKER_C
  3.  
  4. #include ".\TaskSpeaker.h"
  5.  
  6. int startTaskSpeaker( xTaskSpeakerResources *pxResources ){
  7.         if( pxResources->xMutexIO == NULL ){
  8.                 if( NULL == ( pxResources->xMutexIO = xSemaphoreCreateMutex() ) )
  9.                         return -1;
  10.                 vQueueAddToRegistry( pxResources->xMutexIO, ( signed portCHAR * )"xMutexIO" );
  11.         }              
  12.        
  13.         if( pxResources->xQueueSound == NULL ){
  14.                 if( NULL == ( pxResources->xQueueSound = xQueueCreate( 5, sizeof( xMessageSpeakerSound ) ) ) )
  15.                         return -1;
  16.                 vQueueAddToRegistry( pxResources->xQueueSound, ( signed portCHAR * )"xQueueSpeakerSound" );
  17.         }
  18.        
  19.         if( pdPASS != xTaskCreate( vTaskSpeaker, ( signed portCHAR * ) "Speaker", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  20.                 return -1;
  21.                
  22.         return 0;
  23. }
  24.  
  25. void vTaskSpeaker( void *pvParameters ){
  26.  
  27.         int i, j, k;
  28.         xTaskSpeakerResources *pxResources;
  29.         pxResources = (xTaskSpeakerResources*)pvParameters;    
  30.         xMessageSpeakerSound xMessage;
  31.         portTickType xLastWakeTime;
  32.        
  33.         vTaskSuspend( NULL );
  34.        
  35.         while(1){
  36.                 vTaskDelay( 10 );
  37.                 xQueueReceive( pxResources->xQueueSound, &xMessage,  portMAX_DELAY );
  38.                
  39.                 if( pdTRUE == xSemaphoreTake( pxResources->xMutexIO, TASK_SPEAKER_MUTEX_BLOCKTIME ) ){
  40.                         TRISAbits.TRISA1 = 0;
  41.                        
  42.                         xLastWakeTime = xTaskGetTickCount();
  43.                         for( ; xMessage.reps ; xMessage.reps >>= 1 ){
  44.                                 vTaskDelayUntil( &xLastWakeTime, 500 );
  45. //                              taskENTER_CRITICAL();
  46.                                 vTaskPrioritySet( NULL, pxResources->uxPriorityHigh ); //putleds < newprio < wakeup );
  47.                                 if( xMessage.reps&0x1 ) {
  48.                                         for( j = 0 ; j < 100 ; j++ ){
  49.                                                 for( k = 0 ; k < 0x300 ; k++ );
  50.                                                 PORTAbits.RA1 = !PORTAbits.RA1;
  51.                                         }
  52.                                 }
  53. //                              taskEXIT_CRITICAL();
  54.                                 vTaskPrioritySet( NULL, pxResources->uxPriority );
  55.                         }
  56.                                        
  57.                         TRISAbits.TRISA1 = 1;
  58.                         xSemaphoreGive( pxResources->xMutexIO );
  59.                 }      
  60.         }
  61. }
  62.  
  63.  
  64. #endif  // #ifndef TASKSPEAKER_C

Generar el tono
El mensaje recibido posee 3 datos:
Aunque la función que se muestra arriba funciona, aun esta en desarrollo. Aquí voy a describir su funcionamiento final:
   long pattern: Es la secuencia de sonidos que se va a generar. Por ejemplo SOS podría ser algo como 0b00000010101001110111011100101010.
   int pitch: Es el tiempo que dura cada “bit” de la secuencia.
   int reps: Es el número de veces que se repite la secuencia.


Tomar el control del sistema
Como el piezo-altavoz fue un periférico añadido al sistema mucho tiempo después del diseño del PCB, este no cuenta con el pin mas adecuado para generar audio ( como PWM). El tono es generado con muchas instrucciones PORTAbits.x = ! PORTAbits.x y eso requiere que el procesador este en la tarea TaskSpeaker durante un tiempo relativamente largo sin poder atender otras tareas.
Como el RTOS esta configurado en modo preemtitive se deben evitar los cambios de contexto al menos durante un “bit” de la secuencia. Esto se consigue dando a la tarea TaskSpeaker la prioridad más alta.
Una opción anterior fue utilizar las funciones taskENTER_CRITICAL y taskEXIT_CRITICAL pero no resultaron muy adecuadas puesto que modificaban indirectamente el comportamiento de la función vTaskDelayUntil.

Algunos links:
   Preemption:
      http://en.wikipedia.org/wiki/Preemptive_multitasking

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: todopic en 30 de Marzo de 2009, 21:41:11
Realmete IM PRE SIO NAN TE!!!!  :-/

aún me estoy levantando los dientes je je je!!! muy buen proyecto, bien detallado

Gracias por compartirlo!!!!

Norberto
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: barral en 31 de Marzo de 2009, 03:32:23
Simplemente IMPRESIONANTE. Es de admirar tu trabajo, y mucho más que lo compartas y lo expliques tan bien. A estudiar RTOS, la verdad es que yo hace bastante tiempo que quería echarle mano a alguno y siempre falta tiempo... ahora es el momento.

Un saludo y mis más sinceras felicitaciones.
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: RICHI777 en 31 de Marzo de 2009, 11:17:01
Hola jgpeiro, realmente excelente el proyecto, hermano me hiciste googlear bastante ( y eso es bueno ) de como se hacia para leer estados con un LED en inversa, espectacular !!!, excelente tambien lo del acelerometro. Si en este foro se premiara al mejor proyecto, no dudes que para mi lo ganas de una, sencillo, innovador, excelentemente diseñado, escrito y explicado.

Saludos cordiales !
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 31 de Marzo de 2009, 12:48:03
La tarea TaskPutLeds

(http://farm4.static.flickr.com/3659/3401202857_8af7cd8a86_o.jpg)

Responsabilidades:
   Debe imprimir una imagen de 128x24 sincronizada con el movimiento de la mano.
Tipo:
   Es una tarea consumidora.
Estructura de la tarea:
   Esta dividida en dos funciones, vStartTaskSpeaker y vTaskSpeaker. La primera crea los recursos y la segunda recibe la imagen en un mensaje y la imprime cuando se lo
indica la tarea TaskAcce.

Funcionamiento:
   Puesto que los LEDs son un recurso compartido controlado por mutex, lo primero que hace antes de mostrar nada es intentar obtener el mutex.
   Una vez tiene el mutex configura los LEDs y espera, durante 256 ticks, un mensaje de la tarea TaskAcce para mostrar la imagen. Después de mostrar la imagen volverá a esperar el mensaje de la tarea TaskAcc. Cuando pasen mas de 256 ticks y no se reciba un mensaje liberara el mutex.

Código: C
  1. #ifndef TASKPUTLEDS_C
  2. #define     TASKPUTLEDS_C    
  3.  
  4. #include     ".\TaskPutLeds.h"
  5.  
  6. int startPutLedsTask( xTaskPutLedsResources *pxResources )
  7. {
  8.     if( pxResources->xMutexLeds == NULL )
  9.     {
  10.         if( NULL == ( pxResources->xMutexLeds = xSemaphoreCreateMutex( ) ) )
  11.             return -1;
  12.         vQueueAddToRegistry( pxResources->xMutexLeds, ( signed portCHAR * )"xMutexLeds" );
  13.     }
  14.     if( pxResources->xQueueMove == NULL )
  15.     {
  16.         if( NULL
  17.              == ( pxResources->xQueueMove
  18.                    = xQueueCreate( 5, sizeof( xMessagePutLedsMove ) ) ) )
  19.             return -1;
  20.         vQueueAddToRegistry( pxResources->xQueueMove, ( signed portCHAR * )"xQueuePutLedsMove" );
  21.     }
  22.     if( pxResources->xQueueDisp == NULL )
  23.     {
  24.         if( NULL
  25.              == ( pxResources->xQueueDisp
  26.                    = xQueueCreate( 1, sizeof( xMessagePutLedsDisp ) ) ) )
  27.             return -1;
  28.         vQueueAddToRegistry( pxResources->xQueueDisp, ( signed portCHAR * )"xQueuePutLedsDisp" );
  29.     }
  30.  
  31.     if( pdPASS != xTaskCreate( vPutLedsTask, ( signed portCHAR * ) "PutLEDs", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  32.     return -1;
  33.  
  34.     return 0;
  35. }
  36.  
  37.  
  38.  
  39. void vPutLedsTask( void *pxParameters )
  40. {
  41.  
  42.     xTaskPutLedsResources * pxResources;                         /*  < Task parameteres handler. >                 */
  43. //  portTickType xLastWakeTime;             // Task waketime container.
  44.     xMessagePutLedsMove    xMessageMove;
  45.     xMessagePutLedsDisp    xMessageDisp;
  46.  
  47.                     int    i;
  48.                     int    whilecnt;
  49.     unsigned  long         printtime;
  50.                     char   failAcnt = 0,
  51.                            failBcnt = 0,
  52.                            failCcnt = 0;
  53.     Differentiator         dffrnttr1;
  54.  
  55.     DifferentiatorConstruct( &dffrnttr1 );
  56.  
  57.     pxResources = ( xTaskPutLedsResources * )pxParameters;
  58.  
  59.     vTaskSuspend( NULL );
  60.  
  61.     // Wait the first display data.
  62.     while( !uxQueueMessagesWaiting( pxResources->xQueueDisp ) )
  63.         vTaskDelay( 100 );
  64.  
  65.     while( 1 )
  66.     {
  67.  
  68.         vTaskDelay( 1000 );                                     /*    If your task take a mutex, be sure that     */
  69.                                                                 /*        release for a few ticks                 */
  70.  
  71.         xQueueReceive( pxResources->xQueueDisp, &xMessageDisp, 0 );
  72.                                                                 /*    Receive new display data for print. If      */
  73.                                                                 /*        nonew display data in 10 ticks, the     */
  74.                                                                 /*        task print last received data.          */
  75.  
  76.         if( pdTRUE == xSemaphoreTake( pxResources->xMutexLeds, portMAX_DELAY ) )
  77.         {                                                       /*    Take the mutex for operate with LEDs.       */
  78.  
  79.             put_LEDS( 0x0UL );                                  /*      Config LEDs hardware for operation        */
  80.             set_LEDS_DIR( 0x0UL );
  81.             set_COLS_DIR( 3 );
  82.  
  83.             while( pdTRUE == xQueueReceive( pxResources->xQueueMove, &xMessageMove, 256 ) )
  84.                                                                 /*      Wait for a movement in next 256 ticks.    */
  85.             {                                                  
  86.  
  87.                 xQueueReceive( pxResources->xQueueDisp, &xMessageDisp, 0 );
  88.  
  89.                 DifferentiatorProcessV( &dffrnttr1, xMessageMove.time );
  90.                 printtime = xMessageMove.time - 64 + dffrnttr1.out / 2;
  91.                                                                 /*        Check if the printtime is in suposed    */
  92.                                                                 /*            valid range.                        */
  93.                 if( !( xTaskGetTickCount( ) <= printtime
  94.                         && printtime < xTaskGetTickCount( ) + 128 ) )
  95.                 {
  96.                     failAcnt++;                                 /*          Chech a fails occurs, only for        */
  97.                                                                 /*              application debug.                */
  98.                     DifferentiatorPreset( &dffrnttr1, xTaskGetTickCount( ) - 64 );
  99.                                                                 /*          Set approx. valid previous movement   */
  100.                                                                 /*              time.                             */
  101.  
  102.                 }
  103.                 else
  104.                 {
  105.                     vTaskDelay( printtime - xTaskGetTickCount( ) );
  106.                                                                 /*          Wait to printtime. Print 128 cols in  */
  107.                                                                 /*              any direction.                    */
  108.                     for( i = ( xMessageMove.direction == 1 )
  109.                                ? 0:127; 0 <= i && i < 128;
  110.                          i += xMessageMove.direction )
  111.                     {
  112.                         put_LEDS( xMessageDisp.buffer[ i ] );
  113.                         vTaskDelay( 1 );
  114.                     }
  115.                     put_LEDS( 0x0UL );                          /*          Make sure all LEDs are off. Wait next */
  116.                                                                 /*              move with LEDs off.               */
  117.                 }
  118.             }
  119.  
  120.             set_COLS_DIR( 0 );                                  /*      Shutdown LEDs.                            */
  121.             set_LEDS_DIR( ~0x0UL );
  122.             put_LEDS( 0x0UL );
  123.  
  124.             xSemaphoreGive( pxResources->xMutexLeds );          /*      Release LEDs mutex.                       */
  125.         }
  126.     }
  127. }
  128.  
  129.  
  130. #endif                                                          /*#ifndef TASKACCE_C                              */



Sincronización con el movimiento de la mano
La tarea TaskPutLeds recibe mensajes de la tarea TaskAcce indicando que la mano de el usuario esta a la izquierda o a la derecha del todo, pero esto no es suficiente para mantener estable la imagen en el aire.
Se calcula el instante en que se debe mostrar la imagen ( O[n] ) en base a los dos últimos instantes en que la mano se encontraba a la izq./drcha. del todo (I[n] e I[n-1] ). Conociendo estos dos datos y el “tamaño” de la imagen 128 se puede calcular O[n] de la siguiente manera:
      O[n] = I[n] + ( I[n] – I[n-1] )/2 -128/2.
      Donde I[n] – I[n-1] puede ser sido sustituido por DIFF(I[n])
Si por alguna razón el instante O[n] pertenece al pasado o a un futuro lejano se asigna un nuevo instante O[n] aproximado.

En esta imagen se muestra el movimiento y la imagen mostrada.
(http://farm4.static.flickr.com/3605/3401198205_f601af0e47_o.jpg)


Uso de la CPU
Puesto que esta tarea es larga, y además tiene una prioridad alta, se ha diseñado para liberar a la CPU el máximo tiempo posible, permitiendo así a otras tareas tomar la CPU para realizar sus trabajos. El tiempo de CPU consumido por esta tarea es inferior al 5%.

En esta imagen se muestran los estados de la tarea TaskPutLeds
(http://farm4.static.flickr.com/3545/3402002468_79f728b457_o.jpg)

Impedir que el sistema se bloquee debido a queues llenos.
Un criterio de diseño escogido para la mayoría de las tareas productoras/procesadoras ha sido que si el queue de salida de la tarea esta lleno esta se bloquee.
Si la tarea PutLeds no consume mensajes durante mucho tiempo bloqueara indirectamente a la tarea anterior, y esta a la anterior… Para evitar esto periódicamente consume mensajes aunque no se utilicen.

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: SavageChicken en 31 de Marzo de 2009, 14:35:44
Increiblemente bueno, además de toda la documentación anexa y la explicación tan clara. !!!

Me sorprendió mucho el uso del led en inversa como sensor, es un uso que desconocía de este elemento que tan amenudo usamos.

Muchas gracias y salud  8)
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 31 de Marzo de 2009, 20:44:06
La tarea WakeUp
 
(http://farm4.static.flickr.com/3592/3403108250_5526902316_o.jpg)

Responsabilidades:
Debe despertar a todas las tareas cuando el usuario pulse el botón y volverlas a dormir si en 20 Segundos no se pulsa nada.
Tipo:
   Es una tarea de control.
Estructura de la tarea:
Esta dividida en dos funciones, vStartTaskWakeUp y vTaskWakeUp. La primera crea los recursos y la segunda despierta o duerme al resto de tareas.
Funcionamiento:
Esta tarea espera continuamente un mensaje de la tarea TaskButton. Cuando lo recibe despierta a todas las tareas del sistema. Ahora comprueba la tarea TaskGetLeds para resetear un contador y la tarea TaskButton para borrarlo (lo pone a su valor máximo). Cuando el contador llega a su valor máximo suspende todas las tareas del sistema, excepto la tarea TaskButton.
Además, aunque en principio no es necesario, la tarea TaskWakeUp también apaga los LEDs, el mosfet y el acelerómetro.

Codigo de la tarea:
Código: C
  1. #ifndef TASKWAKEUP_C
  2. #define TASKWAKEUP_C
  3.  
  4. #include "Hardware.h"   // incluir en taskwakeup.h
  5. #include "LEDs.h"               // incluir en taskwakeup.h
  6. #include ".\TaskWakeUp.h"
  7.  
  8. int startTaskWakeUp( xTaskWakeUpResources *pxResources ){
  9.        
  10.         if( pxResources->xQueueButtonKey == NULL ){
  11.                 if( NULL == ( pxResources->xQueueButtonKey = xQueueCreate( 5, sizeof( xMessageWakeUpKey ) ) ) )
  12.                         return -1;
  13.                 vQueueAddToRegistry( pxResources->xQueueButtonKey, ( signed portCHAR * )"xQueueWakeUpButtonKey" );
  14.         }
  15.  
  16.         if( pxResources->xQueueGetLedsKey == NULL ){
  17.                 if( NULL == ( pxResources->xQueueGetLedsKey = xQueueCreate( 5, sizeof( xMessageWakeUpKey ) ) ) )
  18.                         return -1;
  19.                 vQueueAddToRegistry( pxResources->xQueueGetLedsKey, ( signed portCHAR * )"xQueueWakeUpGetLedsKey" );
  20.         }
  21.  
  22.         if( pxResources->xQueueAlarm == NULL ){
  23.                 if( NULL == ( pxResources->xQueueAlarm = xQueueCreate( 5, sizeof( xMessageWakeUpAlarm ) ) ) )
  24.                         return -1;
  25.                 vQueueAddToRegistry( pxResources->xQueueAlarm, ( signed portCHAR * )"xQueueWakeUpAlarm" );
  26.         }
  27.  
  28.        
  29.         if( pdPASS != xTaskCreate( vTaskWakeUp, ( signed portCHAR * ) "WakeUp", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  30.                 return -1;
  31.        
  32.         return 0;              
  33. }
  34.  
  35. void vTaskWakeUp( void *pvParameters ){
  36.  
  37.         int i;
  38.         portTickType xLastWakeTime;
  39.         xTaskWakeUpResources *pxResources;
  40.         pxResources = (xTaskWakeUpResources*)pvParameters;     
  41.         xMessageWakeUpKey xMessageKey1, xMessageKey2;
  42. //      xMessageWakeUpAlarm xMessageAlarm;
  43.         IFS3bits.RTCIF = 0;
  44.         IEC3bits.RTCIE = 0;                     // Disable RTCC interrupt
  45.        
  46.         while(1){
  47.                 vTaskDelay( 100 );
  48.                 if( pdTRUE == xQueueReceive( pxResources->xQueueButtonKey, &xMessageKey1, 0 )
  49.                         || IFS3bits.RTCIF == 1 ){       // Poll for interrup flag.
  50.                         // clear irq flag in taskwatch after sendSound Queue
  51.                         //IFS3bits.RTCIF = 0;                   // Clear interrup flag.
  52.                 //      clock = 8MIPS;                                                                                          // Fast operation.
  53.                        
  54.                         ACCS = 1;                                                                                                       // Power-up external hardware
  55.                        
  56.                         for( i = 0 ; i < 8 ; i++ ){                                                                     // Resume a list of task.
  57.                                 if( pxResources->xHandleTasks[i] != NULL )
  58.                                         vTaskResume( pxResources->xHandleTasks[i] );
  59.                         }
  60.                        
  61.                         xLastWakeTime = xTaskGetTickCount();
  62.                         for( i = 0 ; i < 60 ; i++ ){                                                            // Task operation for a 30 seconds.
  63.                                 if( xQueueReceive( pxResources->xQueueButtonKey, &xMessageKey1, 0 ) ) i = 60;
  64.                                 if( xQueuePeek( pxResources->xQueueGetLedsKey, &xMessageKey2, 1000 ) )   i = 0;
  65.                                 vTaskDelayUntil( &xLastWakeTime, 1000 );
  66.                         }
  67.                        
  68.                         for( i = 7 ; i >=0 ; i-- ){                                                                     // Suspend a list of task waiting mutexes in REVERSE ORDER(respect vTaskResume()).
  69.                                 if( pxResources->xHandleTasks[i] != NULL ){
  70.                                         if( pxResources->xMutexes[i] != NULL ){
  71.                                                 while( 1 != uxQueueMessagesWaiting(pxResources->xMutexes[i]) )
  72.                                                         vTaskDelay(1);
  73.                                         }
  74.                                         vTaskSuspend( pxResources->xHandleTasks[i] );
  75.                                 }
  76.                         }
  77.                        
  78.                         set_COLS_DIR( 0 );                                                                                      // Shut-down external hardware.
  79.                         set_LEDS_DIR( ~0x0UL );
  80.                         put_LEDS( 0x0UL );     
  81.                        
  82.                         ACCS = 0;
  83.                        
  84.                 //      clock = 32Khz;                                                                                          // Slow operation, low power.
  85.                 }
  86.         }      
  87. }
  88.  
  89. #endif  // #ifndef TASKWAKEUP_C


El orden para suspender y resumir tareas
Si se suspende una tarea consumidora antes que una productora se llenaran los queues de mensajes. Estos mensajes serán leídos cuando se vuelva a resumir la tarea consumidora y probablemente ya no tengan utilidad.
El orden que se ha escogido para resumir tareas es:
Consumidoras.
Procesadoras.
Productoras.
El orden para suspenderlas es el contrario. Esto evita que se llenen los queues mientras el sistema entero se suspende.
La tarea TaskWakeUp posee la prioridad más alta del sistema, y eso le permite tomar la CPU siempre que la necesite. Por este motivo todas las tareas serán resumidas al mismo tiempo, pero este orden puede ser útil a la hora de suspenderlas.

Suspender una tarea que posee un mutex
Si suspendemos una tarea que tiene un mutex, nunca lo liberará, y estaremos bloqueando indirectamente a otra tarea que se encuentre esperando ese mutex.
También es importante asegurar que un periférico no esta en uso cuando se bloquea a la tarea que lo controla.
Para evitar todo esto la tarea TaskWakeUp “conoce” los mutex que puede poseer cada tarea. Si una tarea posee un mutex, se espera a que lo suelte antes de suspenderla.

Modo bajo consumo
Cuando todas las tareas están suspendidas la tarea TaskWakeUp cambia el reloj del sistema a 32kHz, esto permite seguir funcionando al RTOS y a la tarea TaskButton pero con un consumo aproximado de 0.1 mA y da un tiempo de funcionamiento teórico de 1 año.
Cuando el usuario pulsa el botón, la tarea TaskWakeUp vuelve a cambiar el reloj del sistema a 8MIPs y resume todas las tareas.

“Interrupción” por RTCC
La tarea TaskWakeUp comprueba (pooling) continuamente el flag de interrupción por RTCC. Cuando la alarma esta activada es capaz de despertar al sistema y generar el sonido de alarma. Esta opción no esta terminada ya que aun no he podido programar la alarma del RTCC, pero de momento no tiene importancia.

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 31 de Marzo de 2009, 20:49:46
La función Main

Responsabilidades:
Debe inicializar todos los periféricos de la CPU, configurar la aplicación y arrancar el RTOS
Variables
   xTaskAcceResources   xTaskAcce1;
xTaskPutLedsResources xTaskPutLeds1;
xTaskGetLedsResources xTaskGetLeds1;
xTaskButtonResources xTaskButton1;
xTaskSpeakerResources xTaskSpeaker1;
xTaskWakeUpResources xTaskWakeUp1;
xTaskGraphicsResources xTaskGraphics1;
xTaskWatchResources xTaskWatch1;

Funcionamiento:
Primero llama a la función VisualInitialization(). Esta función ha sigo generada totalmente con la herramienta VisualDeviceInitializar que incorpora el MPLAB.
Después llama a todas las funciones del tipo vStartTaskX y va copiando los recursos de unas tareas a otras.
Finalmente arranca el RTOS llamando a la función vTaskStartScheduler.

Aplicación creada por la tarea main:
(http://farm4.static.flickr.com/3599/3382685114_9590b408c3_o.jpg)

Código: C
  1. /* Standard includes. */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <limits.h>
  5. #include <math.h>
  6. #include <string.h>
  7.  
  8. /* Device description includes */
  9. #include "P24FJ64GA004.h"
  10.  
  11. /* Scheduler includes. */
  12. #include "FreeRTOS.h"
  13. #include "task.h"
  14. #include "queue.h"
  15. #include "semphr.h"
  16.  
  17. /* Application includes*/
  18. #include ".\utils\Hardware.h"
  19. #include ".\utils\LEDs.h"
  20. #include ".\utils\GLCD.h"
  21. //#include "Filters.h"
  22. //#include "CORDIC.h"
  23.  
  24. #include ".\Tasks\TaskAcce.h"
  25. #include ".\Tasks\TaskPutLeds.h"
  26. #include ".\Tasks\TaskGetLeds.h"
  27. #include ".\Tasks\TaskButton.h"
  28. #include ".\Tasks\TaskSpeaker.h"
  29. #include ".\Tasks\TaskWakeUp.h"
  30. #include ".\Tasks\TaskWatch.h"
  31. #include ".\Tasks\TaskGraphics.h"
  32.  
  33. #include ".\Generated Files\VDIInit\init_PIC24FJ64GA004.sinit_vi.h"
  34.  
  35.  
  36. /*-----------------------------------------------------------*/
  37. // Create Tasks handlers, these must be placed as a GLOBAL varibles.
  38. xTaskAcceResources      xTaskAcce1;
  39. xTaskPutLedsResources xTaskPutLeds1;
  40. xTaskGetLedsResources xTaskGetLeds1;
  41. xTaskButtonResources xTaskButton1;
  42. xTaskSpeakerResources xTaskSpeaker1;
  43. xTaskWakeUpResources xTaskWakeUp1;
  44. xTaskGraphicsResources xTaskGraphics1;
  45. xTaskWatchResources xTaskWatch1;
  46.  
  47. /*      Create the demo tasks then start the scheduler.  */
  48. int main( void ){      
  49.        
  50.         /* Configure any hardware required for this demo. */
  51.         VisualInitialization();
  52.  
  53.  
  54.  
  55.         /* Create the test tasks defined within this file. */
  56.        
  57.         // Productor role tasks
  58.         xTaskAcce1.uxPriority = 5;
  59.         if( startAcceTask(&xTaskAcce1) )        return -1;
  60.  
  61.         xTaskGetLeds1.uxPriority = 1;
  62.         if( startGetLedsTask(&xTaskGetLeds1) )  return -1;
  63.  
  64.         xTaskButton1.uxPriority = 1;
  65.         if( startTaskButton( &xTaskButton1 ) )  return -1;
  66.        
  67.         // Processor role tasks
  68.         xTaskWatch1.xQueueSound = xTaskGetLeds1.xQueueSound;
  69.         xTaskWatch1.xQueueKey = xTaskGetLeds1.xQueueKey;
  70.         xTaskWatch1.uxPriority = 2;
  71.         if( startTaskWatch( &xTaskWatch1 ) )    return -1;
  72.  
  73.         xTaskGraphics1.xQueuePrimitive = xTaskWatch1.xQueuePrimitive;
  74.         xTaskGraphics1.uxPriority = 3;
  75.         if( startTaskGraphics( &xTaskGraphics1 ) )      return -1;
  76.        
  77.         // Consumer role tasks 
  78.         xTaskSpeaker1.xMutexIO = xTaskButton1.xMutexIO;
  79.         xTaskSpeaker1.xQueueSound = xTaskWatch1.xQueueSound;
  80.         xTaskSpeaker1.uxPriority = 4;
  81.         xTaskSpeaker1.uxPriorityHigh = 7;
  82.         if( startTaskSpeaker( &xTaskSpeaker1 ) )        return -1;
  83.        
  84.         xTaskPutLeds1.xMutexLeds = xTaskGetLeds1.xMutexLeds;
  85.         xTaskPutLeds1.xQueueMove = xTaskAcce1.xQueue;
  86.         xTaskPutLeds1.xQueueDisp = xTaskGraphics1.xQueueDisp;
  87.         xTaskPutLeds1.uxPriority = 6;
  88.         if( startPutLedsTask(&xTaskPutLeds1) )  return -1;
  89.        
  90.         // Control tasks
  91.         xTaskWakeUp1.xHandleTasks[7] = xTaskGetLeds1.xHandle;   xTaskWakeUp1.xMutexes[7] = xTaskPutLeds1.xMutexLeds;            // Productor Task
  92.         xTaskWakeUp1.xHandleTasks[6] = xTaskAcce1.xHandle;              xTaskWakeUp1.xMutexes[6] = NULL;                                                        // Productor Task
  93.         xTaskWakeUp1.xHandleTasks[5] = NULL;                                    xTaskWakeUp1.xMutexes[5] = NULL;                                                       
  94.         xTaskWakeUp1.xHandleTasks[4] = xTaskWatch1.xHandle;             xTaskWakeUp1.xMutexes[4] = NULL;                                                        // Processor Task
  95.         xTaskWakeUp1.xHandleTasks[3] = xTaskGraphics1.xHandle;  xTaskWakeUp1.xMutexes[3] = NULL;                                                        // Processor Task
  96.         xTaskWakeUp1.xHandleTasks[2] = NULL;                                    xTaskWakeUp1.xMutexes[2] = NULL;
  97.         xTaskWakeUp1.xHandleTasks[1] = xTaskSpeaker1.xHandle;   xTaskWakeUp1.xMutexes[1] = xTaskSpeaker1.xMutexIO;                      // Consumer Task
  98.         xTaskWakeUp1.xHandleTasks[0] = xTaskPutLeds1.xHandle;   xTaskWakeUp1.xMutexes[0] = xTaskPutLeds1.xMutexLeds;            // Consumer Task
  99.        
  100.         xTaskWakeUp1.xQueueButtonKey = xTaskButton1.xQueueKey;
  101.         xTaskWakeUp1.xQueueGetLedsKey = xTaskGetLeds1.xQueueKey;
  102.         xTaskWakeUp1.uxPriority = 8;
  103.         if( startTaskWakeUp( &xTaskWakeUp1 ) )  return -1;     
  104.        
  105.  
  106.  
  107.                
  108.         /* Finally start the scheduler. */
  109.         vTaskStartScheduler();
  110.  
  111.         return 0;
  112. }

Crear, configurar e interconectar tareas
La estructura xTaskXXResources engloba los manejadores (punteros) de los recursos utilizados por la tarea TaskXX. Es inicializada al llamar a la función vStartTaskXX( & xTaskXXn ). Esta devuelve un 0 todo ha ido bien y un -1 si ha habido algún error.
Se puede observar como coincide el diagrama de la aplicación con las asignaciones que se van haciendo entre la inicialización de cada tarea.


La estructura xTaskXXResources esta definida en el archivo TaskXX.h de la siguiente manera:

Código: C
  1. typedef struct xTaskXXResources{       
  2. xTaskHandle xHandle;                            // Task handler.
  3. unsigned portBASE_TYPE uxPriority;              // Task priority.
  4. xSemaphoreHandle xMutexXX;                      // Mutex handler.
  5. xQueueHandle xQueueXX;                          // Queue handler.
  6. }xTaskXXResources;

La tarea vStartTaskXX tiene la siguiente forma básica, definida en el archivo TaskXX.c:

Código: C
  1. int vStartTaskXX( xTaskXXResources *pxResources ){
  2. if( pxResources->xMutexXX == NULL ){
  3.         if( NULL == ( pxResources->xMutexXX = xSemaphoreCreateMutex() ) )
  4.                 return -1;
  5.         vQueueAddToRegistry( pxResources->xMutexXX, ( signed portCHAR * )"xMutexXXName" );
  6. }              
  7.        
  8. if( pxResources->xQueueXX == NULL ){
  9.         if( NULL == ( pxResources->xQueueXX = xQueueCreate( 5, sizeof( xMessageXX ) ) ) )
  10.                 return -1;
  11.         vQueueAddToRegistry( pxResources->xQueueXX, ( signed portCHAR * )"xQueueXXName" );
  12. }
  13.        
  14. if( pdPASS != xTaskCreate( vTaskXX, ( signed portCHAR * ) "TaskName", configMINIMAL_STACK_SIZE, pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
  15.         return -1;
  16.                
  17. return 0;
  18. }

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 31 de Marzo de 2009, 21:06:59
y FIN

Conclusiones
Este gadjet ha sido construido con herramientas caseras. El acabado final no es como el de un producto comercial, pero creo que el diseño, tanto software como hardware, si lo son.

El objetivo de este proyecto ha sido iniciarse en los RTOS y lo ha cumplido a la perfección. También se ha desarrollado una técnica de programación orientada a objetos útil para todos los que programamos en C y he improvisado un método práctico para la divulgación y especificación del funcionamiento global y detallado del sistema.

Según lo que he visto, un RTOS no proporciona una ventaja tan grande como lo es el paso de ASM a C, pero creo que se vuelve imprescindible a la hora de crear sistemas ROBUSTOS con más de 2500 líneas de código.
Para este proyecto se han escrito casi 50 archivos, y creo que no hay ninguno con más de 500 líneas. Esto refleja un buen enfoque en la división de los algoritmos según el principio “divide y vencerás”.También se ha seguido otro principio que ha permitido, de manera invisible, una mayor "módularidad" del código. Este ha sido el principio de “la regularidad favorece la simplicidad”.

La herramienta RTOSViewer del MPLAB ha sido de gran ayuda en la depuración del sistema. Ha permitido conocer en cada momento el estado del RTOS y sus recursos. Es casi imprescindible, sobre cuando uno esta empezando con los RTOS...

Una de las ideas que más me han gustado ha sido la de utilizar un conector ICSP compatible con el conector macho del tipo miniUSB.
Este conector es sencillo, muy asequible, ocupa poco espacio en placa (6mm x 6mm) y es atractivo para el usuario final.



Mejoras
Algoritmo de detección y sincronización: Aunque el algoritmo actual es sencillo y funciona bastante bien, es la mejora más útil que se le puede hacer a este proyecto. Creo que también podría añadirse algún tipo de corrección sobre el desplazamiento vertical.
RTOS: La aplicación ha sido implementada con recursos del FreeRTOS. Quizás se pueda simplificar o añadir nuevas funcionalidades con un RTOS mas completo.
DEPURACION DEL RTOS: El RTOS posee una serie de utilidades para seguir desde el exterior su funcionamiento. En proyectos posteriores intentaré sacarles partido.
   LEDs: Por supuesto cuantos mas leds, mejor resolución vertical.
LEDs RGB: Seria una opción interesante añadir mas colores a la aplicación. La harían mucho más vistosa, aunque el PIC24 no tiene más pines para controlarlos.
PCB: El PCB esta en general bien, aunque, junto con las pilas es algo grande para utilizarlo de llavero.
PESO: La verdad, mover rápido dos pilas AAA durante un rato se hace incomodo. Todo el peso que se pueda eliminar al gadjet se ganara en comodidad de uso. Creo que se podría utilizar una sola pila y un elevador de voltaje.

Bugs
   SCH y PCB
Los componentes MOSFET y ACELEROMETRO no tienen correctamente asignados los pines en el encapsulado. Esto se ha solucionado mediante apaños en la placa ya soldada.
   SOFTWARE:
Si que existirán, pero aun no he encontrado ninguno que se merezca aparecer aquí.

Agradecimientos
Agradezco a toda la comunidad de todopic, y especialmente a Nocturno, septiembre_negro, aitopes, MLO__, vtasco, J1M, todopic, barral, RICHI777 y SavageChicken los buenos comentarios sobre el proyecto que sin duda me han motivado mucho para seguir creando gatjets en el futuro.


Algunos Links
Proyecto: ManualLedScanner, versión 2.45. José Antonio García Peiró. 2009.
   http://rapidshare.com/files/215956617/ManualLedScanner_2v46.rar

(http://farm4.static.flickr.com/3585/3402338583_3ca69f1ec2_m.jpg)
   

Algunos documentos de consulta
   Sistemas embebidos
      Libro: Embedded Systems Firmware
   RTOS
      Libro: Real Time Concepts for Embedded Systems
   Programación orientada a objetos en ANSI C
      PDF: Implementing object-oriented designs in ANSI-standard C
PDF: Object-oriented programming with ANSI C

Software utilizado
   MPLAB 8.30
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en019469∂=SW007002
MPLAB PIC24 Compiler 3.12
http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en535364
Altium Winter 09
http://www.altium.com/products/altium-designer/en/altium-designer_home.cfm
Proteus 7
http://www.labcenter.co.uk/index.cfm

Me despido hasta el próximo proyecto.

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: kain589 en 31 de Marzo de 2009, 21:16:47
Muy buen proyecto, creo que me quedo corto, un "proyectazo" y muy bien documentado, se ve espectacular y util para programas complejos. Espero con el tiempo, despues de todo lo que aún me queda por hacer, poder hacer algo en Rtos y seguro que este post servira de ayuda. Muchas gracias y suerte con los proximos proyectos
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: MLO__ en 31 de Marzo de 2009, 21:55:07
clap clap clap clap clap clap clap clap

Lo digo nuevamente: EXCELENTE TRABAJO

Recibe mis humildes agradecimientos por el hilo. Ya estoy en la tarea de comprenderlo -no he usado nunca el RTOS, pero si que me he motivado a hacerlo-.
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: MGLSOFT en 31 de Marzo de 2009, 23:29:02
FA BU LO SO
No quise molestarte antes con comentarios para no desarmarte el hilo...
Decididamente esperare tu proximo proyecto...
Este ya tiene su chincheta... :mrgreen:
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Nocturno en 01 de Abril de 2009, 01:58:45
y FIN

¡¡¡¿Yaaaaaaaaaaa?!!!, que no se acabe por favor.

Me despido hasta el próximo proyecto.
No tardes  :P
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: barral en 01 de Abril de 2009, 04:42:39
Espero impaciente un nuevo proyecto tuyo. Aunque la verdad, primero voy a mirar este con calma y a hacer experimentos con RTOS, primero en casa (cuando encuentre algo de tiempo) y después intentaré introducirlo en mi trabajo, ya hace tiempo que creo que hay aplicaciones para las que va muy bien y todavía no me he echado "al ruedo".

Un saludo y muchas gracias por tan excelente trabajo.
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Suky en 01 de Abril de 2009, 12:29:27
Acabo de mirar este proyecto, y lo único que puedo es decir Maravilloso! Te felicito y muchas gracias por compartirlo!
Saludos!
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 04 de Abril de 2009, 07:17:44
Estoy pensando en añadir un apendice al hilo como un video tutorial de como compilar y depurar el proyecto. Pero antes necesito saber si alguien que ha intentado probar/modificar el codigo ha tenido alguno problema en especial?.

Tambien quiero añadir un "errata sheet" con algunos fallos en los posts. Yo ya he visto y modificado uno en el post sobre la tarea TaskAcce. Si alguien cree que ha encontrado uno que lo comunique.

Saludos.

Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: AKENAFAB en 02 de Mayo de 2009, 23:41:53

Espectacular.

  Que buen rollo



Felicitaciones, muhcas gracias por compartir.

Me haces ver mi pereza y desorden al programar.Vaya que me gustaria hacerlo de tal manera tan organizada  y bien estructura como tu.

Un saludo!
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: MLO__ en 02 de Mayo de 2009, 23:46:14
Estoy pensando en añadir un apendice al hilo como un video tutorial de como compilar y depurar el proyecto.

No lo pienses mas!!!!! seria excelente
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: Micro23 en 14 de Mayo de 2009, 14:06:10
Guau!! Que laburo que te mandaste realmete es para sacarse la galera. Felicitaciones!!!
Quiciera entender FreeRTOS como lo entendes vos, que consejo me das? por donde puedo empezar?
Saludos
Título: Re: Emulador de un Casio F91W basado en FreeRTOS
Publicado por: jgpeiro06 en 14 de Mayo de 2009, 16:25:59
Citar
Quiciera entender FreeRTOS como lo entendes vos, que consejo me das? por donde puedo empezar?
bueno, yo no soy ningún experto en RTOS, solo he realizado un par de proyectos con este tipo de "herramientas" para el desarrollo de sistemas embebidos.
Mi recomendación es que empieces leyendo este proyecto y que lo descargues desde la pagina de www.micropic.es ya que el link de descarga que aparece aquí esta caducado. Empieza probando el proyecto en el MPLAB, simulando paso a paso, con el RTOS Viewer siempre cerca para ir viendo como se crean las tareas.
Todas las funciones del RTOS estan bien documentadas en su pagina web www.freertos.org así que tendrás que acudir allí a leer como están definidas y que hacen.
Un nivel de C medio es recomendable, para entender bien las estructuras, punteros y demás historias, pero no es indispensable.
También  prueba el ejemplo que trae el propio FreeRTOS para los PIC.
Buscar en la red sobre programación en tiempo real y otros conceptos de sistemas operativos como semáforos, colas, concurrencia, tareas, prioridades, delay...

Sobre todo prueba, modifica, compila, depura.... los programas que ya funcionen. Cuando antes empieces, antes comprobaras las ventajas que ofrecen.

y pregunta/sugiere aquí cualquier cosa, sobre el funcionamiento de este proyecto o sobre los RTOS en general, para que todos podamos aprender un poco más....

Saludos