Emulador de un Casio F91W basado en FreeRTOS (2 de 2)

mayo 15, 2009 en 3:46 pm | Publicado en ATMega y AVR, Electrónica, Microcontroladores | Deja un comentario

Aunque el gadget 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 gadget 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 cronómetro basan su precisión en el periférico Real Time Clock Calendar (RTCC) que incorpora el microcontrolador. El cronómetro tiene opciones de Start, 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 cronómetro definidas en documento USER’S GUIDE 2428 de Casio:


void Inicio( Watch *this );
void Parada( Watch *this );
void Fraccion( Watch *this );
void Libera( Watch *this );
void Borrado( Watch *this );
void ClearSplit( Watch *this );
void StartStop( Watch *this );
/* 
Función Medición de tiempo transcurrido.
Tecla C C C C A
Acción Inicio Parada Reinicio Parada Borrado
Función  Medición de tiempo fraccionado.
Tecla C A A C A
Acción Inicio Fracción Libera Parada Borrado
Función Tiempos fraccionados y/o tiempos del primero y del segundo en llegar.
Tecla C A C A A
Acción Inicio Fracción Parada Libera Borrado
*/

La tarea TaskGetLeds.

Responsabilidades:
Debe informar al sistema cunado el usuario pulse una tecla, indicando la tecla pulsada.
Estructura de la tarea:
Está 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 ésta devuelve un valor con el siguiente significado:
-1: No hay ninguna tecla pulsada.
0: La tecla A está pulsada.
1: La tecla B está pulsada.
2: La tecla C está 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.

unsigned long long order_LEDS( unsigned long value );
void put_ordered_LEDS( unsigned long long out );
void put_LEDS( unsigned long value );
void set_COLS_DIR( unsigned char COLS );
void set_LEDS_DIR( unsigned long value );
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.

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 en C


La tarea TaskGraphics


Generar el tono

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:
Está 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

code

La librería Graphics.c
Es la librería que proporciona el compilador CCS para dibujar primitivas 2D básicas como líneas, píxeles, 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 solucionó copiando toda la cadena (hasta 5 caracteres mas el carácter nulo) en el mensaje enviado.

La tarea TaskSpeaker

Responsabilidades:
Debe generar un sonido utilizando el piezo-altavoz cada vez que reciba un mensaje.
Tipo:
Tarea consumidora.
Estructura de la tarea:
Está dividida en dos funciones, vStartTaskSpeaker y vTaskSpeaker. La primera crea los recursos y la segunda recibe el mensaje y generará 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.

TASKSPEAKER_C

El mensaje recibido posee 3 datos:
Aunque la función que se muestra arriba funciona, aún 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.

La tarea TaskPutLeds

Responsabilidades:
Debe imprimir una imagen de 128×24 sincronizada con el movimiento de la mano.
Tipo:
Es una tarea consumidora.
Estructura de la tarea:
Está 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.

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

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

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.

La tarea WakeUp

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:

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.

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:

/* Standard includes. */
#include
#include
#include
#include
#include
/* Device description includes */
#include "P24FJ64GA004.h"
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
/* Application includes*/
#include ".\utils\Hardware.h"
#include ".\utils\LEDs.h"
#include ".\utils\GLCD.h"
//#include "Filters.h"
//#include "CORDIC.h"
#include ".\Tasks\TaskAcce.h"
#include ".\Tasks\TaskPutLeds.h"
#include ".\Tasks\TaskGetLeds.h"
#include ".\Tasks\TaskButton.h"
#include ".\Tasks\TaskSpeaker.h"
#include ".\Tasks\TaskWakeUp.h"
#include ".\Tasks\TaskWatch.h"
#include ".\Tasks\TaskGraphics.h"
#include ".\Generated Files\VDIInit\init_PIC24FJ64GA004.sinit_vi.h" 

