Autor Tema: Emulador de un Casio F91W basado en FreeRTOS  (Leído 23958 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Emulador de un Casio F91W basado en FreeRTOS
« 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
    

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.
 


La imagen muestra la disposición de todos los elementos que se mostraran según el estado del reloj.
 

Esta  imagen muestra como se usan los LEDs cuando se detecta una pulsación.
 
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.
 

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/   


Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17866
    • MicroPIC
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #1 en: 24 de Marzo de 2009, 13:56:54 »
Plas, plas, plas, a tus pies, jgpeiro.

¿Publicarás el código?
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado septiembre_negro

  • PIC18
  • ****
  • Mensajes: 310
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #2 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

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #3 en: 24 de Marzo de 2009, 17:01:30 »
Aquí añado un video que muestra como se usa el aparato....

« Última modificación: 05 de Abril de 2009, 16:54:06 por jgpeiro06 »

Desconectado aitopes

  • Moderadores
  • DsPIC33
  • *****
  • Mensajes: 5102
    • uControl
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #4 en: 24 de Marzo de 2009, 17:01:56 »
Buenisimo!
Aunque no logro ver las imagenes :(
Si cualquier habilidad que aprende un niño será obsoleta antes de que la use, entonces, ¿qué es lo que tiene que aprender? La respuesta es obvia:
La única habilidad competitiva a largo plazo es la habilidad para aprender
“. Seymour Papert

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #5 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.

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
  58.  

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.

« Última modificación: 07 de Abril de 2009, 06:12:15 por jgpeiro06 »

Desconectado MLO__

  • Colaborador
  • DsPIC33
  • *****
  • Mensajes: 4583
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #6 en: 24 de Marzo de 2009, 22:46:58 »
Sencillamente espectacular .... felicitaciones
El papel lo aguanta todo

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #7 en: 25 de Marzo de 2009, 16:43:37 »
Tarea TaskAcce.

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.

 

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.


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.
 


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
  81.  


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.         }
  11.  
« Última modificación: 07 de Abril de 2009, 06:12:57 por jgpeiro06 »

Desconectado vtasco

  • PIC12
  • **
  • Mensajes: 72
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #8 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!

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17866
    • MicroPIC
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #9 en: 26 de Marzo de 2009, 02:47:09 »
¿Ese diagrama de bloques está generado mediante alguna herramienta de FreeRTOS?
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #10 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.

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #11 en: 27 de Marzo de 2009, 07:11:41 »
La tarea TaskWatch

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
  288.  
  289.  


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 );
  8.  




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;
  17.  

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 );
  7.  

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:



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;
  15.  
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. */
  22.  
  23.  


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
« Última modificación: 07 de Abril de 2009, 06:14:35 por jgpeiro06 »

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #12 en: 28 de Marzo de 2009, 10:57:33 »
La tarea TaskGetLeds.

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 );
  7.  

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.




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:




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. }
  21.  

      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. }
  29.  
Aquí dejo algún link:
Led como sensor:
http://en.wikipedia.org/wiki/LEDs_as_Photodiode_Light_Sensors

« Última modificación: 07 de Abril de 2009, 06:13:41 por jgpeiro06 »

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17866
    • MicroPIC
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #13 en: 28 de Marzo de 2009, 13:28:25 »
¡Coño!, ¿qué Altium te reordena los pines que son asignables dinámicamente?  :shock:
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado jgpeiro06

  • Colaborador
  • PIC18
  • *****
  • Mensajes: 276
Re: Emulador de un Casio F91W basado en FreeRTOS
« Respuesta #14 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


 

anything