scieee AI-readable full text Open interactive document viewer

Diseño de un teclado capacitivo reprogramable

Guzmán Manzanares, Pablo Manuel

Abstract

El proyecto consiste en la creación de un teclado capacitivo reprogramable y personalizable, usando un launchPad EK-TM4C1294XL al que se le conecta una pantalla ME813A-WH50C. En esta pantalla se generarán una serie de botones mediante el uso de modos de edición en diferentes páginas para crear perfiles encapsulados. Se conectará a un ordenador de manera que este detecte el periférico como un teclado HID, siendo los botones creados en la pantalla, sus teclas. Para la personalización se crea un servidor web Local contenido dentro del launchPad desde el que se tendrá la opción de personalizar gráficamente el dispositivo, así como la funcionalidad de sus botones.

Full text

Equation Chapter 1 Section 1 Proyecto Fin de Carrera Ingeniería Electrónica, Robótica y Mecatrónica Diseño de un teclado capacitivo reprogramable Autor: Pablo Manuel Guzmán Manzanares Tutor: Manuel Ángel Perales Esteve Dpto. de Ingeniería Electrónica Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2021 Proyecto Fin de Carrera Ingeniería Electrónica, Robótica y Mecatrónica Diseño de un teclado capacitivo reprogramable Autor: Pablo Manuel Guzmán Manzanares Tutor: Manuel Ángel Perales Esteve Profesor titular Dpto. de Ingeniería Electrónica Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2021 Proyecto Fin de Carrera: Diseño de un teclado capacitivo reprogramable Autor: Pablo Manuel Guzmán Manzanares Tutor: Manuel Ángel Perales Esteve El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2021 El secretario del Tribunal A mi familia A mis amigos Y en especial a Lidia. Agradecimientos ras estar acabando este proyecto, me paro a pensar en todo lo que he vivido desde que comenzó esta etapa de la universidad, todos los amigos que he hecho, todas las relaciones que he desarrollado y cómo ha cambiado todo desde que el Pablo pequeño tomó la decisión de venir a estudiar GIERM en la US porque el nombre sonaba guay. Han sido 4 años durísimos, cada uno más duro que el anterior por diversos motivos. Y este último ha arrasado con absolutamente todo lo que me mantenía con pasión por lo que realizaba en la universidad, así que me agarré a lo único que me quedaba: trabajo duro constante. Este modo de hacer las cosas mío ha producido muchos roces con mi familia y amigos, y ahora se termina, por fin, veo la luz al final del túnel. He avanzado a ciegas durante tanto tiempo, andando por disciplina más que por gusto que estar ya en ese punto de ver el final me hace valorar a todas esas personas que han aguantado junto a mí esa parte del camino. Gracias a mi familia por no colapsar sobre mí porque mis planes chocaban con sus vacaciones, y espero que entender por fin que los quiero como a nada en este mundo, solo que a veces no saben ver cuando necesito esa palmadita extra. No pasa nada, sé que la próxima vez seremos mejores. Y pese a todo lo que pueda pasar o salir, siempre seréis una de mis piedras angulares porque os quiero muchísimos a todos, con vuestros defectos incluidos. Gracias a Lidia por dar todo lo posible por echarme cables que no tenía cuando estaba al borde de la rendición. También gracias por darme mi espacio cuando estaba distante por las preocupaciones que me producía todo este proceso. Has sido una parte crucial de que yo haya llegado aquí de una pieza. Te quiero muchísimo. Y, por último, gracias al gran grupo de gente que puedo llamar mi segunda familia que he desarrollado aquí en Sevilla. Cuando se os ha dicho que se necesitaba estar un ratillo hablando para distraerme, habéis estado. Si necesitaba ayuda para programar algo o revisar un proceso, ahí habéis estado. Si necesitaba ayuda para gestionar información, ahí habéis estado. Sois una parte fundamental de lo que soy ahora. Pablo Manuel Guzmán Manzanares Futuro graduado de GIERM Sevilla, 2021 T Índice Agradecimientos ix Resumen xi Abstract xiii Introducción xv Índice xvii Índice de Tablas xxi Índice de Figuras xxiii Notación xxv 1 Estudio del problema, actualidad y soluciones existentes 1 1.1 Planteamiento del problema 1 1.2 Actualidad de Teclados Táctiles Reprogramables 2 1.2.1 MacBook TouchBar 3 1.2.2 ScreenPad ASUS ZenBook Pro 14 4 1.2.3 ASUS ZenBook Pro Duo 5 1.2.4 Stream Deck de ElGato 6 1.2.5 Numpad reconfigurables, teclados de macros y switches electro-capacitivos 7 2 Resolución planteada del proyecto, Componentes usados y Estructura del proyecto 11 2.1 Resolución del problema planteado 11 2.1.1 Pasos previos, planteamiento de diseños erróneos y descartes 11 2.1.2 Idea final 12 2.2 Hardware del Proyecto 12 2.2.1 ME813A-WH50C 12 2.2.2 EK-TM4C1294XL 14 2.2.3 Conexión de los elementos 14 2.3 Software del Proyecto 16 2.3.1 Pantalla 16 2.3.2 Teclado HID 16 2.3.3 Servidor Local WEB 17 2.3.4 Mejoras de calidad de vida para el usuario 17 3 Pantalla: EVE Block 19 3.1 Archivos desde los que se parte, capa base 19 3.1.1 Capa de pantalla 19 3.1.2 Capa de micro 19 3.1.3 Ficheros extra útiles 19 3.1.4 EVE_TM4C1294XL.c 20 3.2 Bases teóricas de diseño: Estructura de la sintaxis de la creación de pantallas 22 3.3 Estructura custom principal: BOTON 25 3.3.1 Campo físico 26 3.3.2 Campo BotAcción 26 3.3.3 Estrucutra de BOTON conjunta 26 3.4 Creando funciones básicas para el funcionamiento 27 3.4.1 Primeras modificaciones en EVE_API.c 27 3.5 Funciones para botones 28 3.5.1 Funcion base EVE_CMD_BUTTON 29 3.5.2 Botón con animación realista, EVE_BOTON_P 29 3.5.3 Evolucion a EVE_BOTON_P_2 30 3.5.4 Almacenamiento en tabla: CREAR_BOTON 30 3.5.5 Modo de edición libre: EVE_EDIT_MODE_L 31 3.5.6 Modo de edición automática: EVE_EDIT_MODE_MATRIX 34 3.5.7 Botones visibles e invisibles, EVE_MOSTRAR_BOTONES_GLOBAL 37 3.5.8 Borremos botones, EVE_ERASE() 38 3.5.9 Encapsulizando procesos, INIT_ESTRUCT_PRINC 38 3.5.10 El sistema nervioso de los botones, EVE_HANDLER_ACCION 39 3.6 Consideraciones finales 42 3.7 Avances en el main.c 43 3.8 Variables globales únicas 43 4 HID: Teclado USB 44 4.1 Human Interface Device 44 4.1.1 Integración de HID con el launchPad 44 4.1.2 Descriptores de características 44 4.1.3 DeviceDescriptor 46 4.1.4 Manejador del Teclado 46 4.2 USB en el EK-TM4C1294XL 48 4.2.1 Atajos de configuración, PinoutSet 48 4.2.2 Modos de USB, USBStackModeSet 48 4.2.3 Funcion final, Inicia_Teclado 49 4.3 Teclado virtual, interacción del teclado mediante la pantalla, EVE_API.c II 50 4.3.1 Códigos HID de teclas físicas, KeypUsageCodes 50 4.3.2 Comandos de transmisión, EVE_ACTUADOR 51 4.3.3 Comando de envio, KeyStateChange 51 5 Personalización: Servidor Local Web 53 5.1 Inicialización del modulo 53 5.1.1 Función Principal, Init_Ethernet_Server 53 5.1.2 Extras de la función ejemplo 55 5.2 Creación y comunicación del servidor 55 5.2.1 Bases de creación web 55 5.2.2 JavaScript en el Proyecto 56 5.3 Comunicación de parte de la placa 60 5.3.1 Detectando mensajes, io_fs.c 61 5.3.2 Acciones asigandas a los mensajes 62 5.4 Implicaciones de personalización en la pantalla, EVE_API.c III 66 5.4.1 Variables de personalización 66 5.4.2 Actualización de botón, WEB_SUBMITTER 66 5.4.3 Actualización de colores, modificaciones en bloques básicos 67 5.4.4 Usando los Códigos Custom, EVE_ACTUADOR II 68 5.4.5 Mostrando IP a conectar 69 6 Memoria FLASH y revisión final 70 6.1 La memoria FLASH 70 6.2 Usando la FLASH en el sistema 70 6.2.1 Variables a almacenar: estructura principal, colores y calibración 70 6.2.2 Estrategia de guardado 71 6.2.3 Estrategia de recuperación 73 6.2.4 Estrategia de actualización 74 7 Guía de Usuario 75 7.1 Primer Encendido 75 7.2 Pantalla Principal 75 7.3 Modos de Edición 76 7.3.1 Edición Automática 76 7.3.2 Edición Libre 78 7.4 Botones de funcionalidad 79 7.4.1 Botón de calibración 79 7.4.2 Botón de IP 79 7.4.3 Botón de borrado 80 7.5 Personalización en la Web 80 7.5.1 Apertura de la Web 80 7.5.2 Pantalla Principal de la Web 81 7.5.3 Editor de colores 82 7.5.4 Editor de botones 83 7.6 Diferencias principales al ejecutar con una memoria FLASH vacía 84 Bibliografía 86 ANEXO A 88 Raiz principal 88 Main.c 88 Driverlib2.h 90 Evethernet.h 91 Eveheader.h 91 Usbkeyboardheader.h 92 RecursosEve 92 Eve_api.c 92 Eve_calibrate.c 126 EVE_MCU_TM4C1294XL.c 127 Eve_Hal.c 133 Eve_fonts.c 139 Eve_calibrateplus.h 144 Eve_config.h 145 EVE.h 146 FT8xx.h 152 HAL.h 164 MCU.h 168 usbkeyboard 171 usb_keyb_structs.c 171 usb_keyb_structs.h 178 ethernet 179 io_fs.c 179 io.c 188 io.h 196 fs 197 index.htm 197 info.htm 198 instrucciones.htm 199 principal.htm 200 editorPag.htm 202 javascript.js 203 javascript_load.js 210 style.ccs 210 ÍNDICE DE TABLAS Table 1. Tabla de conexionado de los elementos 14 ÍNDICE DE FIGURAS Figure 1.Integración del Touchpad en MacBook 3 Figure 2. Integración del ScreenPad en el ASUS Zenbook 4 Figure 3. Evolución del ScreenPad de ASUS 5 Figure 4. Nueva integración del MousePad en ASUS 5 Figure 5. StreamDeck de ELGATO 6 Figure 6. Numpad externo estándar 7 Figure 7. Ejemplo de la diversificación del NumPad1 8 Figure 8. Ejemplo de la diversificación del NumPad2 8 Figure 9. Ejemplo de la diversificación del Numpad 3 9 Figure 10. Ejemplo de la diversificación del Numpad 4 9 Figure 11. Mecanismo de funcionamiento del switch electro-capacitivo 10 Figure 12.Parte posterior de la ME813A-WH50C 13 Figure 13. Parte superior de la ME813A-WH50C 13 Figure 14. EK-TM4C1294XL 14 Figure 15.Conexionado final de los elementos 15 Figure 16.Esquema de sintaxis de comandos de pantalla 23 Figure 17.Múltiples pantallas a mostrar, esquematización 24 Figure 18. Esquema de contenedor de estructura BOTON 27 Figure 19. Registro de coordenadas de la pantalla táctil 27 Figure 20. Esquematización de pantalla 28 Figure 21.Pantalla principal 75 Figure 22.Pantalla de Edición Automática 76 Figure 23. Pantalla de Edición Automática con uso 77 Figure 24. Matriz generada 77 Figure 25. Uso de la edición libre 1 78 Figure 26. Uso de la edición libre 2 78 Figure 27. botón generado por la edición libre 79 Figure 28. pantalla de calibración 79 Figure 29. Zona donde se muestra la dirección IP 80 Figure 30. Dirección IP 80 Figure 31. Navegador estándar con dirección IP 80 Figure 32. Pagina inicial de la WEB 81 Figure 33.Página inicial de la Web segmentada 81 Figure 34.Pagina de edición de pantalla 82 Figure 35.Edicion de pantalla tras uso 83 Figure 36.Pagina de edición de botones 83 Figure 37.Edición de botones tras uso 84 Notación TI Texas Instruments micro Microcontrolador UART Universal Asynchronous Receiver-Transmitter SPI Serial Peripheral Interface I2C. Inter-Integrated Circuit FLASH Evolución de Electrically Erasable Programmable Read-Only Memory HID Human Interface Device USB Universal Serial Bus Estudio del problema, actualidad y soluciones existentes 6 6 ▪ No pierde las capacidades de integración con aplicaciones para comandos y acciones que tenía la versión anterior, pero al volverse más grande se presupone que su uso principal será en ser una extensión de la pantalla, función alejada del planteamiento del proyecto. ▪ Una inclusión muy interesante es la capacidad que se le da al usuario de convertir el touchpad en un numpad táctil, pudiendo asignar macros a esos números y activar y desactivar la funcionalidad del numpad a voluntad. ▪ Este producto es el resultado de la evolución de una idea muy parecida a sobre la que se fundamente el proyecto, llevada por las características de integración y expansión hacia otro objetivo. Aun siendo así, la inclusión el numpad dentro del ratón por falta de espacio se concibe como una solución que podría incluirse dentro de algunos puntos clave del trabajo, aun siendo alejada del producto final que se desarrollará. 1.2.4 Stream Deck de ElGato Figure 5. StreamDeck de ELGATO ▪ ElGato, empresa perteneciente a Corsair, se ha posicionado como una de las principales compañías en cuanto al desarrollo de dispositivos y periféricos para medios audiovisuales. Se creó en 1992 y desde entonces han estado introduciendo tecnologías punteras al medio. ▪ Este teclado matricial se comercializa de forma masiva desde 2018, y se ha ido actualizando con diferentes versiones que ha modificado el número de teclas totales o la tecnología de sus botones. ▪ El cometido de este teclado es el de un teclado de Macros, con el añadido de usar las pantallas LCD incluidas en los botones individuales para personalizar estos. Además, cuenta con integraciones para diversas aplicaciones famosas en el mundo audiovisual actual gracias a sus acuerdos comerciales y el uso extendido que se le está dando en el mundo de las retransmisiones por internet. ▪ Se conecta mediante un USB, y tiene una aplicación dedicada para el diseño de las teclas que se descarga desde su página web. ▪ Es el sistema en el que se ha inspirado principalmente l proyecto, cumpliendo por lo tanto muchos objetivos principales. ▪ Las mejoras propuestas para realizar en el sistema en comparación con este sistema tienen principalmente una relación con su formato físico, que limita su formato de forma de forma tajante, además de dar funciones limitadas relacionado con su número de botones limitado. ▪ Fuera de ser un inconveniente para la empresa, esta vende mejoras de este sistema a los que le van añadiendo botones, siendo exponencialmente más caro cada vez que se le añade una fila. 1.2.5 Numpad reconfigurables, teclados de macros y switches electro-capacitivos Sin tener puntos específicos debido a que no existe como modelo de una compañía, todo este conjunto de tecnología existente podría ser la combinación alternativa de factores más cercana al producto final que se tendrá. Los teclados numpad de macros reconfigurables llevan en el mercado desde que el teclado perdió el formato completo en portátiles y teclados de ordenador, de forma que, pese a no disponer en el teclado principal de esta zona, se pudiese contar con esta funcionalidad cuando se necesitase. Figure 6. Numpad externo estándar A raíz de estos numpad, nacieron los teclados de macros, que usaban estas teclas que ya no se usaban de forma tan asidua para la asignación de combinaciones específicas de teclas. Además, el sencillo formato de las teclas y PCB, y la extensión de su popularidad hizo que mucha gente empezase a aprender como configurar estos teclados para poder conectarlos a su PC. Esta propagación de la información y democratización de la creación hizo que los formatos se ampliasen, junto con la personalización. He aquí algunos ejemplos de este movimiento: Estudio del problema, actualidad y soluciones existentes 8 8 Figure 7. Ejemplo de la diversificación del NumPad1 Figure 8. Ejemplo de la diversificación del NumPad2 Figure 9. Ejemplo de la diversificación del Numpad 3 Figure 10. Ejemplo de la diversificación del Numpad 4 Como se observa, la apariencia de las teclas ha cambiado físicamente, creando teclas intercambiables para encajar en las diferentes configuraciones. Esta parte es paralela a los objetivos del proyecto planteado, salvo por un detalle: se pretende dar al usuario la posibilidad de la personalización de las teclas de forma cómo da y rápida. Estas opciones implicaban la necesidad de tener las teclas previamente creadas, y para hacer el cambio de configuración se necesitaba que el usuario conociese de primera mano el código que este necesitaba para programar las macros correspondientes. Por la parte del uso de tecnología capacitiva, tenemos los switches capacitivos. El nombre de switches, se le da a la tecnología que contienen las diferentes teclas de un teclado para detectar la pulsación. A lo largo de los años se han diversificado enormemente, siendo las más famosas y utilizadas actualmente los teclados de malla, switches de botón, y los mecánicos, switches que tienen un mecanismo con muelle y disparador para detectar el pulso. Entre toda esta tecnología, una corriente de diseño se fijó en el fenómeno capacitivo de las PCB, permitiendo quedarse en un lugar intermedio entre los mecánicos y de malla. Estos funcionan con un muelle que se presiona ligeramente y hace que una placa capacitiva se active al acercarse. Esta tecnología es parecida a la que se planteará en un primer momento en el proyecto, salvo que, para promover la interactividad del usuario con la Estudio del problema, actualidad y soluciones existentes 10 10 pantalla, se quitará la parte mecánica y usaremos nuestros dedos como activador, lo que es una pantalla táctil. Figure 11. Mecanismo de funcionamiento del switch electro-capacitivo Tras mencionar todas estas tecnologías desarrolladas actualmente, pasemos a la resolución del problema planteado. 2 RESOLUCIÓN PLANTEADA DEL PROYECTO, COMPONENTES USADOS Y ESTRUCTURA DEL PROYECTO rás la descripción de las soluciones existentes descritas en el anterior apartado, se especificará a partir de aquí que estrategia se ha decidido seguir para cumplir todos las exigencias de funcionamiento planteadas. Estas soluciones se han visto limitadas a los dispositivos que el tutor y alumno tenían, formando estos un amplio abanico de opciones, pero dejando de lado opciones que podrían estar mejor preparadas para enfrentarse al proyecto por motivos económicos. Pese a esto, se cree firmemente que los elementos planteados para la resolución son totalmente válidos y cubren, punto por punto, todas las exigencias posibles que el sistema pudiese plantear, pudiendo llegar a competir con proyectos de mayor inversión y disponibles en el mercado. 2.1 Resolución del problema planteado Para dejar constancia de las decisiones que llevaron hasta la decisión final, se comentarán algunas opciones de planteamiento que se adoptaron en un inicio, y como evolucionó el planteamiento del proyecto hasta llegar al actual. 2.1.1 Pasos previos, planteamiento de diseños erróneos y descartes Este proyecto nació con la idea principal, después descartada, del diseño físico de nuestro propio teclado físico capacitivo. Este se imprimiría en una pcb y se conectaría a un microcontrolador de nuestra elección para realizar la lectura de los parches capacitivos. Para desarrollar algo más la idea, se diseñó una serie de patrones para los parches capacitivos de manera que se pudiese leer el porcentaje del contacto con este por el dedo o dispositivo que interactuase con el dispositivo, de cara a poder mapear el contacto en los máximos puntos de la pantalla. Esto como se puede ver, sería plantear el diseño de una pantalla táctil al uso. Esta idea plantea una serie de problemas a solucionar: ▪ La impresión de una PCB en estos tiempos puede resultar cara para hacer exclusivamente un prototipo ▪ La lectura, de la manera que se quería hacer, estaba solo al alcance de algunos launchPad específicos cuyos pines pudiesen leer entradas capacitivas ▪ Una vez terminado el proyecto, se tendría que solucionar la falta de una pantalla que diese feedback al usuario de cara a que pudiese ver que se estaba pulsando o si el dispositivo reaccionaba. Pese a estos problemas, se buscaron componentes de cara a la realización de estos objetivos. Se plantearon diferentes placas de TexasInstruments que contenían estos pines capacitivos, además de la compra de algún sistema intermediario que pudiese convertir esas entradas capacitivas en entradas directas de teclado. Al final, todo esto se descartó debido a que los microcontroladores propuestos de TI tenían los pines capacitivos, pero no existía ni existe soporte funcional ni documental por parte de la empresa para entender como sus dispositivos se inician y gestionan para esta característica. Además, otros micros fueron descartados por faltarles puertos necesarios para el desarrollo de todas las funciones requeridas, como podía ser el de soporte USB para crear el teclado HID. De cara al teclado HID, se planteó el uso de Bluetooth desde un principio. Pero por recomendación del tutor se T Resolución planteada del proyecto, Componentes usados y Estructura del proyecto 12 12 descartó la idea debido a la reciente para de soporte del BT 2.0 por parte de iOS y Windows, para pasar a ser BT 5.0, menos extendió a la hora de su programación que el 2.0, y por lo tanto mucho más obtuso a la hora de realizar las comunicaciones. En el caso de usarlo, el proyecto se encontraría en un punto donde esta característica pudiese funcionar dependiendo de los adaptadores, protocolos y versión del sistema que tuviese un ordenador, no teniendo una fiabilidad consistente. 2.1.2 Idea final Tras repasar tutor y alumno las posibilidades disponibles, descartando la creación de una pantalla táctil propia y el uso del bluetooth, se decide darle una vuelta al planteamiento del proyecto. El proyecto se centrará en la capacidad del usuario para personalizar un teclado mediante diferentes métodos: ▪ Para paliar la imposibilidad de usar el bluetooth, se decide buscar una placa con posibilidad de conexión USB con el objetivo de poder configurarlo como dispositivo USB HID. ▪ Para la personalización, se piensa en desarrollar una aplicación que permita al usuario navegar por unas opciones y hacer y deshacer a su antojo. Pero esto no es posible, debido a que el puerto USB por donde presumiblemente se comunicarían estaría ya conectado. Esto plantea una decisión: se crea un driver personalizado para el uso simultaneo de un dispositivo HID y un puerto de transmisión, o por el contrario se usa otro puerto. De aquí nace el uso de un servidor web que se comunique con la placa. ▪ Con la pantalla, se llega a la conclusión de que lo mejor es trabajar sobre una pantalla táctil capacitiva que de la opción de mostrar por pantalla las acciones que se van realizando de cara a solucionar el problema de feedback existente. Además, aceleraría enormemente el diseño y creación si se contase con una desde el principio, no teniendo que depender de sistemas de envío en verano. Por lo tanto, se tienen los requerimientos de hardware y software iniciales: un launchPad con capacidad de conexión USB HID, conexión a internet y un puerto extra del tipo que sea para conectar una pantalla que corresponda a este. Esta serie de características definen en mayor o menor medida al EK-TM4C1294XL, que estaba ya comprado con antelación al haberlo usado previamente en la carrera. El uso de esta placa aceleraría el proceso de adaptación del proyecto al conocer ya los procesos y quehaceres propias de su programación. Del lado de la pantalla, el tutor proporciona una pantalla ME813A-WH50C que, aunque en principio no existiese soporte para este tipo de microcontrolador, sí que lo tiene para una familia diferente de su empresa: los MSP430. Esto significará que habrá que portear el programa base que proporciona la empresa, y estudiando en profundidad el funcionamiento diseñado para que sea compatible con múltiples micros de diferentes compañías, diseñar las funciones específicas para asegurar su conexión y funcionamiento. Planteado todo esto tenemos los ingredientes iniciales del proyecto determinados. 2.2 Hardware del Proyecto El proyecto consta de dos componentes: El Connected LaunchPad EK-TM4C1294XL de Texas Instruments y la ME813A-WH50C (FT813 Development Module with 5.0 Inch WVGA TFT LCD) de FTDI Chip. El segundo dependerá directamente del primero, que se encargará de que todas las funcionalidades de nuestro teclado capacitivo reprogramable interactúen entre sí y le indicará al segundo que hacer en cada determinado momento. 2.2.1 ME813A-WH50C ▪ La pantalla a utilizar tiene una resolución de 800x480px, variable mediante programación del software, pero ésta será la resolución escogida del proyecto, debido a que aprovecha el máximo espacio posible de la pantalla. ▪ La pantalla cuenta con la posibilidad de controlar los leds instalados en la parte posterior, así como incluir audio, imágenes e incluso video de forma relativamente cómoda gracias a que cuenta con un altavoz mono y que, incluso, tiene el soporte para la conexión de un altavoz externo. Pese a que existen estas posibilidades, este trabajo NO usará estas funciones por decisión de diseño. ▪ Entre otras funcionalidades extra de la pantalla tenemos con que podremos leer hasta 5 toques de pantalla simultáneos, existiendo un registro propio para almacenar las coordenadas de cada uno de estos contactos (REG_CTOUCH_TAGi_XY, siendo i un numero entre el 0 y el 4). En este proyecto solo usamos el primer registro, teniendo la capacidad real de 1 toque simultaneo. ▪ Por último, la alimentación de la pantalla es de 5V, puerto de alimentación que puede proporcionarle el launchPad sin problemas. Figure 12.Parte posterior de la ME813A-WH50C Figure 13. Parte superior de la ME813A-WH50C Resolución planteada del proyecto, Componentes usados y Estructura del proyecto 14 14 2.2.2 EK-TM4C1294XL Este launchPad, que Texas Instruments denomina como low-cost, tiene las características y potencia perfecta para nuestro proyecto, siendo algunas de estas: ▪ Procesador basado en ARM-Cortex-M4F, siendo el TM4C1294NCPDTI ▪ Puerto ETHERNET y puerto micro USB para conexiones a PC de tipo 2.0 ▪ 2 botones y 4 leds del usuario ▪ 2 conexiones de 40 pines pensadas para el uso de boosterpacks, expansiones de la placa con usos como conexión wifi o recolección de datos físicos. ▪ Dispositivo para debug incorporado con controlador y puerto USB dedicado. ▪ Capacidad de hibernación con botón asociado. ▪ Capacidad de alimentación directamente por el puerto de conexión USB-PC ▪ Capacidad de alimentación de 5V en varios puntos. ▪ Múltiples puertos UART, SPI e I2C ▪ Pines analógicos, digitales y mixtos Figure 14. EK-TM4C1294XL 2.2.3 Conexión de los elementos Conectaremos la pantalla por puerto SPI al launchPad del microcontrolador, correspondiendo los pines de la siguiente forma: Table 1. Tabla de conexionado de los elementos PIN EK-TM4C1294XL PIN ME813A-WH50C PD0 MISO PD1 MOSI PD3 CLK PN2 PD PN3 CS Por lo tanto, se usará el puerto 2 de SPI (SSI2) de la EK-TM4C1294XL para la configuración. Tras esto, el resto de conexiones serán las de los cables USB y ETHERNET. Figure 15.Conexionado final de los elementos Pantalla: EVE Block 22 22 // Enable the SSI module. // SSIEnable(SSI_BASE); GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_1); GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_1, GPIO_PIN_1); } Inicializamos el puerto 2 de SPI, pese a que se podría haber iniciado cualquier de los otros puertos con la conexión de pines propia del puerto iniciado. Tras esto configuramos los pines especificados en el punto 2 del documento, y hacemos el procedimiento requerido para la inicialización del módulo SPI. Además, permití el uso de un led de la placa de cara a debuggear y saber en qué estado de la inicialización se encontraba para encontrar diferentes fallos en las primeras estancias del proyecto. 3.1.4.3 Lectura/Escritura del buffer uint8_t MCU_SPIReadWrite8(uint8_t DataToWrite) { uint32_t DataRead; SSIDataPut(SSI_BASE,DataToWrite); while(SSIBusy(SSI_BASE)); SSIDataGet(SSI_BASE, &DataRead); return (uint8_t)DataRead; } A nivel funcional, el dato pasado es puesto en el buffer y el sistema espera la respuesta cuando el dato es totalmente transmitido. Por último, devuelve el dato, que será de 8 bits mediante un casteo hacia ese tipo de variable. A nivel funcional esto es clave para mantener la estructura desarrollada de la librería y hacer los mínimos cambios posibles. Con estos cambios, el proyecto debería ser ejecutable por nuestro launchPad objetivo. 3.2 Bases teóricas de diseño: Estructura de la sintaxis de la creación de pantallas Uno de los problemas principales del proyecto es dar con la estrategia para la creación de forma sistemática de bloques que podamos mostrar por pantalla sin crear malfuncionamientos a causa de pisar comandos en diferentes bloques creados. Sin afrontar este problema, no se podría mostrar el trabajo por pantalla, y el trabajo carecería de sentido. Pues Bridgetek proporciona una forma muy eficiente de poder declarar diversos bloques funcionales en una pantalla y mostrarla sin problemas: La creación de una estructura de bloques para la declaración de las funcionalidades con comandos específicos para el almacenamiento y demostración de la pantalla. La estructura a seguir es la siguiente: 1. La cabecera: Específicamente es la línea de comando EVE_LIB_BeginCoProList(); Esta línea inicializa la declaración del bloque y es totalmente obligatoria. Si esto no se pusiera, la declaración del bloque seria ignorada y podrían producirse errores de declaración al usar comandos de cierre de bloque sin utilizar uno de apertura. 2. El cuerpo del bloque: Aquí va, literalmente, cualquier comando que no sea de cierre o inicio. Existen determinados comandos que nos interesará utilizar juntos en algunos procesos típicos, además de comandos específicos sueltos para realizar tareas comunes. Estos serán: ▪ Limpiar pantalla e inicializar nueva pantalla a un color RGB dado: EVE_CMD_DLSTART(); EVE_CLEAR_COLOR_RGB(R,G,B); EVE_CLEAR(1,1,1); ▪ Terminar la pantalla y mostrarla: EVE_DISPLAY(); EVE_CMD_SWAP(); ▪ Cambiar los elementos (botones, sliders, ruedas…) de la pantalla a un color específico RGB dado: EVE_CMD_FGCOLOR(RGB); Con una pequeña aclaración, RGB será una variable de 24b contenida en 32b, donde se reparten a partes iguales de 8b los campos para cada color. ▪ Cambiar el texto y números de la pantalla (incluido los de dentro de elementos, como los botones) a un color RGB dado: EVE_COLOR_RGB(R,G,B); 3. Cierre del bloque: Combinación de dos líneas que sirven como cola de bloque para cerrar la instrucción. EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); Esto cierra el bloque y produce una espera para poder transferirlo de forma correcta. Cuando termina la transmisión, sale de la última indicación. Por proporcionar algo más gráfico, se puede pensar como un sándwich: Figure 16.Esquema de sintaxis de comandos de pantalla Pantalla: EVE Block 24 24 Al estructurar las ordenes de la pantalla de esta forma tenemos la libertad de programar cada funcionalidad individual en un “sándwich” y añadirlo poco a poco a la lista completa que se mostrará por pantalla. Dicho esto, podemos apilar infinitos sándwiches, pero solo se mostrarán aquellos que se almacenen antes de una orden de mostrar la pantalla, pese a que después de dar esta orden sea posible seguir almacenando bloques de comandos. Es más, cualquier bloque de comandos que se almacene tras una orden de mostrar la pantalla contará para la siguiente lista a mostrar, cosa que podría resultar un problema o una ventaja, depende de nuestra maestría organizando los comandos. Por esquematizarlo: Figure 17.Múltiples pantallas a mostrar, esquematización Pese a la posibilidad de hacer 2,3,4 o bloques de mostrar pantalla, tiene poco sentido o ninguno a nivel funcional salvo para lograr efectos muy específicos. La explicación se puede aplicar a 2 casos y es tan simple como que, en el caso de no estar dentro de un bucle, la última pantalla sobrescribirá a las primeras y a efectos prácticos solo veríamos la última, y en el caso de que si lo esté, veremos una superposición constante de ambas pantallas, no permitiendo una visión clara. Con la teoría aquí explicada, se pueden realizar funciones específicas que, pese a no ser estrictamente necesarias, son beneficiosas para nuestra comodidad a la hora de programar las diversas funcionalidades a las que nos enfrentaremos. Por citar los bloques: void EVE_INICIALIZAR_NUEVA_PANTALLA(){ EVE_LIB_BeginCoProList(); // CS low and send address in RAM_CMD EVE_CMD_DLSTART(); // When executed, EVE will begin a new DL EVE_CLEAR_COLOR_RGB(0, 0, 0); // Select color to clear screen to EVE_CLEAR(1,1,1); // Clear EVE_LIB_EndCoProList(); // CS high and update REG_CMD_WRITE EVE_LIB_AwaitCoProEmpty(); } void EVE_MOSTRAR_FIN_PANTALLA(){ EVE_LIB_BeginCoProList(); // CS low and send address in RAM_CMD EVE_DISPLAY(); // Tells EVE that this is the end EVE_CMD_SWAP(); // Swaps new list into foreground buffer EVE_LIB_EndCoProList(); // CS high and update REG_CMD_WRITE EVE_LIB_AwaitCoProEmpty(); } void EVE_LIMPIAR_PANTALLA_A(uint8_t R,uint8_t G,uint8_t B){ EVE_LIB_BeginCoProList(); // CS low and send address in RAM_CMD EVE_CLEAR_COLOR_RGB(R, G, B); // Select color to clear screen to EVE_CLEAR(1,1,1); // Clear EVE_LIB_EndCoProList(); // CS high and update REG_CMD_WRITE EVE_LIB_AwaitCoProEmpty(); } void EVE_COLOR_ELEMENTO(){ EVE_LIB_BeginCoProList(); // CS low and send address in RAM_CMD // uint32_t Auxiliar=; EVE_COLOR_RGB(256,256,256); EVE_CMD_FGCOLOR(0x00ff00ff); EVE_LIB_EndCoProList(); // CS high and update REG_CMD_WRITE EVE_LIB_AwaitCoProEmpty(); } Tras esto, tenemos los cimientos establecidos para poder empezar a crear las funcionalidades de forma consistente. 3.3 Estructura custom principal: BOTON Esta estructura con nombre tan poco usado dentro del proyecto será la estructura principal, el esqueleto del proyecto, la estrella que guiará al sistema. A partir de aquí, todos los sistemas estarán pensados para que toda la información relevante sea volcada en un array de esta estructura. Pasemos a explicar los diferentes campos escogidos y su justificación. Pantalla: EVE Block 26 26 3.3.1 Campo físico Tan sencillo como los parámetros físicos de posición(X-Y) y tamaño(W-H) asociados al botón. La justificación es bastante trivial, sin estos valores no existiría el botón. Las variables existirán dentro de este campo, y se referenciará a ellas mediante la declaración Boton.físico.[aquí la variable] En código: struct fisico{ uint16_t x; uint16_t y; uint16_t w; uint16_t h; }; 3.3.2 Campo BotAcción Aqui nos estamos adelantando a acontecimientos ya que, pese a que aún no nos hemos metido dentro de las acciones que realizarán nuestros botones, podemos prever que necesitaremos un lugar donde guardar que acciones queremos asignar a determinados botones. Este espacio se proporcionará con la variable TIPO, de la que se dará una tabla completa de comandos y referencias cuando se cree la función que maneje este valor, o función handler. Además, vamos a seguir reservando espacio que, por la naturaleza del proyecto, prevemos que utilizaremos en el futuro. Este es el caso de los campos COMANDO y la estructura de 4bits MOD. Estos campos serán usados cuando empecemos a desarrollar las funcionalidades de teclado USB junto con la modificación mediante el servidor web, y se explicará según se vaya necesitando para no adelantar funcionalidades. Su declaración en código: struct Accion{ uint8_t tipo; // Por ej: // 1-EditMatrixmode // 2-EditLibre // 3-IncPantalla // 4-DecPantalla // 5-ComandoTeclado // 6-Sync?... uint16_t comando; //Si lo hubiera bool mod[4]; }; 3.3.3 Estrucutra de BOTON conjunta Tras declarar los campos anteriores, pasamos a definir en esta estructura de bloques, el contenedor final. Vamos a añadir a los campos anteriores variables interesantes que nos vendrán bien a la hora de facilitarnos la vida a nivel de programación y creación de características como son TAGASOC, para detectar la pulsación, y PANTALLAASOC, para la creación de diferentes pantallas virtuales con una sola estructura, así como una variable de identificación extra en NAME, para poder guardar nombre de botones para etiquetarlos encima del elemento. En código, la estructura final queda: struct Boton{ struct fisico fis; char name[6]; uint8_t tagAsoc; uint8_t pantallaAsoc; //16 es territorio de nadie, 0 es presente en todas as pantallas struct Accion BotAccion; }; Con todo esto hecho, hemos definido prácticamente todo el soporte funcional de nuestro trabajo. Para cualquier gestión correspondiente a los botones, solo tendremos que referenciar el proceso al campo del botón especifico dentro del array, y accederá a la información que considere oportuna. Por tener un pequeño esquemático: Figure 18. Esquema de contenedor de estructura BOTON 3.4 Creando funciones básicas para el funcionamiento Tras poder correr el ejemplo básico en nuestra TM4C1294XL pasamos al siguiente nivel de integración: necesitamos algunas funciones de funcionamiento muy básico para fundamentar nuestras aplicaciones más avanzadas. Con esto me refiero a la capacidad de leer la posición del toque en pantalla y leer elementos que pulsemos. 3.4.1 Primeras modificaciones en EVE_API.c Comenzaremos por la función para saber que coordenadas tienen nuestro contacto con la pantalla. Como podemos comprobar en documentación, el registro que tiene la información que buscamos es el EVE_REG_TOUCH_SCREEN_XY, siendo este de 32 bits y correspondiendo 16b a cada coordenada 2D. Figure 19. Registro de coordenadas de la pantalla táctil Por lo tanto, hacemos lo siguiente, y con esto introduzco la función de funcionamiento básico EVE_LEER_PANTALLA: void EVE_LEER_PANTALLA(){ BufferXY = HAL_MemRead32(EVE_REG_TOUCH_SCREEN_XY); POSX=(BufferXY&0xffff0000)>>16; Pantalla: EVE Block 28 28 POSY=BufferXY&0x0000FFFF; } Aquí, almacenaremos la lectura del registro en una variable intermedia de 32b que usamos para asignar los valores por separado a POSX y POSY, de 16b, mediante un proceso de desplazamiento y enmascaramiento, respectivamente. Por el bien del lector, haré un pequeño recordatorio de que los ejes en las pantallas funcionan de forma diferente al clásico sistema de ejes 2D que se suele enseñar en matemáticas o física. Estos ejes son iguales en perpendicularidad y dirección del eje X, pero el eje Y invierte su dirección: Figure 20. Esquematización de pantalla Tras esto, adoptaremos el modo de funcionamiento de la función helper del ejemplo original, aislándola para que sirva en un propósito más general, quedándonos la función EVE_LEER_TAG: uint8_t EVE_LEER_TAG(){ uint8_t tag=0; uint8_t Read_tag; Read_tag = HAL_MemRead8(EVE_REG_TOUCH_TAG); if (!(HAL_MemRead16(EVE_REG_TOUCH_RAW_XY) & 0x8000)) tag = Read_tag; return tag; } Esta devolverá el tag asociado al elemento tras leer el registro determinado y comprobar que está dentro del espacio interactuarle. Este concepto de tag, no es más que un numero indicador que se le asigna a cada elemento creado en pantalla y en el futuro, cuando gestionemos diversos elementos que se están dibujando, junto con otros que están preprogramados, pero no visibles, tendremos que tener mucho cuidado con problemas de compatibilidad y tag duplicados. 3.5 Funciones para botones El uso y la creación de botones es la funcionalidad en la que se fundamenta el proyecto, por lo que se busca un uso cómodo de éstos, permitiendo al usuario modificar y personalizar rápidamente los aspectos que considere necesario. Para empezar, tenemos que entender cómo funciona a nivel básico la función por defecto para este fin que trae las librerías proporcionadas, calco de lo que indica la guía de usuario para FT81x. 3.5.1 Funcion base EVE_CMD_BUTTON Esto se amplía por el manual en la página 174, punto 5.28. (FTDI, 2015) Para empezar, las variables que tendremos que pasarle a la función: 1. X-Y: coordenada x-y de la esquina superior izquierda 2. W-H: referentes a width(ancho) y height(alto), las dimensiones del botón. 3. FONT: es la fuente con la que queremos que ese botón en específico sea, en el caso de que exista un nombre asociado, etiquetado con su indicador. 4. OPCIONES: Usaremos esta función para cosas como posicionar el texto dentro del botón, y lo más importante, hacer que parezca que sobresale cuando está en reposo y aplastado cuando lo tocamos, es decir, un efecto de pulsación física. 5. NOMBRE: nombre, literalmente, del botón. Se escribirá encima todo lo que se detecte que va delante del típico terminador ‘\0’. Esto será importante más adelante a la hora modificar los nombres para diferenciar botones e indicar su comando a realizar. Pasándole esta información mediante la función, conseguiremos dibujar un botón en pantalla. Si asignásemos un TAG y una acción a la detección específica de este, podríamos hacer que fuese un botón interactuarle. 3.5.2 Botón con animación realista, EVE_BOTON_P El BOTON_P(ulsable) es una función puente que creé en su momento para jugar con las opciones y conseguir el efecto de pulsación realista. Esta evolucionará a la hora de manejar la estructura que se creará más adelante para simplificar sus variables de input y proporcionar un funcionamiento más versátil. A nivel de variables que pasamos a la función, decidí reducir el número de cara a hacer test rápidos de funcionamiento, quedándome con X, Y, W y H. Además, añado la posibilidad de pasar un TAG para que la función lo asocie directamente al elemento que produzca. 3.5.2.1 Asociación a TAGS ¿Como se asocial un TAG? La realidad, es que es muy sencillo. Existe una función proporcionada por la propia librería, llamada EVE_TAG(t), que nos hará el trabajo. Funciona de forma muy similar a la asignación de colores a los elementos de la pantalla, es decir, cuando ponemos el comando con t siendo un número, todas las entidades creadas tras este comando serán asignadas con ese TAG=t. La única forma de que esto no sea así es que, cada vez que queramos crear un nuevo elemento, designemos su TAG. 3.5.2.2 Animación física Como he comentado antes, este efecto depende del campo de OPCIONES de la función base EVE_CMD_BUTTON. Para crear el efecto crearemos una función que dependiendo de si el TAG actual es el asignado al botón creará un botón de 2 posibles: ▪ El primer botón será el reposo, este sale por defecto con efecto 3D por lo que no serán necesarios cambios en las opciones básicas. ▪ El segundo será el botón pulsado, pasando a ser un botón “2D” al estar aplastado a nivel pantalla. Esto se consigue con la opción EVE_OPT_FLAT. La función final queda: void EVE_BOTON_P(int16_t x,int16_t y,int16_t L,int16_t L2, uint8_t t){ extern uint8_t TAG; Pantalla: EVE Block 30 30 EVE_TAG(t); if(TAG==t) EVE_CMD_BUTTON(x, y, L, L2, 28, EVE_OPT_FLAT, "\0"); else EVE_CMD_BUTTON(x, y, L, L2, 28, EVE_OPT_CENTER, "\0"); } Tras esto, y antes de diseñar más funciones, pasé a diseñar la estructura que sería la espina dorsal del proyecto. De esta manera, cualquier función específica de botones a partir de tener esto terminado, podría trabajar con la información que terminaría dominando el proyecto y evitarme hacer adaptaciones y readaptaciones. 3.5.3 Evolucion a EVE_BOTON_P_2 Tras la creación de la estructura, podemos evolucionar la función anterior de EVE_BOTON_P a EVE_BOTON_P_2, a la que única y exclusivamente tendremos que pasarle el índice dentro de la tabla de botones que inicialicemos en el main.c, y esta función buscará la información que necesite automáticamente. En código tendremos lo siguiente: void EVE_BOTON_P_2(uint8_t n){ extern uint8_t TAG; EVE_TAG(Botones[n].tagAsoc); if(TAG==Botones[n].tagAsoc) EVE_CMD_BUTTON(Botones[n].fis.x, Botones[n].fis.y, Botones[n].fis.w, Botones[n].fis.h, 28, EVE_OPT_FLAT, Botones[n].name); else EVE_CMD_BUTTON(Botones[n].fis.x, Botones[n].fis.y, Botones[n].fis.w, Botones[n].fis.h, 28, EVE_OPT_CENTER, Botones[n].name); } 3.5.4 Almacenamiento en tabla: CREAR_BOTON Para que los campos inicializados cambien de valor a los que nos interesen, necesitaremos una función que asigne los campos específicos con datos que le proporcionemos. Este es el cometido de la función CREAR_BOTON. En esta función, además de asignar los valores que le pasemos a la función, haremos un asignador automático de índice dentro de la tabla y de comandos para representar teclas placeholder para el teclado USB. Antes de explicar el mecanismo, introduciré primero una idea de diseño que afecta a la variable PATALLAASOC. Como se podía leer en el comentario anterior, se asigna una pantalla omnipresente en el valor 0, es decir, cualquier botón con pantalla asociada igual a 0, aparecerá en todas las pantallas diferentes que se muestren, además de los botones que estén asignados a la pantalla especifica. A parte de la omnipresencia que significa el 0, establecemos un numero de pantalla como inicialización en el 16, sin ninguna razón detrás, pero con este número conoceré que botones han sido únicamente inicializados y no usados. Con esto, el funcionamiento del TAG automático es simple, se revisa la tabla y se cuenta cuantos botones si han sido definidos tras la inicialización y, tras esto, se le asigna el siguiente número que toque. Por ejemplo, inicializamos la tabla, creamos 3 botones en la pantalla 0, 4 en la 1, 5 en la 2, y 0 en la 3 y 4; tras esto el siguiente TAG asociado al botón será la suma de todos los botones anteriores +1, 3+4+5+1=13. El funcionamiento del placeholder está pensado para que sea un número que se estará asociado a una tabla donde tendremos almacenados diferentes combinaciones de teclas que pasaremos al bloque de teclado USB para que pueda realizar acciones de teclado. Esta tabla se compondrá de diferentes combinaciones de CTRL+SHIFT+NUMPAD, y se usará de funciones básicas. La asignación automática está pensada de forma parecida al del TAG, pero, en vez de contar todos los botones existentes, contará los asignados a su pantalla sin contar los generales. Para agilizar el proceso, se crea el campo origen que hará más rápida el acceso a las partes que nos interesen de la tabla, y con esta variable llega la decisión de diseño de tener en la tabla que contiene los botones un total de: ▪ 12 botones de sistema u omnipresentes. ▪ 26 botones de creación automática. El proceso se explicará más adelante. ▪ 26 botones de creación manual. Símil a los automáticos. Tras todo esto, nos queda la siguiente función: void CREAR_BOTON(uint8_t origen,uint16_t x,uint16_t y,uint16_t w,uint16_t h, char named[5],uint8_t pantallaAsocd, uint8_t tipod, uint16_t comandod){ int i; uint8_t index; uint16_t comandoR=1; if(origen==0){ index=0; comandoR=comandod; } else if(origen==1){ index=OFFSETBOTMATRIX; for(i=OFFSETBOTMATRIX;i<64;i++){ if(Botones[i].pantallaAsoc==PAG && Botones[i].BotAccion.comando!=0){ comandoR++; } } } else if(origen==2){ index=OFFSETBOTLIBRE; for(i=OFFSETBOTMATRIX;i<64;i++){ if(Botones[i].pantallaAsoc==PAG && Botones[i].BotAccion.comando!=0){ comandoR++; } } } while(Botones[index].pantallaAsoc!=16){ index+=1; } Botones[index].fis.x=x; Botones[index].fis.y=y; Botones[index].fis.w=w; Botones[index].fis.h=h; strcpy(Botones[index].name,named); Botones[index].tagAsoc=index+1; Botones[index].pantallaAsoc=pantallaAsocd; Botones[index].BotAccion.tipo=tipod; Botones[index].BotAccion.comando=comandoR; cambioF1=true; } 3.5.5 Modo de edición libre: EVE_EDIT_MODE_L La primera función con la que le daremos poder al usuario para crear los botones que estime oportuno. La idea a realizar es la típica de creación de figuras de toque y arrastre: el primer toque establece la esquina izquierda superior, y al arrastrar redimensionamos el botón. De cara a entrar en este modo, crearemos un botón con esta función asignada para salir y entrar en la funcionalidad, así permitiremos al usuario crear cuando plazca y no tener que estar atento a no crear botones accidentalmente al tocar la pantalla por error. La función empezará con un modo anti rebote, es decir, que el sistema espere a que se suelte el botón para empezar el modo y que no ande cambiando constantemente entre modo de edición y modo normal cuando mantenemos el botón pulsado. Esto se realiza de la siguiente forma: while(TAG!=0){ EVE_LEER_PANTALLA(); Pantalla: EVE Block 38 38 3.5.8 Borremos botones, EVE_ERASE() La base funcional de esta característica es muy sencilla: devolvemos el botón seleccionado a los valores de inicialización, lo que hace que esté virtualmente borrado. Para esto repasamos la tabla buscando el TAG asociado al botón seleccionado, copiamos el indicie que encontremos como correcto, y cambiamos esos valores. La función general: void EVE_ERASE(uint8_t boton){ uint8_t index=OFFSETBOTMATRIX; while(Botones[index].tagAsoc!=boton && index<65){ index+=1; } if(index<65){ Botones[index].fis.x=0; Botones[index].fis.y=0; Botones[index].fis.w=0; Botones[index].fis.h=0; Botones[index].name[0]='\0'; Botones[index].tagAsoc=0; Botones[index].pantallaAsoc=16; Botones[index].BotAccion.tipo=0; Botones[index].BotAccion.comando=0; Botones[index].BotAccion.mod[0]=0; Botones[index].BotAccion.mod[1]=0; Botones[index].BotAccion.mod[2]=0; Botones[index].BotAccion.mod[3]=0; cambioF1=true; } } Ese cambioF1=true, es algo relacionado con la memoria FLASH y será explicado más adelante. 3.5.9 Encapsulizando procesos, INIT_ESTRUCT_PRINC Quería mencionar esto antes de pasar al handler principal que maneja todas las acciones relacionadas con botones. Es un proceso que inicializa la tabla de estructura botón y asigna los valores iniciales antes mencionados. void INIT_ESTRUCT_PRINC(){ uint8_t i; for(i=0;i<64;i++){ Botones[i].fis.x=0; Botones[i].fis.y=0; Botones[i].fis.w=0; Botones[i].fis.h=0; strcpy(Botones[i].name,"\0\0\0\0\0\0"); Botones[i].tagAsoc=0; Botones[i].pantallaAsoc=16; Botones[i].BotAccion.tipo=0; Botones[i].BotAccion.comando=0; Botones[i].BotAccion.mod[0]=0; Botones[i].BotAccion.mod[1]=0; Botones[i].BotAccion.mod[2]=0; Botones[i].BotAccion.mod[3]=0; } } Lo único mencionable es la composición de Botones[i].name, que está hecha de esa manera para que en futuras modificaciones el exceso de caracteres no sea un problema. 3.5.10 El sistema nervioso de los botones, EVE_HANDLER_ACCION Tras la estructura que sustenta el proyecto, necesitaremos un sistema que tenga registrados que hacer dependiendo de que circunstancias. Estas circunstancias dependerán del campo tipo dentro de BotAccion, de la estructura principal. Con la creación de esta función se nos presenta una decisión: que comandos se asignarán a que cosas. Esta es la lista final: 1. Modo de edición automática 2. Modo de edición libre 3. Incrementar la página 4. Decrementar la página 5. Borrar botones 6. Mostrar IP del servidor web, explicado más adelante en el documento 7. Función de calibración, explicado más adelante en el documento 10. Función de pulsación de un botón normal A nivel de asignación, en los comandos 1 y 2 no hay que hacer mucho, solo inicializarlos, ya que la función tiene programada los filtros de rebote y la salida del modo. Con respecto a los puntos 3 y 4, el manejo de páginas, se aplica un filtro de rebotes de entrada, y cuando se sale de este se incremente o decrementa dependiendo del comando. Por tener un ejemplo antes de la función general: case 3: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } if(PAG>=4) PAG=1; else PAG+=1; break; En los casos 5 a 7, incluimos los filtros pertinentes de rebotes rodeando a la ejecución de una función programada. Pondré como ejemplo el único punto explicado hasta ahora: case 5: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); }; while(TAG!=5){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); Pantalla: EVE Block 40 40 TAG=EVE_LEER_TAG(); if(TAG>=OFFSETBOTMATRIX) EVE_ERASE(TAG); } while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); }; break; Y, para terminar, el comando 10 estará asignada a una función que se creará cuando el teclado USB este configurado, al estar relacionada las teclas con comandos de éste. case 10: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } EVE_ACTUADOR(TAG_P); break; } El conjunto sería el siguiente: void EVE_HANDLER_ACCION(){ uint8_t accion=0; uint8_t TAG_P=TAG; uint8_t i; if(SubmitState){ SubmitState=false; WEB_SUBMITER(); } if(TAG!=0){ accion=Botones[TAG-1].BotAccion.tipo; switch (accion) { default: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } break; case 1: EVE_EDIT_MODE_MATRIX(); break; case 2: EVE_EDIT_MODE_L(); break; case 3: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } if(PAG>=4) PAG=1; else PAG+=1; break; case 4: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } if(PAG<=1) PAG=4; else PAG-=1; break; case 5: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); }; while(TAG!=5){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); if(TAG>=OFFSETBOTMATRIX) EVE_ERASE(TAG); } while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); Pantalla: EVE Block 42 42 }; break; case 6: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } muestraIP=!muestraIP; break; case 7: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } for(i=0;i<6;i++){ calib.transform[i]=0; } eve_calibrate(); cambioF2=true; break; case 10: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } EVE_ACTUADOR(TAG_P); break; } } } 3.6 Consideraciones finales Pese a que este apartado de la pantalla se acaba, la biblioteca donde hemos realizado la mayoría de cambios se seguirá actualizando con funcionalidades cruzadas del teclado USB, el servidor web y la gestión de memoria FLASH. El lector no ha de preocuparse por nada ya que estas vueltas hacia atrás serán claramente indicadas. Para redondear lo máximo posible, indicaré a continuación la pantalla que se está creando en el main.c, junto con variables que se han usado hasta ahora que sean globales y una breve explicación. 3.7 Avances en el main.c A nivel de main.c, este apartado se lleva la mayor parte de código obviando los comandos referidos a las inicializaciones pertinentes de los siguientes módulos. Fuera del bucle haremos todas las iniciaciones como INIT_ESTRUCT_PRINC(), EVE_INIT(), y eve_calibrate(), además de crear los botones de sistema asociados a las pantallas, los modos de edición, la papelera de botones, la IP y la recalibración. Estos botones serán así y no sufrirá cambios a lo largo del proyecto: CREAR_BOTON(0,0,420,90,60,"Mtrx",0,1,0); CREAR_BOTON(0,710,420,90,60,"Draw",0,2,0); CREAR_BOTON(0,750,100,50,280,"->",0,3,0); CREAR_BOTON(0,0,100,50,280,"<-",0,4,0); CREAR_BOTON(0,154*3+90-35,420,70,60,"BIN",0,5,0); CREAR_BOTON(0,154*2+90-35,420,70,60,"IP",0,6,0); CREAR_BOTON(0,154*1+90-35,420,70,60,"CAL",0,7,0); Con los nombres, ilustramos que función cumplen. Dentro del bucle while, simplemente creamos la estructura de la pantalla a mostrar, pidiendo la actualización de los registros de toque y tag, y enviándole la información pertinente a las funciones que lo necesiten. EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_LIB_BeginCoProList(); // EVE_CMD_NUMBER(100,50,28,EVE_OPT_CENTER,POSX); // EVE_CMD_NUMBER(700,50,28,EVE_OPT_CENTER,POSY); EVE_CMD_NUMBER(400,20,28,EVE_OPT_CENTER,PAG); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); EVE_HANDLER_ACCION(); Este bucle tendrá algunas adiciones con el paso de la aplicación, pero estaría completo al 75% al recaer prácticamente todo el peso de programación en librerías al haber encapsulado lo máximo posible. 3.8 Variables globales únicas uint16_t POSX, POSY; uint32_t BufferXY,ui32SysClock; uint8_t TAG=0; uint8_t PAG=1; struct Boton Botones[64]; #define OFFSETBOTMATRIX 12 #define OFFSETBOTLIBRE 38 Son variables que necesitaremos usar en EVE_API.c declarándolas como externas, creando así una vía de comunicación global entre main.c y API que podríamos usar para otras funcionalidades futuras. En general son auto explicativas, y se sobreentienden si se ha leído la información expuesta arriba. HID: Teclado USB 44 44 4 HID: TECLADO USB sta parte del proyecto es técnicamente invisible para el usuario, igual que cuando conectamos cualquier dispositivo a nuestros ordenadores y, como por arte de magia, reacciona a lo que pulsamos y movemos. Este punto tratará de esa magia: ¿Cómo funciona realmente un teclado? 4.1 Human Interface Device Una descripción de dispositivos que, por norma general, nos rodea cada vez mas día a día. Cualquier dispositivo que usemos como personas humanas para introducir datos en una máquina suele corresponder al marco de los HID, desde los mandos más complejos para navegación aeroespacial a el ratón más simple que podamos encontrar. Si es verdad que, como es obvio, el grado de complejidad no es la misma pero no deja de ser una forma diferente de usar una serie de piezas disponibles para todos. El HID es una normativa estándar creada por la organización (Universal Serial Bus, 2001), reconocido nombre, para proveer soporte de forma consistente a este tipo de dispositivos. Esta normativa cuenta con descripciones detalladas de las estructuras que deberemos usar para que el dispositivo que programemos funcione de forma predecible en el dispositivo al que lo conectemos. USB, como organización, actualiza esta normativa cuando se hacen necesarias adiciones a la normativa en cuanto a posibilidades funcionales (la última actualización trajo a los queridísimos y globales emojis). Tras esta pequeña aclaración sobre el HID, revisaremos en este punto diferentes aspectos que necesitaremos usar de este origen: tablas, procesos, comandos… 4.1.1 Integración de HID con el launchPad El EK-TM4C1294XL nos pone una serie de facilidades al proporcionar la librería de drivers correspondiente con una serie de ejemplos, librerías y manuales desarrollados por el fabricante (Texas Instruments, 2020) para facilitarnos las cosas a la hora de la programación. Esto es enormemente importante, ya que aligera mucho el proceso de puesta en marcha de la función y comprobación de funcionamiento con el resto de componentes ya que, si no hacemos las cosas de forma correcta, podríamos crear errores de temporización en la transmisión de datos de cualquiera de los módulos individuales. Para empezar, en la librería original determinada para el uso del teclado se encontraban diferentes declaraciones clave de funcionamiento. Esta librería es posteriormente readaptada para el proyecto, así que iré indicando a continuación las diferentes estructuras y procesos que sea adoptaron en el proyecto exigidas por los estándares del protocolo. 4.1.2 Descriptores de características Nombre del teclado, idiomas que maneja, fabricante… Es la ficha técnica a la que podríamos acceder desde dentro del Sistema Operativo correspondiente si tenemos alguna duda. Tras las declaraciones individuales, convergen todas dentro de un descriptor general más grande que las contiene para poder usarlo en el futuro. E El código se nos facilita debido a la previa declaración de códigos numéricos específicos en los que se basa, casi por completo, este tipo de programación. Códigos como “el idioma español” que originalmente sería el número hexadecimal C0A pasa a ser algo más legible como “USB_LANG_ES_MODERN”. Para más indicaciones recomiendo revisar las bibliotecas relacionadas con los dispositivos USB y HID proporcionadas por TexasInstruments, así como el manual USB completo que los acompaña. Sea como sea el código final corresponde al siguiente: const uint8_t g_pui8LangDescriptor[] = { 4, USB_DTYPE_STRING, USBShort(USB_LANG_ES_MODERN) }; const uint8_t g_pui8ManufacturerString[] = { (17 + 1) * 2, USB_DTYPE_STRING, 'G', 0, 'u', 0, 'z', 0, 'M', 0, 'a', 0, 'n', 0, 'z', 0, ' ', 0, 'I', 0, 'n', 0, 'd', 0, 'u', 0, 's', 0, 't', 0, 'r', 0, 'y', 0, ' ', 0, }; const uint8_t g_pui8ProductString[] = { (16 + 1) * 2, USB_DTYPE_STRING, 'E', 0, 'V', 0, 'E', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'O', 0, 'N', 0, 'E', 0 }; const uint8_t g_pui8SeriailNumberString[] = { (8 + 1) * 2, USB_DTYPE_STRING, '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0 }; const uint8_t g_pui8HIDInterfaceString[] = { (22 + 1) * 2, USB_DTYPE_STRING, 'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0 }; const uint8_t g_pui8ConfigString[] = { (26 + 1) * 2, USB_DTYPE_STRING, 'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0 }; const uint8_t * const g_ppui8StringDescriptors[] = { g_pui8LangDescriptor, g_pui8ManufacturerString, g_pui8ProductString, g_pui8SeriailNumberString, g_pui8HIDInterfaceString, g_pui8ConfigString }; HID: Teclado USB 46 46 4.1.3 DeviceDescriptor Tras esta lista descriptora o ficha técnica del dispositivo, pasamos a declarar el propio dispositivo mediante un descriptor de dispositivo. Este descriptor de dispositivo tiene una serie de campos que definirán a nivel funcional el producto final, como el consumo o el ID de producto para detectar el teclado. Estos campos son: ▪ El ID del vendedor: TexasInstruments te recomienda usar el suyo propio que viene programado en la librería de IDs para USBs. ▪ El ID del producto: esto define de qué manera entiende el PC el dispositivo, en nuestro caso pondremos el de teclado: 0x1 ▪ Corriente máxima consumida por el dispositivo: normalmente dejamos la estándar para este tipo de productos, 500mA, salvo en casos específicos que necesitemos una cantidad diferente por tener un requerimiento mayor debido a LEDS, pantallas integradas… ▪ Flags de características de corriente: Según los bits activados, el sistema tendrá unas características u otras. Dejaremos los que recomienda TI. ▪ Puntero a manejador de eventos: será una estructura que explicaremos más adelante. ▪ Puntero a la estructura misma: esta estructura se inicializará con un nombre. Esto lo usaremos para crear un puntero a la información de esta estructura y enviarlo para que sirva como ID de ida y vuelta para el sistema, para que sepa de que dispositivo son las peticiones y hacia donde tiene que enviar las respuestas. ▪ Descriptor de característica: el del anterior punto. ▪ Numero de campos en el descriptor de características: la única forma de que varie es añadiéndole multiplicidad de idiomas. 4.1.4 Manejador del Teclado Es un manejador del estado en relación con el puerto USB, y no un manejador de las acciones del teclado como podrían ser las pulsaciones de teclas, es decir, un manejador de la conexión del dispositivo. Al ser así, este se tendrá que encargar de monitorizar los diferentes estados por los que el dispositivo podría pasar en una conexión, transmisión, desconexión, etc. Para la monitorización del estado, se declara primero una variable que marcará el estado actual del teclado. Se compondrá de un enum, que contendrá los 3 estados posibles del teclado: No configurado, preparado y a la espera, y enviando datos. A nivel código quedará: volatile enum { STATE_UNCONFIGURED, STATE_IDLE, STATE_SENDING } g_eKeyboardState = STATE_UNCONFIGURED; Como es obvio, inicializaremos la variable como no configurada. Tras el proceso apropiado, esto cambará e irá alternándose entre los otros dos estados posibles. Tras la declaración de la variable indicadora, pasamos a declarar la función manejador. Esta función manejador se explica en detalle en el manual de uso de USB proporcionado por TI, en la que se justifican los inputs de la función y los estados totales. La función es la siguiente: uint32_t KeyboardHandler(void *pvCBData, uint32_t ui32Event, uint32_t ui32MsgData, void *pvMsgData) { switch (ui32Event) { case USB_EVENT_CONNECTED: { g_bConnected = true; g_bSuspended = false; break; } case USB_EVENT_DISCONNECTED: { g_bConnected = false; break; } case USB_EVENT_TX_COMPLETE: { g_eKeyboardState = STATE_IDLE; break; } case USB_EVENT_SUSPEND: { g_bSuspended = true; break; } case USB_EVENT_RESUME: { g_bSuspended = false; break; } case USBD_HID_KEYB_EVENT_SET_LEDS: { MAP_GPIOPinWrite(GPIO_PORTN_BASE, GPIO_PIN_0, (ui32MsgData & HID_KEYB_CAPS_LOCK) ? GPIO_PIN_0 : 0); break; } default: { break; } } return(0); } Es un handler básico en el que se limita a cambiar una serie de variables troncales del proceso del teclado, como indicar si se encuentra apagado, suspendido o conectado y los estados generales. Como pequeño extra de funcionamiento, se proporciona la típica función en los teclados de encender un led cuando se tiene el CAP_LOCK activado, comprobando si el mensaje es la susodicha tecla y cambiando el estado del led. La combinación de estas estructuras será la base de cualquier posible futuro proyecto en el que se pretenda usar un HID, siendo los valores anteriores modificables para adaptarlos a un ratón, un mando, un dispositivo mixto Personalización: Servidor Local Web 54 54 ejemplo sigue para la inicialización del módulo señala a las zonas de memoria correspondientes a USER1 y USER2 de la FLASH somo los contenedores de la dirección MAC del launchPad. La gestión de este tipo de memoria se explicará más adelante y lo único que tenemos que ver ahora mismo es que se recuperan los datos de la zona mencionada y se pasan a una variable que contiene la información necesaria de la MAC de la EKTM4C1294XL que usaremos para inicializar y comunicarnos con el servidor. Tras esto, se inicializa el gestor lwIP que se encarga de la gestión de IP y el protocolo TCP. A ésta le pasaremos el reloj, la MAC recuperada y la variable correspondiente a la adquisición de IPs mediante DHCP. Tras esto preparamos el entorno para la inicialización del servidor web mediante la ejecución de las funciones con raíz LOCATOR, en las que además le pasamos la MAC y el nombre del servidor. Tras esto, inicializamos el servidor local. La estructura de archivos, así como los pasos para crear este servidor local serán especificados más adelante debido a su implicación con la comunicación placa-servidor. La última cosa es indicar el orden de prioridades de interrupción, dejando por encima del resto a las relacionadas con el puerto ETHERNET. Esto ocurre, según se ha investigado, porque la temporización usando Internet es crítica, por lo que si se interrumpiese la transmisión o recepción de estos datos en mitad de una conexión se podrían producir problemas graves como la pérdida total de conexión o falla total del sistema. En el transcurso de este proyecto se han producido estos casos, por lo que se recomienda seguir este paso. Tras esta explicación se tendría lo siguiente: void Init_Ethernet_Server(){ uint32_t ui32User0, ui32User1; uint8_t pui8MACArray[8]; SysCtlMOSCConfigSet(SYSCTL_MOSC_HIGHFREQ); MAP_SysTickPeriodSet(ui32SysClock / SYSTICKHZ); MAP_SysTickEnable(); MAP_SysTickIntEnable(); MAP_FlashUserGet(&ui32User0, &ui32User1); pui8MACArray[0] = ((ui32User0 >> 0) & 0xff); pui8MACArray[1] = ((ui32User0 >> 8) & 0xff); pui8MACArray[2] = ((ui32User0 >> 16) & 0xff); pui8MACArray[3] = ((ui32User1 >> 0) & 0xff); pui8MACArray[4] = ((ui32User1 >> 8) & 0xff); pui8MACArray[5] = ((ui32User1 >> 16) & 0xff); lwIPInit(ui32SysClock, pui8MACArray, 0, 0, 0, IPADDR_USE_DHCP); LocatorInit(); LocatorMACAddrSet(pui8MACArray); LocatorAppTitleSet("TFG Web Program"); httpd_init(); MAP_IntPrioritySet(INT_EMAC0, ETHERNET_INT_PRIORITY); MAP_IntPrioritySet(FAULT_SYSTICK, SYSTICK_INT_PRIORITY); } 5.1.1.3 Comprobador en bucle, CHECK_FLAGS Rápido y sencillo, es un comprobador activo que busca flags relacionadas con el puerto ETHERNET. Va en el bucle principal, en contra parte a la función de inicialización, que va antes de este. void CHECK_FLAGS(){ if(g_ulFlags){ HWREGBITW(&g_ulFlags, FLAG_TICK) = 0; } } 5.1.2 Extras de la función ejemplo En este apartado se han incluido una serie de funciones que pese a no referenciarse dentro de las consideradas vitales para el funcionamiento, son funciones con raíces profundas dentro de los archivos de ejemplo y que hace difícil su exclusión sin forzar un error en otro módulo si esencial. Esto hace que se decida dejar tras limpiar las funcionalidades no necesarias dentro del proyecto incluidas en el ejemplo. 5.1.2.1 lwIPHostTimerHandler La primera de estas era la función manejadora lwIPHostTimerHandler, que pese a parecer una función necesaria en funcionamiento por su nombre, originalmente no era más que una manera de comunicar al usuario mediante el uso del puerto serie del estado actual de configuración de la IP del servidor. 5.1.2.2 AnimTimerIntHandler Originalmente era la función que se encargaba de la velocidad de animación de apagado y encendido de un LED mediante el control por el servidor. Podría eliminarse si no fuera porque el uso de esta hubiese forzado el cambio en el fichero base Starup_ccs.c dentro de su tabal de datos fundamental y que, en el caso de borrar el dato asociado, produzca una falla grave en el proyecto dejando todos los módulos fuera de juego. Hablando a nivel de autor, esto me hizo preguntarme si realmente había algo que no estuviese viendo y esa función si se utilizase para sincronizar algo, pero no he encontrado ni una sola referencia en código de esta, por la que planteo la hipótesis de que esta se puede eliminar sin problemas si se quita justo lo necesario y arreglando cualquier referencia externa. 5.2 Creación y comunicación del servidor Este apartado engloba la parte de HTML5, CCS y JavaScript del proyecto y como se crea, de parte del servidor, el canal de interacción y comunicación con el launchPad. 5.2.1 Bases de creación web Se incluye este punto de cara a sentar unas bases comunes sobre las que se redactará lo hecho en el proyecto. Hay diversas maneras de hacer una página web, la seleccionada para este proyecto consiste en: estructurar la idea con HTML5, decorar y organizar la estructura con CCS, y crear las funcionalidades de la página con JavaScript. Sólo mediante la interacción de estos tres idiomas en sus diferentes archivos será posible que la página web creada dentro del servidor local sea y actúe como se planease en un principio. Como añadido, la carpeta contenedora se tendrá que ver sometida a un archivo que nos proporciona TI: el makefsfile. El proceso se indica en el ejemplo y se hace para cargar los cambios que se produzcan en el código de la placa referido al servidor, es decir, el launchPad no lee directamente estos tres idiomas, sino una transcripción. 5.2.1.1 HTML5 A nivel de archivos, cada archivo .htm dentro del contenedor del servidor, es decir, la carpeta contenedora del servidor, corresponderá a una página que podremos cargar individualmente en nuestro navegador si accedemos a su ruta. Estos archivos se dividen en: ▪ Básicos con nombres específicos: como podrían ser “index.htm” para el archivo principal desde el cual nos iremos moviendo al resto, “404.htm” para indicar una búsqueda de archivo fallida o “perror.htm” para casos de errores en el servidor. ▪ Archivos propios: como los archivos editores, “editorBot.htm” y “editorPag.htm”, para la modificación de valores objetivo, o “instrucciones.htm” para indicar el funcionamiento de la página. Todos estos archivos podrán ser comunicados internamente mediante links de HTML5 o externamente mediante Personalización: Servidor Local Web 56 56 acciones onClick de JavaScript, incluso conteniendo dentro de un archivo a otras páginas, como se hará finalmente en el resultado final usando a index de contenedor para incluir al resto. Otra parte importante de HTML5 es que es una programación por bloques, creando diferentes entidades una tras otra que después trataremos con el resto de idiomas para indicar su aspecto y habilidades. Lo que sí hace HTML5 para facilitar las vías de comunicación entre idiomas es asignar una serie de características para la fácil detección de elementos, como pueden ser: ID únicos, clases, cajas preestablecidas como <div>, <a> y <p>, etc. Tras estructurar la información mediante las posibilidades que nos brinda el idioma, pasamos a CCS para decorar y organizarlos dentro de la pantalla. 5.2.1.2 CCS Es un idioma que sirve para propósitos puramente estéticos mediante la configuración de los bloques creados con HTML5, conteniendo todos los estilos en un solo archivo, normalmente. No tiene ningún uso a nivel funcional, pero teniendo en cuenta que una gran parte de la aceptación de una página web se hace por motivos visuales, es una pieza igual de importante que cualquier otra. No hay mucho más que mencionar aquí salvo que preparamos la página index para resultar el contenedor donde se mostrará en su parte central e inferior las páginas pertinentes que se vayan requiriendo. 5.2.1.3 JavaScript JavaScript corresponde a la parte funcional de esta triada de idiomas que se encarga de la creación de la página. Cualquier tipo de función como podría ser la creación de un contador, un reloj, un submitter o un dispositivo interactivo, requiere del uso de este idioma. Se suelen crear los que se precisen, exportando después las funciones dentro del HTML5 mediante un comando de inclusión propio. Por lo tanto, se asemejan enormemente a las librerías de C donde se contienen funciones encapsuladas para usar en el archivo principal. 5.2.2 JavaScript en el Proyecto Entender el funcionamiento del modus operandi de la comunicación en estos archivos ha sido la parte más complicada de todo el proyecto sin lugar a dudas. Enfrentar esto con un conocimiento nulo de este idioma ha hecho que otras opciones que seguramente fueran mejores soluciones para la funcionalidad requerida se hayan tenido que descartar al no ser capaz de lograr una comunicación consistente web-placa. Aun así, se considera que la solución aportada es satisfactoria debido a que se ha proporcionado al usuario todas las herramientas planteadas desde un inicio, sin dejar ninguna funcionalidad atrás. En nuestro caso los archivos de este idioma serán el responsable de la inclusión de páginas en index, de la comunicación con el launchPad y de plasmar los datos que este le indique al usuario mediante la página. Todas estas funcionalidades se contendrán dentro de un único archivo con nombre igual al idioma. El otro archivo de este idioma, “javascript_load.js”, se encargará de cargar las páginas correspondientes al inicio del servidor, para proporcionar la vista compacta desde un primer momento. No será necesario volver a mencionar este archivo ya que es un espejo de la primera función incluida en el otro. Cualquier cambio en esa declaración, tendrá que ser correspondida aquí. 5.2.2.1 OnClick y OnLoad, cargando la información y moviéndonos por los archivos Estas funciones corresponden a la carga de archivos en dos situaciones: ▪ OnLoad cargará el archivo definido en la declaración cuando se produce la primera llamada a la página y se carga la web en el navegador. Se usa para inicialización de funciones, y en este caso se indican las funciones onClick para la navegación del usuario por los diferentes archivos. La función cargará en una ventana dentro de index estos archivos. ▪ OnClick realizará la función pertinente cuando se seleccione en un elemento con un ID determinado, indicado en la declaración de la función. Estos ID se referencian a los puestos en HTML5 a los bloques. Como uso extra de estas funciones, se encargan de ejecutar la función de obtención de datos para recuperar estados de variables que se usan dentro de la página cuando nos movemos hacia esta. Tras establecer estas funciones, se tiene un servidor por el que el usuario puede moverse a voluntad, que está visualmente refinado y que reacciona ante las interacciones. 5.2.2.2 Planificando las conexiones Antes de introducirnos en la función de comunicación y en la estrategia de interacción que esta sigue, sería positivo para el proyecto recapitular y ver de cuantas variables se quiere que el usuario disponga para interactuar dentro del servidor. Como objetivos se ha planteado en primer lugar la modificación de las teclas en su comando designado, significando esto: ▪ Capacidad para decidir la página de la tecla objetivo. ▪ Capacidad para decidir el número de la tecla objetivo. ▪ Capacidad para decidir el modificador que se aplicará a la tecla entre shift, alt, control o una combinación de estos. ▪ Capacidad para elegir la tecla principal que se aplicará entre las del teclado principal. ▪ Capacidad de decisión de cuando se efectúa el cambio, en caso de que se desee cambiar al final. El primer objetivo tiene vinculadas un total de 5 variables posibles a modificar. Como segundo objetivo de personalización se tiene la modificación del color del fondo y las teclas para una pantalla determinada, esto implicaría: ▪ Capacidad para decidir la página objetivo. ▪ Capacidad de elegir entre si modificar elementos o fondo. ▪ Capacidad de elegir colores. Aunque en este caso no es tan directo el cálculo de variables debido a la naturaleza del problema, se concluye que, si la variable de página es igual a la anterior, se tendrá 1 variable de elección más 3 variables de selección de color, una asignada a cada parámetro de los colores RGB por los cuales se rige la pantalla. Pero esto se incrementa al adoptar la solución propuesta. Si los estados los cambia un botón, será necesario un botón por cada estado posible de la variable, lo que significa que: ▪ Numero de pantalla: dividido en siguiente pantalla y anterior pantalla. ▪ Numero de botón: dividido en siguiente botón y anterior botón. ▪ Modificador de tecla: 3 teclas para los 3 modificadores, además de una tecla para limpiarlos. ▪ Tecla: una tecla por cada tecla básica de teclado que queramos incluir, siendo esto alrededor de 65 teclas ▪ Actualizador: se queda en un botón. ▪ Selector fondo/elemento: podemos dejarlo en un botón que invierta el estado. ▪ Rojo, Verde y Azul: la forma más pensada se corresponde al de escribir un numero en un teclado numérico, que corresponderá con el valor R, G o B del color de la pantalla, por lo que serían 10 teclas más. Además, tendremos una variable extra no asociada a botones para mostrar el valor actual en el launchPad y dar así feedback al usuario. Esto dejaría el problema en más o menos 110 posibilidades, haciendo que sea bastante prioritario la optimización de la gestión, creación y recepción de los mensajes por parte de ambos extremos del canal. Personalización: Servidor Local Web 58 58 5.2.2.3 Estructura de comunicación en ambos sentidos por parte del servidor Cuando se estudia el ejemplo original se reconoce una estructura bien establecida que, si nos paramos a leer, contiene variables que parecen de la placa. Al desarrollarla, veremos cómo esta estructura es lo único que necesitaremos a este lado del canal para empezar a enviar y recibir información. La estructura corresponde con una función declarada en HTML5 como onClick al botón correspondiente. Cuando el botón es pulsado, la función se ejecutará y hará lo que le indiquemos. Y aquí está el kit de la cuestión, que tenemos que hacer para que la conexión sea exitosa. La función comienza con la creación de otra función interna llamada ToggleComplete que, tras unas comprobaciones que pueden parecer un poco arcaicas, menciona a la variable contenedor no asociada al botón y que se encarga de dar feedback sobre el estado al usuario. Se usará de ejemplo la función que se encarga de seleccionar si lo personalizamos los elementos o el fondo: function ToggleComplete() { if(req.readyState == 4) { if(req.status == 200) { document.getElementById("WindButState").innerHTML = "<div>" + req.responseText + "</div>"; } } } Tras esto la función pasa a comprobar si existe una petición de HTTP o algún objeto activo para asignarlo a la variable troncal del proceso, lo que se supone que se hace de cara a mandar la información con un identificador o a un sitio determinado. if(window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if(window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } Despúes de esto, se indica una línea de texto que se publicará al otro lado de la conexión. Esto es realmente importante ya que se está declarando que la comunicación entre el dispositivo y el servidor ocurre de manera que se publica un mensaje que el otro sistema busca activamente, y al encontrar este mensaje el sistema remoto decide que hacer, modificando una variable asignada al campo de feedback del usuario, que el servidor recupera para hacer la asignación nueva. Finalmente, y tras la publicación, se ejecuta la función previamente establecida. Esto hace que se especule sobre que esta función se encarga de recuperar el valor nuevo de la variable determinada. if(req) { req.open("GET", "/WindBut" + Math.random(), true); req.onreadystatechange = ToggleComplete; req.send(null); } Tras la declaración de lo anterior quedará una función de la forma: function WindBut() { var req = false; function ToggleComplete() { if(req.readyState == 4) { if(req.status == 200) { document.getElementById("WindButState").innerHTML = "<div>" + req.responseText + "</div>"; } } } if(window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if(window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } if(req) { req.open("GET", "/WindBut" + Math.random(), true); req.onreadystatechange = ToggleComplete; req.send(null); } Ésta será la base para todas las variables, aunque será necesaria una modificación general debido a que el problema principal de esta parte del proyecto es evitar crear una función de este tipo para cada una de las 110 posibilidades. Esto se consigue simplemente añadiendo una variable de entrada a la función y sabiendo algo de gestión de cadenas de caracteres en JavaScript. La explicación es la siguiente: si podemos asignar una variable de entrada que sea a su vez una cadena de caracteres que podamos asignar al mensaje que se publica cuando se interactúe con ese botón se tendrá la capacidad de reducir los 110 a la cantidad total de las variables de feedback para el usuario debido a la dependencia de recuperación que tienen las funciones con sus datos a recuperar. En resumen, tras introducir esta mejora de características se reducirá las funciones requeridas a: ▪ Control del número de página. ▪ Control del número de botón. ▪ Control de los modificadores de tecla seleccionados. ▪ Control de la tecla seleccionada. ▪ Control de configuración fondo/elemento. ▪ Control de Rojo. ▪ Control de Verde. ▪ Control de Azul. ▪ Control de actualizar botón. ▪ Control de numero de página alternativo (no necesario, por pruebas realizadas) ▪ Función de obtención de variables sin interactuar para inicializar o mantener variables entre pantallas, necesaria para funcionamiento en algunos casos. Como se puede observar, la introducción de esta característica implica la reducción del 90% del problema. La función tipo corresponderá a lo siguiente: function edTec(a) { var req = false; Personalización: Servidor Local Web 60 60 function ToggleComplete() { if(req.readyState == 4) { if(req.status == 200) { document.getElementById("TecState").innerHTML = "<div>" + req.responseText + "</div>"; } } } if(window.XMLHttpRequest) { req = new XMLHttpRequest(); } else if(window.ActiveXObject) { req = new ActiveXObject("Microsoft.XMLHTTP"); } var aux = '/Tec' + a + '?id'; if(req) { req.open("GET", aux + Math.random(), true); req.onreadystatechange = ToggleComplete; req.send(null); } } Mientras que la declaración estándar del comando onClick será, poniendo de ejemplo un teclado numérico hecho para los colores: <p>Seleccione la tonalidad de Rojo:</p><a id="RedState">-</a> <div id=DivTecNum> <input id="Tec4" value="7" onclick="edRed('7');" type="button"> <input id="Tec4" value="8" onclick="edRed('8');" type="button"> <input id="Tec4" value="9" onclick="edRed('9');" type="button"> <div></div> <input id="Tec4" value="4" onclick="edRed('4');" type="button"> <input id="Tec4" value="5" onclick="edRed('5');" type="button"> <input id="Tec4" value="6" onclick="edRed('6');" type="button"> <div></div> <input id="Tec4" value="1" onclick="edRed('1');" type="button"> <input id="Tec4" value="2" onclick="edRed('2');" type="button"> <input id="Tec4" value="3" onclick="edRed('3');" type="button"> <div></div> <input id="Tec4" value="0" onclick="edRed('0');" type="button"> </div> 5.3 Comunicación de parte de la placa De parte del EK-TM4C1294XL, la comunicación se hace mediante dos bloques: uno que hace las de detector de los mensajes publicados por el servidor y otro que ejecuta el bloque con la rutina de acción determinada para ese mensaje. La primera de estas funciones la cumple el archivo io_fs.c, que contiene las funciones de apertura, clausura y lectura del canal de comunicación. La segunda función esta descrita por parte del archivo io.c, donde también están contenidas las funciones de arranque del módulo ETHERNET. Este archivo contiene las funciones que se ejecutarán cuando se detecten los mensajes que envía el servidor y tiene una estructura de librería típica, en contraste con su contraparte fs. 5.3.1 Detectando mensajes, io_fs.c El problema al que nos enfrentamos en este archivo es el reflejo de su contraparte en el servidor. Necesitamos optimizar la detección de mensajes de manera que no tengamos que escribir una secuencia de detección por cada una de las 110 posibilidades. 5.3.1.1 Estructura básica de rastreo Para empezar, observamos el funcionamiento de la estructura de detección: 1. Una variable se compara con un mensaje dados un número total de caracteres para la comparación, si es igual en ese número se introduce en el contenido del IF. 2. Se ejecuta la funciona signada al mensaje, a la que se le pasa una variable referente a un búfer y un numero referente al tamaño total del que dispone para pasar datos. Se modificará este último valor para dar el espacio justo y necesario para que la variable a cambiar llegue sin problemas. 3. Tras esto, una variable es desglosada en diferentes valores y se les asigna a sus campos una serie de valores. Esto es lo equivalente a una ficha de datos del mensaje. 4. Esta variable que recoge diferentes datos se devuelve, presumiblemente hacia el servidor. Tras comprender esto, se puede plantear la solución para afrontar el problema principal planteado. 5.3.1.2 Solución del problema de rastreo de mensajes Aplicando las bases de la solución se llega a la conclusión de que utilizar el manejo de cadenas de caracteres para automatizar algunos de estos procesos reduce el problema en un porcentaje elevado. Siguiendo esta línea de pensamiento, se crean diferentes variables auxiliares en forma de arrays de caracteres: ▪ Teclado: array inicializado con los valores posibles que tendría el teclado en el servidor. O sea, números del ‘0’ al ‘9’, y letras de la ‘A’ a la ‘Z’. Esto servirá de variable auxiliar para completar automáticamente las cadenas que se usarán para la comprobación final, pudiendo construir cualquier combinación con prefijo + este array. ▪ auxTecString: array que inicializaremos con la palabra /Tec en todos sus campos, a la que posteriormente le añadiremos las variables de teclado, dando así los mensajes de control referentes al teclado. ▪ auxRedString, auxGrnString y auxBluString: cadenas inicializadas con su correspondiente célula de tres letras a las que se le añadirá los números de teclado del 0 al 9, para obtener todos los mensajes de control del teclado numérico que maneja los colores del RGB. Tras esto, se hacen bucles while para identificar el mensaje de dentro de las variables auxiliares y determinar lo antes posible si se contiene o no. Como ejemplo: ip=0; while(ustrncmp(pcName, auxTecString[ip], 5) != 0 && ip<36){ ip++; } if(ustrncmp(pcName, auxTecString[ip], 5) == 0 && ip<36){ static char pcBuf[2]; io_change_Tec(ip,pcBuf, 2); psFile->data = pcBuf; psFile->len = strlen(pcBuf); psFile->index = psFile->len; psFile->pextension = NULL; return(psFile); } Personalización: Servidor Local Web 62 62 Esto, junto con la introducción de una variable en las funciones asignadas que será explicada a continuación, agilizan la escritura de un código especialmente repetitivo. Como punto final a esta explicación, los cambios se realizan específicamente en la función de apertura, dejando el resto por defecto. La función total de apertura se incluirá en un anexo debido a que su longitud rompería la estructura del documento. 5.3.2 Acciones asigandas a los mensajes La estrategia seguida en la programación será: creación de una función para manejar la variable a la que podremos introducirle una serie de parámetros de cara a generalizar el máximo posible para englobar el mayor número de casos en el menor número de funciones, y junto con esta una función de inicialización de la variable de cara a la primera ejecución del servidor. A partir de aquí se repasarán las funciones de manejo creadas y la estrategia seguida en ellas. 5.3.2.1 Numero de Página Se le añade la introducción de un bool, cuyo estado indica si la página se incrementa o se reduce. Para la devolución de la variable, que tiene que ser una string, se le suma el número determinado al carácter ‘0’, que correspondería con el número en forma de carácter al tener limitada la variable en un rango muy específico. La variable a controlar será page. void io_change_numPag(bool PlusMin,char * pcBuf, int iBufLen) { if(PlusMin) page--; else if(!PlusMin) page++; if(page<1) page=4; else if(page>4) page=1; char pageString[2] = {'0' + page}; usnprintf(pcBuf, iBufLen, pageString); } 5.3.2.2 Numero de Tecla En cuanto a la variable de entrada es prácticamente igual a la función anterior, un bool que controla si suma o resta. Por parte de la devolución, al ser un número que puede superar la decena, se crean dos situaciones: ▪ Cuando estamos por debajo de esta, 0 a 9, se sigue la estrategia del punto anterior. ▪ Si se está por encima, se asigna un ‘1’ al primer carácter, se le resta 10 a la variable de control y se asigna el segundo carácter como en el punto 1. La variable a controlar será VirTec. void io_change_numVirTec(bool PlusMin,char * pcBuf, int iBufLen) { char VirTecString[3]={'0'}; if(PlusMin) VirTec--; else if(!PlusMin) VirTec++; if(VirTec<1) VirTec=12; else if(VirTec>12) VirTec=1; if(VirTec>9){ VirTecString[0]='1'; VirTecString[1]='0' + VirTec-10; } else{ VirTecString[1]='0'+ VirTec; VirTecString[0]='0'; } usnprintf(pcBuf, iBufLen, VirTecString); } 5.3.2.3 Modificadores de Tecla La variable de entrada es un número del 1 al 4, que indica: 1. Pulsado CTRL 2. Pulsado ALT 3. Pulsado SHIFT 4. Pulsado Limpiar Se introducirá la variable en un switch, que asignará los bits a 1 y 0 de un array de 3 bits creado, representación de CTRL|ALT|SHIFT y su estado, pulsación o reposo. El estado de limpiar establecerá todos estos valores a 0, reposo. Por parte de la devolución, se hace que dependa de la variable que hemos modificado con el estado de las teclas y, en una cadena previamente inicializada con espacios, copiamos el acrónimo específico en un sitio específico de la cadena para dar feedback al usuario. La variable a controlar será ModTec. void io_change_ModTec(uint8_t Tecla,char * pcBuf, int iBufLen) { char ModTecString[15]={"______________"}; //ctrl+shift+alt = 15 switch (Tecla){ case 1: ModTec[0]=1; break; case 2: ModTec[1]=1; break; case 3: ModTec[2]=1; break; case 4: ModTec[0]=0; ModTec[1]=0; ModTec[2]=0; break; default: ModTec[0]=0; ModTec[1]=0; ModTec[2]=0; break; } if(ModTec[0]) strcpy(&ModTecString[0],"CTRL"); if(ModTec[1]) Memoria FLASH y revisión final 70 70 6 MEMORIA FLASH Y REVISIÓN FINAL ras la comprobación del funcionamiento de los anteriores bloques implementados, se tiene una serie de situaciones que pueden afectar negativamente a la experiencia del usuario, haciendo muy tedioso el uso de forma continuada del dispositivo. La principal de ellas es: todas las configuraciones, colores y comandos propios, entre otros, que se introduzcan serán borrados del launchPad al desconectar este de una fuente de alimentación. En este momento es donde se plantea la solución típica para estos casos, el uso de la memoria FLASH. 6.1 La memoria FLASH La memoria FLASH es un bloque contenido dentro de nuestro launchPad que tiene como función principal el almacenamiento de datos. Lo especial de esta memoria es que es capaz de establecer datos físicamente dentro de unas células mediante el uso de impulsos eléctricos, y una vez establecidos no necesita de ningún tipo de corriente para mantener su estado, por lo que, si se elimina la fuente de energía, al reintroducirla podremos acceder a esos datos guardados en esta. Este tipo de memoria se usa principalmente para el guardado de comandos, rutinas y datos de sistema básicos para el funcionamiento correcto de los diferentes launchPad del mercado. Además de esto, se suelen dejar reservadas para el uso del usuario una serie de posiciones, dándole la posibilidad al programador de jugar con estas características que se mantienen entre encendidos. Uno de los problemas que presentan este tipo de memoria y el que se solucionará en el proyecto es que, a la hora de cambiar los valores de la memoria FLASH, dependiendo de su tecnología, no podremos convertir ceros en unos. Esto provocará que, si queremos hacer cambios de forma consistente y no producir datos alterados a causa de la conjunción de diferentes bits de datos, tendremos que borrar el campo antes de modificarlo para mantener la integridad. Pero esto provoca el problema principal: para la EK-TM4C129XL, cuando borramos una posición de memoria de la FLASH, esta provocará el borrado del bloque completo que contiene a esta posición, es decir, unas 16kb en posiciones. Este problema está presente en todos los launchPad que tengan esta memoria accesible, debido a que todas las memorias FLASH tienen este problema. La única variabilidad entre los problemas es el tamaño del bloque, que depende de la tipología de la FLASH y cambia entre sistemas. 6.2 Usando la FLASH en el sistema Se van a necesitar dos estrategias para interactuar con la FLASH, una para almacenar información en la FLASH y otra para recuperar información de esta. Para esto, será muy importante que tengamos claro cuáles serán los tamaños de las variables que almacenaremos y recuperaremos debido a que estos procesos estarán principalmente limitados por la cantidad de información que se mueve de una vez. 6.2.1 Variables a almacenar: estructura principal, colores y calibración A la hora de plantear la información más útil que se puede mantener entre apagados tenemos: la estructura funcional principal de botones, los valores de personalización de los colores de elementos y fondo, y los valores de calibración. Estos últimos se incluyen para que no sea necesario recalibrar la pantalla cada vez que se enciende el sistema, dando la posibilidad al usuario de recalibrar cuando desee mediante el botón asignado. Ahora se hará una lista de las variables que nos interesan y sus tamaños. T 6.2.1.1 Estructura principal, Botones ▪ Coordenada X, 16b ▪ Coordenada Y, 16b ▪ Tamaño W, 16b ▪ Tamaño H, 16b ▪ Nombre asociado, 6 caracteres * 8b cada uno ▪ Tag Asociado, 8b ▪ Pantalla Asociada, 8b ▪ Tipo de Acción, 8b ▪ Comando de Acción, 16b ▪ Modificador de tec, 4b 6.2.1.2 Colores Todas las variables relevantes de colores se duplicarán debido a la dualidad elemento/fondo. Además, teniendo en cuenta que los tres colores son iguales tendremos: ▪ Color, 16b por evitar sobrepasos de 256 * 3 colores * 2 situaciones = 6 colores de 16b Pese a que la variable original es de 16b para evitar situaciones de sorpaso de los 256 que el usuario puede provocar, cuando esta información llega a la capa de la pantalla estará limitada a 256, lo que son 8b. Esto hace que se pueda interpretar esto como si tuviese mos 6 colores de 8b. 6.2.1.3 Calibración Según la declaración de la variable de calibración, tendremos que asignar un total de 6 registros de 32b cada uno. 6.2.2 Estrategia de guardado Para el guardado en FLASH se usará la función FLASHPROGRAM. A esta función tendremos que pasarle un puntero al inicio del dato que queremos guardar, la posición de la FLASH donde la guardaremos y el tamaño total del dato en octetos pares de bytes. Las posiciones elegidas serán 2, de cara a partir el problema de sobreescritura para hacer el cambio menos frecuente en cada bloque. Se almacenará en la posición 0x50000 todo lo relacionado a botones, y en la 0x60000 todo lo relacionado a colores y calibración. Se elige este offset de posiciones ya que coincide con el tamaño de bloque de la FLASH, 16kbytes, ya que 0x10000 representa en decimal el numero 16k. Tras esto, el protocolo a seguir será el de enviar los datos a la memoria FLASH en paquetes de 32 bits, agrupando estos para ocupar el máximo tamaño dentro de los paquetes. Los primeros paquetes tendrán la forma: 1. Coordenadas X e Y, 16b + 16b = 32b 2. Tamaños W y H, 16b + 16b = 32b 3. 4 caracteres de name, 4 * 8b = 32b 4. 2 caracteres de name, tag y pantalla, 2 * 8b + 8b + 8b = 32b 5. Tipo de Acción, Comando de Acción y Modificador, 8b + 16b + 4b = 28b Esto hace que haya 4b no usados en el último paquete, pero no existe problema con eso. Se asignará un paquete intermedio vacío para ver gráficamente en la memoria con un lector de memoria FLASH la posición de las Memoria FLASH y revisión final 72 72 variables de forma más clara. Con respecto a los segundos paquetes tendremos en colores: 1. Rfondo, Gfondo, Bfondo y Rboton, 4 * 8b = 32b 2. Gboton y Bboton, 2 * 8b = 16b Estos se almacenarán primero, usando los 16b no usados como separador visual. Tras estos se colocarán los registros de calibración: 1. Registro 1, 32b 2. Registro 2, 32b 3. Registro 3, 32b 4. Registro 4, 32b 5. Registro 5, 32b 6. Registro 6, 32b Por lo tanto, la función estándar lo único que tendrá que hacer es almacenar en una palabra de 32b las variables a guardar en memoria mediante una operación OR, tras esto esa palabra será guardada y el índice que maneja las posiciones donde se va operando, incrementado en 4 unidades. Como ejemplo, la primera función: void FLASH_STOCKER_I(uint8_t i){ uint32_t posicionAux=posicion+i*24; uint32_t posAux; if(Botones[i].fis.w!=0 && Botones[i].fis.h!=0){ posAux=0; posAux|=Botones[i].fis.x; posAux|=Botones[i].fis.y<<16; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].fis.w; posAux|=Botones[i].fis.h<<16; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].name[0]<<0; posAux|=Botones[i].name[1]<<8; posAux|=Botones[i].name[2]<<16; posAux|=Botones[i].name[3]<<24; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].name[4]<<0; posAux|=Botones[i].name[5]<<8; posAux|=Botones[i].tagAsoc<<16; posAux|=Botones[i].pantallaAsoc<<24; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].BotAccion.tipo<<0; posAux|=Botones[i].BotAccion.comando<<8; posAux|=Botones[i].BotAccion.mod[0]<<24; posAux|=Botones[i].BotAccion.mod[1]<<25; posAux|=Botones[i].BotAccion.mod[2]<<26; posAux|=Botones[i].BotAccion.mod[3]<<27; FlashProgram(&posAux,posicionAux,4); } } Tras esto, tendremos nuestras variables hasta ahora volátiles guardadas en memoria para su recuperación cuando estimemos oportuno. 6.2.3 Estrategia de recuperación Una vez tenemos almacenada los datos interesantes para el proyecto en la memoria, el proyecto pasa a necesitar el desarrollo de un bloque que pueda encargarse de recuperar esa memoria y asignarla a las variables correspondientes. Esto se conseguirá mediante el uso de la función HWREG, que permite leer una posición de memoria y devuelve un dato de tamaño 32b. Este tipo de devolución del dato, es en parte causante de la decisión y enviar paquetes de 32b en la estrategia de guardado, de cara a hacer el cálculo necesario con antelación y controlar los números asignados para la recuperación de los datos enviados. Habiendo hecho ese trabajo, lo único que tenemos que hacer es asignar las variables en el mismo orden que se mandaron, correspondiendo los bits de derecha a izquierda con el orden de envió. Lo más complicado, planteado lo anterior, es el enmascaramiento y desplazamiento de los bits de cara a recuperar la forma original que tenían a partir de una palabra de 32b conjunta. Poniendo de nuevo el ejemplo del bloque botones: void FLASH_FISHER_I(uint8_t i){ uint32_t posicionAux=posicion+i*24; uint32_t posAux=0; if(HWREG(posicionAux)!=0xffffffff && HWREG(posicionAux+4)!=0xffffffff && HWREG(posicionAux+8)!=0xffffffff && HWREG(posicionAux+12)!=0xffffffff){ posAux=HWREG(posicionAux); Botones[i].fis.x=(uint16_t)(posAux & 0xffff); Botones[i].fis.y=(uint16_t)(posAux >>16); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].fis.w=(uint16_t)(posAux & 0xffff); Botones[i].fis.h=(uint16_t)(posAux >>16); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].name[0]=(char)(posAux & 0xffffff); Botones[i].name[1]=(char)((posAux & 0xffff)>>8); Botones[i].name[2]=(char)(posAux>>16 & 0xff); Botones[i].name[3]=(char)(posAux >> 24); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].name[4]=(char)(posAux & 0xffffff); Botones[i].name[5]=(char)((posAux & 0xffff)>>8); Botones[i].tagAsoc=(uint8_t)(posAux>>16 & 0xff); Botones[i].pantallaAsoc=(uint8_t)(posAux >> 24); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].BotAccion.tipo=(uint8_t)(posAux & 0x000000ff); Botones[i].BotAccion.comando=(uint16_t)((posAux & 0x00ffff00)>>8); Botones[i].BotAccion.mod[0]=(bool)(posAux>>24 & 0x01); Botones[i].BotAccion.mod[1]=(bool)(posAux>>25 & 0b0000001); Botones[i].BotAccion.mod[2]=(bool)(posAux>>26 & 0b000001); Botones[i].BotAccion.mod[3]=(bool)(posAux>>27 & 0b00001); Memoria FLASH y revisión final 74 74 } } El proceso de recuperación se dará únicamente al principio del programa, copiando estos valores a sus variables si volátiles para trabajar de forma rápida con ellas y no tener que estar accediendo a memoria para la recuperación constantemente. Y debido a que los cambios se ejecutarán en las variables volátiles, cargándose en memoria cada vez que se efectúen, el número necesario de recuperaciones será de 1. 6.2.4 Estrategia de actualización He aquí el problema principal del uso de FLASH. A falta de una ocurrencia mejor, el proyecto tratará este problema de manera que cada vez que se detecte un cambio que tenga que ser guardado en la memoria, recupere el resto de campos almacenados previamente. Esto se hará mediante unas variables marcadoras, que se activarán cuando los procesos de cambio de botones, calibración o la detección del cambio de color, sean activadas. El estado de estas se comprobará en cada bucle, y en el caso de que estén activas se ejecutará la función FLASH_RENEW, que se encargará del proceso de borrado mediante FLASHERASE, y la recuperación de todos los campos actualizados junto con los no modificados. A nivel funcional, el bloque ejecutará las funciones individuales hechas para almacenar los datos en memoria pasando todos los posibles índices que aceptan de cara a no dejar ningún dato olvidado. void FLASH_RENEW(){ uint8_t i; if(cambioF1){ FlashErase(posicion); for(i=0;i<64;i++){ FLASH_STOCKER_I(i); cambioF1=false; } } if(cambioF2){ FlashErase(posicionColor); for(i=1;i<5;i++){ FLASH_STOCKER_C(i); CALIB_STOCKER(); cambioF2=false; } } } Al diferenciar entre los dos apartados de memoria, los cambios en cada bloque son la mitad de frecuentes. Esto es totalmente opcional, pero el proyecto quería ser testeado en bloques diferentes. 7 GUÍA DE USUARIO n este apartado se desarrollará la guía de uso para usuario, en la que se indicarán las instrucciones para el correcto funcionamiento del dispositivo. 7.1 Primer Encendido ▪ Por norma general y en el caso del dispositivo final de este TFG, el dispositivo contará con una versión precargada de prueba. Si el lector está desarrollando su propio sistema esto no será así, para este caso se desarrollará un último punto en el que se listaran las diferencias entre la versión de prueba y la que sería resultante de una carga de código directa. ▪ Se recomienda encarecidamente que se produzca un calibrado al iniciar, pulsando en el botón CAL de la pantalla principal. ▪ Con la versión precargada el usuario puede experimentar en un entorno ya iniciado las funcionalidades del dispositivo 7.2 Pantalla Principal La pantalla principal tendrá la siguiente estructura: Figure 21.Pantalla principal ▪ ROJO: botones de desplazamiento por la pantalla. Derecha incrementa, izquierda decrementa. ▪ VERDE: modos de edición. E Guía de Usuario 76 76 o MTRX es el modo automático. o DRAW el modo manual. ▪ NARANJA: botones de función. o CAL ejecuta la secuencia de calibrado. o IP muestra la IP justo encima de esta zona. o BIN ejecuta el protocolo de borrado de botones. ▪ NEGRO: número de pantalla actual. ▪ MORADO: Cuerpo principal útil de la pantalla. Es la zona que usará el modo automático para crear botones, con el modo libre podríamos saltarnos estos límites, pero eso queda bajo la responsabilidad del usuario. 7.3 Modos de Edición 7.3.1 Edición Automática 1. Al pulsar sobre el botón MTRX entraremos en la pantalla asociada a este modo. Figure 22.Pantalla de Edición Automática 2. El botón central servirá para incrementar el número de botones deseados. Figure 23. Pantalla de Edición Automática con uso 3. Cuando se termine, se pulsará de nuevo el botón MTRX para salir del modo y generar la actriz deseada. Figure 24. Matriz generada Guía de Usuario 78 78 7.3.2 Edición Libre 1. Para entrar en el modo de edición libre, se pulsará el botón correspondiente DRAW. La pantalla es igual a la principal, pero habrá perdido el número de pantalla y al tocar los botones estos no reaccionarán. 2. Para crear un botón, pulse y mantenga en algún punto de la pantalla Figure 25. Uso de la edición libre 1 3. Tras esto, deslice por la pantalla hasta tener el tamaño de botón deseado. Figure 26. Uso de la edición libre 2 4. Suelte para crear el botón. Se confirmará la creación cuando en este aparezca la etiqueta LIB. Figure 27. botón generado por la edición libre 5. Siga creando botones a antojo y, cuando termine, pulse el botón de LIB para volver al estado de funcionamiento normal con los nuevos botones creados. 7.4 Botones de funcionalidad 7.4.1 Botón de calibración Al pulsar en este botón, aparecerá una pantalla interactiva que le pedirá que pulse en los puntos indicados. Figure 28. pantalla de calibración Tras esto saldrá automáticamente del modo, habiendo calibrado la pantalla satisfactoriamente. 7.4.2 Botón de IP Pulse para cambiar el estado entre mostrar la IP u ocultarla Bibliografía 86 86 BIBLIOGRAFÍA APPLE SUPPORT, 2021. Usar la Touch Bar en Mac. En: Manual del usuario de macOS [en línea]. Disponible en: Usar la Touch Bar en la Mac - Soporte técnico de Apple [consulta: 10 septiembre 2021]. BRIDGETEK, 2019. FT8xx Portable MCU Library: Application Note [en linea]. Singapore: Bridgetek Pte Ltd (BRT Chip) [consulta 10 septiembre 2021]. Disponible en: BRT_AN_025_FT8xx_Portable_MCU_Library_DRAFT.pdf FUTURE TECHNOLOGY DEVICES INTERNATIONAL (FTDI), 2015. FT81x Series Programmers Guide [en línea]. United Kingdom: Future Technology Devices International Limited [consulta 3 agosto 2021]. doi: FT_001173. Disponible en: FT81x Series Programmers Guide (ftdichip.com) PASTOR, Javier, 2018. El ASUS Zenbook Pro 14 quiere revolucionar el segmento con un ScreenPad que es touchpad y pantalla auxiliar. Xataka [en línea]. Disponible en: Nuevo ASUS Zenbook Pro 14: características, precio y ficha técnica (xataka.com) [consulta: 10 septiembre 2021]. PENALVA, Javier, 2019. ASUS Zenbook Pro Duo, análisis: el portátil de doble pantalla 4K intenta ser el portátil definitivo. Xataka [en línea]. Disponible en: ASUS Zenbook Pro Duo, análisis: review con características, precio y especificaciones (xataka.com) [consulta: 10 septiembre 2021]. POMEYROL, J., 2021. MacBook Pro: ¿adiós a la Touch Bar? Muy Computer [en línea]. Disponible en: MacBook Pro: ¿adiós a la Touch Bar? (muycomputer.com) [consulta: 10 septiembre 2021]. TEXAS INSTRUMENTS, 2016. Tiva C Series TM4C1294 Connected LaunchPad Evaluation Kit: User’s guide [en línea]. Austin, TX: Texas Instruments Incorportated [consulta 3 agosto 2021]. doi: SPMU365C. Disponible en: Tiva C Series TM4C1294 Connected LaunchPad Evaluation Kit (Rev. C) TEXAS INSTRUMENTS, 2020. TivaWare USB Library: User’s guide [en línea]. Austin, TX: Texas Instruments Incorporated [consulta: 16 agosto 2021]. doi: SW-TM4C-USBL-UG-2.2.0.295. Disponible en: TivaWare™ USB Library for C Series User's Guide (Rev. E) UNIVERSAL SERIAL BUS, 2001. Device Class Definition for Human Interface Devices (HID)[en línea]. World: USB Enabling ConnectionsTM[consulta 20 agosto 2021]Disponible en: Microsoft Word - HID1_11.doc (usb.org) ANEXO A 88 88 ANEXO A Aquí se incluirá todo el código del proyecto dividido en sus diferentes bloques: Raiz principal Main.c #include <stdbool.h> #include <stdint.h> #include <string.h> #include <eveheader.h> #include <usbkeyboardheader.h> #include "driverlib2.h" #include "evethernet.h" #define PARAM_ERROR_RESPONSE "/perror.htm" #define JAVASCRIPT_HEADER \ "<script type='text/javascript' language='JavaScript'><!--\n" #define JAVASCRIPT_FOOTER \ "//--></script>\n" #define OFFSETBOTMATRIZ 12 #define OFFSETBOTLIBRE 38 #ifdef DEBUG void __error__(char *pcFilename, uint32_t ui32Line) { } #endif char auxTecString[36][5]; char auxRedString[10][5]; char auxGrnString[10][5]; char auxBluString[10][5]; char teclado[36][1]={"1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H ","I", "J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}; uint8_t i; uint16_t POSX, POSY; uint32_t BufferXY,g_ui32IPAddress,ui32SysClock; uint8_t TAG=0; uint8_t PAG=1; bool cambioF1=false; bool cambioF2=false; bool muestraIP=false; struct Boton Botones[64]; //12-reservados //26-botones de matriz //26-botones a mano alzada int main(void){ ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 120000000); INIT_ESTRUCT_PRINC(); Init_Cad_Comms(); Init_Ethernet_Server(); Inicia_Teclado(); CALIB_FISHER(); EVE_Init(); eve_calibrate(); cambioF2=true; CREAR_BOTON(0,0,420,90,60,"Mtrx",0,1,0); CREAR_BOTON(0,710,420,90,60,"Draw",0,2,0); CREAR_BOTON(0,750,100,50,280,"->",0,3,0); CREAR_BOTON(0,0,100,50,280,"<-",0,4,0); CREAR_BOTON(0,154*3+90-35,420,70,60,"BIN",0,5,0); CREAR_BOTON(0,154*2+90-35,420,70,60,"IP",0,6,0); CREAR_BOTON(0,154*1+90-35,420,70,60,"CAL",0,7,0); for(i=0;i<64;i++){ FLASH_FISHER_I(i); } for(i=1;i<5;i++){ FLASH_FISHER_C(i); } ////////////////////////////////////////////////////////////////////////////////////// ////////////// while(1) { EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); if(muestraIP) MOSTRAR_IP(); EVE_LIB_BeginCoProList(); // EVE_CMD_NUMBER(100,50,28,EVE_OPT_CENTER,POSX); // EVE_CMD_NUMBER(700,50,28,EVE_OPT_CENTER,POSY); EVE_CMD_NUMBER(400,20,28,EVE_OPT_CENTER,PAG); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); EVE_HANDLER_ACCION(); if(cambioF1 || cambioF2){ FLASH_RENEW(); cambioF1=false; cambioF2=false; } CHECK_FLAGS(); } } ANEXO A 90 90 Driverlib2.h //INCLUIR TODAS LAS LIBRERÍAS de DRIVERLIB #ifndef DRIVERLIB_H_ #define DRIVERLIB_H_ #include "driverlib/adc.h" #include "driverlib/aes.h" #include "driverlib/can.h" #include "driverlib/comp.h" #include "driverlib/cpu.h" #include "driverlib/crc.h" #include "driverlib/debug.h" #include "driverlib/des.h" #include "driverlib/eeprom.h" #include "driverlib/emac.h" #include "driverlib/epi.h" #include "driverlib/flash.h" #include "driverlib/fpu.h" #include "driverlib/gpio.h" #include "driverlib/hibernate.h" #include "driverlib/i2c.h" #include "driverlib/interrupt.h" #include "driverlib/lcd.h" #include "driverlib/mpu.h" #include "driverlib/onewire.h" #include "driverlib/pin_map.h" #include "driverlib/pwm.h" #include "driverlib/qei.h" #include "driverlib/rom.h" #include "driverlib/rom_map.h" #include "driverlib/rtos_bindings.h" #include "driverlib/shamd5.h" #include "driverlib/ssi.h" #include "driverlib/sw_crc.h" #include "driverlib/sysctl.h" #include "driverlib/sysexc.h" #include "driverlib/systick.h" #include "driverlib/timer.h" #include "driverlib/uart.h" #include "driverlib/udma.h" #include "driverlib/usb.h" #include "driverlib/watchdog.h" #include "inc/hw_adc.h" #include "inc/hw_aes.h" #include "inc/hw_can.h" #include "inc/hw_ccm.h" #include "inc/hw_comp.h" #include "inc/hw_des.h" #include "inc/hw_eeprom.h" #include "inc/hw_emac.h" #include "inc/hw_epi.h" #include "inc/hw_fan.h" #include "inc/hw_flash.h" #include "inc/hw_gpio.h" #include "inc/hw_hibernate.h" #include "inc/hw_i2c.h" #include "inc/hw_ints.h" #include "inc/hw_lcd.h" #include "inc/hw_memmap.h" #include "inc/hw_nvic.h" #include "inc/hw_onewire.h" #include "inc/hw_pwm.h" #include "inc/hw_qei.h" #include "inc/hw_shamd5.h" #include "inc/hw_ssi.h" #include "inc/hw_sysctl.h" #include "inc/hw_sysexc.h" #include "inc/hw_timer.h" #include "inc/hw_types.h" #include "inc/hw_uart.h" #include "inc/hw_udma.h" #include "inc/hw_usb.h" #include "inc/hw_watchdog.h" //#include "drvlibh/tm4c1294ncpdt.h" #endif /* DRIVERLIB_H_ */ Evethernet.h /* * evethernet.h * * Created on: 27 ago. 2021 * Author: pablo */ #ifndef EVETHERNET_H_ #define EVETHERNET_H_ #include <utils/locator.h> #include <utils/lwiplib.h> #include <utils/ustdlib.h> #include "httpserver_raw/httpd.h" #include "httpserver_raw/fs.h" #include "httpserver_raw/fsdata.h" #include "ethernet/io.h" #endif /* EVETHERNET_H_ */ Eveheader.h /* * evekeyboardheader.h * * Created on: 17 ago. 2021 * Author: pablo */ #ifndef EVEHEADER_H_ #define EVEHEADER_H_ #include <RecursosEve/eve_calibrateplus.h> #include <RecursosEve/EVE.h> #include <RecursosEve/EVE_config.h> #include <RecursosEve/FT8xx.h> #include <RecursosEve/HAL.h> #include <RecursosEve/MCU.h> #endif /* EVEHEADER_H_ */ ANEXO A 92 92 Usbkeyboardheader.h /* * Usbkeyboardheader2.h * * Created on: 17 ago. 2021 * Author: pablo */ #ifndef USBKEYBOARDHEADER_H_ #define USBKEYBOARDHEADER_H_ #include "usblib/usblib.h" #include "usblib/usbhid.h" #include "usblib/usb-ids.h" #include "usblib/device/usbdevice.h" #include "usblib/device/usbdhid.h" #include "usblib/device/usbdhidkeyb.h" #include "drivers/buttons.h" #include "drivers/pinout.h" #include "usbkeyboard/usb_keyb_structs.h" #include <utils/uartstdio.h> #endif /* USBKEYBOARDHEADER_H_ */ RecursosEve Eve_api.c #include <string.h> #include <stdint.h> // for Uint8/16/32 and Int8/16/32 data types #include <stdbool.h> #include "driverlib2.h" #include<ethernet/io.h> #include <eveheader.h> #include <usbkeyboardheader.h> void EVE_Init(void) { uint8_t regGpio; int i; HAL_EVE_Init(); // ------------------------- Display settings ------------------------------ // LCD display parameters // Active width of LCD display HAL_MemWrite16(EVE_REG_HSIZE, EVE_DISP_WIDTH); // Total number of clocks per line HAL_MemWrite16(EVE_REG_HCYCLE, EVE_DISP_HCYCLE); // Start of active line HAL_MemWrite16(EVE_REG_HOFFSET, EVE_DISP_HOFFSET); // Start of horizontal sync pulse HAL_MemWrite16(EVE_REG_HSYNC0, EVE_DISP_HSYNC0); // End of horizontal sync pulse HAL_MemWrite16(EVE_REG_HSYNC1, EVE_DISP_HSYNC1); // Active height of LCD display HAL_MemWrite16(EVE_REG_VSIZE, EVE_DISP_HEIGHT); // Total number of lines per screen HAL_MemWrite16(EVE_REG_VCYCLE, EVE_DISP_VCYCLE); // Start of active screen HAL_MemWrite16(EVE_REG_VOFFSET, EVE_DISP_VOFFSET); // Start of vertical sync pulse HAL_MemWrite16(EVE_REG_VSYNC0, EVE_DISP_VSYNC0); // End of vertical sync pulse HAL_MemWrite16(EVE_REG_VSYNC1, EVE_DISP_VSYNC1); // Define RGB output pins HAL_MemWrite8(EVE_REG_SWIZZLE, EVE_DISP_SWIZZLE); // Define active edge of PCLK HAL_MemWrite8(EVE_REG_PCLK_POL, EVE_DISP_PCLKPOL); // Write initial display list HAL_MemWrite32((EVE_RAM_DL + 0), EVE_ENC_CLEAR_COLOR_RGB(0,0,0)); HAL_MemWrite32((EVE_RAM_DL + 4), EVE_ENC_CLEAR(1,1,1)); HAL_MemWrite32((EVE_RAM_DL + 8), EVE_ENC_DISPLAY()); HAL_MemWrite8(EVE_REG_DLSWAP, EVE_DLSWAP_FRAME); // Read the GPIO register for a read/modify/write operation regGpio = HAL_MemRead8(EVE_REG_GPIO); // set bit 7 of GPIO register (DISP) - others are inputs regGpio = regGpio | 0x80; // Enable the DISP signal to the LCD panel HAL_MemWrite8(EVE_REG_GPIO, regGpio); // Now start clocking data to the LCD panel HAL_MemWrite8(EVE_REG_PCLK, EVE_DISP_PCLK); HAL_MemWrite8(EVE_REG_PWM_DUTY, 127); // ---------------------- Touch and Audio settings ------------------------- // Eliminate any false touches HAL_MemWrite16(EVE_REG_TOUCH_RZTHRESH, 1200); // turn recorded audio volume down HAL_MemWrite8(EVE_REG_VOL_PB, EVE_VOL_ZERO); // turn synthesizer volume down HAL_MemWrite8(EVE_REG_VOL_SOUND, EVE_VOL_ZERO); // set synthesizer to mute HAL_MemWrite16(EVE_REG_SOUND, 0x6000); // --------------------- Clear screen ready to start ----------------------- ANEXO A 94 94 EVE_LIB_BeginCoProList(); EVE_CMD_DLSTART(); EVE_CLEAR_COLOR_RGB(0, 0, 0); EVE_CLEAR(1,1,1); EVE_DISPLAY(); EVE_CMD_SWAP(); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); // ---------------------- Reset all bitmap properties ------------------------ EVE_LIB_BeginCoProList(); EVE_CMD_DLSTART(); EVE_CLEAR_COLOR_RGB(0, 0, 0); EVE_CLEAR(1,1,1); for (i = 0; i < 16; i++) { EVE_BITMAP_HANDLE(i); EVE_CMD_SETBITMAP(0,0,0,0); } EVE_DISPLAY(); EVE_CMD_SWAP(); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); } // Begins co-pro list for display creation void EVE_LIB_BeginCoProList(void) { // Wait for command FIFO to be empty and record current position in FIFO EVE_LIB_AwaitCoProEmpty(); // Begins SPI transaction HAL_ChipSelect(1); // Send address for writing as the next free location in the co-pro buffer HAL_SetWriteAddress(EVE_RAM_CMD + HAL_GetCmdPointer()); } // Ends co-pro list for display creation void EVE_LIB_EndCoProList(void) { // End SPI transaction HAL_ChipSelect(0); // Update the ring buffer pointer to start decode HAL_WriteCmdPointer(); } // Waits for the read and write pointers to become equal void EVE_LIB_AwaitCoProEmpty(void) { // Await completion of processing HAL_WaitCmdFifoEmpty(); } // Writes a block of data to the RAM_G void EVE_LIB_WriteDataToRAMG(const uint8_t *ImgData, uint32_t DataSize, uint32_t DestAddress) { // Begins SPI transaction HAL_ChipSelect(1); // Send address to which first value will be written HAL_SetWriteAddress(DestAddress); // Pad data length to multiple of 4. DataSize = (DataSize + 3) & (~3); // Send data as 32 bits. while (DataSize) { HAL_Write32(*(uint32_t *)ImgData); ImgData += 4; DataSize -= 4; } // End SPI transaction HAL_ChipSelect(0); } // Reads a block of data from the RAM_G void EVE_LIB_ReadDataFromRAMG(uint8_t *ImgData, uint32_t DataSize, uint32_t SrcAddress) { // Begins SPI transaction HAL_ChipSelect(1); // Send address to which first value will be written HAL_SetReadAddress(SrcAddress); // Pad data length to multiple of 4. DataSize = (DataSize + 3) & (~3); // Send data as 32 bits. while (DataSize) { *(uint32_t *)ImgData = HAL_Read32(); ImgData += 4; DataSize -= 4; } // End SPI transaction HAL_ChipSelect(0); } // Write a block of data to the coprocessor void EVE_LIB_WriteDataToCMD(const uint8_t *ImgData, uint32_t DataSize) { uint32_t CurrentIndex = 0; uint32_t ChunkSize = 0; const uint32_t MaxChunkSize = 128; uint8_t IsLastChunk = 0; uint16_t Freespace = 0; // This code works by sending the data in a series of one or more bursts. // If the data is more than MaxChunkSize bytes, it is sent as a series of // one or more bursts and then the remainder. MaxChunkSize is a size which // is smaller than the command buffer on the EVE and small enough to gain // maximum buffering effect from the MCU SPI hardware. // Pad data length to multiple of 4. DataSize = (DataSize + 3) & (~3); ANEXO A 102 102 uint32_t StringLength; HAL_Write32(EVE_ENC_CMD_BUTTON); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (font & 0xffff)); CommandSize = 16; StringLength = EVE_LIB_SendString(string); CommandSize = CommandSize + StringLength; HAL_IncCmdPointer(CommandSize); } void EVE_CMD_KEYS(int16_t x, int16_t y, int16_t w, int16_t h, int16_t font, uint16_t options, const char* string) { uint32_t CommandSize; uint32_t StringLength; HAL_Write32(EVE_ENC_CMD_KEYS); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (font & 0xffff)); CommandSize = 16; StringLength = EVE_LIB_SendString(string); CommandSize = CommandSize + StringLength; HAL_IncCmdPointer(CommandSize); } void EVE_CMD_NUMBER(int16_t x, int16_t y, int16_t font, uint16_t options, int32_t n) { HAL_Write32(EVE_ENC_CMD_NUMBER); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (font & 0xffff)); HAL_Write32(n); HAL_IncCmdPointer(16); } void EVE_CMD_LOADIDENTITY(void) { HAL_Write32(EVE_ENC_CMD_LOADIDENTITY); HAL_IncCmdPointer(4); } void EVE_CMD_TOGGLE(int16_t x, int16_t y, int16_t w, int16_t font, uint16_t options, uint16_t state, const char* string) { uint32_t CommandSize; uint32_t StringLength; HAL_Write32(EVE_ENC_CMD_TOGGLE); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)font << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)state << 16)|options); CommandSize = 16; StringLength = EVE_LIB_SendString(string); CommandSize = CommandSize + StringLength; HAL_IncCmdPointer(CommandSize); } /* Error handling for val is not done, so better to always use range of 65535 in order that needle is drawn within display region */ void EVE_CMD_GAUGE(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t major, uint16_t minor, uint16_t val, uint16_t range) { HAL_Write32(EVE_ENC_CMD_GAUGE); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (r & 0xffff)); HAL_Write32(((uint32_t)minor << 16) | (major & 0xffff)); HAL_Write32(((uint32_t)range << 16) | (val & 0xffff)); HAL_IncCmdPointer(20); } void EVE_CMD_REGREAD(uint32_t ptr, uint32_t result) { HAL_Write32(EVE_ENC_CMD_REGREAD); HAL_Write32(ptr); HAL_Write32(result); HAL_IncCmdPointer(12); } void EVE_CMD_GETPROPS(uint32_t ptr, uint32_t w, uint32_t h) { HAL_Write32(EVE_ENC_CMD_GETPROPS); HAL_Write32(ptr); HAL_Write32(w); HAL_Write32(h); HAL_IncCmdPointer(16); } void EVE_CMD_MEMCPY(uint32_t dest, uint32_t src, uint32_t num) { HAL_Write32(EVE_ENC_CMD_MEMCPY); HAL_Write32(dest); HAL_Write32(src); HAL_Write32(num); HAL_IncCmdPointer(16); } void EVE_CMD_SPINNER(int16_t x, int16_t y, uint16_t style, uint16_t scale) { HAL_Write32(EVE_ENC_CMD_SPINNER); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)scale << 16) | (style & 0xffff)); HAL_IncCmdPointer(12); } void EVE_CMD_BGCOLOR(uint32_t c) { HAL_Write32(EVE_ENC_CMD_BGCOLOR); HAL_Write32(c); HAL_IncCmdPointer(8); } ANEXO A 104 104 void EVE_CMD_SWAP(void) { HAL_Write32(EVE_ENC_CMD_SWAP); HAL_IncCmdPointer(4); } void EVE_CMD_INFLATE(uint32_t ptr) { HAL_Write32(EVE_ENC_CMD_INFLATE); HAL_Write32(ptr); HAL_IncCmdPointer(8); } void EVE_CMD_TRANSLATE(int32_t tx, int32_t ty) { HAL_Write32(EVE_ENC_CMD_TRANSLATE); HAL_Write32(tx); HAL_Write32(ty); HAL_IncCmdPointer(12); } void EVE_CMD_STOP(void) { HAL_Write32(EVE_ENC_CMD_STOP); HAL_IncCmdPointer(4); } void EVE_CMD_SLIDER(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) { HAL_Write32(EVE_ENC_CMD_SLIDER); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)val << 16) | (options & 0xffff)); HAL_Write32(range); HAL_IncCmdPointer(20); } void EVE_BITMAP_TRANSFORM_A(long a) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_A(a)); // ((21UL << 24) | (((a)&131071UL)<<0)) HAL_IncCmdPointer(4); } void EVE_BITMAP_TRANSFORM_B(long b) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_B(b)); // ((22UL << 24) | (((b)&131071UL)<<0)) HAL_IncCmdPointer(4); } void EVE_BITMAP_TRANSFORM_C(long c) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_C(c)); // ((23UL << 24) | (((c)&16777215UL)<<0)) HAL_IncCmdPointer(4); } void EVE_BITMAP_TRANSFORM_D(long d) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_D(d)); // ((24UL << 24) | (((d)&131071UL)<<0)) HAL_IncCmdPointer(4); } void EVE_BITMAP_TRANSFORM_E(long e) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_E(e)); // ((25UL << 24) | (((e)&131071UL)<<0)) HAL_IncCmdPointer(4); } void EVE_BITMAP_TRANSFORM_F(long f) { HAL_Write32(EVE_ENC_BITMAP_TRANSFORM_F(f)); // ((26UL << 24) | (((f)&16777215UL)<<0)) HAL_IncCmdPointer(4); } void EVE_CMD_INTERRUPT(uint32_t ms) { HAL_Write32(EVE_ENC_CMD_INTERRUPT); HAL_Write32(ms); HAL_IncCmdPointer(8); } void EVE_CMD_FGCOLOR(uint32_t c) { HAL_Write32(EVE_ENC_CMD_FGCOLOR); HAL_Write32(c); HAL_IncCmdPointer(8); } void EVE_CMD_ROTATE(int32_t a) { HAL_Write32(EVE_ENC_CMD_ROTATE); HAL_Write32(a); HAL_IncCmdPointer(8); } void EVE_CMD_MEMWRITE(uint32_t ptr, uint32_t num) { HAL_Write32(EVE_ENC_CMD_MEMWRITE); HAL_Write32(ptr); HAL_Write32(num); HAL_IncCmdPointer(12); } void EVE_CMD_SCROLLBAR(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t size, uint16_t range) { HAL_Write32(EVE_ENC_CMD_SCROLLBAR); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)val << 16) | (options & 0xffff)); HAL_Write32(((uint32_t)range << 16) | (size & 0xffff)); HAL_IncCmdPointer(20); } ANEXO A 106 106 void EVE_CMD_GETMATRIX(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f) { HAL_Write32(EVE_ENC_CMD_GETMATRIX); HAL_Write32(a); HAL_Write32(b); HAL_Write32(c); HAL_Write32(d); HAL_Write32(e); HAL_Write32(f); HAL_IncCmdPointer(28); } void EVE_CMD_SKETCH(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t ptr, uint16_t format) { HAL_Write32(EVE_ENC_CMD_SKETCH); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(ptr); HAL_Write32(format); HAL_IncCmdPointer(20); } void EVE_CMD_MEMSET(uint32_t ptr, uint32_t value, uint32_t num) { HAL_Write32(EVE_ENC_CMD_MEMSET); HAL_Write32(ptr); HAL_Write32(value); HAL_Write32(num); HAL_IncCmdPointer(16); } void EVE_CMD_GRADCOLOR(uint32_t c) { HAL_Write32(EVE_ENC_CMD_GRADCOLOR); HAL_Write32(c); HAL_IncCmdPointer(8); } void EVE_CMD_BITMAP_TRANSFORM(int32_t x0, int32_t y0, int32_t x1, int32_t y1, int32_t x2, int32_t y2, int32_t tx0, int32_t ty0, int32_t tx1, int32_t ty1, int32_t tx2, int32_t ty2, uint16_t result) { HAL_Write32(EVE_ENC_CMD_BITMAP_TRANSFORM); HAL_Write32(x0); HAL_Write32(y0); HAL_Write32(x1); HAL_Write32(y1); HAL_Write32(x2); HAL_Write32(y2); HAL_Write32(tx0); HAL_Write32(ty0); HAL_Write32(tx1); HAL_Write32(ty1); HAL_Write32(tx2); HAL_Write32(ty2); HAL_Write32(result); HAL_IncCmdPointer(56); } void EVE_CMD_CALIBRATE(uint32_t result) { HAL_Write32(EVE_ENC_CMD_CALIBRATE); HAL_Write32(result); HAL_IncCmdPointer(8); } void EVE_CMD_SETFONT(uint32_t font, uint32_t ptr) { HAL_Write32(EVE_ENC_CMD_SETFONT); HAL_Write32(font); HAL_Write32(ptr); HAL_IncCmdPointer(12); } void EVE_CMD_LOGO(void) { HAL_Write32(EVE_ENC_CMD_LOGO); HAL_IncCmdPointer(4); } void EVE_CMD_APPEND(uint32_t ptr, uint32_t num) { HAL_Write32(EVE_ENC_CMD_APPEND); HAL_Write32(ptr); HAL_Write32(num); HAL_IncCmdPointer(12); } void EVE_CMD_MEMZERO(uint32_t ptr, uint32_t num) { HAL_Write32(EVE_ENC_CMD_MEMZERO); HAL_Write32(ptr); HAL_Write32(num); HAL_IncCmdPointer(12); } void EVE_CMD_SCALE(int32_t sx, int32_t sy) { HAL_Write32(EVE_ENC_CMD_SCALE); HAL_Write32(sx); HAL_Write32(sy); HAL_IncCmdPointer(12); } void EVE_CMD_CLOCK(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t h, uint16_t m, uint16_t s, uint16_t ms) { HAL_Write32(EVE_ENC_CMD_CLOCK); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (r & 0xffff)); HAL_Write32(((uint32_t)m << 16) | (h & 0xffff)); HAL_Write32(((uint32_t)ms << 16) | (s & 0xffff)); HAL_IncCmdPointer(20); } ANEXO A 108 108 void EVE_CMD_GRADIENT(int16_t x0, int16_t y0, uint32_t rgb0, int16_t x1, int16_t y1, uint32_t rgb1) { HAL_Write32(EVE_ENC_CMD_GRADIENT); HAL_Write32(((uint32_t)y0 << 16) | (x0 & 0xffff)); HAL_Write32(rgb0); HAL_Write32(((uint32_t)y1 << 16) | (x1 & 0xffff)); HAL_Write32(rgb1); HAL_IncCmdPointer(20); } void EVE_CMD_SETMATRIX(void) { HAL_Write32(EVE_ENC_CMD_SETMATRIX); HAL_IncCmdPointer(4); } void EVE_CMD_TRACK(int16_t x, int16_t y, int16_t w, int16_t h, int16_t tag) { HAL_Write32(EVE_ENC_CMD_TRACK); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(tag); HAL_IncCmdPointer(16); } void EVE_CMD_GETPTR(uint32_t result) { HAL_Write32(EVE_ENC_CMD_GETPTR); HAL_Write32(result); HAL_IncCmdPointer(8); } void EVE_CMD_PROGRESS(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t options, uint16_t val, uint16_t range) { HAL_Write32(EVE_ENC_CMD_PROGRESS); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(((uint32_t)val << 16) | (options & 0xffff)); HAL_Write32(range); HAL_IncCmdPointer(20); } void EVE_CMD_COLDSTART(void) { HAL_Write32(EVE_ENC_CMD_COLDSTART); HAL_IncCmdPointer(4); } void EVE_CMD_DIAL(int16_t x, int16_t y, int16_t r, uint16_t options, uint16_t val) { HAL_Write32(EVE_ENC_CMD_DIAL); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)options << 16) | (r & 0xffff)); HAL_Write32(val); HAL_IncCmdPointer(16); } void EVE_CMD_LOADIMAGE(uint32_t ptr, uint32_t options) { HAL_Write32(EVE_ENC_CMD_LOADIMAGE); HAL_Write32(ptr); HAL_Write32(options); HAL_IncCmdPointer(12); } void EVE_CMD_DLSTART(void) { HAL_Write32(EVE_ENC_CMD_DLSTART); HAL_IncCmdPointer(4); } void EVE_CMD_SNAPSHOT(uint32_t ptr) { HAL_Write32(EVE_ENC_CMD_SNAPSHOT); HAL_Write32(ptr); HAL_IncCmdPointer(8); } void EVE_CMD_SCREENSAVER(void) { HAL_Write32(EVE_ENC_CMD_SCREENSAVER); HAL_IncCmdPointer(4); } void EVE_CMD_MEMCRC(uint32_t ptr, uint32_t num, uint32_t result) { HAL_Write32(EVE_ENC_CMD_MEMCRC); HAL_Write32(ptr); HAL_Write32(num); HAL_Write32(result); HAL_IncCmdPointer(16); } // FT81X-only features #ifdef FT81X_ENABLE // ############### GPU ############################################ void EVE_VERTEX_FORMAT(uint8_t frac) { HAL_Write32(EVE_ENC_VERTEX_FORMAT(frac)); HAL_IncCmdPointer(4); } void EVE_BITMAP_LAYOUT_H(uint8_t linestride, uint8_t height) { HAL_Write32(EVE_ENC_BITMAP_LAYOUT_H(linestride, height)); HAL_IncCmdPointer(4); } void EVE_BITMAP_SIZE_H(uint8_t width, uint8_t height) { HAL_Write32(EVE_ENC_BITMAP_SIZE_H(width, height)); HAL_IncCmdPointer(4); } ANEXO A 110 110 void EVE_PALETTE_SOURCE(uint32_t addr) { HAL_Write32(EVE_ENC_PALETTE_SOURCE(addr)); HAL_IncCmdPointer(4); } void EVE_VERTEX_TRANSLATE_X(uint32_t x) { HAL_Write32(EVE_ENC_VERTEX_TRANSLATE_X(x)); HAL_IncCmdPointer(4); } void EVE_VERTEX_TRANSLATE_Y(uint32_t y) { HAL_Write32(EVE_ENC_VERTEX_TRANSLATE_Y(y)); HAL_IncCmdPointer(4); } void EVE_NOP(void) { HAL_Write32(EVE_ENC_NOP()); HAL_IncCmdPointer(4); } // ############### CO-PRO ######################################### void EVE_CMD_SETROTATE(uint32_t r) { HAL_Write32(EVE_ENC_CMD_SETROTATE); HAL_Write32(r); HAL_IncCmdPointer(8); } void EVE_CMD_SETFONT2(uint32_t font, uint32_t ptr, uint32_t firstchar) { HAL_Write32(EVE_ENC_CMD_SETFONT2); HAL_Write32(font); HAL_Write32(ptr); HAL_Write32(firstchar); HAL_IncCmdPointer(16); } void EVE_CMD_SNAPSHOT2(uint32_t fmt, uint32_t ptr, int16_t x, int16_t y, int16_t w, int16_t h) { HAL_Write32(EVE_ENC_CMD_SNAPSHOT2); HAL_Write32(fmt); HAL_Write32(ptr); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_IncCmdPointer(20); } void EVE_CMD_MEDIAFIFO(uint32_t ptr, uint32_t size) { HAL_Write32(EVE_ENC_CMD_MEDIAFIFO); HAL_Write32(ptr); HAL_Write32(size); HAL_IncCmdPointer(12); } void EVE_CMD_INT_SWLOADIMAGE(uint32_t ptr, uint32_t options) { HAL_Write32(EVE_ENC_CMD_INT_SWLOADIMAGE); HAL_Write32(ptr); HAL_Write32(options); HAL_IncCmdPointer(12); } void EVE_CMD_SYNC(void) { HAL_Write32(EVE_ENC_CMD_SYNC); HAL_IncCmdPointer(4); } void EVE_CMD_CSKETCH(int16_t x, int16_t y, uint16_t w, uint16_t h, uint32_t ptr, uint16_t format, uint16_t freq) { HAL_Write32(EVE_ENC_CMD_CSKETCH); HAL_Write32(((uint32_t)y << 16) | (x & 0xffff)); HAL_Write32(((uint32_t)h << 16) | (w & 0xffff)); HAL_Write32(ptr); HAL_Write32(((uint32_t)freq << 16) | (format & 0xffff)); HAL_IncCmdPointer(20); } void EVE_CMD_ROMFONT(uint32_t font, uint32_t romslot) { HAL_Write32(EVE_ENC_CMD_ROMFONT); HAL_Write32(font); HAL_Write32(romslot); HAL_IncCmdPointer(12); } void EVE_CMD_PLAYVIDEO(uint32_t options) { HAL_Write32(EVE_ENC_CMD_PLAYVIDEO); HAL_Write32(options); HAL_IncCmdPointer(8); } void EVE_CMD_VIDEOFRAME(uint32_t dst, uint32_t ptr) { HAL_Write32(EVE_ENC_CMD_VIDEOFRAME); HAL_Write32(dst); HAL_Write32(ptr); HAL_IncCmdPointer(12); } void EVE_CMD_VIDEOSTART(void) { HAL_Write32(EVE_ENC_CMD_VIDEOSTART); HAL_IncCmdPointer(4); } void EVE_CMD_SETBASE(uint32_t base) { HAL_Write32(EVE_ENC_CMD_SETBASE); ANEXO A 118 118 uint8_t TeclaAPulsar; uint8_t aux=0; char NameTeclaAPulsar[6]="\0\0\0\0\0\0"; while(comandoR!=VirTec && i<64){ i++; if(Botones[i-1].pantallaAsoc==page){ comandoR++; } } if(ModTec[0]){ NameTeclaAPulsar[aux]= 'C'; NameTeclaAPulsar[aux+1]= '+'; aux++; } if(ModTec[1]){ NameTeclaAPulsar[aux]= 'A'; NameTeclaAPulsar[aux+1]= '+'; aux++; } if(ModTec[2]){ NameTeclaAPulsar[aux]= 'S'; NameTeclaAPulsar[aux+1]= '+'; aux++; } if(NameTeclaAPulsar[aux]=='+'){ aux++; } if(Tec<8){ TeclaAPulsar=30 + Tec; NameTeclaAPulsar[aux]= '1' + Tec; } else if(Tec==9){ TeclaAPulsar=0x27; NameTeclaAPulsar[aux]= '0'; } else{ TeclaAPulsar=0x04+Tec-10; NameTeclaAPulsar[aux]= 'A'+ Tec-10; } strcpy(Botones[i-1].name,NameTeclaAPulsar); Botones[i-1].BotAccion.comando=TeclaAPulsar; Botones[i-1].BotAccion.mod[0]=ModTec[0]; Botones[i-1].BotAccion.mod[1]=ModTec[1]; Botones[i-1].BotAccion.mod[2]=ModTec[2]; Botones[i-1].BotAccion.mod[3]=1; cambioF1=true; } void EVE_HANDLER_ACCION(){ uint8_t accion=0; uint8_t TAG_P=TAG; uint8_t i; if(SubmitState){ SubmitState=false; WEB_SUBMITER(); } if(TAG!=0){ accion=Botones[TAG-1].BotAccion.tipo; switch (accion) { default: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } break; case 1: EVE_EDIT_MODE_MATRIX(); break; case 2: EVE_EDIT_MODE_L(); break; case 3: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } if(PAG>=4) PAG=1; else PAG+=1; break; case 4: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } if(PAG<=1) PAG=4; else PAG-=1; break; ANEXO A 120 120 case 5: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); }; while(TAG!=5){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); if(TAG>=OFFSETBOTMATRIX) EVE_ERASE(TAG); } while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); }; break; case 6: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } muestraIP=!muestraIP; break; case 7: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } for(i=0;i<6;i++){ calib.transform[i]=0; } eve_calibrate(); cambioF2=true; break; case 10: while(TAG!=0){ EVE_LEER_PANTALLA(); EVE_INICIALIZAR_NUEVA_PANTALLA(); EVE_COLOR_ELEMENTO(); EVE_MOSTRAR_BOTONES_GLOBAL(); EVE_MOSTRAR_FIN_PANTALLA(); TAG=EVE_LEER_TAG(); } EVE_ACTUADOR(TAG_P); break; } } } void EVE_ERASE(uint8_t boton){ uint8_t index=OFFSETBOTMATRIX; while(Botones[index].tagAsoc!=boton && index<65){ index+=1; } if(index<65){ Botones[index].fis.x=0; Botones[index].fis.y=0; Botones[index].fis.w=0; Botones[index].fis.h=0; Botones[index].name[0]='\0'; Botones[index].tagAsoc=0; Botones[index].pantallaAsoc=16; Botones[index].BotAccion.tipo=0; Botones[index].BotAccion.comando=0; Botones[index].BotAccion.mod[0]=0; Botones[index].BotAccion.mod[1]=0; Botones[index].BotAccion.mod[2]=0; Botones[index].BotAccion.mod[3]=0; cambioF1=true; } } int i; void EVE_MOSTRAR_BOTONES_GLOBAL(){ EVE_LIB_BeginCoProList(); for(i=0;i<64;i++){ if(Botones[i].pantallaAsoc==0 || Botones[i].pantallaAsoc==PAG){ EVE_BOTON_P_2(i); } } EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); ANEXO A 122 122 } void EVE_ACTUADOR(uint8_t tag){ if(Botones[tag-1].BotAccion.mod[3]){ uint8_t AuxModTec=0; if(Botones[tag-1].BotAccion.mod[0]) AuxModTec=AuxModTec|0x01; if(Botones[tag-1].BotAccion.mod[1]) AuxModTec=AuxModTec|0x04; if(Botones[tag-1].BotAccion.mod[2]) AuxModTec=AuxModTec|0x02; USBDHIDKeyboardKeyStateChange(&g_sKeyboardDevice,AuxModTec,Botones[tag- 1].BotAccion.comando,1); SysCtlDelay(40*40000); USBDHIDKeyboardKeyStateChange(&g_sKeyboardDevice,0,Botones[tag- 1].BotAccion.comando,0); SysCtlDelay(40*40000); } else{ USBDHIDKeyboardKeyStateChange(&g_sKeyboardDevice,EveKeypUsageCodes[Botones[tag- 1].BotAccion.comando-1][0],EveKeypUsageCodes[Botones[tag-1].BotAccion.comando- 1][1],1); SysCtlDelay(20*40000); USBDHIDKeyboardKeyStateChange(&g_sKeyboardDevice,0,EveKeypUsageCodes[Botones[tag- 1].BotAccion.comando-1][1],0); SysCtlDelay(20*40000); } } void INIT_ESTRUCT_PRINC(){ uint8_t i; for(i=0;i<64;i++){ Botones[i].fis.x=0; Botones[i].fis.y=0; Botones[i].fis.w=0; Botones[i].fis.h=0; strcpy(Botones[i].name,"\0\0\0\0\0\0"); Botones[i].tagAsoc=0; Botones[i].pantallaAsoc=16; Botones[i].BotAccion.tipo=0; Botones[i].BotAccion.comando=0; Botones[i].BotAccion.mod[0]=0; Botones[i].BotAccion.mod[1]=0; Botones[i].BotAccion.mod[2]=0; Botones[i].BotAccion.mod[3]=0; } } uint32_t posicion=0x50000; uint32_t posicionColor=0x60000; void FLASH_STOCKER_I(uint8_t i){ uint32_t posicionAux=posicion+i*24; uint32_t posAux; if(Botones[i].fis.w!=0 && Botones[i].fis.h!=0){ posAux=0; posAux|=Botones[i].fis.x; posAux|=Botones[i].fis.y<<16; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].fis.w; posAux|=Botones[i].fis.h<<16; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].name[0]<<0; posAux|=Botones[i].name[1]<<8; posAux|=Botones[i].name[2]<<16; posAux|=Botones[i].name[3]<<24; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].name[4]<<0; posAux|=Botones[i].name[5]<<8; posAux|=Botones[i].tagAsoc<<16; posAux|=Botones[i].pantallaAsoc<<24; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=Botones[i].BotAccion.tipo<<0; posAux|=Botones[i].BotAccion.comando<<8; posAux|=Botones[i].BotAccion.mod[0]<<24; posAux|=Botones[i].BotAccion.mod[1]<<25; posAux|=Botones[i].BotAccion.mod[2]<<26; posAux|=Botones[i].BotAccion.mod[3]<<27; FlashProgram(&posAux,posicionAux,4); } } void FLASH_FISHER_I(uint8_t i){ uint32_t posicionAux=posicion+i*24; uint32_t posAux=0; if(HWREG(posicionAux)!=0xffffffff && HWREG(posicionAux+4)!=0xffffffff && HWREG(posicionAux+8)!=0xffffffff && HWREG(posicionAux+12)!=0xffffffff){ posAux=HWREG(posicionAux); Botones[i].fis.x=(uint16_t)(posAux & 0xffff); Botones[i].fis.y=(uint16_t)(posAux >>16); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].fis.w=(uint16_t)(posAux & 0xffff); Botones[i].fis.h=(uint16_t)(posAux >>16); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].name[0]=(char)(posAux & 0xffffff); ANEXO A 124 124 Botones[i].name[1]=(char)((posAux & 0xffff)>>8); Botones[i].name[2]=(char)(posAux>>16 & 0xff); Botones[i].name[3]=(char)(posAux >> 24); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].name[4]=(char)(posAux & 0xffffff); Botones[i].name[5]=(char)((posAux & 0xffff)>>8); Botones[i].tagAsoc=(uint8_t)(posAux>>16 & 0xff); Botones[i].pantallaAsoc=(uint8_t)(posAux >> 24); posicionAux+=4; posAux=0; posAux=HWREG(posicionAux); Botones[i].BotAccion.tipo=(uint8_t)(posAux & 0x000000ff); Botones[i].BotAccion.comando=(uint16_t)((posAux & 0x00ffff00)>>8); Botones[i].BotAccion.mod[0]=(bool)(posAux>>24 & 0x01); Botones[i].BotAccion.mod[1]=(bool)(posAux>>25 & 0b0000001); Botones[i].BotAccion.mod[2]=(bool)(posAux>>26 & 0b000001); Botones[i].BotAccion.mod[3]=(bool)(posAux>>27 & 0b00001); } } void FLASH_STOCKER_C(uint8_t i){ i-=1; uint32_t posicionAux=posicionColor+i*8; uint32_t posAux; posAux=0; posAux|=RedVal[i]; posAux|=GrnVal[i]<<8; posAux|=BluVal[i]<<16; posAux|=RedBot[i]<<24; FlashProgram(&posAux,posicionAux,4); posicionAux+=4; posAux=0; posAux|=GrnBot[i]; posAux|=BluBot[i]<<8; FlashProgram(&posAux,posicionAux,4); } void FLASH_FISHER_C(uint8_t i){ i-=1; uint32_t posicionAux=posicionColor+i*8; uint32_t posAux; posAux=HWREG(posicionAux); RedVal[i]=(uint16_t)(posAux & 0x000000ff); GrnVal[i]=(uint16_t)(posAux>>8 & 0x0000ff); BluVal[i]=(uint16_t)(posAux>>16 & 0x00ff); RedBot[i]=(uint16_t)(posAux >>24); posicionAux+=4; posAux=HWREG(posicionAux); GrnBot[i]=(uint16_t)(posAux & 0x000000ff); BluBot[i]=(uint16_t)(posAux>>8 & 0x0000ff); } void FLASH_RENEW(){ uint8_t i; if(cambioF1){ FlashErase(posicion); for(i=0;i<64;i++){ FLASH_STOCKER_I(i); cambioF1=false; } } if(cambioF2){ FlashErase(posicionColor); for(i=1;i<5;i++){ FLASH_STOCKER_C(i); CALIB_STOCKER(); cambioF2=false; } } } void CALIB_STOCKER(){ uint32_t posicionAux=posicionColor+4*8; uint8_t i; for(i=0;i<6;i++){ FlashProgram(&calib.transform[i],posicionAux,4); posicionAux+=4; } } void CALIB_FISHER(){ uint32_t posicionAux=posicionColor+4*8; if(HWREG(posicionAux)!=0XFFFFFFFF){ for(i=0;i<6;i++){ calib.transform[i]=HWREG(posicionAux); posicionAux+=4; } } } extern uint32_t g_ui32IPAddress; char pcBuf[16]; #include "ustdlib.h" void MOSTRAR_IP(){ EVE_LIB_BeginCoProList(); usprintf(pcBuf, "%d.%d.%d.%d", g_ui32IPAddress & 0xff, (g_ui32IPAddress >> 8) & 0xff, (g_ui32IPAddress >> 16) & 0xff, (g_ui32IPAddress >> 24) & 0xff); EVE_CMD_TEXT(400,380,28,EVE_OPT_CENTER,pcBuf); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); } ANEXO A 126 126 #endif /* FT81X_ENABLE */ Eve_calibrate.c include <eveheader.h> #include <stdint.h> int8_t platform_calib_init(void) { return 1; } int8_t platform_calib_write(struct touchscreen_calibration *calib) { return 0; } int8_t platform_calib_read(struct touchscreen_calibration *calib) { return -1; } extern struct touchscreen_calibration calib; void eve_calibrate(void) { uint8_t dummy; platform_calib_init(); // If no store of calibration or current screen touch. if (calib.transform[0]==0 && calib.transform[1]==0 && calib.transform[2]==0 && calib.transform[3]==0 && calib.transform[4]==0 && calib.transform[5]==0){ // Wait for end of touch. while (eve_read_tag(&dummy)); EVE_LIB_BeginCoProList(); EVE_CMD_DLSTART(); EVE_CLEAR_COLOR_RGB(0, 0, 0); EVE_CLEAR(1,1,1); EVE_COLOR_RGB(255, 255, 255); EVE_CMD_TEXT(EVE_DISP_WIDTH/2, EVE_DISP_HEIGHT/2, 28, EVE_OPT_CENTERX | EVE_OPT_CENTERY,"Please tap on the dots"); EVE_CMD_CALIBRATE(0); EVE_LIB_EndCoProList(); EVE_LIB_AwaitCoProEmpty(); calib.transform[0] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_A); calib.transform[1] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_B); calib.transform[2] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_C); calib.transform[3] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_D); calib.transform[4] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_E); calib.transform[5] = HAL_MemRead32(EVE_REG_TOUCH_TRANSFORM_F); platform_calib_write(&calib); } else { HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_A, calib.transform[0]); HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_B, calib.transform[1]); HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_C, calib.transform[2]); HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_D, calib.transform[3]); HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_E, calib.transform[4]); HAL_MemWrite32(EVE_REG_TOUCH_TRANSFORM_F, calib.transform[5]); } } uint8_t eve_read_tag(uint8_t *key) { uint8_t Read_tag; uint8_t key_detect = 0; Read_tag = HAL_MemRead8(EVE_REG_TOUCH_TAG); if (!(HAL_MemRead16(EVE_REG_TOUCH_RAW_XY) & 0x8000)) { key_detect = 1; *key = Read_tag; } return key_detect; } EVE_MCU_TM4C1294XL.c #include <eveheader.h> #include <stdbool.h> #include <stdint.h> #include "driverlib2.h" // ======================================================================= // Function Declarations // ======================================================================= #define dword long #define byte char #define CPU_FREQ 1000 int RELOJ; // ##################################################################################### ####################### // User Application - Initialization of MCU / FT800 / Display // ##################################################################################### ####################### uint32_t SSI_BASE,CS_PORT,CS_PIN,PD_PORT,PD_PIN; // ##################################################################################### ####################### // FT800 SPI Functions