Ejemplo de aplicación con Arduino: medida de caudal.
Transcription
Ejemplo de aplicación con Arduino: medida de caudal.
Ejemplo de aplicación con Arduino: medida de caudal. TITULACIÓN: Ingeniería Técnica Industrial en Electrónica Industrial AUTOR: Josep Fernandez Daroca DIRECTOR: José Luís Ramírez Falo FECHA: Septiembre / 2012. Índice de contenido 1 OBJETIVO DEL PFC ................................................................................................................. 6 2 ARDUINO ..................................................................................................................................... 6 2.1 INTRODUCCIÓN A ARDUINO ................................................................................................... 6 2.1.1 ¿Qué es Arduino? ........................................................................................................ 6 2.1.2 ¿Qué significa que Arduino sea Open Hardware?[4] ................................................ 6 2.1.3 ¿Por qué Arduino? ...................................................................................................... 7 2.2 LAS PLACAS ARDUINO ........................................................................................................... 8 2.2.1 ¿Qué significa que una placa sea Arduino? ................................................................ 8 2.2.2 Placas de entradas y salidas ....................................................................................... 8 2.2.3 "Shields" y otras placas de terceros .......................................................................... 10 2.2.4 Construir nuestro propio Arduino ............................................................................. 12 2.2.5 ¿Cómo obtener una placa Arduino? ......................................................................... 12 2.2.6 Nuestra elección: El Arduino UNO ........................................................................... 12 2.2.6.1 2.2.6.2 2.2.6.3 El porqué de nuestra elección ................................................................................................ 12 Características ........................................................................................................................ 13 Esquema y pines .................................................................................................................... 14 2.3 EL ENTORNO DE TRABAJO .................................................................................................... 15 2.3.1 El entorno de desarrollo y los drivers de la placa para Windows ............................ 15 2.3.2 Descargar y ejecutar un ejemplo de aplicación Arduino .......................................... 18 2.3.2.1 2.3.2.2 2.3.2.3 Editor ..................................................................................................................................... 18 Compilador ............................................................................................................................ 19 Cargar y depurar .................................................................................................................... 20 2.4 LENGUAJE DE PROGRAMACIÓN ARDUINO ............................................................................ 22 2.4.1 Introducción e historia .............................................................................................. 22 2.4.2 Funciones básicas y operadores ............................................................................... 22 2.4.2.1 2.4.2.2 2.4.2.3 2.4.3 2.4.3.1 2.4.3.2 3 Estructuras ............................................................................................................................. 23 Variables ................................................................................................................................ 24 Funciones............................................................................................................................... 25 Uso de librerías ......................................................................................................... 27 Librerías Estándar .................................................................................................................. 27 Librerías de terceros .............................................................................................................. 28 NUESTRO PROYECTO ARDUINO ....................................................................................... 30 3.1 MEDIDOR DE CAUDAL .......................................................................................................... 30 3.2 REQUERIMIENTOS INICIALES ............................................................................................... 30 3.3 ELECCIÓN DE LOS COMPONENTES ........................................................................................ 31 3.4 LISTA DE MATERIAL Y PRESUPUESTO ................................................................................... 32 3.5 EL HARDWARE ..................................................................................................................... 33 3.5.1 Características técnicas de los componentes ............................................................ 33 3.5.1.1 3.5.1.2 Shield LCD color ................................................................................................................... 33 Sensor de caudal .................................................................................................................... 34 3.5.2 El ensamblado ........................................................................................................... 35 3.6 EL SOFTWARE ...................................................................................................................... 37 3.6.1 Diseño de la pantalla ................................................................................................ 37 3.6.1.1 3.6.1.2 Uso de la librería del shield LCD a color de Sparkfun .......................................................... 37 Información a mostrar e interfaz ............................................................................................ 39 3.6.2 El programa de control de Arduino .......................................................................... 40 3.6.3 El programa de control del PC ................................................................................. 43 3.7 JUEGO DE PRUEBAS .............................................................................................................. 44 3.7.1 Test sobre el caudalímetro ........................................................................................ 44 3.7.2 Test sobre la captura de pulsos ................................................................................. 45 2 3.7.3 4 Test de funcionamiento general................................................................................. 47 USO DE ARDUINO EN ENTORNOS LECTIVOS ................................................................ 48 4.1 EDUCACIÓN SECUNDARIA .................................................................................................... 48 4.2 EDUCACIÓN TÉCNICA SUPERIOR .......................................................................................... 48 4.2.1 Usar AVRStudio, código C y Arduino ....................................................................... 49 4.2.2 Simular y ejecutar el código C en nuestro microcontrolador ................................... 52 4.2.2.1 4.2.2.2 4.2.2.3 4.2.3 4.2.3.1 4.2.3.2 4.2.3.3 4.2.3.4 4.2.3.5 4.2.3.6 4.2.3.7 4.2.4 5 Simular el código en el PC. AVRSimulator........................................................................... 53 Descargar el código en nuestra placa Arduino. AVRDude. ................................................... 53 Uso de entornos de depuración y programación .................................................................... 54 El ATmega328P........................................................................................................ 55 Un breve resumen .................................................................................................................. 55 Puertos de entrada y salida digital ......................................................................................... 55 Uso de la USART/comunicación serie .................................................................................. 58 Las interrupciones en los ATmega328P ................................................................................ 60 Interrupciones por cambio de PIN ......................................................................................... 64 Uso de temporizadores y PWM ............................................................................................. 66 Uso del conversor ADC ......................................................................................................... 74 Codificación del medidor de caudal en C ................................................................. 75 PRESUPUESTO ......................................................................................................................... 78 5.1 PRECIOS UNITARIOS ............................................................................................................. 78 5.2 PRECIOS DESCOMPUESTOS ................................................................................................... 78 5.2.1 Capítulo 1: Estudios previos ..................................................................................... 78 5.2.2 Capítulo 2: Diseño y montaje del hardware ............................................................. 79 5.2.3 Capítulo 3: Diseño del firmware ............................................................................... 79 5.2.4 Capítulo 4: Diseño del software de PC ..................................................................... 79 5.2.5 Capítulo 5: Documentación ...................................................................................... 80 5.3 RESUMEN DEL PRESUPUESTO ............................................................................................... 80 6 CONCLUSIONES Y VALORACIÓN PERSONAL ............................................................... 81 Índice de ilustraciones Ilustración 1: Logo oficial de Arduino ......................................................................... 8 Ilustración 2: En la parte superior Arduino MEGA y en la inferior Arduino UNO .... 9 Ilustración 3: Arduino con tres "shields" ................................................................... 10 Ilustración 4: RobotShop Rover ................................................................................. 11 Ilustración 5: Arduino Single-Sided Serial o Severino .............................................. 12 Ilustración 6: Arduino UNO, vista frontal ................................................................. 13 Ilustración 7: Arduino UNO, vista trasera ................................................................. 13 Ilustración 8: Pines de Arduino contra ATmega328 .................................................. 14 Ilustración 9: Arduino en el administrador de dispositivos de Windows .................. 16 Ilustración 10: Entorno SW de Arduino..................................................................... 17 Ilustración 11: Acceso a los ejemplos a través del menú ........................................... 19 Ilustración 12: Acceso a los ejemplos a través de la barra de herramientas .............. 19 Ilustración 13: Ejemplo de código Arduino ............................................................... 19 Ilustración 14: Resultado de verificación correcto..................................................... 20 Ilustración 15: Resultado de verificación incorrecto. ................................................ 20 Ilustración 16: Ubicación del LED de test en la placa Arduino ................................. 21 3 Ilustración 17: Monitor de comunicación serie integrado en el entorno Arduino ..... 22 Ilustración 18: Diagrama de bloques del caudalímetro .............................................. 31 Ilustración 19: Shield LCD a color ............................................................................ 33 Ilustración 20: Punto a cortocircuitar para activar la retro iluminación..................... 34 Ilustración 21: Sensor de caudal Koolance ................................................................ 34 Ilustración 22: Relación entre litros y frecuencia del medidor de caudal .................. 34 Ilustración 23: Esquema de montaje .......................................................................... 35 Ilustración 24: Esquema de conexionado del caudalímetro ....................................... 36 Ilustración 25: Vista lateral del montaje final ............................................................ 36 Ilustración 26: Vista frontal del montaje final ........................................................... 37 Ilustración 27: Listado de las librerías en el entorno Arduino ................................... 38 Ilustración 28: Uso de una librería una vez instalada en el entorno Arduino ............ 38 Ilustración 29: Pantalla principal ............................................................................... 40 Ilustración 30: Pantalla contadores parciales 1 .......................................................... 40 Ilustración 31: Pantalla contadores parciales 2 .......................................................... 40 Ilustración 32: Diagrama de flujo del firmware ......................................................... 41 Ilustración 33: Diagrama temporal del firmware ....................................................... 42 Ilustración 34: Salida del programa de visualización en el PC .................................. 43 Ilustración 35: Tren de pulsos del caudalímetro girando a baja velocidad ................ 44 Ilustración 36: Tren de pulsos del caudalímetro girando a media velocidad ............. 45 Ilustración 37: Tren de pulsos del caudalímetro girando a alta velocidad ................. 45 Ilustración 38: Respuesta ante un tren de pulsos de 31Hz ......................................... 46 Ilustración 39: Muestra de la pantalla principal ante una entrada de 31Hz ............... 46 Ilustración 40: Secuencia de acciones en funcionamiento normal............................. 47 Ilustración 41: Prueba de precisión del contador de segundos .................................. 48 Ilustración 42: Vista inicial del AVR Studio ............................................................. 49 Ilustración 43: Selección de dispositivo en AVR Studio ........................................... 50 Ilustración 44: Vista de codificación del AVR Studio ............................................... 51 Ilustración 45: Resultado de la compilación en el AVR Studio................................. 52 Ilustración 46: Vista del simulador integrado en AVR Studio .................................. 53 Ilustración 47: Vista de ensamblador en AVRSimulator ........................................... 57 Ilustración 48: Puerto general de entrada/salida digital ............................................. 58 Ilustración 49: Ejemplo de uso del temporizador en modo CTC ............................... 71 Ilustración 50: Generación PWM en modo “Fast PWM” .......................................... 72 Ilustración 51: Generación PWM en modo “Phase Correct PWM” .......................... 72 4 Ilustración 52: Generación PWM en modo “Phase and Frequency Correct PWM”.. 73 Ilustración 53: Uso del temporizador como “Input capture” ..................................... 73 Ilustración 54: Configuración opciones de optimización del compilador ................. 76 Ilustración 55: Configuración opciones de optimización del linker .......................... 76 Ilustración 56: Resultado de la construcción del proyecto en AVRStudio ................ 77 Ilustración 57: Modificación del fichero ColorLCDShield.cpp. 1era parte ............... 84 Ilustración 58: Modificación del fichero ColorLCDShield.cpp. 2da parte ................ 85 Ilustración 59: Configuración de una herramienta externa en AVRStudio ............... 99 Ilustración 60: Vista para crear un botón para las herramientas externas en AVRStudio ........................................................................................................................ 100 Ilustración 61: Cuadro de opciones para botón en la barra de herramientas. I ........ 100 Ilustración 62: Cuadro de opciones para botón en la barra de herramientas. II ....... 101 Ilustración 63: Vista del botón en la barra de herramientas ..................................... 101 Ilustración 64: Estructura de carpetas del soporte digital ........................................ 112 Índice de anexos ANEXOS ............................................................................................................................................. 83 ANEXO 1: INFORMACIÓN USO LIBRERÍA LCD ................................................................................ 83 Modificaciones sobre la librería................................................................................................. 84 ANEXO 2: PROGRAMA DE CONTROL DE ARDUINO. ......................................................................... 86 ANEXO 3: PROGRAMA DE CONTROL DEL PC. ................................................................................. 91 ANEXO 4: CONFIGURACIÓN DEL AVRSTUDIO PARA DESCARGAR EL CÓDIGO EN ARDUINO. .......... 97 Contenido de “Upload.cmd” ...................................................................................................... 97 Contenido de “UploadFromAVRStudio.cmd” ............................................................................ 98 Uso de herramientas externas en AVRStudio ............................................................................. 98 ANEXO 5: LIBRERÍA DE USO DE LA COMUNICACIÓN SERIE EN AVRSTUDIO ................................. 101 Contenido de “Arduino_SERIAL_API.h” ................................................................................ 101 Contenido de “Arduino_SERIAL.c” ......................................................................................... 102 ANEXO 6: COMUNICACIÓN SERIE CON INTERRUPCIONES DE RECEPCIÓN ...................................... 103 Contenido de “Arduino_SERIAL_INTRx_API.h” .................................................................... 103 Contenido de “Arduino_SERIAL_INTRx.c” ............................................................................ 103 Contenido de “Arduino_SERIAL_INTERRUPT_example.c” ................................................... 104 ANEXO 7: MEDIDOR DE CAUDAL IMPLEMENTADO CON AVRSTUDIO ........................................... 105 Contenido de “medidorCaudal_AVR.cpp” .............................................................................. 105 Parte del contenido modificado de “ColorLCDShield.h” ........................................................ 110 ANEXO 8: CONTENIDO DEL SOPORTE DIGITAL DEL PFC ............................................................... 111 5 1 Objetivo del PFC El objetivo principal de este proyecto fin de carrera es conocer Arduino con todo su ecosistema de herramientas y accesorios, y evaluar sus capacidades y posibilidades. Se ha definido como segundo objetivo la evaluación de las capacidades formativas/lectivas de la plataforma Arduino en el ámbito de la educación tanto secundaria como universitaria. Para alcanzar tales objetivos propongo la realización de un medidor de caudal como hilo conductor para poder conocer y evaluar tanto el hardware como el software, así como conocer los distintos canales de distribución. 2 Arduino 2.1 Introducción a Arduino La mayor parte de esta sección es una recopilación de la información disponible en la página web del proyecto Arduino y de la entrada de la Wikipedia que hace referencia a Arduino. 2.1.1 ¿Qué es Arduino? “Arduino es una plataforma de electrónica abierta para la creación de prototipos basada en software y hardware flexibles y fáciles de usar. Se creó para artistas, diseñadores, aficionados y cualquiera interesado en crear entornos u objetos interactivos.”(Página principal de la web del proyecto Arduino [1]) Arduino es una plataforma de hardware libre (Open Source Hardware, OSHW) basada en una placa con un microcontrolador y un entorno de desarrollo. El hardware consiste en una placa con un microcontrolador Atmel AVR y varios puertos de entrada/salida, tanto digitales como analógicos, así como salidas PWM y de comunicaciones, para el control de objetos físicos (LEDs, servos, botones, etc.). Los microcontroladores más usados son el ATmega328 y el ATmega168 para las placas básicas, el ATmega1280 para la de mayor capacidad y el ATmega8 para las placas más antiguas. Todos estos microcontroladores incluyen un cargador de arranque (boot loader) de manera que sea lo más simple posible empezar a trabajar con ellos. El microcontrolador en la placa Arduino se programa mediante el lenguaje de programación Arduino (basado en Wiring[2]) y el entorno de desarrollo Arduino (basado en Processing[3]). 2.1.2 ¿Qué significa que Arduino sea Open Hardware?[4] El hardware open-source (de fuente abierta o libre) comparte muchos de los principios y metodologías del software libre y de código abierto. Algunos de los principios que se pretenden promover para considerar productos físicos como open hardware son los siguientes: 6 Publicar la documentación, incluyendo los archivos de los diseños mismos, que debe permitir su modificación y distribución. Especificar que porción del diseño es abierta en caso de que no se liberen todos sus componentes. Ofrecer el software para el visionado de los archivos de diseño y de la documentación, para que se pueda escribir el código open-source fácilmente. Ofrecer una licencia que permita producir derivados y modificaciones, además de su re-distribución bajo la licencia original, así como su venta y manufactura. La licencia no debe restringir que se venda o comparta la documentación necesaria. No pide una tarifa por su venta o la de sus derivados. La licencia no debe discriminar a ningún grupo o persona La licencia no debe de restringir a ningún campo o actividad el uso de la obra. Es decir, no se puede limitar su uso únicamente para negocios o prohibir sea utilizado para investigación nuclear. El licenciamiento de la obra no puede depender de un producto en particular. La licencia no debe restringir otro hardware o software, es decir que no puede insistir en que otros componentes de hardware o software externos a los dispositivos sean también open-source. La licencia tiene que ser neutral, ninguna disposición de la misma debe de basarse en una tecnología específica, parte o componente, material o interfaz para su uso. Cumpliendo con estas premisas, los diseños y esquemas de Arduino se distribuyen bajo licencia “Creative Commons Attribution-ShareAlike 2.5”[5]. Al seguir este tipo de licencias, donde toda la información es pública, todas las placas pueden ser construidas por uno mismo o bien comprarlas ya montadas. El software puede ser descargado de forma gratuita. Los ficheros de diseño de referencia (CAD), al estar disponibles bajo una licencia abierta, pueden ser libremente adaptados a las necesidades particulares. 2.1.3 ¿Por qué Arduino? Hay distintas soluciones comerciales que facilitan el trabajo de programar un microcontrolador y poder interactuar con ellos, como podrían ser Parallax Basic Stamp[6], BX-24 de Netmedia[7], Phidgets[8] o Handyboard del MIT[9] por citar algunos. Arduino, además de simplificar este proceso intenta ofrecer otras ventajas: Asequible – Las placas Arduino son más asequibles comparadas con otras plataformas de microcontroladores. La versión más cara de un modulo de Arduino puede ser montada a mano, e incluso ya montada cuesta bastante menos de 60€ Multi-Plataforma - El software de Arduino funciona en los sistemas operativos Windows, Macintosh OSX y Linux. La mayoría de los entornos para microcontroladores están limitados a Windows. Entorno de programación simple y directo - El entorno de programación de Arduino es fácil de usar para principiantes y lo suficientemente flexible para los usuarios avanzados. Software ampliable y de código abierto - El software Arduino esta publicado bajo una licencia libre, y preparado para ser ampliado por programadores experimentados. El lenguaje puede ampliarse a través de 7 librerías de C++, y si se está interesado en profundizar en los detalles técnicos, se puede dar el salto a la programación en el lenguaje C en el que está basado. De igual modo se puede añadir directamente código en C en los programas. Hardware ampliable y de Código abierto - Arduino está basado en los microcontroladores ATMEGA168, ATMEGA328 y ATMEGA1280. Los planos de los módulos están publicados bajo licencia Creative Commons, por lo que diseñadores de circuitos con experiencia pueden hacer su propia versión del módulo, ampliándolo u optimizándolo. Incluso usuarios relativamente inexpertos pueden construir la versión para placa de desarrollo para entender cómo funciona y ahorrar algo de dinero. 2.2 Las placas Arduino 2.2.1 ¿Qué significa que una placa sea Arduino? Ilustración 1: Logo oficial de Arduino “Arduino” es el nombre del proyecto “microcontrolador Arduino oficial”, alojado en http://arduino.cc. Aunque el nombre "Arduino" no está oficialmente registrado, generalmente es respetado por la comunidad como propiedad del equipo Arduino. Al tratarse de OSHW existen multitud de proyectos y placas basadas en Arduino que pueden ser totalmente compatibles con este, o que han sufrido ligeras modificaciones, ya sea para hacerlas especificas para ciertos trabajos o bien para reducir su coste. "Freeduino"[10] es un nombre que identifica las variantes del proyecto Arduino que no fueron creadas por el equipo oficial de desarrollo Arduino. Algunos de estos freeduinos serian Boarduino, uDUINO, iDuino, ArduPilot,...[11] 2.2.2 Placas de entradas y salidas Existe una gran variedad de placas Arduino, y de todas ellas se han hecho varias revisiones. 8 Ilustración 2: En la parte superior Arduino MEGA y en la inferior Arduino UNO Las placas Arduino oficiales a día de hoy son[12]: Arduino UNO. Es una placa Arduino que se conecta al PC a través de un cable USB estándar. A través de esta conexión la alimentación y, además, permite programarla y utilizarla. Arduino UNO es la última revisión de este tipo de placas que se conectan al USB. Entre las múltiples revisiones que se han hecho encontraríamos la Duemilanove, Diecimila, NG (Nuova Generazione) o Extreme. Arduino Mega Es una placa Arduino similar a la USB, pero más grande y potente. La última revisión posee el chip ATmega2560. Tiene mayor número de pines de entradas y salidas digitales, más pines PWM, entradas analógicas, etc. Arduino Mega ADK El Arduino ADK es una placa similar al Arduino Mega, pero con una interface USB para conectar con teléfonos basados en Android. Arduino Pro Es una placa similar al Arduino UNO, pero diseñada con la intención de instalaciones semipermanentes. La placa se entrega sin los distintos conectores o “headers”, es compatible con las distintas extensiones de Arduino, y existe una versión de 3.3V para ser alimentado con baterías. Arduino Ethernet Similar al Arduino UNO, sin soporte USB, pero con un conector RJ-45 para dar soporte Ethernet. Existe la posibilidad de tomar la alimentación de la propia Ethernet. Arduino Fio Un Arduino orientado para usarlo como nodo inalámbrico. Posee conectores para un módulo Xbee (módulo inalámbrico)[13], un conector para una batería LiPo (Polímeros de litio), y un circuito para cargar la batería. Arduino LilyPad 9 Una placa Arduino circular, reducida al máximo, diseñada para ser cosida a la ropa o a otro tipo de soporte flexible. Necesita un adaptador adicional para comunicarse con el PC. Arduino BT El Arduino BT contiene un módulo bluetooth integrado para las comunicaciones móviles. Arduino Nano El Arduino Nano es un todo-en-uno, diseño compacto para usar en una placa de prototipo. Arduino Serial Las versiones serial de Arduino se vendieron principalmente como kits no ensamblados o solo PCBs para que lo montara uno mismo, ya sea a modo de aprendizaje o para reducir costes. Arduino Mini El Arduino Mini es la placa compacta de Arduino. Para reducir espacio, la comunicación con el PC se hace a través de un adaptador de USB a Arduino Mini. Arduino Pro Mini Igual que el Arduino Mini, pero sin los “headers” ni lo conectores montados, con la intención de tenerlo en instalaciones semipermanentes o permanentes. Arduino Single-Sided Serial También conocido como "Severino". Esta es la versión de la placa Arduino de una sola cara, haciéndola fácil de hacer a mano. No fue manufacturada, sino que fue publicada en la web de Arduino para que la gente se la pudiese hacer ellos mismos a modo de aprendizaje de todo el proceso de grabación de PCB. El objetivo de Arduino y sus placas es ofrecer a la gente la posibilidad de seguir la filosofía DIY (Do it yourself). 2.2.3 "Shields" y otras placas de terceros Las shields son placas que se colocan encima de la placa Arduino y que amplían una nueva función para que sea controlada desde Arduino, para controlar diferentes aparatos, adquirir datos, etc. Ilustración 3: Arduino con tres "shields" 10 A continuación se citan las shields oficiales de Arduino, pero existe multitud de shields de terceros. Arduino Ethernet Shield Esta shield permite a una placa Arduino conectarse a una red Ethernet y tener acceso a y desde Internet. Arduino Wireless Proto Shield Esta shield le da a una placa Arduino la posibilidad de comunicarse de manera inalámbrica basándose en los módulos Xbee[13], y ofrece al usuario una pequeña área para soldar componentes (prototipage). Arduino Wireless SD Shield Igual que la anterior, pero dando soporte para acceder a una tarjeta de memoria tipo SD. Arduino Motor Shield Esta shield permite a Arduino controlar motores eléctricos de corriente continua, servos y motores paso a paso, y leer encoders. Arduino Proto Shield Esta shield ofrece al usuario un área para soldar componentes. Uno de los principales distribuidores y desarrolladores de shields es Sparkfun, aunque no el único. Esta página web http://shieldlist.org/ intenta mantener una lista de todas las shields de Arduino, tanto las oficiales como las no oficiales. A parte de las shields existe un buen número de plataformas que están listas para ser controladas por una placa Arduino[14]. Entre estas plataformas, quizás una de las más vistosas seria: RobotShop Rover Es una pequeña plataforma móvil diseñada entorno a Arduino. Los usuarios pueden personalizar su móvil añadiendo funcionalidad. Ilustración 4: RobotShop Rover 11 2.2.4 Construir nuestro propio Arduino Al ser OSHW puede montarse los distintos módulos uno mismo. La placa Arduino Single-Sided Serial (cara simple y mono capa) o Severino ha sido diseñada para que sea especialmente fácil de grabar el PCB y montar los distintos componentes. Ilustración 5: Arduino Single-Sided Serial o Severino Se puede encontrar una extensa documentación, incluyendo un detallado manual, en la página web oficial [23]. 2.2.5 ¿Cómo obtener una placa Arduino? Se puede adquirir una placa Arduino desde uno de los distribuidores oficiales, o a través de múltiples tiendas online de electrónica. En la página principal de Arduino se puede encontrar una extensa lista de distribuidores en función del país o región. Después de evaluar la mayoría de ellos por variedad, disponibilidad, precio y gastos de envío, me inclino por recomendar las siguientes: Tienda BricoGeek.com [24] ELECTAN Electrónica y Robótica [25] 2.2.6 Nuestra elección: El Arduino UNO 2.2.6.1 El porqué de nuestra elección El principal objetivo de este proyecto es evaluar las capacidades de Arduino a través de la realización de un pequeño proyecto. Para lograr este objetivo se debía trabajar con un módulo real, pero no era el caso de estudio el montaje ni la depuración de los posibles errores del hardware, con lo que se optó por la compra de módulo ya montado. Como punto de partida, teniendo en cuenta que no necesitabamos gran cantidad de entradas y salidas, se decidió usar una placa Arduino que tuviera todo lo necesario para empezar a trabajar al coste más acotado posible. La actual revisión que mejor encaja con estas características es el Arduino UNO. 12 Ilustración 6: Arduino UNO, vista frontal Ilustración 7: Arduino UNO, vista trasera 2.2.6.2 Características La descripción completa del Arduino UNO se puede encontrar en su página web oficial. Este es un resumen de las principales características: Microcontrolador ATmega328 Voltaje de funcionamiento 5V Voltaje de entrada (recomendado) 7-12V Voltaje de entrada (limite) 6-20V Pines E/S digitales 14 (6 proporcionan salida PWM) Pines de entrada analógica 6 Intensidad máxima por pin 40 mA Intensidad en pin 3.3V 50 mA Memoria Flash 32 KB (ATmega328) de las cuales 0,5 KB las usa el gestor de arranque (boot loader) SRAM 2 KB (ATmega328) EEPROM 1 KB (ATmega328) Velocidad de reloj 16 Mhz El Arduino UNO puede ser alimentado vía la conexión USB o con una fuente de alimentación externa. El origen de la alimentación se selecciona automáticamente. Además, algunos de los pines tienen funciones especializadas: Serie: Pin 0 (RX) y 1 (TX). Usados para recibir (RX) y transmitir (TX) datos a través de puerto serie TTL. Estos pines están conectados a los pines correspondientes del chip de FTDI responsable de la conversión USB-toTTL. Interrupciones Externas: Pin 2 y 3. Estos pines se pueden configurar para que interrumpan la ejecución del programa al detectar un flanco o un nivel. PWM: Pin 3, 5, 6, 9, 10, y 11. Proporciona una salida PWM (Pulse-width modulation, modulación por ancho de pulsos) con temporizadores de 8 bits de resolución. 13 SPI: Pin 10 (CS/SS), 11 (MOSI), 12 (MISO), 13 (SCK). Estos pines proporcionan comunicación SPI (Serial Peripheral Interface). LED: Pin 13. Hay un LED integrado en la placa conectado al pin digital 13, cuando este pin tiene un valor HIGH(5V) el LED se enciende y cuando este tiene un valor LOW(0V) el LED se apaga. 6 entradas analógicas, cada una de ellas proporciona una resolución de 10bits (1024 valores). Por defecto se mide de tierra a 5 voltios, aunque es posible cambiar la cota superior de este rango usando el pin AREF I2C: Pin 4 (SDA) y 5 (SCL). Soporte del protocolo de comunicaciones I2C /TWI. AREF. Este pin proporciona un voltaje de referencia para las entradas analógicas. Reset. Si en este pin se suministra un valor bajo (0V) se reinicia el microcontrolador. El ATmega328 en las placas Arduino UNO viene precargado con un gestor de arranque (boot loader) que permite cargar nuevo código sin necesidad de un programador por hardware externo. La carga de un nuevo código se realiza a través del entorno de desarrollo Arduino y la conexión serie/USB. También es posible saltar el gestor de arranque y programar directamente el microcontrolador a través del puerto ICSP (In Circuit Serial Programming). En tal caso, se debe utilizar un programador externo. 2.2.6.3 Esquema y pines Los esquemas completos del Arduino UNO se pueden encontrar en su página web oficial. En la siguiente imagen se puede ver la correspondencia de pines entre el ATmega168/328 y Arduino. Ilustración 8: Pines de Arduino contra ATmega328 14 2.3 El entorno de trabajo Para empezar a trabajar debemos instalar todo el entorno necesario en el PC. Vamos a describir brevemente este proceso en un entorno Windows. Existe gran cantidad de guías acerca de cómo realizar este proceso, incluyendo una explicación en la propia página del proyecto Arduino[22]. Debo indicar que este proyecto se ha realizado con la versión 1.0 del entorno de programación Arduino. Esta es la primera versión no “Alfa” del entorno Arduino, y presenta grandes cambios en comparación de las anteriores. La mayoría de cambios son a nivel estético (distribución de iconos en las barra de herramientas, esquema de colores,…), pero hay un gran cambio a nivel funcional, los ficheros Arduino han pasado de tener extensión .pde a extensión .ino Estos últimos cambios implican que la mayor parte de la documentación que se encuentra en internet a día de hoy es incorrecta o desfasada con la revisión actual del entorno software de Arduino. 2.3.1 El entorno de desarrollo y los drivers de la placa para Windows La descarga: Descargamos la última versión del software Arduino de la página oficial [26]. El paquete de software Arduino está contenido en un único fichero comprimido. No es necesario instalarlo, simplemente lo extraemos en la carpeta deseada asegurándonos que mantenemos la estructura de subcarpetas. Los controladores (drivers): Seguiremos instalando los drivers de nuestro Arduino. Para realizar este paso necesitaremos conectar el Arduino a nuestro PC, simplemente con un cable USB tipo A-B (el comúnmente usado por las impresoras). Los drivers necesarios para utilizar la placa Arduino junto con su entorno se encuentran en la subcarpeta “\drivers” del paquete que acabamos de extraer. Una vez conectemos nuestra placa Arduino en nuestro PC, deberemos indicarle a Windows que debe buscarlos en esta carpeta. Dependiendo de la versión de Windows usada, y de la variante de la placa Arduino, este proceso puede variar e incluso no funcionar del modo esperado (especialmente Windows 7). En tal caso, nos dirigimos al “Administrador de dispositivos” y veremos algo parecido a esto: 15 Ilustración 9: Arduino en el administrador de dispositivos de Windows En este caso, nos dirigiremos al dispositivo desconocido, el Arduino Uno. Con el botón derecho actualizaremos el software del controlador indicándole la ruta donde hemos extraído el software de Arduino\drivers. Deberemos aceptar la advertencia de que este software no está firmado por Microsoft. Una vez finalizado este proceso, deberíamos ver un nuevo puerto de comunicaciones, en nuestro caso el COM3, que será nuestro Arduino, ya que este driver se comporta como un puerto serie en el PC, con lo que nos añade uno nuevo sobre el último. El IDE (“Integrated Development Environment” o "entorno de desarrollo integrado"): Ya tenemos nuestro PC y nuestra placa Arduino listos para trabajar. En la raíz de la carpeta donde hemos extraído el software Arduino deberíamos encontrar un “arduino.exe”, lo ejecutamos y nos abrirá el entorno de trabajo: el IDE. Para facilitar futuros accesos podemos crear un “acceso directo”. Una vez abierto el entorno Arduino deberíamos ver algo como esto: 16 Ilustración 10: Entorno SW de Arduino Antes que nada debemos saber que un “sketch”, o boceto, es el nombre que usa Arduino para un programa. Es la unidad de código que se sube y se ejecuta en la placa Arduino. El concepto de sketch o boceto, sería el equivalente a proyecto. Un “sketch” puede contener múltiples archivos (pestañas). Cuando un sketch es compilado, todas las pestañas serán concatenadas juntas para formar el archivo principal del sketch. Arduino puede utilizar librerías y código C/C++. Las pestañas .c o .cpp se compilaran por separado y deberán ser incluidas en el sketch usando #include. Es importante configurar correctamente la placa Arduino que vamos a utilizar, y a través de qué puerto de comunicaciones estará conectada. Para tal efecto, debemos ir al menú “Tools” escoger el submenú “Board” e indicar la placa que usaremos, en nuestro 17 caso “Arduino Uno”. En el mismo menú “Tools”, submenú ”Serial Port”, seleccionaremos el puerto correspondiente, en nuestro caso “COM3”. La barra de herramientas nos proporciona un acceso rápido a las siguientes funciones: Verify - Se utiliza para compilar y así comprobar que nuestro boceto Verificar/Compilar es correcto antes de cargarlo a la placa Arduino. Upload - Cargar el boceto actual a la placa Arduino. Debemos Cargar/Programar asegurarnos que la placa y el puerto seleccionado (en el menú Herramientas) es correcto antes de cargar el código. New Nuevo Open Abrir Save Guardar Serial monitor Monitor serie Creará un nuevo boceto para poder empezar a introducir código. Presenta una lista de bocetos almacenados en su entorno, así como una lista de bocetos de ejemplo. Estos se pueden utilizar para comprobar que los periféricos funcionan correctamente. Guarda el boceto de la ventana en el archivo de boceto. Una vez finalizado muestra un mensaje en el área de estado, debajo de la zona de edición de código. Abre una ventana con un monitor del bus serie al que está conectado nuestro Arduino. Esta es una herramienta muy útil, especialmente para depurar el código. El monitor muestra datos serie que se envían desde la placa Arduino (USB o RS232) y también puede enviar datos serie de vuelta a la placa Arduino. En la parte inferior izquierda del monitor serie se puede seleccionar la velocidad de transmisión de datos. La configuración por defecto es 9600 baud. 2.3.2 Descargar y ejecutar un ejemplo de aplicación Arduino Tenemos conectado la placa Arduino en nuestro PC. Hemos instalado los drivers y ejecutado el IDE de Arduino. Conocemos los conceptos básicos acerca de los bocetos de Arduino. Verifiquemos ahora que nuestra placa y el PC funcionan correctamente. 2.3.2.1 Editor En el IDE Arduino, seleccionaremos el menú “File”, submenú “Examples” (o bien directamente, el icono “Open ”) y dentro de “1.Basics” seleccionaremos el boceto (“sketch”) “Blink”. 18 Ilustración 11: Acceso a los ejemplos a través del menú Ilustración 12: Acceso a los ejemplos a través de la barra de herramientas Esto nos abrirá una nueva ventana con el código de este boceto, el objetivo del cual no es más que encender y apagar el LED de test, montado en la placa Arduino, cada segundo (secuencia infinita de un segundo encendido, un segundo apagando. Blink). Ilustración 13: Ejemplo de código Arduino 2.3.2.2 Compilador Como se puede observar en la “Ilustración 13: Ejemplo de código Arduino“, el código necesario para realizar tal acción es bastante simple. Ahora solo falta comprobar que realmente es correcto. Para tal cosa, simplemente pulsando sobre el icono “Verify” , el IDE de Arduino va a verificar y compilar el código escrito en la ventana activa. El entorno Arduino siempre compila el código de las pestañas de la ventana activa como un único boceto. 19 El resultado debería ser correcto y lo veremos en el “área de estado” o de “notificaciones”: Aquí nos indica el estado del proceso, el tamaño de memoria usada y la disponible en nuestra placa Arduino. Ilustración 14: Resultado de verificación correcto. Si modificamos el código del ejemplo para forzar un error en la fase de verificación/compilación, nos encontraríamos con esto: Ilustración 15: Resultado de verificación incorrecto. Hemos remplazado la llamada a la función “PinMode” por “PinModo”, y hemos eliminado el “;” del final de la llamada a la función “delay(1000)”. Al compilar, el entorno Arduino nos advierte de estos errores en la zona de estado, indicando los errores detectados, así como las líneas en que se encuentran. 2.3.2.3 Cargar y depurar Si volvemos al código original, y lo compilamos, ya solo nos falta un paso para poder probarlo en nuestra placa Arduino: Cargar el código. Con la placa Arduino conectada al PC a través del puerto USB, y teniendo configurado correctamente en el entorno nuestra placa Arduino y el puerto de comunicaciones, pulsaremos sobre el icono “Upload” . 20 Ilustración 16: Ubicación del LED de test en la placa Arduino En breves instante, la barra de estado se completará y nos indicará que el proceso ha finalizado. En ese momento, el entorno genera un “reset” a la placa Arduino, y el código empieza a ejecutarse. En nuestro caso, veremos como el LED de test montado en la placa Arduino empieza a encenderse y apagarse cada segundo. Para poder depurar el código, y así ver en qué punto de la ejecución se encuentra, Arduino nos brinda un gran herramienta: el “monitor serie”. El monitor serie sirve para que nos podamos comunicar entre el PC y nuestra placa Arduino. Las posibilidades de este pueden ser enormes, pero de momento lo usaremos de la forma más simple posible. Modificaremos el código del ejemplo de “Blink” añadiendo unas llamadas al monitor serie, de manera que durante la ejecución podremos ver en el PC qué punto del código se está ejecutando en la placa Arduino. Código “Blink” original void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, HIGH); delay(1000); digitalWrite(13, LOW); delay(1000); } Código “Blink” con depuración a través del monitor serie void setup() { pinMode(13, OUTPUT); Serial.begin(9600); } void loop() { digitalWrite(13, HIGH); Serial.println(HIGH); delay(1000); digitalWrite(13, LOW); Serial.println(LOW); delay(1000); } Compilamos y cargamos el código de nuevo, y ahora pulsamos sobre el icono del “serial monitor” . Esto nos ha abierto el monitor serie y, en él, podemos ver como escribe un “1” o un “0” al ejecutar la función correspondiente. 21 Ilustración 17: Monitor de comunicación serie integrado en el entorno Arduino 2.4 Lenguaje de programación Arduino 2.4.1 Introducción e historia La plataforma Arduino se programa mediante el uso de un lenguaje propio basado en el lenguaje de programación de alto nivel Processing[3], este a su vez está basado en Java. El compilador usado por la plataforma de Arduino es el GNU AVR, bajo Windows es el WinAVR [27]. Este compilador está dedicado para los procesadores AVR y está configurado para compilar C y C++. Las librerías del estándar C implementadas en este compilador son avr-libc[15] y están optimizadas para los procesadores AVR. Adicionalmente Arduino tiene un gran conjunto de funciones ya implementadas para realizar de un modo fácil tareas comunes como hacer una espera activa, leer un puerto digital o escribir en él, entre muchas otras. 2.4.2 Funciones básicas y operadores Todo programa Arduino debe contener, de manera obligatoria, al menos dos funciones básica: setup() y loop(). La función setup() se ejecuta cuando se inicia un boceto (el programa o sketch). Se emplea para iniciar variables, establecer el estado de las entradas y salidas, inicializar librerías, etc. Esta función se ejecutará una única vez después de que se conecte la placa Arduino a la fuente de alimentación, o cuando se pulse el botón de reinicio de la placa. Después de ejecutar la función setup(), la función loop() se ejecuta de manera consecutiva e ininterrumpida. Cuando se llega al final de esta función, se vuelve a ejecutar desde el principio hasta que se vuelva a reiniciar la placa. En el interior de esta función es donde se pone el código para controlar de forma activa la placa Arduino. Los siguientes subapartados tienen el objetivo de dar una visión rápida acerca del lenguaje de programación Arduino, pero no se pretende entrar en el detalle del lenguaje de programación. Para tal efecto hay multitud de manuales disponibles por la red, entre ellos y por citar algunos: Página de referencia del lenguaje del proyecto Arduino [28] Sección de tutoriales de TronixStuff [29] Manual del “Starter Kit” de Earthshine Electronics. [30] 22 2.4.2.1 Estructuras Al estar basado en Processing, Java y/o C, las estructuras de control son muy similares a ellos y entre ellos. Sintaxis básica / (división) ; (punto y coma. Delimitador de línea de código) % (resto) ++ (incremento en uno) {} (llaves. Delimitador de bloques de código) -- (decremento en uno) // (comentarios en una línea) += (suma y asignación) /* */ (comentarios en múltiples líneas) -= (resta y asignación) *= (multiplicación y asignación) /= (división y asignación) #define (definición de precompilador) Operadores de comparación #include (inclusión de código externo) Estructuras de control == (igual a) != (distinto de) if (comparador si-entonces) < (menor que) if...else (comparador si...si no) > (mayor que) for (bucle con contador) <= (menor o igual que) switch case (comparador múltiple) >= (mayor o igual que) while (bucle por comparación booleana) do... while (bucle por comparación booleana) break (salida de bloque de código) continue (continuación en bloque de código) Operadores Booleanos && (y) || (o) ! (negación) Operadores de acceso a punteros * operador de indirección & operador de referencia Operaciones a nivel de bits return (devuelve valor a programa) goto (salta a una etiqueta) Operadores Aritméticos & (and - 'y' a nivel de bits) | (or - 'o' a nivel de bits) ^ (xor a nivel de bits) = (asignación) ~ (not a nivel de bits) + (suma) - (resta) << (desplazamiento de bits a la izquierda) * (multiplicación) 23 >> (desplazamiento de bits a la derecha) &= (and - 'y' a nivel de bits y asignación) |= (or - 'o' a nivel de bits y asignación) 2.4.2.2 Variables Los tipos de los datos, su uso y declaración, es muy similar a otros lenguajes de programación, como el C o Java. Constantes Las constantes que vienen predefinidas en el lenguaje de Arduino se usan para facilitar la lectura de los programas. HIGH | LOW (estado de un pin de E/S digital) INPUT | OUTPUT (comportamiento de un pin de E/S digital) true | false (estado de un resultado lógico) Tipos de Datos Las variables pueden ser declaradas en cualquier punto del programa y, si no se indica lo contrario, valen cero. 1 boolean (Booleano. Puede ser cierto o falso. Ocupa 8 bits en memoria) char (Carácter. Almacena un único ASCII, tiene signo y ocupa 8 bits en memoria) unsigned char (Carácter sin signo). byte (Dato de 8 bits, sin signo) int (Entero de 16 bits, con signo) unsigned int (Entero de 16 bits, sin signo) word (Palabra. Equivalente a unsigned int) long (Entero de 32 bits con signo) unsigned long (Entero de 32 bits sin signo) float (Valor en coma flotante de 32 bits. Se debe evitar usar estos tipos ya que consumen mucho tiempo de CPU, espacio de código y pueden ocasionar problemas en comparaciones, ya que por ejemplo 6.0 dividido por 3.0 puede no ser igual a 2.01) double (En Arduino, es lo mismo que float) array (Vector de elementos) void (Vacío) String (Des de la versión 0019 existe la clase String() que permite manipular cadenas de caracteres de un modo sencillo, permitiendo concatenaciones o Se debe tener en cuanta que el uso de float se realiza a través de librerías sw, y que además la propia codificación IEEE-754 que usa el tipo float tiene limitaciones. 24 gestión automática del fin de cadena. Hasta ese momento el uso de string se limitaba a vectores de caracteres sobre los cuales se debía tener en cuenta el final de cadena /0 para poder usarlo con funciones tipo print()) Conversión Estas son una serie de funciones que permiten el cambio entre tipos. char(x) – Convierte el valor de tipo x a carácter. byte(x) – Convierte el valor de tipo x a byte. int(x) – Convierte el valor de tipo x a int. long(x) – Convierte el valor de tipo x a long. float(x) – Convierte el valor de tipo x a float. Ámbito de las variables y calificadores Las variables en el lenguaje de programación usado por Arduino, al igual que el C, tienen una propiedad llamada ámbito. Al contrario de lo que pasa en lenguajes como BASIC en los que todas las variables son globales. En Arduino solo las declaradas fuera de una función son globales. En el caso de declararse dentro de una sección de código delimitada por llaves “{…}”, como el caso de una función, la variable se libera al salir de esta sección y se vuelve a crear con el valor inicial al volver a entrar en esta sección. Esto se debe tener especialmente en cuenta al declarar variables dentro de la función loop(). static – Estática – Las variables que se declaran como estáticas sólo se crearan e inicializarán la primera vez que se ejecute el bloque de código en el que están contenidas. volatile – Volátil – Una variable debe ser declarada volatile siempre que su valor pueda ser modificado por algo más allá de la sección del código en el que aparece. En Arduino, el único lugar en el que se podría dar el caso es en secciones de código asociadas a interrupciones. const – Constante – Es un calificador de variable que modifica el comportamiento de la misma, haciendo una variable de "sólo-lectura". Sería equivalente a utilizar #define. 2.4.2.3 Funciones Aquí se muestran muchas funciones ya implementadas en el entorno Arduino para simplificar la tarea de desarrollo para problemas comunes. Muchas de ellas están limitadas a ser usadas en pines concretos y pueden necesitar de argumentos que no se detallan en este apartado. Para información adicional se puede consultar la página de referencia del lenguaje del proyecto Arduino [28]. E/S Digital pinMode(pin, modo) – configura el pin a modo de entrada o salida digitalWrite(pin, valor) - escritura digital digitalRead(pin) – devuelve una lectura digital E/S Analógica 25 analogReference(tipo) – configura el tipo de referencia analógica analogRead(pin) – devuelve una lectura analógica analogWrite(pin, valor) – “escritura analógica”. Generará una onda cuadrada con el ciclo de trabajo que se le indique como parámetro (0-255 implica un ciclo de trabajo de 0 a 100%). La frecuencia de la señal PWM será de 490 Hz por diseño de Arduino. Proporciona una manera simple de implementar un control de intensidad luminosa sobre un LED. E/S Avanzada tone(pin, frecuencia) - Genera una onda cuadrada de la frecuencia en Hz especificada. Opcionalmente se puede definir la duración del tono en ms. noTone(pin) – Deja de generar el tono en el pin especificado shiftOut(pinDatos, pinReloj, ordenBits, valor) - Desplaza un byte de datos bit a bit a través del SPI. pulseIn(pin, value) - Devuelve la anchura de un pulso en microsegundos empezando a medir cuando el pin se encuentra al nivel definido en value. Tiempo millis(). Tiempo des del arranque en ms. micros(). Tiempo des del arranque en us. delay(ms). Espera activa en ms. delayMicroseconds(us). Espera activa en us. Cálculo min(x, y) – Devuelve el mínimo de dos números. max(x, y) – Devuelve el máximo de dos números. abs(x) - Devuelve el valor absoluto constrain(x, a, b) – Devuelve x siempre que este entre a y b. En caso contrario devuelve los límites a o b. map(value, fromLow, fromHigh, toLow, toHigh) – Devuelve el valor de un “re-mapeo” de un rango hacia otro. Por ejemplo para realizar un cambio de escala de un valor de 0 a 1024 a un rango de 0 a 255. pow(base, exponente) – Devuelve el valor de un número elevado a otro número. sqrt(x) – Devuelve la raíz cuadrada Trigonometría sin(rad) – Devuelve el seno cos(rad) - Devuelve el coseno tan(rad) - Devuelve la tangente Números aleatorios 26 randomSeed(seed) – Inicializa el generador de números pseudoaleatorios. Se puede usar como semilla una entrada mínimamente aleatoria como analogRead() en un pin desconectado. random() – Devuelve un valor pseudoaleatorio. Bits y Bytes lowByte(x) – Devuelve el byte de menor peso (big-endian, el de más al derecha) de una variable highByte(x) - Devuelve el byte de mayor peso (big-endian, el de más al izquierda) de una variable bitRead(x, n) – Devuelve el valor del bit n en x. bitWrite(x, n, b) – Escribe b en el bit n de x. bitSet(x, n) – Pone a 1 el bit n de x. bitClear(x, n) – Pone a 0 el bit n de x. bit(n) – Devuelve un byte con sus bits a cero a excepción del bit n. Por ejemplo, bit(0) devolvería un 1 en decimal, bit(1) un 2, bit(2) un 4 en decimal. Interrupciones externas attachInterrupt(inter, funcion, modo) – Con el parámetro inter indicamos que ISR externa vamos a configurar. Con el parámetro modo indicamos el motivo que la va a disparar (CHANGE, LOW, RISING o FALLING) y con el parámetro función, la función que va a ejecutarse al ser disparada. detachInterrupt(inter) – Desactiva la interrupción inter Interrupciones interrupts() - Habilita las interrupciones noInterrupts() - Desactiva las interrupciones Comunicación (estas dos son clases, con lo que no se pueden invocar directamente, sino que se debe invocar el método que necesitemos) Serial Stream 2.4.3 Uso de librerías El propio entorno Arduino contiene una serie de librerías integradas que facilitan enormemente las tareas más comunes en el mundo del “hobby electrónico”. Las Librerías proveen funcionalidad extra a nuestro sketch al trabajar con hardware o al manipular datos. Para usar una librería dentro de un sketch, puedes seleccionarla desde Sketch > Import Library (Importar Librería)[16]. 2.4.3.1 Librerías Estándar EEPROM - Para leer y escribir en memorias "permanentes". 27 Ethernet - Para conectarse a una red usando el shield Ethernet. Firmata – Para comunicarse con aplicaciones en un PC que se comporta como servidor (host) usando el protocolo Firmata. LiquidCrystal - Para controlar Displays de cristal líquido (LCD) Servo - Para controlar servomotores. SoftwareSerial - Para la comunicación seria utilizando cualquier pin digital. Stepper - Para controlar motores paso a paso (Stepper motors). Wire - Interfaz de dos cables, ó Two Wire Interface (TWI/I2C), para enviar y recibir datos a través de una red de dispositivos y sensores. 2.4.3.2 Librerías de terceros Si se desean usar librerías que no vienen junto con Arduino, es necesario instalarlas. Las librerías suelen entregarse en un fichero comprimido y suelen contener una carpeta propia con dos archivos, uno con sufijo ".h" y otro con sufijo ".cpp". Para instalarla se debe copiar el contenido del fichero comprimido en la carpeta “libraries” que se debería encontrar dentro de la carpeta sketchbook de Arduino. Al reiniciar el IDE de Arduino debería aparecer la nueva librería en el menú Sketch > Import Library. Estas son algunas de las librerías que se pueden encontrar y que están referenciadas en la propia página web del proyecto Arduino [16]. También se puede encontrar una lista alternativa en la Wiki de Arduino [31]. Comunicación (networking y protocolos): Messenger - Para procesar mensajes de texto mandados des del PC. NewSoftSerial - Versión mejorada de la librería SoftwareSerial. OneWire - Controla dispositivos (de Dallas Semiconductor) que usan el protocolo One Wire. PS2Keyboard - Lee caracteres de un teclado PS2. Simple Message System - Envía mensajes entre Arduino y la computadora. SSerial2Mobile - Envía mensajes de texto o emails usando un teléfono móvil (vía comandos AT a través de SoftwareSerial) Webduino - Librería de web server extensible (para usar con Arduino Ethernet Shield) X10 - Para enviar señales X10 a través de líneas de corriente AC. XBee - Para comunicaciones entre XBees en modo API. SerialControl - Para controlar remotamente otras Arduino a través de una conexión serial. Sensores: 28 Capacitive Sensing – Implementa un sensor táctil capacitivo uniendo un pin de escritura con una resistencia relativamente alta (100kOhm – 50MOhm), una lamina de metal o cable y un condensador pequeño (20 - 400 pF) a un pin de lectura, detectando cambios en este al acercarse o tocar la lámina con el dedo. Debounce - Para una lectura filtrada de entradas digitales con rebotes (típicamente las entradas conectadas a botones). Displays y LEDs: Improved LCD library - Arregla errores de inicialización del LCD de la librería LCD oficial de Arduino. GLCD - Rutinas gráficas para LCDs basados en el chipset KS0108 o equivalentes. LedControl - Para controlar matrices de LEDs o displays de siete segmentos con MAX7221 o MAX7219. LedDisplay - Control para marquesina de LED HCMS-29xx. Matrix - Librería para manipular displays de matrices de LED básicas. Sprite - Librería básica para manipulación de sprites para usar en animaciones con matrices de LEDs. Motores y PWM: TLC5940 - Manejador para el chip TLC5940 de Texas Instruments implementando el control de hasta 16 servomotores a la vez. Medición de Tiempo: DateTime - Librería para llevar registro de fecha y hora actual en el software. Metro - Útil para cronometrar acciones en intervalos regulares. MsTimer2 - Utiliza la interrupción del temporizador 2 para disparar una acción cada N milisegundos. Utilidades de cadenas de texto: TextString, PString, Streaming – Implementan métodos de salida más completos para cadenas de texto. Adicionalmente, muchos de los desarrolladores y distribuidores de shields generan sus propias librerías para utilizar las shields, de modo que la cantidad de librerías disponibles es enrome. Además, nosotros podemos crear nuestras propias librerías y utilizarlas para nuestros proyectos e incluso distribuirlas[17]. A raíz del cambio mayor de versión (de alfa a 1.0) en el entorno de programación Arduino, muchas de las librerías de terceros han quedado obsoletas/desactualizadas por la inclusión de ficheros de encabezados (.h) que han sido renombrados en esta nueva versión, entre ellos “Wprogram.h” ha sido renombrado a “Arduino.h”, con lo que nos obligará a modificar el código de estas librerías incluyendo el nuevo fichero. 29 3 Nuestro proyecto Arduino 3.1 Medidor de caudal La medición del consumo de un recurso que tiene unidades de volumen puede ser más complicada de lo que parece. Recursos como el agua o el gas se suele medir por indicadores que determinan el volumen acumulado en el tiempo, como los contadores domésticos. Pero también puede interesar tener el caudal instantáneo. En estos casos la medición se realiza a pequeños intervalos y se van acumulando las lecturas durante un tiempo determinado. Hay varios tipos de medidores de flujo o de caudal. Para medir líquidos, un diseño muy común son los que se disponen en línea con el recurso a medir. Estos suelen tener algún tipo de molinillo que gira al pasar el recurso a través de él y genera una serie de pulsos proporcionales al caudal instantáneo. Para interpretarlo es necesario implementar un frecuencímetro. Este método es similar a como funcionan los velocímetros en muchos vehículos: un sensor de la rueda genera un pulso por cada giro de una rueda que significa que la frecuencia del pulso varía proporcionalmente a la velocidad del vehículo. En el velocímetro se muestra una interpretación de la frecuencia de los pulsos para mostrar la velocidad instantánea, mientras que el odómetro muestra un contador de pulsos acumulativo para mostrar la distancia recorrida. 3.2 Requerimientos iniciales La idea es utilizar un medidor de caudal que genere un tren de pulsos a una frecuencia proporcional al caudal. En el diseño se incluye un módulo LCD, de modo que la unidad pueda informar del caudal y del volumen tanto a través de una conexión serie a un host (PC) o directamente a través de la pantalla LCD. El medidor de caudal debe calcular y mostrar el caudal actual. También se calculará el volumen y el tiempo absoluto. Otros dos contadores acumulativos independientes mostraran el volumen que ha fluido a través del sensor. Dos pulsadores deben permitir poner a cero los contadores de forma independiente, de manera que podamos tener un contador acumulativo a largo plazo y restablecer los otros para tener medidas diferenciales a corto plazo. Un ejemplo de uso real seria para conocer el agua que se consume para rellenar un baño, programar un sistema de riego o en el funcionamiento de una lavadora. 30 Ilustración 18: Diagrama de bloques del caudalímetro 3.3 Elección de los componentes Para la realización de este pequeño proyecto, se ha partido de la placa Arduino UNO; la más básica, pero no por ello menos versátil, de las placas Arduino. La pantalla LCD usada, es una a color de 132x132 puntos. Se distribuye en formato de "shield" para ser utilizada directamente con Arduino sin necesidad de realizar más soldadura, la comunicación es vía SPI y los propios fabricantes te ofrecen las librerías de control[18]. El mismo proyecto se podría realizar usando simplemente un LCD de 16x2 como el "Arduino Shield LCD DFRobot" por unos 25€ (contra los 38€ del LCD 128x128), o simplemente un LCD 16x2 que se podría montar por unos 15€. Finalmente, para poder realizar la medición de caudal, se necesita un sensor. Los sensores de caudal no son fáciles de conseguir a un bajo coste, ya que la gran mayoría son para usos industriales de gran precisión, algo innecesario para la realización de un proyecto de demostración como este. Una alternativa de bajo coste es re-usar un caudalímetro utilizado para la refrigeración líquida de PCs. El rango de trabajo de estos suele ser de 1.0 a 15 litros por minuto, con lo que para una aplicación de monitorización del flujo de agua de un grifo domestico, debería ser suficiente ya que estos suelen tener un máximo entre 15-20 l/min. Encontramos que el caudalímetro Koolance INS-FM17N [32] cubre con los requerimientos que nos habíamos marcado, y se encontró a un precio asequible. Alternativas a este caudalímetro, podrían haber sido los usados en los lavavajillas [33] el precio es similar, pero no se encontraron datos acerca del la relación caudal/frecuencia. 31 3.4 Lista de material y presupuesto Una vez identificado el material necesario, se ha lanzado la orden de compra a los distintos proveedores: En la tienda Electan [25] se ha realizado el pedido de la placa Arduino y la pantalla: 1 x Arduino UNO con ATMega328 21.90€ 1 x Arduino Shield LCD Color Sparkfun 32.90€ Subtotal: 54.80€ Correo Península y Baleares (Entrega en aprox. 7 días. Solo pago con tarjeta o transferencia): 3.50€ IVA 18%: 9.86€ IVA Transporte 18%: 0.63€ Total: 68.79€ Y el sensor de caudal en la tienda HellFire ToyZ [34] de eBay para pagar lo mínimo en gastos de envío. 1 x Koolance INS-FM17N Coolant Flow Meter for LCS $22,99 USD Envío y manipulación $11,68 USD Total $34,67 USD Tipo de cambio: Total Euros El total del material asciende a 94.66€ 32 1 Euro = 1,34016 Dólares €25,87 EUR 3.5 El hardware 3.5.1 Características técnicas de los componentes 3.5.1.1 Shield LCD color Ilustración 19: Shield LCD a color El shield LCD a color proporciona un método fácil de conectar el LCD de un Nokia 6100 a un Arduino. La placa viene como una pantalla mini LCD de 128x128 color, así como un circuito de control para la retro-iluminación (utiliza un elevador a 7V), y tres pulsadores (vinculados a través de un puente a los pines D3-5). El LCD del Nokia 6100 se controla a través un interfaz SPI de 9-bits. Los pines de control de la pantalla LCD se conectan a los pines de hardware SPI de la placa Arduino (D13-SCK, D11 - DIO), el pin CS está ligado a D9 y el pin de reset está conectado a D8. La tensión del pin de '5V' de la placa Arduino se eleva a 7 V para alimentar la retroiluminación de la LCD. En el enlace siguiente, se puede encontrar un manual acerca de como comunicarse con el LCD y toda la información técnica necesaria para poder dibujar en la pantalla. https://docs.google.com/viewer?url=http://www.sparkfun.com/tutorial/Nokia%25206 100%2520LCD%2520Display%2520Driver.pdf [35] Este otro enlace explica un error de diseño en el shield que provoca que la retroiluminación no funcione correctamente. http://richardkaufman.org/blog/how-to-fix-it-backlight-on-sparkfuns-color-lcdshield-not-working [36] Este defecto se ha detectado en nuestro Shield. Para activar la retro-iluminación debemos realizar un cortocircuito entre dos resistencias cada vez que se alimenta por primera vez. 33 Ilustración 20: Punto a cortocircuitar para activar la retro iluminación 3.5.1.2 Sensor de caudal Ilustración 21: Sensor de caudal Koolance El sensor de caudal usado es el Koolance INS-FM17N. Este está compuesto por un interruptor magnético normalmente abierto que responde al paso de un imán situado en la aspa de rotación interna. El movimiento generado por el líquido que circula por su interior nos da una serie de pulsos, que de acuerdo a la hoja de características del fabricante[19], sigue una relación lineal entre litros por minuto (LPM) y pulsos por segundo (expresado en Hz), en función del diámetro del tubo conectado, en nuestro caso 6mm. Ilustración 22: Relación entre litros y frecuencia del medidor de caudal 34 Ya que simplemente mide a qué velocidad fluye el líquido por el interior del sensor, el hecho que el sensor no esté totalmente lleno (presencia de bolsas de aire) provocará una falta de precisión en la lectura del caudal. Para no perder pulsos conectaremos el sensor de caudal en una entrada de interrupción externa de la placa Arduino, en el pin 2. 3.5.2 El ensamblado Después de estudiar como ensamblar la “shield” y decidir a través de que pines conectaremos el caudalímetro, el montaje quedaría así. Ilustración 23: Esquema de montaje Los shields son diseñados para montarse encima de la placa Arduino, con lo que los pines se corresponden uno a uno. El caudalímetro se conecta con un cable a masa y el otro a una entrada digital con pullUp. El ATmega tiene una resistencia de pullUp interna de 20k ohmios con lo que no necesitamos circuitería extra. Cada vez que el aspa del caudalímetro pasa por el interruptor normalmente abierto provoca un cortocircuito a masa. En este momento se lee un cero en la entrada digital. Si la entrada del caudalímetro la situamos en una entrada digital con interrupción por flanco de Arduino, nos aseguramos no perder ningún pulso durante el contaje. 35 Ilustración 24: Esquema de conexionado del caudalímetro El otro cable del caudalímetro se ha conectado al pin digital D6. Este pin se configura como salida a cero dando así la masa al caudalímetro. Esta distribución se ha hecho para simplificar las conexiones y poder realizar ciertas pruebas sin el caudalímetro. Si configuramos el pin D6 como salida PWM nos permite “emular” el funcionamiento del caudalímetro. Para tal fin utilizaremos la función tone() de Arduino. En la siguiente fotografía se puede apreciar el montaje del shield LCD sobre la placa Arduino. Ilustración 25: Vista lateral del montaje final Finalmente, aquí se puede ver el montaje final, con la pantalla LCD y el sensor de caudal. 36 Ilustración 26: Vista frontal del montaje final 3.6 El software 3.6.1 Diseño de la pantalla 3.6.1.1 Uso de la librería del shield LCD a color de Sparkfun En la mayoría de shields comerciales, el propio fabricante ofrece un set mínimo de software para poder controlar el shield y/o verificar que este funciona correctamente una vez ensamblado con la placa Arduino. Este tipo de software se conoce como librerías. En nuestro caso la librería de control la proporciona Sparkfun y se pude encontrar en la página de descripción del producto [18]. Estas suelen venir empaquetadas en un fichero tipo Zip. Para poder usarla en nuestro entorno es necesario instalarla. Copiamos el contenido del Zip en una subcarpeta dentro de la carpeta “libraries” de nuestra instalación de Arduino en el PC. 37 Ilustración 27: Listado de las librerías en el entorno Arduino En nuestro caso, hemos extraído los ficheros “.cpp” y “.h” del fichero Zip de la librería en una carpeta a la que hemos llamado “ColorLCDShield” dentro de “libraries”. Una vez reiniciado el entorno Arduino, se puede ver como la nueva librería ya está disponible. Ilustración 28: Uso de una librería una vez instalada en el entorno Arduino Para usarla en nuestro sketch debemos incluirla al igual como se haría en C. #include <ColorLCDShield.h> Normalmente, dentro de las librerías hay un fichero de texto donde se nos explica cómo se puede o debe usar la librería, así como programas con ejemplos de uso. En nuestra librería hay un fichero llamado “Readme”, donde nos indica la licencia de uso de esta 38 librería y como usarla2. En un modo resumido, nos indica que debemos instar el constructor de la librería usando la declaración: LCDShield lcd; Una vez declarado el objeto de la librería, nos indica varios métodos a usar, entre ellos el método de inicialización lcd.init(EPSON); o métodos para escribir en ella lcd.clear(int color); lcd.setStr(char *pString, int x, int y, int fColor, int bColor); Como ya se ha indicado, a raíz de la última actualización del entorno Arduino, hay una gran cantidad de librerías que han dejado de funcionar, entre ellas la proporcionada para nuestra pantalla LCD. Para solucionar los problemas de compatibilidad y compilación, en el fichero ColorLCDShield.h se ha remplazado la inclusión de WProgram.h por Arduino.h; y en ColorLCDShield.cpp se ha eliminado la inclusión de wiring.h, ya que el contenido de este fichero ha sido incluido en Arduino.h y a su vez, este ya lo habíamos incluido en ColorLCDShield.h. Se ha detectado que la librería que nos proporciona el fabricante necesita mucha memoria RAM debido al el tipo de fuente que utiliza. La fuente original de la librería es de 8x16 puntos de tamaño y se ha remplazado por otra de 8x8 para reducir el tamaño de RAM requerido. Existe la posibilidad de indicar al entorno Arduino que una variable tipo constante la aloje directamente en la FLASH. Para ello se debe usar el modificador de tipo PROGMEM de la librería pgmspace.h. Esta podría haber sido otra modificación a hacer sobre la librería para reducir el uso de RAM[38]. 3.6.1.2 Información a mostrar e interfaz Se han definido tres pantallas distintas para mostrar la información relativa a las mediciones absolutas y a las parciales. Para conmutar de una pantalla a otra se va a utilizar el pulsador superior de la pantalla. Los dos pulsadores inferiores servirán para borrar los dos contadores parciales. Este es el aspecto deseado de las distintas pantallas: 2 Véase ANEXO 1 39 Ilustración 29: Pantalla principal Ilustración 30: Pantalla contadores parciales 1 Ilustración 31: Pantalla contadores parciales 2 3.6.2 El programa de control de Arduino Este sería el diagrama de flujo del programa de control que hemos implementado. 40 Ilustración 32: Diagrama de flujo del firmware La tarea periódica se lanza cada 1 segundo. Aquí se pone a cero el contador de pulsos detectados y lleva la cuenta en cada contador. También es la responsable de refrescar los valores que se muestran en la pantalla LCD y los valores instantáneos que se mandan por el puerto serie. De manera asíncrona, y con mayor prioridad, el código asociado a la interrupción de flanco se encarga de contar los pulsos generados por el caudalímetro. 41 Loop() Tarea_1sec() Interrupción() Ilustración 33: Diagrama temporal del firmware La variable que se utiliza dentro de la función de la interrupción debe ser declarada como volatile para indicar al compilador que puede ser modificada des de diferentes contextos y así evitar optimizaciones. Esto obliga al compilador a acceder siempre a la dirección de memoria donde se encuentra la variable y no copiarla en registros para mejorar la velocidad de acceso. Los accesos a esta variable desde el contexto de menos prioridad (la tarea periódica) se deben realiza a través de una sección crítica. Esto implica que los accesos a esta variable se harán con las interrupciones deshabilitadas. volatile unsigned int contadorPulsos = 0; tarea_periodica(){ /* Inicio zona segura intercambio de datos / noInterrupts(); unsigned int contadorPulsosLocal = contadorPulsos; contadorPulsos = 0; interrupts(); /* Fin zona segura intercambio de datos */ } rutina_servicio_interrupcio(){ contadorPulsos++; } Esto debe hacerse porque al tratarse de una variable de 16 bits, y el microcontrolador de Arduino de 8 bits, implica que como mínimo necesitaremos dos instrucciones para acceder a este dato. Si mientras se esta ejecutando la primera instrucción de acceso a la variable recibimos una nueva interrupción, el valor de esta variable se modificará por la interrupción. Al volver a la tarea periódica se ejecutará la segunda instrucción de acceso a la variable pero el valor ya será distinto. Por ejemplo: La variable es igual a 0x00FF. Mientras leemos la parte baja (0xFF) se ejecuta la interrupción y la incrementa en 1 (ahora valdría 0x0100), al volver a la tarea periódica y leer la parte alta del dato, veríamos un 0x01. Entonces nuestra variable valdría 0x01FF y no 0x0100 que seria el valor correcto. El código fuente del programa Arduino se puede encontrar en el ANEXO 2. 42 3.6.3 El programa de control del PC El programa de control de Arduino nos manda cada segundo, a través del puerto serie, el caudal instantáneo. En el PC hemos realizado un programa capaz de interpretar estos datos y dibujar en la pantalla una gráfica en tiempo real de las mediciones realizadas por nuestro Arduino. La pantalla de nuestro programa de PC es la siguiente: Ilustración 34: Salida del programa de visualización en el PC Esta aplicación ha sido realizada en python. Este es un lenguaje interpretado de muy alto nivel, lo que nos permite establecer una comunicación con nuestra placa Arduino con muy pocas líneas de código. Lo primero es importar la librería: import serial Luego procedemos a conectarnos al puerto de serie con una sencilla función y encapsulamos dicha conexión en un objeto: ser = serial.Serial(port='\\.\COM3', baudrate=9600) Ahora es el momento de leer lo que Arduino nos dice: while 1: ser.readline() La parte de crear una ventana y dibujar la gráfica se realiza a través de una librería python con lo que la totalidad del programa se puede reducir a unas 300 líneas de código.3 3 Véase ANEXO 3 43 3.7 Juego de pruebas Para verificar que nuestro sistema funciona como esperamos se han realizado una serie de pruebas a distintos niveles. 3.7.1 Test sobre el caudalímetro Para verificar que la configuración de los pines es la correcta, se ha monitorizado la entrada del caudalímetro (D2) con un osciloscopio mientras hacíamos girar las aspas. El resultado debería ser un tren de pulsos de una frecuencia entre 0 Hz y poco más de 30 Hz, ya que es el rango de trabajo del caudalímetro. El tiempo a nivel bajo de este tren de pulsos, debería ser del 15-20% del pulso. Esto se debe a que el caudalímetro tiene 4 aspas y solo una de ellas activa el interruptor magnético. A continuación se pueden ver distintas capturas de osciloscopio verificando que el montaje funciona. Ilustración 35: Tren de pulsos del caudalímetro girando a baja velocidad 44 Ilustración 36: Tren de pulsos del caudalímetro girando a media velocidad Ilustración 37: Tren de pulsos del caudalímetro girando a alta velocidad 3.7.2 Test sobre la captura de pulsos El objetivo de esta prueba es asegurar que capturamos los pulsos que esperamos. 45 El hecho de dar la masa del caudalímetro a través de una salida digital nos permite testear la captura de pulsos si configuramos este pin como salida PWM. Para genera un PWM de 31Hz en el pin D6 añadimos la siguiente línea al principio de la función loop(): tone(caudalGNDPin,31); Si quitamos el caudalímetro y cortocircuitamos los dos pines, el programa de control debería detectar 31 pulsos cada segundo. Lo verificamos viendo que por la comunicación serie está mandando continuamente un 31. Ilustración 38: Respuesta ante un tren de pulsos de 31Hz En la pantalla del LCD debe reportar que el consumo actual es de 9.52 litros por minuto (31Hz x 0.302 = 9.517 LPM), acorde con la hoja de características del caudalímetro. Ilustración 39: Muestra de la pantalla principal ante una entrada de 31Hz 46 3.7.3 Test de funcionamiento general Con el código final y el caudalímetro conectado, verificamos que al pulsar el 1er botón cambiamos de pantalla a mostrar. Al pulsar el segundo botón, el contador de litros y tiempo parcial 1 se pone a cero. Al pulsar el tercer botón, el contador de litros y tiempo parcial 2 se pone a cero. Ilustración 40: Secuencia de acciones en funcionamiento normal Con la ayuda de un cronómetro verificamos que el contador de segundos funciona con suficiente precisión. Esperaremos unos 10 minutos para poder detectar mejor las desviaciones. 47 Ilustración 41: Prueba de precisión del contador de segundos En la pantalla principal soplamos sobre el caudalímetro y vemos que el contador de consumo actual incrementa y que los litros totales también crecen. 4 Uso de Arduino en entornos lectivos 4.1 Educación secundaria Arduino fue diseñado para que personas sin un conocimiento profundo de electrónica pudieran realizar pequeños proyectos donde se necesita un microcontrolador. Encontramos en internet varios ejemplos de proyectos hechos con Arduino, desde un simple caudalímetro a un cuadricoptero[20], robots seguidores, controlar un Arduino con un móvil Android[21],… y cualquier cosa que encaje en la filosofía DYI (Do It Yourself). En apartados anteriores hemos visto como sin tener demasiados conocimientos acerca de microcontroladores y con una sintaxis básica hemos podido realizar un proyecto relativamente complejo. Esto ofrece un gran potencial en entornos lectivos, especialmente en secundaria, ya que es posible despertar la curiosidad por la tecnología, instruir conceptos de electrónica básica de un modo práctico y hacer una buena introducción a la algoritmia de un modo ameno y visual. 4.2 Educación técnica superior Arduino no deja de ser una placa con un microcontrolador y todo lo necesario para interactuar con él. Esto implica que no solo se puede utilizar como pequeña plataforma para proyectos, sino que en el aprendizaje de microcontroladores puede tener un gran potencial, ya que en la red existen varias herramientas para el microcontrolador Atmel AVR. Hasta este punto apenas se ha mencionado el Atmel AVR ya que las placas Arduino, y todo su entorno software, ha sido desarrollado para no tener que entrar en este tipo de 48 detalles. Pero estos detalles son precisamente lo que nos va a interesar a partir de este punto. Para ello empezaremos configurando un entorno de desarrollo que nos permita trabajar de un modo más directo con el microcontrolador. 4.2.1 Usar AVRStudio, código C y Arduino En primer lugar vamos a instalar AVRStudio, que es el IDE y el conjunto de herramientas oficial de Atmel (el fabricante de los microcontroladores utilizados en la Arduinos). Podemos obtenerlo des de la propia página web de Atmel [37]. La versión sobre la que se ha trabajado se ha adjuntado en el soporte digital del PFC. Nosotros instalamos el paquete “AVR Studio 5.1 Installer - Full”. Para realizar la descarga es necesario registrarse en la web de Atmel, pero el proceso es simple y rápido. Una vez instalado todo lo necesario, el proceso es el típico de Windows, perfectamente guiado a través de ventanas. Ejecutamos el AVRStudio y deberíamos ver algo como en la Ilustración 42 Ilustración 42: Vista inicial del AVR Studio Aquí seleccionamos “New project”, en la nueva ventana seleccionamos de los “Installed templates” el “AVR GCC → C → C Executable Project”, indicándole en el campo nombre “Arduino_Blink”, como el típico ejemplo Arduino, y “OK”. 49 Una vez hecho esto, tenemos que seleccionar con que dispositivo Atmel vamos a trabajar. Nuestro Arduino tiene el ATmega328P, con lo que seleccionaremos este. Ilustración 43: Selección de dispositivo en AVR Studio Como se puede ver, en la parte derecha de la pantalla de selección nos proporciona el acceso a la hoja de características (datasheet) y las herramientas que tendremos disponibles para este dispositivo. Entre ellas la que quizás más nos puede interesar para nuestros desarrollos: el “AVR Simulator”. Al darle “OK”, el entorno nos va a crear un proyecto listo para introducir el código C que queramos. 50 Ilustración 44: Vista de codificación del AVR Studio El ejemplo que queremos implementar es uno de los más típicos en las placas Arduino. Aprovechando que la propia placa tiene el pin D13 conectado a un LED, vamos a hacer que este LED este encendiéndose y apagándose para siempre, 1 segundo encendido y 1 segundo apagado. Este sería el código a implementar: /* * Arduino_blink.c */ #include <avr/io.h> //Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA #define F_CPU 16000000UL //F_CPU indica al compilador que nuestro cristal es de 16Mhz. Debe ser declarado antes que la libreria delay.h #include <util/delay.h> //Contiene funciones de espera en ms y us int main(void) { DDRB |= (1<<PB5); //Definimos el pin13/PORTB5 como salida para poder encender y apagar nuestro LED while(1) //Este es el bucle infinito obligatorio en la programación de micro-controladores. Todo nuestro código estará aquí dentro { PORTB |= (1<<PB5); //Enciende el LED, este es el LED incluido en la placa Arduino, en el pin digital 13 51 _delay_ms(1000); //Espera 1 segundo PORTB &= ~(1<<PB5); //Apaga el LED _delay_ms(1000); //Espera 1 segundo } } Como podíamos ver en la Ilustración 8 (Pines de Arduino contra ATmega328) debemos tener en cuenta la correspondencia entre los pines del ATMEGA con los de la placa Arduino. Pulsamos la tecla F7, o en menú “Build → Build Solution”, para iniciar el proceso de generación de los ficheros ejecutables (compilado, ensamblado y enlazado). Si todo ha ido bien, deberíamos ver en la parte inferior del IDE un mensaje indicando que no hay errores y un resumen de memoria usada; y en la parte derecha los ficheros que se han generado durante el proceso: Ilustración 45: Resultado de la compilación en el AVR Studio 4.2.2 Simular y ejecutar el código C en nuestro microcontrolador En este punto deberíamos ser capaces de compilar, enlazar y generar un fichero .hex. Este fichero será el ejecutable en el microcontrolador, el equivalente a un .exe en un PC. Ahora querremos ejecutar nuestro código, y verificar que se comporta como nosotros esperamos. Para tal fin tenemos distintas opciones, entre ellas simular el código en el PC, descargarlo en nuestro Arduino o utilizar herramientas de depuración más avanzadas. 52 4.2.2.1 Simular el código en el PC. AVRSimulator. El AVRSimulator es una herramienta gráfica integrada en el propio AVRStudio que nos permite ejecutar nuestro código viendo e interactuando con todos los registros internos del microcontrolador, así como sus pines de entrada y salida. Al mismo tiempo nos sirve como depurador, ya que nos permite poner “breakpoints” en el código y ver el valor de las variables, así como modificarlas y forzar otras ramas de ejecución, pudiendo probar mejor nuestro código. Ilustración 46: Vista del simulador integrado en AVR Studio 4.2.2.2 Descargar el código en nuestra placa Arduino. AVRDude. Nuestro ATmega por el hecho de estar en una placa Arduino lleva grabado un gestor de descarga o boot loader. El AVRStudio no soporta la comunicación con este gestor de descarga de manera nativa, ya que no ha sido diseñado para entornos Arduino. El AVRDude es una herramienta que nos permite descargar nuestro código una vez compilado a nuestra placa Arduino a través de su propio gestor de descarga. Esto significa que no necesitamos ningún tipo de hardware adicional para poder trabajar con el ATmega, ni con la placa Arduino. El AVRDude forma parte del software de Arduino, para utilizarlo de una manera cómoda des de nuestro entorno integrado, se han copiado los ficheros necesarios a una nueva carpeta llamada “ArduinoUpload” en la misma carpeta que los proyectos del AVRStudio. Esta suele ser una carpeta llamada “AVRStudio 5.1”, en la carpeta de 53 documentos del usuario que ha instalado el AVRStudio. Dentro de esta carpeta “AVRStudio 5.1”. Los archivos necesarios para poder usar el AVRDude se encuentran en la ruta de instalación del entrono Arduino bajo las siguientes rutas relativas: arduino-1.0\hardware\tools\avr\bin\avrdude.exe arduino-1.0\hardware\tools\avr\bin\libusb0.dll arduino-1.0\hardware\tools\avr\etc\avrdude.conf El siguiente fichero, aun no siendo obligatorio, contiene la información necesaria para poder usar el AVRDude con la configuración apropiada. arduino-1.0\hardware\arduino\boards.txt arduino-1.0\hardware\tools\avr\doc\avrdude\avrdude.pdf La línea de comandos a ejecutar para descargar nuestro código sería la siguiente: avrdude -F -V -c arduino -p ATMEGA328P -P COM3 -b 115200 -D -U flash:w:Arduino_Blink.hex El significado de esos parámetros esta descrito en la documentación del propio AVRDude que hemos copiado en nuestra carpeta, el fichero “avrdude.pdf”. Para facilitar esta tarea se ha creado un fichero por lotes de MS-DOS llamado “upload.cmd” como el que se puede encontrar en el ANEXO 4, donde solo le deberemos indicar el puerto serie a utilizar y el nombre del binario a descargar… A su vez, crearemos otro fichero por lotes de MS-DOS que será llamado desde el propio AVRStudio, como una herramienta externa, que será incluida en la barra de botones, de manera que el proceso de descargar el código en la placa será similar al utilizado en el propio entorno Arduino. Este otro fichero por lotes, llamado “uploadFromAVRStudio.cmd”, también se puede encontrar en el ANEXO 4, así como la manera de configurar la herramienta externa en el AVRStudio. 4.2.2.3 Uso de entornos de depuración y programación Existen distintas herramientas de Atmel para programar sus microcontroladores e incluso poder depurar el código realizando ejecuciones paso a paso en el propio microcontrolador. Muchas de estas herramientas son simples programadores, fáciles de montar uno mismo y de bajo coste, que nos permitirían poder descargar el boot loader o directamente nuestra aplicación en un microcontrolador virgen. Alguno de estos seria el USBtinyISP, USBasp entre muchas otras propuestas disponibles en internet. En cuanto a los depuradores (debuggers) que permiten programar y ejecutar código paso a paso en el dispositivo real y utilizar múltiples “breakpoints” son bastante costosos siendo quizás el más económico el AVR Dragon (50€ aprox.). 54 4.2.3 El ATmega328P La intención de este bloque es mostrar cómo se pueden usar los distintos periféricos del microcontrolador de nuestro Arduino y realizar en C el mismo proyecto realizado en lenguaje Arduino. No se entrará en detalle en todos los periféricos, ya que la hoja de características (datasheet) del microcontrolador contiene todos los detalles. La hoja de características del ATmega328P se puede abrir fácilmente des del propio IDE del AVRStudio o en la página web del fabricante [37]. 4.2.3.1 Un breve resumen El ATmega328P es el microcontrolador que lleva el Arduino UNO, forma parte de la familia de procesadores Atmel AVR. Los AVR son una familia de microcontroladores RISC, la arquitectura de los cuales fue concebida por dos estudiantes en el Norwegian Institute of Technology, y posteriormente refinada y desarrollada en Atmel Norway, la empresa subsidiaria de Atmel, fundada por los dos arquitectos del chip. Los AVR son CPUs con arquitectura Harvard. Tiene 32 registros de propósito general de 8 bits. Estos registros de propósito general, los registros de periféricos y la memoria de datos forman un solo espacio de direcciones unificado, que se acceden mediante operaciones de carga y de almacenamiento. A diferencia de los microcontroladores PIC, la pila se ubica en este espacio de memoria unificado, y no está limitado a un tamaño fijo. Los microcontroladores AVR tienen una cañería o “pipeline” con dos etapas (traer y ejecutar), que les permite utilizar un ciclo de reloj en la mayoría de instrucciones, lo que los hace relativamente rápidos entre los microcontroladores de 8 bits. El conjunto de instrucciones es más regular que la de la mayoría de los microcontroladores de 8 bits. Sin embargo, no es completamente ortogonal. 4.2.3.2 Puertos de entrada y salida digital Hay distintos registros del microcontrolador que sirven para indicar como debe comportarse cada pin, ya sea como un pin de entrada digital, de salida digital, de entrada analógica, salida PWM… En el ATmega328P los registros para configurar y acceder a los pines están agrupados por puertos. En nuestro Arduino tenemos un ATmega de 28 pines, esto hace que solo dispongamos el puerto B, C y D. En la Ilustración 8: Pines de Arduino contra ATmega328 (Pines de Arduino contra ATmega328), se puede ver la correspondencia entre los pines del ATmega y del Arduino, así como los distintos modos en los que puede funcionar cada uno de los pines. Para cada uno de los puertos tenemos un registro DDRx asociado, donde la 'x' indica el puerto al que hacemos referencia, por ejemplo DDRB hace referencia al puerto B. Este registro nos permite configurar los distintos pines de puerto como entrada o salida digital. El acrónimo DDR significa Data Direction Register, y cada bit dentro del registro hace referencia al pin correspondiente. Escribir un 1 implica configurar el pin como salida y 0 como entrada. 55 Por ejemplo, para configurar como salida el pin digital 5 de nuestro Arduino vemos que corresponde con el pin 5 del puerto B, haremos lo siguiente: DDRB = 0b00100000; Con esta instrucción también se ha configurado como entrada los demás pins del puerto B. Más adelante se comenta como evitar modificar todo el puerto para configurar un único pin. Si ahora queremos asignar un valor de salida a este puerto debemos utilizar el registro PORTx, donde 1 implica un nivel alto y 0 un nivel bajo. PORTB = 0b00100000; En este ejemplo tenemos configurados todos los pines del puerto B como entradas menos el pin 5 que lo tenemos como salida a nivel alto. Para conocer el nivel de los pines leeremos los registros PINx. Estos registros se pueden consultar independientemente de la configuración del pin, ya sea como entrada o como salida. Esto se puede utilizar para saber el estado real en que se encuentra un pin. char contenidoPuertoB = 0u; contenidoPuertoB = PIND; El ATmega tiene funcionalidad real de lectura-modificación-escritura para todos los registros de entrada/salida (concretamente los registros del 0x00 al 0x1F). Esto significa que tiene una instrucción que permite cambiar un bit de un registro sin modificar los demás. Estas instrucciones en ensamblador son SBI y CBI. La manera en C más estándar de actuar sobre un pin en concreto es utilizando mascaras y desplazamientos de bits. Para tal efecto el compilador AVR lib-c tiene una serie de constantes definidas para tal fin, como Px0..7. Utilizando estas constantes y la lógica de bits, tenemos que para poner a nivel alto el pin 5 y no modificar los demás pines podemos hacer: PORTB |= (1<<PB5); y si quisiéramos ponerlo a nivel bajo: PORTB &= ~(1<<PB5); Del mismo modo, si quisiéramos actuar en función de una entrada haríamos: if ((PINB & (1<<PB2)) != 0){ /* Pin a nivel alto */ }else{ /* Pin a nivel bajo */ } Utilizando el AVRStudio y ejecutando estas instrucciones en el AVRSimulator, utilizando la vista de “Disassembly” fácilmente podemos ver el código en ensamblador que ha generado el compilador. 00000040 00000041 DDRB |= (1<<PB5); SBI 0x04,5 PORTB |= (1<<PB5); SBI 0x05,5 Set bit in I/O register Set bit in I/O register y podemos ver como se comportaría el código en el microcontrolador real. 56 Ilustración 47: Vista de ensamblador en AVRSimulator Si asignamos un valor a un pin en PORTx y luego lo leemos desde PINx, se debe esperar un ciclo para que PINx se sincronice, por ejemplo, unsigned char val; PORTD = (1 << PD3 | 1 << PD5); /*Esperamos un ciclo para sincronización */ asm volatile("nop"); /*Leemos los pines */ val = PINB; Esto es debido al diseño de la circuitería de un pin de entrada y salida, donde en el registro de entrada hay un “latch” que se comporta como sincronizador para evitar metaestabilidad si hay cambios en el nivel del pin cerca del flanco del reloj interno. 57 Ilustración 48: Puerto general de entrada/salida digital 4.2.3.3 Uso de la USART/comunicación serie El entorno Arduino utiliza básicamente el puerto serie para depurar el código e interaccionar con el PC. En este apartado se mostrará cómo realizar las operaciones equivalentes a través del periférico del ATmega y código C. El protocolo de comunicación serie utiliza 2 cables, uno para recibir datos y otro para enviar. La masa debe ser común entre la placa Arduino/micro-controlador y el otro dispositivo. La interfaz serie permite enviar entre 5 y 9 bits de datos con un bit de inicio y uno o dos de parada, así como un bit de paridad. La USART hace todo el trabajo acerca de la configuración de inicio, del bit de parada y de la paridad. La comunicación es asíncrona y sólo es necesario indicar a la USART la velocidad de reloj que queremos usar, la velocidad de transmisión. La propia documentación del microcontrolador (datasheet) nos indica una serie de funciones en ensamblador y en código C a modo de ejemplo para utilizar la USART. A partir de estos ejemplos, aquí podemos ver un ejemplo de código para mandar y recibir a través del puerto serie. /* * Arduino_USART.c */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #define F_CPU (16000000UL) /* F_CPU indica al compilador que nuestro cristal es de 16Mhz. Debe ser declarado antes que la libreria delay.h */ 58 #include <util/delay.h> */ /* Contiene funciones de espera en ms y us #define BAUDRATE (9600u) /* Definimos a que velocidad queremos utilizar la comunicación serie */ #define BAUD_RATE_REGISTER ((F_CPU / (BAUDRATE * 16ul)) - 1u) /* Datasheet. Tabla 20-1. Equations for Calculating Baud Rate Register Setting */ char String[]="Hola mundo!!"; /* Función de inicialización. Datasheet “20.5 USART Initialization” */ void USART_Init( unsigned int ubrr) { /* Configuramos la velocidad de la comunicación (baud rate) */ UBRR0H = (unsigned char)(ubrr>>8); UBRR0L = (unsigned char)ubrr; /* Valores por defecto después de reset en el registre USCR0A. Esta escritura no esta definida en el datasheet, pero parece necesaria ya que de lo contrario el bit de "Double USART Transmission Speed" parece activo, de modo que no configura el baud rate como esperamos. Posiblemente el bootloader de Arduino está configurando este bit, ya que usa el periférico para descargar el código. */ UCSR0A = (1<<TXC0); /* Activar la recepción y la transmisión */ UCSR0B = (1<<RXEN0)|(1<<TXEN0); /* Configuramos el formato de la trama: 8data, 2stop bit */ UCSR0C = (1<<USBS0)|(3<<UCSZ00); } /* Función de transmisión. Datasheet “20.6 Data Transmission – The USART Transmitter” */ void USART_Transmit( unsigned char data ) { /* Esperamos a vaciar el buffer de transmisión */ while ( !( UCSR0A & (1<<UDRE0) ) ); /* Ponemos el dato en el buffer y mandamos el dato */ UDR0 = data; } /* Función de recepción. Datasheet 20.7 Data Reception – The USART Receiver” */ unsigned char USART_Receive( void ) { /* Esperamos a recibir todo el dato */ while ( !(UCSR0A & (1<<RXC0) ) ); /* Devolvemos el dato del buffer */ return UDR0; } /* Esta función transmite una cadena de caracteres */ void USART_putstring(char* StringPtr){ while(*StringPtr != 0x00){ /* Mientras la cadena no esta vacia transmite */ USART_Transmit(*StringPtr); StringPtr++; } } int main( void ) { USART_Init(BAUD_RATE_REGISTER); /* Inicializamos la comunicación */ while(1){ USART_putstring(String); /* Mandamos “Hola mundo!!!” */ _delay_ms(5000); /* Esperamos 5 seg y volvemos a re-enviar el mensaje cada 5 seg */ } return 0; 59 } Estas funciones son muy básicas y no utilizan las interrupciones que el periférico ofrece para indicar que los buffers de recepción y/o transmisión están vacíos/llenos, o si ha detectado errores,… Este código sirve a modo de ejemplo para implementar una funcionalidad similar a la que ofrece el módulo Arduino. En el ANEXO 5, se puede ver como estas funciones han sido exportadas a una librería, de modo que en futuros proyectos simplemente hay que incluirla indicando la velocidad de transmisión deseada y llamar a las funciones de transmisión/recepción, de manera que facilita enormemente el desarrollo y hace el código más legible: /* * Arduino_SERIAL_example.c */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #define F_CPU (16000000UL) /* F_CPU indica al compilador que nuestro cristal es de 16Mhz. Debe ser declarado antes que la libreria delay.h */ #include <util/delay.h> /* Contiene funciones de espera en ms y us */ #include "Arduino_SERIAL_Api.h" /* Contiene funciones para trbajar con la comunicación serie */ char String[]="Hola mundo!!"; int main( void ) { SERIAL_Init(); /* Inicializamos la comunicación */ while(1){ SERIAL_PutString(String); /* Mandamos “Hola mundo!!!” */ _delay_ms(5000); /* Esperamos 5 seg y volvemos a re-enviar el mensaje cada 5 seg */ } return 0; } La configuración de la velocidad de la comunicación serie se encuentra definida en el fichero Arduino_SERIAL_API.h. Se ha implementado de este modo para que el cálculo del valor del registro lo haga el pre-compilador, de manera que no se añada más código innecesario en la aplicación final. Para poder comunicar el PC y nuestra placa con la comunicación serie, necesitaremos un programa en el PC que haga de monitor. Hay infinidad, pero por simplicidad, y por la no necesidad de instalación, yo recomendaría “Terminal”, que se puede encontrar en https://sites.google.com/site/terminalbpp/ y se ha añadido en el soporte digital del PFC. De igual modo como hemos creado una herramienta externa en AVRStudio para el AVRDude, ahora podemos hacer lo mismo para el monitor serie, copiando así las facilidades del entrono de desarrollo de Arduino. 4.2.3.4 Las interrupciones en los ATmega328P Una interrupción es una llamada “inesperada”, urgente e inmediata a una función especial denominada Interrupt Service Routine (ISR). 60 El mecanismo funciona así: sin importar lo que esté haciendo nuestro código, cuando ocurra la interrupción la CPU hará una pausa y pasará a ejecutar el código de la ISR. Tras terminarlo, la CPU regresará a la tarea que estaba realizando antes de la interrupción, justo donde la había suspendido. Las interrupciones son disparadas (llamadas) por eventos del hardware del microcontrolador. El evento puede ser algún cambio en cierto pin de E/S, el desbordamiento de un temporizador, la llegada de un dato serie, etc. Se puede deducir por tanto que las fuentes de interrupción están relacionadas con la cantidad de recursos del microcontrolador. 4.2.3.4.1 Los Vectores de Interrupción Cada interrupción está identificada por un Vector de Interrupción, que no es otra cosa que una dirección particular en la memoria FLASH. Todos los Vectores están ubicados en posiciones consecutivas de la memoria FLASH y forman la denominada Tabla de Vectores de Interrupción. El RESET no es una interrupción pero su dirección 0x0000 también se conoce como Vector de Reset. Por defecto, la Tabla de Vectores de Interrupción está ubicada en las primeras posiciones de la memoria, tal como se ve abajo. Solo cuando se habilita el uso de la Sección de Boot Loader toda la tabla se desplazará al inicio de dicha sección. Aquí se presenta la tabla con las 26 interrupciones posibles en los ATmega328: Num Vector Dirección de Programa Nombre de Vector de Interrupción Fuente de interrupción 1 0x0000 RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset 2 0x0002 INT0 External Interrupt Request 0 3 0x0004 INT1 External Interrupt Request 1 4 0x0006 PCINT0 Pin Change Interrupt Request 0 5 0x0008 PCINT1 Pin Change Interrupt Request 1 6 0x000A PCINT2 Pin Change Interrupt Request 2 7 0x000C WDT Watchdog Time-out Interrupt 8 0x000E TIMER2_COMPA Timer/Counter2 Compare Match A 9 0x0010 TIMER2_COMPB Timer/Counter2 Compare Match B 10 0x0012 TIMER2_OVF Timer/Counter2 Overflow 11 0x0014 TIMER1_CAPT Timer/Counter1 Capture Event 12 0x0016 TIMER1_COMPA Timer/Counter1 Compare Match A 13 0x0018 TIMER1_COMPB Timer/Coutner1 Compare Match B 14 0x001A TIMER1_OVF Timer/Counter1 Overflow 15 0x001C TIMER0_COMPA Timer/Counter0 Compare Match A 16 0x001E TIMER0_COMPB Timer/Counter0 Compare Match B 17 0x0020 TIMER0_OVF Timer/Counter0 Overflow 18 0x0022 SPI_STC SPI Serial Transfer Complete 19 0x0024 USART_RX USART Rx Complete 20 0x0026 USART_UDRE USART, Data Register Empty 21 0x0028 USART_TX USART, Tx Complete 61 22 0x002A ADC ADC Conversion Complete 23 0x002C EE_READY EEPROM Ready 24 0x002E ANALOG_COMP Analog Comparator 25 0x0030 TWI 2-wire Serial Interface 26 0x0032 SPM_READY Store Program Memory Ready Para entender cómo funciona el mecanismo de las interrupciones debemos saber que el Contador de Programa es un registro que dirige cada una de las instrucciones que ejecuta la CPU. Al dispararse una interrupción el hardware guardará en la pila el valor actual del Contador de Programa y lo actualizará con el valor del Vector de Interrupción respectivo. De este modo la CPU pasará a ejecutar el código que se encuentre a partir de esa dirección. Al final del código de la interrupción debe haber una instrucción de retorno que restaure el Contador de Programa con el valor que se había guardado en la pila. La instrucción es RETI. Esta instrucción la pone el compilador si le indicamos que la función ejecutada es una interrupción. Si se llegara a producir el evento excepcional en que se disparen dos o más interrupciones al mismo tiempo, se ejecutarán las interrupciones en orden de prioridad. Tiene mayor prioridad la interrupción cuyo Vector se ubique más abajo, es decir, entre todas, la interrupción INT0 tiene siempre las de ganar. 4.2.3.4.2 Las Funciones de Interrupción La Función de Interrupción o ISR va siempre identificada por su Vector de Interrupción. Su esquema varía ligeramente entre un compilador y otro, puesto que no existe en el lenguaje C un formato estándar. Lo único seguro es que es una función que no puede recibir ni devolver ningún parámetro. En el compilador AVR GCC (WinAVR) la función de interrupción se escribe utilizando la palabra reservada ISR. En el caso de nuestro compilador, el Vector de Interrupción debe tener la terminación _vect. En caso de dudas acerca de cómo está declarada se puede buscar en la carpeta include del directorio de instalación de WinAVR. ISR (Vector_de_Interrupcion) { // Código de la función de interrupción. // No requiere limpiar el flag respectivo. El flag se limpia por hardware } 4.2.3.4.3 Control de las Interrupciones Hay dos tipos de bits para controlar las interrupciones: los Bits Enable, que habilitan las interrupciones, y los Bits de Flag, que notifican cuál ha sido la interrupción que se ha producido. Hay un bit de habilitación individual para cada interrupción y además hay un bit de habilitación general, ubicado en el registro SREG, que afecta a todas las interrupciones. Para habilitar una interrupción hay que poner un 1 en su bit de habilitación individual y en el bit de habilitación general. Ninguna habilitación individual tendrá efecto si el bit de habilitación general está a 0. 62 Por otro lado, cada interrupción tiene un bit de notificación único. Este se pone a 1 automáticamente por hardware cuando ocurre el evento de dicha interrupción. Eso pasará independientemente de si la interrupción está habilitada o no. Cada interrupción habilitada y disparada, saltará a su correspondiente Función de Interrupción o ISR. El bit de notificación se limpia automáticamente justo cuando se empieza a ejecutar la función de interrupción. La única excepción es la interrupción de recepción de la USART que se limpia automáticamente al leer el registro con el dato recibido. Este comportamiento evita perder una interrupción mientras se está atendiendo a ella misma, ya que al acabar volvería a ejecutarse la función al encontrarse de nuevo el bit de notificación a 1. Puesto que los bits de notificación se activan independientemente de si las interrupciones están habilitadas o no, al momento de activarlas, puede ser necesario limpiarlos manualmente para evitar entrar directamente por algún evento anterior. Para borrar el bit de notificación se debe escribir un 1 en el bit respectivo. Al ejecutarse la función de interrupción también se limpia por hardware el bit de habilitación general para evitar que se disparen otras interrupciones cuando se esté ejecutando la interrupción actual. Sin embargo, si queremos interrupciones recurrentes o anidadas podemos poner a 1 el bit de habilitación general dentro de la ISR actual. El bit de habilitación general se puede escribir como cualquier otro bit de un registro de E/S. Pero dada su especial importancia, existen dos instrucciones exclusivas de ensamblador llamadas SEI (para habilitarlas) y CLI (para deshabilitarlas). El archivo avr_compiler.h ofrece las macros sei() y cli() para usar estas instrucciones. 4.2.3.4.4 Recepción de una trama serie por interrupción En el apartado anterior se ha visto como utilizar la USART sin las interrupciones. Esto es una pérdida de eficiencia, ya que nos obliga a estar escuchando el bus para recibir una trama. La alternativa a esto es utilizar las interrupciones de recepción de trama. Para utilizar la interrupción del puerto serie, es necesario configurar el bit RXCIE (bit 7 del registro UCSRB) a 1, lo que activa la interrupción. También es necesario activar las interrupciones globalmente. El vector correspondiente a la interrupción de recepción de datos a través del puerto serie es USART_RX_vect. El código que deberíamos añadir en la función SERIAL_init() sería: /* Activar la interrupción del puerto serie */ UCSR0B |= (1<<RXCIE0); /* Activar interrupciones generales */ sei(); También deberíamos incluir la librería de interrupciones en nuestro fichero API: #include <avr/interrupt.h> Y la Función de Interrupción debe ser parecida a: ISR(USART_RX_vect) { read = SERIAL_Receive(); SERIAL_Transmit(read); /* Mandamos lo que recivimos a modo de eco */ } 63 Desde la función principal, o en cualquier parte del código, podríamos consultar el contenido del dato recibido sin depender de en qué momento nos han mandado el mensaje. En el ANEXO 6 se puede ver un ejemplo completo de la librería para la comunicación serie utilizando interrupciones de recepción para realizar un eco del dato recibido y la recepción de “Ctr + x” para finalizar el programa. 4.2.3.5 Interrupciones por cambio de PIN Existen dos tipos de interrupción por cambio de PIN, las INTx y las PCINTx. Las primeras son disparadas por un flanco (de subida y/o de bajada) o un nivel bajo detectado en el pin INTx del micro controlador. La PCINTx se dispara cada vez que se detecta un cambio de nivel lógico ‘1’ a ‘0’, o viceversa, en cualquiera de los pines de los puertos del ATmega. 4.2.3.5.1 Las Interrupciones INT0 e INT1 El evento que puede disparar la interrupción INTx es un flanco (de subida y/o de bajada) o un nivel bajo detectado en el pin INTx del ATmega, sin importar si ese pin está configurado como entrada o como salida. Esto es relevante en el caso de querer tener interrupciones vía software. En el ATmega368, estas dos interrupciones están vinculadas a los pines PD2 y PD3 respectivamente (corresponden al digitalPin 2 y 3 de Arduino). Los bits de habilitación (enable) y notificación (flag) de estas interrupciones se encuentran en los registros EIMSK = External interrupts Mask Register. Contiene los bits de habilitación. EIFR = External Interrupts Flags Register. Contiene los bits de notificación. EICRA = External Interrupts Control Register A. Configura la señal externa que va a generar la interrupción. Es A porque hay AVRs más grandes con más interrupciones INTx donde además existe el registro EICRB. EIMSK --- --- --- --- --- --- INT1 INT0 EIFR --- --- --- --- --- --- INTF1 INTF0 EICRA --- --- --- --- ISC11 ISC10 ISC01 ISC00 Para habilitar la interrupción INTx hay que poner a 1 el bit INTx, del registro EIMSK, además del bit de habilitación general de las interrupciones en el registro SREG Una vez producido el evento, el hardware pondrá a 1 la notificación INTFx del registro EIFR, y luego se disparará la interrupción. Este evento se debe configurar previamente en el registro EICRA y hay cuatro opciones posibles. Modo ISCx1 ISCx0 Evento de la Interrupción 0 0 0 Nivel bajo en el pin INTx. 1 0 1 Cualquier flanco (de subida y/o de bajada) detectado en el pin INTx. 2 1 0 Flanco de bajada detectado en el pin INTx. 64 Modo ISCx1 ISCx0 Evento de la Interrupción 3 1 EICRA 1 --- Flanco de subida detectado en el pin INTx. --- --- --- ISC11 ISC10 ISC01 ISC00 Configuración de interrupción INT 1 Configuración de interrupción INT 0 Como la mayoría de los registros, EICRA inicia con todos sus bits a cero lo que significa que por defecto la interrupción INTx habilitada se disparará cuando dicho pin esté a nivel bajo. La interrupción externa INTx tiene la capacidad de “despertar” al AVR, es decir, de sacarlo del modo sleep. Este modo sleep, es un modo de muy bajo consumo, muy apropiado para aplicaciones que funcionan con baterías, donde el microcontrolador y todos sus periféricos están “parados” esperando un evento que lo despierte. 4.2.3.5.2 Interrupciones de Cambio de Pin, PCINTx Esta interrupción se dispara cada vez que se detecta un cambio de nivel lógico ‘1’ a ‘0’, o viceversa, en cualquiera de los pines de los puertos del ATmega, sin importar si están configurados como entrada o como salida. Se podría decir que se dispara con los flancos de subida y de bajada en los pines de puertos. En ese sentido, se parece bastante a las interrupciones INTx. La interrupción de Cambio de Pin también puede sacar al AVR del modo Sleep. En el ATmega368 los PCINTx corresponden a los puertos siguientes: PCINT0 corresponde al PUERTO B, PCINT[7:0] – PB[7:0] PCINT1 corresponde al PUERTO C, PCINT[14:8] – PC[6:0] PCINT2 corresponde al PUERTO D, PCINT[23:16] – PD[7:0]. Las interrupciones de cambio de pin se habilitan poniendo a 1 correspondiente para los respectivos puertos que se encuentran en el registro PCICR (Pin Change Interrupt Control Register). PCICR --- --- --- --- --- PCIE2 PCIE1 PCIE0 Habilita interrupciones de PORT D Habilita interrupciones de PORT C Habilita interrupciones de PORT B Después se deben poner a 1 los bits de habilitación que identifican individualmente los pines de los puertos. Estos bits se encuentran en los registros de máscara PCMSK (Pin Change Mask Register). 65 Registro de máscara Puerto PCMSK0 PORTB PCMSK1 PORTC PCMSK2 PORTD Otra forma de seleccionar los pines de interrupción es ubicándolos directamente por sus nombres PCINTx. PCMSK0 PCINT7 PCINT6 PCINT5 PCINT4 PCINT3 PCINT2 PCINT1 PCINT0 PCMSK1 --- PCINT14 PCINT13 PCINT12 PCINT11 PCINT10 PCINT9 PCINT8 PCMSK2 PCINT23 PCINT22 PCINT21 PCINT20 PCINT19 PCINT18 PCINT17 PCINT16 Una vez producido el cambio de nivel en uno o varios de los pines habilitados para interrupción, se activará el bit de notificación respectivo PCIF (Pin Change Interrupt Flag) del registro PCIFR y luego se llamará a la función de interrupción ISR . Así como hay un bit de habilitación para cada puerto, también hay un bit de notificación para uno. PCIFR --- --- --- --- --- PCIF2 PCIF1 PCIF0 Flag de interrupciones de PORT D Flag de interrupciones de PORT C Flag de interrupciones de PORT B El bit de notificación PCIF es puesto a cero (limpiado) por el hardware al ejecutarse el gestor de interrupción ISR. Sin embargo, como esta notificación se puede activar sin necesidad de que esté habilidato el bit de habilitación general del registro SREG, a veces se tendrá que limpiar por software. En este caso se limpia escribiendo un 1. Es recomendable habilitar la Interrupción de Cambio de Pin después de realizar en los puertos todas las operaciones necesarias que pudieran ocasionar cambios de nivel en sus pines, por ejemplo, activar las pullUps. 4.2.3.6 Uso de temporizadores y PWM El ATmega328p tiene 3 temporizadores, el timer 0, el timer 1 y el timer 2. La principal diferencia entre ellos es que el 0 y el 2 son de 8 bits y el 1 de 16 bits. Todos los temporizadores tienen tres valores/conceptos asociados el TOP, el BOTTOM y el MAX: TOP define el valor máximo que se puede contar con el temporizador, que puede ser el valor máximo u otro valor definido por el usuario en el registro OCRxA, en función del modo seleccionado. BOTTOM es el valor mínimo y siempre es 0. MAX es siempre el valor máximo que un temporizador puede contar, en el temporizador de 16 bits 65535 y en los de 8bits 255. La fuente de reloj para los temporizadores puede ser el propio reloj del sistema u una fuente externa. En nuestro Arduino el reloj del sistema es de 16MHz, pero se pueden usar divisores para conseguir distintos rangos de frecuencias según nuestras necesidades. El divisor del reloj se puede ajustar a 1, 8, 64, 256 o 1024. 66 TCCRxB --- --- CSx2 --- --- --- CSx2 CSx1 CSx0 CSx1 CSx0 Selección de la funte de reloj 0 0 0 No hay reloj (Timer/Counter parado) 0 0 1 Reloj del sitema (No divisor) 0 1 0 Reloj del sitema / 8 (Divisor interno) 0 1 1 Reloj del sitema / 64 (Divisor interno) 1 0 0 Reloj del sitema / 256 (Divisor interno) 1 0 1 Reloj del sitema / 1024 (Divisor interno) 1 1 0 Reloj externo en pin T0. Flanco de bajada 1 1 1 Reloj externo en pin T0. Flanco de subida Los temporizadores tienen distintos modos de operación, los 4 principales son phase correct PWM, fast PWM, CTC(Clear Timer on Compare Match) y modo normal, donde este se comporta como un simple contador. Dependiendo si usamos temporizadores de 8 o 16 bits pueden tener distintos sub-modos de funcionamiento. TCCRxA --- --- --- --- --- --- WGMx1 WGMx0 TCCRxB --- --- --- --- WGMx2 --- --- --- Modo de WGMx2 WGMx1 WGMx0 operación Descripción Normal TOP Actualiza Activa el OCRx en flag TOV 0 0 0 0 0xFF/0xFFFF Inmediato 1 0 0 1 2 0 1 0 CTC 3 0 1 1 Fast PWM 4 1 0 0 Reservado --- --- --- 5 1 0 1 PWM, Phase Correct OCRxA TOP BOTTOM 6 1 1 0 Reservado --- --- --- 7 1 1 1 Fast PWM OCRxA BOTTOM TOP PWM, Phase Correct 0xFF/0xFFFF MAX TOP BOTTOM Inmediato MAX 0xFF/0xFFFF BOTTOM MAX OCRxA La cantidad de registros y los distintos modos de funcionamiento hacen obligatorio el uso de la documentación del microcontrolador (datasheet). Aquí solo se pretende explicar cómo se comporta en cada modo y realizar algún pequeño ejemplo demostrativo. 4.2.3.6.1 Modo normal Este es el modo básico de funcionamiento y el que está configurado después de reset. Simplemente configurando la fuente del reloj este temporizador empezaría a contar. Si configuramos la fuente de reloj para que utilice el reloj de sistema sin divisor, esto implicaría que en el registro TCNTx incrementaría en uno a cada 0.0625us (1/16MHz), 67 llegando a desbordarse en 15,9375us en el caso de utilizar el 0 o el 2, ya que son de 8bits; o en poco más de 4ms en el caso de usar el de 16bits. Esta es la importancia del divisor. Si nosotros quisiéramos realizar una espera de 1 segundo nos quedaríamos fuera de escala. Si dividimos por 1024 obtenemos incrementos del temporizador de 64us con lo que nos daría para contar hasta 16.320ms con 8bits o 4.18424 segundos con 16bits. Esta configuración nos permite poder leer el estado del TCNTx en distintos momentos. Sabiendo que tiempo ha transcurrido podemos implementar nuestra propia función de espera, similar a la de la librería delay. En este ejemplo se implementa el ejemplo del parpadeo del LED de nuestra placa Arduino utilizando el timer 1 en lugar de usar la librería delay.h /* * Arduino_blink_timer.c * * Parpadeo del LED de Arduino cada 0.5seg usando timers en lugar de la * libreria delay.h */ #include <avr/io.h> // Frequencia de la fuente de reloj en Hz #define F_CPU (16000000) // Frequencia deseada en Hz. 2Hz => 0.5 sec #define F_DESEADA (2) /* Para seleccionar el divisor comprobamos cuantas cuentas del contador necesitamos: NUM_CUENTAS = (((F_CPU / DIVISOR) / F_DESEADA) - 1) DIVISOR 1 8 64 256 1024 NUM_CUENTAS 7999999 999999 124999 31249 7811,5 Nuestros contadores son de 8 o 16 bits, con lo que como máximo pueden contar hasta 255 o 65365. Con las cuentas hechas anteriormente nos obliga a usar un contador de 16bits (el TCNT1) y un divisor configurado a 256, de manera que cuando el contador alcance 31249 habrá transcurrido 0.5 seg (2 Hz) */ #define TIEMPO_A_ESPERAR (31249) int main(void) { //Definimos el pin13/PORTB5 como salida DDRB |= (1<<PB5); //Configuramos el divisor del timer. El timer empieza a contar TCCR1B = (1<<CS12); //1:256 while(1){ PORTB |= (1<<PB5); //Enciende el LED while(TCNT1 <= TIEMPO_A_ESPERAR); // Esperamos el tiempo deseado TCNT1 = 0; //Limpiamos el timer para que vuelva a empezar PORTB &= ~(1<<PB5); //Apaga el LED while(TCNT1 <= TIEMPO_A_ESPERAR); // Esperamos el tiempo deseado TCNT1 = 0; //Limpiamos el timer para que vuelva a empezar 68 } } En este ejemplo en particular utilizamos un temporizador de 16 bits cuando la palabra del microcontrolador es de 8bits, esto hace que el registro TCNT1 en realidad sean dos registros del micro controlador. El compilador de AVRStudio ya nos proporciona unas librerías de manera que nosotros no nos tengamos de preocupar de esto. La configuración del divisor involucra a 3 bits, el CS12, CS11 y CS10. Para modificar los tres bits a la vez utilizaremos mascaras. TCCR1B &= 0b11111000; // Limpiamos los 3 bits del CS TCCR1B |= 0b00000100; // Configuramos el divisor 1:256 (CS = 100) Cada vez que el temporizador alcanza su valor máximo (TOP) pone a 1 el bit de notificación TOVx (Timer Overflow Flag) en el registro TIFRx (Timer Interrupt Flag Register). Activando el bit TOIEx (Timer Overflow Interrupt Enable) del TIMSKx (Timer Interrupt Mask Register) genera una interrupción al alcanzar este punto. Este comportamiento es muy útil para ampliar el contaje del temporizador incrementando una variable en la función de atención de la interrupción. 4.2.3.6.2 Modo CTC El modo Clear Time on Compare match (CTC) nos activa una interrupción cuando el contador a alcanzado un valor pre-definido y vuelve a empezar el contaje. Este modo de funcionamiento nos aporta una gran ventaja en el diseño de las aplicaciones, ya que no debemos detener el flujo de ejecución de nuestro programa para realizar alguna acción. Volviendo al ejemplo del LED que parpadea, lo que haremos es cargar el valor a contar en el registro OCRxA (Output Compare Register A) y lo configuraremos para que nos dispare una interrupción cuando alcance el valor definido pone a 1 el bit OCIExA (Output Compare Match A Interrupt Enable) del registro TIMSKx (Timer Interrupt Mask Register). /* * Arduino_blink_timer_INTERRUPT.c * * Parpadeo del LED de Arduino cada 0.5seg usando interrupciones de timers * en lugar de la libreria delay.h */ #include <avr/io.h> #include <avr/interrupt.h> // Frequencia de la fuente de reloj en Hz #define F_CPU (16000000) // Frequencia deseada en Hz. 2Hz => 0.5 sec #define F_DESEADA (2) /* Para seleccionar el divisor comprobamos cuantas cuentas del contador necesitamos: NUM_CUENTAS = (((F_CPU / DIVISOR) / F_DESEADA) - 1) DIVISOR 1 8 64 256 1024 NUM_CUENTAS 7999999 999999 124999 31249 7811,5 Nuestros contadores son de 8 o 16 bits, con lo que como máximo pueden contar hasta 255 o 65365. Con las cuentas hechas anteriormente nos obliga a usar un contador de 16bits (el TCNT1) y un divisor configurado a 256, de manera que cuando 69 el contador alcance 31249 habrá transcurrido 0.5 seg (2 Hz) */ #define TIEMPO_A_ESPERAR (31249) int main(void) { //Definimos el pin13/PORTB5 como salida DDRB |= (1<<PB5); //Configuramos el timer en modo CTC y el divisor del timer. //El timer empieza a contar TCCR1B = (1<<WGM12)|(1<<CS12); // CTC y 1:256 // Cargamos el valor a contar OCR1A = TIEMPO_A_ESPERAR; // Habilitamos las interrupciones del timer TIMSK1 = (1<<OCIE1A); // Habilitamos las interrupciones generales sei(); while(1){ /* * Ahora podemos implementar otras funcionalidades sin * preocuparnos del parpadeo del LED, ya que se gestiona des * de la interrupción del timer */ } } ISR(TIMER1_COMPA_vect){ PORTB ^= (1<<PB5); // Invertimos el estado del LED a cada ejecución } Dependiendo de cómo se configura los bits COMxA (Compare Match Output A Mode) del registro TCCRxA (Timer Control Register A), el pin OCxA puede realizar una acción pre configurada cuando se alcance el valor configurado. TCCRxA COMxA1 COMxA0 --- --- --- --- --- --- COMxA1 COMxA0 Comportamiento del pin OCxA 0 0 Puerto Normal, OCxA desconectado 0 1 Inversión del OCxA al alcanzar el valor 1 0 Poner a zero elOCxA al alcanzar el valor 1 1 Poner a uno el OCxA al alcanzar el valor Por ejemplo se puede generar un parpadeo (invertir su estado) cada vez que alcancemos el valor configurado en OCRxA siempre que este pin este configurado como salida. Todo esto gestionado por el propio micro controlador y sin que interrumpa el flujo de ejecución de nuestro programa. 70 Ilustración 49: Ejemplo de uso del temporizador en modo CTC 4.2.3.6.3 Generación de PWMs Existe la posibilidad de generar dos tipos de PWM para los temporizadores de 8bits, el “Fast PWM” y el “Phase Correct PWM”; y el de 16bits adicionalmente puede generar “Phase and Frequency Correct PWM”. La diferencia entre ambos es muy sutil y es muy particular para cada aplicación. El comportamiento de los distintos modos se podría resumir como: “Fast” aplica el nuevo periodo, ciclo de trabajo (duty) y/o frecuencia a continuación del periodo en curso. “Phase Correct” se podría decir que la salida está centrada. Aplica el nuevo periodo, ciclo de trabajo (duty) y/o frecuencia de manera que el punto medio del semiciclo de ON caiga en la mitad del periodo. “Phase and Frequency Correct”. Este modo es igual al anterior pero garantizando que el pulso siempre es correcto incluso cuando la frecuencia va variando. Este uso es muy particular y solo está disponible en el temporizador de 16bits. Aquí se puede ver el comportamiento de los tres modos. 71 Ilustración 50: Generación PWM en modo “Fast PWM” Ilustración 51: Generación PWM en modo “Phase Correct PWM” 72 Ilustración 52: Generación PWM en modo “Phase and Frequency Correct PWM” Como norma general el modo “Fast PWM” es suficiente para la mayoría de aplicaciones como regulación de potencia, rectificación,… 4.2.3.6.4 Uso del Input capture El temporizador de 16bits también incorpora una unidad de “input capture” que permite capturar eventos externos asociados a cambios en el pin ICP1 y ofrecer una marca temporal de frecuencia de ejecución. Típicamente esta funcionalidad se utiliza para calcular la frecuencia y/o el ciclo de trabajo (duty cycle) de una señal. Ilustración 53: Uso del temporizador como “Input capture” 73 4.2.3.7 Uso del conversor ADC El conversor analógico digital permite que convirtamos los niveles analógicos de tensión a una representación digital. Esto nos permite leer cosas como la salida de un potenciómetro, sensores de temperatura, sensores de humedad, acelerómetros y giroscopios que tienen una tensión de salida analógica, sensores de presión y mucho más las cosas. El ADC interno de nuestro microcontrolador tiene una resolución de 10 bits. Esto significa que el voltaje de entrada se convierte en un valor numérico entre 0 y 1023. El ATmega328P dispone de 6 pines que son capaces de ser utilizados como pines de entrada analógicos pero sólo hay un ADC en el microcontrolador de modo que entre los pines y el conversor hay un multiplexor analógico. El ATmega se alimenta con 5v con lo que podemos conectar cualquier señal de entre 0 y 5V en la entrada. También podemos cambiar la tensión máxima que el ADC utiliza como tensión de referencia, en el pin Aref. Hay que tener en cuenta que la frecuencia máxima de trabajo del ADC es de 200Khz. Esta se genera a partir del reloj del sistema de 16MHz y un divisor (2, 4, 8, 16, 32, 64 y 128). Dada la alta frecuencia a la que trabaja nuestro Arduino y la limitación del ADC, solo podemos configurar el divisor a 128 y así obtener una frecuencia de trabajo del conversor analógico de 125kHz. En caso de configurar una frecuencia mayor se pierde mucha precisión. Por ejemplo a 250kHz (divisor a 64) la precisión esta alrededor de 8bits. Por defecto el ADC está deshabilitado. Hasta que no lo habilitamos, la selección de la tensión de referencia o de canal, no surge ningún efecto. Para reducir el consumo se recomienda deshabilitarlo cuando no se necesite o antes de entrar en modos de bajo consumo. A continuación podemos ver un ejemplo básico donde va convirtiendo cíclicamente las distintas entradas analógicas: #include <avr/io.h> int adc_value[5]; //Valor leido del ADC int main(void) { ADCSRA |= ((1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //Divisor a 128 frec. del ADC a 125Khz ADMUX |= (1<<REFS0); ADMUX &= ~(1<<REFS1); //Avcc(+5v) como tensión de referencia ADCSRA |= (1<<ADEN); //Alimentamos el ADC ADCSRA |= (1<<ADSC); //Empezamos la conversión while(1){ for (channel=0;channel<=5;channel++){ ADMUX &= 0xF0; //Borramos el anterior canal del multiplexor ADMUX |= channel; //Definimos el nuevo canal a convertir ADCSRA |= (1<<ADSC); //Empezamos una nueva conversión while(ADCSRA & (1<<ADSC)); //Esperamos a finalizar la conversión adc_value[channel] = ADCW; //Guardamos el valor de la conversión } } return 0; } 74 4.2.4 Codificación del medidor de caudal en C Una vez revisados los periféricos que necesitamos para realizar el caudalímetro vamos a proceder a ensamblar las distintas partes de código que hemos visto. Para controlar la pantalla LCD se utilizará la librería que nos proporciona el fabricante como ya hicimos con el proyecto de Arduino. Esta librería está escrita en C++ cosa que nos obliga a crear un proyecto C++ en AVRStudio. El hecho de crear un proyecto en C++ tiene muy poco impacto para nosotros. Solo que si queremos aprovechar, a modo de librería, parte del código que hemos creado en C, deberemos incluirlo dentro de un bloque indicando que es C. extern "C" { /* Indica que la librería es en C */ #include "Arduino_SERIAL_Api.h" /* Contiene funciones para trabajar con la comunicación serie */ } /* Librería en C++ */ #include "ColorLCDShield.h" Para desligar la librería del fabricante del entorno Arduino, y evitar dependencias durante la compilación, la editaremos comentando la inclusión de “Arduino.h” y corrigiendo los errores de dependencias añadiendo las librerías necesarias del entorno AVRStudio, como por ejemplo math.h o delay.h. La única librería que utilizaremos del entorno Arduino es la de pins_arduino.h, que la copiaremos del entorno Arduino a nuestra carpeta del proyecto AVRStudio, ya que esta simplemente es la definición de los puertos de entrada y salida, y nos ahorra el tener que redefinirlos dentro de la propia librería. En el ANEXO 7 se puede ver la codificación del medidor de caudal en C++ así como las librerías usadas. Si utilizamos librerías de terceros es importante cambiar las opciones del compilador y del enlazador (linker) para que el binario generado sea lo más optimo posible. Las siguientes opciones del compilador fuerzan al compilador a alojar cada función en una sección de memoria interna separada. -ffunction-sections -fdata-sections Con las opciones del compilador anteriores y la siguiente opción del enlazador (linker) le indicamos que no incluya las funciones que no se utilizan. Estas opciones son de gran ayuda para reducir tamaño en memoria al usar librerías genéricas ya que no solemos usar todas las funciones que esta proporciona. –gc-sections Para realizar esta modificaciones en el AVRStudio, iremos a “Porperties…” en el menú “Project”. Esto nos abrirá una nueva pestaña donde veremos las opciones de configuración de nuestro proyecto. En la pestaña “ToolChain” encontraremos las configuraciones del compilador C, del C++ y del enlazador (linker). 75 Ilustración 54: Configuración opciones de optimización del compilador Ilustración 55: Configuración opciones de optimización del linker Una vez configurado el compilador y el enlazador (linker) construimos el proyecto. En el menú “Build” seleccionamos la opción “Rebuil Solution”. Como resultado veremos en la ventana “Output” que todo ha ido bien. Así como el resumen de memoria necesaria en el microcontrolador. 76 Ilustración 56: Resultado de la construcción del proyecto en AVRStudio El propio entorno de compilación del AVR-GCC nos proporciona una herramienta que determina las necesidades de memoria flash y RAM de un fichero .elf. Este fichero es el binario a descargar. Este también contiene toda la información de depuración y la distribución de la memoria. Ejecutando esta herramienta sobre el fichero que hemos generado con el AVRStudio obtenemos el siguiente resultado: avr-size.exe -C --mcu=atmega328p AVR Memory Usage ---------------Device: atmega328p medidorCaudal_AVR.elf Program: 7380 bytes (22.5% Full) (.text + .data + .bootloader) Data: 1070 bytes (52.2% Full) (.data + .bss + .noinit) Si la misma herramienta la ejecutamos sobre el fichero resultado del proyecto en el entorno Arduino, obtenemos lo siguiente: avr-size.exe -C --mcu=atmega328p AVR Memory Usage ---------------Device: atmega328p medidorCaudal_Arduino.cpp.elf Program: 10390 bytes (31.7% Full) (.text + .data + .bootloader) Data: 1269 bytes (62.0% Full) (.data + .bss + .noinit) 77 Se puede observar que al desarrollar en C y teniendo mayor control sobre las opciones a aplicar al compilador y al enlazador (linker) conseguimos usar un 10% menos de memoria, tanto flash como RAM. 5 Presupuesto En este presupuesto se pretende mostrar el coste de la realización del proyecto en sí mismo, pero diferenciando claramente el coste de realizarlo en entorno Arduino y en AVRStudio y C. El objetivo es poder ver de una forma clara la diferencia en la curva de aprendizaje de Arduino vs AVRStudio. 5.1 Precios unitarios CÓDIGO UD DESCRIPCIÓN PRECIO IT001 h Ingeniero Técnico Industrial 25,00 VEINTICINCO EUROS HW001 u Arduino UNO con ATMega328 21,90 VENTIÚN EUROS con NOVENTA céntimos HW002 u Arduino Sparkfun Color 32,90 TRENTIDÓS EUROS con NOVENTA céntimos HW003 u Koolance INS-FM17N Flow Meter for LCS Coolant 25,87 VEINTICINCO EUROS con OCHENTA Y SIETE céntimos HW004 u Tira de pines macho 40 pines 0,53 paso 2,54 mm CINCUENTA Y TRES céntimos HW005 u Rollo de cable de 10Mts sección 2,70 0,5 mm2 DOS EUROS con SETENTA céntimos Shield LCD 5.2 Precios descompuestos 5.2.1 Capítulo 1: Estudios previos CÓDIGO UD DESCRIPCIÓN CANTIDAD PRECIO SUBTOTAL IT001 h Ingeniero Técnico Industrial recopilando información acerca de Arduino y su entorno de programación 80,00 25,00 2.000,00 IT001 h Ingeniero Técnico Industrial recopilando información acerca de ATmega328P y AVRStudio 220,00 25,00 5.500,00 Suma de la Partida 7.500,00 2,00 % Costes Indirectos 150,00 TOTAL PARTIDA 78 7.650,00 5.2.2 Capítulo 2: Diseño y montaje del hardware CÓDIGO UD DESCRIPCIÓN CANTIDAD PRECIO SUBTOTAL IT001 h Ingeniero Técnico Industrial 20,00 25,00 500,00 HW001 u Arduino UNO con ATMega328 1,00 21,90 21,90 HW002 u Arduino Shield LCD Color Sparkfun 1,00 32,90 32,90 HW003 u Koolance INS-FM17N Flow Meter for LCS Coolant 1,00 25,87 25,87 HW004 u Tira de pines macho 40 pines paso 0,25 2,54 mm 0,53 0,13 HW005 u Rollo de cable de 10Mts sección 0,25 0,5 mm2 2,70 0.68 Suma de la Partida 581,48 Envío y manipulación TOTAL PARTIDA 10,00 591,48 5.2.3 Capítulo 3: Diseño del firmware CÓDIGO UD DESCRIPCIÓN CANTIDAD PRECIO SUBTOTAL IT001 h Ingeniero Técnico Industrial. 10,00 25,00 250,00 25,00 25,00 625,00 15,00 25,00 375,00 20,00 25,00 500,00 Diseño en entorno y lenguaje “Arduino” IT001 h Ingeniero Técnico Industrial. Testeo y corrección de errores en entorno “Arduino” IT001 h Ingeniero Técnico Industrial. Diseño en entorno AVRStudio y C IT001 h Ingeniero Técnico Industrial. Testeo y corrección de errores en entorno AVRStudio Suma de la Partida 1.750,00 2,00 % Costes Indirectos 35,00 TOTAL PARTIDA 1.785,00 5.2.4 Capítulo 4: Diseño del software de PC CÓDIGO UD DESCRIPCIÓN CANTIDAD PRECIO SUBTOTAL IT001 h Ingeniero Técnico Industrial. 12,00 25,00 300,00 79 Implementación de aplicación en Python IT001 h Ingeniero Técnico Industrial. 25,00 25,00 625,00 Testeo y corrección de errores Suma de la Partida 925,00 2,00 % Costes Indirectos 18,50 TOTAL PARTIDA 943,50 5.2.5 Capítulo 5: Documentación CÓDIGO UD DESCRIPCIÓN CANTIDAD PRECIO SUBTOTAL IT001 h Ingeniero Técnico Industrial 80,00 25,00 2.000,00 Documentación de todo el proceso de estudio. Suma de la Partida 2.000,00 2,00 % Costes Indirectos 40,00 TOTAL PARTIDA 2.040,00 5.3 Resumen del presupuesto CAPÍTULO RESUMEN IMPORTE Capítulo 1 Estudios previos 7.650,00 Capítulo 2 Diseño y montaje del hardware 591,48 Capítulo 3 Diseño del firmware 1.785,00 Capítulo 4 Diseño del software de PC 943,50 Capítulo 5 Documentación 2.040,00 TOTAL EJECUCIÓN MATERIAL 13,00 % Gastos Generales 13.009,98 1,691,30 6,00 % Beneficio Industrial 780,60 TOTAL EJECUCIÓN POR CONTRATO 15.481,87 18,00 % IVA 2.786,74 TOTAL PRESUPUESTO LICITACIÓN 18.268,61 80 6 Conclusiones y valoración personal El objetivo principal de este PFC ha sido conocer Arduino a través del montaje de un caudalímetro. Después de realizar este proyecto se puede concluir que Arduino es una muy buena plataforma para realizar pequeños proyectos de sistemas basados en microcontrolador. La curva de aprendizaje de Arduino es realmente rápida. Dispone de gran cantidad de documentación oficial y no oficial. La comunidad de usuarios es muy grande, accesible y colaborativa. Hay gran cantidad de ejemplos de uso y proyectos hechos con licencia abierta, cosa que permite reaprovechar todo lo que ya funciona. Hay una gran cantidad de canales de distribución, tanto a nivel nacional como internacional. El coste de las placas es realmente ajustado y no suele haber problemas de aprovisionamiento. Esto facilita mucho el poder disponer del hardware necesario para nuestros proyectos en plazos de tiempo pequeños y a un coste muy reducido. El hecho de haber definido una manera estándar de crear extensiones, las shields, ha permitido crear una gran cantidad de placas accesorias para cubrir la mayoría de necesidades habituales en proyectos de electrónica. Estas extensiones se consiguen a través de los mismos canales de distribución. Estas suelen entregarse con ejemplos de uso o incluso librerías de código integrables en el entorno Arduino, con lo que amplifican mucho el potencial de la plataforma. El nivel de abstracción de cómo funcionan los circuitos electrónicos o como se deben programar los dispositivos es muy alto. Esto permite realizar operaciones complejas sin gran esfuerzo. En contra, estas limitado al comportamiento proporcionado por las librerías o por la funcionalidad de la circuitería de la que dispones. El desempeño de estas librerías o extensiones no siempre es el más óptimo y hay veces que es necesario realizar modificaciones. Pero incluso en estos casos el soporte que se puede encontrar en la comunidad de usuarios es muy grande y no debe suponer un gran escollo para la realización del proyecto. En cualquier caso, siempre podemos crear nuestras propias librerías, dando así soporte a nuevas funcionalidades o mejoras de rendimiento. En estos casos sí que es necesario un mayor nivel de conocimientos de electrónica o de programación, pero el alcanzar este nivel puede hacerse de un modo muy progresivo. Enlazando con el segundo objetivo que nos habíamos marcado en la realización de este PFC, podemos decir que las capacidades lectivas o formativas de la plataforma Arduino son muy grandes. Los aplicativos para el PC son simples y fáciles de usar. La sintaxis necesaria para el desarrollo de aplicaciones es simple y tiene muchas funciones que ya implementan operaciones habituales y relativamente complejas, como la generación de un PWM o lecturas analógicas. Esto permite poder introducir conceptos de programación y algoritmia a niveles de educación secundaria. El hecho de interactuar con el mundo real de un modo directo, puede generar una mayor curiosidad en el alumnado. Esto permite adquirir unos conocimientos de electrónica o programación de un modo muy ameno. El hecho que el coste de una placa sea razonablemente bajo, y el hardware muy robusto, puede facilitar el acceso a este por gran parte de centros, permitiendo su uso durante un largo camino formativo. Se podría decir que tiene un bajo coste por el rendimiento que puede ofrecer. 81 En ámbitos más técnicos o superiores, la plataforma ofrece la posibilidad seguir aprendiendo implementando nuestras propias librerías. Esto obliga a tener un mayor conocimiento de los dispositivos con los que se va a trabajar. Si queremos abandonar las herramientas software que nos proporciona Arduino, el propio fabricante del microcontrolador ofrece un gran número de herramientas para permitir el desarrollo ensamblador, C y C++. Esto permite que sobre el mismo hardware Arduino se pueda aprender cómo funciona un microcontrolador des de cero, ofreciendo una plataforma continuista hacia nuevos niveles de conocimiento. Las placas Arduino, el microcontrolador usado en estas y el conjunto de recursos gratuitos disponibles, permiten aprender todo lo relativo a un proyecto de electrónica y software embebido de una manera progresiva y sin perder visibilidad en ningún punto. Se puede definir un camino formativo que pase des del diseño de la placa, su funcionalidad y el algoritmo de control; hasta las optimizaciones del compilador, la definición del mapa de memoria o todo el proceso de construcción (make). Ha sido muy enriquecedor trabajar y conocer una plataforma tan versátil y fácil de usar como Arduino. Siempre aparecen contratiempos pero son fáciles de superar gracias a la gran comunidad que existe. El potencial de la plataforma es enorme y las limitaciones iniciales del software Arduino se pueden superar fácilmente al poder trabajar en C. Es realmente una plataforma muy recomendable para todo aquel que tenga una mínima inquietud para hacerse el mismo las cosas. DIY!!! 82 ANEXOS ANEXO 1: Información uso librería LCD ************************************ * * * ColorLCDShield * * * * an Arduino Library * * * * by Jim Lindblom * * SparkFun Electronics * * * ************************************ License: CC-BY SA 3.0 - Creative commons share-alike 3.0 use this code however you'd like, just keep this license and attribute. Let me know if you make hugely, awesome, great changes. Huge thanks to Jim Lynch, Mark Sproul, and Peter Davenport. If I'm missing anyone, I apologize, all of this code's been through the ringer more than a few times. This is a (hopefully) simple Arduino library for use specifically with SparkFun Electronics' Color LCD Shield (http://www.sparkfun.com/products/9363). It'll no doubt work with other incarnations of LCD breakouts and what not, you just might have to do some modifying of the pin defintions. The code gives you access to the following LCD functions: LCDShield(); - The library's constructor. An instance of the lcd must be created at the beginning of any code. e.g: LCDShield lcd; void init(int type); - This initializes an LCD. Either EPSON or PHILLIPS should be passed as the type variable. This function should be called near the beginning of any function (setup()!). Turns on the display, sets the contrast and uinitializes the LCD into 12-bit RGB color mode. void clear(int color); - Clears the entire screen, filling it with the 12bit RGB color passed to it. There are a number of predefined colors in ColorLCDShield.h void contrast(char setting); - Manually adjusts the contrast. A value between 0 and 60 should be passed. 40 works pretty well for me. void setPixel(int color, unsigned char x, unsigned char y); - Draws a single pixel at the specified x, y location. Color is a 12-bit RGB value. void setCircle (int x0, int y0, int radius, int color); - Draws a circle centered around x0, y0, of the specified radius and color. Radius is in pixels, color is a 12-bit RGB value. void setChar(char c, int x, int y, int fColor, int bColor); - Sets a single character down at the specified x/y coordinate. You can pick both the foreground and background color, they're 12-bit RGB values. Only one font is 83 available in this library. Definitely room for growth, though at an added cost of memory. void setStr(char *pString, int x, int y, int fColor, int bColor); - Sets a string of characters down, beginning at x/y coordinates. You can pick both the foreground and background color, they're 12-bit RGB values. Only one font is available in this library. Definitely room for growth, though at an added cost of memory. void setLine(int x0, int y0, int x1, int y1, int color); - Draws a line from x0,y0 to x1,y1. Color is a 12-bit RGB value. void setRect(int x0, int y0, int x1, int y1, unsigned char fill, int color); - Draws a rectangle with opposing corners at x0,y0 and x1,y1. A 1 for fill will fill the entire rectangle, a 0 will only draw the border. Color is a 12-bit RGB value. void printLogo(void); - One trick pony. Prints the SparkFun logo. void on(void); - Turns the display on. void off(void); - Turns the display off. You'll still see the backlight on! Modificaciones sobre la librería En el fichero de librería ColorLCDShield.cpp se han modificado las siguientes líneas para poder utilizar un tipo de fuente que requiera menos RAM. Se ha pasado de utilizar una tipografía de 8x16 puntos a otra de 8x8. Ilustración 57: Modificación del fichero ColorLCDShield.cpp. 1era parte 84 Ilustración 58: Modificación del fichero ColorLCDShield.cpp. 2da parte En el fichero ColorLCDShield.h se ha definido el nuevo tipo de fuente: const unsigned char FONT8x8[97][8] = { {0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00}, num_bytes_per_char {0x08,0x08,0x08,0x00,0x00,0x00,0x00,0x00}, num_bytes_per_char {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x30,0x78,0x78,0x30,0x30,0x00,0x30,0x00}, {0x6C,0x6C,0x6C,0x00,0x00,0x00,0x00,0x00}, {0x6C,0x6C,0xFE,0x6C,0xFE,0x6C,0x6C,0x00}, {0x18,0x3E,0x60,0x3C,0x06,0x7C,0x18,0x00}, {0x00,0x63,0x66,0x0C,0x18,0x33,0x63,0x00}, {0x1C,0x36,0x1C,0x3B,0x6E,0x66,0x3B,0x00}, {0x30,0x30,0x60,0x00,0x00,0x00,0x00,0x00}, {0x0C,0x18,0x30,0x30,0x30,0x18,0x0C,0x00}, {0x30,0x18,0x0C,0x0C,0x0C,0x18,0x30,0x00}, {0x00,0x66,0x3C,0xFF,0x3C,0x66,0x00,0x00}, {0x00,0x30,0x30,0xFC,0x30,0x30,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x30}, {0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, {0x03,0x06,0x0C,0x18,0x30,0x60,0x40,0x00}, {0x3E,0x63,0x63,0x6B,0x63,0x63,0x3E,0x00}, {0x18,0x38,0x58,0x18,0x18,0x18,0x7E,0x00}, {0x3C,0x66,0x06,0x1C,0x30,0x66,0x7E,0x00}, {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, {0x0E,0x1E,0x36,0x66,0x7F,0x06,0x0F,0x00}, {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, {0x7E,0x66,0x06,0x0C,0x18,0x18,0x18,0x00}, {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x00}, {0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x30}, {0x0C,0x18,0x30,0x60,0x30,0x18,0x0C,0x00}, {0x00,0x00,0x7E,0x00,0x00,0x7E,0x00,0x00}, {0x30,0x18,0x0C,0x06,0x0C,0x18,0x30,0x00}, {0x3C,0x66,0x06,0x0C,0x18,0x00,0x18,0x00}, {0x3E,0x63,0x6F,0x69,0x6F,0x60,0x3E,0x00}, {0x18,0x3C,0x66,0x66,0x7E,0x66,0x66,0x00}, {0x7E,0x33,0x33,0x3E,0x33,0x33,0x7E,0x00}, {0x1E,0x33,0x60,0x60,0x60,0x33,0x1E,0x00}, {0x7C,0x36,0x33,0x33,0x33,0x36,0x7C,0x00}, 85 // columns, rows, // columns, rows, // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // space 0x20 ! " # $ % & ' ( ) * + , . / (forward slash) 0 0x30 1 2 3 4 5 6 7 8 9 : ; < = > ? @ 0x40 A B C D {0x7F,0x31,0x34,0x3C,0x34,0x31,0x7F,0x00}, // E {0x7F,0x31,0x34,0x3C,0x34,0x30,0x78,0x00}, // F {0x1E,0x33,0x60,0x60,0x67,0x33,0x1F,0x00}, // G {0x66,0x66,0x66,0x7E,0x66,0x66,0x66,0x00}, // H {0x3C,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, // I {0x0F,0x06,0x06,0x06,0x66,0x66,0x3C,0x00}, // J {0x73,0x33,0x36,0x3C,0x36,0x33,0x73,0x00}, // K {0x78,0x30,0x30,0x30,0x31,0x33,0x7F,0x00}, // L {0x63,0x77,0x7F,0x7F,0x6B,0x63,0x63,0x00}, // M {0x63,0x73,0x7B,0x6F,0x67,0x63,0x63,0x00}, // N {0x3E,0x63,0x63,0x63,0x63,0x63,0x3E,0x00}, // O {0x7E,0x33,0x33,0x3E,0x30,0x30,0x78,0x00}, // P {0x3C,0x66,0x66,0x66,0x6E,0x3C,0x0E,0x00}, // Q {0x7E,0x33,0x33,0x3E,0x36,0x33,0x73,0x00}, // R {0x3C,0x66,0x30,0x18,0x0C,0x66,0x3C,0x00}, // S {0x7E,0x5A,0x18,0x18,0x18,0x18,0x3C,0x00}, // T {0x66,0x66,0x66,0x66,0x66,0x66,0x7E,0x00}, // U {0x66,0x66,0x66,0x66,0x66,0x3C,0x18,0x00}, // V {0x63,0x63,0x63,0x6B,0x7F,0x77,0x63,0x00}, // W {0x63,0x63,0x36,0x1C,0x1C,0x36,0x63,0x00}, // X {0x66,0x66,0x66,0x3C,0x18,0x18,0x3C,0x00}, // Y {0x7F,0x63,0x46,0x0C,0x19,0x33,0x7F,0x00}, // Z {0x3C,0x30,0x30,0x30,0x30,0x30,0x3C,0x00}, // [ {0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x00}, // \ {0x3C,0x0C,0x0C,0x0C,0x0C,0x0C,0x3C,0x00}, // ] {0x08,0x1C,0x36,0x63,0x00,0x00,0x00,0x00}, // ^ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF}, // _ {0x18,0x18,0x0C,0x00,0x00,0x00,0x00,0x00}, // ` {0x00,0x00,0x3C,0x06,0x3E,0x66,0x3B,0x00}, // a {0x70,0x30,0x3E,0x33,0x33,0x33,0x6E,0x00}, // b {0x00,0x00,0x3C,0x66,0x60,0x66,0x3C,0x00}, // c {0x0E,0x06,0x3E,0x66,0x66,0x66,0x3B,0x00}, // d {0x00,0x00,0x3C,0x66,0x7E,0x60,0x3C,0x00}, // e {0x1C,0x36,0x30,0x78,0x30,0x30,0x78,0x00}, // f {0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x7C}, // g {0x70,0x30,0x36,0x3B,0x33,0x33,0x73,0x00}, // h {0x18,0x00,0x38,0x18,0x18,0x18,0x3C,0x00}, // i {0x06,0x00,0x06,0x06,0x06,0x66,0x66,0x3C}, // j {0x70,0x30,0x33,0x36,0x3C,0x36,0x73,0x00}, // k {0x38,0x18,0x18,0x18,0x18,0x18,0x3C,0x00}, // l {0x00,0x00,0x66,0x7F,0x7F,0x6B,0x63,0x00}, // m {0x00,0x00,0x7C,0x66,0x66,0x66,0x66,0x00}, // n {0x00,0x00,0x3C,0x66,0x66,0x66,0x3C,0x00}, // o {0x00,0x00,0x6E,0x33,0x33,0x3E,0x30,0x78}, // p {0x00,0x00,0x3B,0x66,0x66,0x3E,0x06,0x0F}, // q {0x00,0x00,0x6E,0x3B,0x33,0x30,0x78,0x00}, // r {0x00,0x00,0x3E,0x60,0x3C,0x06,0x7C,0x00}, // s {0x08,0x18,0x3E,0x18,0x18,0x1A,0x0C,0x00}, // t {0x00,0x00,0x66,0x66,0x66,0x66,0x3B,0x00}, // u {0x00,0x00,0x66,0x66,0x66,0x3C,0x18,0x00}, // v {0x00,0x00,0x63,0x6B,0x7F,0x7F,0x36,0x00}, // w {0x00,0x00,0x63,0x36,0x1C,0x36,0x63,0x00}, // x {0x00,0x00,0x66,0x66,0x66,0x3E,0x06,0x7C}, // y {0x00,0x00,0x7E,0x4C,0x18,0x32,0x7E,0x00}, // z {0x0E,0x18,0x18,0x70,0x18,0x18,0x0E,0x00}, // { {0x0C,0x0C,0x0C,0x00,0x0C,0x0C,0x0C,0x00}, // | {0x70,0x18,0x18,0x0E,0x18,0x18,0x70,0x00}, // } {0x3B,0x6E,0x00,0x00,0x00,0x00,0x00,0x00} // ~ }; 0x50 (back slash) 0x60 0x70 ANEXO 2: Programa de control de Arduino. /* * Medidor de caudal * * Recibe una serie de pulsos proporcionales al caudal detectado por un caudalimetro 86 * en un linea de interrupcion. * Muestra en una pantalla LCD la cantidad de litros detectados y el flujo instantaneo. * El LCD tiene tres botones con distintas acciones programadas: * - 1er boton: Cambia la informacion a mostrar en la pantalla. * - 2do boton: Pone a zero un primer contador parcial. * - 3er boton: Pone a zero un segundo contador parcial. */ /* * Incluye la libreria de la pantalla LCD */ #include <ColorLCDShield.h> /* * Variables y constantes usadas para el LCD y los botones */ LCDShield lcd; // Objeto LCD const byte lcdContraste = 40; // Valor del contraste par el LCD char lcdStr[16]; // Cadena usada para mostrar textos en el LCD /* Mapeo de los botones del LCD contra los pines del Arduino */ const int lcdBotones[3] = {3, 4, 5}; // S1 = PIN 3, S2 = PIN 4, S3 = PIN 5 const byte botonAntirebote = 10; // Numero de veces que se debe leer un valor estable en los botones /* * Variables y constantes usadas para el temporizado */ /* Las siguientes variables son tipo long ya que mediremos el tiempo en milisegundos y en seguida creceran y un integer no seria suficiente. */ const unsigned int intervalo_1000ms = 1000; // Intervalo de ejecucion de la tarea periodica (ms) unsigned long previoMilis = 0; // instante anterior de ejecucion int contadorSegundos = 0; int contadorSegundosParcial_1 = 0; int contadorSegundosParcial_2 = 0; /* * Variables y */ const byte const byte const byte constantes usadas para las interrupciones caudalInt = 0; // 0 = pin 2; 1 = pin 3 caudalPin = 2; caudalGNDPin = 6; /* * Variables y constantes usadas para el contaje de volumen */ const float contadorRelacion = 0.307; // Relacion entre pulsos y litros por minuto volatile unsigned int contadorPulsos = 0; // volatile ya que se accede des de interrupcion unsigned int contadorPulsosLocal = 0; float litrosMinuto = 0.0; float litrosTotales = 0.0; float litrosParcial_1 = 0.0; float litrosParcial_2 = 0.0; /* * Variables y constantes usadas para la gestion de las pantallas */ const byte numeroPantallas = 2; // Numero de distintas pantallas a mostrar byte pantalla = 0; // Pantalla inicial a mostrar /* 87 * Funcion de inicializacion del sistema */ void setup() { /* Configuracion puerto serie (USB) */ Serial.begin(9600); Serial.println("Empecemos..."); /* Configuracion del LCD */ lcd.init(EPSON); // Inicializa el LCD, utilizar PHILLIPS si no funciona lcd.contrast(lcdContraste); // Inicializamos el contraste lcd.clear(WHITE); // Fondo de pantalla en blanco (sin color) /* Configuracion del sensor */ /* Configuramos el pin de entrada del sensor como entrada con un pullUp */ pinMode(caudalPin, INPUT); // Configuramos como entrada digitalWrite(caudalPin, HIGH); // Activamos pullUp pinMode(caudalGNDPin, OUTPUT); // Configuramos como salida digitalWrite(caudalGNDPin, LOW); // Le damos GND al sensor /* La linea del interruptor del caudalimetro esta conectada al pin 2 que utiliza la intterupcion 0. Configuramos para capturar los flancos de bajada (transicion de HIGH a LOW) */ attachInterrupt(caudalInt, isr_caudalimetro, FALLING); contadorPulsos = 0; } /* * Funcion principal */ void loop() { unsigned long currentMillis = millis(); /* Variables para gestion del anti rebote (debouncing) de los botones */ static byte boton0 = 0; static byte boton1 = 0; static byte boton2 = 0; /* Para ser usado como test. Remplazamos el caudalimetro por un corto circuito entre las lineas del Arduino. Pin2 y Pin6. Esto equivaldria a que el sensor nos entrega una seoal de 31Hz */ // tone(caudalGNDPin,31); // Al utilizar contadores de 8 bits, 31Hz es la menor freq que nos permite esta funcion directamente. /* Acciones a realizar cada segundo */ if (currentMillis - previoMilis > intervalo_1000ms) { previoMilis = currentMillis; task_1000ms(); } /* Lectura continua sobre los pulsadores con debouncing */ if (!digitalRead(lcdBotones[0])){ // Pulsado if (boton0 < 255){ // Control de rangos boton0++; } }else{ // No pulsado if (boton0 > botonAntirebote){ // Ha estado pulsado al menos "botonAntirebote" veces /* Ponemos a zero el contador parcial 2 */ contadorSegundosParcial_2 = 0; litrosParcial_2 = 0.0; } boton0 = 0; } if (!digitalRead(lcdBotones[1])){ // Pulsado 88 if (boton1 < 255){ // Control de rangos boton1++; } }else{ // No pulsado if (boton1 > botonAntirebote){ // Ha estado pulsado al menos "botonAntirebote" veces /* Ponemos a zero el contador parcial 1 */ contadorSegundosParcial_1 = 0; litrosParcial_1 = 0.0; } boton1 = 0; } if (!digitalRead(lcdBotones[2])){ // Pulsado if (boton2 < 255){ // Control de rangos boton2++; } }else{ // No pulsado if (boton2 > botonAntirebote){ // Ha estado pulsado al menos "botonAntirebote" veces lcd.clear(WHITE); // Limpiamos la pantalla pantalla++; // Cambiamos la pantalla a mostrar if (pantalla > numeroPantallas){ pantalla = 0; } } boton2 = 0; } } /* * Funcion periodica cada 1 segundo. * Cada segundo leemos los pulsos recibidos por el cuadalimetro, * actualizamos los contadores de volumen acumulado e instantaneo, * mandamos por la linea serie los pulsos detectados * y refrescamos la pantalla a mostrar */ void task_1000ms() { float litrosUltimoMinuto; /* Inicio zona segura intercambio de datos. No hay interrupciones */ noInterrupts(); contadorPulsosLocal = contadorPulsos; contadorPulsos = 0; // Se escribe el la interrupción y se limpia una vez leido interrupts(); /* Fin zona segura intercambio de datos */ Serial.println(contadorPulsosLocal); // Mandamos los pulsos x segundo a la linea serie /* Convertimos los pulsos a litros por minuto y acumulamos los totales */ litrosMinuto = contadorPulsosLocal * contadorRelacion; litrosUltimoMinuto = (litrosMinuto / 60); litrosTotales += litrosUltimoMinuto; litrosParcial_1 += litrosUltimoMinuto; litrosParcial_2 += litrosUltimoMinuto; /* Incrementamos los contadores de tiempo */ contadorSegundos++; contadorSegundosParcial_1++; contadorSegundosParcial_2++; /* Mostramos la pantalla que corresponda */ switch (pantalla){ case 0: pantalla_principal(); 89 break; case 1: pantalla_parcial_1(); break; case 2: pantalla_parcial_2(); break; default: break; } } /* * Pantalla principal a mostrar */ void pantalla_principal() { lcd.setStr("Tiempo total", 3, 3, BLACK, WHITE); lcd.setStr(" segundos", 19, 3, BLACK, WHITE); itoa(contadorSegundos,lcdStr,10); lcd.setStr(lcdStr, 19, 11, BLACK, WHITE); lcd.setStr("Consumo actual", 35, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros totales ", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr("Cont.1 Cont.2 ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosParcial_1, 4, 2, lcdStr); lcd.setStr(lcdStr, 112, 3, BLACK, WHITE); dtostrf(litrosParcial_2, 4, 2, lcdStr); lcd.setStr(lcdStr, 112, 75, BLACK, WHITE); } /* * Pantalla contadores parciales 1 */ void pantalla_parcial_1() { lcd.setStr("Tiempo parcial 1", 0, 3, BLACK, WHITE); lcd.setStr(" segundos ", 16, 3, BLACK, WHITE); itoa(contadorSegundosParcial_1,lcdStr,10); lcd.setStr(lcdStr, 16, 11, BLACK, WHITE); lcd.setStr("Consumo actual ", 32, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros parcial 1", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosParcial_1, 4, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr(" Totales ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 112, 59, BLACK, WHITE); } /* 90 * Pantalla contadores parciales 2 */ void pantalla_parcial_2() { lcd.setStr("Tiempo parcial 2", 0, 3, BLACK, WHITE); lcd.setStr(" segundos ", 16, 3, BLACK, WHITE); itoa(contadorSegundosParcial_2,lcdStr,10); lcd.setStr(lcdStr, 16, 11, BLACK, WHITE); lcd.setStr("Consumo actual ", 32, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros parcial 2", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosParcial_2, 4, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr(" Totales ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 112, 59, BLACK, WHITE); } /* * Funcion llamada por la interrupcion 0 a cada deteccion de flanco de bajada * del caudalimetro. */ void isr_caudalimetro() { contadorPulsos++; // Incrementamos el contador de pulsos } ANEXO 3: Programa de control del PC. """ This demo demonstrates how to draw a dynamic mpl (matplotlib) plot in a wxPython application. It allows "live" plotting as well as manual zooming to specific regions. Both X and Y axes allow "auto" or "manual" settings. For Y, auto mode sets the scaling of the graph to see all the data points. For X, auto mode makes the graph "follow" the data. Set it X min to manual 0 to always see the whole data from the beginning. Note: press Enter in the 'manual' text box to make a new value affect the plot. Eli Bendersky ([email protected]) License: this code is in the public domain Last modified: 31.07.2008 """ import os import pprint import random import sys import wx 91 import serial # The recommended way to use wx with mpl is with the WXAgg # backend. # import matplotlib matplotlib.use('WXAgg') from matplotlib.figure import Figure from matplotlib.backends.backend_wxagg import \ FigureCanvasWxAgg as FigCanvas, \ NavigationToolbar2WxAgg as NavigationToolbar import numpy as np import pylab class DataGet(object): """ Get the number of pulses received from serial port """ def __init__(self, init=0): self.data = self.init = init self.ser = serial.Serial( port='\\.\COM3', baudrate=9600, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=1 ) def next(self): test = self.ser.readline() if (test != '') and (test[0].isdigit()): self.data = int(test,10)*0.307 return self.data class BoundControlBox(wx.Panel): """ A static box with a couple of radio buttons and a text box. Allows to switch between an automatic mode and a manual mode with an associated value. """ def __init__(self, parent, ID, label, initval): wx.Panel.__init__(self, parent, ID) self.value = initval box = wx.StaticBox(self, -1, label) sizer = wx.StaticBoxSizer(box, wx.VERTICAL) self.radio_auto = wx.RadioButton(self, -1, label="Auto", style=wx.RB_GROUP) self.radio_manual = wx.RadioButton(self, -1, label="Manual") self.manual_text = wx.TextCtrl(self, -1, size=(35,-1), value=str(initval), 92 style=wx.TE_PROCESS_ENTER) self.Bind(wx.EVT_UPDATE_UI, self.on_update_manual_text, self.manual_text) self.Bind(wx.EVT_TEXT_ENTER, self.on_text_enter, self.manual_text) manual_box = wx.BoxSizer(wx.HORIZONTAL) manual_box.Add(self.radio_manual, flag=wx.ALIGN_CENTER_VERTICAL) manual_box.Add(self.manual_text, flag=wx.ALIGN_CENTER_VERTICAL) sizer.Add(self.radio_auto, 0, wx.ALL, 10) sizer.Add(manual_box, 0, wx.ALL, 10) self.SetSizer(sizer) sizer.Fit(self) def on_update_manual_text(self, event): self.manual_text.Enable(self.radio_manual.GetValue()) def on_text_enter(self, event): self.value = self.manual_text.GetValue() def is_auto(self): return self.radio_auto.GetValue() def manual_value(self): return self.value class GraphFrame(wx.Frame): """ The main frame of the application """ title = 'Demo: dynamic matplotlib graph' def __init__(self): wx.Frame.__init__(self, None, -1, self.title) self.dataget = DataGet() self.data = [self.dataget.next()] self.paused = False self.create_menu() self.create_status_bar() self.create_main_panel() self.redraw_timer = wx.Timer(self) self.Bind(wx.EVT_TIMER, self.on_redraw_timer, self.redraw_timer) self.redraw_timer.Start(100) def create_menu(self): self.menubar = wx.MenuBar() menu_file = wx.Menu() m_expt = menu_file.Append(-1, "&Save plot\tCtrl-S", "Save plot to file") self.Bind(wx.EVT_MENU, self.on_save_plot, m_expt) menu_file.AppendSeparator() 93 m_exit = menu_file.Append(-1, "E&xit\tCtrl-X", "Exit") self.Bind(wx.EVT_MENU, self.on_exit, m_exit) self.menubar.Append(menu_file, "&File") self.SetMenuBar(self.menubar) def create_main_panel(self): self.panel = wx.Panel(self) self.init_plot() self.canvas = FigCanvas(self.panel, -1, self.fig) self.xmin_control self.xmax_control self.ymin_control self.ymax_control = = = = BoundControlBox(self.panel, BoundControlBox(self.panel, BoundControlBox(self.panel, BoundControlBox(self.panel, -1, -1, -1, -1, "X "X "Y "Y min", max", min", max", 0) 50) 0) 100) self.pause_button = wx.Button(self.panel, -1, "Pause") self.Bind(wx.EVT_BUTTON, self.on_pause_button, self.pause_button) self.Bind(wx.EVT_UPDATE_UI, self.on_update_pause_button, self.pause_button) self.cb_grid = wx.CheckBox(self.panel, -1, "Show Grid", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_grid, self.cb_grid) self.cb_grid.SetValue(True) self.cb_xlab = wx.CheckBox(self.panel, -1, "Show X labels", style=wx.ALIGN_RIGHT) self.Bind(wx.EVT_CHECKBOX, self.on_cb_xlab, self.cb_xlab) self.cb_xlab.SetValue(True) self.hbox1 = wx.BoxSizer(wx.HORIZONTAL) self.hbox1.Add(self.pause_button, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(20) self.hbox1.Add(self.cb_grid, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox1.AddSpacer(10) self.hbox1.Add(self.cb_xlab, border=5, flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) self.hbox2 = wx.BoxSizer(wx.HORIZONTAL) self.hbox2.Add(self.xmin_control, border=5, self.hbox2.Add(self.xmax_control, border=5, self.hbox2.AddSpacer(24) self.hbox2.Add(self.ymin_control, border=5, self.hbox2.Add(self.ymax_control, border=5, flag=wx.ALL) flag=wx.ALL) flag=wx.ALL) flag=wx.ALL) self.vbox = wx.BoxSizer(wx.VERTICAL) self.vbox.Add(self.canvas, 1, flag=wx.LEFT | wx.TOP | wx.GROW) self.vbox.Add(self.hbox1, 0, flag=wx.ALIGN_LEFT | wx.TOP) self.vbox.Add(self.hbox2, 0, flag=wx.ALIGN_LEFT | wx.TOP) self.panel.SetSizer(self.vbox) 94 self.vbox.Fit(self) def create_status_bar(self): self.statusbar = self.CreateStatusBar() def init_plot(self): self.dpi = 100 self.fig = Figure((3.0, 3.0), dpi=self.dpi) self.axes = self.fig.add_subplot(111) self.axes.set_axis_bgcolor('black') self.axes.set_title('Liters per minute', size=12) pylab.setp(self.axes.get_xticklabels(), fontsize=8) pylab.setp(self.axes.get_yticklabels(), fontsize=8) # plot the data as a line series, and save the reference # to the plotted line series # self.plot_data = self.axes.plot( self.data, linewidth=1, color=(1, 1, 0), )[0] def draw_plot(self): """ Redraws the plot """ # when xmin is on auto, it "follows" xmax to produce a # sliding window effect. therefore, xmin is assigned after # xmax. # if self.xmax_control.is_auto(): xmax = len(self.data) if len(self.data) > 50 else 50 else: xmax = int(self.xmax_control.manual_value()) if self.xmin_control.is_auto(): xmin = xmax - 50 else: xmin = int(self.xmin_control.manual_value()) # for ymin and ymax, find the minimal and maximal values # in the data set and add a mininal margin. # # note that it's easy to change this scheme to the # minimal/maximal value in the current display, and not # the whole data set. # if self.ymin_control.is_auto(): ymin = round(min(self.data), 0) - 1 else: ymin = int(self.ymin_control.manual_value()) if self.ymax_control.is_auto(): ymax = round(max(self.data), 0) + 1 95 else: ymax = int(self.ymax_control.manual_value()) self.axes.set_xbound(lower=xmin, upper=xmax) self.axes.set_ybound(lower=ymin, upper=ymax) # anecdote: axes.grid assumes b=True if any other flag is # given even if b is set to False. # so just passing the flag into the first statement won't # work. # if self.cb_grid.IsChecked(): self.axes.grid(True, color='gray') else: self.axes.grid(False) # Using setp here is convenient, because get_xticklabels # returns a list over which one needs to explicitly # iterate, and setp already handles this. # pylab.setp(self.axes.get_xticklabels(), visible=self.cb_xlab.IsChecked()) self.plot_data.set_xdata(np.arange(len(self.data))) self.plot_data.set_ydata(np.array(self.data)) self.canvas.draw() def on_pause_button(self, event): self.paused = not self.paused def on_update_pause_button(self, event): label = "Resume" if self.paused else "Pause" self.pause_button.SetLabel(label) def on_cb_grid(self, event): self.draw_plot() def on_cb_xlab(self, event): self.draw_plot() def on_save_plot(self, event): file_choices = "PNG (*.png)|*.png" dlg = wx.FileDialog( self, message="Save plot as...", defaultDir=os.getcwd(), defaultFile="plot.png", wildcard=file_choices, style=wx.SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() self.canvas.print_figure(path, dpi=self.dpi) self.flash_status_message("Saved to %s" % path) 96 def on_redraw_timer(self, event): # if paused do not add data, but still redraw the plot # (to respond to scale modifications, grid change, etc.) # if not self.paused: self.data.append(self.dataget.next()) self.draw_plot() def on_exit(self, event): self.Destroy() def flash_status_message(self, msg, flash_len_ms=1500): self.statusbar.SetStatusText(msg) self.timeroff = wx.Timer(self) self.Bind( wx.EVT_TIMER, self.on_flash_status_off, self.timeroff) self.timeroff.Start(flash_len_ms, oneShot=True) def on_flash_status_off(self, event): self.statusbar.SetStatusText('') if __name__ == '__main__': app = wx.PySimpleApp() app.frame = GraphFrame() app.frame.Show() app.MainLoop() ANEXO 4: Configuración del AVRStudio para descargar el código en Arduino. Contenido de “Upload.cmd” @ECHO OFF cls Set micro=ATMEGA328P REM Chequeamos los argumentos de la linea de comandos If "%~1"=="" goto NoParam If "%~2"=="" goto NoParam If NOT "%~3"=="" goto Error Set port=%~1 Set file=%~2 Goto CheckPort :NoParam Set /P port=Introduce el puerto serie al que esta conectado el Arduino [COM3]: If "%port%"=="" goto Error Set /P file=Introduce el binario a descargar: If "%file%"=="" goto Error Goto CheckPort :CheckPort IF /I NOT %port:~0,3%==COM GOTO Error Goto Execute 97 :Error echo. echo Debes indicar el puerto serie de Arduino y el binario a descargar. echo upload SERIAL_PORT FILE_TO_FLASH echo. echo Si la ruta del binario incluye espacios en blanco, encierralo entre comillas. echo. echo Ejemplo: echo upload COM3 "Arduino Blink.hex" echo. PAUSE GOTO End :Execute avrdude -F -V -c arduino -p %micro% -P %port% -b 115200 -D -U flash:w:"%file%" :End Pause Contenido de “UploadFromAVRStudio.cmd” echo off cls REM Detectamos des de donde debemos ejecutarnos set currentFolder=%~dps0 REM Vamos a la unidad correcta %~d0 REM Vamos a la carpeta donde tenemos el avrdude cd %currentFolder% REM Eliminamos la '\' extra que añade AVRStudio en el path de donde se encuentra nuestro proyecto set folder=%~1 set folder=%folder:~0,-1% set fileNameWithExt=%~2 REM Eliminamos la extensión del fichero que nos proporciona AVRStudio set fileName=%fileNameWithExt:.atsln=% echo %fileName% REM Componemos la ruta donde se encuentra el binario a descargar con el parametro Debug o Release del AVRStudio set binaryFile=%folder%%fileName%\%3\%fileName%.hex REM Copiamos el fichero a descargar en esta misma carpeta copy "%binaryFile%" REM Lanzamos el batch que tenemos preparado para el avrdude des de la linea de comandos call upload "%4" "%fileName%.hex" REM Borramos el binario que hemos descargado del /F /Q "%fileName%.hex" Uso de herramientas externas en AVRStudio Para la creación de una herramienta externa y su correspondiente botón en la barra de herramientas en el AVRStudio, debemos definirlas en el menú “Tools”, “External Tools…” 98 Ilustración 59: Configuración de una herramienta externa en AVRStudio En el cuadro de dialogo que se nos aparece, debemos usar el botón “Add” y rellenar los siguientes campos de este modo: Title: El nombre que le queramos poner a la herramienta, en mi caso: Program Arduino Debug Command: Ruta completa de donde tengamos el fichero “uploadFromAVRStudio.cmd” que hemos creado anteriormente, en mi caso: d:\Mis documentos\Documents\My Dropbox\Arduino\Code\AVRStudio 5.1\ArduinoUpload\uploadFromAVRStudio.cmd Arguments: Aquí utilizamos las variables que tiene configuradas el propio AVRStudio para indicarle al fichero por lotes donde se encuentra el fichero a descargar, el nombre que tiene, si utilizamos una compilación “Debug” o “Release” y el puerto en que se encuentra nuestro puerto serie. En mi caso: $(SolutionDir) $(SolutionFileName) Debug COM12 Una vez tenemos esto, podemos añadir esta llamada a la barra de herramientas, de modo que tendríamos un acceso parecido al que ofrece el propio entorno Arduino. Pulsamos con el botón izquierdo del ratón sobre el desplegable de la barra de herramientas del AVRStudio, seleccionado “Customize…” 99 Ilustración 60: Vista para crear un botón para las herramientas externas en AVRStudio Una vez dentro de las opciones, debemos añadir un nuevo comando, y seleccionar “External Command 1” o la posición en la que hayamos definido nuestra “external tool”. Ilustración 61: Cuadro de opciones para botón en la barra de herramientas. I 100 Ilustración 62: Cuadro de opciones para botón en la barra de herramientas. II Donde obtendremos como resultado un nuevo botón para programar nuestro Arduino de manera cómoda y sin tener que salir del AVRStudio. Ilustración 63: Vista del botón en la barra de herramientas ANEXO 5: Librería de uso de la comunicación serie en AVRStudio Contenido de “Arduino_SERIAL_API.h” Este es el contenido de la API se ha creado para poder interactuar con la comunicación serie en C de un modo cómodo. /* * Arduino_SERIAL_API.h */ #define SERIAL_BAUDRATE #define F_CPU (9600U) (16000000UL) /* Inicialización de la comunicación serie */ void SERIAL_Init( void ); /* Función de transmisión de un caracter */ void SERIAL_Transmit( unsigned char data ); /* Función de recepción de un caracter */ unsigned char SERIAL_Receive( void ); /* Función de transmisión de una cadena de caracteres */ void SERIAL_PutString(char* StringPtr); 101 Contenido de “Arduino_SERIAL.c” /* * Arduino_SERIAL.c */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #include "Arduino_SERIAL_Api.h" #define BAUD_RATE_REGISTER ((F_CPU / (SERIAL_BAUDRATE * 16ul)) - 1u) /* Datasheet. Tabla 20-1. Equations for Calculating Baud Rate Register Setting */ /* Inicialización de la comunicación serie */ void SERIAL_Init( void ) { /* Configuramos la velocidad de la comunicación (baud rate) */ UBRR0H = (unsigned char)(BAUD_RATE_REGISTER>>8); UBRR0L = (unsigned char)BAUD_RATE_REGISTER; /* Valores por defecto después de reset en el registre USCR0A. Esta escritura no esta definida en el datasheet, pero parece necesaria ya que de lo contrario el bit de "Double USART Transmission Speed" parece activo, de modo que no configura el baud rate como esperamos. Posiblemente el bootloader de Arduino está configurando este bit, ya que usa el periférico para descargar el código. */ UCSR0A = (1<<TXC0); /* Activar la recepción y la transmisión */ UCSR0B = (1<<RXEN0)|(1<<TXEN0); /* Configuramos el formato de la trama: 8data, 2stop bit */ UCSR0C = (1<<USBS0)|(3<<UCSZ00); } /* Función de transmisión de un caracter */ void SERIAL_Transmit( unsigned char data ) { /* Esperamos a vaciar el buffer de transmisión */ while ( !( UCSR0A & (1<<UDRE0) ) ); /* Ponemos el dato en el buffer y mandamos el dato */ UDR0 = data; } /* Función de recepción de un caracter */ unsigned char SERIAL_Receive( void ) { /* Esperamos a recibir todo el dato */ while ( !(UCSR0A & (1<<RXC0) ) ); /* Devolvemos el dato del buffer */ return UDR0; } /* Función de transmisión de una cadena de caracteres */ void SERIAL_PutString(char* StringPtr){ while(*StringPtr != 0x00){ /* Mientras la cadena no esta vacia transmite */ SERIAL_Transmit(*StringPtr); StringPtr++; } /* Añadimos salto de linea y retorno de carro al final de la cadena */ SERIAL_Transmit(0x0Du); SERIAL_Transmit(0x0Au); } 102 ANEXO 6: Comunicación serie con interrupciones de recepción Contenido de “Arduino_SERIAL_INTRx_API.h” Este es el contenido de la API se ha creado para poder interactuar con la comunicación serie en C de un modo cómodo. /* * Arduino_SERIAL_INTRx_API.h */ #include <avr/interrupt.h> #define SERIAL_BAUDRATE #define F_CPU (9600U) (16000000UL) /* Inicialización de la comunicación serie */ void SERIAL_Init( void ); /* Función de transmisión de un caracter */ void SERIAL_Transmit( unsigned char data ); /* Función de recepción de un caracter */ unsigned char SERIAL_Receive( void ); /* Función de transmisión de una cadena de caracteres */ void SERIAL_PutString(char* StringPtr); Contenido de “Arduino_SERIAL_INTRx.c” /* * Arduino_SERIAL_INTRx.c */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #include "Arduino_SERIAL_INTRx_Api.h" #define BAUD_RATE_REGISTER ((F_CPU / (SERIAL_BAUDRATE * 16ul)) - 1u) /* Datasheet. Tabla 20-1. Equations for Calculating Baud Rate Register Setting */ /* Inicialización de la comunicación serie */ void SERIAL_Init( void ) { /* Configuramos la velocidad de la comunicación (baud rate) */ UBRR0H = (unsigned char)(BAUD_RATE_REGISTER>>8); UBRR0L = (unsigned char)BAUD_RATE_REGISTER; /* Valores por defecto después de reset en el registre USCR0A. Esta escritura no esta definida en el datasheet, pero parece necesaria ya que de lo contrario el bit de "Double USART Transmission Speed" parece activo, de modo que no configura el baud rate como esperamos. Posiblemente el bootloader de Arduino está configurando este bit, ya que usa el periférico para descargar el código. */ UCSR0A = (1<<TXC0); /* Activar la recepción y la transmisión */ UCSR0B = (1<<RXEN0)|(1<<TXEN0); /* Configuramos el formato de la trama: 8data, 2stop bit */ UCSR0C = (1<<USBS0)|(3<<UCSZ00); /* Activar la interrupción del puerto serie */ UCSR0B |= (1<<RXCIE0); /* Activar interrupciones generales */ sei(); } /* Función de transmisión de un caracter */ 103 void SERIAL_Transmit( unsigned char data ) { /* Esperamos a vaciar el buffer de transmisión */ while ( !( UCSR0A & (1<<UDRE0) ) ); /* Ponemos el dato en el buffer y mandamos el dato */ UDR0 = data; } /* Función de recepción de un caracter */ unsigned char SERIAL_Receive( void ) { /* Esperamos a recibir todo el dato */ while ( !(UCSR0A & (1<<RXC0) ) ); /* Devolvemos el dato del buffer */ return UDR0; } /* Función de transmisión de una cadena de caracteres */ void SERIAL_PutString(char* StringPtr){ while(*StringPtr != 0x00){ /* Mientras la cadena no esta vacia transmite */ SERIAL_Transmit(*StringPtr); StringPtr++; } } Contenido de “Arduino_SERIAL_INTERRUPT_example.c” /* * Arduino_SERIAL_INTERRUPT_example.c */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #define F_CPU (16000000UL) /* F_CPU indica al compilador que nuestro cristal es de 16Mhz. Debe ser declarado antes que la libreria delay.h */ #include <util/delay.h> /* Contiene funciones de espera en ms y us */ #include "Arduino_SERIAL_INTRx_Api.h" /* Contiene funciones para trbajar con la comunicación serie */ volatile unsigned char read; int main( void ) { int index = 0; char indexCh[2]; SERIAL_Init(); /* Inicializamos la comunicación */ /* Ejemplo de como escribir enteros en el terminal a modo de cuenta atras */ for(index=3;index>0;index--){ itoa(index,indexCh,10); SERIAL_PutString(indexCh); SERIAL_PutString("\r\n"); _delay_ms(500); } SERIAL_PutString("Empieza a escribir en el terminal. \r\n"); read = 0; while(read != 0x18){ /* Para salir Ctr + x */ /* Solo romperemos este bucle des de la interrupción de recepción */ } SERIAL_PutString("\r\n Buen trabajo !!!"); return 0; } 104 ISR(USART_RX_vect) { read = SERIAL_Receive(); SERIAL_Transmit(read); /* Mandamos lo que recivimos a modo de eco */ } ANEXO 7: Medidor de caudal implementado con AVRStudio Este es el programa de control del Arduino codificado en C++ y compilado por el AVRStudio. El proyecto completo se puede encontrar en el soporte digital de este PFC. Contenido de “medidorCaudal_AVR.cpp” /* * Medidor de caudal * * Recibe una serie de pulsos proporcionales al caudal detectado por un caudalimetro * en un linea de interrupcion. * Muestra en una pantalla LCD la cantidad de litros detectados y el flujo instantaneo. * El LCD tiene tres botones con distintas acciones programadas: * - 1er boton: Cambia la informacion a mostrar en la pantalla. * - 2do boton: Pone a zero un primer contador parcial. * - 3er boton: Pone a zero un segundo contador parcial. * * Este codigo esta implementado en C y no utiliza las "facilidades" del entorno Arduino */ /* * Importación de librerias */ #include <avr/io.h> /* Esta libreria contiene la definición de todos los registros, SIEMPRE DEBE SER INCLUIDA */ #define F_CPU (16000000UL) /* F_CPU indica al compilador que nuestro cristal es de 16Mhz. Debe ser declarado antes que la libreria delay.h */ #include "string.h" #include "stdlib.h" #include "math.h" #include <util/delay.h> /* Contiene funciones de espera en ms y us */ #include <avr/interrupt.h> #include "ColorLCDShield.h" extern "C" { /* Importamos las librerias definidas en C */ #include "Arduino_SERIAL_Api.h" /* Contiene funciones para trabajar con la comunicación serie */ } /* * Variables y constantes usadas para el LCD */ static LCDShield lcd; // Objeto LCD #define LCD_CONTRASTE (40) // Valor del contraste par el LCD char lcdStr[16]; // Cadena usada para mostrar textos en el LCD #define LIMITE_ANTIRREBOTE (10) // Numero de veces que se debe leer un valor estable en los botones /* * Variables y constantes usadas para el temporizado */ /* NUM_CUENTAS = (((F_CPU / DIVISOR) / F_DESEADA) - 1) */ 105 #define F_DESEADA (1) // 1Hz => 1 segundo #define DIVISOR (256) #define TIEMPO_A_ESPERAR (((F_CPU / DIVISOR) / F_DESEADA) - 1) #if TIEMPO_A_ESPERAR >= 65535 // Máximo del contador de 16bits #error El tiempo a esperar es demasiado grande #endif volatile unsigned char segundos = 0; // Se actualiza en la interrupción del timer static unsigned int contadorSegundos = 0; static unsigned int contadorSegundosParcial_1 = 0; static unsigned int contadorSegundosParcial_2 = 0; /* * Variables y constantes usadas para el contaje de volumen */ #define CONTADOR_RELACION (0.307F) // Relacion entre pulsos y litros por minuto volatile unsigned int contadorPulsos = 0; // volatile ya que se accede des de interrupcion static unsigned int contadorPulsosLocal = 0; static float litrosMinuto = 0.0; static float litrosTotales = 0.0; static float litrosParcial_1 = 0.0; static float litrosParcial_2 = 0.0; /* * Variables y constantes usadas para la gestion de las pantallas */ #define NUMERO_PANTALLAS (3) // Numero de distintas pantallas a mostrar unsigned char pantalla = 0; // Pantalla inicial a mostrar /* * Definición de prototipos */ void inicializacion(void); void lecturaPulsadorLCD(void); void task_1000ms(void); void pantalla_principal(void); void pantalla_parcial_1(void); void pantalla_parcial_2(void); /* * Definición de funciones */ /* * Función principal */ int main(void){ inicializacion(); while(1){ if (segundos >= 1){ /* Inicio zona segura intercambio de datos. No hay interrupciones */ cli(); // DesHabilitamos las interrupciones generales segundos--; sei(); // Habilitamos las interrupciones generales /* Fin zona segura intercambio de datos */ task_1000ms(); } lecturaPulsadorLCD(); } return 0; } 106 /* * Inicialización de periféricos (puerto serie, timer, LCD e interrupciones) */ void inicializacion(void){ /* Configuramos la comunicación serie */ SERIAL_Init(); SERIAL_PutString("Empecemos..."); /* Configuracion del LCD */ lcd.init(EPSON); // Inicializa el LCD, utilizar PHILLIPS si no funciona lcd.contrast(LCD_CONTRASTE); // Inicializamos el contraste lcd.clear(WHITE); // Fondo de pantalla en blanco (sin color) /* Configuración del sensor */ DDRD = (1<<PORTD6); // Pin digital 6 - PD6 como salida las demas como entrada PORTD = ((1<<PORTD2) | (1<<PORTD3)|(1<<PORTD4)|(1<<PORTD5)); // PullUp en las entradas contadorPulsos = 0; /* Configuramos interrupciones del pin INT 0 */ EIMSK = (1<<INT0); // INT 0 EICRA = (1<<ISC01); // Flanco de bajada /* Configuramos el timer en modo CTC y el divisor del timer. El timer empieza a contar */ TCCR1B = ((1<<WGM12)|(1<<CS12)); // CTC y 1:256 OCR1A = TIEMPO_A_ESPERAR; // Cargamos el valor a contar TIMSK1 = (1<<OCIE1A); // Habilitamos las interrupciones del timer sei(); // Habilitamos las interrupciones generales } /* * Chequea el estado de los switches del LCD */ void lecturaPulsadorLCD(void){ /* Variables para gestion del anti rebote (debouncing) de los botones */ static unsigned char boton0 = 0; static unsigned char boton1 = 0; static unsigned char boton2 = 0; /* Lectura continua sobre los pulsadores con debouncing */ if ((PIND & (1<<PORTD3)) == 0){ // Pulsado if (boton0 < 255){ // Control de rangos boton0++; } }else{ // No pulsado if (boton0 > LIMITE_ANTIRREBOTE){ // Ha estado pulsado al menos "LIMITE_ANTIRREBOTE" veces /* Ponemos a zero el contador parcial 2 */ contadorSegundosParcial_2 = 0; litrosParcial_2 = 0.0; } boton0 = 0; } if ((PIND & (1<<PORTD4)) == 0){ // Pulsado if (boton1 < 255){ // Control de rangos boton1++; } }else{ // No pulsado if (boton1 > LIMITE_ANTIRREBOTE){ // Ha estado pulsado al menos "LIMITE_ANTIRREBOTE" veces /* Ponemos a zero el contador parcial 1 */ contadorSegundosParcial_1 = 0; 107 litrosParcial_1 = 0.0; } boton1 = 0; } if ((PIND & (1<<PORTD5)) == 0){ // Pulsado if (boton2 < 255){ // Control de rangos boton2++; } }else{ // No pulsado if (boton2 > LIMITE_ANTIRREBOTE){ // Ha estado pulsado al menos "LIMITE_ANTIRREBOTE" veces lcd.clear(WHITE); // Limpiamos la pantalla pantalla++; // Cambiamos la pantalla a mostrar if (pantalla >= NUMERO_PANTALLAS){ pantalla = 0; } } boton2 = 0; } } /* * Funcion periodica cada 1 segundo. * Cada segundo leemos los pulsos recibidos por el cuadalimetro, * actualizamos los contadores de volumen acumulado e instantaneo, * mandamos por la linea serie los pulsos detectados * y refrescamos la pantalla a mostrar */ void task_1000ms(void){ float litrosUltimoMinuto; /* Inicio zona segura intercambio de datos. No hay interrupciones */ cli(); // DesHabilitamos las interrupciones generales contadorPulsosLocal = contadorPulsos; contadorPulsos = 0; // Se escribe el la interrupcion y se limpia una vez leido sei(); // Habilitamos las interrupciones generales /* Fin zona segura intercambio de datos */ itoa(contadorPulsosLocal,lcdStr,10); SERIAL_PutString(lcdStr); // Mandamos los pulsos x segundo a la linea serie /* Convertimos los pulsos a litros por minuto y acumulamos los totales */ litrosMinuto = contadorPulsosLocal * CONTADOR_RELACION; litrosUltimoMinuto = (litrosMinuto / 60); litrosTotales += litrosUltimoMinuto; litrosParcial_1 += litrosUltimoMinuto; litrosParcial_2 += litrosUltimoMinuto; /* Incrementamos los contadores de tiempo */ contadorSegundos++; contadorSegundosParcial_1++; contadorSegundosParcial_2++; /* Mostramos la pantalla que corresponda */ switch (pantalla){ case 0: pantalla_principal(); break; case 1: pantalla_parcial_1(); break; case 2: pantalla_parcial_2(); break; default: 108 break; } } /* * Pantalla principal a mostrar */ void pantalla_principal(void) { lcd.setStr("Tiempo total", 3, 3, BLACK, WHITE); lcd.setStr(" segundos", 19, 3, BLACK, WHITE); itoa(contadorSegundos,lcdStr,10); lcd.setStr(lcdStr, 19, 11, BLACK, WHITE); lcd.setStr("Consumo actual", 35, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros totales ", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr("Cont.1 Cont.2 ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosParcial_1, 4, 2, lcdStr); lcd.setStr(lcdStr, 112, 3, BLACK, WHITE); dtostrf(litrosParcial_2, 4, 2, lcdStr); lcd.setStr(lcdStr, 112, 75, BLACK, WHITE); } /* * Pantalla contadores parciales 1 */ void pantalla_parcial_1(void) { lcd.setStr("Tiempo parcial 1", 0, 3, BLACK, WHITE); lcd.setStr(" segundos ", 16, 3, BLACK, WHITE); itoa(contadorSegundosParcial_1,lcdStr,10); lcd.setStr(lcdStr, 16, 11, BLACK, WHITE); lcd.setStr("Consumo actual ", 32, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros parcial 1", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosParcial_1, 4, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr(" Totales ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 112, 59, BLACK, WHITE); } /* * Pantalla contadores parciales 2 */ void pantalla_parcial_2(void) { lcd.setStr("Tiempo parcial 2", 0, 3, BLACK, WHITE); lcd.setStr(" segundos ", 16, 3, BLACK, WHITE); itoa(contadorSegundosParcial_2,lcdStr,10); lcd.setStr(lcdStr, 16, 11, BLACK, WHITE); 109 lcd.setStr("Consumo actual ", 32, 3, BLACK, WHITE); lcd.setStr(" l x min", 48, 3, BLACK, WHITE); dtostrf(litrosMinuto, 4, 2, lcdStr); lcd.setStr(lcdStr, 48, 11, BLACK, WHITE); lcd.setStr("Litros parcial 2", 64, 3, BLACK, WHITE); lcd.setStr(" ", 80, 3, BLACK, WHITE); dtostrf(litrosParcial_2, 4, 2, lcdStr); lcd.setStr(lcdStr, 80, 19, BLACK, WHITE); lcd.setStr(" Totales ", 96, 3, BLACK, WHITE); lcd.setStr(" ", 112, 3, BLACK, WHITE); dtostrf(litrosTotales, 6, 2, lcdStr); lcd.setStr(lcdStr, 112, 59, BLACK, WHITE); } /* * Incrementa el contador de segundos */ ISR(TIMER1_COMPA_vect){ segundos++; } /* * Incrementa el contador de pulsos */ ISR(INT0_vect){ contadorPulsos++; } Parte del contenido modificado de “ColorLCDShield.h” Para compilar la librería sin depender del entorno Arduino, se han modificado los ficheros a incluir, y se ha redefinido las librerías de delay para que utilice las del AVRStudio. /* ColorLCDShield.h - Arduino Library to control a Nokia 6100 LCD, specifically that found on SparkFun's Color LCD Shield. This code should work for both Epson and Phillips display drivers normally found on the Color LCD Shield. License: CC BY-SA 3.0: Creative Commons Share-alike 3.0. Feel free to use and abuse this code however you'd like. If you find it useful please attribute, and SHARE-ALIKE! This is based on code by Mark Sproul, and Peter Davenport. */ #ifndef ColorLCDShield_H #define ColorLCDShield_H #define PHILLIPS #define EPSON 0 1 /* The content of this include had been joined at "Arduino.h" */ /* #include <WProgram.h> */ //#include "Arduino.h" #include "pins_arduino.h" #include <inttypes.h> /* Para aseguranos que trabajamos con la frequencia correcta para nuestro Arduino */ #ifdef F_CPU 110 #undef F_CPU #endif #include "math.h" #define F_CPU 16000000UL #include <util/delay.h> */ #define delayMicroseconds(x) #define delay(x) /* Contiene funciones de espera en ms y us _delay_us(x) _delay_ms(x) A partir de este punto se ha mantenido el contenido del fichero utilizado en el entorno Arduino. ANEXO 8: Contenido del soporte digital del PFC En el soporte digital del PFC se ha añadido el código fuente, los programas a instalar y la documentación técnica referente a los componentes. El objetivo es poder configurar un PC de manera que pueda reproducirse cualquier punto del PFC con las versiones de las aplicaciones aquí descritas. En la raíz del soporte digital podemos encontrar el documento del PFC, su resumen y tres carpetas. La primera carpeta contiene el código fuente del PFC para ser ejecutado en entorno Arduino, AVRStudio y la aplicación python para el PC. También contiene una serie de ejemplos y librerías para el AVRStudio. Y utilidades para programar directamente el Arduino des del AVRStudio, calcular el tamaño en RAM y FLASH de un binario y un monitor serie para Windows que no necesita ser instalado. La segunda contiene los instaladores y programas para Windows necesarios para poder compilar y ejecutar el código del proyecto, tanto en Arduino, como AVRStudio como en python. El entorno Arduino se ha entregado con las librerías del PFC configuradas, y listo para ser ejecutado y lanzar el proyecto Arduino sin necesidad de ser instalado. El AVRStudio es un paquete que contiene todas las dependencias necesarias para poder ser instalado sin necesidad de conexión a internet. La carpeta python contiene el aplicativo python para ser instalado y librerías adicionales que se deben instalar para poder ejecutar la aplicación de PC. La tercera carpeta contiene la documentación técnica referente al microcontrolador, el LCD y caudalímetro utilizada durante el desarrollo del proyecto. Esta es la estructura de carpetas del soporte digital: 111 Ilustración 64: Estructura de carpetas del soporte digital 112 Bibliografía y referencias Bibliografía Arduino team. Arduino homepage [En línea] Página web del equipo de Arduino [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/> Wikipedia. Arduino [En línea] Enciclopedia digital [Fecha consulta: 01-012012] [Acceso gratuito] <http://es.wikipedia.org/wiki/Arduino> Arduino team. Arduino Uno. [En línea] Artículo digital [Fecha consulta: 0101-2012] [Acceso gratuito] <http://arduino.cc/en/Main/ArduinoBoardUno> HeKilledMyWire. How to start using AvrStudio, C code and Arduino. [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://hekilledmywire.wordpress.com/2010/12/04/22/> Arduino team. Referencia del Lenguaje [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/es/Reference/HomePage> tronixstuff, Arduino Tutorials [En línea] Artículo digital [Fecha consulta: 0101-2012] [Acceso gratuito] <http://tronixstuff.wordpress.com/tutorials/> www.EarthshineElectronics.com, A Complete Beginners Guide to the Arduino [En línea] Artículo digital [Fecha consulta: 10-02-2012] [Acceso gratuito] <http://www.earthshineelectronics.com/files/ASKManualRev5.pdf> Atmel Corporation, AVR Studio 5 Overview [En línea] Artículo digital [Fecha consulta: 12-04-2012] [Acceso gratuito] <http://www.atmel.com/dyn/products/tools_card.asp?tool_id=17212&source =avr_5_studio_overview> Atmel Corporation, ATmega328P [En línea] Artículo digital [Fecha consulta: 10-01-2012] [Acceso gratuito] <http://www.atmel.com/dyn/products/product_card.asp?PN=ATMEGA328P #datasheets> cursomicros.com, Las Interrupciones en los AVR [En línea] Artículo digital [Fecha consulta: 20-05-2012] [Acceso gratuito] <http://www.cursomicros.com/avr/interrupciones/interrupciones.html> EngBlaze, Tutorial: Using AVR Studio 5 with Arduino projects [En línea] Artículo digital [Fecha consulta: 10-03-2012] [Acceso gratuito] <http://www.engblaze.com/tutorial-using-avr-studio-5-with-arduinoprojects/> tty1.net, Optimisations of AVR programs using avr-gcc [En línea] Artículo digital [Fecha consulta: 12-06-2012] [Acceso gratuito] <http://www.tty1.net/blog/2008-04-29-avr-gcc-optimisations_en.html> Practical Arduino, Projects [En línea] Artículo digital [Fecha consulta: 01-082012] [Acceso gratuito] < http://www.practicalarduino.com/projects/waterflow-gauge> 113 Teague Labs, DIY Arduino Water Meter [En línea] Artículo digital [Fecha consulta: 15-11-2011] [Acceso gratuito] <http://labs.teague.com/?p=722> Referencias [1] Arduino team. Arduino homepage [En línea] Página web del equipo de Arduino [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://arduino.cc/> [2] Wiring programming framework, Homepage [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://wiring.org.co/> [3] Processing programming language, Homepage [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.processing.org/> [4] Definition of free cultural works, OSHW [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://freedomdefined.org/OSHW > [5] Creative Commons, Attribution-ShareAlike 2.5 Generic [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://creativecommons.org/licenses/by-sa/2.5/> [6] Parallax Inc, The BASIC Stamp [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.parallax.com/tabid/295/Default.aspx> [7] BasicX, Home [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.basicx.com/> [8] Phidgets, Products for USB Sensing and Control [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.phidgets.com/ > [9] The Handy Board, About [En línea] Artículo digital [Fecha consulta: 10-112011] [Acceso gratuito] <http://handyboard.com/hb/about/> [10] Freeduino, Home [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.freeduino.org/> [11] Arduino team. Arduino compatible hardware [En línea] Artículo digital [Fecha consulta: 10-11-2011] [Acceso gratuito] <http://www.arduino.cc/playground/Main/SimilarBoards#goArdComp> [12] Arduino team. Hardware [En línea] Artículo digital [Fecha consulta: 10-112011] [Acceso gratuito] <http://www.arduino.cc/en/Main/hardware> [13] Digi, XBee-PRO 802.15.4 OEM RF Modules [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.digi.com/products/wireless-wired-embedded-solutions/zigbeerf-modules/point-multipoint-rfmodules/xbee-series1-module#overview> [14] Arduino team. Hardware for connecting to Arduino [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.arduino.cc/playground/Main/SimilarBoards#goConn> [15] NonGNU, AVR Libc Home Page [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.nongnu.org/avr-libc/> 114 [16] Arduino team. Libraries [En línea] Artículo digital [Fecha consulta: 01-012012] [Acceso gratuito] <http://arduino.cc/en/Reference/Libraries> [17] Arduino team. Writing a Library for Arduino [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/en/Hacking/LibraryTutorial> [18] Sparkfun electronics, Color LCD Shield [En línea] Artículo digital [Fecha consulta: 15-08-2012] [Acceso gratuito] <http://www.sparkfun.com/products/9363> [19] Koolance Superior Liquid Cooling Solutions, Product manual v1.3 [En línea] Artículo digital [Fecha consulta: 15-08-2012] [Acceso gratuito] < http://koolance.com/files/products/manuals/manual_insfm17,18_d130eng.pdf> [20] RC Electronic World, ARDUIMU V2 QUADROTOR [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.rcelectronicworld.co.cc/p/quadrotor.html> [21] Android Arduino Handbag, Main [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.labradoc.com/i/follower/p/android-arduino-handbag> [22] Arduino team. Getting Started w/ Arduino on Windows [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/en/Guide/Windows> [23] Arduino team. Placa Arduino monocapa [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/es/Main/ArduinoBoardSerialSingleSided3> [24] BricoGeek, Tienda [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.bricogeek.com/shop/> [25] Electan, electrónica y robótica, Home [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.electan.com> [26] Proyecto Arduino, Download the Arduino Software [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/en/Main/Software> [27] SourceForge, WinAVR [En línea] Artículo digital [Fecha consulta: 01-012012] [Acceso gratuito] <http://winavr.sourceforge.net/index.html> [28] Proyecto Arduino, Referencia del Lenguaje [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/es/Reference/HomePage> [29] Sección de tutorials de TronixStuff, Título [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://tronixstuff.wordpress.com/tutorials/> [30] Manual del Starter Kit de Earthshine Electronics, Arduino Starter Kit Manual [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.earthshineelectronics.com/files/ASKManualRev5.pdf> 115 [31] Arduino playground wiki, Libraries for Arduino [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://arduino.cc/playground/Main/LibraryList> [32] Página web de Koolance, Flow meters [En línea] Artículo digital [Fecha consulta: 01-12-2011] [Acceso gratuito] <http://www.koolance.com/watercooling/product_info.php?product_id=740> [33] Tienda Tecniolid, CAUDAL LAVAVAJILLAS - Caudalimetro [En línea] Artículo digital [Fecha consulta: 01-12-2011] [Acceso gratuito] <http://www.tiendatecniolid.com/epages/61636078.sf/es_ES/undefined/es_E S/?ViewObjectID=5056775> [34] eBay Stores, HELLFIRE TOYZ LLC [En línea] Artículo digital [Fecha consulta: 01-12-2011] [Acceso gratuito] <http://stores.ebay.com/HELLFIRETOYZ-LLC> [35] Sparkfun electronics, Nokia LCD Display Driver [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <https://docs.google.com/viewer?url=http://www.sparkfun.com/tutorial/Noki a%25206100%2520LCD%2520Display%2520Driver.pdf> [36] Richard Kaufman’s blog, How to fix it: Backlight on SparkFun’s Color LCD Shield not working [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://richardkaufman.org/blog/how-to-fix-it-backlighton-sparkfuns-color-lcd-shield-not-working> [37] Atmel, Main [En línea] Artículo digital [Fecha consulta: 01-01-2012] [Acceso gratuito] <http://www.atmel.com/> [38] AVR freaks forum, GCC and the ROGMEM Attributes [En línea] Artículo digital [Fecha consulta: 20-07-2012] [Acceso gratuito] <http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t= 38003> 116