/*-----------------------------------------------------------*/
// Create Tasks handlers, these must be placed as a GLOBAL varibles.
xTaskAcceResources xTaskAcce1;
xTaskPutLedsResources xTaskPutLeds1;
xTaskGetLedsResources xTaskGetLeds1;
xTaskButtonResources xTaskButton1;
xTaskSpeakerResources xTaskSpeaker1;
xTaskWakeUpResources xTaskWakeUp1;
xTaskGraphicsResources xTaskGraphics1;
xTaskWatchResources xTaskWatch1;
/* Create the demo tasks then start the scheduler.  */
int main( void ){

/* Configure any hardware required for this demo. */
VisualInitialization();
 /* Create the test tasks defined within this file. */

// Productor role tasks
xTaskAcce1.uxPriority = 5;
if( startAcceTask(&xTaskAcce1) ) return -1;
 xTaskGetLeds1.uxPriority = 1;
if( startGetLedsTask(&xTaskGetLeds1) ) return -1;
 xTaskButton1.uxPriority = 1;
if( startTaskButton( &xTaskButton1 ) ) return -1;

// Processor role tasks
xTaskWatch1.xQueueSound = xTaskGetLeds1.xQueueSound;
xTaskWatch1.xQueueKey = xTaskGetLeds1.xQueueKey;
xTaskWatch1.uxPriority = 2;
if( startTaskWatch( &xTaskWatch1 ) ) return -1;
 xTaskGraphics1.xQueuePrimitive = xTaskWatch1.xQueuePrimitive;
xTaskGraphics1.uxPriority = 3;
if( startTaskGraphics( &xTaskGraphics1 ) ) return -1;

// Consumer role tasks
xTaskSpeaker1.xMutexIO = xTaskButton1.xMutexIO;
xTaskSpeaker1.xQueueSound = xTaskWatch1.xQueueSound;
xTaskSpeaker1.uxPriority = 4;
xTaskSpeaker1.uxPriorityHigh = 7;
if( startTaskSpeaker( &xTaskSpeaker1 ) ) return -1;

xTaskPutLeds1.xMutexLeds = xTaskGetLeds1.xMutexLeds;
xTaskPutLeds1.xQueueMove = xTaskAcce1.xQueue;
xTaskPutLeds1.xQueueDisp = xTaskGraphics1.xQueueDisp;
xTaskPutLeds1.uxPriority = 6;
if( startPutLedsTask(&xTaskPutLeds1) ) return -1;

// Control tasks
xTaskWakeUp1.xHandleTasks[7] = xTaskGetLeds1.xHandle;
xTaskWakeUp1.xMutexes[7] = xTaskPutLeds1.xMutexLeds; // Productor Task
// Productor Task
xTaskWakeUp1.xHandleTasks[6] = xTaskAcce1.xHandle;
xTaskWakeUp1.xMutexes[6] = NULL;
xTaskWakeUp1.xHandleTasks[5] = NULL;
xTaskWakeUp1.xMutexes[5] = NULL;
// Processor Task
xTaskWakeUp1.xHandleTasks[4] = xTaskWatch1.xHandle;
xTaskWakeUp1.xMutexes[4] = NULL;
xTaskWakeUp1.xHandleTasks[3] = xTaskGraphics1.xHandle;
xTaskWakeUp1.xMutexes[3] = NULL;
xTaskWakeUp1.xHandleTasks[2] = NULL;
xTaskWakeUp1.xMutexes[2] = NULL;
xTaskWakeUp1.xHandleTasks[1] = xTaskSpeaker1.xHandle;
// Consumer Task
xTaskWakeUp1.xMutexes[1] = xTaskSpeaker1.xMutexIO;
xTaskWakeUp1.xHandleTasks[0] = xTaskPutLeds1.xHandle;
xTaskWakeUp1.xMutexes[0] = xTaskPutLeds1.xMutexLeds;

xTaskWakeUp1.xQueueButtonKey = xTaskButton1.xQueueKey;
xTaskWakeUp1.xQueueGetLedsKey = xTaskGetLeds1.xQueueKey;
xTaskWakeUp1.uxPriority = 8;
if( startTaskWakeUp( &xTaskWakeUp1 ) ) return -1;


/* Finally start the scheduler. */
vTaskStartScheduler();
 return 0;
}
/*
void _ISRauto _RTCCInterrupt(void)
{}
if( pdTRUE == xTaskResumeFromISR( xTaskWakeUp1.xHandle ) )
{
// We should switch context so the ISR returns to a different task.
// NOTE: How this is done depends on the port you are using. Check
// the documentation and examples for your port.
portYIELD_FROM_ISR();
}
}
*/

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:

typedef struct xTaskXXResources{
xTaskHandle xHandle; // Task handler.
unsigned portBASE_TYPE uxPriority; // Task priority.
xSemaphoreHandle xMutexXX; // Mutex handler.
xQueueHandle xQueueXX; // Queue handler.
}xTaskXXResources;

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

int vStartTaskXX( xTaskXXResources *pxResources ){
if( pxResources->xMutexXX == NULL ){
if( NULL == ( pxResources->xMutexXX = xSemaphoreCreateMutex() ) )
return -1;
vQueueAddToRegistry( pxResources->xMutexXX, ( signed portCHAR * )"xMutexXXName" );
}

if( pxResources->xQueueXX == NULL ){
if( NULL == ( pxResources->xQueueXX = xQueueCreate( 5, sizeof( xMessageXX ) ) ) )
return -1;
vQueueAddToRegistry( pxResources->xQueueXX, ( signed portCHAR * )"xQueueXXName" );
}

if( pdPASS != xTaskCreate(vTaskXX,(signed portCHAR *)"TaskName",
configMINIMAL_STACK_SIZE,
pxResources, pxResources->uxPriority, &pxResources->xHandle ) )
return -1;

return 0;
}

Conclusiones
Este gadGet 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 gadgets en el futuro.

DESCARGA
Proyecto: ManualLedScanner, versión 2.45. José Antonio García Peiró. 2009.

Descarga del proyecto completo

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

Links interesantes

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/
Maquina de estados finitos:
http://en.wikipedia.org/wiki/Finite_state_machine#Software_applications
Led como sensor:
http://en.wikipedia.org/wiki/LEDs_as_Photodiode_Light_Sensors
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
Preemption:
http://en.wikipedia.org/wiki/Preemptive_multitasking

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

Dejar un comentario »

RSS feed for comments on this post. TrackBack URI

Responder

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

Conectando a %s


Entries y comentarios feeds.

A %d blogueros les gusta esto: