Arduino - Un enfoque practico e incremental

    Arduino - Un enfoque practico e incremental

    P5 months ago 335

    AIAI Summary

    toggle
    Bulleted
    toggle
    Text

    Key Insights

    Arduino - Un enfoque practico e incremental - Page 1
    1/127
    Current maintainers of this work are Jos´e Antonio de la Torre las Heras and Julio Daniel Dondo
Gazzano.
Reservados todos los derechos. No se permite la reproducci´on total o parcial de esta obra, ni su
incorporaci´on a un sistema inform´atico, ni su transmisi´on en cualquier forma o por cualquier medio
(electr´onico, mec´anico, fotocopia, grabaci´on u otros) sin autorizaci´on previa y por escrito de los
titulares del copyright. La infracci´on de dichos derechos puede constituir un delito contra la propiedad
intelectual.
First edition: Apr 2016
Digital edition
This book includes illustrations and index.
ISBN 978-84-608-7339-6
©Jos´e Antonio de la Torre las Heras, 2016
©Julio Daniel Dondo Gazzano, 2016
All rights reserved.
    2/127
    Queremos expresar nuestro mas sincero agradecimiento al grupo de investigaci´on ARCO, de la Escuela Superior de Inform´atica de la Universidad de Castilla-La Mancha en Ciudad Real, por su apoyo
en la elaboraci´on de este libro.
Los autores.
    3/127
    A Juan Santiago, Ambar, Bruno, Gabriel, Laureano y Franca...
Julio.
    4/127
    A mi familia por su apoyo incondicional y por creer en mi desde el primer momento. En especial a
mis padres, cuyos consejos y compresi´on han sido un apoyo fundamental en el desarrollo tanto de mi
vida profesional como personal. Agradecer tambi´en a Julio Daniel Dondo y al grupo de investigaci´on
ARCO la confianza depositada en m´ı.
Jos´e Antonio de la Torre las Heras.
    5/127
    6
    6/127
    PREFACIO
El motivo de este libro es dotar al lector de los conocimientos necesarios para
poder dise˜nar sus propios circuitos y aportar soluciones para automatizar y controlar
diferentes procesos tanto dom´esticos, para dom´otica, hobbies, etc., as´ı como tambien
procesos industriales.
Para ello, adoptaremos una metodolog´ıa incremental y lo m´as pr´actica posible, empezando desde lo b´asico como es encender un LED, hasta automatizar una cadena de
montaje y gestionar la misma con diferentes tecnolog´ıas apoy´andonos en el ecosistema
Arduino.
7
    7/127
    8
    8/127
    ´
INDICE GENERAL
1. ¿Qu´e es Arduino? 13
1.1. Elementos de Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2. Primeros pasos en Arduino 19
2.1. Instalando el entorno Arduino . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.1. GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.1.2. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.2. Probando nuestra placa Arduino . . . . . . . . . . . . . . . . . . . . . 20
2.2.1. Conexi´on y configuraci´on de drivers . . . . . . . . . . . . . . . 20
2.2.2. Primer programa . . . . . . . . . . . . . . . . . . . . . . . . . . 21
3. Manejando entradas/salidas 25
3.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2. Utilizando los pines de salida . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.1. Ejemplo 1: Encendiendo un LED . . . . . . . . . . . . . . . . . 26
3.3. Utilizando los pines de entrada . . . . . . . . . . . . . . . . . . . . . . 30
3.3.1. Ejemplo 2: Utilizaci´on de un bot´on o interruptor . . . . . . . . 32
3.3.2. Ejemplo 3: Leyendo temperatura y se˜nales anal´ogicas . . . . . 36
4. Comunicaciones 43
4.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2. Comunicaci´on serie mediante USART . . . . . . . . . . . . . . . . . . 43
4.2.1. Ejemplo 1: Hola mundo por Serie . . . . . . . . . . . . . . . . . 45
4.2.2. Ejemplo 2: Recibiendo informaci´on . . . . . . . . . . . . . . . . 49
4.2.3. Ejemplo 3: Comunicaci´on entre Arduinos . . . . . . . . . . . . 52
4.3. Comunicaci´on I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.1. Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.2. Protocolo I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3.3. Ejemplo 1: Hola mundo mediante I2C . . . . . . . . . . . . . . 60
4.3.4. Ejemplo 2: Obteniendo datos de un IMU . . . . . . . . . . . . 64
4.4. Protocolo SPI (Serial Peripheral Interface . . . . . . . . . . . . . . . . 67
9
    9/127
    ´INDICE GENERAL 10
5. Interrupciones 73
5.1. Interrupciones en el ATmega328 . . . . . . . . . . . . . . . . . . . . . 74
5.2. Manipulaci´on software . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2.1. Librer´ıa avr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2.2. Librer´ıa Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.3. Consideraciones importantes . . . . . . . . . . . . . . . . . . . 77
5.3. Ejemplo 1: Primera rutina de interrupci´on . . . . . . . . . . . . . . . . 78
5.3.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 78
5.3.2. C´odigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.4. Ejemplo 2: Midiendo distancias . . . . . . . . . . . . . . . . . . . . . . 79
5.4.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 80
5.4.2. C´odigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6. Multitasking y Timers 83
6.1. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.1.1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.1.2. Modos de funcionamiento . . . . . . . . . . . . . . . . . . . . . 85
6.1.3. Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2. Multitasking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.1. Encendiendo y apagando un led de manera profesional . . . . . 98
6.2.2. Encendiendo y apagando un led de manera m´as profesional . . 100
A. Construyendo nuestro propio Arduino 103
A.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
A.2. Componentes necesarios . . . . . . . . . . . . . . . . . . . . . . . . . . 103
A.3. Ensamblado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
A.4. Programaci´on del Bootloader . . . . . . . . . . . . . . . . . . . . . . . 107
B. Manipulaci´on de registros 111
B.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
B.2. ¿Qu´e es un registro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
B.3. Operaciones con registros . . . . . . . . . . . . . . . . . . . . . . . . . 113
B.3.1. Activar un bit . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
B.3.2. ORing, activaci´on de un bit . . . . . . . . . . . . . . . . . . . . 114
B.3.3. Bit Shifting, movimiento de bits . . . . . . . . . . . . . . . . . 114
B.3.4. ANDing, desactivando bits . . . . . . . . . . . . . . . . . . . . 116
C. Entorno Eclipse con Arduino 119
C.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
C.2. Qu´e es Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
C.3. Instalaci´on del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
C.4. Configuraci´on del entorno . . . . . . . . . . . . . . . . . . . . . . . . . 121
C.5. Creando el proyecto: ArduinoCore . . . . . . . . . . . . . . . . . . . . 122
C.6. Creando el proyecto final . . . . . . . . . . . . . . . . . . . . . . . . . 124
C.7. Subiendo el proyecto a nuestro Arduino . . . . . . . . . . . . . . . . . 125
    10/127
    ´
INDICE DE FIGURAS
1.1. Placa Arduino Mega 2560 . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1. Instalador Arduino para Windows . . . . . . . . . . . . . . . . . . . . 20
2.2. Editor Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.3. Led y puerto 13 en Arduino Mega 2560 . . . . . . . . . . . . . . . . . 23
3.1. Flujo normal de ejecuci´on para sistemas de control . . . . . . . . . . . 26
3.2. Composici´on de un LED . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.3. Ejemplo 1: Esquema de montaje . . . . . . . . . . . . . . . . . . . . . 28
3.4. Ejemplo 1: Protoboard, esquema de montaje . . . . . . . . . . . . . . 28
3.5. Ejemplo 1: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 29
3.6. Osciloscopio digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.7. Frecuenc´ımetro digital . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.8. Se˜nal digital vs Se˜nal anal´ogica . . . . . . . . . . . . . . . . . . . . . . 33
3.9. Interruptor SPST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.10. Rebote de un bot´on y valores obtenidos . . . . . . . . . . . . . . . . . 34
3.11. M´odulo bot´on Grove . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.12. Ejemplo 2: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 35
3.13. Ejemplo 3: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 40
4.1. Trama con formato 8N1 . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.2. Conexi´on entre dispositivos UART con control de flujo . . . . . . . . . 45
4.3. Representaci´on de comunicaci´on serie . . . . . . . . . . . . . . . . . . . 45
4.4. Ejemplo 1 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 47
4.5. Ejemplo 2 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 50
4.6. Ejemplo 3 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 55
4.7. Esquema de comunicaci´on I2C . . . . . . . . . . . . . . . . . . . . . . 59
4.8. Trama de direcci´on I2C . . . . . . . . . . . . . . . . . . . . . . . . . . 60
4.9. Trama de datos I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
4.10. Sensor MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.11. Conexiones MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
11
    11/127
    ´INDICE DE FIGURAS 12
4.12. Pines SPI en el proc. ATmega328 . . . . . . . . . . . . . . . . . . . . . 68
4.13. Contenidos del registro de entrada del DAC . . . . . . . . . . . . . . . 69
4.14. Forma de onda obtenida conversi´on D/A . . . . . . . . . . . . . . . . . 70
5.1. Ejemplo de indirecci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2. Sensor emisor/receptor ultrasonidos . . . . . . . . . . . . . . . . . . . 80
6.1. Diagrama TIMER 8 bits ATmega328 . . . . . . . . . . . . . . . . . . . 84
6.2. Modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.3. Modo Fast PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.4. Registro TCCR1A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.5. Bits de configuraci´on del presclarer . . . . . . . . . . . . . . . . . . . . 90
6.6. Registro TCCR1B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.7. Bits para configuraci´on del pin de salida . . . . . . . . . . . . . . . . . 91
6.8. Configuraci´on modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.9. Diagrama de bloques del sistema de control . . . . . . . . . . . . . . . 94
6.10. Servo de Grove-Starter Kit for Arduino . . . . . . . . . . . . . . . . . 94
6.11. Diagrama de tiempos de un servo . . . . . . . . . . . . . . . . . . . . . 95
6.12. Ejemplo de programa en ladder . . . . . . . . . . . . . . . . . . . . . . 97
A.1. Protoboard de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . 105
A.2. Esquema de conexiones . . . . . . . . . . . . . . . . . . . . . . . . . . 105
A.3. Patillaje ATmega328 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
A.4. Conectores ICSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
A.5. Captura de pantalla del Arduino IDE . . . . . . . . . . . . . . . . . . 109
B.1. Jerarqu´ıa de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
B.2. Registro de 8 bits (byte) como una ✭✭caja✮✮ . . . . . . . . . . . . . . . . 113
B.3. Operaci´on: 1 << 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
B.4. Activaci´on de pin 3 sobre registro previamente configurado . . . . . . 115
B.5. Configuraci´on del pin 3 como entrada sobre registro previamente configurado (procedimiento err´oneo) . . . . . . . . . . . . . . . . . . . . . 116
B.6. Configuraci´on del pin 3 como entrada sobre registro previamente configurado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
C.1. Pantalla inicial de Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . 120
C.2. Descarga del plugin AVR . . . . . . . . . . . . . . . . . . . . . . . . . 122
C.3. Configuraci´on de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . 123
C.4. Configuraci´on de las librer´ıas . . . . . . . . . . . . . . . . . . . . . . . 124
C.5. Configuraci´on de AVRdude . . . . . . . . . . . . . . . . . . . . . . . . 126
    12/127
    CAP
´
ITULO 1
¿QUE ES ARDUINO?
´
En este libro vamos a describir una serie de dise˜nos y desarrollos pr´acticos para
automatizar el control de sistemas utilizando el entorno Arduino. El entorno Arduino
es una plataforma electr´onica de desarrollo de c´odigo abierto (open source) que ofrece un conjunto de herramientas tanto software como hardware que nos permitir´an
realizar nuestros prototipos de una manera r´apida, sencilla y segura.
Est´a formado principalmente por una placa de desarrollo, equipada con un microcontrolador AVR, puertos de conexi´on de entrada y salida, m´as los elementos
necesarios para programar el microcontrolador, y un ambiente de desarrollo software
con una serie de librer´ıas para construir nuestros dise˜nos.
1.1. Elementos de Arduino
Podemos dividir el entorno Arduino en dos partes bien diferenciadas, por un lado
tenemos un conjunto de soluciones hardware y por otro lado una suite de desarrollo
software.
Hardware
• Placa de desarrollo Arduino: Desde que se lanz´o el primer Arduino
al mercado en 2005 se han dise˜nado numerosas placas de desarrollo por
diferentes empresas de ensamblado, tales como Funduino, FreeDuino. . .
Cada placa est´a compuesta normalmente por un microcontrolador de la
marca Atmel. Dependiendo de la gama, la placa dispondr´a de un modelo de
microcontrolador u otro. En la figura (Figura 1.1) se muestra como ejemplo
el Arduino Mega 2560 que cuenta con un microcontrolador ATmega2560
de 8 bits con una velocidad de 16 MHz adem´as de 86 pines de entrada
salida y 5 puertos de comunicaci´on SPI.
Las placas Arduino son ✭✭libres✮✮ y por lo tanto en Internet se pueden encontrar los esquem´aticos de cada placa e incluso si quieres puedes crearte la
13
    13/127
    1.1. ELEMENTOS DE ARDUINO 14
Figura 1.1: Placa Arduino Mega 2560
tuya propia o a˜nadir funcionalidades a la misma. Diferentes placas de desarrollo pueden verse en la web oficial de Arduino: http://www.arduino.cc/
en/Main/Products .
Esta lista es s´olo una peque˜na selecci´on de las numerosas placas que han
desarrollado en Arduino, si quieres ver la lista completa accede a la web
oficial de Arduino: http://www.arduino.cc/en/Main/Products .
• Tarjetas (Shields) de expansi´on: Unas de las caracter´ısticas que han
hecho que Arduino gane terreno entre otras placas de desarrollo son el
bajo precio y la capacidad de expansi´on que estas placas tienen. En el
mercado existen diferentes tipos de tarjetas de expansi´on para componentes
varios, como controladores de motores, m´odulos de comunicaci´on, etc. En la
p´agina de Arduino se pueden ver algunos shields, aunque existen numerosas
tiendas como Adafruit 1 que nos ofrecen una variedad mayor.
• M´odulos conectar y listo: Adem´as de las shields de expansi´on, Arduino
cuenta con un gran n´umero de m´odulos del tipo ✭✭conectar y listo✮✮. La diferencia con los shields es que estos ´ultimos ´unicamente son interfaces para
diferentes sensores y actuadores de modo que sea m´as sencillo utilizarlos
en nuestros proyectos. Por ejemplo existen m´odulos conectar y listo para
sensores de temperatura, humedad, luz, botones, potenci´ometros, LEDs,
etc.
Software
• Arduino IDE: Adem´as de las placas de desarrollo, Arduino proporciona
un IDE (Entorno de Desarrollo Integrado) basado en processing con el que
podremos dise˜nar nuestros proyectos. Este IDE consta de un editor de texto para escribir nuestros programas, un ´area de mensajes, una consola de
comandos y una barra de herramientas con diferentes men´us. Este entorno
1http://www.adafruit.com/category/17
    14/127
    1.1. ELEMENTOS DE ARDUINO 15 Tipo Procesador Flash EEPROM N´umero de puertos Comunicaciones Arduino Mega ATmega1280 (16MHz) 128KB 4KB 54 digitales, 15 PWM, 16 anal´ogi- cos SPI, I2C, Se- rial(x2) Arduino Mega 2560 ATmega2560 (16MHz) 256KB 4KB 54 digitales, 15 PWM, 16 anal´ogi- cos TWI,SPI, Se- rial(x3) Arduino Board Due AT91SAM3X8E (84MHz) 512 KB 51 digitales, 12 PWM, 12 anal´ogi- cos TWI(x2), Se- rial(x4), SPI, CAN Arduino Yun
a ATmega32u4
(16MHz)
32 KB 1KB 20 digitales, 7
PWM, 12 anal´ogicos
TWI, Serial, SPI,Serial, WiFi
Arduino Micro ATmega32u4
(16MHz)
32KB 1 KB 20 digitales, 7
PWM, 12 anal´ogicos
SPI, Serial, I2C
Arduino Uno ATmega328
(16MHz)
32KB 1KB 14 digitales, 6
PWM, 6 anal´ogicos
I2C, SPI, Serial
Arduino Leonardo ATmega32u4
(16MHz)
32KB 1KB 20 digitales, 7
PWM, 12 anal´ogicos
I2C, SPI, TWI, Serial
aArduino Yun es ligeramente diferente a las dem´as placas, adem´as del microcontrolador ATmega32u4 posee un procesador Atheros AR9331 el cual soporta
OpenWrt, una “distribuci´on” Linux para lo comunicaci´on Ethernet y WiFi
    15/127
    1.1. ELEMENTOS DE ARDUINO 16
se conecta a la placa Arduino para programarla con el c´odigo que hemos
desarrollado y para comunicarse con ella. El c´odigo desarrollado en el Arduino IDE se denomina sketch y es la unidad de c´odigo que es cargado y
ejecutado en una placa Arduino.
• Librer´ıa ✭✭Arduino.h✮✮: Arduino ha conseguido minimizar la curva de
aprendizaje del lenguaje de programaci´on C++ mediante una librer´ıa que
nos evita lidiar al principio con problemas como el uso de operadores
de bits, configuraci´on de registros del microcontrolador, etc. La librer´ıa
✭✭Arduino.h✮✮2 nos ofrece una interface de alto nivel que nos provee de
funcionalidad extra con la cual realizar todas las tareas en nuestro microcontrolador de una manera muy sencilla y descriptiva. Por ejemplo, si
quisi´eramos configurar un GPIO(Pin de Entrada Salida de uso General)
para que actu´e como pin de salida, ´unicamente tendr´ıamos que invocar a la
funci´on pinMode(PIN,OUTPUT). A lo largo de este libro iremos utilizando esta librer´ıa en todos nuestros proyectos y poco a poco, seg´un vayamos
avanzando, veremos la forma equivalente de hacer las mismas operaciones
en C++ ✭✭puro✮✮.
• Atmel Studio: Aunque no es un producto de Arduino conviene nombrarlo
debido a la potencia que tiene esta herramienta. Atmel Studio es un IDE
profesional para los microcontroladores del fabricante Atmel. Al contrario
que el entorno de Arduino, Atmel Studio es m´as complejo y ´unicamente est´a
disponible para la plataforma Windows, pero tiene muchas m´as utilidades
como debugger, gestor de proyectos, etc.
• Arduino Studio: Arduino Studio es el que pretende ser el nuevo entorno
de Arduino. Al contrario del principal IDE de Arduino, este se basa en
una arquitectura modular y no monol´ıtica como as´ı lo hac´ıa su antecesor.
Arduino Studio aprovecha las capacidades de ✭✭Adobe Brackets✮✮. Actualmente se encuentra en versi´on alpha, pero ya cuenta con caracter´ısticas
como:
◦ Sistema basado en la nube.
◦ Soportado por las principales plataformas.
◦ Escrito de cero en Javascript y Node.js.
◦ Interface de usuario amigable.
◦ Autocompletado de c´odigo en tiempo real y documentaci´on en vivo.
◦ Debugger para el M0 Pro.
• Toolchain AVR: En los libros de texto normalmente no se habla de este
componente pero es sin duda el coraz´on de Arduino.
Como hemos dicho anteriormente las placas de desarrollo Arduino est´an
formadas por un microcontrolador de la marca Atmel3, normalmente un
AVR, por lo que podremos utilizar todas las herramientas que nos proporciona Atmel. Por ejemplo, Atmel posee un compilador libre para GNU
llamado avr-g++, el cual es invocado por el IDE Arduino para compilar
el c´odigo C++ de nuestro Sketch. Adem´as, una vez que hemos compilado
2Aunque ✭✭Arduino.h✮✮ es la librer´ıa general, en realidad Arduino se apoya en m´as librer´ıas como
las indicadas en https://www.arduino.cc/en/Reference/Libraries
3Atmel es la empresa desarrolladora de los microcontroladores http://www.Atmel.com
    16/127
    1.1. ELEMENTOS DE ARDUINO 17
el programa, el toolchain nos permite subir el c´odigo m´aquina a nuestra
placa por medio de avrdude, tambi´en libre 4.
El uso de este toolchain nos permite ✭✭saltarnos✮✮ la capa de abstracci´on
que nos proporciona Arduino y programar a m´as bajo nivel, lo que a su
vez nos permitir´a tener una visi´on m´as detallada de la programaci´on de
microcontroladores.
4El toolchain de AVR puede ser descargado de forma gratuita desde http://www.nongnu.org/
avr-libc
    17/127
    1.1. ELEMENTOS DE ARDUINO 18
    18/127
    CAP
´
ITULO 2
PRIMEROS PASOS EN ARDUINO
2.1. Instalando el entorno Arduino
El entorno de desarrollo Arduino est´a programado en Java por lo que es multiplataforma. El entorno lo podemos encontrar en la p´agina oficial de Arduino (http:
//www.arduino.org/en/Main/Software), dependiendo del sistema operativo se deber´a descargar una versi´on u otra. El lector puede preguntarse por qu´e existen varias
versiones si el entorno est´a basado en Java y en ´ultima instancia en la m´aquina
virtual de Java (JVM), esto ocurre porque junto al entorno nos descargamos el toolchain AVR mencionado en la Secci´on 1.1 junto con los drivers de AVR por lo que
dependiendo del sistema operativo tendremos que instalar unos drivers u otros. En
la p´agina de la comunidad de Arduino llamada Arduino Srl (Smart Projects Srl)
(http://www.arduino.org/software)es posible encontrar adem´as del Arduino IDE,
el entorno Arduino Studio que es un ambiente de desarrollo open source basado en el
editor multiplataforma Brackets de Adobe.
2.1.1. GNU/Linux
Los usuarios de GNU/Linux pueden encontrar el entorno Arduino en sus gestores
de paquetes, por ejemplo, si tenemos una distribuci´on ✭✭Debian✮✮, para instalar todo
el entorno ´unicamente tendremos que poner los siguientes comandos en la terminal
como ✭✭superusuario✮✮:
apt - get install arduino arduino - core
Es importante a˜nadir a nuestro usuario a los grupos: uucp, lock y dialout, para poder
manejar los puertos series y los puertos USB de nuestro sistema GNU/Linux. Para
a˜nadir nuestro usuario a los grupos ´unicamente ingresaremos el siguiente comando:
gpasswd -a $USER uucp
gpasswd -a $USER lock
Con esto ya tendremos nuestro entorno listo.
19
    19/127
    2.2. PROBANDO NUESTRA PLACA ARDUINO 20
Figura 2.1: Instalador Arduino para Windows
2.1.2. Windows
En Windows una vez que nos hemos descargado el instalador para nuestra arquitectura, tendremos que ejecutar el ✭✭.exe✮✮ y nos aparecer´a una ventana como la de la
Figura 2.1 en donde se nos ir´a guiando para completar la instalaci´on.
2.2. Probando nuestra placa Arduino
2.2.1. Conexi´on y configuraci´on de drivers
Una vez que tenemos instalados los drivers de la placa y el entorno de desarrollo Arduino, ya podemos probar que nuestro entorno de desarrollo est´e funcionando
correctamente con un simple ejemplo de prueba. Lo primero que debemos hacer es conectar nuestra placa al ordenador mediante un cable USB A/B. Con este mismo cable
podremos alimentar nuestro Arduino, gracias a los 5V que nos proporciona nuestro
ordenador por el puerto USB.
Una vez conectado el Arduino en entornos Windows nos aparecer´a un mensaje
mostrando el proceso de configuraci´on y b´usqueda de drivers, Windows nos informar´a
si todo va bien y ya estaremos en disposici´on de ejecutar nuestro IDE.
En los entornos GNU/Linux podremos saber si nuestro Arduino ha sido reconocido
correctamente mediante el comando:
dmesg | tail
Este comando en realidad se compone de dos ´ordenes enlazadas mediante una tuber´ıa
o Pipe. La primera orden indica al Kernel que nos informe de todo lo sucedido desde
el arranque, mientras que la orden tail nos permite obtener las ´ultimas l´ıneas del
registro.
    20/127
    2.2. PROBANDO NUESTRA PLACA ARDUINO 21
Figura 2.2: Editor Arduino
Si el Kernel ha reconocido el Arduino veremos algo como: detected FT232RL o Product: Arduino XXX donde XXX puede variar en funci´on de la placa que tengamos,
por ejemplo, para una placa UNO aparecer´a Product: Arduino UNO.
2.2.2. Primer programa
Nuestro primer programa ´unicamente nos servir´a para comprobar que durante
todo el Workflow no se genera ning´un error, es decir, podemos programar, compilar
y ✭✭subir✮✮ sketchs sin ning´un problema.
Lo primero que tenemos que hacer es abrir el IDE, una vez que lo tengamos abierto
iremos a: “Archivo, Nuevo” con lo que se nos abrir´a un editor como el de la Figura 2.2
.
A continuaci´on, copia el c´odigo Cod. 2.1
Este programa lo ´unico que hace es encender y apagar repetidamente un LED
conectado al pin de entrada/salida 13.
Los programas desarrollados para Arduino constan en su forma m´as b´asica de dos
funciones: una funci´on setup(), donde las variables y los pines a utilizar se declaran e
    21/127
    2.2. PROBANDO NUESTRA PLACA ARDUINO 22
1 int led = 13;
2 void setup () {
3 pinMode ( led , OUTPUT );
4 }
5
6 void loop () {
7 digitalWrite ( led , HIGH );
8 delay (1000) ;
9 digitalWrite ( led , LOW );
10 delay (1000) ;
11 }
C´odigo 2.1: Primer ejemplo
inicializan y una funci´on loop() donde se define el c´odigo a implementar. Es importante
recordar que los pines se pueden configurar como Entradas o Salidas dependiendo del
uso que se le den. En este caso es necesario definir al pin 13 como de salida (OUTPUT),
como est´a indicado en la l´ınea 3 del c´odigo. Seguidamente en las l´ıneas 7 y 9 alternamos
el estado del pin pas´andolo de estado alto (HIGH) a estado bajo (LOW). La funci´on
delay() retarda la ejecuci´on de la instrucci´on siguiente en la cantidad de milisegundos
indicado por el valor entre par´entesis, en nuestro caso 1000 ms.
Para ver el funcionamiento de este dise˜no el lector deber´a conectar un LED entre
el pin 13 y tierra. Generalmente es necesario conectar una resistencia de 470 Ohms
para limitar la corriente y evitar que el LED se queme. En este caso no har´a falta ya
que este pin ya tiene una resistencia incorporada.
El pin y el led de prueba est´an se˜nalados en la Figura 2.3.
Ahora ´unicamente nos resta seleccionar el modelo de placa que estemos usando,
compilar el c´odigo y subirlo a nuestra placa de desarrollo. Para ello iremos a la pesta˜na: “Herramientas, Tarjeta” y seleccionamos nuestra placa, despu´es tenemos que
habilitar el puerto serie para programar el Arduino, en esta ocasi´on, navegaremos hasta“Herramientas, Puerto Serial” y seleccionaremos el puerto USB de nuestra placa.
Normalmente ´unicamente tendremos un puerto conectado, pero si dispusi´eramos de
varios tendr´ıamos que ver cu´al corresponde a la placa seg´un el sistema operativo.
Si en GNU/Linux no aparece ning´un puerto serial es probable que el usuario no
tenga los permisos adecuados. En este caso revise cada uno de los pasos mencionados
en Subsecci´on 2.1.1.
Una vez hecho esto vamos a compilar y subir el c´odigo a la placa, para ello vamos a
la pesta˜na: “Sketch, Verificar/Compilar” o simplemente pulsamos ✭✭Ctrl-R✮✮ que har´a
que el IDE primero verifique la sintaxis del c´odigo y si no hay errores ✭✭suba✮✮ el mismo
a la placa.
Si todo va bien podremos ver como el LED de prueba conmuta cada segundo.
    22/127
    2.2. PROBANDO NUESTRA PLACA ARDUINO 23
Figura 2.3: Led y puerto 13 en Arduino Mega 2560
    23/127
    2.2. PROBANDO NUESTRA PLACA ARDUINO 24
    24/127
    CAP
´
ITULO 3
MANEJANDO
ENTRADAS/SALIDAS
3.1. Introducci´on
En este cap´ıtulo vamos a trabajar con la placa de desarrollo Arduino enfoc´andonos principalmente en el manejo de sus entradas y salidas. En la siguiente secci´on de
este cap´ıtulo (Secci´on 3.2) abordaremos las diferentes t´ecnicas para controlar actuadores desde Arduino. M´as adelante, en la Secci´on 3.3 veremos como obtener datos del
exterior, tanto anal´ogicos como digitales.
Para simplificar en la medida de lo posible el montaje de los circuitos y centrarnos la mayor parte del tiempo en la programaci´on del Arduino, utilizaremos un kit
de iniciaci´on llamado Grove Starter Kit for Arduino de la marca Seeed el cual se
puede adquirir por Internet a un precio relativamente bajo.1. Adem´as del kit Grove,
utilizaremos el kit de expansi´on Sidekick Basic Kit for Arduino V2.
2
3.2. Utilizando los pines de salida
Normalmente, a la hora de dise˜nar cualquier sistema de control nos encontramos
con una secuencia de ejecuci´on como la mostrada en Figura 3.1.
Como se observa en la Figura 3.1 una parte esencial en un sistema de control son
los actuadores, pero ¿Qu´e es un actuador?. La definici´on formal de actuador es la
siguiente:
1El kit se puede obtener desde: http://www.seeedstudio.com/depot/
Grove-Starter-Kit-for-Arduino-p-1855.html
2Este kit puede se puede obtener desde: http://www.seeedstudio.com/depot/
Sidekick-Basic-Kit-for-Arduino-V2-p-1858.html?cPath=84_13
25
    25/127
    3.2. UTILIZANDO LOS PINES DE SALIDA 26
Figura 3.1: Flujo normal de ejecuci´on para sistemas de control
Actuador
Dispositivo capaz de transformar la energ´ıa neum´atica, el´ectrica o hidr´aulica en
la activaci´on de un proceso con la finalidad de generar un efecto sobre un proceso
automatizado.
Un ejemplo claro es un motor de un robot. Un motor de un robot cumple con los
dos elementos principales de la definici´on. Por un lado transforma la energ´ıa el´ectrica
en un efecto (movimiento del robot) y por otro lado, este movimiento se realiza sobre
un proceso automatizado (l´ogica del robot). Al igual que el ejemplo del robot, existen
otros muchos actuadores. Se deja al lector la tarea de buscar en la red m´as informaci´on
sobre los diferentes tipos.
El objetivo de esta secci´on es controlar estos actuadores, de modo que podamos
actuar sobre el entorno desde Arduino.
La manera m´as inmediata de controlar un actuador en Arduino es mediante los
pines de entrada/salida. La cantidad de pines disponibles depende del tipo de placa
que se utilice. Por ejemplo, en la placa ATmega328 que ✭✭montamos✮✮ en la Secci´on A.1
tenemos un total de 23 pines que pueden ser configurados c´omo entrada o salida.
Veamos algunos ejemplos:
3.2.1. Ejemplo 1: Encendiendo un LED
En este primer ejemplo, veremos c´omo encender un LED, aunque ya se vio de
manera r´apida en Subsecci´on 2.2.2 en esta secci´on lo veremos de manera mas detallada.
El LED (Figura 3.2) es un elemento notificador muy utilizado a d´ıa de hoy. Una de las
razones que han llevado a la industria a utilizar estos dispositivos es el bajo consumo
    26/127
    3.2. UTILIZANDO LOS PINES DE SALIDA 27
Figura 3.2: Composici´on de un LED
que requieren, aproximadamente entre 10 mA y 20 mA. Aunque aqu´ı utilizaremos el
LED ´unicamente con el prop´osito de notificar o avisar sobre un evento. Hoy en d´ıa su
uso se ha extendido a otras ´areas como la de la iluminaci´on e incluso la comunicaci´on.
En las placas oficiales de Arduino se incorpora un LED notificador conectado
al pin 13. Este LED ya tiene una resistencia limitadora de corriente, por lo que
no tendremos que preocuparnos por el consumo de corriente. En el caso de que el
lector haya montado el Arduino siguiendo la gu´ıa del Secci´on A.1, entonces deber´a
conectar el LED y la resistencia al pin 13. Para identificar la posici´on de cada uno
de los pines se puede consultar la hoja de datos del ATmega que se puede descargar
desde el siguiente enlace: 3. Si est´a utilizando una placa con otro microcontrolador,
busque la hoja de datos correspondiente. En el apartado 1 del datasheet veremos los
diferentes encapsulados en los cuales el fabricante nos ofrece el microcontrolador. El
encapsulado utilizado en Secci´on A.1 es el 28 PDIP (Plastic Dual In-line Package).
En este encapsulado el pin 13 se encuentra en la esquina superior derecha teniendo
en el norte el semic´ırculo de referencia. Adem´as del datasheet, para saber el mapeado
de pines es necesario ir al esquem´atico de cada placa y buscar a que pin de la librer´ıa
corresponde cada pin f´ısico.4
En el caso del Arduino realizado en la Secci´on A.1, el montaje debe ser parecido
al mostrado en: Figura 3.3, Figura 3.4.
Tabla de entrada/salida
Para obtener unos dise˜nos fiables, seguros y de calidad se deben seguir procedimientos y normas de calidad de dise˜no. Por ello, aunque este ejemplo sea sencillo,
seguiremos una metodolog´ıa a la hora de afrontar el problema.
El primer paso en cualquier dise˜no es analizar los elementos de control en base a las
especificaciones. En nuestro caso ´unicamente tenemos un ´unico elemento a controlar
(el LED) por lo que este paso se podr´ıa obviar, sin embargo documentar es una buena
costumbre en cualquier dise˜no. En la Tabla 3.1 se puede ver un ejemplo de an´alisis de
elementos de control. En primer lugar, se indica si el elemento es de entrada o salida.
En segundo lugar, se realiza una descripci´on sobre el elemento, de modo que siempre
se sepa la funci´on del mismo. Por otro lado, se indica el nombre de la variable que se
3http://goo.gl/S3oBA2
4Los esquem´aticos se pueden encontrar en la p´agina de Arduino o en el caso de una placa ensamblada por otro fabricante en la p´agina del mismo.
    27/127
    3.2. UTILIZANDO LOS PINES DE SALIDA 28
Figura 3.3: Ejemplo 1: Esquema de montaje
5v-12v
16MHz Cristal
Azul = Tierra
Rojo = Entrada
Naranja = 5v estables
Cyan = Cristal
------------------- Marron = Rx(Arduino) - Tx(FTDI)
Morado = Tx(Arduino) - Tx(FTDI)
Figura 3.4: Ejemplo 1: Protoboard, esquema de montaje
    28/127
    3.2. UTILIZANDO LOS PINES DE SALIDA 29
Cuadro 3.1: Ejemplo 1: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Salida Conmutar cada
1/2 segundo
led 13
Figura 3.5: Ejemplo 1: Diagrama de flujo
utilizar´a en el c´odigo para hacer referencia a dicho elemento y por ´ultimo el pin f´ısico
al cual se conectar´a.
Diagrama de flujo
En base al diagrama general mostrado en la Figura 3.1 al que normalmente se
adapta cualquier dise˜no, vamos a realizar el diagrama correspondiente para nuestro
dise˜no.
La herramienta utilizada para crear dicho diagrama es DIA una herramienta de
c´odigo abierto, gratuita y que permite realizar diagramas de diversos tipos como por
ejemplo diagramas de clases UML o diagramas de flujo.
En la Figura 3.5 se puede ver un ejemplo de diagrama de flujo para nuestro caso.
Este ejemplo no es la ´unica manera de representar nuestro dise˜no. Un mismo problema
puede tener diferente soluciones correctas.
C´odigo
Ahora que tenemos toda la documentaci´on lista y se ha analizado el problema es
el momento de realizar el dise˜no de la soluci´on y su implementaci´on. Para programar
la placa Arduino utilizaremos el entorno de programaci´on instalado descrito en el
Cap´ıtulo 1. Como se adelant´o en el cap´ıtulo anterior todo programa que vaya a ser
ejecutado en el entorno Arduino debe tener dos funciones principales y obligatorias:
setup(void): Es la primera funci´on que se ejecuta tras cargar el programa. En
    29/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 30
1 int led = 13; // De acuerdo a la tabla de entrada / salidas
2
3 void setup () {
4
5 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada )
6 pinMode ( led , OUTPUT );
7
8 }
9
10 void loop () {
11 // digitalWrite " pone " el pin ( led ) en estado HIGH 5 v o LOW 0 v
12 digitalWrite ( led , HIGH );
13 // delay bloquea el m i c r o c o n t r o l a d o r hasta que pasen x ms
14 // delay se implementa mediante el uso del TIMER0 por lo que
15 // si usamos ese TIMER tendremos problemas con esta funcion
16 delay (500) ;
17 digitalWrite ( led , LOW ) ;
18 delay (500) ;
19
20 }
C´odigo 3.1: Ejemplo 1: Encendiendo y apagando un led
esta funci´on se suelen realizar tareas como la inicializaci´on de variables, pines,
comunicaciones, informaci´on de inicio al usuario, tareas de login. etc.
loop(void): Tambi´en llamado bucle principal del programa, se ejecuta de forma constante. Cada pasada por el bucle se suele llamar un ciclo de scan. En
esta funci´on es donde programaremos la l´ogica siguiendo el diagrama general
mostrado en Figura 3.1.
Es importante mantener un orden en la funci´on loop. Un mal encapsulamiento
funcional puede hacer que nuestro programa sea poco mantenible y finalmente fracase.
El mismo programa mostrado en Cod. 3.1 puede ser reescrito utilizando funciones
de modo que sea mucho m´as sencillo de leer tal y como se muestra en: Cod. 3.2.
En este nuevo c´odigo se utilizan dos funciones ademas de las funciones setup() y
loop() b´asicas: la funci´on read sensors() y la funci´on perform actions() La funci´on
read sensors() es una funci´on que se encarga de la lectura de los sensores para que,
de acuerdo a sus valores, ejecutar las actuaciones correspondientes. En este caso es
una funci´on vac´ıa.
La segunda contiene el c´odigo de encendido y apagado de los LEDs.
Como se puede observar, el c´odigo es muy simple y legible. Gracias a la librer´ıa
✭✭Arduino.h✮✮, para configurar un pin como entrada o salida ´unicamente tenemos que
llamar a la funci´on pinMode() indicando en sus par´ametros tanto el pin como el modo.
Por otro lado, para obtener 5V o 0V en el pin, usaremos la funci´on digitalWrite().
3.3. Utilizando los pines de entrada
En la Subsecci´on 3.2.1 vimos como utilizar los pines de entrada/salida del ATmega328 para informar al usuario de una acci´on mediante un LED o de forma m´as
    30/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 31
1 int led = 13; // De acuerdo a la tabla de entrada / salidas
2
3 void setup () {
4
5 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada )
6 pinMode ( led , OUTPUT );
7
8 }
9
10 void read_sensors () {
11 // funcion dummy
12 }
13
14 void perform_actions () {
15 // digitalWrite " pone " el pin ( led ) en estado HIGH 5 v o LOW 0 v
16 digitalWrite ( led , HIGH );
17 // delay bloquea el m i c r o c o n t r o l a d o r hasta que pasen x ms
18 // delay se implementa mediante el uso del TIMER0 por lo que
19 // si usamos ese TIMER tendremos problemas con esta funcion
20 delay (500) ;
21 digitalWrite ( led , LOW ) ;
22 delay (500) ;
23 }
24
25 void loop () {
26 read_sensors () ;
27 perform_actions () ;
28 }
C´odigo 3.2: Ejemplo 1: Recodificaci´on utilizando funciones
    31/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 32
Figura 3.6: Osciloscopio digital
gen´erica como interaccionar con un actuador. En esta secci´on, veremos todo lo contrario, es decir, aprenderemos como captar una acci´on del usuario (por ejemplo la
pulsaci´on de un bot´on).
Los pines de entrada salida, como su nombre indica, se pueden utilizar como
entrada o como salida. La selecci´on del modo de funcionamiento de cada pin se realiza
mediante unos registros hardware. En estos primeros ejemplos no se ver´a el manejo de
dichos registros. Sin embargo, en la Secci´on B.1 el lector puede estudiar como utilizar
dichos registros mediante operaciones de desplazamiento de bits.
A la hora de captar una acci´on o magnitud en un microcontrolador lo primero
que debemos preguntarnos es qu´e tipo de magnitud necesitamos leer o captar. De
forma gen´erica y simplificando, existen dos grandes grupos en los cuales se clasifican
las se˜nales (Figura 3.8):
1. Se˜nales anal´ogicas: Las se˜nales anal´ogicas son continuas en el tiempo, es
decir, se puede representar mediante una funci´on matem´atica continua. ¿Qu´e
elementos de la se˜nal anal´ogica se pueden medir? principalmente se analizan dos
variables: la amplitud y el periodo o frecuencia. Ejemplos de se˜nales anal´ogicas
son el sonido, se˜nales de radio frecuencia, se˜nales electromagn´eticas.
En electr´onica para medir las se˜nales anal´ogicas se utilizan instrumentos como
los osciloscopios (ver Figura 3.6) o frecuencimetro (ver Figura 3.7). Los osciloscopios nos permiten desde visualizar en una pantalla la forma de onda hasta
realizar funciones matem´aticas como la FFT (transformada r´apida de fourier).
2. Se˜nales digitales: Las se˜nales digitales son aquellas en las cuales sus valores
est´an completamente discretizados. Aunque cualquier fen´omeno electromagn´etico como el producido al realizar contacto en un pulsador es continuo, sus estados
pueden ser discretizados en funci´on del n´umero de bits utilizados en la conversi´on, en este caso dos estados (pulsado y no pulsado).
3.3.1. Ejemplo 2: Utilizaci´on de un bot´on o interruptor
En este ejemplo nos centraremos en las se˜nales digitales. M´as adelante hablaremos
sobre las se˜nales anal´ogicas y veremos diferentes casos de uso.
Un bot´on o mejor dicho y en sentido m´as amplio, un interruptor el´ectrico es un
dispositivo con dos estados: en uno de estos estados (cerrado) se permite la circulaci´on
    32/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 33
Figura 3.7: Frecuenc´ımetro digital
0000
0010
0100
0110
1000
1010
1100
Señal
continua
Señal
discreta
Tiempo
Tiempo
Figura 3.8: Se˜nal digital vs Se˜nal anal´ogica
    33/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 34
Figura 3.9: Interruptor SPST
1 1 1 1 X 1 0 1 0 X 0 0 0 0 0
VOL
VIL
VIH
VOH
Rebote
Valor lógico alto
Indeterminado
Valor lógico bajo
Figura 3.10: Rebote de un bot´on y valores obtenidos
de corriente y en el otro estado se impide su paso (abierto). En los circuitos electr´onicos
puedes encontrarlo representado con diferentes s´ımbolos. Sin embargo, el m´as com´un
es el que se muestra en la Figura 3.9. Existen muchos tipos de interruptores en funci´on
de los polos, v´ıas, etc, pero nosotros utilizaremos los simples, es decir, de un polo y
una v´ıa.
Los interruptores como cualquier dispositivo mec´anico son imperfectos y tienen
un desgaste por el propio uso. Aunque parezca que en un dise˜no esto no es necesario
tenerlo en cuenta, una mala elecci´on puede que haga que nuestro dise˜no tenga una
duraci´on menor a la estimada y por lo tanto, se incremente el coste de mantenimiento.
Adem´as, hay que tener en cuenta que los interruptores tiene un efecto de rebote,
dicho efecto puede provocar que nuestro programa trabaje con valores falsos. Con
el objetivo de eliminar estos valores intermedios se utilizan circuitos eliminadores de
rebotes (debounce) como el que se muestra en la figura Figura 3.10.
Otro m´etodo es realizar una m´aquina de estados que detecte varias lecturas consecutivas con el mismo valor de modo que todos los valores intermedios no se tengan
en cuenta. En la Figura 3.10 puedes ver una gr´afica que ilustra lo comentado.
Para simplificar el dise˜no y centrarnos en la programaci´on del microcontrolador,
en este ejemplo utilizaremos uno de los m´odulos ✭✭conectar y listo✮✮ del kit Groove.
En Figura 3.11 se puede ver el aspecto del m´odulo. Dicho modulo nos simplifica el
montaje dado que lleva incorporada la resistencia de pull-down anteriormente citada.
Figura 3.11: M´odulo bot´on Grove
    34/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 35
Cuadro 3.2: Ejemplo 2: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Salida Led notificador de
pulsaci´on
led 13
Entrada Pulsador button 12
Figura 3.12: Ejemplo 2: Diagrama de flujo
Tabla de entrada/salida
En este ejemplo utilizaremos el c´odigo de la Subsecci´on 3.2.1 de modo que cuando
el pulsador se encuentre accionado se encender´a el LED y cuando el pulsador no se
encuentre pulsado el LED se apagar´a. Como en el ejemplo anterior, lo primero que hay
que realizar en el dise˜no es una tabla con las entradas y salidas que utilizaremos. Como
se puede ver en Tabla 3.2 tenemos dos elementos. Por un lado, el LED notificador
que vimos en el ejemplo anterior y por otro lado, el pulsador como entrada.
Diagrama de flujo
En el diagrama de flujo mostrado en Figura 3.12 se muestra un elemento nuevo, el
condicional. En este caso en funci´on del estado de un elemento (condici´on) actuaremos
de una manera o de otra.
C´odigo
Con el diagrama estudiado, trasladar la soluci´on a c´odigo es muy sencillo cuando se tiene un poco de pr´actica. Como dijimos en la Subsecci´on 3.2.1 tenemos dos
    35/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 36
partes diferenciadas, el setup() y el loop(). En el setup() se ha a˜nadido la configuraci´on del bot´on como entrada mediante la funci´on pinMode() con el correspondiente
par´ametro INPUT. Un punto importante a tener en cuenta cuando configuramos un
pin como entrada es la capacidad del microcontrolador para activar resistencias de
pull-down internas. Mediante las resistencias de pull-down internas no es necesario
que agreguemos una resistencia para evitar los rebotes. Para habilitar las resistencias
de pull-down, se debe utilizar la funci´on digitalWrite() dentro del setup() y sobre un
pin configurado anteriormente como entrada.
Una vez configurados los pines de entrada/salida, debemos programar la l´ogica
del bucle. Si revisas el Cod. 3.2 creamos dos funciones dummy5: read sensors() y
perform actions(). En este caso utilizaremos tambi´en la funci´on read sensors() en
la cual leeremos el valor del bot´on mediante la funci´on digitalRead(). Esta funci´on
devuelve HIGH o LOW en funci´on del valor del pin 5v o 0v.
Una vez que hemos le´ıdo el valor del pin de entrada mediante la funci´on digitalRead() s´olo falta implementar la toma de decisiones en funci´on de ese valor le´ıdo. Para
ello usamos la funci´on perform actions() que consulta sobre el valor de la variable y
si es igual a HIGH (bot´on pulsado) se enciende el LED, de lo contrario se apaga.
Como puedes observar tratar con se˜nales digitales es muy sencillo. Existen sensores
como el sensor de temperatura DHT11 que aun monitorizando una se˜nal anal´ogica
(temperatura) son capaces de ofrecer dicha informaci´on como un valor digital. M´as
adelante veremos c´omo se consigue esto y de qu´e manera lo podemos utilizar en los
dise˜nos con Arduino.
3.3.2. Ejemplo 3: Leyendo temperatura y se˜nales anal´ogicas
Hasta ahora hemos trabajado con se˜nales digitales. Normalmente el entorno que
nos rodea es continuo y sus efectos se manifiestan de forma anal´ogica. Un ejemplo
podr´ıa ser la energ´ıa que desprendemos cuando tenemos calor o incluso el sonido que
producimos al hablar. Como puedes imaginar la necesidad de analizar y controlar
estas magnitudes est´a presente en el dise˜no con microcontroladores. En esta secci´on
veremos c´omo leer la temperatura ambiente y un ejemplo de actuaci´on en funci´on de
dichos valores.
Fundamentos
En el mercado existen sensores de muchos tipos, los hay digitales como por ejemplo
los sensores de paso y tambi´en los hay anal´ogicos como los sensores de temperatura.
Normalmente, estos sensores producen se˜nales de un voltaje bajo. Para poder tratar
con dichas se˜nales se suelen realizar diferentes etapas de amplificaci´on y procesamiento.
Como seguramente ya sepas, los computadores y los microcontroladores ´unicamente trabajan con bits, es decir, d´ıgitos que ´unicamente pueden tomar el valor 1
o 0. Para convertir una se˜nal anal´ogica a una se˜nal digital se emplean los llamados
✭✭Conversores Anal´ogicos Digitales✮✮ o de forma abreviada ADC. Si lo que queremos
es producir una se˜nal anal´ogica a partir de una digital se utilizan los ✭✭Conversores
Digitales Anal´ogicos✮✮ o de forma abreviada DAC. La teor´ıa que hay detr´as de estos
5Las funciones dummy son aquellas que no tienen ninguna utilidad pero que se incorporan al
c´odigo por alguna raz´on, normalmente por estandarizaci´on
    36/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 37
1 int led = 13; // De acuerdo a la tabla de entrada / salidas
2 int button = 12; // De acuerdo a la tabla de entrada / salidas
3
4 int button_state ; // Estado del boton
5
6 void setup () {
7
8 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada )
9 pinMode ( led , OUTPUT );
10 pinMode ( button , INPUT );
11
12 }
13
14 void read_sensors () {
15 button_state = digitalRead ( button );
16 }
17
18 void perform_actions () {
19 if( button_state == HIGH ){
20 // HIGH es una constante equivalente a 5 v
21 digitalWrite ( led , HIGH );
22 } else {
23 digitalWrite ( led , LOW ) ;
24 }
25 }
26
27 void loop () {
28 read_sensors () ;
29 perform_actions () ;
30 }
C´odigo 3.3: Ejemplo 2: C´odigo para la utilizaci´on de un bot´on
    37/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 38
dispositivos es muy extensa y no es objeto de este libro el desarrollarla en profundidad. A continuaci´on se dar´a una breve explicaci´on sobre dos de los par´ametros claves
a la hora de identificar si nuestro microcontrolador es adecuado para una determinada
se˜nal.
N´umero de bits: El n´umero de bits del conversor se puede ver como la resoluci´on del mismo. Un ejemplo ilustrar´a de forma clara este concepto. Imaginemos
que tenemos una sensor que produce valores desde 0V hasta 5V y que tenemos
un ADC de 10 bits (como es el caso del ATmega328) ¿cu´al es la resoluci´on
m´axima con la que se podr´ıa trabajar en el dise˜no?. El calculo es sencillo. Si
el conversor codifica los valores con 10 bits, significa que puede tomar valores
entre 0 y 1023 (210 − 1) por lo tanto si dividimos los 5v (rango 5-0) entre los
1024 valores (rango 0-1023) tenemos una divisi´on de 0.0049 o lo que es lo mismo
4,9 mV por cada valor. De este modo si obtenemos una lectura con el valor 53,
significar´a que el sensor ha generado 259 mV.
Velocidad: La velocidad de muestreo es la capacidad del conversor para tratar
con se˜nales de una frecuencia determinada. Seg´un el teorema de muestreo, la
velocidad de muestreo debe ser como m´ınimo el doble del ancho de banda de la
se˜nal de entrada.
Aunque estos dos par´ametros son muy importantes a la hora de seleccionar un
conversor, como ya hemos dicho existen muchos par´ametros est´aticos y din´amicos
que influyen en la elecci´on de los mismos.
Otro de los aspectos que debemos tener en cuenta a la hora de realizar una conversi´on es el valor de referencia. Como ya hemos explicado mediante el n´umero de
bits, la resoluci´on de nuestra conversi´on estar´a dada por el n´umero de bits del conversor pero tambi´en hablamos de un rango fijo de 5 V. Este rango fijo se llama voltaje
de referencia. ¿Para qu´e sirve este voltaje?. Imaginemos que tenemos un sensor que
´unicamente aporta valores entre 0 y 1.5 voltios o que en el entorno en el que estamos,
el m´ınimo valor de un sensor de temperatura es 1.5 y el m´aximo 3.5. Como puedes
observar los voltajes de referencia var´ıan. La soluci´on planteada anteriormente de
usar un voltaje de referencia de 5 voltios es v´alida para ambos casos pero estaremos
perdiendo resoluci´on dado que el Arduino est´a teniendo en cuenta valores que son
imposibles de alcanzar (desde 1.5 V - 5 V). Para solucionar esta situaci´on en Arduino
poseemos una funci´on llamada analogReference() que nos permite variar el voltaje de
referencia pudiendo pasar los siguientes valores como par´ametro:
DEFAULT: Voltaje de referencia de 5 voltios para placas de 5V y 3.3 para
placas de 3.3 voltios.
INTERNAL: Dependiente del microcontrolador. En los microcontroladores
ATmega168 y 328 este voltaje es de 1.1, sin embargo, en los microcontroladores
ATmega8 es de 2.56 voltios.
INTERNAL1V1: Referencia de 1.1V (solo para los Arduino Mega)
INTERNAL2V56: Referencia de 2.56V (solo para los Arduino Mega)
EXTERNAL: Voltaje entre 0 y 5 voltios aplicados a la entrada AREF del
Arduino
    38/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 39
Cuadro 3.3: Ejemplo 2: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Salida Led notificador de
m´ınima temperatura
led 13
Entrada Sensor de temperaturatemperature sensor A1
Si seleccionamos alguno de estos valores es muy importante que se realice la
llamada a la funci´on analogReference() antes de cualquier llamada a la funci´on analogRead() de lo contrario podremos da˜nar la placa.
Si el par´ametro pasado es EXTERNAL se deber´a aplicar un voltaje de entrada al
pin AREF. Existen diferentes maneras de aplicar dicho voltaje. Una manera sencilla
y estable es un diodo zener del valor requerido. Por otro lado, podemos utilizar un
divisor de tensi´on. Hay que tener en cuenta que Arduino posee una resistencia de
32 Kohms interna por lo que la formula para el divisor de tensi´on quedar´a como se
muestra en la Ecuaci´on 3.1. Teniendo en cuenta esto con una resistencia de 75 Khoms
y un voltaje de entrada de 5v aproximadamente obtendr´ıamos un valor de referencia
de 1.5V lo que nos permitir´ıa tener una resoluci´on de 1,5V /1024 = 1,5mV frente a
los 5V /1024 = 4,88mV anteriores.
V Ref = V in ∗
32K
32K + X
(3.1)
Tabla de entrada/salida
Para este ejemplo utilizaremos el m´odulo conectar y listo de Grove termostato.
Existen numerosos sensores de temperatura. Algunos como por ejemplo el DHT11 o
el DHT22 permiten leer la temperatura ´unicamente mediante se˜nales digitales. En
este caso hemos decidido utilizar el modulo de Grove porque ya tiene incorporado los
condensadores de filtro necesarios y diversas protecciones. Adem´as simplifica mucho el
dise˜no y como ya dijimos en otros ejemplos, nos permite centrarnos en la programaci´on
del microcontrolador.
Como siempre lo primero que debemos realizar es la tabla de entradas y salidas
de tal modo que nuestro dise˜no est´e siempre documentado. En este ejemplo vamos a
utilizar un termostato y un LED notificador Tabla 3.3.
Diagrama de flujo
El diagrama de flujo es fundamental en cualquier dise˜no, muchas herramientas
como Scratch for Arduino son capaces de generar todo el c´odigo a partir de un buen
diagrama de flujo. En este ejemplo ´unicamente buscamos encender un LED cuando
pase de la temperatura m´ınima configurada. El diagrama resultante es el mostrado
en la Figura 3.13
Como se puede observar en el diagrama de la Figura 3.13 hay dos procesos a la
hora de captar la temperatura. El primero obtiene la temperatura en formato RAW,
    39/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 40
Figura 3.13: Ejemplo 3: Diagrama de flujo
es decir, un valor sin tratar. Una vez que tenemos dicho valor tenemos que realizar un
proceso de conversi´on (normalmente especificado por el fabricante) mediante el cual
el valor RAW se convierte en un valor ´util para el usuario. En Figura 3.3.2 se ver´a
como realizar dicha conversi´on.
C´odigo
Esta soluci´on a˜nade a los dem´as ejemplos el tratamiento de la se˜nal anal´ogica por
lo que ´unicamente se har´a ´enfasis en dicha parte. En la secci´on setup configuramos
el pin A1 como entrada. Arduino no necesita especificar que dicho pin se comportar´a
como un pin anal´ogico, por lo que este procedimiento es exactamente igual al realizado
con el bot´on en Subsecci´on 3.3.1. En la funci´on read sensors() se realiza la lectura del
sensor conectado a la entrada A1 del Arduino. La funci´on analogRead() devuelve un
valor entre 0 y 1023 tal y como se explic´o en los fundamentos de este ejercicio.
Una vez que tenemos el valor RAW hay que realizar una conversi´on a un valor en
grados celsius. Para ello hay que utilizar una f´ormula especificada por el fabricante en
el datasheet. Cada sensor utiliza una conversi´on diferente por lo que este paso var´ıa
de un sensor a otro, incluso aunque el sensor capte la misma magnitud.
La funci´on perform actions() ´unicamente se encarga de comprobar si la temperatura es menor al m´ınimo en cuyo caso activar´a el led.
    40/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 41
1 int led = 13; // De acuerdo a la tabla de entrada / salidas
2 int temperature_sensor = A0 ; // De acuerdo a la tabla de
3 // entrada / salidas
4
5 float temperature_celsius ;
6 float min_temperature = 25;
7
8 void setup () {
9
10 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada )
11 pinMode ( led , OUTPUT );
12 pinMode ( temperature_sensor , INPUT );
13 Serial . begin (9600) ;
14
15 }
16
17 void read_sensors () {
18 // Obtener la temperatura en formato crudo
19 int temperature_raw = analogRead ( temperature_sensor );
20 // Convertir la temperatura en base a la formula del fabricante
21 temperature_celsius = convert_temperature ( temperature_raw );
22 }
23
24 float convert_temperature ( int temperature_raw ){
25 int factor = 3975;
26 float resistance = ( float ) (1023 - temperature_raw )
27 * 10000 / temperature_raw ;
28 float ctemperature = 1 / ( log ( resistance / 10000)
29 / factor + 1 / 298.15) - 273.15;
30 return ctemperature ;
31 }
32
33 void perform_actions () {
34 if( temperature_celsius < min_temperature ) {
35 digitalWrite ( led , HIGH );
36 } else {
37 digitalWrite ( led , LOW ) ;
38 }
39 }
40
41 void loop () {
42 read_sensors () ;
43 perform_actions () ;
44 Serial . println ( temperature_celsius ) ;
45 }
C´odigo 3.4: Ejemplo 3: C´odigo de ejemplo para se˜nales anal´ogicas
    41/127
    3.3. UTILIZANDO LOS PINES DE ENTRADA 42
    42/127
    CAP
´
ITULO 4
COMUNICACIONES
4.1. Introducci´on
Uno de los aspectos importantes a considerar en el dise˜no de sistemas con microcontroladores son las comunicaciones. Si bien hay aspectos te´oricos y matem´aticos en
el estudio de las comunicaciones muy importantes y que es menester conocer, en este
cap´ıtulo siguiendo el enfoque pr´actico, aprenderemos a utilizar estas comunicaciones
sin necesidad de entrar en profundidad en los aspectos puramente te´oricos.
La elecci´on del modo de comunicaci´on a utilizar depende de las necesidades de
nuestro dise˜no. Aspectos tales como protocolos de comunicaci´on, medios f´ısicos, ancho
de banda, atenuaci´on, distorsi´on, ente otros, deben ser considerados a la hora de elegir
el modo de comunicaci´on entre dispositivos.
A continuaci´on veremos algunos modos de comunicaci´on que son soportados por
Arduino.
4.2. Comunicaci´on serie mediante USART
UART significa Universal Asynchronous Receiver/Transmitter, mientras que USART
significa Universal Synchronous/Asynchronous Receiver/Transmitter, y es un dispositivo que trabaja entre dos elementos que se quieren comunicar, tomando bytes de
datos de uno de ellos (transmisor) y transmiti´endolos hacia el otro (receptor) de manera secuencial bit por bit a una determinada tasa de transmisi´on. La informaci´on
que transmiten estos bits depender´a del protocolo y de la codificaci´on elegidos. El
protocolo nos indica la manera de comunicarnos, esto es, la cantidad y el tama˜no de
las tramas de datos que se env´ıan, los bits de paridad, c´odigo de error, etc., mientras
que la codificaci´on determina el contenido de la informaci´on y la manera en que los
bits son ordenados para transmitirla. La tasa de transmisi´on se mide en baudios, bits/seg y deben ser configurados de la misma manera tanto en el transmisor como en
el receptor para que puedan entenderse.
La unidad USART es igual a la unidad UART con la diferencia que la primera permite la comunicaci´on s´ıncrona y as´ıncrona y la ´ultima ´unicamente as´ıncrona.
43
    43/127
    4.2. COMUNICACION SERIE MEDIANTE USART 44 ´
Data 8b Parity
0b
Start
1b
Stop
1b
Figura 4.1: Trama con formato 8N1
Las diferencias entre la comunicaci´on as´ıncrona serie y s´ıncrona serie se explicar´an a
continuaci´on.
Comunicaci´on as´ıncrona serie: Si configuramos la unidad USART en modo
as´ıncrono tendremos que configurar diferentes par´ametros para que tanto emisor
como receptor sepan cuando tienen que leer o escribir los datos. Estos par´ametros son el n´umero de bits de datos, de paridad y de parada, as´ı como tambi´en
la velocidad en baudios. Existen numerosas configuraciones pero la m´as t´ıpica
es la siguiente: 8N1 que significa 8 bits de datos, ninguno de paridad y uno
de parada. (ver Figura 4.1) En el entorno Arduino la velocidad de transmisi´on
puede configurarse entre diferente valores desde 300 hasta 115200 baudios.
Comunicaci´on s´ıncrona serie: La unidad USART del ATmega328 se puede
configurar para funcionar en modo s´ıncrono, sin embargo no est´a soportado por
la librer´ıa oficial de Arduino. Esto no significa que no pueda ser usado, pero si
queremos usar este modo tendremos que desarrollar nuestra propia librer´ıa o
programar la configuraci´on de la unidad en cada uno de nuestros programas. La
comunicaci´on s´ıncrona se caracteriza por tener una l´ınea de reloj1 que permite
que ambos dispositivos lean y escriban en el evento de reloj. Esto encarece la
soluci´on dado que necesitamos un cable m´as, sin embargo, permite eliminar
todos los bits de control de la comunicaci´on de tal modo que ´unicamente se
trasmitir´an bits con informaci´on ´util.
Se ha hablado del m´odulo UART, sin embargo, hay que tener en cuenta que la
unidad UART es un m´odulo hardware y no un protocolo de comunicaci´on. La unidad
UART se encarga de obtener en paralelo y en un ´unico ciclo de reloj los datos del
registro de datos y los multiplexa en el tiempo seg´un la configuraci´on establecida.
Existen diversas configuraciones, simplex, duplex, full-duplex, de dos dispositivos
o en bus. Sin embargo, la comunicaci´on serie mediante UART fue desarrollada con el
objetivo de comunicar ´unicamente dos dispositivos.
A continuaci´on veremos diferentes ejemplos de como Arduino implementa la comunicaci´on serie en sus dispositivos.
Una unidad UART es un m´odulo hardware y no un protocolo de comunicaci´on. La
unidad UART se encarga de obtener en paralelo y en un ´unico ciclo de reloj los datos
del registro de datos, y los serializa en el tiempo seg´un la configuraci´on establecida.
Posee adem´as soporte para el control de flujo por hardware. Este control de flujo es
realizado a trav´es de dos conexiones RTS (Request to Send) y CTS (Clear to Send)
que permite a cada lado de la comunicaci´on indicar al otro que est´a listo para manejar
datos. El cableado para la comunicaci´on serie as´ıncrona es muy sencillo y se puede
ver en la Figura 4.2. Existen diversas configuraciones, simplex (en un solo sentido),
1En el ATmega328 la l´ınea de control se llama XCK0 y se encuentra en el pin 6 del encapsulado
DIP28
    44/127
    4.2. COMUNICACION SERIE MEDIANTE USART 45 ´
 Dispositivo A Dispositivo B
RTS
CTS CTS
RTS
Tx Tx
Rx Rx
Gnd Gnd
Figura 4.2: Conexi´on entre dispositivos UART con control de flujo
duplex (en ambos sentidos pero no simult´aneamente), y full-duplex (en ambos sentidos
al mismo tiempo), con o sin control de flujo.
Una UART se usa com´unmente con los est´andares de comunicaci´on RS-232, RS422 o RS-485 y tiene soporte para el control de flujo mediante hardware.
Dependiendo de la implementaci´on f´ısica se utilizar´a un determinado nivel de
voltaje u otro. El protocolo RS-232 utiliza de -3V a -15V para el nivel l´ogico ✭✭1✮✮ y
de +3V a +15V para el nivel l´ogico ✭✭0✮✮ Sin embargo, los procolos con l´ogica TTL o
CMOS trabajan con otros niveles. Existen circuitos integrados como el MAX232 que
permiten realizar la conversi´on de niveles.
Figura 4.3: Representaci´on de comunicaci´on serie
En la Figura 4.3 se puede ver una representaci´on de lo explicado anteriormente.
4.2.1. Ejemplo 1: Hola mundo por Serie
Como ya hemos dicho, Arduino tiene soporte para la comunicaci´on serie, tanto
hardware como software. A nivel hardware dependiendo del microcontrolador tendremos m´as o menos unidades UART. La API que proporciona Arduino para el manejo de
    45/127
    4.2. COMUNICACION SERIE MEDIANTE USART 46 ´
Cuadro 4.1: Ejemplo 1: Tabla de entradas/salidas
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Bot´on button 8
Entrada Rx0 RX 0
Salida Tx0 Tx 1
Cuadro 4.2: Ejemplo 1: Comunicaciones
Interfaz serie
Entrada/Salida Comando Evento Descripci´on
Salida Pin 8 ==
HIGH
Env´ıa ”hola mundo”
las comunicaciones es muy sencilla de utilizar y aprenderemos a manejarla mediante
los siguientes ejemplos.
En este primer ejemplo realizaremos un ✭✭hola mundo✮✮ mediante el puerto serie.
Cuando se pulse un bot´on el microcontrolador mandar´a por el puerto serie la cadena
✭✭hola mundo✮✮
Tabla de entrada/salida
Como en los anteriores ejemplos, lo primero que vamos a hacer es la tabla de
entrada/salida. En el enunciado se han se˜nalado dos de los elementos principales. Por
un lado la comunicaci´on serie y por otro lado el bot´on. Es buena costumbre reservar
un apartado ´ıntegro para las comunicaciones. En un dise˜no m´as complejo, lo m´as
probable es que tengamos diferentes tipos de comunicaciones con diversos protocolos.
En ese apartado, podremos definir todos los protocolos y las interfaces que proporciona
nuestro dise˜no al ✭✭exterior✮✮. Volviendo a las entradas y salidas, si bien en este primer
ejemplo lo ´unico que haremos es enviar un string por el puerto Tx (pin 1) dejamos ya
indicada la entrada Rx que ser´a utilizada en los ejemplos que siguen.
Comunicaciones
Este ejemplo es muy sencillo, por lo que no definiremos un protocolo completo, es
decir, sintaxis, sem´antica y temporizaci´on. Unicamente describiremos la comunicaci´on ´
en t´erminos de los mensajes de salida. En la Tabla 4.2 se puede ver un ejemplo de
dicha tabla.
Diagrama de flujo
Las comunicaciones se pueden dise˜nar como eventos as´ıncronos y s´ıncronos, todo
depender´a de como implementemos la soluci´on. En este caso y para no a˜nadir complejidad innecesaria en este punto, modelaremos la soluci´on en base a los ejemplos
anteriores, es decir, de forma s´ıncrona y secuencial.
En la Figura 4.4 puedes ver el diagrama propuesto.
    46/127
    4.2. COMUNICACION SERIE MEDIANTE USART 47 ´
Figura 4.4: Ejemplo 1 - Comunicaciones: Diagrama de flujo
C´odigo
En Cod. 4.1 se puede ver una posible soluci´on a este ejemplo. Lo primero que
debemos hacer es configurar los elementos de entrada salida y las comunicaciones.
Cuando el dise˜no sea m´as complejo, puede que sea necesario encapsular cada una de
estas partes en otras subfunciones como por ejemplo init communications().
Una de las partes m´as importantes de este tutorial se encuentra en la funci´on
Serial.begin(9600). Esta funci´on forma parte de la API de Arduino para la gesti´on y
uso del puerto serie. Como habr´as podido imaginar, el primer par´ametro se refiere a la
velocidad en baudios, recuerda que tanto receptor como emisor deben tener la misma
velocidad. Por otro lado, en la Secci´on 4.2 se habl´o de la configuraci´on de los bits de
parada, datos y paridad. Estos par´ametros se pueden pasar como segundo argumento
a la funci´on Serial.begin(). En la siguiente web se pueden ver los valores que puede
tomar este par´ametro: http://www.arduino.cc/en/pmwiki.php?n=Serial/Begin
En la funci´on perform actions() es donde se realiza la actuaci´on en funci´on del
estado del bot´on. Para este primer ejemplo hemos utilizado la funci´on Serial.println().
Esta funci´on env´ıa al registro de datos de la unidad UART la cadena indicada como
par´ametro, adem´as termina la cadena con los caracteres ✭✭retorno de carro✮✮ y ✭✭salto
de l´ınea✮✮. La funci´on devuelve un ✭✭long✮✮ que ´ındica el n´umero de bytes escritos en el
puerto serie. Es muy importante que tengas en cuenta que esta funci´on no env´ıa
los bytes de ✭✭golpe✮✮ por el ✭✭cable✮✮. Esta funci´on ´unicamente escribe los bytes en el
registro de datos de la UART llamado, en el caso del Atmega328, UDRn.
Si no quieres que finalice la cadena con los car´acteres de fin de l´ınea, puedes utilizar
la funci´on Serial.print()
    47/127
    4.2. COMUNICACION SERIE MEDIANTE USART 48 ´
1 i n t button = 8 ; //De acuerdo a l a t a b l a de
2 // en t r a d a / s a l i d a s
3
4 i n t b u t t o n s t a t e = LOW;
5
6 v oid se tup ( ) {
7
8 // pinMode c o n f i g u r a un p in como OUTPUT ( s a l i d a ) o INPUT ( en t r a d a )
9 pinMode ( button , INPUT) ;
10 S e r i a l . b e gi n ( 9 6 0 0 ) ;
11
12 }
13
14 v oid r e a d s e n s o r s ( ) {
15 b u t t o n s t a t e = di gi t al R e a d ( button ) ;
16 }
17
18 v oid p e r f o rm a c ti o n s ( ) {
19 i f ( b u t t o n s t a t e == HIGH) {
20 S e r i a l . p r i n t l n ( " Hola mundo " ) ;
21 }
22 }
23
24 v oid l o o p ( ) {
25 r e a d s e n s o r s ( ) ;
26 p e r f o rm a c ti o n s ( ) ;
27 }
C´odigo 4.1: Ejemplo 1 - Comunicaciones: C´odigo
    48/127
    4.2. COMUNICACION SERIE MEDIANTE USART 49 ´
Cuadro 4.3: Ejemplo 2: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Rx0 RX 0
Salida Led led red 8
Salida Led led yellow 9
Salida Tx0 Tx 1
4.2.2. Ejemplo 2: Recibiendo informaci´on
Hasta ahora ´unicamente hemos enviado informaci´on al exterior mediante la comunicaci´on serie. En este segundo ejemplo veremos otra de las partes claves en cualquier
dise˜no, la captaci´on de informaci´on mediante el puerto serie. La recepci´on de informaci´on al igual que el env´ıo de la misma normalmente est´a ligado a un protocolo de
aplicaci´on y a una serie de parsers que se ocupan de decodificar dicho protocolo.
Protocolos de comunicaci´on de bajo nivel dependen del medio f´ısico de comunicaci´on, por ejemplo: punto a punto, por buses, usando Ethernet, etc., y establecen el
modo en que la comunicaci´on se lleva a cabo. Por ejemplo establece qui´en inicia la
comunicaci´on, c´omo se direcciona el elemento receptor, c´omo se env´ıan los datos, si
uno a uno o a r´afagas, etc. Protocolos de comunicaci´on de alto nivel o de aplicaci´on se
refiere al conjunto de comandos de una aplicaci´on que se utilizan en la comunicaci´on
para gestionar el funcionamiento de un sistema. El dise˜no de protocolos de aplicaci´on
es una tarea que requiere de experiencia y no es el objetivo de este libro el entrar
en detalle, sin embargo, se anima al lector a definir sus propias codificaciones y a
practicar con la decodificaci´on de la informaci´on, dado que es un problema recurrente
en la soluciones basadas en microcontroladores.
El ejemplo es muy sencillo, en funci´on de la cadena recibida se encender´a un LED
u otro (se utilizan LEDs para no complicar el dise˜no de forma innecesaria, el uso de
uno u otro actuador normalmente radica en diferencias meramente el´ectricas).
Tabla de entrada/salida
Para este ejemplo utilizaremos dos LEDs, uno de color amarillo y otro de color rojo.
En funci´on de la cadena que es recibida se encender´a un LED u otro. El ✭✭protocolo✮✮
es el mostrado en la Tabla 4.4. En la Tabla 4.3 se puede ver la tabla de entrada/salida
con los nombres de las variables y los pines donde se conectar´an los actuadores.
Comunicaciones y protocolos de aplicaci´on
Cuando se dise˜na un protocolo de comunicaci´on de alto nivel lo primero que nos
deber´ıamos preguntar es que objetivo tiene. Si no es un elemento cr´ıtico del dise˜no
o ´unicamente se utiliza como protocolo de gesti´on, seguramente prefiramos definir el
protocolo mediante caracteres ascii. Sin embargo, si lo que buscamos es una comunicaci´on r´apida, de poco tama˜no, lo m´as recomendable es una codificaci´on binaria.
Un car´acter ascii se codifica mediante 8 bits (ascii extendido) ¿En que nos afecta este hecho?. Imaginemos que queremos mandar un comando que tiene 8 modos,
podr´ıamos utilizar un car´acter ascii y poner cualquier valor desde 0 a 9. Esto supondr´a una carga de 8 bits para la comunicaci´on. Sin embargo, si utilizamos 3 bits y
    49/127
    4.2. COMUNICACION SERIE MEDIANTE USART 50 ´
Cuadro 4.4: Ejemplo 2 - Comunicaciones: Comunicaciones
Interfaz serie
Entrada/Salida Comando Evento Descripci´on
Entrada led,yellow,high Pin 9 = HIGH Enciende el led amarillo
Entrada led,yellow,low Pin 9 = LOW Apaga el led amarillo
Entrada led,red,high Pin 8 = HIGH Enciende el led rojo
Entrada led,red,low Pin 8 = LOW Apaga el led rojo
Figura 4.5: Ejemplo 2 - Comunicaciones: Diagrama de flujo
codificamos las posibilidades en binario, ´unicamente necesitaremos 3 bits, es decir, el
tama˜no es un 37.5 % del tama˜no inicial.
Normalmente para los prototipos y para las comunicaciones de gesti´on, se utilizan
caracteres ascii dado que son m´as sencillos de depurar. En este ejemplo nosotros
utilizaremos un protocolo de aplicaci´on ascii muy sencillo para que el lector pueda
apreciar los fundamentos de la recepci´on de informaci´on mediante el puerto serie.
En la Tabla 4.4 se puede ver la tabla de comunicaciones con los comandos y el
formato.
Como se puede observar en la Tabla 4.4 los campos est´an separados por comas, que
se utilizar´an para la decodificaci´on de comandos tal y como veremos en la Figura 4.2.2.
Diagrama de flujo
En la Figura 4.5 se puede ver el diagrama en el que nos apoyaremos a la hora de
dise˜nar nuestro c´odigo. A medida que vayamos avanzando en los dise˜nos, estos diagramas cada vez ser´an m´as gen´ericos y subir´an m´as el nivel de abstracci´on. Un ejemplo
de este nivel de abstracci´on es la funci´on check communications() y parse command()
donde no se entra en detalle en la implementaci´on interna de la misma, ´unicamente
se sit´ua dentro del flujo de programa. Si quisi´eramos detallar una funci´on, una opci´on
ser´ıa la de realizar un diagrama separado para la misma y m´as tarde unirlos.
    50/127
    4.2. COMUNICACION SERIE MEDIANTE USART 51 ´
Normalmente, en cada ciclo del bucle de chequeo de comunicaci´on se realizar´a la
comprobaci´on del n´umero de datos recibido. Si existen datos se guardar´an en el buffer
de procesado. Una vez detectado el fin de l´ınea se procesar´a el comando y realizar´a
la acci´on.
C´odigo
Este ejemplo, aunque sencillo, requiere un poco m´as l´ıneas de c´odigo que el anterior. Un factor clave y determinante para un buen dise˜no es la modularizaci´on. Siempre
que una parte del c´odigo se repita debemos plantearnos el convertirlo en una funci´on.
Vamos a analizar el c´odigo de ejemplo. En este ejemplo (Cod. 4.2), hemos a˜nadido dos
funciones gen´ericas: check communications() y la funci´on parse command(). La primera funci´on hace lo siguiente: Primero comprueba que si buffer de recepci´on tiene
datos. La funci´on del buffer de recepci´on es la de guardar todo dato que entre al puerto serie, hasta 64 bytes, de modo que pueda ser consultado en cualquier momento. La
funci´on Serial.available de la librer´ıa Arduino, nos proporciona un m´etodo para saber
cuantos bytes se encuentran en el buffer de recepci´on. Mediante la funci´on Serial.read
leemos el dato entrante y a˜nadimos el dato al buffer de procesado que hemos creado
y que hemos llamado serial command. Es responsabilidad del programador vaciar el
buffer mediante la llamada a Serial.read. Esta llamada devuelve el byte m´as antiguo
en el buffer, es decir, el primero trasmitido (cola FIFO). Un detalle a tener en cuenta:
en Internet y en la bibliograf´ıa sobre Arduino se pueden ver muchos ejemplos donde
se hace la comprobaci´on Serial.available()>0. Aunque esto puede funcionar, hay que
tener mucho cuidado. Puede que en el buffer haya m´as de 0 bytes, sin embargo, esto
no significa que toda la trama est´e en el buffer, por lo que si se hace esta comprobaci´on
habr´a que tener especial cuidado a la hora de formar la trama. Otra manera de evitar
este comportamiento podr´ıa ser el comprobar que el tama˜no del buffer de recepci´on
sea mayor que el tama˜no de trama.
Otro de los puntos claves a la hora de tratar con protocolos ascii es el detectar el fin de
l´ınea. Dependiendo del sistema operativo y de la consola que se utilice para mandar
los datos, se utilizar´a un car´acter de fin de l´ınea u otro. En el entorno Arduino uno
puede abrir un monitor de terminal serie usando (CTRL+Shift+m). En este monitor,
por defecto, est´a seleccionada la opci´on de mandar como fin de l´ınea los caracteres
✭✭\r \n✮✮, sin embargo en el bloque ✭✭if✮✮ de nuestro c´odigo, se comprueba el fin de linea
´unicamente detectando si el valor recibido es igual a \n por lo tanto el car´acter \r ser´a
insertado en nuestra trama y seguramente ocasione problemas en el dise˜no. La manera
m´as sencilla de modificar este comportamiento es cambiar la opci´on en la consola o
en este caso el monitor serial para que ´unicamente env´ıe el car´acter \n como car´acter
fin de l´ınea.
Una vez detectado el fin del comando y almacenado en el buffer de procesado,
lo siguiente que se debe hacer es pasar dicho buffer a una funci´on que decodifique
la trama y convierta los datos en informaci´on ´util para la l´ogica del programa. Con
este fin se invoca a la funci´on parse command() (linea 32). Esta funci´on se ha creado
apoy´andose en las funciones strcpy() y strtok(), que divide y copia los valores le´ıdos en
la estructura Command protocol() (l´ıneas 9 a 14). Se recomienda al lector que revise
la documentaci´on sobre la funci´on strtok().
La ´ultima fase es la de actuar en funci´on de la entrada, en nuestro caso ´unicamente
tenemos que detectar el primer campo y detectar a que tipo de actuador se refiere la
comunicaci´on. La funci´on perform action led() comprueba qu´e led se va a modificar
    51/127
    4.2. COMUNICACION SERIE MEDIANTE USART 52 ´
y en funci´on de dicho valor y del estado act´ua llamado a la funci´on digitalWrite().
Como puedes observar el dividir el c´odigo en m´odulos con responsabilidad limitada,
permite que el c´odigo sea mucho m´as legible y mantenible. Si quisi´eramos a˜nadir la
posibilidad de encender o apagar un motor, ´unicamente tendr´ıamos que crear otra
funci´on que podr´ıamos llamar perform action motor() y hacer las comprobaciones
pertinentes.
4.2.3. Ejemplo 3: Comunicaci´on entre Arduinos
En los ejemplos anteriores hemos visto como manejar actuadores y como recibir
informaci´on de los sensores. Adem´as se ha practicado con la comunicaci´on serie con
el ordenador. En este ejemplo veremos como comunicar dos Arduinos entre si.
Normalmente en un dise˜no de complejidad media los microcontroladores no se encuentran aislados, por el contrario forman parte de una red de sensores y actuadores.
Un ejemplo claro de ´exito donde se puede apreciar esta jerarqu´ıa de controladores es
el BMW X5. En BMW decidieron a˜nadir un controlador y sensores en cada amortiguador con el objetivo de recibir informaci´on de manera inmediata de las condiciones
de las ruedas y del sistema de amortiguaci´on. En funci´on del valor de los sensores,
el ordenador central del veh´ıculo modifica diferentes par´ametros de modo que la conducci´on sea m´as suave y c´omoda.
Como puedes observar estamos ante un sistema distribuido en el que cada microcontrolador es responsable de un conjunto de sensores y un microcontrolador es
responsable de la actuaci´on.
En este ejemplo vamos a captar informaci´on en un Arduino y mediante las t´ecnicas
aprendidas en los ejemplos anteriores informaremos a otro Arduino de dichos valores
siendo este ´ultimo el responsable de actuar en funci´on de los valores recibidos.
Tabla de entrada/salida
Para simplificar el montaje en este ejemplo vamos a trabajar con sensores utilizados en otros ejemplos. Utilizaremos un bot´on, un sensor de temperatura como
sensores y dos LEDs como actuadores. Un primer Arduino (esclavo) tendr´a conectados los sensores. Leer´a los datos del sensor de temperatura y del bot´on y los enviar´a a
un segundo Arduino (maestro) cuando ´este lo solicite. Este segundo Arduino tendr´a
conectados dos LEDs. Un led se encender´a si el boton ha sido apretado, mientras que
el segundo led lo har´a de acuerdo al valor de temperatura recibido.
Cuadro 4.5: Ejemplo 3: Tabla de entrada/salida Arduino esclavo
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Bot´on button 8
Entrada Sensor de temperaturatemperature sensor A1
Entrada Rx0 RX 0
Salida Tx0 Tx 1
    52/127
    4.2. COMUNICACION SERIE MEDIANTE USART 53 ´
1 #i n cl u d e <s t r i n g . h>
2 #i n cl u d e <s t d l i b . h>
3
4 c o n s t i n t l e d r e d = 1 3;
5 c o n s t i n t l e d y e l l o w = 8 ;
6
7 ch a r se ri al c omm and [ 1 9 ] ;
8 i n t inde x = 0 ;
9 t y p e d e f s t r u c t {
10 ch a r a c t u a t o r [ 5 ] ;
11 ch a r type [ 1 0 ] ;
12 ch a r s t a t e [ 2 ] ;
13 } Command protocol ;
14 Command protocol command protocol ;
15
16 v oid se tup ( ) {
17 pinMode ( l e d r e d , OUTPUT) ;
18 pinMode ( l e d y ell o w , OUTPUT) ;
19 S e r i a l . b e gi n ( 9 6 0 0 ) ;
20 }
21
22 v oid check c ommunic a ti on s ( ) {
23 i n t data = 0 ;
24 w hil e ( S e r i a l . a v a i l a b l e ( ) ) {
25 data = S e r i a l . re ad ( ) ;
26 i f ( data != ’\n’ ) {
27 se ri al c omm and [ inde x ] = data ;
28 inde x++;
29 } e l s e {
30 se ri al c omm and [ inde x ] = ’\0 ’ ;
31 inde x = 0 ;
32 parse command ( ) ;
33 }
34 }
35 }
36
37 v oid parse command ( ) {
38 ch a r ∗ token ;
39 i n t t o k e n i n d e x = 0 ;
40 s t r c p y ( command protocol . a c t u a t o r , s t r t o k ( serial command , "," ) ) ;
41 s t r c p y ( command protocol . type , s t r t o k (NULL, "," ) ) ;
42 s t r c p y ( command protocol . s t a t e , s t r t o k (NULL, "," ) ) ;
43
44 i f ( strcmp ( command protocol . a c t u a t o r , " led " ) == 0 ) {
45 p e r f o r m a c t i o n l e d ( command protocol . type , command protocol . s t a t e )
;
46 }
47
48 }
49
50 v oid p e r f o r m a c t i o n l e d ( ch a r ∗ type , ch a r ∗ s t a t e ) {
51 i f ( strcmp ( type , " yellow " ) == 0 ) {
52 i f ( strcmp ( s t a t e , "on" ) == 0 ) {
53 d i g i t a lW r i t e ( l e d y ell o w , HIGH) ;
54 } e l s e {
55 d i g i t a lW r i t e ( l e d y ell o w , LOW) ;
56 }
57 } e l s e i f ( type , " red " ) {
58 i f ( strcmp ( s t a t e , "on" ) == 0 ) {
59 d i g i t a lW r i t e ( l e d r e d , HIGH) ;
60 } e l s e {
61 d i g i t a lW r i t e ( l e d r e d , LOW) ;
62 }
63 }
64
65 }
66
67 v oid l o o p ( ) {
68 check c ommunic a ti on s ( ) ;
69 }
C´odigo 4.2: Ejemplo 2 - Comunicaciones
    53/127
    4.2. COMUNICACION SERIE MEDIANTE USART 54 ´
Cuadro 4.6: Ejemplo 3: Tabla de entrada/salida Arduino maestro
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Rx0 RX 0
Salida Tx0 Tx 1
Salida Led notificador de
temperatura
temperature led 8
Salida Led notificador de
bot´on
button led 13
Comunicaciones
El protocolo para este ejemplo es muy sencillo. El maestro (Arduino actuador)
manda una petici´on de informaci´on. Esta petici´on ´unicamente contiene el id del sensor
del cual desea obtener la informaci´on. Para simplificar los id se definen mediante
la directiva de preprocesador ✭✭#DEFINE✮✮. Ambos Arduinos tendr´an que tener los
mismos valores. Una vez que el esclavo detecta la petici´on env´ıa un mensaje con el
identificador del sensor y el valor, separado por una coma. El maestro entonces deber´a
capturar la trama y analizar de que sensor ha recibido el valor, si el sensor es un bot´on,
esperar´a un int, si el sensor, por el contrario, es el de temperatura, entonces esperar´a
un float.
Es importante enviar delimitadores de trama de modo que sepamos en todo momento cuando se ha iniciado una trama y cuando ha finalizado la misma. En nuestro
caso hemos utilizado como inicio de trama el car´acter \t y como finalizador de trama
\r.
Diagrama de flujo
El diagrama de flujo est´a dividido en 3 partes. La primera de ellas es com´un a
todos los dise˜nos y es el setup() del microcontrolador. Una vez se ha realizado el setup
correctamente, el siguiente paso es obtener la informaci´on de los sensores (en nuestro
caso el bot´on y el sensor de temperatura). Por ´ultimo comprobaremos si tenemos
alguna comunicaci´on que atender. Si hay una comunicaci´on por atender haremos una
decodificaci´on de la misma (mediante el parser ) y mandaremos un mensaje u otro en
funci´on de la petici´on.
En este esquema el Arduino que va a actuar es el que pide los datos. Podr´ıa
plantearse otra situaci´on en el que el Arduino con los sensores fuera el que env´ıa de
forma constante los valores captados.
Cabe destacar que este diagrama de flujo es el correspondiente al Arduino Esclavo,
se deja como ejercicio al lector la creaci´on del diagrama para el Arduino Maestro.
C´odigo
El c´odigo es muy parecido al realizado en la Subsecci´on 4.2.2. En Cod. 4.4 puedes
ver el c´odigo del Arduino que act´ua como esclavo. En Cod. 4.3 se encuentra todo el
c´odigo del Arduino que realiza las peticiones de informaci´on, es decir, el maestro. Ten
en cuenta que se han omitido partes de c´odigo repetidas como por ejemplo la lectura
de valores de temperatura con el objetivo de no hacer muy extenso el c´odigo.
    54/127
    4.2. COMUNICACION SERIE MEDIANTE USART 55 ´
Figura 4.6: Ejemplo 3 - Comunicaciones: Diagrama de flujo
El c´odigo del esclavo te deber´ıa resultar familiar ya que es una recopilaci´on de los
ejemplos anteriores. La parte m´as importante ahora es la decodificaci´on del protocolo
que es pr´acticamente igual a la realizada en Subsecci´on 4.2.2. Se comprueba si hay
alguna trama sin procesar preguntado por el car´acter \n, si hay alguna se obtienen
todos los bytes del puerto serie hasta llegar al car´acter \n que indica el fin de l´ınea.
Por otro lado, el maestro solicita los valores enviando una cadena por el puerto
serie. Cuando recibe la informaci´on realiza un parser y en funci´on del tipo de informaci´on (temperatura o bot´on) realiza una acci´on u otra.
Otra parte importante del c´odigo es la utilizaci´on de la librer´ıa SoftwareSerial.
Mediante esta librer´ıa podemos implementar un emulador de UART en software (como una UART f´ısica) d´andole un nombre a la instancia creada y asign´andoles los
pines de RX y TX correspondientes (l´ınea 13 del c´odigo). Utilizar estos puertos nos
ayuda mucho a la hora de programar el Arduino dado que si utilizamos los pines
correspondientes a los puertos RX y TX del puerto serie 0 para la comunicaci´on entre
Arduinos, tendremos que desconectar dichos cables cada vez que queramos programar
alguno de los Arduinos ya que la programaci´on tal y como se explic´o en el Secci´on A.1
se realiza mediante el puerto serie.
Este tipo de comunicaci´on aunque muy sencilla puede llevar a problemas muy
dif´ıciles de depurar. Uno de los problemas podr´ıa ser que uno de los Arduinos desbordara el buffer del otro. Para evitar esta condici´on en la funci´on check petition() del
Arduino maestro se queda bloqueado hasta recibir el car´acter fin de trama. Este simple bucle act´ua como un mecanismo de control de flujo no permitiendo enviar ning´un
mensaje hasta que no se reciba la contestaci´on. Aunque esta soluci´on es muy sencilla
tiene muchos problemas dado que si el esclavo no contestara el Arduino maestro se
quedar´ıa constantemente en espera. Para evitar estos problemas existen soluciones
que utilizan ✭✭ACK’s✮✮ o mensajes de verificaci´on para mantener un control de flujo.
Se deja como ejercicio al lector la implementaci´on del mecanismo de ✭✭ACK’s✮✮ a la
soluci´on.
1 #d e f i n e BUTTON 0
    55/127
    4.2. COMUNICACION SERIE MEDIANTE USART 56 ´
2 #d e f i n e TEMPERATURE SENSOR 1
3 #d e f i n e MAXTEMPERATURE 26
4 #i n cl u d e <s t d l i b . h>
5 #i n cl u d e <s t r i n g . h>
6 #i n cl u d e <S o f t w a r e S e r i a l . h>
7
8 S o f t w a r e S e r i a l s e r i a l ( 1 0 , 1 1 ) ;
9 c o n s t i n t b u t t o n l e d = 1 3;
10 c o n s t i n t t em p e r a t u r e l e d = 8 ;
11 i n t inde x = 0 ;
12 ch a r se ri al c omm and [ 1 9 ] ;
13 t y p e d e f s t r u c t {
14 i n t s e n s o r ;
15 } p e t i t i o n t ;
16
17 v oid se tup ( ) {
18 S e r i a l . b e gi n ( 9 6 0 0 ) ; S e r i a l . b e gi n ( 9 6 0 0 ) ;
19 pinMode ( b u t t o n l e d , OUTPUT) ; pinMode ( t em p e r a t u r e l e d ,OUTPUT) ;
20 }
21
22 v oid s e n d p e ti ti o n t e m p e r a t u r e ( ) {
23 s e r i a l . p ri n t ( "\t" ) ;
24 s e r i a l . p ri n t (TEMPERATURE SENSOR) ;
25 s e r i a l . p ri n t ( ’\n’ ) ;
26 }
27
28 v oid s e n d p e t i t i o n b u t t o n ( ) {
29 s e r i a l . p ri n t ( ’\t’ ) ;
30 s e r i a l . p ri n t (BUTTON) ;
31 s e r i a l . p ri n t ( ’\n’ ) ;
32 }
33
34 v oid c h e c k p e t i t i o n ( ) {
35 ch a r data = 0 ;
36 w hil e ( data != ’\n’ ) {
37 i f ( s e r i a l . a v a i l a b l e ( ) > 0 ) {
38 data = s e r i a l . re ad ( ) ;
39 i f ( data == ’\t’ ) {
40 inde x = 0 ;
41 } e l s e i f ( data == ’\n’ ) {
42 se ri al c omm and [ inde x ] = ’\0 ’ ;
43 inde x = 0 ;
44 parse command ( ) ;
45 } e l s e {
46 se ri al c omm and [ inde x ] = data ;
47 inde x++;
48 }
49 }
50 }
51 }
52
53 v oid parse command ( ) {
54 ch a r ∗ token = s t r t o k ( serial command , "," ) ;
55 i n t s e n s o r = a t o i ( token ) ;
56 swi t c h ( s e n s o r ) {
57 c a s e BUTTON:
58 p e r f o rm a c ti o n b u t t o n ( s t r t o k (NULL, "," ) ) ;
59 break ;
60 c a s e TEMPERATURE SENSOR:
61 p e r f o rm a c ti o n t em p e r a t u r e ( s t r t o k (NULL, "," ) ) ;
62 break ;
    56/127
    4.2. COMUNICACION SERIE MEDIANTE USART 57 ´
63 }
64 }
65
66 v oid p e r f o rm a c ti o n b u t t o n ( ch a r ∗ v al u e ) {
67 i n t s t a t e = a t o i ( v al u e ) ;
68 d i g i t a lW r i t e ( b u t t o n l e d , s t a t e ) ;
69 }
70
71 v oid p e r f o rm a c ti o n t em p e r a t u r e ( ch a r ∗ v al u e ) {
72 f l o a t temp = a t o f ( v al u e ) ;
73 i f ( temp > MAXTEMPERATURE) {
74 d i g i t a lW r i t e ( t em p e r a t u r e l e d ,HIGH) ;
75 S e r i a l . p ri n t ( " Max temperature reached : " ) ;
76 S e r i a l . p r i n t l n ( temp ) ;
77 } e l s e {
78 d i g i t a lW r i t e ( t em p e r a t u r e l e d ,LOW) ;
79 }
80 }
81 v oid l o o p ( ) {
82 s e n d p e t i t i o n b u t t o n ( ) ;
83 S e r i a l . p r i n t l n ( " Sent button " ) ; c h e c k p e t i t i o n ( ) ;
84 s e n d p e ti ti o n t e m p e r a t u r e ( ) ;
85 S e r i a l . p r i n t l n ( " Sent temperature " ) ; c h e c k p e t i t i o n ( ) ;
86 d el a y ( 5 0 0 ) ;
87 }
C´odigo 4.3: Ejemplo 3 - Comunicaciones: maestro
1 #i n cl u d e <s t r i n g . h>
2 #i n cl u d e <s t d l i b . h>
3 #i n cl u d e <S o f t w a r e S e r i a l . h>
4 #d e f i n e BUTTON 0
5 #d e f i n e TEMPERATURE SENSOR 1
6 c o n s t i n t button = 8 ;
7 c o n s t i n t t em p e r a t u r e s e n s o r = A1 ;
8 f l o a t t e m p e r a t u r e c e l s i u s ;
9 i n t b u t t o n s t a t e = 0 ;
10 ch a r se ri al c omm and [ 1 9 ] ;
11 i n t inde x = 0 ;
12
13 S o f t w a r e S e r i a l s e r i a l ( 1 0 , 1 1 ) ;
14
15 t y p e d e f s t r u c t {
16 i n t s e n s o r ;
17 } p e t i t i o n t ;
18 p e t i t i o n t l a s t p e t i t i o n ;
19 v oid se tup ( ) {
20 pinMode ( button , INPUT) ;
21 pinMode ( t em p e r a t u r e s e n s o r , INPUT) ;
22 S e r i a l . b e gi n ( 9 6 0 0 ) ;
23 s e r i a l . b e gi n ( 9 6 0 0 ) ;
24 }
25 v oid check c ommunic a ti on s ( ) {
26 ch a r data = 0 ;
27 i f ( s e r i a l . a v a i l a b l e ( ) > 0 ) {
28 data = s e r i a l . re ad ( ) ;
29 i f ( data == ’\t’ ) {
30 inde x = 0 ;
31 } e l s e i f ( data == ’\n’ ) {
32 se ri al c omm and [ inde x ] = ’\0 ’ ;
33 inde x = 0 ;
    57/127
    4.3. COMUNICACION I2C 58 ´
34 parse command ( ) ;
35 } e l s e {
36 se ri al c omm and [ inde x ] = data ;
37 inde x++;
38 }
39 }
40 }
41 v oid parse command ( ) {
42 l a s t p e t i t i o n . s e n s o r = a t o i ( se ri al c omm and ) ;
43 swi t c h ( l a s t p e t i t i o n . s e n s o r ) {
44 c a s e BUTTON:
45 s e n d b u t t o n s t a t e ( ) ;
46 break ;
47 c a s e TEMPERATURE SENSOR:
48 s e n d t em p e r a t u r e ( ) ;
49 break ;
50 }
51 }
52 v oid s e n d b u t t o n s t a t e ( ) {
53 s e r i a l . p ri n t ( ’\t’ ) ; s e r i a l . p ri n t ( l a s t p e t i t i o n . s e n s o r ) ;
54 s e r i a l . p ri n t ( "," ) ; s e r i a l . p ri n t ( b u t t o n s t a t e ) ;
55 s e r i a l . p ri n t ( ’\n’ ) ;
56 }
57 v oid s e n d t em p e r a t u r e ( ) {
58 s e r i a l . p ri n t ( ’\t’ ) ; s e r i a l . p ri n t ( l a s t p e t i t i o n . s e n s o r ) ;
59 s e r i a l . p ri n t ( "," ) ; s e r i a l . p ri n t ( t e m p e r a t u r e c e l s i u s ) ;
60 s e r i a l . p ri n t ( ’\n’ ) ;
61 }
62 v oid l o o p ( ) {
63 r e a d s e n s o r s ( ) ;
64 check c ommunic a ti on s ( ) ;
65 }
C´odigo 4.4: Ejemplo 3 - Comunicaciones: esclavo
4.3. Comunicaci´on I2C
En la Secci´on 4.2 vimos un mecanismo de comunicaci´on muy sencillo (Comunicaci´on mediante UART). En esta secci´on veremos otro protocolo de comunicaci´on
llamado I2C.
I2C fue dise˜nado por la empresa Philips en 1992. Actualmente muchos perif´ericos
implementan este bus de comunicaci´on por su sencillez, fiabilidad y el bajo n´umero
de cables requerido (´unicamente 2).
Es un protocolo de dos cables utilizado para conectar uno o mas maestros a uno
o mas esclavos. Los esclavos pueden ser sensores de temperatura, humedad, de movimiento, memorias series, etc. Cada esclavo se A diferencia de la UART, mediante
I2C podemos direccionar a varios esclavos, es decir, es un bus con m´ultiples esclavos
cada uno de ellos identificados por una direcci´on de 7 bits o 10 bits en funci´on del
est´andar utilizado.
4.3.1. Hardware
Una de las ventajas de I2C frente a otros buses como SPI (Serial Peripheral Interface) es el bajo n´umero de cables requeridos para la comunicaci´on. I2C ´unicamente
    58/127
    4.3. COMUNICACION I2C 59 ´
requiere 2 cables dentro de un mismo circuito y tres cables para la comunicaci´on entre
circuitos. La 3 se˜nales principales son:
SCL (Serial CLock): La se˜nal de reloj siempre la genera el dispositivo que
act´ua como maestro. Los esclavos pueden forzar al maestro a que el reloj deje
de oscilar. En la Subsecci´on 4.3.2 veremos este caso.
SDA (Serial Data): La se˜nal SDA se utiliza para enviar los datos.
GND (Ground): Esta se˜nal no es obligatoria en comunicaciones dentro de un
mismo circuito pero si la comunicaci´on es entre circuitos, entonces se necesitar´a
con el objetivo de proporcionar un mismo nivel de referencia.
4.3.2. Protocolo I2C
Figura 4.7: Esquema de comunicaci´on I2C
El valor de las se˜nales SCL y SDA est´a en alto hasta que uno de los componentes
los ponga en bajo. Los dos resistores de pull-up fuerzan el valor de los cables a VCC
(3,3 - 5 volts). El maestro controla una se˜nal de reloj por la l´ınea SCL, determinando
la tasa de transferencia de datos, y controla o deja controlar por el esclavo la l´ınea de
direcciones SDA.
La comunicaci´on entre un maestro y un esclavo consiste en una secuencia de
transacciones de datos a trav´es de SDA controladas por el maestro o por el esclavo,
sincronizadas por la se˜nal SCL.
Hay tres tipos de transacciones, todas iniciadas por el maestro: escritura (el maestro escribe en la linea SDA, lectura (el esclavo escribe en la linea SDA) y combinadas
(ambos escriben). La comunicaci´on a trav´es de I2C est´a formada por cuatro pasos:
Condici´on de Inicio
Trama de Direccionamiento
Trama de Datos
Condici´on de Parada
Cada transacci´on comienza con una condici´on de inicio (S) y termina con una
condici´on de Parada (P). Estas condiciones son controladas por el maestro. La
condici´on de inicio la realiza el maestro con una transici´on de la se˜nal SDA de 1
a 0 en el semiciclo positivo del reloj SCL, mientras que la condici´on de parada se
realiza con una transici´on de la se˜nal SDA de 0 a 1 en el semiciclo positivo de la se˜nal
SCL. En I2C la transici´on en la l´ınea SDA durante las transacciones se hace en la
    59/127
    4.3. COMUNICACION I2C 60 ´
Figura 4.8: Trama de direcci´on I2C
parte baja del ciclo reloj. Unicamente en la condici´on de inicio y parada se realiza la ´
transici´on en la parte alta del ciclo de reloj.
Una vez que se ha indicado la condici´on de inicio el siguiente paso es enviar una
trama con la direcci´on del dispositivo que se desea seleccionar. Existen dos modos
de direccionamiento: de 7 bits o de 10 bits. El uso de 10 bits de comunicaci´on no
es frecuente y no est´a cubierto en este libro. Al final de los 7 bits de direcci´on se
agrega un octavo bit que indicar´a el tipo de transacci´on que se hace (0 = escritura,
1 = lectura). Hay que tener en cuenta que la direcci´on del esclavo se colocar´a en
los 7 bits mas significativos de la trama de direcciones. Una vez enviada la trama de
direccionamiento los esclavos leen cada bit en la transici´on del reloj, teniendo en cuenta
que el primer bit es el m´as significativo de la direcci´on y que el ´ultimo bit corresponde
al tipo de transferencia. Para comprobar que el esclavo ha detectado la petici´on de
comunicaci´on del maestro, el esclavo debe poner la l´ınea SDA a nivel bajo antes del
d´ecimo pulso de reloj, de lo contrario el maestro puede interpretar que la comunicaci´on
no se ha podido establecer. Una vez que se ha establecido la comunicaci´on el siguiente
paso es enviar la trama de datos. La trama de datos ´unicamente se trasmite bit por bit
mediante codificaci´on MSB. En funci´on de si la transferencia es de lectura o escritura,
ser´a el maestro o el esclavo el encargado de poner los datos en el bus. Es importante
que el receptor en todo momento valide cada byte recibido mediante un ACK. Si el
receptor env´ıa un NACK despu´es de recibir un byte, indicar´a al receptor que quiere
cerrar la comunicaci´on.
El ´ultimo paso consiste en la transici´on del estado de transferencia al estado de
parada. Una vez que todos los datos han sido enviados y se ha recibido un NACK, el
maestro generar´a la condici´on de parada.
El protocolo I2C admite la comunicaci´on continuada entre un maestro y un esclavo.
Para ello, una vez terminado el primer flujo de informaci´on (Condici´on de inicio +
direcci´on + ack + datos + ack) en vez de enviar la condici´on de parada, el maestro
mantiene ocupado el bus poniendo la se˜nal de datos en alto y manteniendo el reloj
en alto. De este modo, no se permite que otro maestro adquiera el bus. Una vez
completada esta fase se puede enviar otra vez la direcci´on del esclavo (cualquiera) y
se procede al env´ıo de datos.
En la Figura 4.8 se puede ver una ilustraci´on de la trama de direcci´on I2C y en
Figura 4.7 un esquema donde se ilustra la comunicaci´on anteriormente explicada. Es
importante tener en cuenta que el ACK lo env´ıa el receptor y no el emisor. En la
Figura 4.9 se puede observar la estructura que presenta una trama de datos, donde
´unicamente se mandan los datos y un bit de ACK que env´ıa el receptor.
En los siguientes ejemplos veremos de manera simplificada como realizar comunicaciones mediante el protocolo I2C en Arduino.
4.3.3. Ejemplo 1: Hola mundo mediante I2C
Arduino posee una librer´ıa llamada Wire que implementa el protocolo I2C. Mediante esta librer´ıa la utilizaci´on de protocolo se reduce a unas sencillas funciones que
    60/127
    4.3. COMUNICACION I2C 61 ´
Figura 4.9: Trama de datos I2C
Cuadro 4.7: Ejemplo 1 - I2C: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Linea SCL SCL A5
Entrada Linea SDA SDA A4
Salida Led notificador notificator led 13
nos abstraer´an por completo de detalles de m´as bajo nivel.
Los pines I2C variar´an en funci´on del microcontrolador que estemos utilizando,
recuerda que siempre puedes recurrir a la p´agina oficial de Arduino o al datasheet
del microcontrolador para m´as informaci´on. En el caso de utilizar el bootloader del
Atmega328 los pines correspondientes a SDA y SCL son A4 y A5 respectivamente.
Algunas tarjetas de expansi´on poseen varios conectores I2C que permite expandir el
bus para varios esclavos.
En este ejemplo utilizaremos dos placas Arduino de modo que una env´ıe un mensaje y el otro reciba el mensaje y en funci´on del mismo encienda o apague un LED. De
nuevo el LED es nuestro mejor amigo a la hora de comprobar que las cosas funcionan.
Tabla de entrada/salida
La tabla de entrada salida es muy simple (ver Tabla 4.7), ´unicamente tenemos un
LED en el nodo esclavo que servir´a para comprobar que el mensaje ha sido recibido
con ´exito.
Diagrama de flujo
Los diagramas de flujo que contienen eventos y comunicaciones suelen ser m´as
complejos dado que tenemos que atendar a eventos que no son s´ıncronos. Se recomienda que el lector cree el diagrama de flujo correspondiente bas´andose en el c´odigo
mostrado en Tabla 4.3.3.
C´odigo
Para evitar tratar con detalles de implementaci´on del protocolo I2C utilizaremos
la librer´ıa Wire cuyas funciones mas utilizadas se explicar´an a continuaci´on. La documentaci´on oficial de la librer´ıa Wire puede ser consultada desde la siguiente direcci´on:
http://playground.arduino.cc/Main/WireLibraryDetailedReference.
begin(): Esta funci´on nos permite iniciar la librer´ıa. Esta funci´on es recurrente
en la mayor´ıa de las librer´ıas. La implementaci´on suele reiniciar o poner en el
estado inicial la m´aquina de estados y los registros de trabajo.
    61/127
    4.3. COMUNICACION I2C 62 ´
requestFrom(direcci´on, tama˜no): Mediante esta funci´on ponemos al maestro a la escucha del esclavo hasta que reciba el tama˜no fijado en el segundo
par´ametro. La direcci´on tiene que ser de 7 bits.
beginTransmission(direcci´on): Si queremos enviar datos desde el maestro al
esclavo, despu´es de llamar a la funci´on begin, tenemos que utilizar la funci´on
beginTransmission. Mediante esta funci´on indicamos a la librer´ıa que pase al
estado preparado y configure el buffer de direccionamiento. Ten en cuenta que
esta funci´on no enviar´a ning´un bit, ´unicamente hace operaciones a nivel interno
con el objetivo de no ocupar el bus de forma innecesaria.
send(buffer): Una vez que se ha establecido la direcci´on la funci´on send nos
permite configurar el buffer de datos de la interfaz TWI2.
endTransmission(): La ´ultima funci´on en cada comunicaci´on es endTransmission() que mandar´a todos los datos del buffer a la direcci´on indicada mediante
beginTransmission() y por ´ultimo enviar´a el bit de parada.
onReceive((void*)func(int)): Cuando la librer´ıa detecta que existe un dato
llama a la funci´on handler pasada como par´ametro. Esta funci´on tiene que tener
la ✭✭firma✮✮ void func(int), el par´ametro que recibe contiene el n´umero de bytes
capturados. El bus no ser´a liberado hasta que se retorne de la funci´on handler.
receive(): Devuelve el siguiente byte en el buffer de recepci´on.
onRequest((void*)func(void)): Si el esclavo recibe la se˜nal SLA+R, es decir,
la direcci´on del dispositivo y la opci´on de lectura, la librer´ıa Wire llama a esta
funci´on handler donde se deber´a insertar los datos en el buffer de escritura con
el objetivo de iniciar el env´ıo.
available(): Devuelve el n´umero de bytes del buffer de recepci´on.
Una vez que se tiene claro cada una de las funciones de la librer´ıa, el c´odigo
mostrado en Cod. 4.5 y Cod. 4.6 no debe suponer ning´un problema. En primer lugar
el Arduino maestro env´ıa un byte de datos con el car´acter ascii ✭✭h✮✮ (recuerda que un
car´acter ascii extendido es igual a un byte) y espera medio segundo. Por otro lado,
el Arduino esclavo ´unicamente espera los datos mediante la funci´on handler. Esta
funci´on handler se inscribe al evento del bus mediante la funci´on onRequest(). Ten en
cuenta que aunque en este c´odigo y a modo de ejemplo se ha realizado la operaci´on
de encender el led dentro de la funci´on, esto no es recomendable dado que mientras
permanezcamos en dicha funci´on el bus se mantendr´a ocupado.
Por ´ultimo, se recomienda que el lector modifique el c´odigo del esclavo y del
maestro con el objetivo de apagar y encender el led mediante alg´un evento, este
evento puede ser la inserci´on de una cadena en el puerto serie o un evento temporal.
Adem´as, se recomienda que se realice la modificaci´on del pin de salida fuera de la
funci´on handler.
2TWI (Two Wire Interface) es otra forma de nombrar al protocolo I2C
    62/127
    4.3. COMUNICACION I2C 63 ´
1 #i n cl u d e <Wire . h>
2
3
4 v oid se tup ( ) {
5 Wire . b e gi n ( ) ;
6
7 }
8
9 v oid l o o p ( ) {
10 Wire . b e gi nT r a n smi s si o n ( 1 ) ;
11 Wire . w ri t e ( "h" ) ;
12 Wire . endT r an smi s si on ( ) ;
13 d el a y ( 5 0 0 ) ;
14 }
C´odigo 4.5: Ejemplo 1 - Comunicaciones I2C: maestro
1 #i n cl u d e <Wire . h>
2 c o n s t i n t n o t i f i c a t i o n l e d = 1 3;
3
4 v oid se tup ( ) {
5 pinMode ( n o t i f i c a t i o n l e d ,OUTPUT) ;
6 Wire . b e gi n ( 1 ) ;
7 S e r i a l . b e gi n ( 9 6 0 0 ) ;
8 Wire . onReceive ( h a n dl e r ) ;
9
10 }
11 v oid h a n dl e r ( i n t num bytes ) {
12 ch a r v al u e = 0 ;
13 w hil e (Wire . a v a i l a b l e ( ) > 0 ) {
14 v al u e = Wire . re ad ( ) ;
15 }
16 S e r i a l . p ri n t ( " Read : " ) ;
17 S e r i a l . p r i n t l n ( v al u e ) ;
18 i f ( v al u e == ’h’ ) {
19 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d ,HIGH) ;
20 } e l s e {
21 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d ,LOW) ;
22 }
23
24 }
25 v oid l o o p ( ) {
26 d el a y ( 1 0 0 ) ;
27 }
C´odigo 4.6: Ejemplo 1 - Comunicaciones I2C: esclavo
    63/127
    4.3. COMUNICACION I2C 64 ´
Figura 4.10: Sensor MPU6050
4.3.4. Ejemplo 2: Obteniendo datos de un IMU
En este ejemplo veremos como utilizar uno de los muchos sensores IMU (unidad
de medici´on inercial) mediante el protocolo I2C para obtener los ´angulos de rotaci´on
con respecto al eje ✭✭X✮✮ y el eje ✭✭Y✮✮.
El uso de estos sensores requiere de pr´actica y de conocimientos trigonom´etricos
para tratar los valores ✭✭crudos✮✮3. En este ejemplo, no nos centraremos en las funciones
matem´aticas, la parte m´as interesante para este capitulo es la comunicaci´on con el
sensor.
Para este ejemplo hemos utilizado el sensor MPU6050 (ver Figura 4.10) este sensor proporciona 6 grados de libertad y posee un controlador DMP (Digital Motion
Processing). Con el objetivo de simplificar las cosas ´unicamente veremos como obtener el valor del giroscopio, aunque tambi´en obtengamos el del aceler´ometro para
utilizarlo en el filtro complementario4.
Debido a la sencillez del ejemplo y al hecho de que ´unicamente utilizaremos un
sensor, en este caso no realizaremos diagrama de flujo ni tabla de comunicaciones,
´unicamente mantendremos la tabla de entrada salida con el objetivo de mostrar las
conexiones necesarias.
Tabla de entrada/salida
Como ya hemos comentado, la estructura de este ejemplo es diferente debido a que
´unicamente estamos utilizando un sensor y no estamos haciendo ning´un tipo actuaci´on
en funci´on de los valores obtenidos. En la Figura 4.11 se puede observar la conexi´on
de este sensor. Unicamente utilizaremos el bus principal I2C. El ´ MPU6050 tiene otro
bus I2C que utiliza para gestionar una cola FIFO.
Es muy importante que te asegures del voltaje de entrada de tu sensor. Algunos
MPU6050 tienen un conversor que permiten conectar el sensor a 5V, pero si dudas
conectalo a 3.3V.
Ten en cuenta que en este caso no hace falta una resistencia de pull-up, esto se
debe a que el sensor trae una resistencia interna. Muchos sensores que se comunican
3Los valores ✭✭crudos✮✮ o ✭✭RAW✮✮ son aquellos valores que no han sido tratados y que son resultado
de una captura
4El filtro complementario es la uni´on de un filtro de paso bajo con un filtro de paso alto
    64/127
    4.3. COMUNICACION I2C 65 ´
Figura 4.11: Conexiones MPU6050
mediante I2C incorporan esta resistencia.
C´odigo
Se puede revisar el c´odigo en Cod. 4.7. A continuaci´on, describiremos las partes
m´as importantes del mismo.
1 #i n cl u d e <Wire . h>
2 #d e f i n e IMU 0 x68
3 #d e f i n e A R 1 6 3 8 4. 0
4 #d e f i n e G R 1 3 1. 0
5 #d e f i n e RAD TO DEG 5 7. 2 9 5 7 7 9
6 i n t 1 6 t AcX, AcY, AcZ , GyX, GyY, GyZ;
7 f l o a t Acc [ 2 ] ;
8 f l o a t Gy [ 2 ] ;
9 f l o a t Angle [ 2 ] ;
10 v oid se tup ( ) {
11 Wire . b e gi n ( ) ;
12 Wire . b e gi nT r a n smi s si o n (IMU) ;
13 Wire . w ri t e ( 0 x6B ) ;
14 Wire . w ri t e ( 0 ) ;
15 Wire . endT r an smi s si on ( t r u e ) ;
16 S e r i a l . b e gi n ( 9 6 0 0 ) ;
    65/127
    4.3. COMUNICACION I2C 66 ´
17 }
18
19 v oid l o o p ( ) {
20 Wire . b e gi nT r a n smi s si o n (IMU) ;
21 Wire . w ri t e ( 0 x3B ) ;
22 Wire . endT r an smi s si on ( f a l s e ) ;
23 Wire . requestFrom (IMU, 6 , t r u e ) ;
24 AcX = Wire . re ad ( ) << 8 | Wire . re ad ( ) ;
25 AcY = Wire . re ad ( ) << 8 | Wire . re ad ( ) ;
26 AcZ = Wire . re ad ( ) << 8 | Wire . re ad ( ) ;
27
28 Acc [ 1 ] = atan (−1 ∗ (AcX / A R) / s q r t ( pow ( (AcY / A R) , 2 ) + pow ( (
AcZ / A R) , 2 ) ) ) ∗ RAD TO DEG;
29 Acc [ 0 ] = atan ( (AcY / A R) / s q r t ( pow ( (AcX / A R) , 2 ) + pow ( ( AcZ /
A R) , 2 ) ) ) ∗ RAD TO DEG;
30
31 Wire . b e gi nT r a n smi s si o n (IMU) ;
32 Wire . w ri t e ( 0 x43 ) ;
33 Wire . endT r an smi s si on ( f a l s e ) ;
34 Wire . requestFrom (IMU, 4 , t r u e ) ;
35 GyX = Wire . re ad ( ) << 8 | Wire . re ad ( ) ;
36 GyY = Wire . re ad ( ) << 8 | Wire . re ad ( ) ;
37
38 Gy [ 0 ] = GyX / G R ;
39 Gy [ 1 ] = GyY / G R ;
40
41 Angle [ 0 ] = 0. 9 8 ∗ ( Angle [ 0 ] + Gy [ 0 ] ∗ 0. 0 1 0 ) + 0. 0 2 ∗ Acc [ 0 ] ;
42 Angle [ 1 ] = 0. 9 8 ∗ ( Angle [ 1 ] + Gy [ 1 ] ∗ 0. 0 1 0 ) + 0. 0 2 ∗ Acc [ 1 ] ;
43
44 S e r i a l . p ri n t ( " Angle X: " ) ; S e r i a l . p r i n t ( Angle [ 0 ] ) ; S e r i a l . p ri n t ( "\n
" ) ;
45 S e r i a l . p ri n t ( " Angle Y: " ) ; S e r i a l . p r i n t ( Angle [ 1 ] ) ; S e r i a l . p ri n t ( "\n
- - - - - - - - - - - -\n" ) ;
46
47 d el a y ( 1 0 ) ;
48
49 }
C´odigo 4.7: Ejemplo 2 - Comunicaciones I2C: Sensor MPU-6050
En la funci´on de setup() iniciamos el dispositivo. La inicializaci´on del sensor depender´a de cada uno, en este caso tenemos que mandar el comando ✭✭0✮✮ al registro
0x6B. Normalmente el procedimiento suele ser el siguiente: En primer lugar se env´ıa
un byte con el registro donde se quiere leer o escribir (en este caos el 0x6B) y seguidamente se env´ıa o recibe el dato del registro (en este caso enviamos el comando 0
que inicia el dispositivo). Una vez que hemos enviado la petici´on de inicio cerramos
la comunicaci´on mediante la llamada a la funci´on endTransmission() de la librer´ıa
Wire.
Por otro lado, en cada ciclo de ✭✭scan✮✮ (funci´on loop()) obtenemos los valores de
cada uno de los registros del sensor5. Para obtener el valor del registro que contiene los
valores del aceler´ometro tenemos que enviar la petici´on a la direcci´on 0x3B. Este paso
es muy importante y te ayudar´a a entender cualquier comunicaci´on I2C mediante
Arduino. Una vez que hemos escrito el valor 0x3B en el buffer de datos del bus I2C del
Arduino, el siguiente paso es enviar dicho buffer por el canal SDA. Ten en cuenta que
como se explic´o en la Tabla 4.3.3 los datos no se env´ıan hasta que se llama a la funci´on
5Todos las direcciones as´ı como el valor de los comandos se obtienen del datasheet de cada sensor
    66/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 67
endTransmission. Al contrario que en el caso del comando de inicializaci´on, en este
caso vamos a recibir datos del sensor. Cuando se env´ıa la petici´on 0x3B tenemos que
activar el bit de lectura del bus I2C con el objetivo de que el esclavo (sensor) pueda
escribir en el maestro (Arduino). Para activar el bit de lectura primero finalizamos
la fase de transmisi´on de datos mediante endTransmission(false). Despu´es indicamos
que queremos obtener datos del esclavo y esperamos hasta tener 6 bytes, todo ello
mediante la funci´on requestFrom(). Los datos se env´ıan byte a byte. Como los datos
son de 16 bits tendremos que utilizar operaciones de manipulaci´on de bits para formar
una palabra de 16 bits. En las l´ıneas 24,25,26 se pueden ver dichas operaciones.
Para obtener el valor del giroscopio se sigue el mismo procedimiento, la ´unica
diferencia est´a en el n´umero de bytes que recibimos del esclavo y la direcci´on del
registro.
En las l´ıneas 41 y 46 se aplica un filtro complementario que no ser´a explicado
debido a que no es el objetivo de este libro.
Si abres el monitor serial podr´as ver los valores obtenidos cada 10ms.
4.4. Protocolo SPI (Serial Peripheral Interface
En esta secci´on veremos otro protocolo de comunicaci´on serial SPI. SPI es un
protocolo de comunicaci´on serie s´ıncrono para comunicaci´on entre microcontroladores
y uno o mas perif´ericos a corta distancia como displays, tarjetas de memoria y sensores.
Cada instancia de SPI tiene un maestro y uno o varios esclavos.
Posee cuatro se˜nales b´asicas: Tres desde el maestro hacia el esclavo
SS (Slave Select): Se˜nal que se replica para cada esclavo. El maestro pone a
cero una de las se˜nales SS para seleccionar el esclavo correspondiente
MOSI (Master Out Slave In): Se˜nal por donde se transmite una palabra
(byte) desde el maestro hacia el esclavo
SCK (Serial Clock): Se˜nal que sincroniza la comunicaci´on Y una se˜nal desde
el esclavo hacia el maestro
MISO (Master In Slave Out): Se˜nal por donde se transmite una palabra
(byte) desde el esclavo hacia el maestro
El procesador ATmega328 tiene una interfaz SPI para comunicaci´on. Las se˜nales
SS, MOSI, MISO y SCK corresponden a los pines 16 al 19 respectivamente. En el
m´odulo Arduino estos pines est´an conectados a los pines 10 al 13 respectivamente.
(Figura 4.12).
El mecanismo de transferencia se realiza de la siguiente manera: Toda la comunicaci´on es controlada por el m´aster que selecciona el esclavo poniendo en bajo la se˜nal SS
correspondiente. Entonces env´ıa en modo serie una palabra (byte) por la l´ınea MOSI
y simult´aneamente acepta un byte proveniente desde el esclavo por la l´ınea MISO.
Esta transferencia se realiza generando 8 pulsos sobre la l´ınea de sincronizaci´on SCK
En Arduino existe una librer´ıa SPI.h que nos proporciona una serie de funciones
para controlar el proceso de comunicaci´on.
SPIbegin: Inicializa el bus configurando SS como salida con pullup interno en
alto y MOSI y SCLK como salida en bajo.
    67/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 68
Figura 4.12: Pines SPI en el proc. ATmega328
SPISetting: Esta funci´on permite configurar algunos par´ametros de comunicaci´on tales como los siguientes, los cuales se deben poner en el orden que se
describen:
• Velocidad de comunicaci´on (generalmente se pone la frecuencia a que opera
el microcontrolador en HZ).
• Si el primer bit enviado es el mas significativo (MSB) o el menos significativo (LSB)
• El modo de comunicaci´on SPI, que nos indica en que flanco del reloj los
datos son enviados o recibidos (Fase del reloj) y si el reloj est´a inactivo
cuando est´a en alto o en bajo (Polaridad) (ver tabla Tabla 4.8).
SPI.beginTransaction: Inicializa el bus SPI con SPISetting
SPI.endTransaction
SPI.transfer
Los pasos para establecer una comunicaci´on SPI son los siguientes,
1. SPI.begin(): Iniciar la librer´ıa SPI (normalmente en el setup() del programa)
2. SPI.beginTransaction(SPISetting()): La librer´ıa se apropia de las interrupciones.
3. SPI.transfer(): Se transfieren 8 bits
4. SPI.endTransaction(): Se libera el bus y se habilitan de nuevo las interrupciones.
    68/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 69
Cuadro 4.8: Modos de comunicaci´on SPI
Modo Clock Polarity Clock Phase
MODO 0 0 0
MODO 1 0 1
MODO 2 1 0
MODO 3 1 1
Figura 4.13: Contenidos del registro de entrada del DAC
Ejemplo: Ejemplo de uso del bus SPI
En este ejemplo mostraremos como generar una se˜nal sinusoidal en el procesador y
enviarla a un conversor DAC (Conversor Digital/Anal´ogico) a trav´es del puerto SPI.
El c´odigo del ejemplo est´a en la figura Cod. 4.8. En la primera parte del c´odigo
se crea una tabla de consulta (look-up table) que en realidad es un arreglo de memoria que llamamos Waveform con valores que forman una sinusoide. Este arreglo
es recorrido y cada valor es enviado a trav´es del bus SPI, al conversor DAC externo
conectado al Arduino para generar la sinusoide. Para este ejemplo se utiliz´o un m´odulo de convers´ıon D/A de 8 canales PMODDA4 de DIGILENT que usa el conversor
AD5628 de ANALOG DEVICES, 6
La comunicaci´on se realiza enviando transacciones de 32 bits a trav´es del puerto
MOSI al esclavo (ver figura Figura 4.137 Sin entrar en detalles t´ecnicos acerca del
DAC AD5628 es necesario saber que los bits C3 al C0 se utilizan para indicar el
comando a ejecutar por el AD5628. En el caso de un Reset el valor es el ”0111”, y
para configurar el valor de referencia en la conversion es el ”1000”. El bit DB0 se debe
poner a 1 si se utiliza valor de referencia. Los bits A3 a A0 indican la direcci´on de
uno de los 8 canales del conversor desde el ”0000”, hasta el ”0111”. Para seleccionar
todos los canales se coloca el valor ”1111”.
El primer paso es inicializar la comunicaci´on SPI. Para ello hemos creado la funci´on init spi que pone en alto la se˜nal slave select y llama a la funci´on begin de la
librer´ıa SPI (SPI.begin). El siguiente paso es configurar el m´odulo esclavo, seg´un se
indica en la l´ınea 27 del c´odigo, donde se indica la frecuencia del reloj, si el primer
bit es el m´as significativo, y el modo de comunicaci´on. Para configurar el esclavo es
necesario enviarle un comando de reset y luego otro comando con el setup del valor de referencia. Para ello es necesario colocar la se˜nal slave selecta ’0’ durante la
transacci´on. En este ejemplo se realizan cuatro transacciones de 8 bits cada una (tanto
en la funci´on config dac como en la funci´on Loop), pero se puede utilizar la funci´on
6
Informaci´on acerca del conversor PMODDA4 de DIGILENT puede encontrarse en este enlace
https://reference.digilentinc.com/pmod:pmod:DA4
7
tomada de https://reference.digilentinc.com/pmod:pmod:DA4
    69/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 70
Figura 4.14: Forma de onda obtenida conversi´on D/A
SPI.Transfer16() de Arduino y realizar dos transacciones de 16 bits.
Una vez configurado se entra en la funci´on Loop y se comienzan a enviar los datos
que se sacan de memoria al conversor. La salida obtenida en nuestro ejemplo puede
verse en la figura Figura 4.14
    70/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 71
1 # include < SPI .h >
2 const int slave_select = 22;
3 const long waveform [120]
4 {
5 0 x7ff , 0 x86a , 0 x8d5 , 0 x93f , 0 x9a9 , 0 xa11 , 0 xa78 , 0 xadd , 0 xb40 ,
6 0 xba1 , 0 xbff , 0 xc5a , 0 xcb2 , 0 xd08 , 0 xd59 , 0 xda7 , 0 xdf1 , 0 xe36 ,
7 0 xe77 , 0 xeb4 , 0 xeec , 0 xf1f , 0 xf4d , 0 xf77 , 0 xf9a , 0 xfb9 , 0 xfd2 ,
8 0 xfe5 , 0 xff3 , 0 xffc , 0 xfff , 0 xffc , 0 xff3 , 0 xfe5 , 0 xfd2 , 0 xfb9 ,
9 0 xf9a , 0 xf77 , 0 xf4d , 0 xf1f , 0 xeec , 0 xeb4 , 0 xe77 , 0 xe36 , 0 xdf1 ,
10 0 xda7 , 0 xd59 , 0 xd08 , 0 xcb2 , 0 xc5a , 0 xbff , 0 xba1 , 0 xb40 , 0 xadd ,
11 0 xa78 , 0 xa11 , 0 x9a9 , 0 x93f , 0 x8d5 , 0 x86a , 0 x7ff , 0 x794 , 0 x729 ,
12 0 x6bf , 0 x655 , 0 x5ed , 0 x586 , 0 x521 , 0 x4be , 0 x45d , 0 x3ff , 0 x3a4 ,
13 0 x34c , 0 x2f6 , 0 x2a5 , 0 x257 , 0 x20d , 0 x1c8 , 0 x187 , 0 x14a , 0 x112 ,
14 0 xdf , 0 xb1 , 0 x87 , 0 x64 , 0 x45 , 0 x2c , 0 x19 , 0xb , 0 x2 , 0x0 , 0x2 ,
15 0xb , 0 x19 , 0 x2c , 0 x45 , 0 x64 , 0 x87 , 0 xb1 , 0 xdf , 0 x112 , 0 x14a ,
16 0 x187 , 0 x1c8 , 0 x20d , 0 x257 , 0 x2a5 , 0 x2f6 , 0 x34c , 0 x3a4 , 0 x3ff ,
17 0 x45d , 0 x4be , 0 x521 , 0 x586 , 0 x5ed , 0 x655 , 0 x6bf , 0 x729 , 0 x794
18 };
19 void init_spi () {
20 pinMode ( slave_select , OUTPUT );
21 digitalWrite ( slave_select , HIGH );
22 SPI . begin () ;
23 }
24 void config_dac () {
25 unsigned long reset = 0 x07000000 ;
26 unsigned long reference = 0 x08000001 ;
27 SPI . beginTransaction ( SPISettings (12000000 , MSBFIRST , SPI_MODE0 ) );
// gain control of SPI bus
28 digitalWrite ( slave_select , LOW );
29 SPI . transfer (( reset & 0 xFF000000 ) >> 24) ;
30 SPI . transfer (( reset & 0 x00FF0000 ) >> 16) ;
31 SPI . transfer (( reset & 0 x0000FF00 ) >> 8) ;
32 SPI . transfer (( reset & 0 x000000FF ));
33 digitalWrite ( slave_select , HIGH );
34 SPI . endTransaction () ; // release the SPI bus
35 SPI . beginTransaction ( SPISettings (12000000 , MSBFIRST , SPI_MODE0 )) ; //
gain control of SPI bus
36 digitalWrite ( slave_select , LOW );
37 SPI . transfer (( reference & 0 xFF000000 ) >> 24) ;
38 SPI . transfer (( reference & 0 x00FF0000 ) >> 16) ;
39 SPI . transfer (( reference & 0 x0000FF00 ) >> 8) ;
40 SPI . transfer (( reference & 0 x000000FF ) >> 0) ;
41 digitalWrite ( slave_select , HIGH );
42 SPI . endTransaction () ; // release the SPI bus
43 }
44 void setup () {
45 init_spi () ;
46 config_dac () ;
47 }
48 void loop () {
49 unsigned long value = 0 x03F00000 ;
50 unsigned long temp_value = 0 x00000000 ;
51 for (int i = 0; i < 120; i ++) {
52 temp_value |= (( waveform [ i] << 8) );
53 SPI . beginTransaction ( SPISettings (16000000 , MSBFIRST , SPI_MODE0 ) );
// gain control of SPI bus
54 digitalWrite ( slave_select , LOW );
55 unsigned long value2 = value | temp_value ;
56 SPI . transfer ((( value2 & 0 xFF000000 ) >> 24) );
57 SPI . transfer ((( value2 & 0 x00FF0000 ) >> 16) );
58 SPI . transfer ((( value2 & 0 x0000FF00 ) >> 8) ) ;
59 SPI . transfer ((( value2 & 0 x000000FF ) >> 0) ) ;
60 digitalWrite ( slave_select , HIGH );
61 SPI . endTransaction () ; // release the SPI bus
62 temp_value = 0 x00000000 ;
63 }
64 }
C´odigo 4.8: Ejemplo - Bus SPI: C´odigo
    71/127
    4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 72
    72/127
    CAP
´
ITULO 5
INTERRUPCIONES
Hasta ahora toda la obtenci´on de informaci´on se ha realizado de manera unidireccional y siempre iniciando la petici´on desde el Arduino. En este capitulo veremos c´omo
trabajar con las interrupciones en el microcontrolador Atmega328. Las interrupciones
son se˜nales recibidas por el procesador que indican que se ha producido un evento en
alguna de sus entradas o que un perif´erico quiere comunicarse con el procesador.
Las peticiones de interrupci´on se pueden implementar de dos maneras diferentes.
La primera es utilizando la t´ecnica de polling que consiste en mirar cada cierto tiempo,
de manera regular, si un perif´erico o el procesador requiere de atenci´on. Como puedes
imaginar este procedimiento es altamente ineficiente dado que el procesador pierde
mucho tiempo de procesamiento en comprobaciones, por lo que hoy en d´ıa no se suele
utilizar. El segundo m´etodo es a trav´es de pedidos de interrupci´on enviados por el
perif´erico o componente que quiere ser atendido. Al recibir el pedido de interrupci´on
el procesador deja de ejecutar su tarea actual y atiende a la interrupci´on mediante la
ejecuci´on de la rutina de interrupci´on correspondiente al tipo de interrupci´on que se
produjo..
La rutina de interrupci´on es la funci´on que se ejecuta cada vez que ocurre una
interrupci´on en el procesador y es una pieza de c´odigo que depende del elemento
que interrumpe. Por ejemplo cuando apretamos una tecla el controlador del teclado
env´ıa un pedido de interrupci´on que hace que el contador de programa en logar de
continuar lo que estaba ejecutando salta a una direcci´on de memoria donde est´a a su
vez la direcci´on de la rutina de atenci´on al teclado.
Existen diversas maneras de configurar la direcci´on de salto cuando ocurre una
interrupci´on. A continuaci´on, se nombran las m´as importantes:
Direcci´on fija: Las direcciones se encuentran ✭✭cableadas✮✮ al procesador por lo
que no se puede modificar la posici´on de las interrupciones.
Direcci´on variable: Las direcciones se pueden encontrar en cualquier lugar
y pueden variar de un procesador a otro del mismo tipo (configuraci´on del
procesador).
El m´etodo que nos interesa a nosotros es el de las direcciones variables, dentro de
este grupo, existen diversas implementaciones. Una de las m´as utilizadas y que adem´as
73
    73/127
    5.1. INTERRUPCIONES EN EL ATMEGA328 74
Figura 5.1: Ejemplo de indirecci´on
ser´a la que utilizaremos, es el direccionamiento vectorizado o indirecto. Mediante
esta t´ecnica una l´ınea de interrupci´on tiene asociada una posici´on en el vector de
interrupciones donde se encuentra la direcci´on de la funci´on que finalmente se lanzar´a.
En la Figura 5.1 se puede ver una ilustraci´on del procedimiento.
En este cap´ıtulo veremos c´omo trabajar con las interrupciones del microcontrolador ATmega328 y realizaremos algunos ejemplos con el objetivo de asentar todos los
conceptos te´oricos.
5.1. Interrupciones en el ATmega328
Cuando afrontamos la tarea de desarrollar una soluci´on basada en microcontroladores, lo primero que debemos realizar es un an´alisis de caracter´ısticas del mismo.
Algunas de las caracter´ısticas son el n´umero de pines de entrada/salida, tipos de comunicaciones soportadas, tipos de timers, interrupciones, etc. . . En este caso vamos
a analizar las interrupciones del microcontrolador Atmega328. Este microcontrolador
soporta hasta 27 tipos de interrupciones distintas, cada una con su nivel de prioridad correspondiente (ver Tabla 5.1). La prioridad va de mayor a menor, es decir, la
interrupci´on 1 (reset) tiene la m´axima prioridad y la n´umero 26 (spm ready) tiene la
m´ınima prioridad.
En la Tabla 5.1 podemos ver las interrupciones, internas y externas, soportadas
por el ATmega328. En la tabla se observa claramente lo explicado anteriormente en el
ejemplo del teclado, la interrupci´on asociada al puerto de interrupci´on INT0 mover´a
el contador de programa a la direcci´on 0x0002 y ser´a en ese lugar en el cual pondremos
el c´odigo para tratar dicha interrupci´on.
5.2. Manipulaci´on software
A la hora de utilizar las interrupciones en el entorno Arduino, tenemos 3 posibilidades:
Librer´ıa AVR: Utilizar la librer´ıa avr que implementa todas las interrupciones.
Librer´ıa Arduino: Gestionar las interrupciones mediante las herramientas proporcionadas por Arduino, aunque muchas interrupciones no se han implementado aun en esta librer´ıa.
    74/127
    5.2. MANIPULACION SOFTWARE 75 ´
Cuadro 5.1: Tabla de interrupciones
VectorNo. Program Address(2) Source Interrupt Definition
1 0x0000(1) 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
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
Manipulando directamente los registros correspondientes: Mediante las operaciones explicadas en Secci´on B.1 puedes manipular los registros de interrupciones
y gestionar al nivel m´as bajo posible las interrupciones. Hay que tener en cuenta
que esta manera es la menos portable, adem´as de ser la m´as complicada.
5.2.1. Librer´ıa avr
Como ya se ha comentado a lo largo del libro, el entorno Arduino realmente es un
conjunto de librer´ıas de abstracci´on que hacen las cosas m´as sencillas aunque puede
ocurrir que si queremos realizar tareas espec´ıficas, es probable que en algunos casos
esta librer´ıa no nos proporcione todas las comodidades que necesitemos, dado que ha
sido pensado para prototipos sencillos principalmente. En esta secci´on, veremos c´omo
utilizar la librer´ıa avr-libc para el manejo de las interrupciones. Esta librer´ıa es la que
utiliza Arduino y por lo tanto no tendremos que instalar ning´un paquete adicional.
Para empezar a utilizar las interrupciones lo primero que debemos hacer es incluir
el archivo de cabecera que contiene las definiciones de las funciones que vamos a
utilizar. Este archivo se llama ¡avr/interrupt.h¿, en el podemos encontrar diferentes
macros y definiciones que nos simplificar´an la tarea de gestionar las interrupciones.
El ATmega, al igual que la mayor´ıa de los microcontroladores, utiliza un bit de
habilitaci´on global de interrupciones. Este bit es el bit 7 del registro de estado y control
SREGdel ATmega. Adem´as cada perif´erico tiene su registro de interrupciones con su
bit de habilitaci´on correspondiente. Luego para que una habilitaci´on sea atendida
deber´a tener en 1 el bit de habilitaci´on global y a 1 el bit de habilitaci´on espec´ıfico.
Para habilitar o deshabilitar el bit de habilitaci´on global utilizaremos las macros
sei() o cli() respectivamente. Estas macros escriben un 1 o un 0 en el bit 7 de la
direcci´on de memoria 0x3F, que corresponde al registro de estado y control SREG.
Cuando una interrupci´on ocurre, se pone a 0 el bit de habilitaci´on global y se
deshabilitan todas las interrupciones. El software de usuario puede poner a 1 otra
    75/127
    5.2. MANIPULACION SOFTWARE 76 ´
1 # include < avr / interrupt .h >
2 ISR ( INT0 )
3 {
4 // Pro ce sa mi en to
5 evento = 1;
6 }
C´odigo 5.1: Ejemplo de definici´on de ISR
1 # include < avr / interrupt .h >
2 ISR ( BADISR_vect )
3 {
4 error =1;
5 }
C´odigo 5.2: Captura de interrupci´on no esperada
vez el bit de habilitaci´on para poder atender interrupciones anidadas, de manera que
cualquier interrupci´on habilitada pueda interrumpir la rutina de interrupci´on actual.
Luego, cuando se retorna de la interrupci´on, el bit de habilitaci´on global se pone
autom´aticamente a 1.
Para habilitar las interrupciones correspondientes a cada perif´erico debemos poner
a 1 el bit correspondiente en el registro de m´ascara de Interrupci´on. Si el bit de m´ascara
esta a 1 y a su vez el bit de habilitaci´on global est´a a 1 entonces el procesador atender´a
la interrupci´on cuando ocurra. Para el caso de interrupciones externas existen registros
que permiten configurar que tipo de eventos se atender´an. Como se detallar´a mas
adelante, estos eventos pueden ser un cambio de nivel en un pin, un flanco ascendente
o descendente, etc. Si se desea profundizar mas acerca del manejo de registros de
interrupciones se recomienda al lector la hoja de datos del fabricante.
Ahora que sabemos como habilitar o deshabilitar las interrupciones, el siguiente
paso para poder atender a la misma es definir la rutina de interrupci´on.
Para definir la rutina debemos utilizar la macro ISR. Esta macro tiene como
par´ametro una constante que define la interrupci´on que se desea atender. Normalmente el nombre de la constante es el mismo que el especificado en el datasheet del
fabricante. Como ejemplo, si quisi´eramos definir una rutina de interrupci´on para INT0
el c´odigo ser´ıa similar al mostrado en Cod. 5.1
Otra pr´actica que es recomendable sobre todo en dise˜nos que hacen uso intensivo
de las interrupciones, es capturar todas aquellas interrupciones para las cuales no se
ha definido ninguna rutina de interrupci´on (normalmente esto indica un bug). Para
capturar todas las interrupciones se debe pasar la constante BADISR vect a la macro
ISR. En Cod. 5.2 se puede ver un ejemplo.
Otro caso particular, y para el cual existe una soluci´on, es cuando se desea utilizar
una misma rutina de interrupci´on para dos interrupciones diferentes. Para conseguir
este comportamiento debemos utilizar la macro ISR ALIASOF junto a la macro ISR.
En Cod. 5.3 se puede ver un ejemplo de uso, en el cual la rutina de interrupci´on de
la interrupci´on PCINT1 pasa a ser igual a la de PCINT0.
    76/127
    5.2. MANIPULACION SOFTWARE 77 ´
1 # include < avr / interrupt .h >
2 ISR ( PCINT0_vect )
3 {
4 // Pro ce sa mi en to
5 evento = 1;
6 }
7 ISR ( PCINT1_vect , ISR_ALIASOF ( PCINT0_vect ) );
C´odigo 5.3: Captura de interrupci´on no esperada
5.2.2. Librer´ıa Arduino
Arduino provee de un conjunto de funciones para el tratamiento de interrupciones
en sus placas de desarrollo. La librer´ıa de Arduino solo da soporte a un n´umero
limitado de interrupciones, a continuaci´on explicaremos cada una de las funciones de
la librer´ıa:
noInterrupts(): Esta funci´on al igual que cli nos permite deshabilitar las interrupciones.
interrupts(): Habilita las interrupciones del mismo modo que sei.
attachInterrupt(): Permite seleccionar una rutina de interrupci´on para una
interrupci´on determinada. En funci´on de la placa de desarrollo el n´umero de pin
variar´a, se recomienda ver el datasheet, as´ı como el esquem´atico para averiguar el
pin correspondiente a la interrupci´on. Las interrupciones que est´an soportadas
por esta funci´on son las siguientes: INT0, INT1, INT2, INT3, INT4, INT5.
Adem´as, mediante un par´ametro podemos indicar en qu´e momento queremos
que se active la rutina de interrupci´on pudiendo ser:
• LOW: Cuando el pin est´a en estado ✭✭bajo✮✮.
• CHANGE: Cuando se produce un cambio de estado en el pin (de alto a
bajo o viceversa).
• RISING: En el flanco positivo de un cambio de estado.
• FALLING: En el flanco negativo. de un cambio de estado
detachInterrupt(): Mediante esta funci´on podemos eliminar la rutina de interrupci´on que se indique como par´ametro.
En general se recomienda usar la librer´ıa de AVR dado que aparte de ser m´as
eficiente es mucho m´as flexible.
5.2.3. Consideraciones importantes
A la hora de utilizar las interrupciones tenemos que tener algunas consideraciones
en cuenta.
Las interrupciones por defecto est´an habilitadas desde el inicio, por lo que no es
necesario utilizar la macro sei en un primer instante, aunque si es recomendable.
Algunas funciones como delay() o millis() utilizan las interrupciones para su funcionamiento. Es importante que se tenga en cuenta el uso que hacen las funciones de
    77/127
    5.3. EJEMPLO 1: PRIMERA RUTINA DE INTERRUPCION 78 ´
Cuadro 5.2: Ejemplo 1: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Bot´on button 2
Salida Led notification led 13
las interrupciones ya que por ejemplo, para usar una funci´on delay() dentro de una
rutina de interrupci´on debemos habilitar otra vez el bit de habilitaci´on global, dado
que por defecto al entrar en una rutina de interrupci´on se llama de forma impl´ıcita
a la macro cli.
5.3. Ejemplo 1: Primera rutina de interrupci´on
En este primer ejemplo veremos un ejemplo de utilizaci´on de las interrupciones en
el procesador ATmega328 usando las librer´ıas avr. El ejemplo consistir´a en encender
y apagar un led pulsando un bot´on. Seguramente al lector le resultar´a familiar pues
en la Secci´on 3.2 se estudi´o, sin embargo la principal diferencia es que en este caso no
leeremos el valor de un pin, sino que el propio evento ser´a el encargado de realizar la
acci´on.
A partir de esta secci´on no realizaremos los diagramas de flujo con el objetivo de
no hacer tan extensos los ejemplos, pero se recomienda que el lector tenga presente
siempre este paso en sus dise˜nos.
5.3.1. Tabla de entrada/salida
Lo m´as importante de la Tabla 5.2 es la elecci´on del pin para el bot´on. Si observamos el datasheet del ATmega328 veremos que los pines que tienen asociadas a
las interrupciones externas INT0 e INT1 son los pines PD1 y PD2 los cuales, en
el Arduino, est´an conectados a los pines f´ısicos 2 y 3. El pin para el led puede ser
cualquiera de los de salida digitales.
5.3.2. C´odigo
El c´odigo para manejar las interrupciones en los microcontroladores ATmega es
muy sencillo y se puede resumir en los siguientes pasos:
Habilitar registro global de interrupciones: Mediante las macros sei y cli
podemos habilitar y deshabilitar el bit global de interrupciones.
Habilitar interrupci´on espec´ıfica: Cada interrupci´on tiene asociado un bit
de m´ascara de activaci´on individual. Mediante este bit podemos permitir interrumpir al procesador mediante dicha interrupci´on. En nuestro caso y para
nuestro procesador el registro se llama EIMSK y el bit que pondremos en 1 es
el correspondiente a la INT0 (l´ınea 13).
Configurar la interrupci´on: Algunas interrupciones tienen registros que permiten configurar cuando lanzar la misma. En nuestro caso, para la interrupci´on
INT0 seleccionaremos el flanco descendente en el pin 2 poniendo un ’1’ en el
bit ISC01.
    78/127
    5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 79
1 #i n cl u d e <avr / i n t e r r u p t . h>
2
3 c o n s t i n t n o t i f i c a t i o n l e d = 1 3;
4 c o n s t i n t button = 2 ;
5
6
7 v oid se tup ( v oid )
8 {
9 pinMode ( button , INPUT) ;
10 pinMode ( n o t i f i c a t i o n l e d , OUTPUT) ;
11 d i g i t a lW r i t e ( button , HIGH) ;
12 s e i ( ) ;
13 EIMSK |= ( 1 << INT0 ) ;
14 EICRA |= ( 1 << ISC01 ) ;
15 }
16
17 v oid l o o p ( v oid )
18 {
19
20 }
21
22 ISR ( INT0 vect )
23 {
24 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d , ! di gi t al R e a d ( n o t i f i c a t i o n l e d ) ) ;
25 }
C´odigo 5.4: Ejemplo 1 - Interrupciones
Definir la rutina de interrupci´on: Mediante la macro ISR podemos indicar
las acciones que se deber´an tomar al atender una interrupci´on.
En Cod. 5.4 se puede ver la instanciaci´on de los pasos anteriormente nombrados.
Un procesamiento intensivo dentro de la rutina de interrupci´on no es una buena idea
dado que durante el tiempo en que el procesador se encuentre en esta funci´on no
realizar´a ninguna otra acci´on. En este ejemplo se ha modificado el estado del pin 13
dentro de la funci´on ´unicamente con el objetivo de simplificar el c´odigo, sin embargo
se deja como ejercicio al lector modificar el ejercicio para que la modificaci´on no se
haga dentro de dicha rutina de interrupci´on1.
Como puedes observar, el bucle principal est´a vac´ıo, sin embargo si pulsas al bot´on
ver´as como el led se enciende y apaga. Este comportamiento se llama asincron´ıa, dado
que no hay ning´un lugar donde se llame a la funci´on que enciende y apaga el led, es
el propio suceso el que llama a dicha funci´on.
5.4. Ejemplo 2: Midiendo distancias
En este ejemplo vamos a utilizar las interrupciones con la ayuda de la librer´ıa de
Arduino para medir las distancias a la que se encuentran los objetos haciendo uso
tambi´en del sensor HC-SR04 que se puede observar en la Figura 5.2.
El funcionamiento de este sensor es muy sencillo. El HC-SR04 posee 4 pines. A
continuaci´on se explicar´a cada uno de ellos:
1Como pista se podr´ıa utilizar alg´un tipo de ✭✭bandera✮✮ o ✭✭flag✮✮
    79/127
    5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 80
Figura 5.2: Sensor emisor/receptor ultrasonidos
Cuadro 5.3: Ejemplo 2: Tabla de entrada/salida
Entrada/Salida Descripci´on Nombre variable Pin
Entrada Pin de recepci´on
del sensor HCSR04
echo pin 2
Salida Pin gatillo del sensor HC-SR04tigger pin 3
Salida Transmisi´on puerto
serie
Tx 1
VCC: Este sensor trabaja a 5V por lo que podr´a ser conectado directamente
al Arduino a trav´es del pin de 5V del mismo.
GND: Com´un.
Trigger: El trigger o gatillo nos permite indicar al sensor de que queremos
hacer una medici´on. Para que el sensor sepa que queremos realizar la medici´on
y que no es un falso positivo debemos activar este pin como m´ınimo durante 10
microsegundos.
Echo: Este pin n0s indicar´a la distancia a la que se encuentra el objeto. ¿C´omo?
muy sencillo. Una vez que se recibe la se˜nal del trigger el sensor manda una
se˜nal de 40Khz que cuando rebota contra un objeto y vuelve al sensor de nuevo
hace que el sensor emita un pulso positivo con una longitud proporcional a la
distancia del objeto por el pin echo.
5.4.1. Tabla de entrada/salida
La Tabla 5.3 es bastante clara por lo que no se explicar´a nada m´as al respecto.
    80/127
    5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 81
5.4.2. C´odigo
El c´odigo se puede ver en Cod. 5.5 a continuaci´on veremos las partes m´as importantes del mismo.
Cuando el Arduino entra en la fase de setup configuramos la comunicaci´on serie
para poder mostrar la distancia por pantalla y adem´as configuramos los pines de
entrada/salida. En este caso tal y como nombramos en la Tabla 5.3 tenemos un pin
de entrada y dos de salida. Los de salida corresponden uno al trigger del sensor y el otro
al pin de transmisi´on Tx del puerto serie. El pin de entrada est´a conectado a la se˜nal
echo del sensor. Es importante en este punto recordar que tenemos l´ıneas limitadas de
interrupci´on en nuestra placa y que estas est´an conectadas a unos pines determinados.
En este caso, la l´ınea de interrupci´on que hemos utilizado es la conectada al pin 2 del
Arduino uno, que corresponde a la interrupci´on INT0 (ver en el datasheet).
El siguiente paso y seguramente el m´as importante en este ejemplo es la habilitaci´on de la interrupci´on del pin 2. Para ello utilizamos la funci´on attachInterrupt de
la librer´ıa de Arduino. Esta funci´on tiene tres par´ametros: Nº de interrupci´on, rutina
de interrupci´on que se ejecutar´a, y el tipo de evento que disparar´a la interrupci´on
(cambio de estado, flanco, etc). Como puedes observar esto es lo mismo que hemos
hecho en las l´ıneas 13 y 14 del ejemplo 1, solo que en lugar de escribir en los registros
utilizando las librer´ıas avr, lo que hacemos es llamar a esta funci´on que se encarga de
eso. Por lo tanto no incluimos el archivo de cabecera avr/interrupt en este ejemplo.
En este ejemplo el primer par´ametro es un 0. Este par´ametro no se refiere al pin al
que est´a conectado el sensor, sino a la interrupci´on del microcontrolador. En este caso
el pin 2 est´a conectado como ya hemos dicho a la interrupci´on 0 por lo que este ser´a
el n´umero que tendremos que utilizar. En las ´ultimas versiones del entorno Arduino
se ha a˜nadido una macro llamada digitalPinToInterrupt(pin) que permite despreocuparnos de este par´ametro. Comprueba si tu entorno ha a˜nadido este macro y si es
as´ı puedes utilizarla. El otro par´ametro importante es la funci´on que ser´a llamada
cuando recibamos la interrupci´on (rutina de Interrupci´on). La funci´on no deber´a retornar ning´un tipo (void). Por ´ultimo el par´ametro CHANGE indica que nos avise
en cualquier cambio en el pin, esto es, alto a bajo o bajo a alto. (en Subsecci´on 5.2.2
puedes ver las opciones que se pueden pasar a la funci´on).
Como ya comentamos en Secci´on 5.4 para saber la distancia a la que se encuentra
un objeto debemos medir el tiempo entre el flanco de subida del pin echo y el flanco de
bajada del mismo. Recordemos que la distancia es proporcional a dicho tiempo. Como
la funci´on interrupt echo() se llama cada vez que hay una variaci´on en el pin echo lo
´unico que tenemos que registrar es cuando empieza (flanco de subida) y cuando termina (flanco de bajada). Esta comprobaci´on se hace mediante la funci´on digitalRead()
que permite saber en que estado estamos y en funci´on del mismo registra el tiempo
en las variables correspondientes. El tiempo se registra con la funci´on micros(). Se
podr´ıa usar la funci´on millis() para medir tiempos en milisegundos pero esto no es
posible dentro de una rutina de interrupci´on.
En el datasheet del sensor se indica que el tiempo se debe dividir entre 58 para
obtener la distancia en cent´ımetros.
Otro punto a resaltar es la activaci´on del pin trigger que debe estar en estado
alto durante al menos 10 microsegundos. Aunque esto se podr´ıa realizar mediante un
timer e interrupciones, en este caso hemos optado por un delay en microsegundos.
El retardo final de 100 milisegundos (linea 21) ´unicamente tiene la funci´on de
mejorar la visualizaci´on de la distancia.
    81/127
    5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 82
1 c o n s t i n t e c h o pi n = 2 ;
2 c o n s t i n t t r i g g e r p i n = 3 ;
3
4 l o n g ech o end = 0 ;
5 l o n g e c h o s t a r t = 0 ;
6 l o n g e c h o d u r a ti o n = 0 ;
7
8
9 v oid se tup ( ) {
10 S e r i a l . b e gi n ( 9 6 0 0 ) ;
11 pinMode ( ec h o pin , INPUT) ;
12 pinMode ( t r i g g e r p i n , OUTPUT) ;
13 a t t a c h I n t e r r u p t ( d i gi t al P i n T o I n t e r r u p t ( e c h o pi n ) , i n t e r r u p t e c h o ,
CHANGE) ;
14 }
15
16 v oid l o o p ( ) {
17 d i g i t a lW r i t e ( t r i g g e r p i n ,HIGH) ;
18 d el a yMi c r o s e c o n d s ( 5 0 ) ;
19 d i g i t a lW r i t e ( t r i g g e r p i n ,LOW) ;
20 S e r i a l . p r i n t l n ( e c h o d u r a ti o n / 5 8 ) ;
21 d el a y ( 1 0 0 ) ;
22 }
23
24 v oid i n t e r r u p t e c h o ( ) {
25 i f ( di gi t al R e a d ( e c h o pi n ) == HIGH) {
26 ech o end = 0 ;
27 e c h o s t a r t = mic r o s ( ) ;
28 } e l s e {
29 ech o end = mic r o s ( ) ;
30 e c h o d u r a ti o n = ech o end − e c h o s t a r t ;
31 }
32 }
C´odigo 5.5: Ejemplo 2 - Midiendo distancias
    82/127
    CAP
´
ITULO 6
MULTITASKING Y TIMERS
Hasta ahora cada vez que buscamos repetir una acci´on en un tiempo determinado
hemos utilizado la funci´on delay() que nos provoca un retardo en la ejecuci´on durante el tiempo especificado en la misma, en milisegundos. Como se explicar´a en este
cap´ıtulo la utilizaci´on de dicha funci´on no debe realizarse en todos los casos, es m´as,
por regla general deber´ıa evitarse su uso.
Cuando se utiliza la funci´on delay() el Arduino se queda en un bucle activo. Por
lo tanto, durante ese tiempo el microcontrolador no realizar´a ninguna otra funci´on,
esto es, estar´a consumiendo energ´ıa pero no estar´a haciendo ning´un trabajo ´util como
por ejemplo podr´ıa ser registrar diferentes valores de los sensores.
En este cap´ıtulo veremos de manera resumida como utilizar otras funciones como
por ejemplo millis() para evitar los problemas que trae consigo la funci´on delay().
6.1. Timers
Los microcontroladores como ya sabr´as est´an compuestos de un conjunto de perif´ericos como por ejemplo los TIMER, USARTS, WDG, etc que nos permiten expandir su funcionalidad. En este caso hablaremos de un perif´erico muy importante y que
sin duda alguna es la base para todas las funciones relacionadas con el tiempo. Este
perif´erico es el timer. Un timer es un circuito que nos permite contar eventos, por
ejemplo ciclos de reloj, eventos externos, etc, para generar formas de ondas, circuitos
de reloj, comparadores, etc.
Dependiendo de en que microcontrolador est´e basado nuestro Arduino tendremos
m´as o menos timers, en el caso del microcontrolador ATmega328 podemos encontrar
tres timers: dos de 8 bits y uno m´as de 16 bits.
En la Figura 6.1 puedes encontrar un diagrama en bloques del timer de 8 bits del
ATmega328. A continuaci´on pasaremos a explicar cada uno de los componentes que
forman este m´odulo de tal modo que podamos tener una visi´on amplia a la hora de
utilizar cualquier funci´on relacionada con el tiempo en nuestros dise˜nos.
B´asicamente un timer esta formado por un circuito de control (Control Logic) que
se encarga de contar de manera ascendente o descendente los eventos que se producen
83
    83/127
    6.1. TIMERS 84
Figura 6.1: Diagrama TIMER 8 bits ATmega328
a su entrada, guardando la cuenta en un registro espec´ıfico (TCNTn). Este valor de
cuenta se puede utilizar para generar diferentes funcionalidades tales como comparar
con otro valor contenido en otro registro, generar una forma de onda o generar una
interrupci´on cuando se llega a un determinado valor, por ejemplo. El circuito selector
de reloj (Clock Select) determina cual es la entrada que se lee, reloj o evento externo
y en que flanco del evento se produce la captura, y si se va a tener en cuenta o no un
factor de escala. El factor de escala es un circuito denominado prescaler que genera un
evento cada cierta cantidad de ciclos de reloj. Esta generaci´on de factor de escala es
configurable desde el registro Clock prescaler del ATMega328, ubicado en la direcci´on
de memoria 0x61. Empezaremos explicando los registros para continuar con los modos
de funcionamiento del timer.
6.1.1. Registros
TCNTn: Este registro nos permite saber en todo momento el valor del timer
correspondiente (la letra n indica el n´umero de timer que puede ser 0 o 1). Este
valor se incrementa o decrementa, seg´un sea el modo habilitado, cada vez que
se produzca un evento de reloj, o de evento externo, a la entrada del circuito de
control. El registro TCNT0 es un registro de 8 bits por lo que el valor m´aximo
que se podr´a alcanzar es 255 o 0xFF en hexadecimal.
OCR0a: El valor de este registro se compara cada ciclo del timer con el valor
del registro TCNTn. Cuando se alcanza el valor configurado en este registro se
activar´a el bit OCF0A. El resultado de la comparaci´on se puede utilizar para
generar distintas formas de onda o PWM como se ver´a m´as adelante.
OCR0b: El mismo comportamiento que OCR0a.
    84/127
    6.1. TIMERS 85
TCCR0A, TCCR0B: Estos dos registros de control nos permiten configurar
el timer de tal modo que se comporte como un comparador, como un generador
de funciones, etc.
TIMSK0: Mediante este registro podremos habilitar o deshabilitar las diferentes interrupciones generadas por el TIMER (ver datasheet).
TIFR0: Parecido a TIMSK0 la diferencia radica en que este ´ultimo ´unicamente
nos indica que se ha generado una interrupci´on, como por ejemplo cuando se
llega al desbordamiento que se activa el bit TOV0.
6.1.2. Modos de funcionamiento
A continuaci´on veremos los distintos modos de funcionamiento que posee el timer.
Estos modos se ver´an de manera general, lo suficiente como para abordar los ejemplos
de este cap´ıtulo, y se deja como tarea para el lector una lectura mas detallada de los
mismos.
Modo normal: Es el modo m´as simple de utilizaci´on. Para activar este modo
se deben poner a 0 los bits WGM0:2 ubicados en los registros TCCR0A y
TCCR0B (cuidado, los dos primeros bits se encuentran en el registro TCCR0A,
sin embargo el ´ultimo bit WGM02 se encuentra en el registro TCCR0B). En
este modo, el registro TCNT0 se incrementa en cada ciclo de reloj hasta llegar
al valor 0xFF donde se desborda y vuelve al valor 0x00, activando a la vez la
se˜nal de overflow (TOV0). Esta se˜nal no volver´a a 0 de forma autom´atica, ser´a
el programador el encargado de realizar dicho cambio. Si est´a habilitado el bit
de interrupci´on por desbordamiento, cuando se produzca el mismo se generar´a
una interrupci´on.
Modo CTC o Clear Timer on Compare Match: Para activar este modo
debemos configurar los bits WGM02:0 al valor 2, esto es, ✭✭010✮✮. En este modo
el registro OCR0A se utiliza para variar la resoluci´on del timer. La diferencia
con el modo normal es que en este modo cuando se alcanza el valor configurado
en el registro OCR0a, el registro TCNT0 se pone de nuevo al valor 0. En la
Figura 6.2 se puede ver un esquema que ejemplifica su uso. TCNT0 empieza
en el valor 0 y va ascendiendo hasta llegar al valor configurado en OCR0a
(raya horizontal que indica el valor de comparaci´on). Una vez que se alcanza
dicho valor la bandera OC0a del registro TIFR0 se activa de tal modo que si
el programador comprueba dicho bit en su programa sabr´a si se ha alcanzado
el valor de comparaci´on. La ejecuci´on repetida del timer en modo CTC nos
genera una forma de onda en el pin de salida OCn. Esta forma de onda se
puede ver en un osciloscopio si conectamos una de las puntas del mismo al
pin asociado a la se˜nal OC0 en el caso de que el timer est´e configurado para
permutar dicho pin en cada acierto de comparaci´on (esto se configura en el
registro TCCR0A, TCCR0B). En el diagrama de la Figura 6.2 se muestran
diferentes valores del registro de comparaci´on OCR0a, variando el periodo de la
se˜nal. Seg´un el datasheet para generar una se˜nal cuadrada con una frecuencia
determinada podemos utilizar la formula mostrada en la Ecuaci´on 6.1. M´as
adelante, en los ejemplos, veremos c´omo utilizar esta formula y c´omo seleccionar
los valores para cada uno de los par´ametros.
    85/127
    6.1. TIMERS 86
Figura 6.2: Modo CTC
Figura 6.3: Modo Fast PWM
Modo Fast PWM: El modo Fast PWM o desde ahora PWM (Pulse Width
Modulation) (con el objetivo de simplificar), se habilita mediante la activaci´on
de los bit WGM02:0 = 3 o 7. Con este modo se nos proporciona un manera
de generar formas de onda PWM de alta frecuencia y alta precisi´on. El timer
cuenta desde el valor indicado en BOTTOM hasta el valor indicado en TOP,
cuando se alcanza el valor TOP se vuelve a iniciar la cuenta desde BOTTOM.
El valor de TOP es 0xFF cuando WGM02:0 es 3, sino toma el valor del registro
OCR0A cuando es 7. Posee dos modos, salida de comparaci´on no-invertida o
invertida. En el modo no-invertida la salida se pone a 0 cuando se alcanza el
valor de comparaci´on y a 1 cuando se llega al valor BOTTOM. En los ejemplos
veremos c´omo se materializa este modo pero para que el lector se haga una idea,
este modo nos permite ✭✭indicar✮✮ al microcontrolador qu´e porcentaje de tiempo
queremos que el pin est´e en alto y cuanto tiempo queremos que est´e en bajo,
pudiendo emular valores anal´ogicos variando el ancho del semiciclo positivo de
la se˜nal generada. Si por ejemplo alimentamos un motor con una frecuencia de
trabajo ✭✭X✮✮, podremos variar su velocidad modificando el porcentaje de tiempo
que se entrega energ´ıa al motor. En la Figura 6.3 se puede ver un ejemplo de
uso del modo PWM, a continuaci´on pasaremos a explicar el mismo aunque en
los ejemplos se ver´a de manera m´as detallada.
    86/127
    6.1. TIMERS 87
Figura 6.4: Registro TCCR1A
El contador en este caso empieza en 0 y el valor de OCRn no ha sido seleccionado
de modo que hasta que el mismo no se selecciona (tercera barra horizontal) no
se modifica el estado del pin OCnX. Una vez que se selecciona un valor para
OCRnx (OCR0a,b), cuando el contador alcanza el valor del registro OCRnx se
pone a estado bajo el pin OCnx en el caso de la se˜nal normal y se pone en alto
en el caso de la se˜nal invertida. El contador continua su incremento hasta que
llega al valor top. Ser´a cuando alcance este valor cuando el pin vuelva a cambiar
su estado.
Como puedes observar, en funci´on del valor del registro OCRnx podremos mantener m´as tiempo la se˜nal en alto, o lo que es lo mismo para nuestro ejemplo
podremos entregar mas energ´ıa al motor.
La ecuaci´on para el c´alculo de la frecuencia se puede consultar en Ecuaci´on 6.2.
Modo Fast PWM con correcci´on de fase: Este modo es muy parecido
al Fast PWM con la ´unica diferencia que la salida est´a en fase y adem´as la
frecuencia m´axima que se puede alcanzar es la mitad con respecto al modo
PWM ya que ahora el contador trabaja en modo ascendente y descendente.
La f´ormula para calcular el valor de TOP y del prescaler es la mostrada en
Ecuaci´on 6.3.
f0Cnx =
f clk
2 ∗ N ∗ (1 + OCRnX)
(6.1)
f0Cnx =
f clk
N ∗ (1 + T OP)
(6.2)
f0CnxP CPWM =
f clk
2 ∗ N ∗ T OP (6.3)
6.1.3. Ejemplos de uso
Una vez que tenemos claro los modos de uso de los timer y cada uno de los registros
que est´an implicados en su funcionamiento es el momento de programar el ATmega
para poner en pr´actica lo aprendido.
Como el Arduino utiliza el timer 0 para diferentes funciones como por ejemplo la
funci´on delay() dejaremos de lado este timer y utilizaremos en su lugar el timer 1. La
diferencia entre el timer 0 y el timer 1 es ´unicamente la cantidad de bits de ancho as´ı
como el n´umero de funciones que poseen cada uno. Tambi´en se podr´ıa haber utilizad
el timer 2 el cual es de 8 bits.
    87/127
    6.1. TIMERS 88
Ejemplo de uso b´asico
En este primer ejemplo veremos c´omo podemos configurar el timer ´unicamente
para que nos ✭✭avise✮✮ cuando haya pasado un determinado tiempo que indicaremos.
Lo primero que debemos tener claro a la hora de configurar un timer en este
modo es, como parece l´ogico, el tiempo en el cual queremos que nos avise. En este
caso, vamos a imaginar que queremos que nos avise cada 4 segundos de tal manera
que cada vez que se alcance ese instante de tiempo imprimiremos por el puerto serie
un mensaje.
Una vez que tenemos claro el tiempo en el que queremos que nos avise el TIMER,
el siguiente paso es configurar cada uno de los registros implicados. A continuaci´on,
explicaremos c´omo configurar cada registro. Es importante tener en cuenta que todas
esta informaci´on est´a extra´ıda del datasheet del microcontrolador. Es imposible
configurar bien un microcontrolador sin leerse el datasheet varias veces.
El registro TCCR1A posee diferentes bits que nos permiten modificar el comportamiento de la unidad de comparaci´on y dos de los bits que configuran el comportamiento del propio timer (WGM11 y WGM10 ). La raz´on por la cual solo se
pusieron dos bits de los cuatro necesarios para configurar el timer en este registro es
una decisi´on de dise˜no pero los bits WGM12 y WGM13 se encuentran en el registro
TCCR1B. En este caso buscamos configurar el timer en modo normal y deshabilitar
las unidades de comparaci´on pero todav´ıa nos falta un dato m´as, el prescaler. Para
calcular el valor del prescaler en primer lugar calculamos el tiempo que tarda nuestro
ATmega en incrementar el valor del registro TCNT1. Si estamos en un sistema con
reloj de 16Mhz, el tiempo por cada ciclo ser´a igual a 1/16000000 o lo que es lo mismo:
62.5 ns. Teniendo en cuenta esto, el tiempo que tardar´a el timer en desbordarse ser´a
igual a: 216 ∗ 62,5
−9 = 4ms. Como puedes observar este valor es muy peque˜no y no
nos sirve para alcanzar esos 4 segundos que buscamos. La soluci´on podr´ıa ser aumentar el n´umero de bits del timer o por otro lado aumentar el tiempo que tarda en
incrementar el registro TCNT. Como la primera opci´on (aumentar el n´umero de bits)
no depende de nosotros y solo podr´ıamos tenerla en cuenta si tuvi´eramos un timer
con m´as bits (no lo tenemos), abordaremos la segunda soluci´on, es decir, aumentar el
tiempo que tarda el timer en incrementar el registro TCNT. Si utilizamos un prescaler de 1024 entonces significar´a que por cada 1024 ciclos de reloj incrementaremos
en uno el valor del registro TCNT1 por lo que volviendo a los c´alculos anteriores el
tiempo que tardar´ıa en desbordarse el timer ser´ıa igual a: 1024∗2
16 ∗62,5−9 = 4,194s.
¡Lo conseguimos! ya tenemos el timer configurado para que tarde 4 segundos. . . En
realidad esto no es del todo cierto dado que seg´un nuestros c´alculos el tiempo es de
4.194 s por lo que intentaremos ajustar m´as el valor, recuerda, 1 d´ecima de segundo
puede significar ganar o perder una carrera en formula uno pero para una primera
aproximaci´on no est´a nada mal. Si queremos un tiempo exactamente igual a 4 segundos ´unicamente tenemos que realizar una ✭✭regla de tres✮✮ sabiendo que el tiempo que
tarda en incrementar el registros TCNT es de 1024 ∗
1
16000000 obteniendo el resultado
de que el n´umero de incrementos que se realizar´an en 4 segundos ser´a igual a 62500.
Con todos estos c´alculos ya sabemos que el valor del prescaler es de 1024 y que nos
gustar´ıa que en vez de a los 62536 incrementos el TIMER se desbordar´a a los 62500.
Para esto ´ultimo ´unicamente tenemos que modificar el valor de TOP o modificar el
valor en el que empieza el registro TCNT.
En el c´odigo mostrado en Cod. 6.1 puedes ver como se instancian los pasos anteriormente mencionados. A continuaci´on explicaremos las partes m´as importantes del
    88/127
    6.1. TIMERS 89
c´odigo.
En el setup() se llama a la macro cli(), como ya se explic´o en la Cap´ıtulo 5 esta
macro ´unicamente deshabilita las interrupciones de manera global, de manera que
podamos realizar toda la configuraci´on sin miedo a ser interrumpidos. Una vez deshabilitadas las interrupciones, el siguiente paso es configurar tanto el registro TCCR1A
como el registro TCCR1B. El valor 0 asignado a TCCR1A tiene que ver con lo explicado anteriormente. En la Figura 6.4 puedes la definici´on del registro. El registro
TCCR1B sin embargo s´ı que tiene un valor ✭✭´util✮✮ asignado. En un primer momento
el lector podr´ıa preguntarse por qu´e se asigna dicho valor. La raz´on se encuentra en
la configuraci´on del prescaler. Si echamos un vistazo a la Figura 6.6 y a la Figura 6.5
podemos ver que para aplicar un prescaler de 1024 tenemos que asignar el valor ✭✭101✮✮
a los bits ✭✭CS1:2-0✮✮. Como todos los dem´as bits deben est´ar a cero (ver explicaci´on
anterior y datasheet) el valor ✭✭00-00101✮✮ en decimal es 5.
El siguiente paso consiste en configurar el registro TIMSK1. En este caso hemos
optado por otro modo de asignaci´on el cual se explica en Secci´on B.1. El bit que hay
que habilitar es el TOIE1 que nos permitir´a indicar al microcontrolador que queremos
interrumpir nuestro programa cuando se produzca un overflow.
Por ´ultimo, en la fase de setup() tenemos que poner el valor inicial de 536 al registro
contador. Es muy importante que siempre que modifiquemos un registro de 16 bits
en la arquitectura AVR, en primer lugar modifiquemos el registro que representa la
parte alta (H) y despu´es el que representa la parte baja (L). En este caso el valor
en binario de 536 es ✭✭00000010 00011000✮✮ por lo que la parte alta la escribimos en
el registro TCNT1H y la parte baja en el registro TCNT1L. Como puedes observar
en este caso hemos utilizado la notaci´on binaria con el objetivo de que se cubran
todas las posibilidades, aunque la m´as portable es la notaci´on utilizada en el registro
TIMSK1.
Lo que resta del c´odigo ya ha sido explicado en cap´ıtulos anteriores y por lo tanto
no se volver´a a explicar.
Ejemplo de uso modo CTC
En este ejemplo cubriremos el modo de uso CTC. Este modo es el m´as utilizado
cuando queremos medir tiempos dado que es m´as sencillo que el modo normal y
permite actuar sobre un pin de manera inmediata sin necesidad de ninguna instrucci´on
software, adem´as de contar con una precisi´on mayor al poder modificar el valor TOP.
Se configurar´a el timer 1 de modo que cada medio segundo se encienda un led y cada segundo se encienda otro. En este caso podremos ver c´omo sin necesidad de ning´un
c´odigo extra podemos realizar acciones cada X segundos y con una precisi´on bastante
buena. Si sustituyes el led por un transistor que sustituya el bot´on de la c´amara de
fotos tendr´as un temporizador de obturaci´on. (S´olo realizar esta modificaci´on si se
tienen los conocimientos suficientes sobre electr´onica)
El modo de proceder es muy parecido al de Subsecci´on 6.1.3 con la diferencia que
en este caso tendremos que configurar el pin de salida y adem´as modificaremos el
valor de ✭✭overflow✮✮ del timer (aunque como ya veremos, realmente no es el valor de
✭✭overflow✮✮).
Los c´alculos para el tiempo en este caso se dejar´an como tarea para el lector
de modo que pueda comprobar si ha comprendido los conceptos explicados en la
Subsecci´on 6.1.3
En Figura 6.7 podemos ver los diferentes modos que posee el timer para actuar
    89/127
    6.1. TIMERS 90
1 v o l a t i l e l o n g l a s t u p d a t e = 0 ;
2 v o l a t i l e l o n g l a s t c om p a r e u p d a t e = 0 ;
3 i n t l e d = 1 3;
4 v oid se tup ( ) {
5 c l i ( ) ;
6 TCCR1A = 0 ;
7 TCCR1B = 5 ; //0b00−−0101
8 TIMSK1 |= ( 1 << TOIE1 ) ;
9 TCNT1H = 0 b00000010 ;
10 TCNT1L = 0 b00011000 ;
11 S e r i a l . b e gi n ( 9 6 0 0 ) ;
12 s e i ( ) ;
13
14 }
15
16
17 v oid l o o p ( ) {
18 i f ( l a s t u p d a t e != l a s t c om p a r e u p d a t e ) {
19 S e r i a l . p r i n t l n ( l a s t u p d a t e ) ;
20 l a s t c om p a r e u p d a t e = l a s t u p d a t e ;
21 }
22 }
23
24 ISR ( TIMER1 OVF vect ) {
25 l a s t u p d a t e = mic r o s ( ) ;
26 TCNT1H = 0 b00000010 ;
27 TCNT1L = 0 b00011000 ;
28 }
C´odigo 6.1: Ejemplo de uso TIMER
Figura 6.5: Bits de configuraci´on del presclarer
Figura 6.6: Registro TCCR1B
    90/127
    6.1. TIMERS 91
Figura 6.7: Bits para configuraci´on del pin de salida
sobre la salida. En nuestro caso elegiremos el modo ✭✭01✮✮ que nos permite permutar
la salida en cada comparaci´on con los registros OCR1A/B.
El siguiente paso consiste en configurar el timer para que actu´e en modo CTC.
En la Figura 6.8 se puede ver la tabla de configuraci´on en funci´on de los bits WGM.
A continuaci´on, explicaremos los referentes al modo CTC.
Si configuramos los bits WGM en el modo ✭✭0100✮✮ el timer empezar´a a contar
hasta llegar al valor configurado en el registro OCRxA y OCRxB, en este momento se
generar´a una interrupci´on siempre y cuando el bit OCIEXa o OCIEXb del registro
TIMSKX est´e habilitado, una vez que ocurra ese evento, el contador seguir´a ascendiendo hasta llegar al valor TOP donde se generar´a la interrupci´on de overflow y
finalmente se volver´a a contar desde 0.
El lector puede observar que lo que estamos realizando en este ejercicio es generar
dos frecuencias a partir de un ´unico timer, esto no siempre ser´a posible, en este
caso al ser una frecuencia m´ultiplo de la otra no habr´a ning´un problema.
Para ejemplificar todo lo explicado pasaremos a analizar el c´odigo Cod. 6.2.
En primer lugar, se bloquean las interrupciones con la macro cli() esto mismo
se podr´ıa haber hecho con la funci´on noInterrupts(), sin embargo, es m´as eficiente
y portable la segunda opci´on. Seguidamente se ponen a cero los dos registros de
configuraci´on del timer de modo que tengamos certeza de que partimos de inicialmente
de 0. Bas´andonos en el datasheet del ATmega328, configuramos el registro TCCR1A.
C´omo el lector puede observar el ´unico bit que tiene que estar habilitado es el 0 de los
comparadores, es decir, el COM1B0 y el COM1A0. Observa como en este caso se ha
utilizado la notaci´on que se considera m´as portable (l´ınea 5). Con el registro TCCR1B
hacemos lo mismo, solo que en este caso los bits a activar son los del prescaler y los
del modo de funcionamiento del timer.
Para ✭✭permitir✮✮ al timer enviar interrupciones al procesador, debemos activar el
bit OCIE1A. Con esto conseguiremos generar dos frecuencias distintas. La frecuencia
de dos hercios corresponde al comparador ✭✭A✮✮ mientras que la frecuencia de un hercio
pertenece al registro ✭✭B✮✮. Por otro lado, el registro ICR1 es el encargado de situar el
valor de TOP. Es importante observar que tanto el valor de ICR1 como el de OCR1B
es el mismo ¿por qu´e?.
Una vez configurados todos los registros se procede a poner los pines de entrada/salida en modo salida. Hay que tener en cuenta que hemos a˜nadido un pin m´as,
esto nos permitir´a generar una se˜nal de referencia para saber que nuestros c´alculos
son correctos. La elecci´on de estos pines se debe al mapeo que realiza el Arduino
de los pines del ATmega328. Si quieres saber m´as sobre esto se recomienda ver el
esquem´atico del Arduino UNO as´ı como el datasheet del ATmega328.
En la rutina de interrupci´on TIMER1 COMPA vect() se comprueba el valor del
    91/127
    6.1. TIMERS 92
Figura 6.8: Configuraci´on modo CTC
registro y en funci´on de si es menor que el registro ✭✭B✮✮ se modifica el valor de la
siguiente comparaci´on. Se deja al lector el razonar el porque de estas comprobaciones.
En el loop se genera una se˜nal de un hercio con el objetivo de compararla con la
generada por nosotros mediante el timer.
Ejemplo de uso modo Fast PWM
El ´ultimo modo que veremos en profundidad ser´a el modo Fast PWM. Como ya
se coment´o anteriormente, con este modo podemos modificar la cantidad de tiempo
que pasa una se˜nal en alto y en bajo, es decir, podemos modificar el ciclo de trabajo.
Existen diversas aplicaciones en las que se utilizan este tipo de se˜nales, como
ejemplos podemos nombrar los servos de los aviones radio control o los variadores de
los motores industriales, etc.
En este ejemplo veremos c´omo utilizar el modo Fast PWM para mover un servo
como el de la Figura 6.10. Este servo est´a incluido en el kit ✭✭Grove-Starter Kit for
Arduino✮✮.
Para mover un servo el primer paso que tenemos que seguir es el de estudiar los
fundamentos de los servos. Un servo es un dispositivo electromec´anico, que mediante
una l´ogica empotrada consigue decodificar una se˜nal de entrada para generar como
resultado un desplazamiento a una posici´on determinada. Estos dispositivos se suelen
utilizar en aeromodelismo pero su campo de aplicaci´on es mucho m´as extenso, otro
ejemplo podr´ıan ser los robots.
A continuaci´on pasaremos a explicar cada una de las partes que forman un servomotor:
Control de posici´on: El control de posici´on b´asicamente es un peque˜no controlador que decodifica la se˜nal de entrada, la compara con el feedback y decide
si mover o no el motor de corriente continua. (Ver Figura 6.9)
Motor de corriente continua: Es la parte fundamental en el apartado mec´anico y ´unicamente consiste en un motor conectado a un ✭✭puente en H✮✮ y una
reductora que permite transformar la mayor parte del giro en torsi´on.
    92/127
    6.1. TIMERS 93
1 v oid se tup ( ) {
2 c l i ( ) ;
3 TCCR1A = 0 ;
4 TCCR1B = 0 ;
5 TCCR1A = (1<<COM1B0) | (1<<COM1A0) ;
6 TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS12 ) ;
7
8 TIMSK1 = (1<<OCIE1A) ;
9
10 ICR1H = 0 b11110100 ;
11 ICR1L = 0 b00100011 ;
12
13 OCR1AH = 0 b01111010 ;
14 OCR1AL = 0 b00010001 ;
15
16 OCR1BH = 0 b11110100 ;
17 OCR1BL = 0 b00100011 ;
18
19 pinMode ( 9 ,OUTPUT) ;
20 pinMode ( 1 0 ,OUTPUT) ;
21 pinMode ( 7 ,OUTPUT) ;
22
23 S e r i a l . b e gi n ( 9 6 0 0 ) ;
24
25 s e i ( ) ;
26
27 }
28
29 ISR (TIMER1 COMPA vect ) {
30 // S e r i a l . p r i n t l n (” Entro ”) ;
31 i f (OCR1A < OCR1B) {
32 OCR1A = OCR1B;
33 } e l s e {
34 OCR1AH = 0 b01111010 ;
35 OCR1AL = 0 b00010001 ;
36 }
37
38 }
39 v oid l o o p ( ) {
40 d i g i t a lW r i t e ( 7 ,HIGH) ;
41 d el a y ( 5 0 0 ) ;
42 d i g i t a lW r i t e ( 7 ,LOW) ;
43 d el a y ( 5 0 0 ) ;
44 }
C´odigo 6.2: Ejemplo de uso TIMER modo CTC
    93/127
    6.1. TIMERS 94
Figura 6.9: Diagrama de bloques del sistema de control
Figura 6.10: Servo de Grove-Starter Kit for Arduino
Engranajes o gearbox: El sistema de engranajes es la parte donde se diferencia un servo de otro. En funci´on de la calidad de los mismos el movimiento
ser´a m´as o menos suave. Normalmente el potenci´ometro de feedback va unido
al sistema de engranajes.
Para mover un servo a una posici´on tenemos que enviar una se˜nal con unos par´ametros concretos. Normalmente los servomotores de aeromodelismo utilizan una frecuencia de 20 Hz por lo que la se˜nal viajar´a con un periodo de 50 ms. En la Figura 6.11
puedes ver el principio de funcionamiento.
Normalmente se confunden los t´erminos PWM y PPM, a continuaci´on intentaremos clarificar ambos conceptos. Desde el ATmega328 generaremos una se˜nal a una
frecuencia determinada, esa se˜nal la podemos modificar de modo que el ciclo de trabajo vaya desde el 0 % hasta 100 %. El servomotor espera un pulso de una duraci´on
concreta, por ejemplo, siguiendo los tiempos de la Figura 6.11, el servo espera una
se˜nal de 1ms para colocarse en la posici´on 0. Si nuestro periodo es de 50 ms, un pulso
de 1ms ser´a equivalente a un 2 % de la se˜nal en alto. Si modificamos el valor de la
frecuencia por ejemplo a 200 hz, el periodo ser´a 5 ms y un 2 % de dicha se˜nal ser´a
0.1 ms. Con esto llegamos a la conclusi´on de que para codificar un movimiento el
servomotor espera pulsos de un tiempo determinado y no pulsos de un determinado
voltaje (PWM).
En resumidas cuentas, para mover nuestro servo lo primero que tendremos que
hacer ser´a configurar un timer en nuestro caso el timer 1 en modo Fast PWM, luego
calcular el valor del registro TOP para generar una frecuencia de 20 Hz (se deja como
tarea para el lector el c´alculo de dicho valor) y por ´ultimo a˜nadir la l´ogica de control
para calcular los ciclos de trabajo en funci´on de los grados que se desee mover. (Aqu´ı
    94/127
    6.1. TIMERS 95
1 ms
1,5 ms
2 ms
20 ms
Figura 6.11: Diagrama de tiempos de un servo
1 v oid se tup ( ) {
2 c l i ( ) ;
3 TCCR1A = 0 ;
4 TCCR1B = 0 ;
5 TCCR1A |= ( 1 << COM1A1) | ( 1 << WGM11) ;
6 TCCR1B |= ( 1 << WGM13) | ( 1 << WGM12) | ( 1 << CS11 ) | ( 1 << CS10 ) ;
7 ICR1 = 5 0 0 0;
8 pinMode ( 9 ,OUTPUT) ;
9 pinMode ( 1 0 ,OUTPUT) ;
10 pinMode ( 7 ,INPUT) ;
11 S e r i a l . b e gi n ( 9 6 0 0 ) ;
12 s e i ( ) ;
13 OCR1A = 3 7 5;
14 }
15
16 v oid l o o p ( ) {
17 i f ( S e r i a l . a v a i l a b l e ( ) ) {
18 OCR1A = S e r i a l . p a r s e I n t ( ) ;
19 }
20 }
C´odigo 6.3: Ejemplo de uso PWM con servo
´unicamente a˜nadiremos la l´ogica de control para modificar el ciclo de trabajo a un
valor fijo, se deja como tarea para el lector el a˜nadir al c´odigo la l´ogica necesaria para
generar un ciclo de trabajo u otro en funci´on de los grados que se desee mover).
En Cod. 6.3 se puede estudiar el c´odigo implementado. A continuaci´on explicaremos como siempre las partes m´as interesantes del mismo.
En primer lugar, como siempre, se deshabilitan las interrupciones mediante la
macro cli(), seguidamente se configura el registro TCCR1A siguiendo el datasheet de
modo que la se˜nal permute en cada comparaci´on con OCR1A.
En el bucle loop() ´unicamente comprobamos si existen datos en el buffer de recepci´on serial. Si existe alg´un dato lo convertimos a entero y lo escribimos en el registro de
comparaci´on. Esto nunca se debe realizar dado que no hemos realizado ning´un control
de entrada y el usuario podr´ıa ingresar cualquier valor, incluso letras o s´ımbolos que
podr´ıan causar serios problemas en un dise˜no en producci´on.
Los valores recomendados son 125,250,375,500 que corresponden a 0,45,90,135,180
    95/127
    6.2. MULTITASKING 96
grados, la manera de calcular estos valores es muy sencilla. Pongamos un ejemplo: 180º
son 2ms o 2.5 ms en funci´on del servomotor utilizado, en este caso hemos utilizado 2.5
porque tras probar hemos visto que es el valor al que mejor responde el servomotor.
Adem´as hemos utilizado una frecuencia de 50Hz, por lo que tenemos un periodo de 20
ms. 2.5 ms es un 12,5 % del periodo. Si el periodo est´a determinado por el valor TOP
almacenado en el ICR1, en este caso 5000, un 12,5 de 5000 ser´a igual a 625. Como
ya hemos dicho estos valores hay que probarlos y en el caso del servo de ✭✭Seeed✮✮ este
valor hace que el servo se bloquee porque el potenci´ometro no tiene tanto recorrido,
por esta raz´on hemos recomendado un valor de 500.
6.2. Multitasking
Hasta ahora hemos visto c´omo funciona el core de tiempos de Arduino, al final
todo se resume en una palabra, TIMERs. En esta secci´on veremos c´omo abstraernos
de todos los detalles de implementaci´on y utilizando las librer´ıas de Arduino crear
nuestros programas ✭✭paralelos✮✮.
La idea que reside en esta implementaci´on del ✭✭multitasking✮✮ es muy sencilla,
nuestro programa principal actuar´a de ✭✭scheduler✮✮ de tal modo que en su bucle loop()
comprobar´a para cada elemento si es su turno y si es as´ı le conceder´a el ✭✭procesador✮✮
para que realice las acciones pertinentes.
En la industria se suele trabajar con dispositivos PLC, estos dispositivos se programan de una manera diferente a como estamos acostumbrados. La principal diferencia
es que no se programa de forma textual (hoy en d´ıa se est´a incorporando a la industria
esta manera de programar los PLC), por el contrario se programan mediante diagramas Ladder. Estos diagramas tienen un elemento llamado l´ıneas de alimentaci´on que
representan la entrada de se˜nales a nuestro elemento de proceso y las salidas del mismo. En la Figura 6.12 puedes ver un ejemplo de un programa realizado en ladder.
Cada pasada vertical, se denomina un ciclo de ✭✭scan✮✮. Para adaptar nuestros dise˜nos
a los entornos industriales y que en la medida de lo posible se minimice el coste de
mantenimiento o lo que es lo mismo el tiempo requerido para entender nuestro programa. En este libro hemos buscado una soluci´on estructurada que ayudar´a bastante
a la consecuci´on de este objetivo.
Apoy´andonos en la orientaci´on a objetos encapsularemos cada elemento en una
clase y dotaremos a la misma de un m´etodo llamado ✭✭scan✮✮, este m´etodo para simplificar el dise˜no nos devolver´a un valor ✭✭boolean✮✮ con la informaci´on del ciclo (correcto,
defecto), aunque lo correcto ser´ıa encapsular el valor de retorno en otra clase que nos
brindara m´as informaci´on.
La orientaci´on a objetos no es el objetivo de este libro, ´unicamente lo utilizaremos
como una manera de mostrar al usuario la verdadera importancia de estructurar el
c´odigo y para que pueda ver los beneficios inmediatos de su utilizaci´on. El c´odigo
que se utilizar´a en los ejemplos tratar´a utilizar´a los conceptos m´as sencillos posibles
en cuanto a orientaci´on a objetos se refiere intentando evitar aspectos m´as complejos
como el polimorfismo, herencia, etc.
Este es un buen momento para que te animes a leer el Secci´on C.1 y prepares
tu entorno de desarrollo para estos ejemplos, aunque nosotros sigamos utilizando el
entorno Arduino te servir´a para ver las bondades de Eclipse en cuanto tenemos un
programa medianamente complejo.
Como ya se coment´o en la Secci´on 6.1, para medir tiempos podemos utilizar varias
    96/127
    6.2. MULTITASKING 97
Figura 6.12: Ejemplo de programa en ladder
    97/127
    6.2. MULTITASKING 98
funciones de Arduino, nosotros utilizaremos la funci´on millis() que nos permite saber
cuanto tiempo ha pasado desde que se ha iniciado el programa. Para aplicaciones que
estar´an activas durante mucho tiempo hay que tener en cuenta que el contador se
pondr´a a 0 en 50 a˜nos, por lo que tendremos que salvar de alguna manera el tiempo
(por ejemplo guard´andolo en la memoria EEPROM).
6.2.1. Encendiendo y apagando un led de manera profesional
El led ha sido nuestro compa˜nero desde el primer cap´ıtulo, con ´el hemos realizado
la mayor´ıa de ejemplos y como no puede ser de otra manera, en este primer ejemplo
sobre multitasking recurriremos de nuevo a ´el. En la mayor´ıa de los ejemplos que
puedes encontrar por Internet ver´as que para encender y apagar un led se utiliza la
funci´on delay(). En entornos industriales esta funci´on es muy temida y la raz´on es
bastante sencilla: Si un programador que est´a encargado de mover un motor utiliza la
funci´on delay() para programar las esperas en el arranque ✭✭estrella-triangulo✮✮ puede
hacer que nuestro LCD deje de refrescarse y no avise de un error grave en un cilindro.
En este ejemplo vamos a retomar la tarea de encender y apagar un led cada
segundo. Para ello en vez de dejar que pase el tiempo vamos a cuantificar este tiempo
y mientras tanto aprovecharemos para hacer trabajo ´util.
En Cod. 6.4 puedes encontrar el c´odigo que a continuaci´on comentaremos. En
primer lugar en el setup() como siempre se inicializan las comunicaciones as´ı como
se configuran los pines en el modo correspondiente (hasta aqu´ı nada nuevo para el
lector).
Lo verdaderamente importante se encuentra dentro del bucle loop(), es aqu´ı donde
podemos ver dos funciones: scan led() y scan communications() estas funciones son las
encargadas de encender el led y mandar mensajes por el puerto serie respectivamente.
Los ✭✭timing✮✮ de estas acciones ser´an los siguientes:
Led: Se encender´a y apagar´a cada segundo.
Comunicaciones: Se mandar´a un mensaje con la cadena .Estoy aqu´ı ”m´as el
tiempo en milisegundos cada 100 milisegundos.
Empecemos por la funci´on scan led(), se utiliza una variable de tipo est´atica. La
raz´on por la cual hemos utilizado este tipo de variable es ´unicamente educativa de
modo que el lector sepa que estas variables solo se inicializan una vez y luego cada vez
que se entra a la funci´on se mantiene el valor anterior, es decir, es pr´acticamente igual
que una variable declarada fuera de todos los m´etodos tal y como hab´ıamos hecho
anteriormente. Mediante esta funci´on registramos el valor de la ´ultima ejecuci´on de la
acci´on a controlar. En el ✭✭if✮✮ es donde se encuentra el ✭✭scheduler✮✮. En este ✭✭if✮✮ comprobamos si ha pasado el tiempo m´ınimo que hab´ıamos programado. Es importante
observar que solo podemos asegurar un tiempo m´ınimo y no un tiempo m´aximo. Si lo
que buscamos es acotar el tiempo en el cual se debe realizar una determinada acci´on,
en ese caso necesitaremos dotar a nuestro sistema de un ✭✭scheduler✮✮ apropiativo de
modo que cuando se alcance un determinado tiempo m´aximo cancele todas las tareas y vuelva a la tarea que requiere atenci´on. Un sistema operativo muy liviano que
permite dotar al sistema de un ✭✭scheduler✮✮ apropiativo es el sistema FreeRTOS. Si
ejecutas el c´odigo podr´as ver que efectivamente por cada mensaje y permutaci´on del
led, se imprimen 5 mensajes con el mensaje ✭✭I’m here xxxx✮✮.
En la Subsecci´on 6.2.2 veremos una manera m´as elegante de ordenar el c´odigo como
se explic´o anteriormente, con el objetivo de facilitar el mantenimiento del mismo.
    98/127
    6.2. MULTITASKING 99
1
2 c o n s t i n t l e d p i n = 1 3;
3
4 #d e f i n e TIME TOOGLE LED 500
5 #d e f i n e TIME COMMUNICATION SEND 100
6
7 b o ol s c a n l e d ( ) {
8 s t a t i c l o n g l a s t t i m e l e d = 0 ;
9 i f ( ( m i l l i s ( ) − l a s t t i m e l e d )>= TIME TOOGLE LED) {
10 l a s t t i m e l e d = m i l l i s ( ) ;
11 d i g i t a lW r i t e ( l e d pi n , ! di gi t al R e a d ( l e d p i n ) ) ;
12 S e r i a l . p ri n t ( " Led toogle at: " ) ;
13 S e r i a l . p r i n t l n ( l a s t t i m e l e d ) ;
14 }
15 r e t u r n t r u e ;
16 }
17
18 b o ol sc an c ommunic a ti on s ( ) {
19 s t a t i c l o n g l a s t tim e c omm u ni c a ti o n = 0 ;
20 i f ( ( m i l l i s ( ) − l a s t tim e c omm u ni c a ti o n ) >= TIME COMMUNICATION SEND)
{
21 l a s t tim e c omm u ni c a ti o n = m i l l i s ( ) ;
22 S e r i a l . p ri n t ( "I’m here : " ) ;
23 S e r i a l . p r i n t l n ( l a s t tim e c omm u ni c a ti o n ) ;
24 }
25
26 }
27
28 v oid se tup ( ) {
29 pinMode ( l e d pi n , OUTPUT) ;
30 S e r i a l . b e gi n ( 9 6 0 0 ) ;
31 }
32
33 v oid l o o p ( ) {
34 s c a n l e d ( ) ;
35 sc an c ommunic a ti on s ( ) ;
36 }
C´odigo 6.4: Ejemplo de multitasking
    99/127
    6.2. MULTITASKING 100
6.2.2. Encendiendo y apagando un led de manera m´as profesional
La orientaci´on a objetos, como ya se ha comentado, puede reducir much´ısimo los
costes de mantenimiento as´ı como posibles fallos en la implementaci´on. Mantener un
programa en un ´unico fichero y de forma completamente acoplada no es buena idea.
En este ejemplo vamos a suponer que somos programadores de una empresa a la que
se le ha asignado la tarea de implementar un m´odulo ✭✭led✮✮ para proyecto futuro.
La empresa en su gu´ıa de estilos tiene definida una interfaz clara de que m´etodos
se deben exponer. En este caso, para simplificar, supondremos que ´unicamente est´a
especificado el m´etodo scan.
Para que nuestro trabajo pueda ser reutilizado en los dem´as proyectos tendremos
que encapsular el mismo lo m´aximo posible.
En Cod. 6.5 puedes ver el ejemplo completamente implementado. A continuaci´on
explicaremos las partes m´as importantes del mismo.
En C++, para declarar una clase, hay que utilizar la palabra reservada class. En
este caso hemos creado una clase que se llama LED. Esta clase ofrece al exterior un
m´etodo llamado scan(), tal y como se nos indic´o en la gu´ıa de estilos. La clase tambi´en
tiene un m´etodo privado update() que es el encargado de comprobar si ha llegado el
tiempo de permutaci´on. Ten en cuenta que este m´etodo es privado y por lo tanto no
le afecta la gu´ıa de estilos. Por otro lado, el constructor recibe el n´umero de pin y el
tiempo en el cual se quiere permutar el led (l´ınea 16).
Como puedes ver esta manera es mucho m´as elegante dado que si queremos crear
otro led, ´unicamente tenemos que a˜nadir otra l´ınea como LED led1 (13, 500). En este
ejemplo tan sencillo puede que no se vean las virtudes de este m´etodo, pero imagina
tener que controlar 200 LEDs, ¿ves ahora la diferencia?, probablemente con el m´etodo
tradicional (copia de c´odigo) y variables globales nos quedar´ıamos sin memoria en el
microcontrolador y la tarea de mantenimiento ser´ıa muy tediosa, con el incremento
en el coste asociado.
Se recomienda al lector extender este ejemplo con una nueva clase ServoMotor
que controle un motor de manera sencilla apoy´andose en el c´odigo Cod. 6.3 y en todo
lo aprendido hasta ahora. Una vez creada la clase a˜n´adela al proyecto y observa las
bondades del multitasking en Arduino.
    100/127
    6.2. MULTITASKING 101
1 c l a s s LED {
2 p r i v a t e :
3 i n t p i n l e d ;
4 l o n g l a s t u p d a t e t i m e ;
5 l o n g tim e t o u p d a t e ;
6
7 v oid update ( ) {
8 i f ( ( m i l l i s ( ) − t hi s −> l a s t u p d a t e t i m e ) >= tim e t o u p d a t e ) {
9 t hi s −> l a s t u p d a t e t i m e = m i l l i s ( ) ;
10 d i g i t a lW r i t e ( p i n l e d , ! di gi t al R e a d ( p i n l e d ) ) ;
11 }
12 }
13
14 p u bli c :
15
16 LED( i n t pi n l e d , l o n g tim e t o u p d a t e ) {
17 t hi s −> p i n l e d = p i n l e d ;
18 t hi s −> l a s t u p d a t e t i m e = 0 ;
19 t hi s −>tim e t o u p d a t e = tim e t o u p d a t e ;
20 pinMode ( p i n l e d , OUTPUT) ;
21 }
22
23 b o ol sc an ( ) {
24 update ( ) ;
25 }
26 } ;
27
28 LED l e d 1 ( 1 3 , 5 0 0 ) ;
29
30 v oid se tup ( ) {
31
32 }
33
34 v oid l o o p ( ) {
35 l e d 1 . sc an ( ) ;
36
37 }
C´odigo 6.5: Ejemplo de multitasking orientado a objetos
    101/127
    6.2. MULTITASKING 102
    102/127
    APENDICE A
´
CONSTRUYENDO NUESTRO
PROPIO ARDUINO
A.1. Introducci´on
En este ap´endice se guiar´a al lector en la construcci´on de su propio Arduino Uno,
el cual ser´a completamente compatible con un Arduino comprado en la tienda oficial.
Como se coment´o en el Cap´ıtulo 1, Arduino mantiene la filosof´ıa del software libre y
nos proporciona todos los esquemas necesarios para construir o mejorar su plataforma.
El Arduino que vamos a construir en este ap´endice es muy sencillo y ´unicamente
tendr´a lo indispensable para que el lector pueda conectarlo al ordenador y ejecutar
los ejemplos realizados en este libro, por lo que eliminaremos muchos elementos de
seguridad y filtrado que tiene el Arduino original con el objetivo de simplificar su
montaje.
A.2. Componentes necesarios
A continuaci´on, se detallar´a una lista con los componentes que se necesitan para
el montaje. Muchos de los componentes tienen equivalentes por lo que en este manual
hemos optado por los m´as comunes, no obstante, si el lector encuentra un equivalente
al componente no habr´a problema en sustituirlo, aunque se recomienda escoger en la
medida de lo posible los citados en la siguiente lista.
1. Protoboard
2. Fuente de alimentaci´on 5-18V CC
3. Cables Jumper
4. LM7805
5. Condensador electrol´ıtico 10 uF (x2)
103
    103/127
    A.3. ENSAMBLADO 104
6. Reloj de cristal de 16MHz
7. ATmega328
8. Condensador electrol´ıtico de 22 pf (x2)
9. FTDI232RL
10. USBASP1
A.3. Ensamblado
El primer paso es situar los componentes en la placa de montaje (protoboard desde ahora), aunque pueda parecer que la colocaci´on es algo meramente decorativo y
✭✭superfluo✮✮ es muy importante tenerlo en cuenta. La diferencia entre poner el condensador de desacoplo a 1 cent´ımetro del microcontrolador o ponerlo a 20 cent´ımetros
puede ser determinante en algunos sistemas2
. En la Figura A.1 se han dispuesto los
componentes de modo que al lector le sea sencillo identificar los mismos y situarlos
en el circuito por lo que, se recomienda que realice ligeras modificaciones manteniendo las reglas de conexi´on (mismo esquema) de modo que los componentes est´en m´as
pr´oximos entre ellos.
El circuito se puede dividir en tres partes. A continuaci´on, describiremos cada una
de ellas:
Etapa de alimentaci´on: El circuito est´a preparado para ser alimentado con
un voltaje desde 5 V a 18 V de corriente continua, es importante que la
corriente sea continua pues de lo contrario el circuito no funcionar´a, pudiendo
llegar a dejarlo inservible. Si el lector no tuviera una fuente de corriente continua con estas caracter´ısticas, puede montarse una peque˜na fuente, para lo cual
necesitar´a entre otras cosas un transformador (fuente con transformador) y un
puente de diodos (en Internet hay mucha informaci´on a cerca de las fuentes de
alimentaci´on).
Lo siguiente es conectar los terminales a la protoboard. Existe un convenio de
conexi´on por el cual se recomienda conectar el terminal positivo a la primera
fila de la protoboard y el negativo a la segunda. El lector podr´a reconocer estas
filas porque normalmente vienen separadas del resto de la protoboard.
Ahora que la placa est´a alimentada, solo queda situar los componentes tal cual
aparecen en el esquema Figura A.2 tambi´en puedes guiarte por la Figura A.1
aunque recuerda, ´esta no es la distribuci´on ´optima. Es importante que se respete
la polaridad del condensador electrol´ıtico dado que de lo contrario, si se
supera la tensi´on inversa umbral, podremos romper el mismo. Para saber c´omo
conectar el condensador busca una l´ınea vertical de color blanco que indica el
terminal negativo. (ver Figura A.1)
A la salida del integrado LM7805 (l´ınea naranja) tendremos 5 V estables que
ser´a la ✭✭fuente de alimentaci´on✮✮ del microcontrolador ATmega328.
1USBASP es un programador para los microcontroladores AVR libre, por lo que desde la web del
autor (http://www.fischl.de/usbasp/) el lector podr´a acceder tanto al firmware como al circuito
pudiendo construir su propio programador.
2Los cables tienen una capacitancia y una inductancia que afecta al circuito, en dise˜nos donde la
precisi´on es muy importante, hay que tener en cuenta la longitud de los cables entre otros factores
    104/127
    A.3. ENSAMBLADO 105
5v-12v
16MHz Cristal
Azul = Tierra
Rojo = Entrada
Naranja = 5v estables
Cyan = Cristal
------------------- Marron = Rx(Arduino) - Tx(FTDI)
Morado = Tx(Arduino) - Tx(FTDI)
Figura A.1: Protoboard de referencia
Figura A.2: Esquema de conexiones
    105/127
    A.3. ENSAMBLADO 106
Figura A.3: Patillaje ATmega328
Microcontrolador: El microcontrolador ATmega328 es el coraz´on del Arduino
UNO, es en ´el donde se cargaran los programas. En esta parte del montaje nos
encargaremos de que el coraz´on empiece a ✭✭latir✮✮. Para ello, lo primero que hay
que saber es c´omo est´an distribuidos los terminales. En la Figura A.3 se puede
observar que los pines para alimentar el microcontrolador son el 7 y 8, pero...
¿D´onde empieza la numeraci´on? para ello se sigue un convenio por el cual todos
los circuitos integrados disponen de una marca que indica el pin con el inicio de
la numeraci´on. En la Figura A.1 se puede observar el punto que indica el inicio.
Una vez que el los terminales de alimentaci´on del microcontrolador est´an conectados a la salida del LM7805, ahora solo resta el incorporar un componente
que permita al ATmega328 latir a una frecuencia determinada (siguiendo la
analog´ıa del coraz´on humano) este componente es un reloj de cuarzo. El reloj
de cuarzo genera una onda cuadrada con una frecuencia determinada, normalmente los microcontroladores poseen un reloj RC3 pero debido a su precisi´on
y a que normalmente oscilan a una velocidad inferior, a˜nadiremos el reloj de
cuarzo.
Siempre que se va a dotar al microcontrolador de un reloj externo se utiliza el
mismo circuito, que consta de un reloj y dos condensadores, cada uno de ellos
en paralelo al reloj. Cuanto m´as cerca est´e el reloj de los terminales de conexi´on
del microcontrolador mayor precisi´on, por lo que de nuevo se recomienda que
´unicamente se siga la Figura A.2 para guiarse y se modifiqu´e la disposici´on de
los elementos.
Finalmente falta comunicar el pin 20 (AVCC) con VCC, esto se mantendr´a
as´ı hasta que el lector quiera hacer uso del ADC (Digital Analogic Converter)
cuando tendr´a que conectar el pin 20 a VCC a trav´es de un filtro de paso bajo.
Comunicaci´on: Ahora que el microcontrolador ya es capaz de oscilar a una
frecuencia determinada (en nuestro caso 16MHz) tenemos que situar el canal de
comunicaci´on entre el computador y el microcontrolador. Si el microcontrolador
tuviera integrado un chip para USB ´unicamente tendr´ıamos que conectar los
cables del USB al mismo, pero como el ATmega328 solo posee de comunicaci´on
serial (para la programaci´on) tendremos que hacer una serie de modificaciones
de modo que el Arduino se pueda programar desde el computador:
• Ordenador con puerto serie: Si tu ordenador posee de puerto serie, entonces
3Los relojes RC suelen tener una frecuencia inferior y una precisi´on menor frente al reloj de
cuarzo.
    106/127
    A.4. PROGRAMACION DEL BOOTLOADER 107 ´
no hay ning´un problema, ´unicamente tendr´as que conectar el pin Tx al Rx
del ATmega328 (pin 2) y el pin Rx al Tx (pin 3).
• Ordenador con USB: Si tu ordenador ´unicamente dispone de puertos USB,
tendr´as que utilizar un conversor como puede ser FTDI232RL que realiza
la labor de identificar el dispositivo y gestionar la UART de modo que los
datos que escribamos bajo la pila USB se trasmitan de forma correcta bajo
el protocolo serial.
En el caso de estar utilizando el conversor FTDI232RL y estando conectados
los puertos Rx y Tx a los pines Tx y Rx del ATmega328 respectivamente.
Solo queda alimentar el microcontrolador FTDI232RL desde el LM7805 y ya
tendremos el Arduino listo para ser programado.
A.4. Programaci´on del Bootloader
Para que el ATmega328 se convierta en un Arduino, necesitamos cargar el bootloader, para ello podemos seguir diferentes m´etodos. En el este libro se explicar´a el
✭✭quemado✮✮ con un programador USBasp el cual es completamente libre.
Antes de empezar a quemar el bootloader en primer lugar se explicar´a qu´e es y
qu´e funciones realiza por nosotros.
El ATmega328 tiene una memoria Flash de 32 KB en la que se podr´ıa almacenar
un programa compilado para este microcontrolador y ejecutarlo posteriormente. Para
programar esta memoria se necesitan herramientas como el programador STK-500 por
lo que Arduino decidi´o a˜nadir una interfaz USB de modo que realice la conversi´on de
los datos que llegan por USB a un protocolo serial y los multiplexe en el tiempo del
mismo modo en que lo har´ıa un programador como el STK-500.
El bootloader ocupa un espacio determinado en la memoria flash dado que realmente es un programa que se inicia cuando alimentamos el microcontrolador ejecutando un conjunto de instrucciones que permite al ATmega saber si hay alguna
petici´on para programar el Arduino y si es as´ı seleccionar la direcci´on de memoria
donde ✭✭situar✮✮ dicho programa. Si el bootloader no encuentra ning´un petici´on de programaci´on entonces salta a la direcci´on de memoria del bucle de setup del programa
y a continuaci´on empieza el bucle de operaci´on.
Como el lector habr´a podido apreciar, no es necesario ✭✭quemar✮✮ este bootloader
pero si no se quema el lector tendr´a que contar con alg´un programador AVR para
✭✭quemar✮✮ el programa y adem´as tendr´a que realizar la programaci´on por ISP mediante
el conector ICSP4lo cual es mucho m´as incomodo.
A continuaci´on vamos a proceder a ✭✭quemar✮✮ el bootloader, para ello lo primero
que necesitamos es un programador de AVR, en este libro, como ya se ha dicho
anteriormente se utilizar´a el programador libre USBasp. La instalaci´on de los drivers
del mismo se deja como trabajo para el lector. Una vez que el programador est´a
correctamente instalado, tenemos que realizar las conexiones, para ello hay que tener
en cuenta que el conexionado ICSP hay que realizarlo con mucho cuidado.
4
ICSP son las siglas de In Chip Serial Programming, con este conector, se permite reprogramar el microcrocontrolador sin necesidad de desoldarlo y por lo tanto aumentando la vida ´util del
microcontrolador
    107/127
    A.4. PROGRAMACION DEL BOOTLOADER 108 ´
(a)Conector ICSP de 6 pines (b)Conector ICSP de 10 pines
Figura A.4: Conectores ICSP
Existen dos tipos de conectores ICSP, de 10 pines (ver Figura A.4b) y de 6 pines
(ver Figura A.4b).
Para localizar la ubicaci´on de cada uno de los pines se puede utilizar un mult´ımetro
y medir la diferencia de potencial (voltaje) entre un pin y tierra de tal manera que
el pin que devuelva el voltaje de programaci´on (3.3-5V) ser´a el VTG o VCC y el
pin com´un ser´a tierra. El lector tambi´en se puede guiar por el cable rojo, de modo
que ´este ser´a el pin 1. Existen alternativas para facilitar la conexi´on del programador
al Arduino como por ejemplo el conversor ✭✭ICSP Header✮✮ cuya ´unica funcionalidad
es proporcionar una interfaz ✭✭inline✮✮ que se podr´a conectar a la protoboard siendo
mucho m´as sencilla la conexi´on final al ATmega.
Una vez que el lector haya localizado cada uno de los pines del conector o haya
adquirido un conversor, ya est´a en disposici´on de realizar la conexi´on entre el ICSP y
el microcontrolador. Los pines son los proporcionados por la interfaz de comunicaci´on
ISP, es decir, GND, VCC, MISO (Master Input Slave Output), MOSI(Master Output
Slave Input), SCK (Clock), RESET. La conexi´on ISP es muy sencilla gracias a lo
descriptivo de los nombres de sus pines, sin embargo, a continuaci´on se detalla las
conexiones a realizar:
Pin MISO programador → Pin 18 ATmega328
Pin MOSI programador → Pin 17 ATmega328
Pin SCK programador → Pin 13 ATmega328
Pin RESET programador → Pin 1 ATmega328
Ahora solo queda cargar el bootloader al ATmega328, para ello hay que conectar
el programador al conversor ICSP o en caso de no haber utilizado el conversor, conectar cada uno de los pines del conector ICSP al microcontrolador. Despu´es conectar
el programador al computador, es importante que los drivers est´en completamente
configurados, de lo contrario el proceso no se completar´a con ´exito. El siguiente paso es abrir el Arduino IDE y seleccionar “Herramientas → tarjeta → Arduino Uno”
(Figura A.5) con lo que indicaremos al IDE que vamos a programar un Arduino Uno,
despu´es seleccionamos “Herramientas → programador → USBasp”, por ´ultimo hay
    108/127
    A.4. PROGRAMACION DEL BOOTLOADER 109 ´
Figura A.5: Captura de pantalla del Arduino IDE
que pulsar sobre “Herramientas → Grabar secuencia de inicio”. Si todo ha ido bien
el lector podr´a quemar sus propios sketch.
    109/127
    A.4. PROGRAMACION DEL BOOTLOADER 110 ´
    110/127
    APENDICE B
´
MANIPULACION DE REGISTROS
´
B.1. Introducci´on
Cuando un dise˜nador se enfrenta a la tarea de dise˜nar una soluci´on inform´atica en
un microcontrolador, debe manejar una serie de conceptos que normalmente no son
necesarios o indispensables cuando se programa una soluci´on en un computador de
prop´osito general. Tener claro donde almacenar una cadena de texto o que espacio de
memoria asignar para una determinada tarea son algunos de estos conceptos.
En este ap´endice se va a guiar al lector en el aprendizaje de las operaciones l´ogicas
con bits. Es importante tener en cuenta que la informaci´on en un microcontrolador
se suele almacenar en registros. Estos registros pueden almacenar datos de diferente
naturaleza, por ejemplo, un microcontrolador que forme parte de una impresora puede
tener un registro para el control de la misma (calentar rodillo, situar en posici´on inicial,
iniciar operaci´on) y otro registro para los datos como por ejemplo la hoja a imprimir.
Cuando el dise˜nador programa la l´ogica de la impresora, deber´a obtener y validar la
informaci´on contenida en estos registros. En este ap´endice se mostrar´an las diferentes
t´ecnicas que se utilizan para obtener y tratar los datos de los registros.
B.2. ¿Qu´e es un registro?
Antes de tratar de realizar operaciones con los registros, debemos tener claro qu´e
es realmente un registro. Se puede explicar qu´e es un registro desde dos perspectivas:
la perspectiva l´ogica y la perspectiva f´ısica. En este ap´endice nos centraremos en la
primera dado que buscamos el tratar con la informaci´on almacenada en los registros
y no el implementar f´ısicamente el mismo.
El registro es la unidad de almacenamiento que se encuentra en el nivel superior
dentro de la jerarqu´ıa de memorias (ver Figura B.1), es decir, es la memoria m´as
r´apida y m´as cara de todas. Este es el motivo por el cual es un recurso limitado en
los computadores y tan preciado por los compiladores. Como ya hemos nombrado en
la introducci´on, existen registros de muchos tipos como por ejemplo los registros de
datos que tienen la funci´on principal de almacenar datos. Un ejemplo de este tipo de
111
    111/127
    B.2. ¿QUE ES UN REGISTRO? 112 ´
registros es el registro acumulador de la ALU de los microcontroladores PIC. Tambi´en
existen registros espec´ıficos como el registro SP (stack pointer) de la arquitectura
ARM el cual ´unicamente se utiliza para almacenar la direcci´on de memoria del
✭✭top✮✮ de la pila.
Registros del 
procesador
Memoria caché
(L1,L2,L3)
Memoria RAM
(random access memory)
Disco duro
Almacenamiento secundario
Cloud
(Copias de seguridad)
Capacidad Velocidad y
Coste por bit
Figura B.1: Jerarqu´ıa de memoria
Todos estos registros tienen unas propiedades comunes como son:
1. Capacidad (Ancho): Los registros tienen un ancho determinado que depender´a del prop´osito para el que se haya dise˜nado. Por ejemplo, un registro que
mantenga la direcci´on de la siguiente instrucci´on a ejecutar IR (instruction register) deber´a tener la capacidad suficiente que permita codificar las instrucciones
de un programa. Si se quiere un computador con la capacidad de ejecutar 256
instrucciones se deber´a dise˜nar un registro con un ancho de 8 bits.1
2. Tecnolog´ıa: Los registros f´ısicamente se dise˜nan con una tecnolog´ıa determinada. Esta tecnolog´ıa ser´a determinante a la hora de comparar otras caracter´ısticas
como la latencia, capacidad de integraci´on. . .
3. Direcci´on: Un registro queda identificado univocamente por su direcci´on de
memoria como por ejemplo podr´ıa ser la direcci´on 0x00001A1B
Podemos imaginar un registro como una caja (ver Figura B.2) que tiene diferentes
compartimentos. Cada uno de estos compartimentos es un bit. Dependiendo del tipo
de registro nos interesar´a toda la ✭✭caja✮✮ (registro) o ´unicamente uno de sus ✭✭cajones✮✮
(bit). Por ejemplo, si tenemos un registro de datos, seguramente nos interese el registro
entero y no uno de sus bit dado que normalmente cuando se accede a un registro de
datos se busca el valor del n´umero almacenado, esto no implica que no se pueda
acceder a un ´unico bit, por ejemplo para saber si el n´umero es positivo o negativo en
un n´umero representado en signo magnitud.
1Con 8 bits se pueden codificar 28
instrucciones
    112/127
    B.3. OPERACIONES CON REGISTROS 113
0 0 0 0 0 0 0 0
Figura B.2: Registro de 8 bits (byte) como una ✭✭caja✮✮
B.3. Operaciones con registros
Ahora que ya sabemos qu´e es un registro, estamos en disposici´on de realizar diferentes operaciones con ellos. En esta secci´on y con el objetivo de que el lector pueda
poner sus conocimientos en pr´actica vamos a utilizar registros reales del microcontrolador ATmega328, a continuaci´on se realizar´a una peque˜na descripci´on de la utilidad
de cada uno de estos registros:
DDRC (Data Direction Register C ): Este registro se encuentra en la direcci´on 0x07 y permite configurar los pines del puerto2
como entrada(0) o salida(1),
en funci´on del bit que se situ´e en cada uno de las posiciones del registro, por
ejemplo la configuraci´on 0b00000001 configura todos los puertos como entrada(0) y el primero3
como salida(1)4.
PORTC (Port Output Register C ): Este registro sirve para activar las
resistencias de pull-up que el microcontrolador posee en cada uno de los pines
configurados como entrada. Si el pin est´a configurado como salida (DDRX(n)
== 1) este registro determinar´a el voltaje ✭✭alto✮✮ o ✭✭bajo✮✮ a la salida del pin.
PINC (Pin Input Register C ): Este registro normalmente formar´a parte de
las estructuras ✭✭IF ... THEN✮✮, en cada uno de sus bits obtendremos el valor de
un pin configurado como entrada. Si el valor del bit es 1 significar´a que el pin
que representa tiene VCC como entrada, sin embargo, si el valor es 0 entonces
significar´a que el voltaje de entrada es GND.
B.3.1. Activar un bit
Imaginemos que queremos configurar el pin 0 del puerto ✭✭c✮✮ como salida, para
ello sabemos que tenemos un registro espec´ıfico como es el registro DDRC. Sabemos
tambi´en que la direcci´on de memoria del registro es la 0x07 y que podemos acceder
a ´el mediante el puntero DDRC que apunta a dicha direcci´on5. Si queremos activar
el led 0 como salida podr´ıamos asignar el siguiente valor a DDRC :
1
0b00000001
0x01
DDRC = 1 << PIN0
2Los pines de entrada/salida se agrupan en los llamados ✭✭puertos✮✮. Cada puerto controla un
determinado n´umero de pines.
3Es importante tener en cuenta que los bits se empiezan a numerar por 0.
4Toda esta informaci´on se puede obtener del Datasheet del microcontrolador ATmega328.
5Estos punteros son mapeados normalmente por la librer´ıa del fabricante.
    113/127
    B.3. OPERACIONES CON REGISTROS 114
Los cuatro elementos de la lista anterior hacen referencia al mismo valor, es decir,
al 1 en decimal. El primer elemento representa el 1 en decimal. Se sabe que un
valor est´a expresado en binario cuando le antecede el prefijo ✭✭0b✮✮. El tercer elemento
(0x01 ) representa el mismo valor pero en hexadecimal, es importante recordar, que
un d´ıgito hexadecimal equivale a cuatro bits (el conjunto de 4 bits se le conoce como
nibble). La ´ultima representaci´on es la m´as com´un pero no ser´a explicada hasta la
Subsecci´on B.3.3
Ahora imaginemos que pasa si ocurrir´ıa si quisi´eramos configurar el pin 1 del
puerto ✭✭c✮✮ como salida. En principio actuar´ıamos de la misma manera, es decir,
configurar´ıamos el valor 0b0000010, pero como el lector habr´a podido observar, el
valor del pin 0 cambiar´a y se configurar´a como entrada (0). La manera m´as sencilla
de solventar este comportamiento es asignar el valor 0b00000011 pero ¿si no se conoce
que pines est´an como salida en un estado anterior, c´omo se calcula el valor a escribir?
esta pregunta ser´a contestada en la siguiente secci´on.
B.3.2. ORing, activaci´on de un bit
Para evitar el comportamiento observado en la secci´on anterior, se hace uso de la
operaci´on OR. Esta operaci´on permite activar un bit sin ✭✭afectar✮✮ a los dem´as bits
del registro.
Imaginemos el mismo ejemplo que el de la Subsecci´on B.3.1, es decir, deseamos
configurar el primer y segundo bit como salida. La soluci´on planteada en la primera
secci´on es incomoda e ineficiente dado que se necesita saber que bits est´an activados
anteriormente para poder calcular el valor a escribir. Por lo que vamos a aplicar la
funci´on OR de la siguiente manera:
1 DDRC = DDRC | 0 b10
De este modo conseguiremos el siguiente valor:
0b00000001
OR 0b00000010
-------------
0b00000011
Como el lector habr´a observado, el resultado es el esperado, tanto el pin 1 como
el 2 est´an configurados como salida. La operaci´on ORing ´unicamente afecta a los
valores que poseen un 1 l´ogico, esto es debido a que el 0 act´ua como operador neutro
(A OR 0 = A). El operando que utilizamos para realizar la operaci´on OR sobre el
registro se llama ✭✭mascara✮✮.
B.3.3. Bit Shifting, movimiento de bits
La aritm´etica booleana proporciona herramientas extremadamente ´utiles al programador. Por ejemplo, una de las mejoras que un compilador puede realizar es la
transformaci´on de una multiplicaci´on de potencia 2 que normalmente lleva varios ciclos (las unidades de multiplicaci´on suelen ser lentas) en una ´unica operaci´on l´ogica
llamada ✭✭Bit Shifting✮✮. En este apartado vamos a explotar una de sus utilidades
    114/127
    B.3. OPERACIONES CON REGISTROS 115
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 0
Figura B.3: Operaci´on: 1 << 1
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0
0 0 0 0 0 0 1 1
OR
1 << 3
0 0 0 0 1 0 1 1
=
Figura B.4: Activaci´on de pin 3 sobre registro previamente configurado
que nos permitir´a hacer nuestro c´odigo m´as legible y nos ahorrar´a mucho c´odigo innecesario.
Continuando con nuestro ejemplo de la configuraci´on del pin 0 y 1 como salida,
en este apartado se ver´a como construir o hallar el valor que debe ser utilizado como
operando extra en la operaci´on OR.
En la Subsecci´on B.3.2 se utiliz´o el operando ✭✭extra✮✮ 0b00000010, a este operando
como ya hemos dicho anteriormente, se le llama m´ascara. Para obtener este valor
se recurre a la operaci´on Bit Shifting. Si queremos crear una mascara para el pin 1
´unicamente tenemos que utilizar la siguiente operaci´on (1 << 1) (en la Figura B.3
est´a representada gr´aficamente la operaci´on).
Si quisi´eramos configurar el pin 3 como salida proceder´ıamos del mismo modo,
es decir, calcular la m´ascara y aplicarla mediante la operaci´on OR. Para calcular la
m´ascara utilizamos la t´ecnica del Bit Shifting (1 << 3). En la Figura B.4 se puede
ver de forma gr´afica el procedimiento.
En el Cod. B.1 se puede ver un extracto de c´odigo donde se configura el pin PC6
y PC7 como salida. Esta es la forma m´as com´un para la configuraci´on de registros. Es
importante observar que la operaci´on OR se realiza sobre el valor actual del registro
(| =) y que se pueden realizar varias operaciones ORing en una ´unica asignaci´on.
    115/127
    B.3. OPERACIONES CON REGISTROS 116
1 # include < avr / io .h >
2
3 int main () {
4
5 // Data Direction Register C = 0 b01100000
6 // Habilitamos como salida el pin PC6 y PC7
7 DDRC |= (1 << DDC6 ) | (1 << DDC7 ) ;
8 // Port Output Register C = 0 b01000000
9 // Salida del pin PC6 como valor HIGH
10 PORTC |= (1 << DDC6 );
11
12 }
C´odigo B.1: Ejemplo de asignaci´on mediante oring
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 1 0 1 1
OR
0 << 3
0 0 0 0 1 0 1 1
=
Figura B.5: Configuraci´on del pin 3 como entrada sobre registro previamente configurado (procedimiento err´oneo)
B.3.4. ANDing, desactivando bits
Hasta ahora ´unicamente hemos configurado bits con el valor 1 pero ¿Se puede
configurar del mismo modo el pin con el valor 0?. La mejor manera de ilustrar esta
situaci´on es mediante un ejemplo. Imaginemos que sobre el ´ultimo valor del registro
que hemos utilizado para los ejemplos (0b00001011 ) deseamos configurar el pin 3
como entrada (0).
El valor 0 en binario ✭✭ocupa✮✮ un ´unico bit por lo que para crear la m´ascara
podr´ıamos pensar en utilizar el mismo m´etodo que en apartados anteriores. En la
figura Figura B.5 se ilustra el proceso.
Como se puede observar el resultado obtenido es 0b00001011, es decir, no es el
resultado esperado dado que el bit 3 est´a a 1. A continuaci´on, explicaremos el modo
de realizar este tipo de operaciones.
Si utilizamos la m´ascara normal, (la utilizada para configurar un bit a 1) y negamos
la misma, podemos observar que el resultado tras realizar la operaci´on AND es el
esperado. Se recurre al elemento neutro de la operaci´on AND, es decir, al 1, de modo
que todos los bit que eran 0 al negarse se convierten a 1 consiguiendo que la operaci´on
AND no tengan efecto (A AND 1 = A) sobre los bits que ya est´a configurados y s´ı
tenga efecto (configurar con el valor 0) sobre los bits que est´an en 1 en el operando
    116/127
    B.3. OPERACIONES CON REGISTROS 117
7 6 5 4 3 2 1 0
0 0 0 0 1 0 0 0
1 1 1 1 0 1 1 1
0 0 0 0 0 0 1 1
AND
NOT
0 0 0 0 0 0 1 1
=
Figura B.6: Configuraci´on del pin 3 como entrada sobre registro previamente configurado
✭✭extra✮✮.
En la Figura B.6 se puede ver el procedimiento seguido paso a paso.
    117/127
    B.3. OPERACIONES CON REGISTROS 118
    118/127
    APENDICE C
´
ENTORNO ECLIPSE CON
ARDUINO
C.1. Introducci´on
En este ap´endice veremos c´omo configurar el entorno de desarrollo Eclipse de
modo que el mismo pueda ser utilizado para programar y grabar programas en la
plataforma Arduino.
El lector puede preguntarse por qu´e utilizar otro IDE si ya ha utilizado el oficial
de Arduino y no ha tenido ning´un problema.
Arduino fue pensado como una plataforma con una curva de aprendizaje muy
baja, la ventaja obvia es la r´apida adaptaci´on de cualquier persona con m´ınimos
conocimientos de inform´atica al entorno. El problema, no tan obvio en un primer
momento, es la carencia de herramientas tan importantes como: control de versiones,
gesti´on de proyectos, ✭✭tasks list✮✮, etc.
Con el objetivo de mostrar al lector el potencial de Arduino, en este ap´endice configuraremos un entorno con todas las herramientas necesarias en cualquier ambiente
profesional.
C.2. Qu´e es Eclipse
Eclipse es un entorno de desarrollo integrado de c´odigo libre y multiplataforma.
Eclipse ofrece un ✭✭core✮✮ sobre el que otros desarrolladores realizan sus modificaciones
para crear entornos de desarrollo espec´ıficos para cada tecnolog´ıa. Un ejemplo podr´ıa
ser el entorno para Java o el entorno de Xilinx para el desarrollo de software sobre su
plataforma hardware.
Aunque existen plugins para Eclipse que ofrecen una capa de compatibilidad con
Arduino, en este ap´endice trabajaremos sobre Eclipse para C++ con el objetivo de
poder tener control sobre cada una de las partes que conforman la construcci´on de
un binario para Arduino.
119
    119/127
    C.3. INSTALACION DEL ENTORNO 120 ´
Figura C.1: Pantalla inicial de Eclipse
La p´agina oficial de Eclipse se puede visitar desde el siguiente enlace: https:
//eclipse.org, en ella podr´as encontrar toda la informaci´on de la fundaci´on Eclipse,
adem´as, desde el apartado ✭✭Download✮✮ podr´as descargar cada una de las versiones
oficiales del entorno.
C.3. Instalaci´on del entorno
El primer paso ser´a el descargar nuestro entorno de desarrollo Eclipse sobre el que
realizaremos todas las ✭✭modificaciones✮✮ necesarias para que sea compatible compatible
con la plataforma Arduino.
Para descargar el entorno, en primer lugar, deberemos acceder a la web de Eclipse
y luego al apartado de descargas: http://www.eclipse.org/downloads/ una vez
dentro deberemos elegir la opci´on de ✭✭Eclipse IDE for C/C++ Developers✮✮. Esta
versi´on nos ofrece muchas herramientas de gran utilidad como: Mylyn Task, Git o un
sistema de acceso remoto.
Una vez descargado el entorno para nuestra arquitectura, el segundo paso ser´a la
descompresi´on del mismo. Eclipse es un programa portable, esto quiere decir que no
requiere instalaci´on por lo que una vez descomprimido estaremos en disposici´on de
utilizar el mismo. El lugar donde se descomprima no es relevante.
El siguiente paso ser´a ejecutar el programa, en teor´ıa si tenemos Java en nuestro
computador el entorno se ejecutar´a sin ning´un tipo de fallo, en caso de no tener Java
el entorno nos avisar´a y tendremos que instalarlo.
La pantalla inicial tendr´a la apariencia de la Figura C.1.
Ahora deberemos instalar el entorno Arduino tal y como lo hicimos en Secci´on 2.1.
Si ya lo tienes instalado no hace falta que vuelvas a hacerlo.
Con la instalaci´on del entorno Arduino tendremos todas las herramientas necesarias, como son:
Compilador: Utilizaremos el compilador ✭✭avr-gcc✮✮ y ✭✭avr-g++✮✮ para compilar
    120/127
    C.4. CONFIGURACION DEL ENTORNO 121 ´
nuestros proyectos.
Conjunto de librer´ıas Arduino: Las librer´ıas de Arduino, forman parte del
llamado ✭✭ArduinoCore✮✮. Estas librer´ıas nos simplifican tareas repetitivas como
la configuraci´on del modulo USART, configuraci´on de pines, etc.
Librer´ıas AVR: Como ya sabr´as la librer´ıa de Arduino utiliza las librer´ıas de
AVR por lo que tambi´en tendremos estas ´ultimas en nuestra instalaci´on.
Programador: Para programar el Arduino necesitamos un programador ✭✭hardware✮✮
y un programador ✭✭software✮✮. En el caso del programador ✭✭hardware✮✮ ya lo tenemos instanciado dentro de la placa Arduino (una vez instalado el bootloader).
Por otro lado, el programador ✭✭software✮✮ que utilizaremos ser´a ✭✭avrdude✮✮ que
forma parte del conjunto de herramientas de AVR.
Mapeado de pines: Como ya sabr´as, el entorno Arduino utiliza su propia
numeraci´on de pines, de tal modo que si pones pin 13 en una funci´on, el Arduino
✭✭sabe✮✮ qu´e pin es y a qu´e puerto le corresponde (en realidad el encargado de
hacer esta conversi´on como es l´ogico, no es Arduino, es el compilador). Esto se
realiza mediante el archivo de variants que veremos m´as adelante.
Es importante que anotemos la direcci´on de instalaci´on de Arduino para los pasos
siguientes. En el caso de los sistemas GNU/Linux, esta direcci´on suele ser: /usr/share/arduino.
C.4. Configuraci´on del entorno
Ahora que tenemos todo descargado es el momento de realizar la configuraci´on del
IDE Eclipse. En primer lugar descargaremos un plugin para el desarrollo de soluciones
basadas en microprocesadores AVR dentro de eclipse.
Para descargar un plugin en Eclipse, podemos acceder al men´u ✭✭Help, Install new
software✮✮ o ✭✭Ayuda, Instalar nuevo software✮✮ en Espa˜nol. Una vez en esa pantalla
deberemos pulsar al bot´on ✭✭Add✮✮ y luego en el apartado de ✭✭Name✮✮ poner el nombre
que se desee, por ejemplo avr-descargas, y en location la siguiente direcci´on: http://
avr-eclipse.sourceforge.net/updatesite, deber´a quedar como en la Figura C.2.
Una vez rellenado el formulario aparecer´a ante nosotros un plugin llamado AVR
Eclipse Plugin con un ✭✭checkbox✮✮, deberemos seleccionar dicho ✭✭checkbox✮✮, seguidamente pulsaremos ✭✭Next✮✮ y ✭✭Finish✮✮ seg´un las instrucciones, hasta finalizar la instalaci´on.
Una vez instalado, el siguiente paso consiste en la configuraci´on de las variables
que permiten al plugin saber d´onde se encuentran los binarios del compilador, etc.
Para configurar el plugin hay que ir al men´u ✭✭Window, Preferences✮✮ o ✭✭Ventana,
Preferencias✮✮. Una vez en el men´u deberemos acceder al apartado de ✭✭AVR, Paths✮✮.
Hay que configurar cada una de las variables para que apunten al binario dentro del
SDK descargado en el paso anterior.
AVR-GCC: <directorioSDK>/hardware/tools/avr/bin
GNU make: Con el entorno descargado para Windows se descarga tambi´en
la herramienta make por lo que el mismo estar´a en el directorio del SDK, sin
    121/127
    C.5. CREANDO EL PROYECTO: ARDUINOCORE 122
Figura C.2: Descarga del plugin AVR
embargo para GNU/Linux esta herramienta no viene incluida dado que forma
parte de GNU/Linux y esta de forma nativa para todas las distribuciones por lo
que en este caso seguramente el propio plugin detecte el lugar donde se encuentra
instalado y aparecer´a algo como ✭✭system✮✮ en esta variable.
AVR Header Files: <directorioSDK>/hardware/tools/avr/avr/include
AVRDude: <directorioSDK>/hardware/tools/avr/bin
En la Figura C.3 puedes ver un ejemplo de como podr´ıa quedar la configuraci´on, ten en cuenta que en funci´on de donde se realice la instalaci´on, los path y en
consecuencia la configuraci´on variar´a.
C.5. Creando el proyecto: ArduinoCore
Como ya hemos comentado, Arduino nos proporciona un conjunto de librer´ıas
que hacen que sea mucho m´as sencillo el utilizar algunos de los m´odulos ✭✭hardware✮✮
del microcontrolador. Para poder contar con todas estas comodidades tendremos que
compilar las librer´ıas en un proyecto a parte (esto nos permitir´a reutilizar las librer´ıa
en otros proyectos). Esta tarea en el entorno oficial de Arduino se realiza sin que
nosotros tengamos que realizarlo de forma explicita, esto hace que sea mucho m´as
r´apido para el usuario pero a la vez, hace poco did´actico el proceso.
Para compilar las librer´ıas en primer lugar crearemos un proyecto, para ello vamos al men´u ✭✭New, C++ Project✮✮ y en tipo de proyecto ✭✭AVR Cross Target Static
Library✮✮ el nombre que pondremos al proyecto ser´a ✭✭ArduinoCore✮✮, el nombre no
es determinante, sin embargo este nombre simplifica la labor de comprensi´on de los
pasos que estamos realizando.
En configuraciones ´unicamente seleccionaremos la de ✭✭Release✮✮ esto es debido a
que esta librer´ıa no la modificaremos y por lo tanto no necesitamos toda la configuraci´on de ✭✭Debug✮✮. En la siguiente pantalla de configuraci´on se nos preguntar´a por
    122/127
    C.5. CREANDO EL PROYECTO: ARDUINOCORE 123
Figura C.3: Configuraci´on de ejemplo
el microcontrolador y por la frecuencia. Ambos par´ametros deber´an ser configurados
en funci´on del Arduino sobre el que se vaya a realizar el proyecto. En el caso del
Arduino construido en este libro en la Secci´on A.1 configuraremos estos par´ametros
con ATmega328 y 16000000 (como se puede observar en este ejemplo la frecuencia
viene determinada en hercios).
Una vez creado el proyecto el siguiente paso consiste en a˜nadir el c´odigo fuente de las librer´ıas, y configurar los includes. Para esto ´ultimo daremos ✭✭click✮✮ derecho sobre el proyecto y buscaremos el men´u de propiedades. Una vez dentro del
mismo deberemos buscar el apartado ✭✭C/C++ Build, Settings✮✮ y en el apartado
✭✭AVR Compiler✮✮ ir a la pesta˜na de ✭✭Directories✮✮ y a˜nadir el directorio: <directorioArduino>/hardware/arduino/avr/cores/arduino. Este directorio contiene todos los
✭✭headers✮✮ de las librer´ıas. Por otro lado tenemos que a˜nadir el ✭✭header✮✮ que utiliza
el entorno Arduino para referirse a sus pines. Este fichero como habr´as podido adivinar var´ıa en funci´on del microcontrolador. El fichero se puede encontrar dentro de
<directorioSDK>/hardware/arduino/variants luego selecciona el que necesites, por
ejemplo, para el microcontrolador ATmega328 deber´ıamos utilizar el mapeado standard. Hay que hacer exactamente lo mismo con el otro apartado llamado ✭✭AVR C++
Compiler✮✮.
Ahora que ya tenemos todas las referencias configuradas, el siguiente paso consiste
en importar el c´odigo fuente de las librer´ıas, para ello damos bot´on derecho sobre el
proyecto y seleccionamos la opci´on de ✭✭Import, File System✮✮. En el cuadro de b´usqueda hay que ingresar el directorio:
<directorioArduino>/hardware/arduino/avr/cores/arduino. Una vez dentro, selecciona todos los archivos (.cpp y .h) menos el archivo main.cpp.
Por ´ultimo ya solo queda compilar el proyecto, para ello cruzamos los dedos y
damos bot´on derecho ✭✭Build Project✮✮. Si todo va bien ya tendremos las librer´ıas de
Arduino compiladas.
    123/127
    C.6. CREANDO EL PROYECTO FINAL 124
Figura C.4: Configuraci´on de las librer´ıas
C.6. Creando el proyecto final
Ahora que tenemos compilado el conjunto de librer´ıas de Arduino para el microcontrolador que estamos utilizando, ya podemos crear un proyecto tal y como
lo har´ıamos en el IDE oficial de Arduino.
Para crear un proyecto en primer lugar accedemos al men´u: ✭✭New, C++ Project✮✮
y en tipo de proyecto ponemos: ✭✭AVR Cross Target Application✮✮ tal y como en la
Secci´on C.5. El nombre del proyecto en este caso no es relevante.
Como este proyecto s´ı que pasar´a por la fase de ✭✭debug✮✮ y de ✭✭release✮✮ dejaremos
habilitadas ambas configuraciones.
El siguiente paso consiste en a˜nadir los mismos directorios que en la Secci´on C.5
con el objetivo que se pueda referenciar a los ficheros de cabecera del ✭✭core✮✮. Adem´as,
hay que a˜nadir al propio proyecto en la lista de directorios. Para ello a˜nadir la cadena
✭✭${workspace loc:/${ProjName}}✮✮ , tanto en ✭✭AVR Compiler✮✮ como en ✭✭AVR C++
compiler✮✮, que indica al compilador que compruebe los ficheros de cabecera de este
mismo proyecto.
Una vez que tenemos las tres direcciones completadas podemos proceder a enlazar
este proyecto con el ✭✭core✮✮. Para enlazar los c´odigo objeto, el primer paso es ir al men´u
del linker llamado ✭✭AVR C++ Linker✮✮ y en el apartado ✭✭General✮✮ sustituir la cadena
del cuadro de texto ✭✭Command line pattern✮✮ por la siguiente ✭✭${COMMAND} -s -Os
${OUTPUT FLAG}${OUTPUT PREFIX}
${OUTPUT} ${INPUTS} -lm ${FLAGS}✮✮, por ´ultimo en la apartado ✭✭libraries✮✮
tenemos que indicar d´onde se encuentra el fichero con la librer´ıa, as´ı como el nombre
de la misma. En nuestro caso el nombre era ✭✭ArduinoCore✮✮ y el ejecutable se puede
encontrar en la carpeta release del proyecto ✭✭ArduinoCore✮✮. Para completar este paso
por tanto, en el primer cuadro (✭✭libraries -l✮✮ debemos poner ArduinoCore y en el
cuadro inferior con el nombre ✭✭libraries path -L✮✮ la siguiente cadena que es relativa
al workspace: ✭✭${workspace loc:/ArduinoCore/Release}✮✮ el aspecto final deber´ıa ser
parecido al mostrado en la Figura C.4.
    124/127
    C.7. SUBIENDO EL PROYECTO A NUESTRO ARDUINO 125
Una vez configuradas las librer´ıas deberemos ✭✭decir✮✮ a eclipse que genere el ✭✭.hex✮✮
para el Arduino. Para ello hay que ir a las propiedades de AVR dentro del proyecto
luego ✭✭C/C++ Build, Settings, Additional Tools in Toolchain✮✮ y seleccionar la opci´on
✭✭Generate HEX file for flash memory✮✮.
C.7. Subiendo el proyecto a nuestro Arduino
Para ✭✭subir✮✮ el ✭✭.hex✮✮ generado para nuestro proyecto a la memoria flash del
Arduino, lo primero que tenemos que hacer es configurar la herramienta ✭✭avrdude✮✮
que ser´a el programador software encargado de realizar la comunicaci´on con el Arduino
para subir el ✭✭.hex✮✮ al mismo.
✭✭avrdude✮✮ utiliza un fichero de configuraci´on llamado ✭✭avrconf✮✮ para saber los
diferentes tipos de programadores hardware con los que cuenta as´ı como las configuraciones que debe realizar para comunicarse con cada uno de ellos.
Para configurar este archivo en ✭✭avrdude✮✮ tenemos que ir a las preferencias generales de ✭✭eclipse✮✮ y buscar el men´u ✭✭AVR, AVRdude✮✮. Una vez en el men´u, el siguiente
paso consiste en marcar la casilla Use custom configuration file for AVRDude y buscar
el fichero ✭✭avrconf✮✮ en:
<directorioSDK>/hardware/tools/avr/etc/avrdude.conf.
Ahora que tenemos preparado el fichero ✭✭avrconf✮✮ lo siguiente consiste en configurar las propiedades del proyecto para indicar qu´e tipo de programador ✭✭hardware✮✮
utilizaremos y que acciones debe realizar ✭✭avrdude✮✮ para programar el Arduino. El
primer paso consiste en ir a las propiedades del proyecto y buscar el men´u ✭✭AVR,
AVRDude✮✮. Ahora crearemos una nueva configuraci´on dando al bot´on ✭✭new✮✮ de la
pesta˜na ✭✭Programmer✮✮. El nombre de la configuraci´on puede ser el que desees, se
recomienda utilizar el nombre de tu placa as´ı te ser´a m´as sencillo tener todo ordenado. En ✭✭programmer hardware✮✮ deberemos buscar ✭✭Wiring✮✮. En el cuadro de texto
llamado ✭✭Override default port✮✮ deberemos poner el puerto en el cual se encuentra
conectado nuestro Arduino. En el caso de GNU/Linux este suele ser /dev/ttyACMX.
En Windows el puerto COM deber´a escribirse de la siguiente manera ✭✭//./COMX✮✮.
En cuanto a la velocidad en baudios variar´a en funci´on del ✭✭bootloader✮✮ y de la placa,
los valores t´ıpicos son 57600 y 115200. El aspecto de esta ventana de configuraci´on
deber´ıa ser similar a la Figura C.5.
Con todo configurado ya solo queda crear un archivo ✭✭main.cpp✮✮ en nuestro proyecto con el esquema mostrado en Cod. C.1.
Como puedes observar, aqu´ı se puede apreciar como cuando creamos un sketch
en el IDE oficial de Arduino realmente estamos creando las dos funciones principales
como son setup y loop pero nos despreocupamos de iniciar la plataforma (init()) y de
asegurarnos de que el programa nunca finaliza (bucle while).
Una vez guardado el programa tendremos que compilar el mismo dando bot´on
derecho sobre el proyecto y sobre la opci´on ✭✭Build Project✮✮. Si todo est´a bien se
crear´a una carpeta llamada ✭✭Debug✮✮ donde podremos encontrar un fichero llamado
✭✭nombreDelProyecto.hex✮✮. Ahora solo nos queda subirlo a nuestra placa, para ello
pulsamos bot´on derecho sobre el proyecto y buscamos ✭✭AVR, Upload Project to Target
Device✮✮.
    125/127
    C.7. SUBIENDO EL PROYECTO A NUESTRO ARDUINO 126
Figura C.5: Configuraci´on de AVRdude
1 # include < Arduino .h >
2 void setup () {
3 // config
4 }
5 void loop ()
6 {
7 // code
8 }
9 int main ( void ) {
10 init () ;
11 setup () ;
12 while ( true ) {
13 loop () ;
14 }
15 }
C´odigo C.1: Esqueleto de programa
    126/127
    WARRANTY
NO WARRANTY
There is no warranty for this work. Except when otherwise stated in writing, the
Copyright Holder provides the work ‘as is’, without warranty of any kind. The entire
risk as to the quality and performance of the Work is with you. The Copyright Holder,
or any author named in the components of the work, or any other party who may
distribute and/or modify the Work as permitted above, be liable to you for damages
arising out of any use of the work (including, but not limited to, loss of data, data
being rendered inaccurate, or losses sustained by anyone as a result of any failure of
the Work to operate with any other programs), even if the Copyright Holder or said
author or said other party has been advised of the possibility of such damages.
127
    127/127

    Arduino - Un enfoque practico e incremental

    • 2. Current maintainers of this work are Jos´e Antonio de la Torre las Heras and Julio Daniel Dondo Gazzano. Reservados todos los derechos. No se permite la reproducci´on total o parcial de esta obra, ni su incorporaci´on a un sistema inform´atico, ni su transmisi´on en cualquier forma o por cualquier medio (electr´onico, mec´anico, fotocopia, grabaci´on u otros) sin autorizaci´on previa y por escrito de los titulares del copyright. La infracci´on de dichos derechos puede constituir un delito contra la propiedad intelectual. First edition: Apr 2016 Digital edition This book includes illustrations and index. ISBN 978-84-608-7339-6 ©Jos´e Antonio de la Torre las Heras, 2016 ©Julio Daniel Dondo Gazzano, 2016 All rights reserved.
    • 3. Queremos expresar nuestro mas sincero agradecimiento al grupo de investigaci´on ARCO, de la Escuela Superior de Inform´atica de la Universidad de Castilla-La Mancha en Ciudad Real, por su apoyo en la elaboraci´on de este libro. Los autores.
    • 4. A Juan Santiago, Ambar, Bruno, Gabriel, Laureano y Franca... Julio.
    • 5. A mi familia por su apoyo incondicional y por creer en mi desde el primer momento. En especial a mis padres, cuyos consejos y compresi´on han sido un apoyo fundamental en el desarrollo tanto de mi vida profesional como personal. Agradecer tambi´en a Julio Daniel Dondo y al grupo de investigaci´on ARCO la confianza depositada en m´ı. Jos´e Antonio de la Torre las Heras.
    • 6. 6
    • 7. PREFACIO El motivo de este libro es dotar al lector de los conocimientos necesarios para poder dise˜nar sus propios circuitos y aportar soluciones para automatizar y controlar diferentes procesos tanto dom´esticos, para dom´otica, hobbies, etc., as´ı como tambien procesos industriales. Para ello, adoptaremos una metodolog´ıa incremental y lo m´as pr´actica posible, empezando desde lo b´asico como es encender un LED, hasta automatizar una cadena de montaje y gestionar la misma con diferentes tecnolog´ıas apoy´andonos en el ecosistema Arduino. 7
    • 8. 8
    • 9. ´ INDICE GENERAL 1. ¿Qu´e es Arduino? 13 1.1. Elementos de Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2. Primeros pasos en Arduino 19 2.1. Instalando el entorno Arduino . . . . . . . . . . . . . . . . . . . . . . . 19 2.1.1. GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.1.2. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.2. Probando nuestra placa Arduino . . . . . . . . . . . . . . . . . . . . . 20 2.2.1. Conexi´on y configuraci´on de drivers . . . . . . . . . . . . . . . 20 2.2.2. Primer programa . . . . . . . . . . . . . . . . . . . . . . . . . . 21 3. Manejando entradas/salidas 25 3.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.2. Utilizando los pines de salida . . . . . . . . . . . . . . . . . . . . . . . 25 3.2.1. Ejemplo 1: Encendiendo un LED . . . . . . . . . . . . . . . . . 26 3.3. Utilizando los pines de entrada . . . . . . . . . . . . . . . . . . . . . . 30 3.3.1. Ejemplo 2: Utilizaci´on de un bot´on o interruptor . . . . . . . . 32 3.3.2. Ejemplo 3: Leyendo temperatura y se˜nales anal´ogicas . . . . . 36 4. Comunicaciones 43 4.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4.2. Comunicaci´on serie mediante USART . . . . . . . . . . . . . . . . . . 43 4.2.1. Ejemplo 1: Hola mundo por Serie . . . . . . . . . . . . . . . . . 45 4.2.2. Ejemplo 2: Recibiendo informaci´on . . . . . . . . . . . . . . . . 49 4.2.3. Ejemplo 3: Comunicaci´on entre Arduinos . . . . . . . . . . . . 52 4.3. Comunicaci´on I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.3.1. Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.3.2. Protocolo I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.3.3. Ejemplo 1: Hola mundo mediante I2C . . . . . . . . . . . . . . 60 4.3.4. Ejemplo 2: Obteniendo datos de un IMU . . . . . . . . . . . . 64 4.4. Protocolo SPI (Serial Peripheral Interface . . . . . . . . . . . . . . . . 67 9
    • 10. ´INDICE GENERAL 10 5. Interrupciones 73 5.1. Interrupciones en el ATmega328 . . . . . . . . . . . . . . . . . . . . . 74 5.2. Manipulaci´on software . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 5.2.1. Librer´ıa avr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 5.2.2. Librer´ıa Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . 77 5.2.3. Consideraciones importantes . . . . . . . . . . . . . . . . . . . 77 5.3. Ejemplo 1: Primera rutina de interrupci´on . . . . . . . . . . . . . . . . 78 5.3.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 78 5.3.2. C´odigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 5.4. Ejemplo 2: Midiendo distancias . . . . . . . . . . . . . . . . . . . . . . 79 5.4.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 80 5.4.2. C´odigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 6. Multitasking y Timers 83 6.1. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 6.1.1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 6.1.2. Modos de funcionamiento . . . . . . . . . . . . . . . . . . . . . 85 6.1.3. Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . 87 6.2. Multitasking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 6.2.1. Encendiendo y apagando un led de manera profesional . . . . . 98 6.2.2. Encendiendo y apagando un led de manera m´as profesional . . 100 A. Construyendo nuestro propio Arduino 103 A.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 A.2. Componentes necesarios . . . . . . . . . . . . . . . . . . . . . . . . . . 103 A.3. Ensamblado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 A.4. Programaci´on del Bootloader . . . . . . . . . . . . . . . . . . . . . . . 107 B. Manipulaci´on de registros 111 B.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 B.2. ¿Qu´e es un registro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 B.3. Operaciones con registros . . . . . . . . . . . . . . . . . . . . . . . . . 113 B.3.1. Activar un bit . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 B.3.2. ORing, activaci´on de un bit . . . . . . . . . . . . . . . . . . . . 114 B.3.3. Bit Shifting, movimiento de bits . . . . . . . . . . . . . . . . . 114 B.3.4. ANDing, desactivando bits . . . . . . . . . . . . . . . . . . . . 116 C. Entorno Eclipse con Arduino 119 C.1. Introducci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 C.2. Qu´e es Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 C.3. Instalaci´on del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 C.4. Configuraci´on del entorno . . . . . . . . . . . . . . . . . . . . . . . . . 121 C.5. Creando el proyecto: ArduinoCore . . . . . . . . . . . . . . . . . . . . 122 C.6. Creando el proyecto final . . . . . . . . . . . . . . . . . . . . . . . . . 124 C.7. Subiendo el proyecto a nuestro Arduino . . . . . . . . . . . . . . . . . 125
    • 11. ´ INDICE DE FIGURAS 1.1. Placa Arduino Mega 2560 . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.1. Instalador Arduino para Windows . . . . . . . . . . . . . . . . . . . . 20 2.2. Editor Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3. Led y puerto 13 en Arduino Mega 2560 . . . . . . . . . . . . . . . . . 23 3.1. Flujo normal de ejecuci´on para sistemas de control . . . . . . . . . . . 26 3.2. Composici´on de un LED . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.3. Ejemplo 1: Esquema de montaje . . . . . . . . . . . . . . . . . . . . . 28 3.4. Ejemplo 1: Protoboard, esquema de montaje . . . . . . . . . . . . . . 28 3.5. Ejemplo 1: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 29 3.6. Osciloscopio digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.7. Frecuenc´ımetro digital . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 3.8. Se˜nal digital vs Se˜nal anal´ogica . . . . . . . . . . . . . . . . . . . . . . 33 3.9. Interruptor SPST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.10. Rebote de un bot´on y valores obtenidos . . . . . . . . . . . . . . . . . 34 3.11. M´odulo bot´on Grove . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.12. Ejemplo 2: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 35 3.13. Ejemplo 3: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 40 4.1. Trama con formato 8N1 . . . . . . . . . . . . . . . . . . . . . . . . . . 44 4.2. Conexi´on entre dispositivos UART con control de flujo . . . . . . . . . 45 4.3. Representaci´on de comunicaci´on serie . . . . . . . . . . . . . . . . . . . 45 4.4. Ejemplo 1 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 47 4.5. Ejemplo 2 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 50 4.6. Ejemplo 3 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 55 4.7. Esquema de comunicaci´on I2C . . . . . . . . . . . . . . . . . . . . . . 59 4.8. Trama de direcci´on I2C . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.9. Trama de datos I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 4.10. Sensor MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.11. Conexiones MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 11
    • 12. ´INDICE DE FIGURAS 12 4.12. Pines SPI en el proc. ATmega328 . . . . . . . . . . . . . . . . . . . . . 68 4.13. Contenidos del registro de entrada del DAC . . . . . . . . . . . . . . . 69 4.14. Forma de onda obtenida conversi´on D/A . . . . . . . . . . . . . . . . . 70 5.1. Ejemplo de indirecci´on . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 5.2. Sensor emisor/receptor ultrasonidos . . . . . . . . . . . . . . . . . . . 80 6.1. Diagrama TIMER 8 bits ATmega328 . . . . . . . . . . . . . . . . . . . 84 6.2. Modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 6.3. Modo Fast PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 6.4. Registro TCCR1A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 6.5. Bits de configuraci´on del presclarer . . . . . . . . . . . . . . . . . . . . 90 6.6. Registro TCCR1B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 6.7. Bits para configuraci´on del pin de salida . . . . . . . . . . . . . . . . . 91 6.8. Configuraci´on modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . 92 6.9. Diagrama de bloques del sistema de control . . . . . . . . . . . . . . . 94 6.10. Servo de Grove-Starter Kit for Arduino . . . . . . . . . . . . . . . . . 94 6.11. Diagrama de tiempos de un servo . . . . . . . . . . . . . . . . . . . . . 95 6.12. Ejemplo de programa en ladder . . . . . . . . . . . . . . . . . . . . . . 97 A.1. Protoboard de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . 105 A.2. Esquema de conexiones . . . . . . . . . . . . . . . . . . . . . . . . . . 105 A.3. Patillaje ATmega328 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 A.4. Conectores ICSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 A.5. Captura de pantalla del Arduino IDE . . . . . . . . . . . . . . . . . . 109 B.1. Jerarqu´ıa de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 B.2. Registro de 8 bits (byte) como una ✭✭caja✮✮ . . . . . . . . . . . . . . . . 113 B.3. Operaci´on: 1 << 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 B.4. Activaci´on de pin 3 sobre registro previamente configurado . . . . . . 115 B.5. Configuraci´on del pin 3 como entrada sobre registro previamente configurado (procedimiento err´oneo) . . . . . . . . . . . . . . . . . . . . . 116 B.6. Configuraci´on del pin 3 como entrada sobre registro previamente configurado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 C.1. Pantalla inicial de Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . 120 C.2. Descarga del plugin AVR . . . . . . . . . . . . . . . . . . . . . . . . . 122 C.3. Configuraci´on de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . 123 C.4. Configuraci´on de las librer´ıas . . . . . . . . . . . . . . . . . . . . . . . 124 C.5. Configuraci´on de AVRdude . . . . . . . . . . . . . . . . . . . . . . . . 126
    • 13. CAP ´ ITULO 1 ¿QUE ES ARDUINO? ´ En este libro vamos a describir una serie de dise˜nos y desarrollos pr´acticos para automatizar el control de sistemas utilizando el entorno Arduino. El entorno Arduino es una plataforma electr´onica de desarrollo de c´odigo abierto (open source) que ofrece un conjunto de herramientas tanto software como hardware que nos permitir´an realizar nuestros prototipos de una manera r´apida, sencilla y segura. Est´a formado principalmente por una placa de desarrollo, equipada con un microcontrolador AVR, puertos de conexi´on de entrada y salida, m´as los elementos necesarios para programar el microcontrolador, y un ambiente de desarrollo software con una serie de librer´ıas para construir nuestros dise˜nos. 1.1. Elementos de Arduino Podemos dividir el entorno Arduino en dos partes bien diferenciadas, por un lado tenemos un conjunto de soluciones hardware y por otro lado una suite de desarrollo software. Hardware • Placa de desarrollo Arduino: Desde que se lanz´o el primer Arduino al mercado en 2005 se han dise˜nado numerosas placas de desarrollo por diferentes empresas de ensamblado, tales como Funduino, FreeDuino. . . Cada placa est´a compuesta normalmente por un microcontrolador de la marca Atmel. Dependiendo de la gama, la placa dispondr´a de un modelo de microcontrolador u otro. En la figura (Figura 1.1) se muestra como ejemplo el Arduino Mega 2560 que cuenta con un microcontrolador ATmega2560 de 8 bits con una velocidad de 16 MHz adem´as de 86 pines de entrada salida y 5 puertos de comunicaci´on SPI. Las placas Arduino son ✭✭libres✮✮ y por lo tanto en Internet se pueden encontrar los esquem´aticos de cada placa e incluso si quieres puedes crearte la 13
    • 14. 1.1. ELEMENTOS DE ARDUINO 14 Figura 1.1: Placa Arduino Mega 2560 tuya propia o a˜nadir funcionalidades a la misma. Diferentes placas de desarrollo pueden verse en la web oficial de Arduino: http://www.arduino.cc/ en/Main/Products . Esta lista es s´olo una peque˜na selecci´on de las numerosas placas que han desarrollado en Arduino, si quieres ver la lista completa accede a la web oficial de Arduino: http://www.arduino.cc/en/Main/Products . • Tarjetas (Shields) de expansi´on: Unas de las caracter´ısticas que han hecho que Arduino gane terreno entre otras placas de desarrollo son el bajo precio y la capacidad de expansi´on que estas placas tienen. En el mercado existen diferentes tipos de tarjetas de expansi´on para componentes varios, como controladores de motores, m´odulos de comunicaci´on, etc. En la p´agina de Arduino se pueden ver algunos shields, aunque existen numerosas tiendas como Adafruit 1 que nos ofrecen una variedad mayor. • M´odulos conectar y listo: Adem´as de las shields de expansi´on, Arduino cuenta con un gran n´umero de m´odulos del tipo ✭✭conectar y listo✮✮. La diferencia con los shields es que estos ´ultimos ´unicamente son interfaces para diferentes sensores y actuadores de modo que sea m´as sencillo utilizarlos en nuestros proyectos. Por ejemplo existen m´odulos conectar y listo para sensores de temperatura, humedad, luz, botones, potenci´ometros, LEDs, etc. Software • Arduino IDE: Adem´as de las placas de desarrollo, Arduino proporciona un IDE (Entorno de Desarrollo Integrado) basado en processing con el que podremos dise˜nar nuestros proyectos. Este IDE consta de un editor de texto para escribir nuestros programas, un ´area de mensajes, una consola de comandos y una barra de herramientas con diferentes men´us. Este entorno 1http://www.adafruit.com/category/17
    • 15. 1.1. ELEMENTOS DE ARDUINO 15 Tipo Procesador Flash EEPROM N´umero de puertos Comunicaciones Arduino Mega ATmega1280 (16MHz) 128KB 4KB 54 digitales, 15 PWM, 16 anal´ogi- cos SPI, I2C, Se- rial(x2) Arduino Mega 2560 ATmega2560 (16MHz) 256KB 4KB 54 digitales, 15 PWM, 16 anal´ogi- cos TWI,SPI, Se- rial(x3) Arduino Board Due AT91SAM3X8E (84MHz) 512 KB 51 digitales, 12 PWM, 12 anal´ogi- cos TWI(x2), Se- rial(x4), SPI, CAN Arduino Yun a ATmega32u4 (16MHz) 32 KB 1KB 20 digitales, 7 PWM, 12 anal´ogicos TWI, Serial, SPI,Serial, WiFi Arduino Micro ATmega32u4 (16MHz) 32KB 1 KB 20 digitales, 7 PWM, 12 anal´ogicos SPI, Serial, I2C Arduino Uno ATmega328 (16MHz) 32KB 1KB 14 digitales, 6 PWM, 6 anal´ogicos I2C, SPI, Serial Arduino Leonardo ATmega32u4 (16MHz) 32KB 1KB 20 digitales, 7 PWM, 12 anal´ogicos I2C, SPI, TWI, Serial aArduino Yun es ligeramente diferente a las dem´as placas, adem´as del microcontrolador ATmega32u4 posee un procesador Atheros AR9331 el cual soporta OpenWrt, una “distribuci´on” Linux para lo comunicaci´on Ethernet y WiFi
    • 16. 1.1. ELEMENTOS DE ARDUINO 16 se conecta a la placa Arduino para programarla con el c´odigo que hemos desarrollado y para comunicarse con ella. El c´odigo desarrollado en el Arduino IDE se denomina sketch y es la unidad de c´odigo que es cargado y ejecutado en una placa Arduino. • Librer´ıa ✭✭Arduino.h✮✮: Arduino ha conseguido minimizar la curva de aprendizaje del lenguaje de programaci´on C++ mediante una librer´ıa que nos evita lidiar al principio con problemas como el uso de operadores de bits, configuraci´on de registros del microcontrolador, etc. La librer´ıa ✭✭Arduino.h✮✮2 nos ofrece una interface de alto nivel que nos provee de funcionalidad extra con la cual realizar todas las tareas en nuestro microcontrolador de una manera muy sencilla y descriptiva. Por ejemplo, si quisi´eramos configurar un GPIO(Pin de Entrada Salida de uso General) para que actu´e como pin de salida, ´unicamente tendr´ıamos que invocar a la funci´on pinMode(PIN,OUTPUT). A lo largo de este libro iremos utilizando esta librer´ıa en todos nuestros proyectos y poco a poco, seg´un vayamos avanzando, veremos la forma equivalente de hacer las mismas operaciones en C++ ✭✭puro✮✮. • Atmel Studio: Aunque no es un producto de Arduino conviene nombrarlo debido a la potencia que tiene esta herramienta. Atmel Studio es un IDE profesional para los microcontroladores del fabricante Atmel. Al contrario que el entorno de Arduino, Atmel Studio es m´as complejo y ´unicamente est´a disponible para la plataforma Windows, pero tiene muchas m´as utilidades como debugger, gestor de proyectos, etc. • Arduino Studio: Arduino Studio es el que pretende ser el nuevo entorno de Arduino. Al contrario del principal IDE de Arduino, este se basa en una arquitectura modular y no monol´ıtica como as´ı lo hac´ıa su antecesor. Arduino Studio aprovecha las capacidades de ✭✭Adobe Brackets✮✮. Actualmente se encuentra en versi´on alpha, pero ya cuenta con caracter´ısticas como: ◦ Sistema basado en la nube. ◦ Soportado por las principales plataformas. ◦ Escrito de cero en Javascript y Node.js. ◦ Interface de usuario amigable. ◦ Autocompletado de c´odigo en tiempo real y documentaci´on en vivo. ◦ Debugger para el M0 Pro. • Toolchain AVR: En los libros de texto normalmente no se habla de este componente pero es sin duda el coraz´on de Arduino. Como hemos dicho anteriormente las placas de desarrollo Arduino est´an formadas por un microcontrolador de la marca Atmel3, normalmente un AVR, por lo que podremos utilizar todas las herramientas que nos proporciona Atmel. Por ejemplo, Atmel posee un compilador libre para GNU llamado avr-g++, el cual es invocado por el IDE Arduino para compilar el c´odigo C++ de nuestro Sketch. Adem´as, una vez que hemos compilado 2Aunque ✭✭Arduino.h✮✮ es la librer´ıa general, en realidad Arduino se apoya en m´as librer´ıas como las indicadas en https://www.arduino.cc/en/Reference/Libraries 3Atmel es la empresa desarrolladora de los microcontroladores http://www.Atmel.com
    • 17. 1.1. ELEMENTOS DE ARDUINO 17 el programa, el toolchain nos permite subir el c´odigo m´aquina a nuestra placa por medio de avrdude, tambi´en libre 4. El uso de este toolchain nos permite ✭✭saltarnos✮✮ la capa de abstracci´on que nos proporciona Arduino y programar a m´as bajo nivel, lo que a su vez nos permitir´a tener una visi´on m´as detallada de la programaci´on de microcontroladores. 4El toolchain de AVR puede ser descargado de forma gratuita desde http://www.nongnu.org/ avr-libc
    • 18. 1.1. ELEMENTOS DE ARDUINO 18
    • 19. CAP ´ ITULO 2 PRIMEROS PASOS EN ARDUINO 2.1. Instalando el entorno Arduino El entorno de desarrollo Arduino est´a programado en Java por lo que es multiplataforma. El entorno lo podemos encontrar en la p´agina oficial de Arduino (http: //www.arduino.org/en/Main/Software), dependiendo del sistema operativo se deber´a descargar una versi´on u otra. El lector puede preguntarse por qu´e existen varias versiones si el entorno est´a basado en Java y en ´ultima instancia en la m´aquina virtual de Java (JVM), esto ocurre porque junto al entorno nos descargamos el toolchain AVR mencionado en la Secci´on 1.1 junto con los drivers de AVR por lo que dependiendo del sistema operativo tendremos que instalar unos drivers u otros. En la p´agina de la comunidad de Arduino llamada Arduino Srl (Smart Projects Srl) (http://www.arduino.org/software)es posible encontrar adem´as del Arduino IDE, el entorno Arduino Studio que es un ambiente de desarrollo open source basado en el editor multiplataforma Brackets de Adobe. 2.1.1. GNU/Linux Los usuarios de GNU/Linux pueden encontrar el entorno Arduino en sus gestores de paquetes, por ejemplo, si tenemos una distribuci´on ✭✭Debian✮✮, para instalar todo el entorno ´unicamente tendremos que poner los siguientes comandos en la terminal como ✭✭superusuario✮✮: apt - get install arduino arduino - core Es importante a˜nadir a nuestro usuario a los grupos: uucp, lock y dialout, para poder manejar los puertos series y los puertos USB de nuestro sistema GNU/Linux. Para a˜nadir nuestro usuario a los grupos ´unicamente ingresaremos el siguiente comando: gpasswd -a $USER uucp gpasswd -a $USER lock Con esto ya tendremos nuestro entorno listo. 19
    • 20. 2.2. PROBANDO NUESTRA PLACA ARDUINO 20 Figura 2.1: Instalador Arduino para Windows 2.1.2. Windows En Windows una vez que nos hemos descargado el instalador para nuestra arquitectura, tendremos que ejecutar el ✭✭.exe✮✮ y nos aparecer´a una ventana como la de la Figura 2.1 en donde se nos ir´a guiando para completar la instalaci´on. 2.2. Probando nuestra placa Arduino 2.2.1. Conexi´on y configuraci´on de drivers Una vez que tenemos instalados los drivers de la placa y el entorno de desarrollo Arduino, ya podemos probar que nuestro entorno de desarrollo est´e funcionando correctamente con un simple ejemplo de prueba. Lo primero que debemos hacer es conectar nuestra placa al ordenador mediante un cable USB A/B. Con este mismo cable podremos alimentar nuestro Arduino, gracias a los 5V que nos proporciona nuestro ordenador por el puerto USB. Una vez conectado el Arduino en entornos Windows nos aparecer´a un mensaje mostrando el proceso de configuraci´on y b´usqueda de drivers, Windows nos informar´a si todo va bien y ya estaremos en disposici´on de ejecutar nuestro IDE. En los entornos GNU/Linux podremos saber si nuestro Arduino ha sido reconocido correctamente mediante el comando: dmesg | tail Este comando en realidad se compone de dos ´ordenes enlazadas mediante una tuber´ıa o Pipe. La primera orden indica al Kernel que nos informe de todo lo sucedido desde el arranque, mientras que la orden tail nos permite obtener las ´ultimas l´ıneas del registro.
    • 21. 2.2. PROBANDO NUESTRA PLACA ARDUINO 21 Figura 2.2: Editor Arduino Si el Kernel ha reconocido el Arduino veremos algo como: detected FT232RL o Product: Arduino XXX donde XXX puede variar en funci´on de la placa que tengamos, por ejemplo, para una placa UNO aparecer´a Product: Arduino UNO. 2.2.2. Primer programa Nuestro primer programa ´unicamente nos servir´a para comprobar que durante todo el Workflow no se genera ning´un error, es decir, podemos programar, compilar y ✭✭subir✮✮ sketchs sin ning´un problema. Lo primero que tenemos que hacer es abrir el IDE, una vez que lo tengamos abierto iremos a: “Archivo, Nuevo” con lo que se nos abrir´a un editor como el de la Figura 2.2 . A continuaci´on, copia el c´odigo Cod. 2.1 Este programa lo ´unico que hace es encender y apagar repetidamente un LED conectado al pin de entrada/salida 13. Los programas desarrollados para Arduino constan en su forma m´as b´asica de dos funciones: una funci´on setup(), donde las variables y los pines a utilizar se declaran e
    • 22. 2.2. PROBANDO NUESTRA PLACA ARDUINO 22 1 int led = 13; 2 void setup () { 3 pinMode ( led , OUTPUT ); 4 } 5 6 void loop () { 7 digitalWrite ( led , HIGH ); 8 delay (1000) ; 9 digitalWrite ( led , LOW ); 10 delay (1000) ; 11 } C´odigo 2.1: Primer ejemplo inicializan y una funci´on loop() donde se define el c´odigo a implementar. Es importante recordar que los pines se pueden configurar como Entradas o Salidas dependiendo del uso que se le den. En este caso es necesario definir al pin 13 como de salida (OUTPUT), como est´a indicado en la l´ınea 3 del c´odigo. Seguidamente en las l´ıneas 7 y 9 alternamos el estado del pin pas´andolo de estado alto (HIGH) a estado bajo (LOW). La funci´on delay() retarda la ejecuci´on de la instrucci´on siguiente en la cantidad de milisegundos indicado por el valor entre par´entesis, en nuestro caso 1000 ms. Para ver el funcionamiento de este dise˜no el lector deber´a conectar un LED entre el pin 13 y tierra. Generalmente es necesario conectar una resistencia de 470 Ohms para limitar la corriente y evitar que el LED se queme. En este caso no har´a falta ya que este pin ya tiene una resistencia incorporada. El pin y el led de prueba est´an se˜nalados en la Figura 2.3. Ahora ´unicamente nos resta seleccionar el modelo de placa que estemos usando, compilar el c´odigo y subirlo a nuestra placa de desarrollo. Para ello iremos a la pesta˜na: “Herramientas, Tarjeta” y seleccionamos nuestra placa, despu´es tenemos que habilitar el puerto serie para programar el Arduino, en esta ocasi´on, navegaremos hasta“Herramientas, Puerto Serial” y seleccionaremos el puerto USB de nuestra placa. Normalmente ´unicamente tendremos un puerto conectado, pero si dispusi´eramos de varios tendr´ıamos que ver cu´al corresponde a la placa seg´un el sistema operativo. Si en GNU/Linux no aparece ning´un puerto serial es probable que el usuario no tenga los permisos adecuados. En este caso revise cada uno de los pasos mencionados en Subsecci´on 2.1.1. Una vez hecho esto vamos a compilar y subir el c´odigo a la placa, para ello vamos a la pesta˜na: “Sketch, Verificar/Compilar” o simplemente pulsamos ✭✭Ctrl-R✮✮ que har´a que el IDE primero verifique la sintaxis del c´odigo y si no hay errores ✭✭suba✮✮ el mismo a la placa. Si todo va bien podremos ver como el LED de prueba conmuta cada segundo.
    • 23. 2.2. PROBANDO NUESTRA PLACA ARDUINO 23 Figura 2.3: Led y puerto 13 en Arduino Mega 2560
    • 24. 2.2. PROBANDO NUESTRA PLACA ARDUINO 24
    • 25. CAP ´ ITULO 3 MANEJANDO ENTRADAS/SALIDAS 3.1. Introducci´on En este cap´ıtulo vamos a trabajar con la placa de desarrollo Arduino enfoc´andonos principalmente en el manejo de sus entradas y salidas. En la siguiente secci´on de este cap´ıtulo (Secci´on 3.2) abordaremos las diferentes t´ecnicas para controlar actuadores desde Arduino. M´as adelante, en la Secci´on 3.3 veremos como obtener datos del exterior, tanto anal´ogicos como digitales. Para simplificar en la medida de lo posible el montaje de los circuitos y centrarnos la mayor parte del tiempo en la programaci´on del Arduino, utilizaremos un kit de iniciaci´on llamado Grove Starter Kit for Arduino de la marca Seeed el cual se puede adquirir por Internet a un precio relativamente bajo.1. Adem´as del kit Grove, utilizaremos el kit de expansi´on Sidekick Basic Kit for Arduino V2. 2 3.2. Utilizando los pines de salida Normalmente, a la hora de dise˜nar cualquier sistema de control nos encontramos con una secuencia de ejecuci´on como la mostrada en Figura 3.1. Como se observa en la Figura 3.1 una parte esencial en un sistema de control son los actuadores, pero ¿Qu´e es un actuador?. La definici´on formal de actuador es la siguiente: 1El kit se puede obtener desde: http://www.seeedstudio.com/depot/ Grove-Starter-Kit-for-Arduino-p-1855.html 2Este kit puede se puede obtener desde: http://www.seeedstudio.com/depot/ Sidekick-Basic-Kit-for-Arduino-V2-p-1858.html?cPath=84_13 25
    • 26. 3.2. UTILIZANDO LOS PINES DE SALIDA 26 Figura 3.1: Flujo normal de ejecuci´on para sistemas de control Actuador Dispositivo capaz de transformar la energ´ıa neum´atica, el´ectrica o hidr´aulica en la activaci´on de un proceso con la finalidad de generar un efecto sobre un proceso automatizado. Un ejemplo claro es un motor de un robot. Un motor de un robot cumple con los dos elementos principales de la definici´on. Por un lado transforma la energ´ıa el´ectrica en un efecto (movimiento del robot) y por otro lado, este movimiento se realiza sobre un proceso automatizado (l´ogica del robot). Al igual que el ejemplo del robot, existen otros muchos actuadores. Se deja al lector la tarea de buscar en la red m´as informaci´on sobre los diferentes tipos. El objetivo de esta secci´on es controlar estos actuadores, de modo que podamos actuar sobre el entorno desde Arduino. La manera m´as inmediata de controlar un actuador en Arduino es mediante los pines de entrada/salida. La cantidad de pines disponibles depende del tipo de placa que se utilice. Por ejemplo, en la placa ATmega328 que ✭✭montamos✮✮ en la Secci´on A.1 tenemos un total de 23 pines que pueden ser configurados c´omo entrada o salida. Veamos algunos ejemplos: 3.2.1. Ejemplo 1: Encendiendo un LED En este primer ejemplo, veremos c´omo encender un LED, aunque ya se vio de manera r´apida en Subsecci´on 2.2.2 en esta secci´on lo veremos de manera mas detallada. El LED (Figura 3.2) es un elemento notificador muy utilizado a d´ıa de hoy. Una de las razones que han llevado a la industria a utilizar estos dispositivos es el bajo consumo
    • 27. 3.2. UTILIZANDO LOS PINES DE SALIDA 27 Figura 3.2: Composici´on de un LED que requieren, aproximadamente entre 10 mA y 20 mA. Aunque aqu´ı utilizaremos el LED ´unicamente con el prop´osito de notificar o avisar sobre un evento. Hoy en d´ıa su uso se ha extendido a otras ´areas como la de la iluminaci´on e incluso la comunicaci´on. En las placas oficiales de Arduino se incorpora un LED notificador conectado al pin 13. Este LED ya tiene una resistencia limitadora de corriente, por lo que no tendremos que preocuparnos por el consumo de corriente. En el caso de que el lector haya montado el Arduino siguiendo la gu´ıa del Secci´on A.1, entonces deber´a conectar el LED y la resistencia al pin 13. Para identificar la posici´on de cada uno de los pines se puede consultar la hoja de datos del ATmega que se puede descargar desde el siguiente enlace: 3. Si est´a utilizando una placa con otro microcontrolador, busque la hoja de datos correspondiente. En el apartado 1 del datasheet veremos los diferentes encapsulados en los cuales el fabricante nos ofrece el microcontrolador. El encapsulado utilizado en Secci´on A.1 es el 28 PDIP (Plastic Dual In-line Package). En este encapsulado el pin 13 se encuentra en la esquina superior derecha teniendo en el norte el semic´ırculo de referencia. Adem´as del datasheet, para saber el mapeado de pines es necesario ir al esquem´atico de cada placa y buscar a que pin de la librer´ıa corresponde cada pin f´ısico.4 En el caso del Arduino realizado en la Secci´on A.1, el montaje debe ser parecido al mostrado en: Figura 3.3, Figura 3.4. Tabla de entrada/salida Para obtener unos dise˜nos fiables, seguros y de calidad se deben seguir procedimientos y normas de calidad de dise˜no. Por ello, aunque este ejemplo sea sencillo, seguiremos una metodolog´ıa a la hora de afrontar el problema. El primer paso en cualquier dise˜no es analizar los elementos de control en base a las especificaciones. En nuestro caso ´unicamente tenemos un ´unico elemento a controlar (el LED) por lo que este paso se podr´ıa obviar, sin embargo documentar es una buena costumbre en cualquier dise˜no. En la Tabla 3.1 se puede ver un ejemplo de an´alisis de elementos de control. En primer lugar, se indica si el elemento es de entrada o salida. En segundo lugar, se realiza una descripci´on sobre el elemento, de modo que siempre se sepa la funci´on del mismo. Por otro lado, se indica el nombre de la variable que se 3http://goo.gl/S3oBA2 4Los esquem´aticos se pueden encontrar en la p´agina de Arduino o en el caso de una placa ensamblada por otro fabricante en la p´agina del mismo.
    • 28. 3.2. UTILIZANDO LOS PINES DE SALIDA 28 Figura 3.3: Ejemplo 1: Esquema de montaje 5v-12v 16MHz Cristal Azul = Tierra Rojo = Entrada Naranja = 5v estables Cyan = Cristal ------------------- Marron = Rx(Arduino) - Tx(FTDI) Morado = Tx(Arduino) - Tx(FTDI) Figura 3.4: Ejemplo 1: Protoboard, esquema de montaje
    • 29. 3.2. UTILIZANDO LOS PINES DE SALIDA 29 Cuadro 3.1: Ejemplo 1: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Salida Conmutar cada 1/2 segundo led 13 Figura 3.5: Ejemplo 1: Diagrama de flujo utilizar´a en el c´odigo para hacer referencia a dicho elemento y por ´ultimo el pin f´ısico al cual se conectar´a. Diagrama de flujo En base al diagrama general mostrado en la Figura 3.1 al que normalmente se adapta cualquier dise˜no, vamos a realizar el diagrama correspondiente para nuestro dise˜no. La herramienta utilizada para crear dicho diagrama es DIA una herramienta de c´odigo abierto, gratuita y que permite realizar diagramas de diversos tipos como por ejemplo diagramas de clases UML o diagramas de flujo. En la Figura 3.5 se puede ver un ejemplo de diagrama de flujo para nuestro caso. Este ejemplo no es la ´unica manera de representar nuestro dise˜no. Un mismo problema puede tener diferente soluciones correctas. C´odigo Ahora que tenemos toda la documentaci´on lista y se ha analizado el problema es el momento de realizar el dise˜no de la soluci´on y su implementaci´on. Para programar la placa Arduino utilizaremos el entorno de programaci´on instalado descrito en el Cap´ıtulo 1. Como se adelant´o en el cap´ıtulo anterior todo programa que vaya a ser ejecutado en el entorno Arduino debe tener dos funciones principales y obligatorias: setup(void): Es la primera funci´on que se ejecuta tras cargar el programa. En
    • 30. 3.3. UTILIZANDO LOS PINES DE ENTRADA 30 1 int led = 13; // De acuerdo a la tabla de entrada / salidas 2 3 void setup () { 4 5 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada ) 6 pinMode ( led , OUTPUT ); 7 8 } 9 10 void loop () { 11 // digitalWrite " pone " el pin ( led ) en estado HIGH 5 v o LOW 0 v 12 digitalWrite ( led , HIGH ); 13 // delay bloquea el m i c r o c o n t r o l a d o r hasta que pasen x ms 14 // delay se implementa mediante el uso del TIMER0 por lo que 15 // si usamos ese TIMER tendremos problemas con esta funcion 16 delay (500) ; 17 digitalWrite ( led , LOW ) ; 18 delay (500) ; 19 20 } C´odigo 3.1: Ejemplo 1: Encendiendo y apagando un led esta funci´on se suelen realizar tareas como la inicializaci´on de variables, pines, comunicaciones, informaci´on de inicio al usuario, tareas de login. etc. loop(void): Tambi´en llamado bucle principal del programa, se ejecuta de forma constante. Cada pasada por el bucle se suele llamar un ciclo de scan. En esta funci´on es donde programaremos la l´ogica siguiendo el diagrama general mostrado en Figura 3.1. Es importante mantener un orden en la funci´on loop. Un mal encapsulamiento funcional puede hacer que nuestro programa sea poco mantenible y finalmente fracase. El mismo programa mostrado en Cod. 3.1 puede ser reescrito utilizando funciones de modo que sea mucho m´as sencillo de leer tal y como se muestra en: Cod. 3.2. En este nuevo c´odigo se utilizan dos funciones ademas de las funciones setup() y loop() b´asicas: la funci´on read sensors() y la funci´on perform actions() La funci´on read sensors() es una funci´on que se encarga de la lectura de los sensores para que, de acuerdo a sus valores, ejecutar las actuaciones correspondientes. En este caso es una funci´on vac´ıa. La segunda contiene el c´odigo de encendido y apagado de los LEDs. Como se puede observar, el c´odigo es muy simple y legible. Gracias a la librer´ıa ✭✭Arduino.h✮✮, para configurar un pin como entrada o salida ´unicamente tenemos que llamar a la funci´on pinMode() indicando en sus par´ametros tanto el pin como el modo. Por otro lado, para obtener 5V o 0V en el pin, usaremos la funci´on digitalWrite(). 3.3. Utilizando los pines de entrada En la Subsecci´on 3.2.1 vimos como utilizar los pines de entrada/salida del ATmega328 para informar al usuario de una acci´on mediante un LED o de forma m´as
    • 31. 3.3. UTILIZANDO LOS PINES DE ENTRADA 31 1 int led = 13; // De acuerdo a la tabla de entrada / salidas 2 3 void setup () { 4 5 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada ) 6 pinMode ( led , OUTPUT ); 7 8 } 9 10 void read_sensors () { 11 // funcion dummy 12 } 13 14 void perform_actions () { 15 // digitalWrite " pone " el pin ( led ) en estado HIGH 5 v o LOW 0 v 16 digitalWrite ( led , HIGH ); 17 // delay bloquea el m i c r o c o n t r o l a d o r hasta que pasen x ms 18 // delay se implementa mediante el uso del TIMER0 por lo que 19 // si usamos ese TIMER tendremos problemas con esta funcion 20 delay (500) ; 21 digitalWrite ( led , LOW ) ; 22 delay (500) ; 23 } 24 25 void loop () { 26 read_sensors () ; 27 perform_actions () ; 28 } C´odigo 3.2: Ejemplo 1: Recodificaci´on utilizando funciones
    • 32. 3.3. UTILIZANDO LOS PINES DE ENTRADA 32 Figura 3.6: Osciloscopio digital gen´erica como interaccionar con un actuador. En esta secci´on, veremos todo lo contrario, es decir, aprenderemos como captar una acci´on del usuario (por ejemplo la pulsaci´on de un bot´on). Los pines de entrada salida, como su nombre indica, se pueden utilizar como entrada o como salida. La selecci´on del modo de funcionamiento de cada pin se realiza mediante unos registros hardware. En estos primeros ejemplos no se ver´a el manejo de dichos registros. Sin embargo, en la Secci´on B.1 el lector puede estudiar como utilizar dichos registros mediante operaciones de desplazamiento de bits. A la hora de captar una acci´on o magnitud en un microcontrolador lo primero que debemos preguntarnos es qu´e tipo de magnitud necesitamos leer o captar. De forma gen´erica y simplificando, existen dos grandes grupos en los cuales se clasifican las se˜nales (Figura 3.8): 1. Se˜nales anal´ogicas: Las se˜nales anal´ogicas son continuas en el tiempo, es decir, se puede representar mediante una funci´on matem´atica continua. ¿Qu´e elementos de la se˜nal anal´ogica se pueden medir? principalmente se analizan dos variables: la amplitud y el periodo o frecuencia. Ejemplos de se˜nales anal´ogicas son el sonido, se˜nales de radio frecuencia, se˜nales electromagn´eticas. En electr´onica para medir las se˜nales anal´ogicas se utilizan instrumentos como los osciloscopios (ver Figura 3.6) o frecuencimetro (ver Figura 3.7). Los osciloscopios nos permiten desde visualizar en una pantalla la forma de onda hasta realizar funciones matem´aticas como la FFT (transformada r´apida de fourier). 2. Se˜nales digitales: Las se˜nales digitales son aquellas en las cuales sus valores est´an completamente discretizados. Aunque cualquier fen´omeno electromagn´etico como el producido al realizar contacto en un pulsador es continuo, sus estados pueden ser discretizados en funci´on del n´umero de bits utilizados en la conversi´on, en este caso dos estados (pulsado y no pulsado). 3.3.1. Ejemplo 2: Utilizaci´on de un bot´on o interruptor En este ejemplo nos centraremos en las se˜nales digitales. M´as adelante hablaremos sobre las se˜nales anal´ogicas y veremos diferentes casos de uso. Un bot´on o mejor dicho y en sentido m´as amplio, un interruptor el´ectrico es un dispositivo con dos estados: en uno de estos estados (cerrado) se permite la circulaci´on
    • 33. 3.3. UTILIZANDO LOS PINES DE ENTRADA 33 Figura 3.7: Frecuenc´ımetro digital 0000 0010 0100 0110 1000 1010 1100 Señal continua Señal discreta Tiempo Tiempo Figura 3.8: Se˜nal digital vs Se˜nal anal´ogica
    • 34. 3.3. UTILIZANDO LOS PINES DE ENTRADA 34 Figura 3.9: Interruptor SPST 1 1 1 1 X 1 0 1 0 X 0 0 0 0 0 VOL VIL VIH VOH Rebote Valor lógico alto Indeterminado Valor lógico bajo Figura 3.10: Rebote de un bot´on y valores obtenidos de corriente y en el otro estado se impide su paso (abierto). En los circuitos electr´onicos puedes encontrarlo representado con diferentes s´ımbolos. Sin embargo, el m´as com´un es el que se muestra en la Figura 3.9. Existen muchos tipos de interruptores en funci´on de los polos, v´ıas, etc, pero nosotros utilizaremos los simples, es decir, de un polo y una v´ıa. Los interruptores como cualquier dispositivo mec´anico son imperfectos y tienen un desgaste por el propio uso. Aunque parezca que en un dise˜no esto no es necesario tenerlo en cuenta, una mala elecci´on puede que haga que nuestro dise˜no tenga una duraci´on menor a la estimada y por lo tanto, se incremente el coste de mantenimiento. Adem´as, hay que tener en cuenta que los interruptores tiene un efecto de rebote, dicho efecto puede provocar que nuestro programa trabaje con valores falsos. Con el objetivo de eliminar estos valores intermedios se utilizan circuitos eliminadores de rebotes (debounce) como el que se muestra en la figura Figura 3.10. Otro m´etodo es realizar una m´aquina de estados que detecte varias lecturas consecutivas con el mismo valor de modo que todos los valores intermedios no se tengan en cuenta. En la Figura 3.10 puedes ver una gr´afica que ilustra lo comentado. Para simplificar el dise˜no y centrarnos en la programaci´on del microcontrolador, en este ejemplo utilizaremos uno de los m´odulos ✭✭conectar y listo✮✮ del kit Groove. En Figura 3.11 se puede ver el aspecto del m´odulo. Dicho modulo nos simplifica el montaje dado que lleva incorporada la resistencia de pull-down anteriormente citada. Figura 3.11: M´odulo bot´on Grove
    • 35. 3.3. UTILIZANDO LOS PINES DE ENTRADA 35 Cuadro 3.2: Ejemplo 2: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Salida Led notificador de pulsaci´on led 13 Entrada Pulsador button 12 Figura 3.12: Ejemplo 2: Diagrama de flujo Tabla de entrada/salida En este ejemplo utilizaremos el c´odigo de la Subsecci´on 3.2.1 de modo que cuando el pulsador se encuentre accionado se encender´a el LED y cuando el pulsador no se encuentre pulsado el LED se apagar´a. Como en el ejemplo anterior, lo primero que hay que realizar en el dise˜no es una tabla con las entradas y salidas que utilizaremos. Como se puede ver en Tabla 3.2 tenemos dos elementos. Por un lado, el LED notificador que vimos en el ejemplo anterior y por otro lado, el pulsador como entrada. Diagrama de flujo En el diagrama de flujo mostrado en Figura 3.12 se muestra un elemento nuevo, el condicional. En este caso en funci´on del estado de un elemento (condici´on) actuaremos de una manera o de otra. C´odigo Con el diagrama estudiado, trasladar la soluci´on a c´odigo es muy sencillo cuando se tiene un poco de pr´actica. Como dijimos en la Subsecci´on 3.2.1 tenemos dos
    • 36. 3.3. UTILIZANDO LOS PINES DE ENTRADA 36 partes diferenciadas, el setup() y el loop(). En el setup() se ha a˜nadido la configuraci´on del bot´on como entrada mediante la funci´on pinMode() con el correspondiente par´ametro INPUT. Un punto importante a tener en cuenta cuando configuramos un pin como entrada es la capacidad del microcontrolador para activar resistencias de pull-down internas. Mediante las resistencias de pull-down internas no es necesario que agreguemos una resistencia para evitar los rebotes. Para habilitar las resistencias de pull-down, se debe utilizar la funci´on digitalWrite() dentro del setup() y sobre un pin configurado anteriormente como entrada. Una vez configurados los pines de entrada/salida, debemos programar la l´ogica del bucle. Si revisas el Cod. 3.2 creamos dos funciones dummy5: read sensors() y perform actions(). En este caso utilizaremos tambi´en la funci´on read sensors() en la cual leeremos el valor del bot´on mediante la funci´on digitalRead(). Esta funci´on devuelve HIGH o LOW en funci´on del valor del pin 5v o 0v. Una vez que hemos le´ıdo el valor del pin de entrada mediante la funci´on digitalRead() s´olo falta implementar la toma de decisiones en funci´on de ese valor le´ıdo. Para ello usamos la funci´on perform actions() que consulta sobre el valor de la variable y si es igual a HIGH (bot´on pulsado) se enciende el LED, de lo contrario se apaga. Como puedes observar tratar con se˜nales digitales es muy sencillo. Existen sensores como el sensor de temperatura DHT11 que aun monitorizando una se˜nal anal´ogica (temperatura) son capaces de ofrecer dicha informaci´on como un valor digital. M´as adelante veremos c´omo se consigue esto y de qu´e manera lo podemos utilizar en los dise˜nos con Arduino. 3.3.2. Ejemplo 3: Leyendo temperatura y se˜nales anal´ogicas Hasta ahora hemos trabajado con se˜nales digitales. Normalmente el entorno que nos rodea es continuo y sus efectos se manifiestan de forma anal´ogica. Un ejemplo podr´ıa ser la energ´ıa que desprendemos cuando tenemos calor o incluso el sonido que producimos al hablar. Como puedes imaginar la necesidad de analizar y controlar estas magnitudes est´a presente en el dise˜no con microcontroladores. En esta secci´on veremos c´omo leer la temperatura ambiente y un ejemplo de actuaci´on en funci´on de dichos valores. Fundamentos En el mercado existen sensores de muchos tipos, los hay digitales como por ejemplo los sensores de paso y tambi´en los hay anal´ogicos como los sensores de temperatura. Normalmente, estos sensores producen se˜nales de un voltaje bajo. Para poder tratar con dichas se˜nales se suelen realizar diferentes etapas de amplificaci´on y procesamiento. Como seguramente ya sepas, los computadores y los microcontroladores ´unicamente trabajan con bits, es decir, d´ıgitos que ´unicamente pueden tomar el valor 1 o 0. Para convertir una se˜nal anal´ogica a una se˜nal digital se emplean los llamados ✭✭Conversores Anal´ogicos Digitales✮✮ o de forma abreviada ADC. Si lo que queremos es producir una se˜nal anal´ogica a partir de una digital se utilizan los ✭✭Conversores Digitales Anal´ogicos✮✮ o de forma abreviada DAC. La teor´ıa que hay detr´as de estos 5Las funciones dummy son aquellas que no tienen ninguna utilidad pero que se incorporan al c´odigo por alguna raz´on, normalmente por estandarizaci´on
    • 37. 3.3. UTILIZANDO LOS PINES DE ENTRADA 37 1 int led = 13; // De acuerdo a la tabla de entrada / salidas 2 int button = 12; // De acuerdo a la tabla de entrada / salidas 3 4 int button_state ; // Estado del boton 5 6 void setup () { 7 8 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada ) 9 pinMode ( led , OUTPUT ); 10 pinMode ( button , INPUT ); 11 12 } 13 14 void read_sensors () { 15 button_state = digitalRead ( button ); 16 } 17 18 void perform_actions () { 19 if( button_state == HIGH ){ 20 // HIGH es una constante equivalente a 5 v 21 digitalWrite ( led , HIGH ); 22 } else { 23 digitalWrite ( led , LOW ) ; 24 } 25 } 26 27 void loop () { 28 read_sensors () ; 29 perform_actions () ; 30 } C´odigo 3.3: Ejemplo 2: C´odigo para la utilizaci´on de un bot´on
    • 38. 3.3. UTILIZANDO LOS PINES DE ENTRADA 38 dispositivos es muy extensa y no es objeto de este libro el desarrollarla en profundidad. A continuaci´on se dar´a una breve explicaci´on sobre dos de los par´ametros claves a la hora de identificar si nuestro microcontrolador es adecuado para una determinada se˜nal. N´umero de bits: El n´umero de bits del conversor se puede ver como la resoluci´on del mismo. Un ejemplo ilustrar´a de forma clara este concepto. Imaginemos que tenemos una sensor que produce valores desde 0V hasta 5V y que tenemos un ADC de 10 bits (como es el caso del ATmega328) ¿cu´al es la resoluci´on m´axima con la que se podr´ıa trabajar en el dise˜no?. El calculo es sencillo. Si el conversor codifica los valores con 10 bits, significa que puede tomar valores entre 0 y 1023 (210 − 1) por lo tanto si dividimos los 5v (rango 5-0) entre los 1024 valores (rango 0-1023) tenemos una divisi´on de 0.0049 o lo que es lo mismo 4,9 mV por cada valor. De este modo si obtenemos una lectura con el valor 53, significar´a que el sensor ha generado 259 mV. Velocidad: La velocidad de muestreo es la capacidad del conversor para tratar con se˜nales de una frecuencia determinada. Seg´un el teorema de muestreo, la velocidad de muestreo debe ser como m´ınimo el doble del ancho de banda de la se˜nal de entrada. Aunque estos dos par´ametros son muy importantes a la hora de seleccionar un conversor, como ya hemos dicho existen muchos par´ametros est´aticos y din´amicos que influyen en la elecci´on de los mismos. Otro de los aspectos que debemos tener en cuenta a la hora de realizar una conversi´on es el valor de referencia. Como ya hemos explicado mediante el n´umero de bits, la resoluci´on de nuestra conversi´on estar´a dada por el n´umero de bits del conversor pero tambi´en hablamos de un rango fijo de 5 V. Este rango fijo se llama voltaje de referencia. ¿Para qu´e sirve este voltaje?. Imaginemos que tenemos un sensor que ´unicamente aporta valores entre 0 y 1.5 voltios o que en el entorno en el que estamos, el m´ınimo valor de un sensor de temperatura es 1.5 y el m´aximo 3.5. Como puedes observar los voltajes de referencia var´ıan. La soluci´on planteada anteriormente de usar un voltaje de referencia de 5 voltios es v´alida para ambos casos pero estaremos perdiendo resoluci´on dado que el Arduino est´a teniendo en cuenta valores que son imposibles de alcanzar (desde 1.5 V - 5 V). Para solucionar esta situaci´on en Arduino poseemos una funci´on llamada analogReference() que nos permite variar el voltaje de referencia pudiendo pasar los siguientes valores como par´ametro: DEFAULT: Voltaje de referencia de 5 voltios para placas de 5V y 3.3 para placas de 3.3 voltios. INTERNAL: Dependiente del microcontrolador. En los microcontroladores ATmega168 y 328 este voltaje es de 1.1, sin embargo, en los microcontroladores ATmega8 es de 2.56 voltios. INTERNAL1V1: Referencia de 1.1V (solo para los Arduino Mega) INTERNAL2V56: Referencia de 2.56V (solo para los Arduino Mega) EXTERNAL: Voltaje entre 0 y 5 voltios aplicados a la entrada AREF del Arduino
    • 39. 3.3. UTILIZANDO LOS PINES DE ENTRADA 39 Cuadro 3.3: Ejemplo 2: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Salida Led notificador de m´ınima temperatura led 13 Entrada Sensor de temperaturatemperature sensor A1 Si seleccionamos alguno de estos valores es muy importante que se realice la llamada a la funci´on analogReference() antes de cualquier llamada a la funci´on analogRead() de lo contrario podremos da˜nar la placa. Si el par´ametro pasado es EXTERNAL se deber´a aplicar un voltaje de entrada al pin AREF. Existen diferentes maneras de aplicar dicho voltaje. Una manera sencilla y estable es un diodo zener del valor requerido. Por otro lado, podemos utilizar un divisor de tensi´on. Hay que tener en cuenta que Arduino posee una resistencia de 32 Kohms interna por lo que la formula para el divisor de tensi´on quedar´a como se muestra en la Ecuaci´on 3.1. Teniendo en cuenta esto con una resistencia de 75 Khoms y un voltaje de entrada de 5v aproximadamente obtendr´ıamos un valor de referencia de 1.5V lo que nos permitir´ıa tener una resoluci´on de 1,5V /1024 = 1,5mV frente a los 5V /1024 = 4,88mV anteriores. V Ref = V in ∗ 32K 32K + X (3.1) Tabla de entrada/salida Para este ejemplo utilizaremos el m´odulo conectar y listo de Grove termostato. Existen numerosos sensores de temperatura. Algunos como por ejemplo el DHT11 o el DHT22 permiten leer la temperatura ´unicamente mediante se˜nales digitales. En este caso hemos decidido utilizar el modulo de Grove porque ya tiene incorporado los condensadores de filtro necesarios y diversas protecciones. Adem´as simplifica mucho el dise˜no y como ya dijimos en otros ejemplos, nos permite centrarnos en la programaci´on del microcontrolador. Como siempre lo primero que debemos realizar es la tabla de entradas y salidas de tal modo que nuestro dise˜no est´e siempre documentado. En este ejemplo vamos a utilizar un termostato y un LED notificador Tabla 3.3. Diagrama de flujo El diagrama de flujo es fundamental en cualquier dise˜no, muchas herramientas como Scratch for Arduino son capaces de generar todo el c´odigo a partir de un buen diagrama de flujo. En este ejemplo ´unicamente buscamos encender un LED cuando pase de la temperatura m´ınima configurada. El diagrama resultante es el mostrado en la Figura 3.13 Como se puede observar en el diagrama de la Figura 3.13 hay dos procesos a la hora de captar la temperatura. El primero obtiene la temperatura en formato RAW,
    • 40. 3.3. UTILIZANDO LOS PINES DE ENTRADA 40 Figura 3.13: Ejemplo 3: Diagrama de flujo es decir, un valor sin tratar. Una vez que tenemos dicho valor tenemos que realizar un proceso de conversi´on (normalmente especificado por el fabricante) mediante el cual el valor RAW se convierte en un valor ´util para el usuario. En Figura 3.3.2 se ver´a como realizar dicha conversi´on. C´odigo Esta soluci´on a˜nade a los dem´as ejemplos el tratamiento de la se˜nal anal´ogica por lo que ´unicamente se har´a ´enfasis en dicha parte. En la secci´on setup configuramos el pin A1 como entrada. Arduino no necesita especificar que dicho pin se comportar´a como un pin anal´ogico, por lo que este procedimiento es exactamente igual al realizado con el bot´on en Subsecci´on 3.3.1. En la funci´on read sensors() se realiza la lectura del sensor conectado a la entrada A1 del Arduino. La funci´on analogRead() devuelve un valor entre 0 y 1023 tal y como se explic´o en los fundamentos de este ejercicio. Una vez que tenemos el valor RAW hay que realizar una conversi´on a un valor en grados celsius. Para ello hay que utilizar una f´ormula especificada por el fabricante en el datasheet. Cada sensor utiliza una conversi´on diferente por lo que este paso var´ıa de un sensor a otro, incluso aunque el sensor capte la misma magnitud. La funci´on perform actions() ´unicamente se encarga de comprobar si la temperatura es menor al m´ınimo en cuyo caso activar´a el led.
    • 41. 3.3. UTILIZANDO LOS PINES DE ENTRADA 41 1 int led = 13; // De acuerdo a la tabla de entrada / salidas 2 int temperature_sensor = A0 ; // De acuerdo a la tabla de 3 // entrada / salidas 4 5 float temperature_celsius ; 6 float min_temperature = 25; 7 8 void setup () { 9 10 // pinMode configura un pin como OUTPUT ( salida ) o INPUT ( entrada ) 11 pinMode ( led , OUTPUT ); 12 pinMode ( temperature_sensor , INPUT ); 13 Serial . begin (9600) ; 14 15 } 16 17 void read_sensors () { 18 // Obtener la temperatura en formato crudo 19 int temperature_raw = analogRead ( temperature_sensor ); 20 // Convertir la temperatura en base a la formula del fabricante 21 temperature_celsius = convert_temperature ( temperature_raw ); 22 } 23 24 float convert_temperature ( int temperature_raw ){ 25 int factor = 3975; 26 float resistance = ( float ) (1023 - temperature_raw ) 27 * 10000 / temperature_raw ; 28 float ctemperature = 1 / ( log ( resistance / 10000) 29 / factor + 1 / 298.15) - 273.15; 30 return ctemperature ; 31 } 32 33 void perform_actions () { 34 if( temperature_celsius < min_temperature ) { 35 digitalWrite ( led , HIGH ); 36 } else { 37 digitalWrite ( led , LOW ) ; 38 } 39 } 40 41 void loop () { 42 read_sensors () ; 43 perform_actions () ; 44 Serial . println ( temperature_celsius ) ; 45 } C´odigo 3.4: Ejemplo 3: C´odigo de ejemplo para se˜nales anal´ogicas
    • 42. 3.3. UTILIZANDO LOS PINES DE ENTRADA 42
    • 43. CAP ´ ITULO 4 COMUNICACIONES 4.1. Introducci´on Uno de los aspectos importantes a considerar en el dise˜no de sistemas con microcontroladores son las comunicaciones. Si bien hay aspectos te´oricos y matem´aticos en el estudio de las comunicaciones muy importantes y que es menester conocer, en este cap´ıtulo siguiendo el enfoque pr´actico, aprenderemos a utilizar estas comunicaciones sin necesidad de entrar en profundidad en los aspectos puramente te´oricos. La elecci´on del modo de comunicaci´on a utilizar depende de las necesidades de nuestro dise˜no. Aspectos tales como protocolos de comunicaci´on, medios f´ısicos, ancho de banda, atenuaci´on, distorsi´on, ente otros, deben ser considerados a la hora de elegir el modo de comunicaci´on entre dispositivos. A continuaci´on veremos algunos modos de comunicaci´on que son soportados por Arduino. 4.2. Comunicaci´on serie mediante USART UART significa Universal Asynchronous Receiver/Transmitter, mientras que USART significa Universal Synchronous/Asynchronous Receiver/Transmitter, y es un dispositivo que trabaja entre dos elementos que se quieren comunicar, tomando bytes de datos de uno de ellos (transmisor) y transmiti´endolos hacia el otro (receptor) de manera secuencial bit por bit a una determinada tasa de transmisi´on. La informaci´on que transmiten estos bits depender´a del protocolo y de la codificaci´on elegidos. El protocolo nos indica la manera de comunicarnos, esto es, la cantidad y el tama˜no de las tramas de datos que se env´ıan, los bits de paridad, c´odigo de error, etc., mientras que la codificaci´on determina el contenido de la informaci´on y la manera en que los bits son ordenados para transmitirla. La tasa de transmisi´on se mide en baudios, bits/seg y deben ser configurados de la misma manera tanto en el transmisor como en el receptor para que puedan entenderse. La unidad USART es igual a la unidad UART con la diferencia que la primera permite la comunicaci´on s´ıncrona y as´ıncrona y la ´ultima ´unicamente as´ıncrona. 43
    • 44. 4.2. COMUNICACION SERIE MEDIANTE USART 44 ´ Data 8b Parity 0b Start 1b Stop 1b Figura 4.1: Trama con formato 8N1 Las diferencias entre la comunicaci´on as´ıncrona serie y s´ıncrona serie se explicar´an a continuaci´on. Comunicaci´on as´ıncrona serie: Si configuramos la unidad USART en modo as´ıncrono tendremos que configurar diferentes par´ametros para que tanto emisor como receptor sepan cuando tienen que leer o escribir los datos. Estos par´ametros son el n´umero de bits de datos, de paridad y de parada, as´ı como tambi´en la velocidad en baudios. Existen numerosas configuraciones pero la m´as t´ıpica es la siguiente: 8N1 que significa 8 bits de datos, ninguno de paridad y uno de parada. (ver Figura 4.1) En el entorno Arduino la velocidad de transmisi´on puede configurarse entre diferente valores desde 300 hasta 115200 baudios. Comunicaci´on s´ıncrona serie: La unidad USART del ATmega328 se puede configurar para funcionar en modo s´ıncrono, sin embargo no est´a soportado por la librer´ıa oficial de Arduino. Esto no significa que no pueda ser usado, pero si queremos usar este modo tendremos que desarrollar nuestra propia librer´ıa o programar la configuraci´on de la unidad en cada uno de nuestros programas. La comunicaci´on s´ıncrona se caracteriza por tener una l´ınea de reloj1 que permite que ambos dispositivos lean y escriban en el evento de reloj. Esto encarece la soluci´on dado que necesitamos un cable m´as, sin embargo, permite eliminar todos los bits de control de la comunicaci´on de tal modo que ´unicamente se trasmitir´an bits con informaci´on ´util. Se ha hablado del m´odulo UART, sin embargo, hay que tener en cuenta que la unidad UART es un m´odulo hardware y no un protocolo de comunicaci´on. La unidad UART se encarga de obtener en paralelo y en un ´unico ciclo de reloj los datos del registro de datos y los multiplexa en el tiempo seg´un la configuraci´on establecida. Existen diversas configuraciones, simplex, duplex, full-duplex, de dos dispositivos o en bus. Sin embargo, la comunicaci´on serie mediante UART fue desarrollada con el objetivo de comunicar ´unicamente dos dispositivos. A continuaci´on veremos diferentes ejemplos de como Arduino implementa la comunicaci´on serie en sus dispositivos. Una unidad UART es un m´odulo hardware y no un protocolo de comunicaci´on. La unidad UART se encarga de obtener en paralelo y en un ´unico ciclo de reloj los datos del registro de datos, y los serializa en el tiempo seg´un la configuraci´on establecida. Posee adem´as soporte para el control de flujo por hardware. Este control de flujo es realizado a trav´es de dos conexiones RTS (Request to Send) y CTS (Clear to Send) que permite a cada lado de la comunicaci´on indicar al otro que est´a listo para manejar datos. El cableado para la comunicaci´on serie as´ıncrona es muy sencillo y se puede ver en la Figura 4.2. Existen diversas configuraciones, simplex (en un solo sentido), 1En el ATmega328 la l´ınea de control se llama XCK0 y se encuentra en el pin 6 del encapsulado DIP28
    • 45. 4.2. COMUNICACION SERIE MEDIANTE USART 45 ´ Dispositivo A Dispositivo B RTS CTS CTS RTS Tx Tx Rx Rx Gnd Gnd Figura 4.2: Conexi´on entre dispositivos UART con control de flujo duplex (en ambos sentidos pero no simult´aneamente), y full-duplex (en ambos sentidos al mismo tiempo), con o sin control de flujo. Una UART se usa com´unmente con los est´andares de comunicaci´on RS-232, RS422 o RS-485 y tiene soporte para el control de flujo mediante hardware. Dependiendo de la implementaci´on f´ısica se utilizar´a un determinado nivel de voltaje u otro. El protocolo RS-232 utiliza de -3V a -15V para el nivel l´ogico ✭✭1✮✮ y de +3V a +15V para el nivel l´ogico ✭✭0✮✮ Sin embargo, los procolos con l´ogica TTL o CMOS trabajan con otros niveles. Existen circuitos integrados como el MAX232 que permiten realizar la conversi´on de niveles. Figura 4.3: Representaci´on de comunicaci´on serie En la Figura 4.3 se puede ver una representaci´on de lo explicado anteriormente. 4.2.1. Ejemplo 1: Hola mundo por Serie Como ya hemos dicho, Arduino tiene soporte para la comunicaci´on serie, tanto hardware como software. A nivel hardware dependiendo del microcontrolador tendremos m´as o menos unidades UART. La API que proporciona Arduino para el manejo de
    • 46. 4.2. COMUNICACION SERIE MEDIANTE USART 46 ´ Cuadro 4.1: Ejemplo 1: Tabla de entradas/salidas Entrada/Salida Descripci´on Nombre variable Pin Entrada Bot´on button 8 Entrada Rx0 RX 0 Salida Tx0 Tx 1 Cuadro 4.2: Ejemplo 1: Comunicaciones Interfaz serie Entrada/Salida Comando Evento Descripci´on Salida Pin 8 == HIGH Env´ıa ”hola mundo” las comunicaciones es muy sencilla de utilizar y aprenderemos a manejarla mediante los siguientes ejemplos. En este primer ejemplo realizaremos un ✭✭hola mundo✮✮ mediante el puerto serie. Cuando se pulse un bot´on el microcontrolador mandar´a por el puerto serie la cadena ✭✭hola mundo✮✮ Tabla de entrada/salida Como en los anteriores ejemplos, lo primero que vamos a hacer es la tabla de entrada/salida. En el enunciado se han se˜nalado dos de los elementos principales. Por un lado la comunicaci´on serie y por otro lado el bot´on. Es buena costumbre reservar un apartado ´ıntegro para las comunicaciones. En un dise˜no m´as complejo, lo m´as probable es que tengamos diferentes tipos de comunicaciones con diversos protocolos. En ese apartado, podremos definir todos los protocolos y las interfaces que proporciona nuestro dise˜no al ✭✭exterior✮✮. Volviendo a las entradas y salidas, si bien en este primer ejemplo lo ´unico que haremos es enviar un string por el puerto Tx (pin 1) dejamos ya indicada la entrada Rx que ser´a utilizada en los ejemplos que siguen. Comunicaciones Este ejemplo es muy sencillo, por lo que no definiremos un protocolo completo, es decir, sintaxis, sem´antica y temporizaci´on. Unicamente describiremos la comunicaci´on ´ en t´erminos de los mensajes de salida. En la Tabla 4.2 se puede ver un ejemplo de dicha tabla. Diagrama de flujo Las comunicaciones se pueden dise˜nar como eventos as´ıncronos y s´ıncronos, todo depender´a de como implementemos la soluci´on. En este caso y para no a˜nadir complejidad innecesaria en este punto, modelaremos la soluci´on en base a los ejemplos anteriores, es decir, de forma s´ıncrona y secuencial. En la Figura 4.4 puedes ver el diagrama propuesto.
    • 47. 4.2. COMUNICACION SERIE MEDIANTE USART 47 ´ Figura 4.4: Ejemplo 1 - Comunicaciones: Diagrama de flujo C´odigo En Cod. 4.1 se puede ver una posible soluci´on a este ejemplo. Lo primero que debemos hacer es configurar los elementos de entrada salida y las comunicaciones. Cuando el dise˜no sea m´as complejo, puede que sea necesario encapsular cada una de estas partes en otras subfunciones como por ejemplo init communications(). Una de las partes m´as importantes de este tutorial se encuentra en la funci´on Serial.begin(9600). Esta funci´on forma parte de la API de Arduino para la gesti´on y uso del puerto serie. Como habr´as podido imaginar, el primer par´ametro se refiere a la velocidad en baudios, recuerda que tanto receptor como emisor deben tener la misma velocidad. Por otro lado, en la Secci´on 4.2 se habl´o de la configuraci´on de los bits de parada, datos y paridad. Estos par´ametros se pueden pasar como segundo argumento a la funci´on Serial.begin(). En la siguiente web se pueden ver los valores que puede tomar este par´ametro: http://www.arduino.cc/en/pmwiki.php?n=Serial/Begin En la funci´on perform actions() es donde se realiza la actuaci´on en funci´on del estado del bot´on. Para este primer ejemplo hemos utilizado la funci´on Serial.println(). Esta funci´on env´ıa al registro de datos de la unidad UART la cadena indicada como par´ametro, adem´as termina la cadena con los caracteres ✭✭retorno de carro✮✮ y ✭✭salto de l´ınea✮✮. La funci´on devuelve un ✭✭long✮✮ que ´ındica el n´umero de bytes escritos en el puerto serie. Es muy importante que tengas en cuenta que esta funci´on no env´ıa los bytes de ✭✭golpe✮✮ por el ✭✭cable✮✮. Esta funci´on ´unicamente escribe los bytes en el registro de datos de la UART llamado, en el caso del Atmega328, UDRn. Si no quieres que finalice la cadena con los car´acteres de fin de l´ınea, puedes utilizar la funci´on Serial.print()
    • 48. 4.2. COMUNICACION SERIE MEDIANTE USART 48 ´ 1 i n t button = 8 ; //De acuerdo a l a t a b l a de 2 // en t r a d a / s a l i d a s 3 4 i n t b u t t o n s t a t e = LOW; 5 6 v oid se tup ( ) { 7 8 // pinMode c o n f i g u r a un p in como OUTPUT ( s a l i d a ) o INPUT ( en t r a d a ) 9 pinMode ( button , INPUT) ; 10 S e r i a l . b e gi n ( 9 6 0 0 ) ; 11 12 } 13 14 v oid r e a d s e n s o r s ( ) { 15 b u t t o n s t a t e = di gi t al R e a d ( button ) ; 16 } 17 18 v oid p e r f o rm a c ti o n s ( ) { 19 i f ( b u t t o n s t a t e == HIGH) { 20 S e r i a l . p r i n t l n ( " Hola mundo " ) ; 21 } 22 } 23 24 v oid l o o p ( ) { 25 r e a d s e n s o r s ( ) ; 26 p e r f o rm a c ti o n s ( ) ; 27 } C´odigo 4.1: Ejemplo 1 - Comunicaciones: C´odigo
    • 49. 4.2. COMUNICACION SERIE MEDIANTE USART 49 ´ Cuadro 4.3: Ejemplo 2: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Entrada Rx0 RX 0 Salida Led led red 8 Salida Led led yellow 9 Salida Tx0 Tx 1 4.2.2. Ejemplo 2: Recibiendo informaci´on Hasta ahora ´unicamente hemos enviado informaci´on al exterior mediante la comunicaci´on serie. En este segundo ejemplo veremos otra de las partes claves en cualquier dise˜no, la captaci´on de informaci´on mediante el puerto serie. La recepci´on de informaci´on al igual que el env´ıo de la misma normalmente est´a ligado a un protocolo de aplicaci´on y a una serie de parsers que se ocupan de decodificar dicho protocolo. Protocolos de comunicaci´on de bajo nivel dependen del medio f´ısico de comunicaci´on, por ejemplo: punto a punto, por buses, usando Ethernet, etc., y establecen el modo en que la comunicaci´on se lleva a cabo. Por ejemplo establece qui´en inicia la comunicaci´on, c´omo se direcciona el elemento receptor, c´omo se env´ıan los datos, si uno a uno o a r´afagas, etc. Protocolos de comunicaci´on de alto nivel o de aplicaci´on se refiere al conjunto de comandos de una aplicaci´on que se utilizan en la comunicaci´on para gestionar el funcionamiento de un sistema. El dise˜no de protocolos de aplicaci´on es una tarea que requiere de experiencia y no es el objetivo de este libro el entrar en detalle, sin embargo, se anima al lector a definir sus propias codificaciones y a practicar con la decodificaci´on de la informaci´on, dado que es un problema recurrente en la soluciones basadas en microcontroladores. El ejemplo es muy sencillo, en funci´on de la cadena recibida se encender´a un LED u otro (se utilizan LEDs para no complicar el dise˜no de forma innecesaria, el uso de uno u otro actuador normalmente radica en diferencias meramente el´ectricas). Tabla de entrada/salida Para este ejemplo utilizaremos dos LEDs, uno de color amarillo y otro de color rojo. En funci´on de la cadena que es recibida se encender´a un LED u otro. El ✭✭protocolo✮✮ es el mostrado en la Tabla 4.4. En la Tabla 4.3 se puede ver la tabla de entrada/salida con los nombres de las variables y los pines donde se conectar´an los actuadores. Comunicaciones y protocolos de aplicaci´on Cuando se dise˜na un protocolo de comunicaci´on de alto nivel lo primero que nos deber´ıamos preguntar es que objetivo tiene. Si no es un elemento cr´ıtico del dise˜no o ´unicamente se utiliza como protocolo de gesti´on, seguramente prefiramos definir el protocolo mediante caracteres ascii. Sin embargo, si lo que buscamos es una comunicaci´on r´apida, de poco tama˜no, lo m´as recomendable es una codificaci´on binaria. Un car´acter ascii se codifica mediante 8 bits (ascii extendido) ¿En que nos afecta este hecho?. Imaginemos que queremos mandar un comando que tiene 8 modos, podr´ıamos utilizar un car´acter ascii y poner cualquier valor desde 0 a 9. Esto supondr´a una carga de 8 bits para la comunicaci´on. Sin embargo, si utilizamos 3 bits y
    • 50. 4.2. COMUNICACION SERIE MEDIANTE USART 50 ´ Cuadro 4.4: Ejemplo 2 - Comunicaciones: Comunicaciones Interfaz serie Entrada/Salida Comando Evento Descripci´on Entrada led,yellow,high Pin 9 = HIGH Enciende el led amarillo Entrada led,yellow,low Pin 9 = LOW Apaga el led amarillo Entrada led,red,high Pin 8 = HIGH Enciende el led rojo Entrada led,red,low Pin 8 = LOW Apaga el led rojo Figura 4.5: Ejemplo 2 - Comunicaciones: Diagrama de flujo codificamos las posibilidades en binario, ´unicamente necesitaremos 3 bits, es decir, el tama˜no es un 37.5 % del tama˜no inicial. Normalmente para los prototipos y para las comunicaciones de gesti´on, se utilizan caracteres ascii dado que son m´as sencillos de depurar. En este ejemplo nosotros utilizaremos un protocolo de aplicaci´on ascii muy sencillo para que el lector pueda apreciar los fundamentos de la recepci´on de informaci´on mediante el puerto serie. En la Tabla 4.4 se puede ver la tabla de comunicaciones con los comandos y el formato. Como se puede observar en la Tabla 4.4 los campos est´an separados por comas, que se utilizar´an para la decodificaci´on de comandos tal y como veremos en la Figura 4.2.2. Diagrama de flujo En la Figura 4.5 se puede ver el diagrama en el que nos apoyaremos a la hora de dise˜nar nuestro c´odigo. A medida que vayamos avanzando en los dise˜nos, estos diagramas cada vez ser´an m´as gen´ericos y subir´an m´as el nivel de abstracci´on. Un ejemplo de este nivel de abstracci´on es la funci´on check communications() y parse command() donde no se entra en detalle en la implementaci´on interna de la misma, ´unicamente se sit´ua dentro del flujo de programa. Si quisi´eramos detallar una funci´on, una opci´on ser´ıa la de realizar un diagrama separado para la misma y m´as tarde unirlos.
    • 51. 4.2. COMUNICACION SERIE MEDIANTE USART 51 ´ Normalmente, en cada ciclo del bucle de chequeo de comunicaci´on se realizar´a la comprobaci´on del n´umero de datos recibido. Si existen datos se guardar´an en el buffer de procesado. Una vez detectado el fin de l´ınea se procesar´a el comando y realizar´a la acci´on. C´odigo Este ejemplo, aunque sencillo, requiere un poco m´as l´ıneas de c´odigo que el anterior. Un factor clave y determinante para un buen dise˜no es la modularizaci´on. Siempre que una parte del c´odigo se repita debemos plantearnos el convertirlo en una funci´on. Vamos a analizar el c´odigo de ejemplo. En este ejemplo (Cod. 4.2), hemos a˜nadido dos funciones gen´ericas: check communications() y la funci´on parse command(). La primera funci´on hace lo siguiente: Primero comprueba que si buffer de recepci´on tiene datos. La funci´on del buffer de recepci´on es la de guardar todo dato que entre al puerto serie, hasta 64 bytes, de modo que pueda ser consultado en cualquier momento. La funci´on Serial.available de la librer´ıa Arduino, nos proporciona un m´etodo para saber cuantos bytes se encuentran en el buffer de recepci´on. Mediante la funci´on Serial.read leemos el dato entrante y a˜nadimos el dato al buffer de procesado que hemos creado y que hemos llamado serial command. Es responsabilidad del programador vaciar el buffer mediante la llamada a Serial.read. Esta llamada devuelve el byte m´as antiguo en el buffer, es decir, el primero trasmitido (cola FIFO). Un detalle a tener en cuenta: en Internet y en la bibliograf´ıa sobre Arduino se pueden ver muchos ejemplos donde se hace la comprobaci´on Serial.available()>0. Aunque esto puede funcionar, hay que tener mucho cuidado. Puede que en el buffer haya m´as de 0 bytes, sin embargo, esto no significa que toda la trama est´e en el buffer, por lo que si se hace esta comprobaci´on habr´a que tener especial cuidado a la hora de formar la trama. Otra manera de evitar este comportamiento podr´ıa ser el comprobar que el tama˜no del buffer de recepci´on sea mayor que el tama˜no de trama. Otro de los puntos claves a la hora de tratar con protocolos ascii es el detectar el fin de l´ınea. Dependiendo del sistema operativo y de la consola que se utilice para mandar los datos, se utilizar´a un car´acter de fin de l´ınea u otro. En el entorno Arduino uno puede abrir un monitor de terminal serie usando (CTRL+Shift+m). En este monitor, por defecto, est´a seleccionada la opci´on de mandar como fin de l´ınea los caracteres ✭✭\r \n✮✮, sin embargo en el bloque ✭✭if✮✮ de nuestro c´odigo, se comprueba el fin de linea ´unicamente detectando si el valor recibido es igual a \n por lo tanto el car´acter \r ser´a insertado en nuestra trama y seguramente ocasione problemas en el dise˜no. La manera m´as sencilla de modificar este comportamiento es cambiar la opci´on en la consola o en este caso el monitor serial para que ´unicamente env´ıe el car´acter \n como car´acter fin de l´ınea. Una vez detectado el fin del comando y almacenado en el buffer de procesado, lo siguiente que se debe hacer es pasar dicho buffer a una funci´on que decodifique la trama y convierta los datos en informaci´on ´util para la l´ogica del programa. Con este fin se invoca a la funci´on parse command() (linea 32). Esta funci´on se ha creado apoy´andose en las funciones strcpy() y strtok(), que divide y copia los valores le´ıdos en la estructura Command protocol() (l´ıneas 9 a 14). Se recomienda al lector que revise la documentaci´on sobre la funci´on strtok(). La ´ultima fase es la de actuar en funci´on de la entrada, en nuestro caso ´unicamente tenemos que detectar el primer campo y detectar a que tipo de actuador se refiere la comunicaci´on. La funci´on perform action led() comprueba qu´e led se va a modificar
    • 52. 4.2. COMUNICACION SERIE MEDIANTE USART 52 ´ y en funci´on de dicho valor y del estado act´ua llamado a la funci´on digitalWrite(). Como puedes observar el dividir el c´odigo en m´odulos con responsabilidad limitada, permite que el c´odigo sea mucho m´as legible y mantenible. Si quisi´eramos a˜nadir la posibilidad de encender o apagar un motor, ´unicamente tendr´ıamos que crear otra funci´on que podr´ıamos llamar perform action motor() y hacer las comprobaciones pertinentes. 4.2.3. Ejemplo 3: Comunicaci´on entre Arduinos En los ejemplos anteriores hemos visto como manejar actuadores y como recibir informaci´on de los sensores. Adem´as se ha practicado con la comunicaci´on serie con el ordenador. En este ejemplo veremos como comunicar dos Arduinos entre si. Normalmente en un dise˜no de complejidad media los microcontroladores no se encuentran aislados, por el contrario forman parte de una red de sensores y actuadores. Un ejemplo claro de ´exito donde se puede apreciar esta jerarqu´ıa de controladores es el BMW X5. En BMW decidieron a˜nadir un controlador y sensores en cada amortiguador con el objetivo de recibir informaci´on de manera inmediata de las condiciones de las ruedas y del sistema de amortiguaci´on. En funci´on del valor de los sensores, el ordenador central del veh´ıculo modifica diferentes par´ametros de modo que la conducci´on sea m´as suave y c´omoda. Como puedes observar estamos ante un sistema distribuido en el que cada microcontrolador es responsable de un conjunto de sensores y un microcontrolador es responsable de la actuaci´on. En este ejemplo vamos a captar informaci´on en un Arduino y mediante las t´ecnicas aprendidas en los ejemplos anteriores informaremos a otro Arduino de dichos valores siendo este ´ultimo el responsable de actuar en funci´on de los valores recibidos. Tabla de entrada/salida Para simplificar el montaje en este ejemplo vamos a trabajar con sensores utilizados en otros ejemplos. Utilizaremos un bot´on, un sensor de temperatura como sensores y dos LEDs como actuadores. Un primer Arduino (esclavo) tendr´a conectados los sensores. Leer´a los datos del sensor de temperatura y del bot´on y los enviar´a a un segundo Arduino (maestro) cuando ´este lo solicite. Este segundo Arduino tendr´a conectados dos LEDs. Un led se encender´a si el boton ha sido apretado, mientras que el segundo led lo har´a de acuerdo al valor de temperatura recibido. Cuadro 4.5: Ejemplo 3: Tabla de entrada/salida Arduino esclavo Entrada/Salida Descripci´on Nombre variable Pin Entrada Bot´on button 8 Entrada Sensor de temperaturatemperature sensor A1 Entrada Rx0 RX 0 Salida Tx0 Tx 1
    • 53. 4.2. COMUNICACION SERIE MEDIANTE USART 53 ´ 1 #i n cl u d e <s t r i n g . h> 2 #i n cl u d e <s t d l i b . h> 3 4 c o n s t i n t l e d r e d = 1 3; 5 c o n s t i n t l e d y e l l o w = 8 ; 6 7 ch a r se ri al c omm and [ 1 9 ] ; 8 i n t inde x = 0 ; 9 t y p e d e f s t r u c t { 10 ch a r a c t u a t o r [ 5 ] ; 11 ch a r type [ 1 0 ] ; 12 ch a r s t a t e [ 2 ] ; 13 } Command protocol ; 14 Command protocol command protocol ; 15 16 v oid se tup ( ) { 17 pinMode ( l e d r e d , OUTPUT) ; 18 pinMode ( l e d y ell o w , OUTPUT) ; 19 S e r i a l . b e gi n ( 9 6 0 0 ) ; 20 } 21 22 v oid check c ommunic a ti on s ( ) { 23 i n t data = 0 ; 24 w hil e ( S e r i a l . a v a i l a b l e ( ) ) { 25 data = S e r i a l . re ad ( ) ; 26 i f ( data != ’\n’ ) { 27 se ri al c omm and [ inde x ] = data ; 28 inde x++; 29 } e l s e { 30 se ri al c omm and [ inde x ] = ’\0 ’ ; 31 inde x = 0 ; 32 parse command ( ) ; 33 } 34 } 35 } 36 37 v oid parse command ( ) { 38 ch a r ∗ token ; 39 i n t t o k e n i n d e x = 0 ; 40 s t r c p y ( command protocol . a c t u a t o r , s t r t o k ( serial command , "," ) ) ; 41 s t r c p y ( command protocol . type , s t r t o k (NULL, "," ) ) ; 42 s t r c p y ( command protocol . s t a t e , s t r t o k (NULL, "," ) ) ; 43 44 i f ( strcmp ( command protocol . a c t u a t o r , " led " ) == 0 ) { 45 p e r f o r m a c t i o n l e d ( command protocol . type , command protocol . s t a t e ) ; 46 } 47 48 } 49 50 v oid p e r f o r m a c t i o n l e d ( ch a r ∗ type , ch a r ∗ s t a t e ) { 51 i f ( strcmp ( type , " yellow " ) == 0 ) { 52 i f ( strcmp ( s t a t e , "on" ) == 0 ) { 53 d i g i t a lW r i t e ( l e d y ell o w , HIGH) ; 54 } e l s e { 55 d i g i t a lW r i t e ( l e d y ell o w , LOW) ; 56 } 57 } e l s e i f ( type , " red " ) { 58 i f ( strcmp ( s t a t e , "on" ) == 0 ) { 59 d i g i t a lW r i t e ( l e d r e d , HIGH) ; 60 } e l s e { 61 d i g i t a lW r i t e ( l e d r e d , LOW) ; 62 } 63 } 64 65 } 66 67 v oid l o o p ( ) { 68 check c ommunic a ti on s ( ) ; 69 } C´odigo 4.2: Ejemplo 2 - Comunicaciones
    • 54. 4.2. COMUNICACION SERIE MEDIANTE USART 54 ´ Cuadro 4.6: Ejemplo 3: Tabla de entrada/salida Arduino maestro Entrada/Salida Descripci´on Nombre variable Pin Entrada Rx0 RX 0 Salida Tx0 Tx 1 Salida Led notificador de temperatura temperature led 8 Salida Led notificador de bot´on button led 13 Comunicaciones El protocolo para este ejemplo es muy sencillo. El maestro (Arduino actuador) manda una petici´on de informaci´on. Esta petici´on ´unicamente contiene el id del sensor del cual desea obtener la informaci´on. Para simplificar los id se definen mediante la directiva de preprocesador ✭✭#DEFINE✮✮. Ambos Arduinos tendr´an que tener los mismos valores. Una vez que el esclavo detecta la petici´on env´ıa un mensaje con el identificador del sensor y el valor, separado por una coma. El maestro entonces deber´a capturar la trama y analizar de que sensor ha recibido el valor, si el sensor es un bot´on, esperar´a un int, si el sensor, por el contrario, es el de temperatura, entonces esperar´a un float. Es importante enviar delimitadores de trama de modo que sepamos en todo momento cuando se ha iniciado una trama y cuando ha finalizado la misma. En nuestro caso hemos utilizado como inicio de trama el car´acter \t y como finalizador de trama \r. Diagrama de flujo El diagrama de flujo est´a dividido en 3 partes. La primera de ellas es com´un a todos los dise˜nos y es el setup() del microcontrolador. Una vez se ha realizado el setup correctamente, el siguiente paso es obtener la informaci´on de los sensores (en nuestro caso el bot´on y el sensor de temperatura). Por ´ultimo comprobaremos si tenemos alguna comunicaci´on que atender. Si hay una comunicaci´on por atender haremos una decodificaci´on de la misma (mediante el parser ) y mandaremos un mensaje u otro en funci´on de la petici´on. En este esquema el Arduino que va a actuar es el que pide los datos. Podr´ıa plantearse otra situaci´on en el que el Arduino con los sensores fuera el que env´ıa de forma constante los valores captados. Cabe destacar que este diagrama de flujo es el correspondiente al Arduino Esclavo, se deja como ejercicio al lector la creaci´on del diagrama para el Arduino Maestro. C´odigo El c´odigo es muy parecido al realizado en la Subsecci´on 4.2.2. En Cod. 4.4 puedes ver el c´odigo del Arduino que act´ua como esclavo. En Cod. 4.3 se encuentra todo el c´odigo del Arduino que realiza las peticiones de informaci´on, es decir, el maestro. Ten en cuenta que se han omitido partes de c´odigo repetidas como por ejemplo la lectura de valores de temperatura con el objetivo de no hacer muy extenso el c´odigo.
    • 55. 4.2. COMUNICACION SERIE MEDIANTE USART 55 ´ Figura 4.6: Ejemplo 3 - Comunicaciones: Diagrama de flujo El c´odigo del esclavo te deber´ıa resultar familiar ya que es una recopilaci´on de los ejemplos anteriores. La parte m´as importante ahora es la decodificaci´on del protocolo que es pr´acticamente igual a la realizada en Subsecci´on 4.2.2. Se comprueba si hay alguna trama sin procesar preguntado por el car´acter \n, si hay alguna se obtienen todos los bytes del puerto serie hasta llegar al car´acter \n que indica el fin de l´ınea. Por otro lado, el maestro solicita los valores enviando una cadena por el puerto serie. Cuando recibe la informaci´on realiza un parser y en funci´on del tipo de informaci´on (temperatura o bot´on) realiza una acci´on u otra. Otra parte importante del c´odigo es la utilizaci´on de la librer´ıa SoftwareSerial. Mediante esta librer´ıa podemos implementar un emulador de UART en software (como una UART f´ısica) d´andole un nombre a la instancia creada y asign´andoles los pines de RX y TX correspondientes (l´ınea 13 del c´odigo). Utilizar estos puertos nos ayuda mucho a la hora de programar el Arduino dado que si utilizamos los pines correspondientes a los puertos RX y TX del puerto serie 0 para la comunicaci´on entre Arduinos, tendremos que desconectar dichos cables cada vez que queramos programar alguno de los Arduinos ya que la programaci´on tal y como se explic´o en el Secci´on A.1 se realiza mediante el puerto serie. Este tipo de comunicaci´on aunque muy sencilla puede llevar a problemas muy dif´ıciles de depurar. Uno de los problemas podr´ıa ser que uno de los Arduinos desbordara el buffer del otro. Para evitar esta condici´on en la funci´on check petition() del Arduino maestro se queda bloqueado hasta recibir el car´acter fin de trama. Este simple bucle act´ua como un mecanismo de control de flujo no permitiendo enviar ning´un mensaje hasta que no se reciba la contestaci´on. Aunque esta soluci´on es muy sencilla tiene muchos problemas dado que si el esclavo no contestara el Arduino maestro se quedar´ıa constantemente en espera. Para evitar estos problemas existen soluciones que utilizan ✭✭ACK’s✮✮ o mensajes de verificaci´on para mantener un control de flujo. Se deja como ejercicio al lector la implementaci´on del mecanismo de ✭✭ACK’s✮✮ a la soluci´on. 1 #d e f i n e BUTTON 0
    • 56. 4.2. COMUNICACION SERIE MEDIANTE USART 56 ´ 2 #d e f i n e TEMPERATURE SENSOR 1 3 #d e f i n e MAXTEMPERATURE 26 4 #i n cl u d e <s t d l i b . h> 5 #i n cl u d e <s t r i n g . h> 6 #i n cl u d e <S o f t w a r e S e r i a l . h> 7 8 S o f t w a r e S e r i a l s e r i a l ( 1 0 , 1 1 ) ; 9 c o n s t i n t b u t t o n l e d = 1 3; 10 c o n s t i n t t em p e r a t u r e l e d = 8 ; 11 i n t inde x = 0 ; 12 ch a r se ri al c omm and [ 1 9 ] ; 13 t y p e d e f s t r u c t { 14 i n t s e n s o r ; 15 } p e t i t i o n t ; 16 17 v oid se tup ( ) { 18 S e r i a l . b e gi n ( 9 6 0 0 ) ; S e r i a l . b e gi n ( 9 6 0 0 ) ; 19 pinMode ( b u t t o n l e d , OUTPUT) ; pinMode ( t em p e r a t u r e l e d ,OUTPUT) ; 20 } 21 22 v oid s e n d p e ti ti o n t e m p e r a t u r e ( ) { 23 s e r i a l . p ri n t ( "\t" ) ; 24 s e r i a l . p ri n t (TEMPERATURE SENSOR) ; 25 s e r i a l . p ri n t ( ’\n’ ) ; 26 } 27 28 v oid s e n d p e t i t i o n b u t t o n ( ) { 29 s e r i a l . p ri n t ( ’\t’ ) ; 30 s e r i a l . p ri n t (BUTTON) ; 31 s e r i a l . p ri n t ( ’\n’ ) ; 32 } 33 34 v oid c h e c k p e t i t i o n ( ) { 35 ch a r data = 0 ; 36 w hil e ( data != ’\n’ ) { 37 i f ( s e r i a l . a v a i l a b l e ( ) > 0 ) { 38 data = s e r i a l . re ad ( ) ; 39 i f ( data == ’\t’ ) { 40 inde x = 0 ; 41 } e l s e i f ( data == ’\n’ ) { 42 se ri al c omm and [ inde x ] = ’\0 ’ ; 43 inde x = 0 ; 44 parse command ( ) ; 45 } e l s e { 46 se ri al c omm and [ inde x ] = data ; 47 inde x++; 48 } 49 } 50 } 51 } 52 53 v oid parse command ( ) { 54 ch a r ∗ token = s t r t o k ( serial command , "," ) ; 55 i n t s e n s o r = a t o i ( token ) ; 56 swi t c h ( s e n s o r ) { 57 c a s e BUTTON: 58 p e r f o rm a c ti o n b u t t o n ( s t r t o k (NULL, "," ) ) ; 59 break ; 60 c a s e TEMPERATURE SENSOR: 61 p e r f o rm a c ti o n t em p e r a t u r e ( s t r t o k (NULL, "," ) ) ; 62 break ;
    • 57. 4.2. COMUNICACION SERIE MEDIANTE USART 57 ´ 63 } 64 } 65 66 v oid p e r f o rm a c ti o n b u t t o n ( ch a r ∗ v al u e ) { 67 i n t s t a t e = a t o i ( v al u e ) ; 68 d i g i t a lW r i t e ( b u t t o n l e d , s t a t e ) ; 69 } 70 71 v oid p e r f o rm a c ti o n t em p e r a t u r e ( ch a r ∗ v al u e ) { 72 f l o a t temp = a t o f ( v al u e ) ; 73 i f ( temp > MAXTEMPERATURE) { 74 d i g i t a lW r i t e ( t em p e r a t u r e l e d ,HIGH) ; 75 S e r i a l . p ri n t ( " Max temperature reached : " ) ; 76 S e r i a l . p r i n t l n ( temp ) ; 77 } e l s e { 78 d i g i t a lW r i t e ( t em p e r a t u r e l e d ,LOW) ; 79 } 80 } 81 v oid l o o p ( ) { 82 s e n d p e t i t i o n b u t t o n ( ) ; 83 S e r i a l . p r i n t l n ( " Sent button " ) ; c h e c k p e t i t i o n ( ) ; 84 s e n d p e ti ti o n t e m p e r a t u r e ( ) ; 85 S e r i a l . p r i n t l n ( " Sent temperature " ) ; c h e c k p e t i t i o n ( ) ; 86 d el a y ( 5 0 0 ) ; 87 } C´odigo 4.3: Ejemplo 3 - Comunicaciones: maestro 1 #i n cl u d e <s t r i n g . h> 2 #i n cl u d e <s t d l i b . h> 3 #i n cl u d e <S o f t w a r e S e r i a l . h> 4 #d e f i n e BUTTON 0 5 #d e f i n e TEMPERATURE SENSOR 1 6 c o n s t i n t button = 8 ; 7 c o n s t i n t t em p e r a t u r e s e n s o r = A1 ; 8 f l o a t t e m p e r a t u r e c e l s i u s ; 9 i n t b u t t o n s t a t e = 0 ; 10 ch a r se ri al c omm and [ 1 9 ] ; 11 i n t inde x = 0 ; 12 13 S o f t w a r e S e r i a l s e r i a l ( 1 0 , 1 1 ) ; 14 15 t y p e d e f s t r u c t { 16 i n t s e n s o r ; 17 } p e t i t i o n t ; 18 p e t i t i o n t l a s t p e t i t i o n ; 19 v oid se tup ( ) { 20 pinMode ( button , INPUT) ; 21 pinMode ( t em p e r a t u r e s e n s o r , INPUT) ; 22 S e r i a l . b e gi n ( 9 6 0 0 ) ; 23 s e r i a l . b e gi n ( 9 6 0 0 ) ; 24 } 25 v oid check c ommunic a ti on s ( ) { 26 ch a r data = 0 ; 27 i f ( s e r i a l . a v a i l a b l e ( ) > 0 ) { 28 data = s e r i a l . re ad ( ) ; 29 i f ( data == ’\t’ ) { 30 inde x = 0 ; 31 } e l s e i f ( data == ’\n’ ) { 32 se ri al c omm and [ inde x ] = ’\0 ’ ; 33 inde x = 0 ;
    • 58. 4.3. COMUNICACION I2C 58 ´ 34 parse command ( ) ; 35 } e l s e { 36 se ri al c omm and [ inde x ] = data ; 37 inde x++; 38 } 39 } 40 } 41 v oid parse command ( ) { 42 l a s t p e t i t i o n . s e n s o r = a t o i ( se ri al c omm and ) ; 43 swi t c h ( l a s t p e t i t i o n . s e n s o r ) { 44 c a s e BUTTON: 45 s e n d b u t t o n s t a t e ( ) ; 46 break ; 47 c a s e TEMPERATURE SENSOR: 48 s e n d t em p e r a t u r e ( ) ; 49 break ; 50 } 51 } 52 v oid s e n d b u t t o n s t a t e ( ) { 53 s e r i a l . p ri n t ( ’\t’ ) ; s e r i a l . p ri n t ( l a s t p e t i t i o n . s e n s o r ) ; 54 s e r i a l . p ri n t ( "," ) ; s e r i a l . p ri n t ( b u t t o n s t a t e ) ; 55 s e r i a l . p ri n t ( ’\n’ ) ; 56 } 57 v oid s e n d t em p e r a t u r e ( ) { 58 s e r i a l . p ri n t ( ’\t’ ) ; s e r i a l . p ri n t ( l a s t p e t i t i o n . s e n s o r ) ; 59 s e r i a l . p ri n t ( "," ) ; s e r i a l . p ri n t ( t e m p e r a t u r e c e l s i u s ) ; 60 s e r i a l . p ri n t ( ’\n’ ) ; 61 } 62 v oid l o o p ( ) { 63 r e a d s e n s o r s ( ) ; 64 check c ommunic a ti on s ( ) ; 65 } C´odigo 4.4: Ejemplo 3 - Comunicaciones: esclavo 4.3. Comunicaci´on I2C En la Secci´on 4.2 vimos un mecanismo de comunicaci´on muy sencillo (Comunicaci´on mediante UART). En esta secci´on veremos otro protocolo de comunicaci´on llamado I2C. I2C fue dise˜nado por la empresa Philips en 1992. Actualmente muchos perif´ericos implementan este bus de comunicaci´on por su sencillez, fiabilidad y el bajo n´umero de cables requerido (´unicamente 2). Es un protocolo de dos cables utilizado para conectar uno o mas maestros a uno o mas esclavos. Los esclavos pueden ser sensores de temperatura, humedad, de movimiento, memorias series, etc. Cada esclavo se A diferencia de la UART, mediante I2C podemos direccionar a varios esclavos, es decir, es un bus con m´ultiples esclavos cada uno de ellos identificados por una direcci´on de 7 bits o 10 bits en funci´on del est´andar utilizado. 4.3.1. Hardware Una de las ventajas de I2C frente a otros buses como SPI (Serial Peripheral Interface) es el bajo n´umero de cables requeridos para la comunicaci´on. I2C ´unicamente
    • 59. 4.3. COMUNICACION I2C 59 ´ requiere 2 cables dentro de un mismo circuito y tres cables para la comunicaci´on entre circuitos. La 3 se˜nales principales son: SCL (Serial CLock): La se˜nal de reloj siempre la genera el dispositivo que act´ua como maestro. Los esclavos pueden forzar al maestro a que el reloj deje de oscilar. En la Subsecci´on 4.3.2 veremos este caso. SDA (Serial Data): La se˜nal SDA se utiliza para enviar los datos. GND (Ground): Esta se˜nal no es obligatoria en comunicaciones dentro de un mismo circuito pero si la comunicaci´on es entre circuitos, entonces se necesitar´a con el objetivo de proporcionar un mismo nivel de referencia. 4.3.2. Protocolo I2C Figura 4.7: Esquema de comunicaci´on I2C El valor de las se˜nales SCL y SDA est´a en alto hasta que uno de los componentes los ponga en bajo. Los dos resistores de pull-up fuerzan el valor de los cables a VCC (3,3 - 5 volts). El maestro controla una se˜nal de reloj por la l´ınea SCL, determinando la tasa de transferencia de datos, y controla o deja controlar por el esclavo la l´ınea de direcciones SDA. La comunicaci´on entre un maestro y un esclavo consiste en una secuencia de transacciones de datos a trav´es de SDA controladas por el maestro o por el esclavo, sincronizadas por la se˜nal SCL. Hay tres tipos de transacciones, todas iniciadas por el maestro: escritura (el maestro escribe en la linea SDA, lectura (el esclavo escribe en la linea SDA) y combinadas (ambos escriben). La comunicaci´on a trav´es de I2C est´a formada por cuatro pasos: Condici´on de Inicio Trama de Direccionamiento Trama de Datos Condici´on de Parada Cada transacci´on comienza con una condici´on de inicio (S) y termina con una condici´on de Parada (P). Estas condiciones son controladas por el maestro. La condici´on de inicio la realiza el maestro con una transici´on de la se˜nal SDA de 1 a 0 en el semiciclo positivo del reloj SCL, mientras que la condici´on de parada se realiza con una transici´on de la se˜nal SDA de 0 a 1 en el semiciclo positivo de la se˜nal SCL. En I2C la transici´on en la l´ınea SDA durante las transacciones se hace en la
    • 60. 4.3. COMUNICACION I2C 60 ´ Figura 4.8: Trama de direcci´on I2C parte baja del ciclo reloj. Unicamente en la condici´on de inicio y parada se realiza la ´ transici´on en la parte alta del ciclo de reloj. Una vez que se ha indicado la condici´on de inicio el siguiente paso es enviar una trama con la direcci´on del dispositivo que se desea seleccionar. Existen dos modos de direccionamiento: de 7 bits o de 10 bits. El uso de 10 bits de comunicaci´on no es frecuente y no est´a cubierto en este libro. Al final de los 7 bits de direcci´on se agrega un octavo bit que indicar´a el tipo de transacci´on que se hace (0 = escritura, 1 = lectura). Hay que tener en cuenta que la direcci´on del esclavo se colocar´a en los 7 bits mas significativos de la trama de direcciones. Una vez enviada la trama de direccionamiento los esclavos leen cada bit en la transici´on del reloj, teniendo en cuenta que el primer bit es el m´as significativo de la direcci´on y que el ´ultimo bit corresponde al tipo de transferencia. Para comprobar que el esclavo ha detectado la petici´on de comunicaci´on del maestro, el esclavo debe poner la l´ınea SDA a nivel bajo antes del d´ecimo pulso de reloj, de lo contrario el maestro puede interpretar que la comunicaci´on no se ha podido establecer. Una vez que se ha establecido la comunicaci´on el siguiente paso es enviar la trama de datos. La trama de datos ´unicamente se trasmite bit por bit mediante codificaci´on MSB. En funci´on de si la transferencia es de lectura o escritura, ser´a el maestro o el esclavo el encargado de poner los datos en el bus. Es importante que el receptor en todo momento valide cada byte recibido mediante un ACK. Si el receptor env´ıa un NACK despu´es de recibir un byte, indicar´a al receptor que quiere cerrar la comunicaci´on. El ´ultimo paso consiste en la transici´on del estado de transferencia al estado de parada. Una vez que todos los datos han sido enviados y se ha recibido un NACK, el maestro generar´a la condici´on de parada. El protocolo I2C admite la comunicaci´on continuada entre un maestro y un esclavo. Para ello, una vez terminado el primer flujo de informaci´on (Condici´on de inicio + direcci´on + ack + datos + ack) en vez de enviar la condici´on de parada, el maestro mantiene ocupado el bus poniendo la se˜nal de datos en alto y manteniendo el reloj en alto. De este modo, no se permite que otro maestro adquiera el bus. Una vez completada esta fase se puede enviar otra vez la direcci´on del esclavo (cualquiera) y se procede al env´ıo de datos. En la Figura 4.8 se puede ver una ilustraci´on de la trama de direcci´on I2C y en Figura 4.7 un esquema donde se ilustra la comunicaci´on anteriormente explicada. Es importante tener en cuenta que el ACK lo env´ıa el receptor y no el emisor. En la Figura 4.9 se puede observar la estructura que presenta una trama de datos, donde ´unicamente se mandan los datos y un bit de ACK que env´ıa el receptor. En los siguientes ejemplos veremos de manera simplificada como realizar comunicaciones mediante el protocolo I2C en Arduino. 4.3.3. Ejemplo 1: Hola mundo mediante I2C Arduino posee una librer´ıa llamada Wire que implementa el protocolo I2C. Mediante esta librer´ıa la utilizaci´on de protocolo se reduce a unas sencillas funciones que
    • 61. 4.3. COMUNICACION I2C 61 ´ Figura 4.9: Trama de datos I2C Cuadro 4.7: Ejemplo 1 - I2C: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Entrada Linea SCL SCL A5 Entrada Linea SDA SDA A4 Salida Led notificador notificator led 13 nos abstraer´an por completo de detalles de m´as bajo nivel. Los pines I2C variar´an en funci´on del microcontrolador que estemos utilizando, recuerda que siempre puedes recurrir a la p´agina oficial de Arduino o al datasheet del microcontrolador para m´as informaci´on. En el caso de utilizar el bootloader del Atmega328 los pines correspondientes a SDA y SCL son A4 y A5 respectivamente. Algunas tarjetas de expansi´on poseen varios conectores I2C que permite expandir el bus para varios esclavos. En este ejemplo utilizaremos dos placas Arduino de modo que una env´ıe un mensaje y el otro reciba el mensaje y en funci´on del mismo encienda o apague un LED. De nuevo el LED es nuestro mejor amigo a la hora de comprobar que las cosas funcionan. Tabla de entrada/salida La tabla de entrada salida es muy simple (ver Tabla 4.7), ´unicamente tenemos un LED en el nodo esclavo que servir´a para comprobar que el mensaje ha sido recibido con ´exito. Diagrama de flujo Los diagramas de flujo que contienen eventos y comunicaciones suelen ser m´as complejos dado que tenemos que atendar a eventos que no son s´ıncronos. Se recomienda que el lector cree el diagrama de flujo correspondiente bas´andose en el c´odigo mostrado en Tabla 4.3.3. C´odigo Para evitar tratar con detalles de implementaci´on del protocolo I2C utilizaremos la librer´ıa Wire cuyas funciones mas utilizadas se explicar´an a continuaci´on. La documentaci´on oficial de la librer´ıa Wire puede ser consultada desde la siguiente direcci´on: http://playground.arduino.cc/Main/WireLibraryDetailedReference. begin(): Esta funci´on nos permite iniciar la librer´ıa. Esta funci´on es recurrente en la mayor´ıa de las librer´ıas. La implementaci´on suele reiniciar o poner en el estado inicial la m´aquina de estados y los registros de trabajo.
    • 62. 4.3. COMUNICACION I2C 62 ´ requestFrom(direcci´on, tama˜no): Mediante esta funci´on ponemos al maestro a la escucha del esclavo hasta que reciba el tama˜no fijado en el segundo par´ametro. La direcci´on tiene que ser de 7 bits. beginTransmission(direcci´on): Si queremos enviar datos desde el maestro al esclavo, despu´es de llamar a la funci´on begin, tenemos que utilizar la funci´on beginTransmission. Mediante esta funci´on indicamos a la librer´ıa que pase al estado preparado y configure el buffer de direccionamiento. Ten en cuenta que esta funci´on no enviar´a ning´un bit, ´unicamente hace operaciones a nivel interno con el objetivo de no ocupar el bus de forma innecesaria. send(buffer): Una vez que se ha establecido la direcci´on la funci´on send nos permite configurar el buffer de datos de la interfaz TWI2. endTransmission(): La ´ultima funci´on en cada comunicaci´on es endTransmission() que mandar´a todos los datos del buffer a la direcci´on indicada mediante beginTransmission() y por ´ultimo enviar´a el bit de parada. onReceive((void*)func(int)): Cuando la librer´ıa detecta que existe un dato llama a la funci´on handler pasada como par´ametro. Esta funci´on tiene que tener la ✭✭firma✮✮ void func(int), el par´ametro que recibe contiene el n´umero de bytes capturados. El bus no ser´a liberado hasta que se retorne de la funci´on handler. receive(): Devuelve el siguiente byte en el buffer de recepci´on. onRequest((void*)func(void)): Si el esclavo recibe la se˜nal SLA+R, es decir, la direcci´on del dispositivo y la opci´on de lectura, la librer´ıa Wire llama a esta funci´on handler donde se deber´a insertar los datos en el buffer de escritura con el objetivo de iniciar el env´ıo. available(): Devuelve el n´umero de bytes del buffer de recepci´on. Una vez que se tiene claro cada una de las funciones de la librer´ıa, el c´odigo mostrado en Cod. 4.5 y Cod. 4.6 no debe suponer ning´un problema. En primer lugar el Arduino maestro env´ıa un byte de datos con el car´acter ascii ✭✭h✮✮ (recuerda que un car´acter ascii extendido es igual a un byte) y espera medio segundo. Por otro lado, el Arduino esclavo ´unicamente espera los datos mediante la funci´on handler. Esta funci´on handler se inscribe al evento del bus mediante la funci´on onRequest(). Ten en cuenta que aunque en este c´odigo y a modo de ejemplo se ha realizado la operaci´on de encender el led dentro de la funci´on, esto no es recomendable dado que mientras permanezcamos en dicha funci´on el bus se mantendr´a ocupado. Por ´ultimo, se recomienda que el lector modifique el c´odigo del esclavo y del maestro con el objetivo de apagar y encender el led mediante alg´un evento, este evento puede ser la inserci´on de una cadena en el puerto serie o un evento temporal. Adem´as, se recomienda que se realice la modificaci´on del pin de salida fuera de la funci´on handler. 2TWI (Two Wire Interface) es otra forma de nombrar al protocolo I2C
    • 63. 4.3. COMUNICACION I2C 63 ´ 1 #i n cl u d e <Wire . h> 2 3 4 v oid se tup ( ) { 5 Wire . b e gi n ( ) ; 6 7 } 8 9 v oid l o o p ( ) { 10 Wire . b e gi nT r a n smi s si o n ( 1 ) ; 11 Wire . w ri t e ( "h" ) ; 12 Wire . endT r an smi s si on ( ) ; 13 d el a y ( 5 0 0 ) ; 14 } C´odigo 4.5: Ejemplo 1 - Comunicaciones I2C: maestro 1 #i n cl u d e <Wire . h> 2 c o n s t i n t n o t i f i c a t i o n l e d = 1 3; 3 4 v oid se tup ( ) { 5 pinMode ( n o t i f i c a t i o n l e d ,OUTPUT) ; 6 Wire . b e gi n ( 1 ) ; 7 S e r i a l . b e gi n ( 9 6 0 0 ) ; 8 Wire . onReceive ( h a n dl e r ) ; 9 10 } 11 v oid h a n dl e r ( i n t num bytes ) { 12 ch a r v al u e = 0 ; 13 w hil e (Wire . a v a i l a b l e ( ) > 0 ) { 14 v al u e = Wire . re ad ( ) ; 15 } 16 S e r i a l . p ri n t ( " Read : " ) ; 17 S e r i a l . p r i n t l n ( v al u e ) ; 18 i f ( v al u e == ’h’ ) { 19 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d ,HIGH) ; 20 } e l s e { 21 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d ,LOW) ; 22 } 23 24 } 25 v oid l o o p ( ) { 26 d el a y ( 1 0 0 ) ; 27 } C´odigo 4.6: Ejemplo 1 - Comunicaciones I2C: esclavo
    • 64. 4.3. COMUNICACION I2C 64 ´ Figura 4.10: Sensor MPU6050 4.3.4. Ejemplo 2: Obteniendo datos de un IMU En este ejemplo veremos como utilizar uno de los muchos sensores IMU (unidad de medici´on inercial) mediante el protocolo I2C para obtener los ´angulos de rotaci´on con respecto al eje ✭✭X✮✮ y el eje ✭✭Y✮✮. El uso de estos sensores requiere de pr´actica y de conocimientos trigonom´etricos para tratar los valores ✭✭crudos✮✮3. En este ejemplo, no nos centraremos en las funciones matem´aticas, la parte m´as interesante para este capitulo es la comunicaci´on con el sensor. Para este ejemplo hemos utilizado el sensor MPU6050 (ver Figura 4.10) este sensor proporciona 6 grados de libertad y posee un controlador DMP (Digital Motion Processing). Con el objetivo de simplificar las cosas ´unicamente veremos como obtener el valor del giroscopio, aunque tambi´en obtengamos el del aceler´ometro para utilizarlo en el filtro complementario4. Debido a la sencillez del ejemplo y al hecho de que ´unicamente utilizaremos un sensor, en este caso no realizaremos diagrama de flujo ni tabla de comunicaciones, ´unicamente mantendremos la tabla de entrada salida con el objetivo de mostrar las conexiones necesarias. Tabla de entrada/salida Como ya hemos comentado, la estructura de este ejemplo es diferente debido a que ´unicamente estamos utilizando un sensor y no estamos haciendo ning´un tipo actuaci´on en funci´on de los valores obtenidos. En la Figura 4.11 se puede observar la conexi´on de este sensor. Unicamente utilizaremos el bus principal I2C. El ´ MPU6050 tiene otro bus I2C que utiliza para gestionar una cola FIFO. Es muy importante que te asegures del voltaje de entrada de tu sensor. Algunos MPU6050 tienen un conversor que permiten conectar el sensor a 5V, pero si dudas conectalo a 3.3V. Ten en cuenta que en este caso no hace falta una resistencia de pull-up, esto se debe a que el sensor trae una resistencia interna. Muchos sensores que se comunican 3Los valores ✭✭crudos✮✮ o ✭✭RAW✮✮ son aquellos valores que no han sido tratados y que son resultado de una captura 4El filtro complementario es la uni´on de un filtro de paso bajo con un filtro de paso alto
    • 65. 4.3. COMUNICACION I2C 65 ´ Figura 4.11: Conexiones MPU6050 mediante I2C incorporan esta resistencia. C´odigo Se puede revisar el c´odigo en Cod. 4.7. A continuaci´on, describiremos las partes m´as importantes del mismo. 1 #i n cl u d e <Wire . h> 2 #d e f i n e IMU 0 x68 3 #d e f i n e A R 1 6 3 8 4. 0 4 #d e f i n e G R 1 3 1. 0 5 #d e f i n e RAD TO DEG 5 7. 2 9 5 7 7 9 6 i n t 1 6 t AcX, AcY, AcZ , GyX, GyY, GyZ; 7 f l o a t Acc [ 2 ] ; 8 f l o a t Gy [ 2 ] ; 9 f l o a t Angle [ 2 ] ; 10 v oid se tup ( ) { 11 Wire . b e gi n ( ) ; 12 Wire . b e gi nT r a n smi s si o n (IMU) ; 13 Wire . w ri t e ( 0 x6B ) ; 14 Wire . w ri t e ( 0 ) ; 15 Wire . endT r an smi s si on ( t r u e ) ; 16 S e r i a l . b e gi n ( 9 6 0 0 ) ;
    • 66. 4.3. COMUNICACION I2C 66 ´ 17 } 18 19 v oid l o o p ( ) { 20 Wire . b e gi nT r a n smi s si o n (IMU) ; 21 Wire . w ri t e ( 0 x3B ) ; 22 Wire . endT r an smi s si on ( f a l s e ) ; 23 Wire . requestFrom (IMU, 6 , t r u e ) ; 24 AcX = Wire . re ad ( ) << 8 | Wire . re ad ( ) ; 25 AcY = Wire . re ad ( ) << 8 | Wire . re ad ( ) ; 26 AcZ = Wire . re ad ( ) << 8 | Wire . re ad ( ) ; 27 28 Acc [ 1 ] = atan (−1 ∗ (AcX / A R) / s q r t ( pow ( (AcY / A R) , 2 ) + pow ( ( AcZ / A R) , 2 ) ) ) ∗ RAD TO DEG; 29 Acc [ 0 ] = atan ( (AcY / A R) / s q r t ( pow ( (AcX / A R) , 2 ) + pow ( ( AcZ / A R) , 2 ) ) ) ∗ RAD TO DEG; 30 31 Wire . b e gi nT r a n smi s si o n (IMU) ; 32 Wire . w ri t e ( 0 x43 ) ; 33 Wire . endT r an smi s si on ( f a l s e ) ; 34 Wire . requestFrom (IMU, 4 , t r u e ) ; 35 GyX = Wire . re ad ( ) << 8 | Wire . re ad ( ) ; 36 GyY = Wire . re ad ( ) << 8 | Wire . re ad ( ) ; 37 38 Gy [ 0 ] = GyX / G R ; 39 Gy [ 1 ] = GyY / G R ; 40 41 Angle [ 0 ] = 0. 9 8 ∗ ( Angle [ 0 ] + Gy [ 0 ] ∗ 0. 0 1 0 ) + 0. 0 2 ∗ Acc [ 0 ] ; 42 Angle [ 1 ] = 0. 9 8 ∗ ( Angle [ 1 ] + Gy [ 1 ] ∗ 0. 0 1 0 ) + 0. 0 2 ∗ Acc [ 1 ] ; 43 44 S e r i a l . p ri n t ( " Angle X: " ) ; S e r i a l . p r i n t ( Angle [ 0 ] ) ; S e r i a l . p ri n t ( "\n " ) ; 45 S e r i a l . p ri n t ( " Angle Y: " ) ; S e r i a l . p r i n t ( Angle [ 1 ] ) ; S e r i a l . p ri n t ( "\n - - - - - - - - - - - -\n" ) ; 46 47 d el a y ( 1 0 ) ; 48 49 } C´odigo 4.7: Ejemplo 2 - Comunicaciones I2C: Sensor MPU-6050 En la funci´on de setup() iniciamos el dispositivo. La inicializaci´on del sensor depender´a de cada uno, en este caso tenemos que mandar el comando ✭✭0✮✮ al registro 0x6B. Normalmente el procedimiento suele ser el siguiente: En primer lugar se env´ıa un byte con el registro donde se quiere leer o escribir (en este caos el 0x6B) y seguidamente se env´ıa o recibe el dato del registro (en este caso enviamos el comando 0 que inicia el dispositivo). Una vez que hemos enviado la petici´on de inicio cerramos la comunicaci´on mediante la llamada a la funci´on endTransmission() de la librer´ıa Wire. Por otro lado, en cada ciclo de ✭✭scan✮✮ (funci´on loop()) obtenemos los valores de cada uno de los registros del sensor5. Para obtener el valor del registro que contiene los valores del aceler´ometro tenemos que enviar la petici´on a la direcci´on 0x3B. Este paso es muy importante y te ayudar´a a entender cualquier comunicaci´on I2C mediante Arduino. Una vez que hemos escrito el valor 0x3B en el buffer de datos del bus I2C del Arduino, el siguiente paso es enviar dicho buffer por el canal SDA. Ten en cuenta que como se explic´o en la Tabla 4.3.3 los datos no se env´ıan hasta que se llama a la funci´on 5Todos las direcciones as´ı como el valor de los comandos se obtienen del datasheet de cada sensor
    • 67. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 67 endTransmission. Al contrario que en el caso del comando de inicializaci´on, en este caso vamos a recibir datos del sensor. Cuando se env´ıa la petici´on 0x3B tenemos que activar el bit de lectura del bus I2C con el objetivo de que el esclavo (sensor) pueda escribir en el maestro (Arduino). Para activar el bit de lectura primero finalizamos la fase de transmisi´on de datos mediante endTransmission(false). Despu´es indicamos que queremos obtener datos del esclavo y esperamos hasta tener 6 bytes, todo ello mediante la funci´on requestFrom(). Los datos se env´ıan byte a byte. Como los datos son de 16 bits tendremos que utilizar operaciones de manipulaci´on de bits para formar una palabra de 16 bits. En las l´ıneas 24,25,26 se pueden ver dichas operaciones. Para obtener el valor del giroscopio se sigue el mismo procedimiento, la ´unica diferencia est´a en el n´umero de bytes que recibimos del esclavo y la direcci´on del registro. En las l´ıneas 41 y 46 se aplica un filtro complementario que no ser´a explicado debido a que no es el objetivo de este libro. Si abres el monitor serial podr´as ver los valores obtenidos cada 10ms. 4.4. Protocolo SPI (Serial Peripheral Interface En esta secci´on veremos otro protocolo de comunicaci´on serial SPI. SPI es un protocolo de comunicaci´on serie s´ıncrono para comunicaci´on entre microcontroladores y uno o mas perif´ericos a corta distancia como displays, tarjetas de memoria y sensores. Cada instancia de SPI tiene un maestro y uno o varios esclavos. Posee cuatro se˜nales b´asicas: Tres desde el maestro hacia el esclavo SS (Slave Select): Se˜nal que se replica para cada esclavo. El maestro pone a cero una de las se˜nales SS para seleccionar el esclavo correspondiente MOSI (Master Out Slave In): Se˜nal por donde se transmite una palabra (byte) desde el maestro hacia el esclavo SCK (Serial Clock): Se˜nal que sincroniza la comunicaci´on Y una se˜nal desde el esclavo hacia el maestro MISO (Master In Slave Out): Se˜nal por donde se transmite una palabra (byte) desde el esclavo hacia el maestro El procesador ATmega328 tiene una interfaz SPI para comunicaci´on. Las se˜nales SS, MOSI, MISO y SCK corresponden a los pines 16 al 19 respectivamente. En el m´odulo Arduino estos pines est´an conectados a los pines 10 al 13 respectivamente. (Figura 4.12). El mecanismo de transferencia se realiza de la siguiente manera: Toda la comunicaci´on es controlada por el m´aster que selecciona el esclavo poniendo en bajo la se˜nal SS correspondiente. Entonces env´ıa en modo serie una palabra (byte) por la l´ınea MOSI y simult´aneamente acepta un byte proveniente desde el esclavo por la l´ınea MISO. Esta transferencia se realiza generando 8 pulsos sobre la l´ınea de sincronizaci´on SCK En Arduino existe una librer´ıa SPI.h que nos proporciona una serie de funciones para controlar el proceso de comunicaci´on. SPIbegin: Inicializa el bus configurando SS como salida con pullup interno en alto y MOSI y SCLK como salida en bajo.
    • 68. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 68 Figura 4.12: Pines SPI en el proc. ATmega328 SPISetting: Esta funci´on permite configurar algunos par´ametros de comunicaci´on tales como los siguientes, los cuales se deben poner en el orden que se describen: • Velocidad de comunicaci´on (generalmente se pone la frecuencia a que opera el microcontrolador en HZ). • Si el primer bit enviado es el mas significativo (MSB) o el menos significativo (LSB) • El modo de comunicaci´on SPI, que nos indica en que flanco del reloj los datos son enviados o recibidos (Fase del reloj) y si el reloj est´a inactivo cuando est´a en alto o en bajo (Polaridad) (ver tabla Tabla 4.8). SPI.beginTransaction: Inicializa el bus SPI con SPISetting SPI.endTransaction SPI.transfer Los pasos para establecer una comunicaci´on SPI son los siguientes, 1. SPI.begin(): Iniciar la librer´ıa SPI (normalmente en el setup() del programa) 2. SPI.beginTransaction(SPISetting()): La librer´ıa se apropia de las interrupciones. 3. SPI.transfer(): Se transfieren 8 bits 4. SPI.endTransaction(): Se libera el bus y se habilitan de nuevo las interrupciones.
    • 69. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 69 Cuadro 4.8: Modos de comunicaci´on SPI Modo Clock Polarity Clock Phase MODO 0 0 0 MODO 1 0 1 MODO 2 1 0 MODO 3 1 1 Figura 4.13: Contenidos del registro de entrada del DAC Ejemplo: Ejemplo de uso del bus SPI En este ejemplo mostraremos como generar una se˜nal sinusoidal en el procesador y enviarla a un conversor DAC (Conversor Digital/Anal´ogico) a trav´es del puerto SPI. El c´odigo del ejemplo est´a en la figura Cod. 4.8. En la primera parte del c´odigo se crea una tabla de consulta (look-up table) que en realidad es un arreglo de memoria que llamamos Waveform con valores que forman una sinusoide. Este arreglo es recorrido y cada valor es enviado a trav´es del bus SPI, al conversor DAC externo conectado al Arduino para generar la sinusoide. Para este ejemplo se utiliz´o un m´odulo de convers´ıon D/A de 8 canales PMODDA4 de DIGILENT que usa el conversor AD5628 de ANALOG DEVICES, 6 La comunicaci´on se realiza enviando transacciones de 32 bits a trav´es del puerto MOSI al esclavo (ver figura Figura 4.137 Sin entrar en detalles t´ecnicos acerca del DAC AD5628 es necesario saber que los bits C3 al C0 se utilizan para indicar el comando a ejecutar por el AD5628. En el caso de un Reset el valor es el ”0111”, y para configurar el valor de referencia en la conversion es el ”1000”. El bit DB0 se debe poner a 1 si se utiliza valor de referencia. Los bits A3 a A0 indican la direcci´on de uno de los 8 canales del conversor desde el ”0000”, hasta el ”0111”. Para seleccionar todos los canales se coloca el valor ”1111”. El primer paso es inicializar la comunicaci´on SPI. Para ello hemos creado la funci´on init spi que pone en alto la se˜nal slave select y llama a la funci´on begin de la librer´ıa SPI (SPI.begin). El siguiente paso es configurar el m´odulo esclavo, seg´un se indica en la l´ınea 27 del c´odigo, donde se indica la frecuencia del reloj, si el primer bit es el m´as significativo, y el modo de comunicaci´on. Para configurar el esclavo es necesario enviarle un comando de reset y luego otro comando con el setup del valor de referencia. Para ello es necesario colocar la se˜nal slave selecta ’0’ durante la transacci´on. En este ejemplo se realizan cuatro transacciones de 8 bits cada una (tanto en la funci´on config dac como en la funci´on Loop), pero se puede utilizar la funci´on 6 Informaci´on acerca del conversor PMODDA4 de DIGILENT puede encontrarse en este enlace https://reference.digilentinc.com/pmod:pmod:DA4 7 tomada de https://reference.digilentinc.com/pmod:pmod:DA4
    • 70. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 70 Figura 4.14: Forma de onda obtenida conversi´on D/A SPI.Transfer16() de Arduino y realizar dos transacciones de 16 bits. Una vez configurado se entra en la funci´on Loop y se comienzan a enviar los datos que se sacan de memoria al conversor. La salida obtenida en nuestro ejemplo puede verse en la figura Figura 4.14
    • 71. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 71 1 # include < SPI .h > 2 const int slave_select = 22; 3 const long waveform [120] 4 { 5 0 x7ff , 0 x86a , 0 x8d5 , 0 x93f , 0 x9a9 , 0 xa11 , 0 xa78 , 0 xadd , 0 xb40 , 6 0 xba1 , 0 xbff , 0 xc5a , 0 xcb2 , 0 xd08 , 0 xd59 , 0 xda7 , 0 xdf1 , 0 xe36 , 7 0 xe77 , 0 xeb4 , 0 xeec , 0 xf1f , 0 xf4d , 0 xf77 , 0 xf9a , 0 xfb9 , 0 xfd2 , 8 0 xfe5 , 0 xff3 , 0 xffc , 0 xfff , 0 xffc , 0 xff3 , 0 xfe5 , 0 xfd2 , 0 xfb9 , 9 0 xf9a , 0 xf77 , 0 xf4d , 0 xf1f , 0 xeec , 0 xeb4 , 0 xe77 , 0 xe36 , 0 xdf1 , 10 0 xda7 , 0 xd59 , 0 xd08 , 0 xcb2 , 0 xc5a , 0 xbff , 0 xba1 , 0 xb40 , 0 xadd , 11 0 xa78 , 0 xa11 , 0 x9a9 , 0 x93f , 0 x8d5 , 0 x86a , 0 x7ff , 0 x794 , 0 x729 , 12 0 x6bf , 0 x655 , 0 x5ed , 0 x586 , 0 x521 , 0 x4be , 0 x45d , 0 x3ff , 0 x3a4 , 13 0 x34c , 0 x2f6 , 0 x2a5 , 0 x257 , 0 x20d , 0 x1c8 , 0 x187 , 0 x14a , 0 x112 , 14 0 xdf , 0 xb1 , 0 x87 , 0 x64 , 0 x45 , 0 x2c , 0 x19 , 0xb , 0 x2 , 0x0 , 0x2 , 15 0xb , 0 x19 , 0 x2c , 0 x45 , 0 x64 , 0 x87 , 0 xb1 , 0 xdf , 0 x112 , 0 x14a , 16 0 x187 , 0 x1c8 , 0 x20d , 0 x257 , 0 x2a5 , 0 x2f6 , 0 x34c , 0 x3a4 , 0 x3ff , 17 0 x45d , 0 x4be , 0 x521 , 0 x586 , 0 x5ed , 0 x655 , 0 x6bf , 0 x729 , 0 x794 18 }; 19 void init_spi () { 20 pinMode ( slave_select , OUTPUT ); 21 digitalWrite ( slave_select , HIGH ); 22 SPI . begin () ; 23 } 24 void config_dac () { 25 unsigned long reset = 0 x07000000 ; 26 unsigned long reference = 0 x08000001 ; 27 SPI . beginTransaction ( SPISettings (12000000 , MSBFIRST , SPI_MODE0 ) ); // gain control of SPI bus 28 digitalWrite ( slave_select , LOW ); 29 SPI . transfer (( reset & 0 xFF000000 ) >> 24) ; 30 SPI . transfer (( reset & 0 x00FF0000 ) >> 16) ; 31 SPI . transfer (( reset & 0 x0000FF00 ) >> 8) ; 32 SPI . transfer (( reset & 0 x000000FF )); 33 digitalWrite ( slave_select , HIGH ); 34 SPI . endTransaction () ; // release the SPI bus 35 SPI . beginTransaction ( SPISettings (12000000 , MSBFIRST , SPI_MODE0 )) ; // gain control of SPI bus 36 digitalWrite ( slave_select , LOW ); 37 SPI . transfer (( reference & 0 xFF000000 ) >> 24) ; 38 SPI . transfer (( reference & 0 x00FF0000 ) >> 16) ; 39 SPI . transfer (( reference & 0 x0000FF00 ) >> 8) ; 40 SPI . transfer (( reference & 0 x000000FF ) >> 0) ; 41 digitalWrite ( slave_select , HIGH ); 42 SPI . endTransaction () ; // release the SPI bus 43 } 44 void setup () { 45 init_spi () ; 46 config_dac () ; 47 } 48 void loop () { 49 unsigned long value = 0 x03F00000 ; 50 unsigned long temp_value = 0 x00000000 ; 51 for (int i = 0; i < 120; i ++) { 52 temp_value |= (( waveform [ i] << 8) ); 53 SPI . beginTransaction ( SPISettings (16000000 , MSBFIRST , SPI_MODE0 ) ); // gain control of SPI bus 54 digitalWrite ( slave_select , LOW ); 55 unsigned long value2 = value | temp_value ; 56 SPI . transfer ((( value2 & 0 xFF000000 ) >> 24) ); 57 SPI . transfer ((( value2 & 0 x00FF0000 ) >> 16) ); 58 SPI . transfer ((( value2 & 0 x0000FF00 ) >> 8) ) ; 59 SPI . transfer ((( value2 & 0 x000000FF ) >> 0) ) ; 60 digitalWrite ( slave_select , HIGH ); 61 SPI . endTransaction () ; // release the SPI bus 62 temp_value = 0 x00000000 ; 63 } 64 } C´odigo 4.8: Ejemplo - Bus SPI: C´odigo
    • 72. 4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 72
    • 73. CAP ´ ITULO 5 INTERRUPCIONES Hasta ahora toda la obtenci´on de informaci´on se ha realizado de manera unidireccional y siempre iniciando la petici´on desde el Arduino. En este capitulo veremos c´omo trabajar con las interrupciones en el microcontrolador Atmega328. Las interrupciones son se˜nales recibidas por el procesador que indican que se ha producido un evento en alguna de sus entradas o que un perif´erico quiere comunicarse con el procesador. Las peticiones de interrupci´on se pueden implementar de dos maneras diferentes. La primera es utilizando la t´ecnica de polling que consiste en mirar cada cierto tiempo, de manera regular, si un perif´erico o el procesador requiere de atenci´on. Como puedes imaginar este procedimiento es altamente ineficiente dado que el procesador pierde mucho tiempo de procesamiento en comprobaciones, por lo que hoy en d´ıa no se suele utilizar. El segundo m´etodo es a trav´es de pedidos de interrupci´on enviados por el perif´erico o componente que quiere ser atendido. Al recibir el pedido de interrupci´on el procesador deja de ejecutar su tarea actual y atiende a la interrupci´on mediante la ejecuci´on de la rutina de interrupci´on correspondiente al tipo de interrupci´on que se produjo.. La rutina de interrupci´on es la funci´on que se ejecuta cada vez que ocurre una interrupci´on en el procesador y es una pieza de c´odigo que depende del elemento que interrumpe. Por ejemplo cuando apretamos una tecla el controlador del teclado env´ıa un pedido de interrupci´on que hace que el contador de programa en logar de continuar lo que estaba ejecutando salta a una direcci´on de memoria donde est´a a su vez la direcci´on de la rutina de atenci´on al teclado. Existen diversas maneras de configurar la direcci´on de salto cuando ocurre una interrupci´on. A continuaci´on, se nombran las m´as importantes: Direcci´on fija: Las direcciones se encuentran ✭✭cableadas✮✮ al procesador por lo que no se puede modificar la posici´on de las interrupciones. Direcci´on variable: Las direcciones se pueden encontrar en cualquier lugar y pueden variar de un procesador a otro del mismo tipo (configuraci´on del procesador). El m´etodo que nos interesa a nosotros es el de las direcciones variables, dentro de este grupo, existen diversas implementaciones. Una de las m´as utilizadas y que adem´as 73
    • 74. 5.1. INTERRUPCIONES EN EL ATMEGA328 74 Figura 5.1: Ejemplo de indirecci´on ser´a la que utilizaremos, es el direccionamiento vectorizado o indirecto. Mediante esta t´ecnica una l´ınea de interrupci´on tiene asociada una posici´on en el vector de interrupciones donde se encuentra la direcci´on de la funci´on que finalmente se lanzar´a. En la Figura 5.1 se puede ver una ilustraci´on del procedimiento. En este cap´ıtulo veremos c´omo trabajar con las interrupciones del microcontrolador ATmega328 y realizaremos algunos ejemplos con el objetivo de asentar todos los conceptos te´oricos. 5.1. Interrupciones en el ATmega328 Cuando afrontamos la tarea de desarrollar una soluci´on basada en microcontroladores, lo primero que debemos realizar es un an´alisis de caracter´ısticas del mismo. Algunas de las caracter´ısticas son el n´umero de pines de entrada/salida, tipos de comunicaciones soportadas, tipos de timers, interrupciones, etc. . . En este caso vamos a analizar las interrupciones del microcontrolador Atmega328. Este microcontrolador soporta hasta 27 tipos de interrupciones distintas, cada una con su nivel de prioridad correspondiente (ver Tabla 5.1). La prioridad va de mayor a menor, es decir, la interrupci´on 1 (reset) tiene la m´axima prioridad y la n´umero 26 (spm ready) tiene la m´ınima prioridad. En la Tabla 5.1 podemos ver las interrupciones, internas y externas, soportadas por el ATmega328. En la tabla se observa claramente lo explicado anteriormente en el ejemplo del teclado, la interrupci´on asociada al puerto de interrupci´on INT0 mover´a el contador de programa a la direcci´on 0x0002 y ser´a en ese lugar en el cual pondremos el c´odigo para tratar dicha interrupci´on. 5.2. Manipulaci´on software A la hora de utilizar las interrupciones en el entorno Arduino, tenemos 3 posibilidades: Librer´ıa AVR: Utilizar la librer´ıa avr que implementa todas las interrupciones. Librer´ıa Arduino: Gestionar las interrupciones mediante las herramientas proporcionadas por Arduino, aunque muchas interrupciones no se han implementado aun en esta librer´ıa.
    • 75. 5.2. MANIPULACION SOFTWARE 75 ´ Cuadro 5.1: Tabla de interrupciones VectorNo. Program Address(2) Source Interrupt Definition 1 0x0000(1) 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 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 Manipulando directamente los registros correspondientes: Mediante las operaciones explicadas en Secci´on B.1 puedes manipular los registros de interrupciones y gestionar al nivel m´as bajo posible las interrupciones. Hay que tener en cuenta que esta manera es la menos portable, adem´as de ser la m´as complicada. 5.2.1. Librer´ıa avr Como ya se ha comentado a lo largo del libro, el entorno Arduino realmente es un conjunto de librer´ıas de abstracci´on que hacen las cosas m´as sencillas aunque puede ocurrir que si queremos realizar tareas espec´ıficas, es probable que en algunos casos esta librer´ıa no nos proporcione todas las comodidades que necesitemos, dado que ha sido pensado para prototipos sencillos principalmente. En esta secci´on, veremos c´omo utilizar la librer´ıa avr-libc para el manejo de las interrupciones. Esta librer´ıa es la que utiliza Arduino y por lo tanto no tendremos que instalar ning´un paquete adicional. Para empezar a utilizar las interrupciones lo primero que debemos hacer es incluir el archivo de cabecera que contiene las definiciones de las funciones que vamos a utilizar. Este archivo se llama ¡avr/interrupt.h¿, en el podemos encontrar diferentes macros y definiciones que nos simplificar´an la tarea de gestionar las interrupciones. El ATmega, al igual que la mayor´ıa de los microcontroladores, utiliza un bit de habilitaci´on global de interrupciones. Este bit es el bit 7 del registro de estado y control SREGdel ATmega. Adem´as cada perif´erico tiene su registro de interrupciones con su bit de habilitaci´on correspondiente. Luego para que una habilitaci´on sea atendida deber´a tener en 1 el bit de habilitaci´on global y a 1 el bit de habilitaci´on espec´ıfico. Para habilitar o deshabilitar el bit de habilitaci´on global utilizaremos las macros sei() o cli() respectivamente. Estas macros escriben un 1 o un 0 en el bit 7 de la direcci´on de memoria 0x3F, que corresponde al registro de estado y control SREG. Cuando una interrupci´on ocurre, se pone a 0 el bit de habilitaci´on global y se deshabilitan todas las interrupciones. El software de usuario puede poner a 1 otra
    • 76. 5.2. MANIPULACION SOFTWARE 76 ´ 1 # include < avr / interrupt .h > 2 ISR ( INT0 ) 3 { 4 // Pro ce sa mi en to 5 evento = 1; 6 } C´odigo 5.1: Ejemplo de definici´on de ISR 1 # include < avr / interrupt .h > 2 ISR ( BADISR_vect ) 3 { 4 error =1; 5 } C´odigo 5.2: Captura de interrupci´on no esperada vez el bit de habilitaci´on para poder atender interrupciones anidadas, de manera que cualquier interrupci´on habilitada pueda interrumpir la rutina de interrupci´on actual. Luego, cuando se retorna de la interrupci´on, el bit de habilitaci´on global se pone autom´aticamente a 1. Para habilitar las interrupciones correspondientes a cada perif´erico debemos poner a 1 el bit correspondiente en el registro de m´ascara de Interrupci´on. Si el bit de m´ascara esta a 1 y a su vez el bit de habilitaci´on global est´a a 1 entonces el procesador atender´a la interrupci´on cuando ocurra. Para el caso de interrupciones externas existen registros que permiten configurar que tipo de eventos se atender´an. Como se detallar´a mas adelante, estos eventos pueden ser un cambio de nivel en un pin, un flanco ascendente o descendente, etc. Si se desea profundizar mas acerca del manejo de registros de interrupciones se recomienda al lector la hoja de datos del fabricante. Ahora que sabemos como habilitar o deshabilitar las interrupciones, el siguiente paso para poder atender a la misma es definir la rutina de interrupci´on. Para definir la rutina debemos utilizar la macro ISR. Esta macro tiene como par´ametro una constante que define la interrupci´on que se desea atender. Normalmente el nombre de la constante es el mismo que el especificado en el datasheet del fabricante. Como ejemplo, si quisi´eramos definir una rutina de interrupci´on para INT0 el c´odigo ser´ıa similar al mostrado en Cod. 5.1 Otra pr´actica que es recomendable sobre todo en dise˜nos que hacen uso intensivo de las interrupciones, es capturar todas aquellas interrupciones para las cuales no se ha definido ninguna rutina de interrupci´on (normalmente esto indica un bug). Para capturar todas las interrupciones se debe pasar la constante BADISR vect a la macro ISR. En Cod. 5.2 se puede ver un ejemplo. Otro caso particular, y para el cual existe una soluci´on, es cuando se desea utilizar una misma rutina de interrupci´on para dos interrupciones diferentes. Para conseguir este comportamiento debemos utilizar la macro ISR ALIASOF junto a la macro ISR. En Cod. 5.3 se puede ver un ejemplo de uso, en el cual la rutina de interrupci´on de la interrupci´on PCINT1 pasa a ser igual a la de PCINT0.
    • 77. 5.2. MANIPULACION SOFTWARE 77 ´ 1 # include < avr / interrupt .h > 2 ISR ( PCINT0_vect ) 3 { 4 // Pro ce sa mi en to 5 evento = 1; 6 } 7 ISR ( PCINT1_vect , ISR_ALIASOF ( PCINT0_vect ) ); C´odigo 5.3: Captura de interrupci´on no esperada 5.2.2. Librer´ıa Arduino Arduino provee de un conjunto de funciones para el tratamiento de interrupciones en sus placas de desarrollo. La librer´ıa de Arduino solo da soporte a un n´umero limitado de interrupciones, a continuaci´on explicaremos cada una de las funciones de la librer´ıa: noInterrupts(): Esta funci´on al igual que cli nos permite deshabilitar las interrupciones. interrupts(): Habilita las interrupciones del mismo modo que sei. attachInterrupt(): Permite seleccionar una rutina de interrupci´on para una interrupci´on determinada. En funci´on de la placa de desarrollo el n´umero de pin variar´a, se recomienda ver el datasheet, as´ı como el esquem´atico para averiguar el pin correspondiente a la interrupci´on. Las interrupciones que est´an soportadas por esta funci´on son las siguientes: INT0, INT1, INT2, INT3, INT4, INT5. Adem´as, mediante un par´ametro podemos indicar en qu´e momento queremos que se active la rutina de interrupci´on pudiendo ser: • LOW: Cuando el pin est´a en estado ✭✭bajo✮✮. • CHANGE: Cuando se produce un cambio de estado en el pin (de alto a bajo o viceversa). • RISING: En el flanco positivo de un cambio de estado. • FALLING: En el flanco negativo. de un cambio de estado detachInterrupt(): Mediante esta funci´on podemos eliminar la rutina de interrupci´on que se indique como par´ametro. En general se recomienda usar la librer´ıa de AVR dado que aparte de ser m´as eficiente es mucho m´as flexible. 5.2.3. Consideraciones importantes A la hora de utilizar las interrupciones tenemos que tener algunas consideraciones en cuenta. Las interrupciones por defecto est´an habilitadas desde el inicio, por lo que no es necesario utilizar la macro sei en un primer instante, aunque si es recomendable. Algunas funciones como delay() o millis() utilizan las interrupciones para su funcionamiento. Es importante que se tenga en cuenta el uso que hacen las funciones de
    • 78. 5.3. EJEMPLO 1: PRIMERA RUTINA DE INTERRUPCION 78 ´ Cuadro 5.2: Ejemplo 1: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Entrada Bot´on button 2 Salida Led notification led 13 las interrupciones ya que por ejemplo, para usar una funci´on delay() dentro de una rutina de interrupci´on debemos habilitar otra vez el bit de habilitaci´on global, dado que por defecto al entrar en una rutina de interrupci´on se llama de forma impl´ıcita a la macro cli. 5.3. Ejemplo 1: Primera rutina de interrupci´on En este primer ejemplo veremos un ejemplo de utilizaci´on de las interrupciones en el procesador ATmega328 usando las librer´ıas avr. El ejemplo consistir´a en encender y apagar un led pulsando un bot´on. Seguramente al lector le resultar´a familiar pues en la Secci´on 3.2 se estudi´o, sin embargo la principal diferencia es que en este caso no leeremos el valor de un pin, sino que el propio evento ser´a el encargado de realizar la acci´on. A partir de esta secci´on no realizaremos los diagramas de flujo con el objetivo de no hacer tan extensos los ejemplos, pero se recomienda que el lector tenga presente siempre este paso en sus dise˜nos. 5.3.1. Tabla de entrada/salida Lo m´as importante de la Tabla 5.2 es la elecci´on del pin para el bot´on. Si observamos el datasheet del ATmega328 veremos que los pines que tienen asociadas a las interrupciones externas INT0 e INT1 son los pines PD1 y PD2 los cuales, en el Arduino, est´an conectados a los pines f´ısicos 2 y 3. El pin para el led puede ser cualquiera de los de salida digitales. 5.3.2. C´odigo El c´odigo para manejar las interrupciones en los microcontroladores ATmega es muy sencillo y se puede resumir en los siguientes pasos: Habilitar registro global de interrupciones: Mediante las macros sei y cli podemos habilitar y deshabilitar el bit global de interrupciones. Habilitar interrupci´on espec´ıfica: Cada interrupci´on tiene asociado un bit de m´ascara de activaci´on individual. Mediante este bit podemos permitir interrumpir al procesador mediante dicha interrupci´on. En nuestro caso y para nuestro procesador el registro se llama EIMSK y el bit que pondremos en 1 es el correspondiente a la INT0 (l´ınea 13). Configurar la interrupci´on: Algunas interrupciones tienen registros que permiten configurar cuando lanzar la misma. En nuestro caso, para la interrupci´on INT0 seleccionaremos el flanco descendente en el pin 2 poniendo un ’1’ en el bit ISC01.
    • 79. 5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 79 1 #i n cl u d e <avr / i n t e r r u p t . h> 2 3 c o n s t i n t n o t i f i c a t i o n l e d = 1 3; 4 c o n s t i n t button = 2 ; 5 6 7 v oid se tup ( v oid ) 8 { 9 pinMode ( button , INPUT) ; 10 pinMode ( n o t i f i c a t i o n l e d , OUTPUT) ; 11 d i g i t a lW r i t e ( button , HIGH) ; 12 s e i ( ) ; 13 EIMSK |= ( 1 << INT0 ) ; 14 EICRA |= ( 1 << ISC01 ) ; 15 } 16 17 v oid l o o p ( v oid ) 18 { 19 20 } 21 22 ISR ( INT0 vect ) 23 { 24 d i g i t a lW r i t e ( n o t i f i c a t i o n l e d , ! di gi t al R e a d ( n o t i f i c a t i o n l e d ) ) ; 25 } C´odigo 5.4: Ejemplo 1 - Interrupciones Definir la rutina de interrupci´on: Mediante la macro ISR podemos indicar las acciones que se deber´an tomar al atender una interrupci´on. En Cod. 5.4 se puede ver la instanciaci´on de los pasos anteriormente nombrados. Un procesamiento intensivo dentro de la rutina de interrupci´on no es una buena idea dado que durante el tiempo en que el procesador se encuentre en esta funci´on no realizar´a ninguna otra acci´on. En este ejemplo se ha modificado el estado del pin 13 dentro de la funci´on ´unicamente con el objetivo de simplificar el c´odigo, sin embargo se deja como ejercicio al lector modificar el ejercicio para que la modificaci´on no se haga dentro de dicha rutina de interrupci´on1. Como puedes observar, el bucle principal est´a vac´ıo, sin embargo si pulsas al bot´on ver´as como el led se enciende y apaga. Este comportamiento se llama asincron´ıa, dado que no hay ning´un lugar donde se llame a la funci´on que enciende y apaga el led, es el propio suceso el que llama a dicha funci´on. 5.4. Ejemplo 2: Midiendo distancias En este ejemplo vamos a utilizar las interrupciones con la ayuda de la librer´ıa de Arduino para medir las distancias a la que se encuentran los objetos haciendo uso tambi´en del sensor HC-SR04 que se puede observar en la Figura 5.2. El funcionamiento de este sensor es muy sencillo. El HC-SR04 posee 4 pines. A continuaci´on se explicar´a cada uno de ellos: 1Como pista se podr´ıa utilizar alg´un tipo de ✭✭bandera✮✮ o ✭✭flag✮✮
    • 80. 5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 80 Figura 5.2: Sensor emisor/receptor ultrasonidos Cuadro 5.3: Ejemplo 2: Tabla de entrada/salida Entrada/Salida Descripci´on Nombre variable Pin Entrada Pin de recepci´on del sensor HCSR04 echo pin 2 Salida Pin gatillo del sensor HC-SR04tigger pin 3 Salida Transmisi´on puerto serie Tx 1 VCC: Este sensor trabaja a 5V por lo que podr´a ser conectado directamente al Arduino a trav´es del pin de 5V del mismo. GND: Com´un. Trigger: El trigger o gatillo nos permite indicar al sensor de que queremos hacer una medici´on. Para que el sensor sepa que queremos realizar la medici´on y que no es un falso positivo debemos activar este pin como m´ınimo durante 10 microsegundos. Echo: Este pin n0s indicar´a la distancia a la que se encuentra el objeto. ¿C´omo? muy sencillo. Una vez que se recibe la se˜nal del trigger el sensor manda una se˜nal de 40Khz que cuando rebota contra un objeto y vuelve al sensor de nuevo hace que el sensor emita un pulso positivo con una longitud proporcional a la distancia del objeto por el pin echo. 5.4.1. Tabla de entrada/salida La Tabla 5.3 es bastante clara por lo que no se explicar´a nada m´as al respecto.
    • 81. 5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 81 5.4.2. C´odigo El c´odigo se puede ver en Cod. 5.5 a continuaci´on veremos las partes m´as importantes del mismo. Cuando el Arduino entra en la fase de setup configuramos la comunicaci´on serie para poder mostrar la distancia por pantalla y adem´as configuramos los pines de entrada/salida. En este caso tal y como nombramos en la Tabla 5.3 tenemos un pin de entrada y dos de salida. Los de salida corresponden uno al trigger del sensor y el otro al pin de transmisi´on Tx del puerto serie. El pin de entrada est´a conectado a la se˜nal echo del sensor. Es importante en este punto recordar que tenemos l´ıneas limitadas de interrupci´on en nuestra placa y que estas est´an conectadas a unos pines determinados. En este caso, la l´ınea de interrupci´on que hemos utilizado es la conectada al pin 2 del Arduino uno, que corresponde a la interrupci´on INT0 (ver en el datasheet). El siguiente paso y seguramente el m´as importante en este ejemplo es la habilitaci´on de la interrupci´on del pin 2. Para ello utilizamos la funci´on attachInterrupt de la librer´ıa de Arduino. Esta funci´on tiene tres par´ametros: Nº de interrupci´on, rutina de interrupci´on que se ejecutar´a, y el tipo de evento que disparar´a la interrupci´on (cambio de estado, flanco, etc). Como puedes observar esto es lo mismo que hemos hecho en las l´ıneas 13 y 14 del ejemplo 1, solo que en lugar de escribir en los registros utilizando las librer´ıas avr, lo que hacemos es llamar a esta funci´on que se encarga de eso. Por lo tanto no incluimos el archivo de cabecera avr/interrupt en este ejemplo. En este ejemplo el primer par´ametro es un 0. Este par´ametro no se refiere al pin al que est´a conectado el sensor, sino a la interrupci´on del microcontrolador. En este caso el pin 2 est´a conectado como ya hemos dicho a la interrupci´on 0 por lo que este ser´a el n´umero que tendremos que utilizar. En las ´ultimas versiones del entorno Arduino se ha a˜nadido una macro llamada digitalPinToInterrupt(pin) que permite despreocuparnos de este par´ametro. Comprueba si tu entorno ha a˜nadido este macro y si es as´ı puedes utilizarla. El otro par´ametro importante es la funci´on que ser´a llamada cuando recibamos la interrupci´on (rutina de Interrupci´on). La funci´on no deber´a retornar ning´un tipo (void). Por ´ultimo el par´ametro CHANGE indica que nos avise en cualquier cambio en el pin, esto es, alto a bajo o bajo a alto. (en Subsecci´on 5.2.2 puedes ver las opciones que se pueden pasar a la funci´on). Como ya comentamos en Secci´on 5.4 para saber la distancia a la que se encuentra un objeto debemos medir el tiempo entre el flanco de subida del pin echo y el flanco de bajada del mismo. Recordemos que la distancia es proporcional a dicho tiempo. Como la funci´on interrupt echo() se llama cada vez que hay una variaci´on en el pin echo lo ´unico que tenemos que registrar es cuando empieza (flanco de subida) y cuando termina (flanco de bajada). Esta comprobaci´on se hace mediante la funci´on digitalRead() que permite saber en que estado estamos y en funci´on del mismo registra el tiempo en las variables correspondientes. El tiempo se registra con la funci´on micros(). Se podr´ıa usar la funci´on millis() para medir tiempos en milisegundos pero esto no es posible dentro de una rutina de interrupci´on. En el datasheet del sensor se indica que el tiempo se debe dividir entre 58 para obtener la distancia en cent´ımetros. Otro punto a resaltar es la activaci´on del pin trigger que debe estar en estado alto durante al menos 10 microsegundos. Aunque esto se podr´ıa realizar mediante un timer e interrupciones, en este caso hemos optado por un delay en microsegundos. El retardo final de 100 milisegundos (linea 21) ´unicamente tiene la funci´on de mejorar la visualizaci´on de la distancia.
    • 82. 5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 82 1 c o n s t i n t e c h o pi n = 2 ; 2 c o n s t i n t t r i g g e r p i n = 3 ; 3 4 l o n g ech o end = 0 ; 5 l o n g e c h o s t a r t = 0 ; 6 l o n g e c h o d u r a ti o n = 0 ; 7 8 9 v oid se tup ( ) { 10 S e r i a l . b e gi n ( 9 6 0 0 ) ; 11 pinMode ( ec h o pin , INPUT) ; 12 pinMode ( t r i g g e r p i n , OUTPUT) ; 13 a t t a c h I n t e r r u p t ( d i gi t al P i n T o I n t e r r u p t ( e c h o pi n ) , i n t e r r u p t e c h o , CHANGE) ; 14 } 15 16 v oid l o o p ( ) { 17 d i g i t a lW r i t e ( t r i g g e r p i n ,HIGH) ; 18 d el a yMi c r o s e c o n d s ( 5 0 ) ; 19 d i g i t a lW r i t e ( t r i g g e r p i n ,LOW) ; 20 S e r i a l . p r i n t l n ( e c h o d u r a ti o n / 5 8 ) ; 21 d el a y ( 1 0 0 ) ; 22 } 23 24 v oid i n t e r r u p t e c h o ( ) { 25 i f ( di gi t al R e a d ( e c h o pi n ) == HIGH) { 26 ech o end = 0 ; 27 e c h o s t a r t = mic r o s ( ) ; 28 } e l s e { 29 ech o end = mic r o s ( ) ; 30 e c h o d u r a ti o n = ech o end − e c h o s t a r t ; 31 } 32 } C´odigo 5.5: Ejemplo 2 - Midiendo distancias
    • 83. CAP ´ ITULO 6 MULTITASKING Y TIMERS Hasta ahora cada vez que buscamos repetir una acci´on en un tiempo determinado hemos utilizado la funci´on delay() que nos provoca un retardo en la ejecuci´on durante el tiempo especificado en la misma, en milisegundos. Como se explicar´a en este cap´ıtulo la utilizaci´on de dicha funci´on no debe realizarse en todos los casos, es m´as, por regla general deber´ıa evitarse su uso. Cuando se utiliza la funci´on delay() el Arduino se queda en un bucle activo. Por lo tanto, durante ese tiempo el microcontrolador no realizar´a ninguna otra funci´on, esto es, estar´a consumiendo energ´ıa pero no estar´a haciendo ning´un trabajo ´util como por ejemplo podr´ıa ser registrar diferentes valores de los sensores. En este cap´ıtulo veremos de manera resumida como utilizar otras funciones como por ejemplo millis() para evitar los problemas que trae consigo la funci´on delay(). 6.1. Timers Los microcontroladores como ya sabr´as est´an compuestos de un conjunto de perif´ericos como por ejemplo los TIMER, USARTS, WDG, etc que nos permiten expandir su funcionalidad. En este caso hablaremos de un perif´erico muy importante y que sin duda alguna es la base para todas las funciones relacionadas con el tiempo. Este perif´erico es el timer. Un timer es un circuito que nos permite contar eventos, por ejemplo ciclos de reloj, eventos externos, etc, para generar formas de ondas, circuitos de reloj, comparadores, etc. Dependiendo de en que microcontrolador est´e basado nuestro Arduino tendremos m´as o menos timers, en el caso del microcontrolador ATmega328 podemos encontrar tres timers: dos de 8 bits y uno m´as de 16 bits. En la Figura 6.1 puedes encontrar un diagrama en bloques del timer de 8 bits del ATmega328. A continuaci´on pasaremos a explicar cada uno de los componentes que forman este m´odulo de tal modo que podamos tener una visi´on amplia a la hora de utilizar cualquier funci´on relacionada con el tiempo en nuestros dise˜nos. B´asicamente un timer esta formado por un circuito de control (Control Logic) que se encarga de contar de manera ascendente o descendente los eventos que se producen 83
    • 84. 6.1. TIMERS 84 Figura 6.1: Diagrama TIMER 8 bits ATmega328 a su entrada, guardando la cuenta en un registro espec´ıfico (TCNTn). Este valor de cuenta se puede utilizar para generar diferentes funcionalidades tales como comparar con otro valor contenido en otro registro, generar una forma de onda o generar una interrupci´on cuando se llega a un determinado valor, por ejemplo. El circuito selector de reloj (Clock Select) determina cual es la entrada que se lee, reloj o evento externo y en que flanco del evento se produce la captura, y si se va a tener en cuenta o no un factor de escala. El factor de escala es un circuito denominado prescaler que genera un evento cada cierta cantidad de ciclos de reloj. Esta generaci´on de factor de escala es configurable desde el registro Clock prescaler del ATMega328, ubicado en la direcci´on de memoria 0x61. Empezaremos explicando los registros para continuar con los modos de funcionamiento del timer. 6.1.1. Registros TCNTn: Este registro nos permite saber en todo momento el valor del timer correspondiente (la letra n indica el n´umero de timer que puede ser 0 o 1). Este valor se incrementa o decrementa, seg´un sea el modo habilitado, cada vez que se produzca un evento de reloj, o de evento externo, a la entrada del circuito de control. El registro TCNT0 es un registro de 8 bits por lo que el valor m´aximo que se podr´a alcanzar es 255 o 0xFF en hexadecimal. OCR0a: El valor de este registro se compara cada ciclo del timer con el valor del registro TCNTn. Cuando se alcanza el valor configurado en este registro se activar´a el bit OCF0A. El resultado de la comparaci´on se puede utilizar para generar distintas formas de onda o PWM como se ver´a m´as adelante. OCR0b: El mismo comportamiento que OCR0a.
    • 85. 6.1. TIMERS 85 TCCR0A, TCCR0B: Estos dos registros de control nos permiten configurar el timer de tal modo que se comporte como un comparador, como un generador de funciones, etc. TIMSK0: Mediante este registro podremos habilitar o deshabilitar las diferentes interrupciones generadas por el TIMER (ver datasheet). TIFR0: Parecido a TIMSK0 la diferencia radica en que este ´ultimo ´unicamente nos indica que se ha generado una interrupci´on, como por ejemplo cuando se llega al desbordamiento que se activa el bit TOV0. 6.1.2. Modos de funcionamiento A continuaci´on veremos los distintos modos de funcionamiento que posee el timer. Estos modos se ver´an de manera general, lo suficiente como para abordar los ejemplos de este cap´ıtulo, y se deja como tarea para el lector una lectura mas detallada de los mismos. Modo normal: Es el modo m´as simple de utilizaci´on. Para activar este modo se deben poner a 0 los bits WGM0:2 ubicados en los registros TCCR0A y TCCR0B (cuidado, los dos primeros bits se encuentran en el registro TCCR0A, sin embargo el ´ultimo bit WGM02 se encuentra en el registro TCCR0B). En este modo, el registro TCNT0 se incrementa en cada ciclo de reloj hasta llegar al valor 0xFF donde se desborda y vuelve al valor 0x00, activando a la vez la se˜nal de overflow (TOV0). Esta se˜nal no volver´a a 0 de forma autom´atica, ser´a el programador el encargado de realizar dicho cambio. Si est´a habilitado el bit de interrupci´on por desbordamiento, cuando se produzca el mismo se generar´a una interrupci´on. Modo CTC o Clear Timer on Compare Match: Para activar este modo debemos configurar los bits WGM02:0 al valor 2, esto es, ✭✭010✮✮. En este modo el registro OCR0A se utiliza para variar la resoluci´on del timer. La diferencia con el modo normal es que en este modo cuando se alcanza el valor configurado en el registro OCR0a, el registro TCNT0 se pone de nuevo al valor 0. En la Figura 6.2 se puede ver un esquema que ejemplifica su uso. TCNT0 empieza en el valor 0 y va ascendiendo hasta llegar al valor configurado en OCR0a (raya horizontal que indica el valor de comparaci´on). Una vez que se alcanza dicho valor la bandera OC0a del registro TIFR0 se activa de tal modo que si el programador comprueba dicho bit en su programa sabr´a si se ha alcanzado el valor de comparaci´on. La ejecuci´on repetida del timer en modo CTC nos genera una forma de onda en el pin de salida OCn. Esta forma de onda se puede ver en un osciloscopio si conectamos una de las puntas del mismo al pin asociado a la se˜nal OC0 en el caso de que el timer est´e configurado para permutar dicho pin en cada acierto de comparaci´on (esto se configura en el registro TCCR0A, TCCR0B). En el diagrama de la Figura 6.2 se muestran diferentes valores del registro de comparaci´on OCR0a, variando el periodo de la se˜nal. Seg´un el datasheet para generar una se˜nal cuadrada con una frecuencia determinada podemos utilizar la formula mostrada en la Ecuaci´on 6.1. M´as adelante, en los ejemplos, veremos c´omo utilizar esta formula y c´omo seleccionar los valores para cada uno de los par´ametros.
    • 86. 6.1. TIMERS 86 Figura 6.2: Modo CTC Figura 6.3: Modo Fast PWM Modo Fast PWM: El modo Fast PWM o desde ahora PWM (Pulse Width Modulation) (con el objetivo de simplificar), se habilita mediante la activaci´on de los bit WGM02:0 = 3 o 7. Con este modo se nos proporciona un manera de generar formas de onda PWM de alta frecuencia y alta precisi´on. El timer cuenta desde el valor indicado en BOTTOM hasta el valor indicado en TOP, cuando se alcanza el valor TOP se vuelve a iniciar la cuenta desde BOTTOM. El valor de TOP es 0xFF cuando WGM02:0 es 3, sino toma el valor del registro OCR0A cuando es 7. Posee dos modos, salida de comparaci´on no-invertida o invertida. En el modo no-invertida la salida se pone a 0 cuando se alcanza el valor de comparaci´on y a 1 cuando se llega al valor BOTTOM. En los ejemplos veremos c´omo se materializa este modo pero para que el lector se haga una idea, este modo nos permite ✭✭indicar✮✮ al microcontrolador qu´e porcentaje de tiempo queremos que el pin est´e en alto y cuanto tiempo queremos que est´e en bajo, pudiendo emular valores anal´ogicos variando el ancho del semiciclo positivo de la se˜nal generada. Si por ejemplo alimentamos un motor con una frecuencia de trabajo ✭✭X✮✮, podremos variar su velocidad modificando el porcentaje de tiempo que se entrega energ´ıa al motor. En la Figura 6.3 se puede ver un ejemplo de uso del modo PWM, a continuaci´on pasaremos a explicar el mismo aunque en los ejemplos se ver´a de manera m´as detallada.
    • 87. 6.1. TIMERS 87 Figura 6.4: Registro TCCR1A El contador en este caso empieza en 0 y el valor de OCRn no ha sido seleccionado de modo que hasta que el mismo no se selecciona (tercera barra horizontal) no se modifica el estado del pin OCnX. Una vez que se selecciona un valor para OCRnx (OCR0a,b), cuando el contador alcanza el valor del registro OCRnx se pone a estado bajo el pin OCnx en el caso de la se˜nal normal y se pone en alto en el caso de la se˜nal invertida. El contador continua su incremento hasta que llega al valor top. Ser´a cuando alcance este valor cuando el pin vuelva a cambiar su estado. Como puedes observar, en funci´on del valor del registro OCRnx podremos mantener m´as tiempo la se˜nal en alto, o lo que es lo mismo para nuestro ejemplo podremos entregar mas energ´ıa al motor. La ecuaci´on para el c´alculo de la frecuencia se puede consultar en Ecuaci´on 6.2. Modo Fast PWM con correcci´on de fase: Este modo es muy parecido al Fast PWM con la ´unica diferencia que la salida est´a en fase y adem´as la frecuencia m´axima que se puede alcanzar es la mitad con respecto al modo PWM ya que ahora el contador trabaja en modo ascendente y descendente. La f´ormula para calcular el valor de TOP y del prescaler es la mostrada en Ecuaci´on 6.3. f0Cnx = f clk 2 ∗ N ∗ (1 + OCRnX) (6.1) f0Cnx = f clk N ∗ (1 + T OP) (6.2) f0CnxP CPWM = f clk 2 ∗ N ∗ T OP (6.3) 6.1.3. Ejemplos de uso Una vez que tenemos claro los modos de uso de los timer y cada uno de los registros que est´an implicados en su funcionamiento es el momento de programar el ATmega para poner en pr´actica lo aprendido. Como el Arduino utiliza el timer 0 para diferentes funciones como por ejemplo la funci´on delay() dejaremos de lado este timer y utilizaremos en su lugar el timer 1. La diferencia entre el timer 0 y el timer 1 es ´unicamente la cantidad de bits de ancho as´ı como el n´umero de funciones que poseen cada uno. Tambi´en se podr´ıa haber utilizad el timer 2 el cual es de 8 bits.
    • 88. 6.1. TIMERS 88 Ejemplo de uso b´asico En este primer ejemplo veremos c´omo podemos configurar el timer ´unicamente para que nos ✭✭avise✮✮ cuando haya pasado un determinado tiempo que indicaremos. Lo primero que debemos tener claro a la hora de configurar un timer en este modo es, como parece l´ogico, el tiempo en el cual queremos que nos avise. En este caso, vamos a imaginar que queremos que nos avise cada 4 segundos de tal manera que cada vez que se alcance ese instante de tiempo imprimiremos por el puerto serie un mensaje. Una vez que tenemos claro el tiempo en el que queremos que nos avise el TIMER, el siguiente paso es configurar cada uno de los registros implicados. A continuaci´on, explicaremos c´omo configurar cada registro. Es importante tener en cuenta que todas esta informaci´on est´a extra´ıda del datasheet del microcontrolador. Es imposible configurar bien un microcontrolador sin leerse el datasheet varias veces. El registro TCCR1A posee diferentes bits que nos permiten modificar el comportamiento de la unidad de comparaci´on y dos de los bits que configuran el comportamiento del propio timer (WGM11 y WGM10 ). La raz´on por la cual solo se pusieron dos bits de los cuatro necesarios para configurar el timer en este registro es una decisi´on de dise˜no pero los bits WGM12 y WGM13 se encuentran en el registro TCCR1B. En este caso buscamos configurar el timer en modo normal y deshabilitar las unidades de comparaci´on pero todav´ıa nos falta un dato m´as, el prescaler. Para calcular el valor del prescaler en primer lugar calculamos el tiempo que tarda nuestro ATmega en incrementar el valor del registro TCNT1. Si estamos en un sistema con reloj de 16Mhz, el tiempo por cada ciclo ser´a igual a 1/16000000 o lo que es lo mismo: 62.5 ns. Teniendo en cuenta esto, el tiempo que tardar´a el timer en desbordarse ser´a igual a: 216 ∗ 62,5 −9 = 4ms. Como puedes observar este valor es muy peque˜no y no nos sirve para alcanzar esos 4 segundos que buscamos. La soluci´on podr´ıa ser aumentar el n´umero de bits del timer o por otro lado aumentar el tiempo que tarda en incrementar el registro TCNT. Como la primera opci´on (aumentar el n´umero de bits) no depende de nosotros y solo podr´ıamos tenerla en cuenta si tuvi´eramos un timer con m´as bits (no lo tenemos), abordaremos la segunda soluci´on, es decir, aumentar el tiempo que tarda el timer en incrementar el registro TCNT. Si utilizamos un prescaler de 1024 entonces significar´a que por cada 1024 ciclos de reloj incrementaremos en uno el valor del registro TCNT1 por lo que volviendo a los c´alculos anteriores el tiempo que tardar´ıa en desbordarse el timer ser´ıa igual a: 1024∗2 16 ∗62,5−9 = 4,194s. ¡Lo conseguimos! ya tenemos el timer configurado para que tarde 4 segundos. . . En realidad esto no es del todo cierto dado que seg´un nuestros c´alculos el tiempo es de 4.194 s por lo que intentaremos ajustar m´as el valor, recuerda, 1 d´ecima de segundo puede significar ganar o perder una carrera en formula uno pero para una primera aproximaci´on no est´a nada mal. Si queremos un tiempo exactamente igual a 4 segundos ´unicamente tenemos que realizar una ✭✭regla de tres✮✮ sabiendo que el tiempo que tarda en incrementar el registros TCNT es de 1024 ∗ 1 16000000 obteniendo el resultado de que el n´umero de incrementos que se realizar´an en 4 segundos ser´a igual a 62500. Con todos estos c´alculos ya sabemos que el valor del prescaler es de 1024 y que nos gustar´ıa que en vez de a los 62536 incrementos el TIMER se desbordar´a a los 62500. Para esto ´ultimo ´unicamente tenemos que modificar el valor de TOP o modificar el valor en el que empieza el registro TCNT. En el c´odigo mostrado en Cod. 6.1 puedes ver como se instancian los pasos anteriormente mencionados. A continuaci´on explicaremos las partes m´as importantes del
    • 89. 6.1. TIMERS 89 c´odigo. En el setup() se llama a la macro cli(), como ya se explic´o en la Cap´ıtulo 5 esta macro ´unicamente deshabilita las interrupciones de manera global, de manera que podamos realizar toda la configuraci´on sin miedo a ser interrumpidos. Una vez deshabilitadas las interrupciones, el siguiente paso es configurar tanto el registro TCCR1A como el registro TCCR1B. El valor 0 asignado a TCCR1A tiene que ver con lo explicado anteriormente. En la Figura 6.4 puedes la definici´on del registro. El registro TCCR1B sin embargo s´ı que tiene un valor ✭✭´util✮✮ asignado. En un primer momento el lector podr´ıa preguntarse por qu´e se asigna dicho valor. La raz´on se encuentra en la configuraci´on del prescaler. Si echamos un vistazo a la Figura 6.6 y a la Figura 6.5 podemos ver que para aplicar un prescaler de 1024 tenemos que asignar el valor ✭✭101✮✮ a los bits ✭✭CS1:2-0✮✮. Como todos los dem´as bits deben est´ar a cero (ver explicaci´on anterior y datasheet) el valor ✭✭00-00101✮✮ en decimal es 5. El siguiente paso consiste en configurar el registro TIMSK1. En este caso hemos optado por otro modo de asignaci´on el cual se explica en Secci´on B.1. El bit que hay que habilitar es el TOIE1 que nos permitir´a indicar al microcontrolador que queremos interrumpir nuestro programa cuando se produzca un overflow. Por ´ultimo, en la fase de setup() tenemos que poner el valor inicial de 536 al registro contador. Es muy importante que siempre que modifiquemos un registro de 16 bits en la arquitectura AVR, en primer lugar modifiquemos el registro que representa la parte alta (H) y despu´es el que representa la parte baja (L). En este caso el valor en binario de 536 es ✭✭00000010 00011000✮✮ por lo que la parte alta la escribimos en el registro TCNT1H y la parte baja en el registro TCNT1L. Como puedes observar en este caso hemos utilizado la notaci´on binaria con el objetivo de que se cubran todas las posibilidades, aunque la m´as portable es la notaci´on utilizada en el registro TIMSK1. Lo que resta del c´odigo ya ha sido explicado en cap´ıtulos anteriores y por lo tanto no se volver´a a explicar. Ejemplo de uso modo CTC En este ejemplo cubriremos el modo de uso CTC. Este modo es el m´as utilizado cuando queremos medir tiempos dado que es m´as sencillo que el modo normal y permite actuar sobre un pin de manera inmediata sin necesidad de ninguna instrucci´on software, adem´as de contar con una precisi´on mayor al poder modificar el valor TOP. Se configurar´a el timer 1 de modo que cada medio segundo se encienda un led y cada segundo se encienda otro. En este caso podremos ver c´omo sin necesidad de ning´un c´odigo extra podemos realizar acciones cada X segundos y con una precisi´on bastante buena. Si sustituyes el led por un transistor que sustituya el bot´on de la c´amara de fotos tendr´as un temporizador de obturaci´on. (S´olo realizar esta modificaci´on si se tienen los conocimientos suficientes sobre electr´onica) El modo de proceder es muy parecido al de Subsecci´on 6.1.3 con la diferencia que en este caso tendremos que configurar el pin de salida y adem´as modificaremos el valor de ✭✭overflow✮✮ del timer (aunque como ya veremos, realmente no es el valor de ✭✭overflow✮✮). Los c´alculos para el tiempo en este caso se dejar´an como tarea para el lector de modo que pueda comprobar si ha comprendido los conceptos explicados en la Subsecci´on 6.1.3 En Figura 6.7 podemos ver los diferentes modos que posee el timer para actuar
    • 90. 6.1. TIMERS 90 1 v o l a t i l e l o n g l a s t u p d a t e = 0 ; 2 v o l a t i l e l o n g l a s t c om p a r e u p d a t e = 0 ; 3 i n t l e d = 1 3; 4 v oid se tup ( ) { 5 c l i ( ) ; 6 TCCR1A = 0 ; 7 TCCR1B = 5 ; //0b00−−0101 8 TIMSK1 |= ( 1 << TOIE1 ) ; 9 TCNT1H = 0 b00000010 ; 10 TCNT1L = 0 b00011000 ; 11 S e r i a l . b e gi n ( 9 6 0 0 ) ; 12 s e i ( ) ; 13 14 } 15 16 17 v oid l o o p ( ) { 18 i f ( l a s t u p d a t e != l a s t c om p a r e u p d a t e ) { 19 S e r i a l . p r i n t l n ( l a s t u p d a t e ) ; 20 l a s t c om p a r e u p d a t e = l a s t u p d a t e ; 21 } 22 } 23 24 ISR ( TIMER1 OVF vect ) { 25 l a s t u p d a t e = mic r o s ( ) ; 26 TCNT1H = 0 b00000010 ; 27 TCNT1L = 0 b00011000 ; 28 } C´odigo 6.1: Ejemplo de uso TIMER Figura 6.5: Bits de configuraci´on del presclarer Figura 6.6: Registro TCCR1B
    • 91. 6.1. TIMERS 91 Figura 6.7: Bits para configuraci´on del pin de salida sobre la salida. En nuestro caso elegiremos el modo ✭✭01✮✮ que nos permite permutar la salida en cada comparaci´on con los registros OCR1A/B. El siguiente paso consiste en configurar el timer para que actu´e en modo CTC. En la Figura 6.8 se puede ver la tabla de configuraci´on en funci´on de los bits WGM. A continuaci´on, explicaremos los referentes al modo CTC. Si configuramos los bits WGM en el modo ✭✭0100✮✮ el timer empezar´a a contar hasta llegar al valor configurado en el registro OCRxA y OCRxB, en este momento se generar´a una interrupci´on siempre y cuando el bit OCIEXa o OCIEXb del registro TIMSKX est´e habilitado, una vez que ocurra ese evento, el contador seguir´a ascendiendo hasta llegar al valor TOP donde se generar´a la interrupci´on de overflow y finalmente se volver´a a contar desde 0. El lector puede observar que lo que estamos realizando en este ejercicio es generar dos frecuencias a partir de un ´unico timer, esto no siempre ser´a posible, en este caso al ser una frecuencia m´ultiplo de la otra no habr´a ning´un problema. Para ejemplificar todo lo explicado pasaremos a analizar el c´odigo Cod. 6.2. En primer lugar, se bloquean las interrupciones con la macro cli() esto mismo se podr´ıa haber hecho con la funci´on noInterrupts(), sin embargo, es m´as eficiente y portable la segunda opci´on. Seguidamente se ponen a cero los dos registros de configuraci´on del timer de modo que tengamos certeza de que partimos de inicialmente de 0. Bas´andonos en el datasheet del ATmega328, configuramos el registro TCCR1A. C´omo el lector puede observar el ´unico bit que tiene que estar habilitado es el 0 de los comparadores, es decir, el COM1B0 y el COM1A0. Observa como en este caso se ha utilizado la notaci´on que se considera m´as portable (l´ınea 5). Con el registro TCCR1B hacemos lo mismo, solo que en este caso los bits a activar son los del prescaler y los del modo de funcionamiento del timer. Para ✭✭permitir✮✮ al timer enviar interrupciones al procesador, debemos activar el bit OCIE1A. Con esto conseguiremos generar dos frecuencias distintas. La frecuencia de dos hercios corresponde al comparador ✭✭A✮✮ mientras que la frecuencia de un hercio pertenece al registro ✭✭B✮✮. Por otro lado, el registro ICR1 es el encargado de situar el valor de TOP. Es importante observar que tanto el valor de ICR1 como el de OCR1B es el mismo ¿por qu´e?. Una vez configurados todos los registros se procede a poner los pines de entrada/salida en modo salida. Hay que tener en cuenta que hemos a˜nadido un pin m´as, esto nos permitir´a generar una se˜nal de referencia para saber que nuestros c´alculos son correctos. La elecci´on de estos pines se debe al mapeo que realiza el Arduino de los pines del ATmega328. Si quieres saber m´as sobre esto se recomienda ver el esquem´atico del Arduino UNO as´ı como el datasheet del ATmega328. En la rutina de interrupci´on TIMER1 COMPA vect() se comprueba el valor del
    • 92. 6.1. TIMERS 92 Figura 6.8: Configuraci´on modo CTC registro y en funci´on de si es menor que el registro ✭✭B✮✮ se modifica el valor de la siguiente comparaci´on. Se deja al lector el razonar el porque de estas comprobaciones. En el loop se genera una se˜nal de un hercio con el objetivo de compararla con la generada por nosotros mediante el timer. Ejemplo de uso modo Fast PWM El ´ultimo modo que veremos en profundidad ser´a el modo Fast PWM. Como ya se coment´o anteriormente, con este modo podemos modificar la cantidad de tiempo que pasa una se˜nal en alto y en bajo, es decir, podemos modificar el ciclo de trabajo. Existen diversas aplicaciones en las que se utilizan este tipo de se˜nales, como ejemplos podemos nombrar los servos de los aviones radio control o los variadores de los motores industriales, etc. En este ejemplo veremos c´omo utilizar el modo Fast PWM para mover un servo como el de la Figura 6.10. Este servo est´a incluido en el kit ✭✭Grove-Starter Kit for Arduino✮✮. Para mover un servo el primer paso que tenemos que seguir es el de estudiar los fundamentos de los servos. Un servo es un dispositivo electromec´anico, que mediante una l´ogica empotrada consigue decodificar una se˜nal de entrada para generar como resultado un desplazamiento a una posici´on determinada. Estos dispositivos se suelen utilizar en aeromodelismo pero su campo de aplicaci´on es mucho m´as extenso, otro ejemplo podr´ıan ser los robots. A continuaci´on pasaremos a explicar cada una de las partes que forman un servomotor: Control de posici´on: El control de posici´on b´asicamente es un peque˜no controlador que decodifica la se˜nal de entrada, la compara con el feedback y decide si mover o no el motor de corriente continua. (Ver Figura 6.9) Motor de corriente continua: Es la parte fundamental en el apartado mec´anico y ´unicamente consiste en un motor conectado a un ✭✭puente en H✮✮ y una reductora que permite transformar la mayor parte del giro en torsi´on.
    • 93. 6.1. TIMERS 93 1 v oid se tup ( ) { 2 c l i ( ) ; 3 TCCR1A = 0 ; 4 TCCR1B = 0 ; 5 TCCR1A = (1<<COM1B0) | (1<<COM1A0) ; 6 TCCR1B = (1<<WGM12) | (1<<WGM13) | (1<<CS12 ) ; 7 8 TIMSK1 = (1<<OCIE1A) ; 9 10 ICR1H = 0 b11110100 ; 11 ICR1L = 0 b00100011 ; 12 13 OCR1AH = 0 b01111010 ; 14 OCR1AL = 0 b00010001 ; 15 16 OCR1BH = 0 b11110100 ; 17 OCR1BL = 0 b00100011 ; 18 19 pinMode ( 9 ,OUTPUT) ; 20 pinMode ( 1 0 ,OUTPUT) ; 21 pinMode ( 7 ,OUTPUT) ; 22 23 S e r i a l . b e gi n ( 9 6 0 0 ) ; 24 25 s e i ( ) ; 26 27 } 28 29 ISR (TIMER1 COMPA vect ) { 30 // S e r i a l . p r i n t l n (” Entro ”) ; 31 i f (OCR1A < OCR1B) { 32 OCR1A = OCR1B; 33 } e l s e { 34 OCR1AH = 0 b01111010 ; 35 OCR1AL = 0 b00010001 ; 36 } 37 38 } 39 v oid l o o p ( ) { 40 d i g i t a lW r i t e ( 7 ,HIGH) ; 41 d el a y ( 5 0 0 ) ; 42 d i g i t a lW r i t e ( 7 ,LOW) ; 43 d el a y ( 5 0 0 ) ; 44 } C´odigo 6.2: Ejemplo de uso TIMER modo CTC
    • 94. 6.1. TIMERS 94 Figura 6.9: Diagrama de bloques del sistema de control Figura 6.10: Servo de Grove-Starter Kit for Arduino Engranajes o gearbox: El sistema de engranajes es la parte donde se diferencia un servo de otro. En funci´on de la calidad de los mismos el movimiento ser´a m´as o menos suave. Normalmente el potenci´ometro de feedback va unido al sistema de engranajes. Para mover un servo a una posici´on tenemos que enviar una se˜nal con unos par´ametros concretos. Normalmente los servomotores de aeromodelismo utilizan una frecuencia de 20 Hz por lo que la se˜nal viajar´a con un periodo de 50 ms. En la Figura 6.11 puedes ver el principio de funcionamiento. Normalmente se confunden los t´erminos PWM y PPM, a continuaci´on intentaremos clarificar ambos conceptos. Desde el ATmega328 generaremos una se˜nal a una frecuencia determinada, esa se˜nal la podemos modificar de modo que el ciclo de trabajo vaya desde el 0 % hasta 100 %. El servomotor espera un pulso de una duraci´on concreta, por ejemplo, siguiendo los tiempos de la Figura 6.11, el servo espera una se˜nal de 1ms para colocarse en la posici´on 0. Si nuestro periodo es de 50 ms, un pulso de 1ms ser´a equivalente a un 2 % de la se˜nal en alto. Si modificamos el valor de la frecuencia por ejemplo a 200 hz, el periodo ser´a 5 ms y un 2 % de dicha se˜nal ser´a 0.1 ms. Con esto llegamos a la conclusi´on de que para codificar un movimiento el servomotor espera pulsos de un tiempo determinado y no pulsos de un determinado voltaje (PWM). En resumidas cuentas, para mover nuestro servo lo primero que tendremos que hacer ser´a configurar un timer en nuestro caso el timer 1 en modo Fast PWM, luego calcular el valor del registro TOP para generar una frecuencia de 20 Hz (se deja como tarea para el lector el c´alculo de dicho valor) y por ´ultimo a˜nadir la l´ogica de control para calcular los ciclos de trabajo en funci´on de los grados que se desee mover. (Aqu´ı
    • 95. 6.1. TIMERS 95 1 ms 1,5 ms 2 ms 20 ms Figura 6.11: Diagrama de tiempos de un servo 1 v oid se tup ( ) { 2 c l i ( ) ; 3 TCCR1A = 0 ; 4 TCCR1B = 0 ; 5 TCCR1A |= ( 1 << COM1A1) | ( 1 << WGM11) ; 6 TCCR1B |= ( 1 << WGM13) | ( 1 << WGM12) | ( 1 << CS11 ) | ( 1 << CS10 ) ; 7 ICR1 = 5 0 0 0; 8 pinMode ( 9 ,OUTPUT) ; 9 pinMode ( 1 0 ,OUTPUT) ; 10 pinMode ( 7 ,INPUT) ; 11 S e r i a l . b e gi n ( 9 6 0 0 ) ; 12 s e i ( ) ; 13 OCR1A = 3 7 5; 14 } 15 16 v oid l o o p ( ) { 17 i f ( S e r i a l . a v a i l a b l e ( ) ) { 18 OCR1A = S e r i a l . p a r s e I n t ( ) ; 19 } 20 } C´odigo 6.3: Ejemplo de uso PWM con servo ´unicamente a˜nadiremos la l´ogica de control para modificar el ciclo de trabajo a un valor fijo, se deja como tarea para el lector el a˜nadir al c´odigo la l´ogica necesaria para generar un ciclo de trabajo u otro en funci´on de los grados que se desee mover). En Cod. 6.3 se puede estudiar el c´odigo implementado. A continuaci´on explicaremos como siempre las partes m´as interesantes del mismo. En primer lugar, como siempre, se deshabilitan las interrupciones mediante la macro cli(), seguidamente se configura el registro TCCR1A siguiendo el datasheet de modo que la se˜nal permute en cada comparaci´on con OCR1A. En el bucle loop() ´unicamente comprobamos si existen datos en el buffer de recepci´on serial. Si existe alg´un dato lo convertimos a entero y lo escribimos en el registro de comparaci´on. Esto nunca se debe realizar dado que no hemos realizado ning´un control de entrada y el usuario podr´ıa ingresar cualquier valor, incluso letras o s´ımbolos que podr´ıan causar serios problemas en un dise˜no en producci´on. Los valores recomendados son 125,250,375,500 que corresponden a 0,45,90,135,180
    • 96. 6.2. MULTITASKING 96 grados, la manera de calcular estos valores es muy sencilla. Pongamos un ejemplo: 180º son 2ms o 2.5 ms en funci´on del servomotor utilizado, en este caso hemos utilizado 2.5 porque tras probar hemos visto que es el valor al que mejor responde el servomotor. Adem´as hemos utilizado una frecuencia de 50Hz, por lo que tenemos un periodo de 20 ms. 2.5 ms es un 12,5 % del periodo. Si el periodo est´a determinado por el valor TOP almacenado en el ICR1, en este caso 5000, un 12,5 de 5000 ser´a igual a 625. Como ya hemos dicho estos valores hay que probarlos y en el caso del servo de ✭✭Seeed✮✮ este valor hace que el servo se bloquee porque el potenci´ometro no tiene tanto recorrido, por esta raz´on hemos recomendado un valor de 500. 6.2. Multitasking Hasta ahora hemos visto c´omo funciona el core de tiempos de Arduino, al final todo se resume en una palabra, TIMERs. En esta secci´on veremos c´omo abstraernos de todos los detalles de implementaci´on y utilizando las librer´ıas de Arduino crear nuestros programas ✭✭paralelos✮✮. La idea que reside en esta implementaci´on del ✭✭multitasking✮✮ es muy sencilla, nuestro programa principal actuar´a de ✭✭scheduler✮✮ de tal modo que en su bucle loop() comprobar´a para cada elemento si es su turno y si es as´ı le conceder´a el ✭✭procesador✮✮ para que realice las acciones pertinentes. En la industria se suele trabajar con dispositivos PLC, estos dispositivos se programan de una manera diferente a como estamos acostumbrados. La principal diferencia es que no se programa de forma textual (hoy en d´ıa se est´a incorporando a la industria esta manera de programar los PLC), por el contrario se programan mediante diagramas Ladder. Estos diagramas tienen un elemento llamado l´ıneas de alimentaci´on que representan la entrada de se˜nales a nuestro elemento de proceso y las salidas del mismo. En la Figura 6.12 puedes ver un ejemplo de un programa realizado en ladder. Cada pasada vertical, se denomina un ciclo de ✭✭scan✮✮. Para adaptar nuestros dise˜nos a los entornos industriales y que en la medida de lo posible se minimice el coste de mantenimiento o lo que es lo mismo el tiempo requerido para entender nuestro programa. En este libro hemos buscado una soluci´on estructurada que ayudar´a bastante a la consecuci´on de este objetivo. Apoy´andonos en la orientaci´on a objetos encapsularemos cada elemento en una clase y dotaremos a la misma de un m´etodo llamado ✭✭scan✮✮, este m´etodo para simplificar el dise˜no nos devolver´a un valor ✭✭boolean✮✮ con la informaci´on del ciclo (correcto, defecto), aunque lo correcto ser´ıa encapsular el valor de retorno en otra clase que nos brindara m´as informaci´on. La orientaci´on a objetos no es el objetivo de este libro, ´unicamente lo utilizaremos como una manera de mostrar al usuario la verdadera importancia de estructurar el c´odigo y para que pueda ver los beneficios inmediatos de su utilizaci´on. El c´odigo que se utilizar´a en los ejemplos tratar´a utilizar´a los conceptos m´as sencillos posibles en cuanto a orientaci´on a objetos se refiere intentando evitar aspectos m´as complejos como el polimorfismo, herencia, etc. Este es un buen momento para que te animes a leer el Secci´on C.1 y prepares tu entorno de desarrollo para estos ejemplos, aunque nosotros sigamos utilizando el entorno Arduino te servir´a para ver las bondades de Eclipse en cuanto tenemos un programa medianamente complejo. Como ya se coment´o en la Secci´on 6.1, para medir tiempos podemos utilizar varias
    • 97. 6.2. MULTITASKING 97 Figura 6.12: Ejemplo de programa en ladder
    • 98. 6.2. MULTITASKING 98 funciones de Arduino, nosotros utilizaremos la funci´on millis() que nos permite saber cuanto tiempo ha pasado desde que se ha iniciado el programa. Para aplicaciones que estar´an activas durante mucho tiempo hay que tener en cuenta que el contador se pondr´a a 0 en 50 a˜nos, por lo que tendremos que salvar de alguna manera el tiempo (por ejemplo guard´andolo en la memoria EEPROM). 6.2.1. Encendiendo y apagando un led de manera profesional El led ha sido nuestro compa˜nero desde el primer cap´ıtulo, con ´el hemos realizado la mayor´ıa de ejemplos y como no puede ser de otra manera, en este primer ejemplo sobre multitasking recurriremos de nuevo a ´el. En la mayor´ıa de los ejemplos que puedes encontrar por Internet ver´as que para encender y apagar un led se utiliza la funci´on delay(). En entornos industriales esta funci´on es muy temida y la raz´on es bastante sencilla: Si un programador que est´a encargado de mover un motor utiliza la funci´on delay() para programar las esperas en el arranque ✭✭estrella-triangulo✮✮ puede hacer que nuestro LCD deje de refrescarse y no avise de un error grave en un cilindro. En este ejemplo vamos a retomar la tarea de encender y apagar un led cada segundo. Para ello en vez de dejar que pase el tiempo vamos a cuantificar este tiempo y mientras tanto aprovecharemos para hacer trabajo ´util. En Cod. 6.4 puedes encontrar el c´odigo que a continuaci´on comentaremos. En primer lugar en el setup() como siempre se inicializan las comunicaciones as´ı como se configuran los pines en el modo correspondiente (hasta aqu´ı nada nuevo para el lector). Lo verdaderamente importante se encuentra dentro del bucle loop(), es aqu´ı donde podemos ver dos funciones: scan led() y scan communications() estas funciones son las encargadas de encender el led y mandar mensajes por el puerto serie respectivamente. Los ✭✭timing✮✮ de estas acciones ser´an los siguientes: Led: Se encender´a y apagar´a cada segundo. Comunicaciones: Se mandar´a un mensaje con la cadena .Estoy aqu´ı ”m´as el tiempo en milisegundos cada 100 milisegundos. Empecemos por la funci´on scan led(), se utiliza una variable de tipo est´atica. La raz´on por la cual hemos utilizado este tipo de variable es ´unicamente educativa de modo que el lector sepa que estas variables solo se inicializan una vez y luego cada vez que se entra a la funci´on se mantiene el valor anterior, es decir, es pr´acticamente igual que una variable declarada fuera de todos los m´etodos tal y como hab´ıamos hecho anteriormente. Mediante esta funci´on registramos el valor de la ´ultima ejecuci´on de la acci´on a controlar. En el ✭✭if✮✮ es donde se encuentra el ✭✭scheduler✮✮. En este ✭✭if✮✮ comprobamos si ha pasado el tiempo m´ınimo que hab´ıamos programado. Es importante observar que solo podemos asegurar un tiempo m´ınimo y no un tiempo m´aximo. Si lo que buscamos es acotar el tiempo en el cual se debe realizar una determinada acci´on, en ese caso necesitaremos dotar a nuestro sistema de un ✭✭scheduler✮✮ apropiativo de modo que cuando se alcance un determinado tiempo m´aximo cancele todas las tareas y vuelva a la tarea que requiere atenci´on. Un sistema operativo muy liviano que permite dotar al sistema de un ✭✭scheduler✮✮ apropiativo es el sistema FreeRTOS. Si ejecutas el c´odigo podr´as ver que efectivamente por cada mensaje y permutaci´on del led, se imprimen 5 mensajes con el mensaje ✭✭I’m here xxxx✮✮. En la Subsecci´on 6.2.2 veremos una manera m´as elegante de ordenar el c´odigo como se explic´o anteriormente, con el objetivo de facilitar el mantenimiento del mismo.
    • 99. 6.2. MULTITASKING 99 1 2 c o n s t i n t l e d p i n = 1 3; 3 4 #d e f i n e TIME TOOGLE LED 500 5 #d e f i n e TIME COMMUNICATION SEND 100 6 7 b o ol s c a n l e d ( ) { 8 s t a t i c l o n g l a s t t i m e l e d = 0 ; 9 i f ( ( m i l l i s ( ) − l a s t t i m e l e d )>= TIME TOOGLE LED) { 10 l a s t t i m e l e d = m i l l i s ( ) ; 11 d i g i t a lW r i t e ( l e d pi n , ! di gi t al R e a d ( l e d p i n ) ) ; 12 S e r i a l . p ri n t ( " Led toogle at: " ) ; 13 S e r i a l . p r i n t l n ( l a s t t i m e l e d ) ; 14 } 15 r e t u r n t r u e ; 16 } 17 18 b o ol sc an c ommunic a ti on s ( ) { 19 s t a t i c l o n g l a s t tim e c omm u ni c a ti o n = 0 ; 20 i f ( ( m i l l i s ( ) − l a s t tim e c omm u ni c a ti o n ) >= TIME COMMUNICATION SEND) { 21 l a s t tim e c omm u ni c a ti o n = m i l l i s ( ) ; 22 S e r i a l . p ri n t ( "I’m here : " ) ; 23 S e r i a l . p r i n t l n ( l a s t tim e c omm u ni c a ti o n ) ; 24 } 25 26 } 27 28 v oid se tup ( ) { 29 pinMode ( l e d pi n , OUTPUT) ; 30 S e r i a l . b e gi n ( 9 6 0 0 ) ; 31 } 32 33 v oid l o o p ( ) { 34 s c a n l e d ( ) ; 35 sc an c ommunic a ti on s ( ) ; 36 } C´odigo 6.4: Ejemplo de multitasking
    • 100. 6.2. MULTITASKING 100 6.2.2. Encendiendo y apagando un led de manera m´as profesional La orientaci´on a objetos, como ya se ha comentado, puede reducir much´ısimo los costes de mantenimiento as´ı como posibles fallos en la implementaci´on. Mantener un programa en un ´unico fichero y de forma completamente acoplada no es buena idea. En este ejemplo vamos a suponer que somos programadores de una empresa a la que se le ha asignado la tarea de implementar un m´odulo ✭✭led✮✮ para proyecto futuro. La empresa en su gu´ıa de estilos tiene definida una interfaz clara de que m´etodos se deben exponer. En este caso, para simplificar, supondremos que ´unicamente est´a especificado el m´etodo scan. Para que nuestro trabajo pueda ser reutilizado en los dem´as proyectos tendremos que encapsular el mismo lo m´aximo posible. En Cod. 6.5 puedes ver el ejemplo completamente implementado. A continuaci´on explicaremos las partes m´as importantes del mismo. En C++, para declarar una clase, hay que utilizar la palabra reservada class. En este caso hemos creado una clase que se llama LED. Esta clase ofrece al exterior un m´etodo llamado scan(), tal y como se nos indic´o en la gu´ıa de estilos. La clase tambi´en tiene un m´etodo privado update() que es el encargado de comprobar si ha llegado el tiempo de permutaci´on. Ten en cuenta que este m´etodo es privado y por lo tanto no le afecta la gu´ıa de estilos. Por otro lado, el constructor recibe el n´umero de pin y el tiempo en el cual se quiere permutar el led (l´ınea 16). Como puedes ver esta manera es mucho m´as elegante dado que si queremos crear otro led, ´unicamente tenemos que a˜nadir otra l´ınea como LED led1 (13, 500). En este ejemplo tan sencillo puede que no se vean las virtudes de este m´etodo, pero imagina tener que controlar 200 LEDs, ¿ves ahora la diferencia?, probablemente con el m´etodo tradicional (copia de c´odigo) y variables globales nos quedar´ıamos sin memoria en el microcontrolador y la tarea de mantenimiento ser´ıa muy tediosa, con el incremento en el coste asociado. Se recomienda al lector extender este ejemplo con una nueva clase ServoMotor que controle un motor de manera sencilla apoy´andose en el c´odigo Cod. 6.3 y en todo lo aprendido hasta ahora. Una vez creada la clase a˜n´adela al proyecto y observa las bondades del multitasking en Arduino.
    • 101. 6.2. MULTITASKING 101 1 c l a s s LED { 2 p r i v a t e : 3 i n t p i n l e d ; 4 l o n g l a s t u p d a t e t i m e ; 5 l o n g tim e t o u p d a t e ; 6 7 v oid update ( ) { 8 i f ( ( m i l l i s ( ) − t hi s −> l a s t u p d a t e t i m e ) >= tim e t o u p d a t e ) { 9 t hi s −> l a s t u p d a t e t i m e = m i l l i s ( ) ; 10 d i g i t a lW r i t e ( p i n l e d , ! di gi t al R e a d ( p i n l e d ) ) ; 11 } 12 } 13 14 p u bli c : 15 16 LED( i n t pi n l e d , l o n g tim e t o u p d a t e ) { 17 t hi s −> p i n l e d = p i n l e d ; 18 t hi s −> l a s t u p d a t e t i m e = 0 ; 19 t hi s −>tim e t o u p d a t e = tim e t o u p d a t e ; 20 pinMode ( p i n l e d , OUTPUT) ; 21 } 22 23 b o ol sc an ( ) { 24 update ( ) ; 25 } 26 } ; 27 28 LED l e d 1 ( 1 3 , 5 0 0 ) ; 29 30 v oid se tup ( ) { 31 32 } 33 34 v oid l o o p ( ) { 35 l e d 1 . sc an ( ) ; 36 37 } C´odigo 6.5: Ejemplo de multitasking orientado a objetos
    • 102. 6.2. MULTITASKING 102
    • 103. APENDICE A ´ CONSTRUYENDO NUESTRO PROPIO ARDUINO A.1. Introducci´on En este ap´endice se guiar´a al lector en la construcci´on de su propio Arduino Uno, el cual ser´a completamente compatible con un Arduino comprado en la tienda oficial. Como se coment´o en el Cap´ıtulo 1, Arduino mantiene la filosof´ıa del software libre y nos proporciona todos los esquemas necesarios para construir o mejorar su plataforma. El Arduino que vamos a construir en este ap´endice es muy sencillo y ´unicamente tendr´a lo indispensable para que el lector pueda conectarlo al ordenador y ejecutar los ejemplos realizados en este libro, por lo que eliminaremos muchos elementos de seguridad y filtrado que tiene el Arduino original con el objetivo de simplificar su montaje. A.2. Componentes necesarios A continuaci´on, se detallar´a una lista con los componentes que se necesitan para el montaje. Muchos de los componentes tienen equivalentes por lo que en este manual hemos optado por los m´as comunes, no obstante, si el lector encuentra un equivalente al componente no habr´a problema en sustituirlo, aunque se recomienda escoger en la medida de lo posible los citados en la siguiente lista. 1. Protoboard 2. Fuente de alimentaci´on 5-18V CC 3. Cables Jumper 4. LM7805 5. Condensador electrol´ıtico 10 uF (x2) 103
    • 104. A.3. ENSAMBLADO 104 6. Reloj de cristal de 16MHz 7. ATmega328 8. Condensador electrol´ıtico de 22 pf (x2) 9. FTDI232RL 10. USBASP1 A.3. Ensamblado El primer paso es situar los componentes en la placa de montaje (protoboard desde ahora), aunque pueda parecer que la colocaci´on es algo meramente decorativo y ✭✭superfluo✮✮ es muy importante tenerlo en cuenta. La diferencia entre poner el condensador de desacoplo a 1 cent´ımetro del microcontrolador o ponerlo a 20 cent´ımetros puede ser determinante en algunos sistemas2 . En la Figura A.1 se han dispuesto los componentes de modo que al lector le sea sencillo identificar los mismos y situarlos en el circuito por lo que, se recomienda que realice ligeras modificaciones manteniendo las reglas de conexi´on (mismo esquema) de modo que los componentes est´en m´as pr´oximos entre ellos. El circuito se puede dividir en tres partes. A continuaci´on, describiremos cada una de ellas: Etapa de alimentaci´on: El circuito est´a preparado para ser alimentado con un voltaje desde 5 V a 18 V de corriente continua, es importante que la corriente sea continua pues de lo contrario el circuito no funcionar´a, pudiendo llegar a dejarlo inservible. Si el lector no tuviera una fuente de corriente continua con estas caracter´ısticas, puede montarse una peque˜na fuente, para lo cual necesitar´a entre otras cosas un transformador (fuente con transformador) y un puente de diodos (en Internet hay mucha informaci´on a cerca de las fuentes de alimentaci´on). Lo siguiente es conectar los terminales a la protoboard. Existe un convenio de conexi´on por el cual se recomienda conectar el terminal positivo a la primera fila de la protoboard y el negativo a la segunda. El lector podr´a reconocer estas filas porque normalmente vienen separadas del resto de la protoboard. Ahora que la placa est´a alimentada, solo queda situar los componentes tal cual aparecen en el esquema Figura A.2 tambi´en puedes guiarte por la Figura A.1 aunque recuerda, ´esta no es la distribuci´on ´optima. Es importante que se respete la polaridad del condensador electrol´ıtico dado que de lo contrario, si se supera la tensi´on inversa umbral, podremos romper el mismo. Para saber c´omo conectar el condensador busca una l´ınea vertical de color blanco que indica el terminal negativo. (ver Figura A.1) A la salida del integrado LM7805 (l´ınea naranja) tendremos 5 V estables que ser´a la ✭✭fuente de alimentaci´on✮✮ del microcontrolador ATmega328. 1USBASP es un programador para los microcontroladores AVR libre, por lo que desde la web del autor (http://www.fischl.de/usbasp/) el lector podr´a acceder tanto al firmware como al circuito pudiendo construir su propio programador. 2Los cables tienen una capacitancia y una inductancia que afecta al circuito, en dise˜nos donde la precisi´on es muy importante, hay que tener en cuenta la longitud de los cables entre otros factores
    • 105. A.3. ENSAMBLADO 105 5v-12v 16MHz Cristal Azul = Tierra Rojo = Entrada Naranja = 5v estables Cyan = Cristal ------------------- Marron = Rx(Arduino) - Tx(FTDI) Morado = Tx(Arduino) - Tx(FTDI) Figura A.1: Protoboard de referencia Figura A.2: Esquema de conexiones
    • 106. A.3. ENSAMBLADO 106 Figura A.3: Patillaje ATmega328 Microcontrolador: El microcontrolador ATmega328 es el coraz´on del Arduino UNO, es en ´el donde se cargaran los programas. En esta parte del montaje nos encargaremos de que el coraz´on empiece a ✭✭latir✮✮. Para ello, lo primero que hay que saber es c´omo est´an distribuidos los terminales. En la Figura A.3 se puede observar que los pines para alimentar el microcontrolador son el 7 y 8, pero... ¿D´onde empieza la numeraci´on? para ello se sigue un convenio por el cual todos los circuitos integrados disponen de una marca que indica el pin con el inicio de la numeraci´on. En la Figura A.1 se puede observar el punto que indica el inicio. Una vez que el los terminales de alimentaci´on del microcontrolador est´an conectados a la salida del LM7805, ahora solo resta el incorporar un componente que permita al ATmega328 latir a una frecuencia determinada (siguiendo la analog´ıa del coraz´on humano) este componente es un reloj de cuarzo. El reloj de cuarzo genera una onda cuadrada con una frecuencia determinada, normalmente los microcontroladores poseen un reloj RC3 pero debido a su precisi´on y a que normalmente oscilan a una velocidad inferior, a˜nadiremos el reloj de cuarzo. Siempre que se va a dotar al microcontrolador de un reloj externo se utiliza el mismo circuito, que consta de un reloj y dos condensadores, cada uno de ellos en paralelo al reloj. Cuanto m´as cerca est´e el reloj de los terminales de conexi´on del microcontrolador mayor precisi´on, por lo que de nuevo se recomienda que ´unicamente se siga la Figura A.2 para guiarse y se modifiqu´e la disposici´on de los elementos. Finalmente falta comunicar el pin 20 (AVCC) con VCC, esto se mantendr´a as´ı hasta que el lector quiera hacer uso del ADC (Digital Analogic Converter) cuando tendr´a que conectar el pin 20 a VCC a trav´es de un filtro de paso bajo. Comunicaci´on: Ahora que el microcontrolador ya es capaz de oscilar a una frecuencia determinada (en nuestro caso 16MHz) tenemos que situar el canal de comunicaci´on entre el computador y el microcontrolador. Si el microcontrolador tuviera integrado un chip para USB ´unicamente tendr´ıamos que conectar los cables del USB al mismo, pero como el ATmega328 solo posee de comunicaci´on serial (para la programaci´on) tendremos que hacer una serie de modificaciones de modo que el Arduino se pueda programar desde el computador: • Ordenador con puerto serie: Si tu ordenador posee de puerto serie, entonces 3Los relojes RC suelen tener una frecuencia inferior y una precisi´on menor frente al reloj de cuarzo.
    • 107. A.4. PROGRAMACION DEL BOOTLOADER 107 ´ no hay ning´un problema, ´unicamente tendr´as que conectar el pin Tx al Rx del ATmega328 (pin 2) y el pin Rx al Tx (pin 3). • Ordenador con USB: Si tu ordenador ´unicamente dispone de puertos USB, tendr´as que utilizar un conversor como puede ser FTDI232RL que realiza la labor de identificar el dispositivo y gestionar la UART de modo que los datos que escribamos bajo la pila USB se trasmitan de forma correcta bajo el protocolo serial. En el caso de estar utilizando el conversor FTDI232RL y estando conectados los puertos Rx y Tx a los pines Tx y Rx del ATmega328 respectivamente. Solo queda alimentar el microcontrolador FTDI232RL desde el LM7805 y ya tendremos el Arduino listo para ser programado. A.4. Programaci´on del Bootloader Para que el ATmega328 se convierta en un Arduino, necesitamos cargar el bootloader, para ello podemos seguir diferentes m´etodos. En el este libro se explicar´a el ✭✭quemado✮✮ con un programador USBasp el cual es completamente libre. Antes de empezar a quemar el bootloader en primer lugar se explicar´a qu´e es y qu´e funciones realiza por nosotros. El ATmega328 tiene una memoria Flash de 32 KB en la que se podr´ıa almacenar un programa compilado para este microcontrolador y ejecutarlo posteriormente. Para programar esta memoria se necesitan herramientas como el programador STK-500 por lo que Arduino decidi´o a˜nadir una interfaz USB de modo que realice la conversi´on de los datos que llegan por USB a un protocolo serial y los multiplexe en el tiempo del mismo modo en que lo har´ıa un programador como el STK-500. El bootloader ocupa un espacio determinado en la memoria flash dado que realmente es un programa que se inicia cuando alimentamos el microcontrolador ejecutando un conjunto de instrucciones que permite al ATmega saber si hay alguna petici´on para programar el Arduino y si es as´ı seleccionar la direcci´on de memoria donde ✭✭situar✮✮ dicho programa. Si el bootloader no encuentra ning´un petici´on de programaci´on entonces salta a la direcci´on de memoria del bucle de setup del programa y a continuaci´on empieza el bucle de operaci´on. Como el lector habr´a podido apreciar, no es necesario ✭✭quemar✮✮ este bootloader pero si no se quema el lector tendr´a que contar con alg´un programador AVR para ✭✭quemar✮✮ el programa y adem´as tendr´a que realizar la programaci´on por ISP mediante el conector ICSP4lo cual es mucho m´as incomodo. A continuaci´on vamos a proceder a ✭✭quemar✮✮ el bootloader, para ello lo primero que necesitamos es un programador de AVR, en este libro, como ya se ha dicho anteriormente se utilizar´a el programador libre USBasp. La instalaci´on de los drivers del mismo se deja como trabajo para el lector. Una vez que el programador est´a correctamente instalado, tenemos que realizar las conexiones, para ello hay que tener en cuenta que el conexionado ICSP hay que realizarlo con mucho cuidado. 4 ICSP son las siglas de In Chip Serial Programming, con este conector, se permite reprogramar el microcrocontrolador sin necesidad de desoldarlo y por lo tanto aumentando la vida ´util del microcontrolador
    • 108. A.4. PROGRAMACION DEL BOOTLOADER 108 ´ (a)Conector ICSP de 6 pines (b)Conector ICSP de 10 pines Figura A.4: Conectores ICSP Existen dos tipos de conectores ICSP, de 10 pines (ver Figura A.4b) y de 6 pines (ver Figura A.4b). Para localizar la ubicaci´on de cada uno de los pines se puede utilizar un mult´ımetro y medir la diferencia de potencial (voltaje) entre un pin y tierra de tal manera que el pin que devuelva el voltaje de programaci´on (3.3-5V) ser´a el VTG o VCC y el pin com´un ser´a tierra. El lector tambi´en se puede guiar por el cable rojo, de modo que ´este ser´a el pin 1. Existen alternativas para facilitar la conexi´on del programador al Arduino como por ejemplo el conversor ✭✭ICSP Header✮✮ cuya ´unica funcionalidad es proporcionar una interfaz ✭✭inline✮✮ que se podr´a conectar a la protoboard siendo mucho m´as sencilla la conexi´on final al ATmega. Una vez que el lector haya localizado cada uno de los pines del conector o haya adquirido un conversor, ya est´a en disposici´on de realizar la conexi´on entre el ICSP y el microcontrolador. Los pines son los proporcionados por la interfaz de comunicaci´on ISP, es decir, GND, VCC, MISO (Master Input Slave Output), MOSI(Master Output Slave Input), SCK (Clock), RESET. La conexi´on ISP es muy sencilla gracias a lo descriptivo de los nombres de sus pines, sin embargo, a continuaci´on se detalla las conexiones a realizar: Pin MISO programador → Pin 18 ATmega328 Pin MOSI programador → Pin 17 ATmega328 Pin SCK programador → Pin 13 ATmega328 Pin RESET programador → Pin 1 ATmega328 Ahora solo queda cargar el bootloader al ATmega328, para ello hay que conectar el programador al conversor ICSP o en caso de no haber utilizado el conversor, conectar cada uno de los pines del conector ICSP al microcontrolador. Despu´es conectar el programador al computador, es importante que los drivers est´en completamente configurados, de lo contrario el proceso no se completar´a con ´exito. El siguiente paso es abrir el Arduino IDE y seleccionar “Herramientas → tarjeta → Arduino Uno” (Figura A.5) con lo que indicaremos al IDE que vamos a programar un Arduino Uno, despu´es seleccionamos “Herramientas → programador → USBasp”, por ´ultimo hay
    • 109. A.4. PROGRAMACION DEL BOOTLOADER 109 ´ Figura A.5: Captura de pantalla del Arduino IDE que pulsar sobre “Herramientas → Grabar secuencia de inicio”. Si todo ha ido bien el lector podr´a quemar sus propios sketch.
    • 110. A.4. PROGRAMACION DEL BOOTLOADER 110 ´
    • 111. APENDICE B ´ MANIPULACION DE REGISTROS ´ B.1. Introducci´on Cuando un dise˜nador se enfrenta a la tarea de dise˜nar una soluci´on inform´atica en un microcontrolador, debe manejar una serie de conceptos que normalmente no son necesarios o indispensables cuando se programa una soluci´on en un computador de prop´osito general. Tener claro donde almacenar una cadena de texto o que espacio de memoria asignar para una determinada tarea son algunos de estos conceptos. En este ap´endice se va a guiar al lector en el aprendizaje de las operaciones l´ogicas con bits. Es importante tener en cuenta que la informaci´on en un microcontrolador se suele almacenar en registros. Estos registros pueden almacenar datos de diferente naturaleza, por ejemplo, un microcontrolador que forme parte de una impresora puede tener un registro para el control de la misma (calentar rodillo, situar en posici´on inicial, iniciar operaci´on) y otro registro para los datos como por ejemplo la hoja a imprimir. Cuando el dise˜nador programa la l´ogica de la impresora, deber´a obtener y validar la informaci´on contenida en estos registros. En este ap´endice se mostrar´an las diferentes t´ecnicas que se utilizan para obtener y tratar los datos de los registros. B.2. ¿Qu´e es un registro? Antes de tratar de realizar operaciones con los registros, debemos tener claro qu´e es realmente un registro. Se puede explicar qu´e es un registro desde dos perspectivas: la perspectiva l´ogica y la perspectiva f´ısica. En este ap´endice nos centraremos en la primera dado que buscamos el tratar con la informaci´on almacenada en los registros y no el implementar f´ısicamente el mismo. El registro es la unidad de almacenamiento que se encuentra en el nivel superior dentro de la jerarqu´ıa de memorias (ver Figura B.1), es decir, es la memoria m´as r´apida y m´as cara de todas. Este es el motivo por el cual es un recurso limitado en los computadores y tan preciado por los compiladores. Como ya hemos nombrado en la introducci´on, existen registros de muchos tipos como por ejemplo los registros de datos que tienen la funci´on principal de almacenar datos. Un ejemplo de este tipo de 111
    • 112. B.2. ¿QUE ES UN REGISTRO? 112 ´ registros es el registro acumulador de la ALU de los microcontroladores PIC. Tambi´en existen registros espec´ıficos como el registro SP (stack pointer) de la arquitectura ARM el cual ´unicamente se utiliza para almacenar la direcci´on de memoria del ✭✭top✮✮ de la pila. Registros del procesador Memoria caché (L1,L2,L3) Memoria RAM (random access memory) Disco duro Almacenamiento secundario Cloud (Copias de seguridad) Capacidad Velocidad y Coste por bit Figura B.1: Jerarqu´ıa de memoria Todos estos registros tienen unas propiedades comunes como son: 1. Capacidad (Ancho): Los registros tienen un ancho determinado que depender´a del prop´osito para el que se haya dise˜nado. Por ejemplo, un registro que mantenga la direcci´on de la siguiente instrucci´on a ejecutar IR (instruction register) deber´a tener la capacidad suficiente que permita codificar las instrucciones de un programa. Si se quiere un computador con la capacidad de ejecutar 256 instrucciones se deber´a dise˜nar un registro con un ancho de 8 bits.1 2. Tecnolog´ıa: Los registros f´ısicamente se dise˜nan con una tecnolog´ıa determinada. Esta tecnolog´ıa ser´a determinante a la hora de comparar otras caracter´ısticas como la latencia, capacidad de integraci´on. . . 3. Direcci´on: Un registro queda identificado univocamente por su direcci´on de memoria como por ejemplo podr´ıa ser la direcci´on 0x00001A1B Podemos imaginar un registro como una caja (ver Figura B.2) que tiene diferentes compartimentos. Cada uno de estos compartimentos es un bit. Dependiendo del tipo de registro nos interesar´a toda la ✭✭caja✮✮ (registro) o ´unicamente uno de sus ✭✭cajones✮✮ (bit). Por ejemplo, si tenemos un registro de datos, seguramente nos interese el registro entero y no uno de sus bit dado que normalmente cuando se accede a un registro de datos se busca el valor del n´umero almacenado, esto no implica que no se pueda acceder a un ´unico bit, por ejemplo para saber si el n´umero es positivo o negativo en un n´umero representado en signo magnitud. 1Con 8 bits se pueden codificar 28 instrucciones
    • 113. B.3. OPERACIONES CON REGISTROS 113 0 0 0 0 0 0 0 0 Figura B.2: Registro de 8 bits (byte) como una ✭✭caja✮✮ B.3. Operaciones con registros Ahora que ya sabemos qu´e es un registro, estamos en disposici´on de realizar diferentes operaciones con ellos. En esta secci´on y con el objetivo de que el lector pueda poner sus conocimientos en pr´actica vamos a utilizar registros reales del microcontrolador ATmega328, a continuaci´on se realizar´a una peque˜na descripci´on de la utilidad de cada uno de estos registros: DDRC (Data Direction Register C ): Este registro se encuentra en la direcci´on 0x07 y permite configurar los pines del puerto2 como entrada(0) o salida(1), en funci´on del bit que se situ´e en cada uno de las posiciones del registro, por ejemplo la configuraci´on 0b00000001 configura todos los puertos como entrada(0) y el primero3 como salida(1)4. PORTC (Port Output Register C ): Este registro sirve para activar las resistencias de pull-up que el microcontrolador posee en cada uno de los pines configurados como entrada. Si el pin est´a configurado como salida (DDRX(n) == 1) este registro determinar´a el voltaje ✭✭alto✮✮ o ✭✭bajo✮✮ a la salida del pin. PINC (Pin Input Register C ): Este registro normalmente formar´a parte de las estructuras ✭✭IF ... THEN✮✮, en cada uno de sus bits obtendremos el valor de un pin configurado como entrada. Si el valor del bit es 1 significar´a que el pin que representa tiene VCC como entrada, sin embargo, si el valor es 0 entonces significar´a que el voltaje de entrada es GND. B.3.1. Activar un bit Imaginemos que queremos configurar el pin 0 del puerto ✭✭c✮✮ como salida, para ello sabemos que tenemos un registro espec´ıfico como es el registro DDRC. Sabemos tambi´en que la direcci´on de memoria del registro es la 0x07 y que podemos acceder a ´el mediante el puntero DDRC que apunta a dicha direcci´on5. Si queremos activar el led 0 como salida podr´ıamos asignar el siguiente valor a DDRC : 1 0b00000001 0x01 DDRC = 1 << PIN0 2Los pines de entrada/salida se agrupan en los llamados ✭✭puertos✮✮. Cada puerto controla un determinado n´umero de pines. 3Es importante tener en cuenta que los bits se empiezan a numerar por 0. 4Toda esta informaci´on se puede obtener del Datasheet del microcontrolador ATmega328. 5Estos punteros son mapeados normalmente por la librer´ıa del fabricante.
    • 114. B.3. OPERACIONES CON REGISTROS 114 Los cuatro elementos de la lista anterior hacen referencia al mismo valor, es decir, al 1 en decimal. El primer elemento representa el 1 en decimal. Se sabe que un valor est´a expresado en binario cuando le antecede el prefijo ✭✭0b✮✮. El tercer elemento (0x01 ) representa el mismo valor pero en hexadecimal, es importante recordar, que un d´ıgito hexadecimal equivale a cuatro bits (el conjunto de 4 bits se le conoce como nibble). La ´ultima representaci´on es la m´as com´un pero no ser´a explicada hasta la Subsecci´on B.3.3 Ahora imaginemos que pasa si ocurrir´ıa si quisi´eramos configurar el pin 1 del puerto ✭✭c✮✮ como salida. En principio actuar´ıamos de la misma manera, es decir, configurar´ıamos el valor 0b0000010, pero como el lector habr´a podido observar, el valor del pin 0 cambiar´a y se configurar´a como entrada (0). La manera m´as sencilla de solventar este comportamiento es asignar el valor 0b00000011 pero ¿si no se conoce que pines est´an como salida en un estado anterior, c´omo se calcula el valor a escribir? esta pregunta ser´a contestada en la siguiente secci´on. B.3.2. ORing, activaci´on de un bit Para evitar el comportamiento observado en la secci´on anterior, se hace uso de la operaci´on OR. Esta operaci´on permite activar un bit sin ✭✭afectar✮✮ a los dem´as bits del registro. Imaginemos el mismo ejemplo que el de la Subsecci´on B.3.1, es decir, deseamos configurar el primer y segundo bit como salida. La soluci´on planteada en la primera secci´on es incomoda e ineficiente dado que se necesita saber que bits est´an activados anteriormente para poder calcular el valor a escribir. Por lo que vamos a aplicar la funci´on OR de la siguiente manera: 1 DDRC = DDRC | 0 b10 De este modo conseguiremos el siguiente valor: 0b00000001 OR 0b00000010 ------------- 0b00000011 Como el lector habr´a observado, el resultado es el esperado, tanto el pin 1 como el 2 est´an configurados como salida. La operaci´on ORing ´unicamente afecta a los valores que poseen un 1 l´ogico, esto es debido a que el 0 act´ua como operador neutro (A OR 0 = A). El operando que utilizamos para realizar la operaci´on OR sobre el registro se llama ✭✭mascara✮✮. B.3.3. Bit Shifting, movimiento de bits La aritm´etica booleana proporciona herramientas extremadamente ´utiles al programador. Por ejemplo, una de las mejoras que un compilador puede realizar es la transformaci´on de una multiplicaci´on de potencia 2 que normalmente lleva varios ciclos (las unidades de multiplicaci´on suelen ser lentas) en una ´unica operaci´on l´ogica llamada ✭✭Bit Shifting✮✮. En este apartado vamos a explotar una de sus utilidades
    • 115. B.3. OPERACIONES CON REGISTROS 115 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 Figura B.3: Operaci´on: 1 << 1 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 OR 1 << 3 0 0 0 0 1 0 1 1 = Figura B.4: Activaci´on de pin 3 sobre registro previamente configurado que nos permitir´a hacer nuestro c´odigo m´as legible y nos ahorrar´a mucho c´odigo innecesario. Continuando con nuestro ejemplo de la configuraci´on del pin 0 y 1 como salida, en este apartado se ver´a como construir o hallar el valor que debe ser utilizado como operando extra en la operaci´on OR. En la Subsecci´on B.3.2 se utiliz´o el operando ✭✭extra✮✮ 0b00000010, a este operando como ya hemos dicho anteriormente, se le llama m´ascara. Para obtener este valor se recurre a la operaci´on Bit Shifting. Si queremos crear una mascara para el pin 1 ´unicamente tenemos que utilizar la siguiente operaci´on (1 << 1) (en la Figura B.3 est´a representada gr´aficamente la operaci´on). Si quisi´eramos configurar el pin 3 como salida proceder´ıamos del mismo modo, es decir, calcular la m´ascara y aplicarla mediante la operaci´on OR. Para calcular la m´ascara utilizamos la t´ecnica del Bit Shifting (1 << 3). En la Figura B.4 se puede ver de forma gr´afica el procedimiento. En el Cod. B.1 se puede ver un extracto de c´odigo donde se configura el pin PC6 y PC7 como salida. Esta es la forma m´as com´un para la configuraci´on de registros. Es importante observar que la operaci´on OR se realiza sobre el valor actual del registro (| =) y que se pueden realizar varias operaciones ORing en una ´unica asignaci´on.
    • 116. B.3. OPERACIONES CON REGISTROS 116 1 # include < avr / io .h > 2 3 int main () { 4 5 // Data Direction Register C = 0 b01100000 6 // Habilitamos como salida el pin PC6 y PC7 7 DDRC |= (1 << DDC6 ) | (1 << DDC7 ) ; 8 // Port Output Register C = 0 b01000000 9 // Salida del pin PC6 como valor HIGH 10 PORTC |= (1 << DDC6 ); 11 12 } C´odigo B.1: Ejemplo de asignaci´on mediante oring 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 OR 0 << 3 0 0 0 0 1 0 1 1 = Figura B.5: Configuraci´on del pin 3 como entrada sobre registro previamente configurado (procedimiento err´oneo) B.3.4. ANDing, desactivando bits Hasta ahora ´unicamente hemos configurado bits con el valor 1 pero ¿Se puede configurar del mismo modo el pin con el valor 0?. La mejor manera de ilustrar esta situaci´on es mediante un ejemplo. Imaginemos que sobre el ´ultimo valor del registro que hemos utilizado para los ejemplos (0b00001011 ) deseamos configurar el pin 3 como entrada (0). El valor 0 en binario ✭✭ocupa✮✮ un ´unico bit por lo que para crear la m´ascara podr´ıamos pensar en utilizar el mismo m´etodo que en apartados anteriores. En la figura Figura B.5 se ilustra el proceso. Como se puede observar el resultado obtenido es 0b00001011, es decir, no es el resultado esperado dado que el bit 3 est´a a 1. A continuaci´on, explicaremos el modo de realizar este tipo de operaciones. Si utilizamos la m´ascara normal, (la utilizada para configurar un bit a 1) y negamos la misma, podemos observar que el resultado tras realizar la operaci´on AND es el esperado. Se recurre al elemento neutro de la operaci´on AND, es decir, al 1, de modo que todos los bit que eran 0 al negarse se convierten a 1 consiguiendo que la operaci´on AND no tengan efecto (A AND 1 = A) sobre los bits que ya est´a configurados y s´ı tenga efecto (configurar con el valor 0) sobre los bits que est´an en 1 en el operando
    • 117. B.3. OPERACIONES CON REGISTROS 117 7 6 5 4 3 2 1 0 0 0 0 0 1 0 0 0 1 1 1 1 0 1 1 1 0 0 0 0 0 0 1 1 AND NOT 0 0 0 0 0 0 1 1 = Figura B.6: Configuraci´on del pin 3 como entrada sobre registro previamente configurado ✭✭extra✮✮. En la Figura B.6 se puede ver el procedimiento seguido paso a paso.
    • 118. B.3. OPERACIONES CON REGISTROS 118
    • 119. APENDICE C ´ ENTORNO ECLIPSE CON ARDUINO C.1. Introducci´on En este ap´endice veremos c´omo configurar el entorno de desarrollo Eclipse de modo que el mismo pueda ser utilizado para programar y grabar programas en la plataforma Arduino. El lector puede preguntarse por qu´e utilizar otro IDE si ya ha utilizado el oficial de Arduino y no ha tenido ning´un problema. Arduino fue pensado como una plataforma con una curva de aprendizaje muy baja, la ventaja obvia es la r´apida adaptaci´on de cualquier persona con m´ınimos conocimientos de inform´atica al entorno. El problema, no tan obvio en un primer momento, es la carencia de herramientas tan importantes como: control de versiones, gesti´on de proyectos, ✭✭tasks list✮✮, etc. Con el objetivo de mostrar al lector el potencial de Arduino, en este ap´endice configuraremos un entorno con todas las herramientas necesarias en cualquier ambiente profesional. C.2. Qu´e es Eclipse Eclipse es un entorno de desarrollo integrado de c´odigo libre y multiplataforma. Eclipse ofrece un ✭✭core✮✮ sobre el que otros desarrolladores realizan sus modificaciones para crear entornos de desarrollo espec´ıficos para cada tecnolog´ıa. Un ejemplo podr´ıa ser el entorno para Java o el entorno de Xilinx para el desarrollo de software sobre su plataforma hardware. Aunque existen plugins para Eclipse que ofrecen una capa de compatibilidad con Arduino, en este ap´endice trabajaremos sobre Eclipse para C++ con el objetivo de poder tener control sobre cada una de las partes que conforman la construcci´on de un binario para Arduino. 119
    • 120. C.3. INSTALACION DEL ENTORNO 120 ´ Figura C.1: Pantalla inicial de Eclipse La p´agina oficial de Eclipse se puede visitar desde el siguiente enlace: https: //eclipse.org, en ella podr´as encontrar toda la informaci´on de la fundaci´on Eclipse, adem´as, desde el apartado ✭✭Download✮✮ podr´as descargar cada una de las versiones oficiales del entorno. C.3. Instalaci´on del entorno El primer paso ser´a el descargar nuestro entorno de desarrollo Eclipse sobre el que realizaremos todas las ✭✭modificaciones✮✮ necesarias para que sea compatible compatible con la plataforma Arduino. Para descargar el entorno, en primer lugar, deberemos acceder a la web de Eclipse y luego al apartado de descargas: http://www.eclipse.org/downloads/ una vez dentro deberemos elegir la opci´on de ✭✭Eclipse IDE for C/C++ Developers✮✮. Esta versi´on nos ofrece muchas herramientas de gran utilidad como: Mylyn Task, Git o un sistema de acceso remoto. Una vez descargado el entorno para nuestra arquitectura, el segundo paso ser´a la descompresi´on del mismo. Eclipse es un programa portable, esto quiere decir que no requiere instalaci´on por lo que una vez descomprimido estaremos en disposici´on de utilizar el mismo. El lugar donde se descomprima no es relevante. El siguiente paso ser´a ejecutar el programa, en teor´ıa si tenemos Java en nuestro computador el entorno se ejecutar´a sin ning´un tipo de fallo, en caso de no tener Java el entorno nos avisar´a y tendremos que instalarlo. La pantalla inicial tendr´a la apariencia de la Figura C.1. Ahora deberemos instalar el entorno Arduino tal y como lo hicimos en Secci´on 2.1. Si ya lo tienes instalado no hace falta que vuelvas a hacerlo. Con la instalaci´on del entorno Arduino tendremos todas las herramientas necesarias, como son: Compilador: Utilizaremos el compilador ✭✭avr-gcc✮✮ y ✭✭avr-g++✮✮ para compilar
    • 121. C.4. CONFIGURACION DEL ENTORNO 121 ´ nuestros proyectos. Conjunto de librer´ıas Arduino: Las librer´ıas de Arduino, forman parte del llamado ✭✭ArduinoCore✮✮. Estas librer´ıas nos simplifican tareas repetitivas como la configuraci´on del modulo USART, configuraci´on de pines, etc. Librer´ıas AVR: Como ya sabr´as la librer´ıa de Arduino utiliza las librer´ıas de AVR por lo que tambi´en tendremos estas ´ultimas en nuestra instalaci´on. Programador: Para programar el Arduino necesitamos un programador ✭✭hardware✮✮ y un programador ✭✭software✮✮. En el caso del programador ✭✭hardware✮✮ ya lo tenemos instanciado dentro de la placa Arduino (una vez instalado el bootloader). Por otro lado, el programador ✭✭software✮✮ que utilizaremos ser´a ✭✭avrdude✮✮ que forma parte del conjunto de herramientas de AVR. Mapeado de pines: Como ya sabr´as, el entorno Arduino utiliza su propia numeraci´on de pines, de tal modo que si pones pin 13 en una funci´on, el Arduino ✭✭sabe✮✮ qu´e pin es y a qu´e puerto le corresponde (en realidad el encargado de hacer esta conversi´on como es l´ogico, no es Arduino, es el compilador). Esto se realiza mediante el archivo de variants que veremos m´as adelante. Es importante que anotemos la direcci´on de instalaci´on de Arduino para los pasos siguientes. En el caso de los sistemas GNU/Linux, esta direcci´on suele ser: /usr/share/arduino. C.4. Configuraci´on del entorno Ahora que tenemos todo descargado es el momento de realizar la configuraci´on del IDE Eclipse. En primer lugar descargaremos un plugin para el desarrollo de soluciones basadas en microprocesadores AVR dentro de eclipse. Para descargar un plugin en Eclipse, podemos acceder al men´u ✭✭Help, Install new software✮✮ o ✭✭Ayuda, Instalar nuevo software✮✮ en Espa˜nol. Una vez en esa pantalla deberemos pulsar al bot´on ✭✭Add✮✮ y luego en el apartado de ✭✭Name✮✮ poner el nombre que se desee, por ejemplo avr-descargas, y en location la siguiente direcci´on: http:// avr-eclipse.sourceforge.net/updatesite, deber´a quedar como en la Figura C.2. Una vez rellenado el formulario aparecer´a ante nosotros un plugin llamado AVR Eclipse Plugin con un ✭✭checkbox✮✮, deberemos seleccionar dicho ✭✭checkbox✮✮, seguidamente pulsaremos ✭✭Next✮✮ y ✭✭Finish✮✮ seg´un las instrucciones, hasta finalizar la instalaci´on. Una vez instalado, el siguiente paso consiste en la configuraci´on de las variables que permiten al plugin saber d´onde se encuentran los binarios del compilador, etc. Para configurar el plugin hay que ir al men´u ✭✭Window, Preferences✮✮ o ✭✭Ventana, Preferencias✮✮. Una vez en el men´u deberemos acceder al apartado de ✭✭AVR, Paths✮✮. Hay que configurar cada una de las variables para que apunten al binario dentro del SDK descargado en el paso anterior. AVR-GCC: <directorioSDK>/hardware/tools/avr/bin GNU make: Con el entorno descargado para Windows se descarga tambi´en la herramienta make por lo que el mismo estar´a en el directorio del SDK, sin
    • 122. C.5. CREANDO EL PROYECTO: ARDUINOCORE 122 Figura C.2: Descarga del plugin AVR embargo para GNU/Linux esta herramienta no viene incluida dado que forma parte de GNU/Linux y esta de forma nativa para todas las distribuciones por lo que en este caso seguramente el propio plugin detecte el lugar donde se encuentra instalado y aparecer´a algo como ✭✭system✮✮ en esta variable. AVR Header Files: <directorioSDK>/hardware/tools/avr/avr/include AVRDude: <directorioSDK>/hardware/tools/avr/bin En la Figura C.3 puedes ver un ejemplo de como podr´ıa quedar la configuraci´on, ten en cuenta que en funci´on de donde se realice la instalaci´on, los path y en consecuencia la configuraci´on variar´a. C.5. Creando el proyecto: ArduinoCore Como ya hemos comentado, Arduino nos proporciona un conjunto de librer´ıas que hacen que sea mucho m´as sencillo el utilizar algunos de los m´odulos ✭✭hardware✮✮ del microcontrolador. Para poder contar con todas estas comodidades tendremos que compilar las librer´ıas en un proyecto a parte (esto nos permitir´a reutilizar las librer´ıa en otros proyectos). Esta tarea en el entorno oficial de Arduino se realiza sin que nosotros tengamos que realizarlo de forma explicita, esto hace que sea mucho m´as r´apido para el usuario pero a la vez, hace poco did´actico el proceso. Para compilar las librer´ıas en primer lugar crearemos un proyecto, para ello vamos al men´u ✭✭New, C++ Project✮✮ y en tipo de proyecto ✭✭AVR Cross Target Static Library✮✮ el nombre que pondremos al proyecto ser´a ✭✭ArduinoCore✮✮, el nombre no es determinante, sin embargo este nombre simplifica la labor de comprensi´on de los pasos que estamos realizando. En configuraciones ´unicamente seleccionaremos la de ✭✭Release✮✮ esto es debido a que esta librer´ıa no la modificaremos y por lo tanto no necesitamos toda la configuraci´on de ✭✭Debug✮✮. En la siguiente pantalla de configuraci´on se nos preguntar´a por
    • 123. C.5. CREANDO EL PROYECTO: ARDUINOCORE 123 Figura C.3: Configuraci´on de ejemplo el microcontrolador y por la frecuencia. Ambos par´ametros deber´an ser configurados en funci´on del Arduino sobre el que se vaya a realizar el proyecto. En el caso del Arduino construido en este libro en la Secci´on A.1 configuraremos estos par´ametros con ATmega328 y 16000000 (como se puede observar en este ejemplo la frecuencia viene determinada en hercios). Una vez creado el proyecto el siguiente paso consiste en a˜nadir el c´odigo fuente de las librer´ıas, y configurar los includes. Para esto ´ultimo daremos ✭✭click✮✮ derecho sobre el proyecto y buscaremos el men´u de propiedades. Una vez dentro del mismo deberemos buscar el apartado ✭✭C/C++ Build, Settings✮✮ y en el apartado ✭✭AVR Compiler✮✮ ir a la pesta˜na de ✭✭Directories✮✮ y a˜nadir el directorio: <directorioArduino>/hardware/arduino/avr/cores/arduino. Este directorio contiene todos los ✭✭headers✮✮ de las librer´ıas. Por otro lado tenemos que a˜nadir el ✭✭header✮✮ que utiliza el entorno Arduino para referirse a sus pines. Este fichero como habr´as podido adivinar var´ıa en funci´on del microcontrolador. El fichero se puede encontrar dentro de <directorioSDK>/hardware/arduino/variants luego selecciona el que necesites, por ejemplo, para el microcontrolador ATmega328 deber´ıamos utilizar el mapeado standard. Hay que hacer exactamente lo mismo con el otro apartado llamado ✭✭AVR C++ Compiler✮✮. Ahora que ya tenemos todas las referencias configuradas, el siguiente paso consiste en importar el c´odigo fuente de las librer´ıas, para ello damos bot´on derecho sobre el proyecto y seleccionamos la opci´on de ✭✭Import, File System✮✮. En el cuadro de b´usqueda hay que ingresar el directorio: <directorioArduino>/hardware/arduino/avr/cores/arduino. Una vez dentro, selecciona todos los archivos (.cpp y .h) menos el archivo main.cpp. Por ´ultimo ya solo queda compilar el proyecto, para ello cruzamos los dedos y damos bot´on derecho ✭✭Build Project✮✮. Si todo va bien ya tendremos las librer´ıas de Arduino compiladas.
    • 124. C.6. CREANDO EL PROYECTO FINAL 124 Figura C.4: Configuraci´on de las librer´ıas C.6. Creando el proyecto final Ahora que tenemos compilado el conjunto de librer´ıas de Arduino para el microcontrolador que estamos utilizando, ya podemos crear un proyecto tal y como lo har´ıamos en el IDE oficial de Arduino. Para crear un proyecto en primer lugar accedemos al men´u: ✭✭New, C++ Project✮✮ y en tipo de proyecto ponemos: ✭✭AVR Cross Target Application✮✮ tal y como en la Secci´on C.5. El nombre del proyecto en este caso no es relevante. Como este proyecto s´ı que pasar´a por la fase de ✭✭debug✮✮ y de ✭✭release✮✮ dejaremos habilitadas ambas configuraciones. El siguiente paso consiste en a˜nadir los mismos directorios que en la Secci´on C.5 con el objetivo que se pueda referenciar a los ficheros de cabecera del ✭✭core✮✮. Adem´as, hay que a˜nadir al propio proyecto en la lista de directorios. Para ello a˜nadir la cadena ✭✭${workspace loc:/${ProjName}}✮✮ , tanto en ✭✭AVR Compiler✮✮ como en ✭✭AVR C++ compiler✮✮, que indica al compilador que compruebe los ficheros de cabecera de este mismo proyecto. Una vez que tenemos las tres direcciones completadas podemos proceder a enlazar este proyecto con el ✭✭core✮✮. Para enlazar los c´odigo objeto, el primer paso es ir al men´u del linker llamado ✭✭AVR C++ Linker✮✮ y en el apartado ✭✭General✮✮ sustituir la cadena del cuadro de texto ✭✭Command line pattern✮✮ por la siguiente ✭✭${COMMAND} -s -Os ${OUTPUT FLAG}${OUTPUT PREFIX} ${OUTPUT} ${INPUTS} -lm ${FLAGS}✮✮, por ´ultimo en la apartado ✭✭libraries✮✮ tenemos que indicar d´onde se encuentra el fichero con la librer´ıa, as´ı como el nombre de la misma. En nuestro caso el nombre era ✭✭ArduinoCore✮✮ y el ejecutable se puede encontrar en la carpeta release del proyecto ✭✭ArduinoCore✮✮. Para completar este paso por tanto, en el primer cuadro (✭✭libraries -l✮✮ debemos poner ArduinoCore y en el cuadro inferior con el nombre ✭✭libraries path -L✮✮ la siguiente cadena que es relativa al workspace: ✭✭${workspace loc:/ArduinoCore/Release}✮✮ el aspecto final deber´ıa ser parecido al mostrado en la Figura C.4.
    • 125. C.7. SUBIENDO EL PROYECTO A NUESTRO ARDUINO 125 Una vez configuradas las librer´ıas deberemos ✭✭decir✮✮ a eclipse que genere el ✭✭.hex✮✮ para el Arduino. Para ello hay que ir a las propiedades de AVR dentro del proyecto luego ✭✭C/C++ Build, Settings, Additional Tools in Toolchain✮✮ y seleccionar la opci´on ✭✭Generate HEX file for flash memory✮✮. C.7. Subiendo el proyecto a nuestro Arduino Para ✭✭subir✮✮ el ✭✭.hex✮✮ generado para nuestro proyecto a la memoria flash del Arduino, lo primero que tenemos que hacer es configurar la herramienta ✭✭avrdude✮✮ que ser´a el programador software encargado de realizar la comunicaci´on con el Arduino para subir el ✭✭.hex✮✮ al mismo. ✭✭avrdude✮✮ utiliza un fichero de configuraci´on llamado ✭✭avrconf✮✮ para saber los diferentes tipos de programadores hardware con los que cuenta as´ı como las configuraciones que debe realizar para comunicarse con cada uno de ellos. Para configurar este archivo en ✭✭avrdude✮✮ tenemos que ir a las preferencias generales de ✭✭eclipse✮✮ y buscar el men´u ✭✭AVR, AVRdude✮✮. Una vez en el men´u, el siguiente paso consiste en marcar la casilla Use custom configuration file for AVRDude y buscar el fichero ✭✭avrconf✮✮ en: <directorioSDK>/hardware/tools/avr/etc/avrdude.conf. Ahora que tenemos preparado el fichero ✭✭avrconf✮✮ lo siguiente consiste en configurar las propiedades del proyecto para indicar qu´e tipo de programador ✭✭hardware✮✮ utilizaremos y que acciones debe realizar ✭✭avrdude✮✮ para programar el Arduino. El primer paso consiste en ir a las propiedades del proyecto y buscar el men´u ✭✭AVR, AVRDude✮✮. Ahora crearemos una nueva configuraci´on dando al bot´on ✭✭new✮✮ de la pesta˜na ✭✭Programmer✮✮. El nombre de la configuraci´on puede ser el que desees, se recomienda utilizar el nombre de tu placa as´ı te ser´a m´as sencillo tener todo ordenado. En ✭✭programmer hardware✮✮ deberemos buscar ✭✭Wiring✮✮. En el cuadro de texto llamado ✭✭Override default port✮✮ deberemos poner el puerto en el cual se encuentra conectado nuestro Arduino. En el caso de GNU/Linux este suele ser /dev/ttyACMX. En Windows el puerto COM deber´a escribirse de la siguiente manera ✭✭//./COMX✮✮. En cuanto a la velocidad en baudios variar´a en funci´on del ✭✭bootloader✮✮ y de la placa, los valores t´ıpicos son 57600 y 115200. El aspecto de esta ventana de configuraci´on deber´ıa ser similar a la Figura C.5. Con todo configurado ya solo queda crear un archivo ✭✭main.cpp✮✮ en nuestro proyecto con el esquema mostrado en Cod. C.1. Como puedes observar, aqu´ı se puede apreciar como cuando creamos un sketch en el IDE oficial de Arduino realmente estamos creando las dos funciones principales como son setup y loop pero nos despreocupamos de iniciar la plataforma (init()) y de asegurarnos de que el programa nunca finaliza (bucle while). Una vez guardado el programa tendremos que compilar el mismo dando bot´on derecho sobre el proyecto y sobre la opci´on ✭✭Build Project✮✮. Si todo est´a bien se crear´a una carpeta llamada ✭✭Debug✮✮ donde podremos encontrar un fichero llamado ✭✭nombreDelProyecto.hex✮✮. Ahora solo nos queda subirlo a nuestra placa, para ello pulsamos bot´on derecho sobre el proyecto y buscamos ✭✭AVR, Upload Project to Target Device✮✮.
    • 126. C.7. SUBIENDO EL PROYECTO A NUESTRO ARDUINO 126 Figura C.5: Configuraci´on de AVRdude 1 # include < Arduino .h > 2 void setup () { 3 // config 4 } 5 void loop () 6 { 7 // code 8 } 9 int main ( void ) { 10 init () ; 11 setup () ; 12 while ( true ) { 13 loop () ; 14 } 15 } C´odigo C.1: Esqueleto de programa
    • 127. WARRANTY NO WARRANTY There is no warranty for this work. Except when otherwise stated in writing, the Copyright Holder provides the work ‘as is’, without warranty of any kind. The entire risk as to the quality and performance of the Work is with you. The Copyright Holder, or any author named in the components of the work, or any other party who may distribute and/or modify the Work as permitted above, be liable to you for damages arising out of any use of the work (including, but not limited to, loss of data, data being rendered inaccurate, or losses sustained by anyone as a result of any failure of the Work to operate with any other programs), even if the Copyright Holder or said author or said other party has been advised of the possibility of such damages. 127


    • Previous
    • Next
    • f Fullscreen
    • esc Exit Fullscreen