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