current maintainers of this work are jos e antonio de la ...queremos expresar nuestro mas sincero...

127

Upload: others

Post on 04-Mar-2020

2 views

Category:

Documents


0 download

TRANSCRIPT

Current maintainers of this work are Jose Antonio de la Torre las Heras and Julio Daniel DondoGazzano.

Reservados todos los derechos. No se permite la reproduccion total o parcial de esta obra, ni suincorporacion a un sistema informatico, ni su transmision en cualquier forma o por cualquier medio(electronico, mecanico, fotocopia, grabacion u otros) sin autorizacion previa y por escrito de lostitulares del copyright. La infraccion de dichos derechos puede constituir un delito contra la propiedadintelectual.

First edition: Apr 2016

Digital editionThis book includes illustrations and index.ISBN 978-84-608-7339-6

©Jose Antonio de la Torre las Heras, 2016©Julio Daniel Dondo Gazzano, 2016All rights reserved.

Queremos expresar nuestro mas sincero agradecimiento al grupo de investigacion ARCO, de la Es-cuela Superior de Informatica de la Universidad de Castilla-La Mancha en Ciudad Real, por su apoyoen la elaboracion de este libro.

Los autores.

A Juan Santiago, Ambar, Bruno, Gabriel, Laureano y Franca...

Julio.

A mi familia por su apoyo incondicional y por creer en mi desde el primer momento. En especial amis padres, cuyos consejos y compresion han sido un apoyo fundamental en el desarrollo tanto de mivida profesional como personal. Agradecer tambien a Julio Daniel Dondo y al grupo de investigacionARCO la confianza depositada en mı.

Jose Antonio de la Torre las Heras.

6

PREFACIO

El motivo de este libro es dotar al lector de los conocimientos necesarios parapoder disenar sus propios circuitos y aportar soluciones para automatizar y controlardiferentes procesos tanto domesticos, para domotica, hobbies, etc., ası como tambienprocesos industriales.Para ello, adoptaremos una metodologıa incremental y lo mas practica posible, em-pezando desde lo basico como es encender un LED, hasta automatizar una cadena demontaje y gestionar la misma con diferentes tecnologıas apoyandonos en el ecosistemaArduino.

7

8

INDICE GENERAL

1. ¿Que es Arduino? 131.1. Elementos de Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

2. Primeros pasos en Arduino 192.1. Instalando el entorno Arduino . . . . . . . . . . . . . . . . . . . . . . . 19

2.1.1. GNU/Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.1.2. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

2.2. Probando nuestra placa Arduino . . . . . . . . . . . . . . . . . . . . . 202.2.1. Conexion y configuracion de drivers . . . . . . . . . . . . . . . 202.2.2. Primer programa . . . . . . . . . . . . . . . . . . . . . . . . . . 21

3. Manejando entradas/salidas 253.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253.2. Utilizando los pines de salida . . . . . . . . . . . . . . . . . . . . . . . 25

3.2.1. Ejemplo 1: Encendiendo un LED . . . . . . . . . . . . . . . . . 263.3. Utilizando los pines de entrada . . . . . . . . . . . . . . . . . . . . . . 30

3.3.1. Ejemplo 2: Utilizacion de un boton o interruptor . . . . . . . . 323.3.2. Ejemplo 3: Leyendo temperatura y senales analogicas . . . . . 36

4. Comunicaciones 434.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 434.2. Comunicacion serie mediante USART . . . . . . . . . . . . . . . . . . 43

4.2.1. Ejemplo 1: Hola mundo por Serie . . . . . . . . . . . . . . . . . 454.2.2. Ejemplo 2: Recibiendo informacion . . . . . . . . . . . . . . . . 494.2.3. Ejemplo 3: Comunicacion entre Arduinos . . . . . . . . . . . . 52

4.3. Comunicacion I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584.3.1. Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 584.3.2. Protocolo I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 594.3.3. Ejemplo 1: Hola mundo mediante I2C . . . . . . . . . . . . . . 604.3.4. Ejemplo 2: Obteniendo datos de un IMU . . . . . . . . . . . . 64

4.4. Protocolo SPI (Serial Peripheral Interface . . . . . . . . . . . . . . . . 67

9

INDICE GENERAL 10

5. Interrupciones 735.1. Interrupciones en el ATmega328 . . . . . . . . . . . . . . . . . . . . . 745.2. Manipulacion software . . . . . . . . . . . . . . . . . . . . . . . . . . . 74

5.2.1. Librerıa avr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 755.2.2. Librerıa Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . 775.2.3. Consideraciones importantes . . . . . . . . . . . . . . . . . . . 77

5.3. Ejemplo 1: Primera rutina de interrupcion . . . . . . . . . . . . . . . . 785.3.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 785.3.2. Codigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78

5.4. Ejemplo 2: Midiendo distancias . . . . . . . . . . . . . . . . . . . . . . 795.4.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 805.4.2. Codigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81

6. Multitasking y Timers 836.1. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83

6.1.1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 846.1.2. Modos de funcionamiento . . . . . . . . . . . . . . . . . . . . . 856.1.3. Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . 87

6.2. Multitasking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 966.2.1. Encendiendo y apagando un led de manera profesional . . . . . 986.2.2. Encendiendo y apagando un led de manera mas profesional . . 100

A. Construyendo nuestro propio Arduino 103A.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103A.2. Componentes necesarios . . . . . . . . . . . . . . . . . . . . . . . . . . 103A.3. Ensamblado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104A.4. Programacion del Bootloader . . . . . . . . . . . . . . . . . . . . . . . 107

B. Manipulacion de registros 111B.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111B.2. ¿Que es un registro? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111B.3. Operaciones con registros . . . . . . . . . . . . . . . . . . . . . . . . . 113

B.3.1. Activar un bit . . . . . . . . . . . . . . . . . . . . . . . . . . . 113B.3.2. ORing, activacion de un bit . . . . . . . . . . . . . . . . . . . . 114B.3.3. Bit Shifting, movimiento de bits . . . . . . . . . . . . . . . . . 114B.3.4. ANDing, desactivando bits . . . . . . . . . . . . . . . . . . . . 116

C. Entorno Eclipse con Arduino 119C.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119C.2. Que es Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119C.3. Instalacion del entorno . . . . . . . . . . . . . . . . . . . . . . . . . . . 120C.4. Configuracion del entorno . . . . . . . . . . . . . . . . . . . . . . . . . 121C.5. Creando el proyecto: ArduinoCore . . . . . . . . . . . . . . . . . . . . 122C.6. Creando el proyecto final . . . . . . . . . . . . . . . . . . . . . . . . . 124C.7. Subiendo el proyecto a nuestro Arduino . . . . . . . . . . . . . . . . . 125

INDICE DE FIGURAS

1.1. Placa Arduino Mega 2560 . . . . . . . . . . . . . . . . . . . . . . . . . 14

2.1. Instalador Arduino para Windows . . . . . . . . . . . . . . . . . . . . 202.2. Editor Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.3. Led y puerto 13 en Arduino Mega 2560 . . . . . . . . . . . . . . . . . 23

3.1. Flujo normal de ejecucion para sistemas de control . . . . . . . . . . . 263.2. Composicion de un LED . . . . . . . . . . . . . . . . . . . . . . . . . . 273.3. Ejemplo 1: Esquema de montaje . . . . . . . . . . . . . . . . . . . . . 283.4. Ejemplo 1: Protoboard, esquema de montaje . . . . . . . . . . . . . . 283.5. Ejemplo 1: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 293.6. Osciloscopio digital . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323.7. Frecuencımetro digital . . . . . . . . . . . . . . . . . . . . . . . . . . . 333.8. Senal digital vs Senal analogica . . . . . . . . . . . . . . . . . . . . . . 333.9. Interruptor SPST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.10. Rebote de un boton y valores obtenidos . . . . . . . . . . . . . . . . . 343.11. Modulo boton Grove . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343.12. Ejemplo 2: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 353.13. Ejemplo 3: Diagrama de flujo . . . . . . . . . . . . . . . . . . . . . . . 40

4.1. Trama con formato 8N1 . . . . . . . . . . . . . . . . . . . . . . . . . . 444.2. Conexion entre dispositivos UART con control de flujo . . . . . . . . . 454.3. Representacion de comunicacion serie . . . . . . . . . . . . . . . . . . . 454.4. Ejemplo 1 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 474.5. Ejemplo 2 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 504.6. Ejemplo 3 - Comunicaciones: Diagrama de flujo . . . . . . . . . . . . . 554.7. Esquema de comunicacion I2C . . . . . . . . . . . . . . . . . . . . . . 594.8. Trama de direccion I2C . . . . . . . . . . . . . . . . . . . . . . . . . . 604.9. Trama de datos I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 614.10. Sensor MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 644.11. Conexiones MPU6050 . . . . . . . . . . . . . . . . . . . . . . . . . . . 65

11

INDICE DE FIGURAS 12

4.12. Pines SPI en el proc. ATmega328 . . . . . . . . . . . . . . . . . . . . . 684.13. Contenidos del registro de entrada del DAC . . . . . . . . . . . . . . . 694.14. Forma de onda obtenida conversion D/A . . . . . . . . . . . . . . . . . 70

5.1. Ejemplo de indireccion . . . . . . . . . . . . . . . . . . . . . . . . . . . 745.2. Sensor emisor/receptor ultrasonidos . . . . . . . . . . . . . . . . . . . 80

6.1. Diagrama TIMER 8 bits ATmega328 . . . . . . . . . . . . . . . . . . . 846.2. Modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866.3. Modo Fast PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 866.4. Registro TCCR1A . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 876.5. Bits de configuracion del presclarer . . . . . . . . . . . . . . . . . . . . 906.6. Registro TCCR1B . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 906.7. Bits para configuracion del pin de salida . . . . . . . . . . . . . . . . . 916.8. Configuracion modo CTC . . . . . . . . . . . . . . . . . . . . . . . . . 926.9. Diagrama de bloques del sistema de control . . . . . . . . . . . . . . . 946.10. Servo de Grove-Starter Kit for Arduino . . . . . . . . . . . . . . . . . 946.11. Diagrama de tiempos de un servo . . . . . . . . . . . . . . . . . . . . . 956.12. Ejemplo de programa en ladder . . . . . . . . . . . . . . . . . . . . . . 97

A.1. Protoboard de referencia . . . . . . . . . . . . . . . . . . . . . . . . . . 105A.2. Esquema de conexiones . . . . . . . . . . . . . . . . . . . . . . . . . . 105A.3. Patillaje ATmega328 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106A.4. Conectores ICSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108A.5. Captura de pantalla del Arduino IDE . . . . . . . . . . . . . . . . . . 109

B.1. Jerarquıa de memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . 112B.2. Registro de 8 bits (byte) como una ((caja)) . . . . . . . . . . . . . . . . 113B.3. Operacion: 1 << 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115B.4. Activacion de pin 3 sobre registro previamente configurado . . . . . . 115B.5. Configuracion del pin 3 como entrada sobre registro previamente con-

figurado (procedimiento erroneo) . . . . . . . . . . . . . . . . . . . . . 116B.6. Configuracion del pin 3 como entrada sobre registro previamente con-

figurado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

C.1. Pantalla inicial de Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . 120C.2. Descarga del plugin AVR . . . . . . . . . . . . . . . . . . . . . . . . . 122C.3. Configuracion de ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . 123C.4. Configuracion de las librerıas . . . . . . . . . . . . . . . . . . . . . . . 124C.5. Configuracion de AVRdude . . . . . . . . . . . . . . . . . . . . . . . . 126

CAPITULO 1

¿QUE ES ARDUINO?

En este libro vamos a describir una serie de disenos y desarrollos practicos paraautomatizar el control de sistemas utilizando el entorno Arduino. El entorno Arduinoes una plataforma electronica de desarrollo de codigo abierto (open source) que ofre-ce un conjunto de herramientas tanto software como hardware que nos permitiranrealizar nuestros prototipos de una manera rapida, sencilla y segura.

Esta formado principalmente por una placa de desarrollo, equipada con un mi-crocontrolador AVR, puertos de conexion de entrada y salida, mas los elementosnecesarios para programar el microcontrolador, y un ambiente de desarrollo softwarecon una serie de librerıas para construir nuestros disenos.

1.1. Elementos de Arduino

Podemos dividir el entorno Arduino en dos partes bien diferenciadas, por un ladotenemos un conjunto de soluciones hardware y por otro lado una suite de desarrollosoftware.

Hardware

• Placa de desarrollo Arduino: Desde que se lanzo el primer Arduinoal mercado en 2005 se han disenado numerosas placas de desarrollo pordiferentes empresas de ensamblado, tales como Funduino, FreeDuino. . .

Cada placa esta compuesta normalmente por un microcontrolador de lamarca Atmel. Dependiendo de la gama, la placa dispondra de un modelo demicrocontrolador u otro. En la figura (Figura 1.1) se muestra como ejemploel Arduino Mega 2560 que cuenta con un microcontrolador ATmega2560de 8 bits con una velocidad de 16 MHz ademas de 86 pines de entradasalida y 5 puertos de comunicacion SPI.

Las placas Arduino son ((libres)) y por lo tanto en Internet se pueden encon-trar los esquematicos de cada placa e incluso si quieres puedes crearte la

13

1.1. ELEMENTOS DE ARDUINO 14

Figura 1.1: Placa Arduino Mega 2560

tuya propia o anadir funcionalidades a la misma. Diferentes placas de desa-rrollo pueden verse en la web oficial de Arduino: http://www.arduino.cc/en/Main/Products .

Esta lista es solo una pequena seleccion de las numerosas placas que handesarrollado en Arduino, si quieres ver la lista completa accede a la weboficial de Arduino: http://www.arduino.cc/en/Main/Products .

• Tarjetas (Shields) de expansion: Unas de las caracterısticas que hanhecho que Arduino gane terreno entre otras placas de desarrollo son elbajo precio y la capacidad de expansion que estas placas tienen. En elmercado existen diferentes tipos de tarjetas de expansion para componentesvarios, como controladores de motores, modulos de comunicacion, etc. En lapagina de Arduino se pueden ver algunos shields, aunque existen numerosastiendas como Adafruit 1 que nos ofrecen una variedad mayor.

• Modulos conectar y listo: Ademas de las shields de expansion, Arduinocuenta con un gran numero de modulos del tipo ((conectar y listo)). La dife-rencia con los shields es que estos ultimos unicamente son interfaces paradiferentes sensores y actuadores de modo que sea mas sencillo utilizarlosen nuestros proyectos. Por ejemplo existen modulos conectar y listo parasensores de temperatura, humedad, luz, botones, potenciometros, LEDs,etc.

Software

• Arduino IDE: Ademas de las placas de desarrollo, Arduino proporcionaun IDE (Entorno de Desarrollo Integrado) basado en processing con el quepodremos disenar nuestros proyectos. Este IDE consta de un editor de tex-to para escribir nuestros programas, un area de mensajes, una consola decomandos y una barra de herramientas con diferentes menus. Este entorno

1http://www.adafruit.com/category/17

1.1. ELEMENTOS DE ARDUINO 15

Tip

oP

roce

sad

orF

lash

EE

PR

OM

Nu

mer

od

ep

uer

tos

Com

un

icaci

on

es

Ard

uin

oM

ega

AT

meg

a128

0(1

6MH

z)12

8KB

4K

B54

dig

itale

s,15

PW

M,

16

an

alo

gi-

cos

SP

I,I2

C,

Se-

rial(

x2)

Ard

uin

oM

ega

2560

AT

meg

a256

0(1

6MH

z)25

6KB

4K

B54

dig

itale

s,15

PW

M,

16

an

alo

gi-

cos

TW

I,S

PI,

Se-

rial(

x3)

Ard

uin

oB

oard

Du

eA

T91

SA

M3X

8E(8

4MH

z)51

2K

B51

dig

itale

s,12

PW

M,

12

an

alo

gi-

cos

TW

I(x2),

Se-

rial(

x4),

SP

I,C

AN

Ard

uin

oY

una

AT

meg

a32u

4(1

6MH

z)32

KB

1K

B20

dig

itale

s,7

PW

M,

12

an

alo

gi-

cos

TW

I,S

eria

l,SP

I,S

eria

l,W

iFi

Ard

uin

oM

icro

AT

meg

a32u

4(1

6MH

z)32

KB

1K

B20

dig

itale

s,7

PW

M,

12

an

alo

gi-

cos

SP

I,S

eria

l,I2

C

Ard

uin

oU

no

AT

meg

a328

(16M

Hz)

32K

B1K

B14

dig

itale

s,6

PW

M,

6an

alo

gi-

cos

I2C

,S

PI,

Ser

ial

Ard

uin

oL

eon

ard

oA

Tm

ega3

2u4

(16M

Hz)

32K

B1K

B20

dig

itale

s,7

PW

M,

12

an

alo

gi-

cos

I2C

,S

PI,

TW

I,S

e-ri

al

aA

rdu

ino

Yu

nes

liger

am

ente

dif

eren

tea

las

dem

as

pla

cas,

ad

emas

del

mic

roco

ntr

ola

dor

AT

meg

a32u

4p

ose

eu

np

roce

sad

or

Ath

eros

AR

9331

elcu

al

sop

ort

aO

pen

Wrt

,u

na

“d

istr

ibu

cion

”L

inux

para

loco

mu

nic

aci

on

Eth

ern

ety

WiF

i

1.1. ELEMENTOS DE ARDUINO 16

se conecta a la placa Arduino para programarla con el codigo que hemosdesarrollado y para comunicarse con ella. El codigo desarrollado en el Ar-duino IDE se denomina sketch y es la unidad de codigo que es cargado yejecutado en una placa Arduino.

• Librerıa ((Arduino.h)): Arduino ha conseguido minimizar la curva deaprendizaje del lenguaje de programacion C++ mediante una librerıa quenos evita lidiar al principio con problemas como el uso de operadoresde bits, configuracion de registros del microcontrolador, etc. La librerıa((Arduino.h))2 nos ofrece una interface de alto nivel que nos provee defuncionalidad extra con la cual realizar todas las tareas en nuestro mi-crocontrolador de una manera muy sencilla y descriptiva. Por ejemplo, siquisieramos configurar un GPIO(Pin de Entrada Salida de uso General)para que actue como pin de salida, unicamente tendrıamos que invocar a lafuncion pinMode(PIN,OUTPUT). A lo largo de este libro iremos utilizan-do esta librerıa en todos nuestros proyectos y poco a poco, segun vayamosavanzando, veremos la forma equivalente de hacer las mismas operacionesen C++ ((puro)).

• Atmel Studio: Aunque no es un producto de Arduino conviene nombrarlodebido a la potencia que tiene esta herramienta. Atmel Studio es un IDEprofesional para los microcontroladores del fabricante Atmel. Al contrarioque el entorno de Arduino, Atmel Studio es mas complejo y unicamente estadisponible para la plataforma Windows, pero tiene muchas mas utilidadescomo debugger, gestor de proyectos, etc.

• Arduino Studio: Arduino Studio es el que pretende ser el nuevo entornode Arduino. Al contrario del principal IDE de Arduino, este se basa enuna arquitectura modular y no monolıtica como ası lo hacıa su antecesor.Arduino Studio aprovecha las capacidades de ((Adobe Brackets)). Actual-mente se encuentra en version alpha, pero ya cuenta con caracterısticascomo:

◦ Sistema basado en la nube.

◦ Soportado por las principales plataformas.

◦ Escrito de cero en Javascript y Node.js.

◦ Interface de usuario amigable.

◦ Autocompletado de codigo en tiempo real y documentacion en vivo.

◦ Debugger para el M0 Pro.

• Toolchain AVR: En los libros de texto normalmente no se habla de estecomponente pero es sin duda el corazon de Arduino.Como hemos dicho anteriormente las placas de desarrollo Arduino estanformadas por un microcontrolador de la marca Atmel3, normalmente unAVR, por lo que podremos utilizar todas las herramientas que nos pro-porciona Atmel. Por ejemplo, Atmel posee un compilador libre para GNUllamado avr-g++, el cual es invocado por el IDE Arduino para compilarel codigo C++ de nuestro Sketch. Ademas, una vez que hemos compilado

2Aunque ((Arduino.h)) es la librerıa general, en realidad Arduino se apoya en mas librerıas comolas indicadas en https://www.arduino.cc/en/Reference/Libraries

3Atmel es la empresa desarrolladora de los microcontroladores http://www.Atmel.com

1.1. ELEMENTOS DE ARDUINO 17

el programa, el toolchain nos permite subir el codigo maquina a nuestraplaca por medio de avrdude, tambien libre 4.

El uso de este toolchain nos permite ((saltarnos)) la capa de abstraccionque nos proporciona Arduino y programar a mas bajo nivel, lo que a suvez nos permitira tener una vision mas detallada de la programacion demicrocontroladores.

4El toolchain de AVR puede ser descargado de forma gratuita desde http://www.nongnu.org/

avr-libc

1.1. ELEMENTOS DE ARDUINO 18

CAPITULO 2

PRIMEROS PASOS EN ARDUINO

2.1. Instalando el entorno Arduino

El entorno de desarrollo Arduino esta programado en Java por lo que es multi-plataforma. El entorno lo podemos encontrar en la pagina oficial de Arduino (http://www.arduino.org/en/Main/Software), dependiendo del sistema operativo se de-bera descargar una version u otra. El lector puede preguntarse por que existen variasversiones si el entorno esta basado en Java y en ultima instancia en la maquinavirtual de Java (JVM), esto ocurre porque junto al entorno nos descargamos el tool-chain AVR mencionado en la Seccion 1.1 junto con los drivers de AVR por lo quedependiendo del sistema operativo tendremos que instalar unos drivers u otros. Enla pagina de la comunidad de Arduino llamada Arduino Srl (Smart Projects Srl)(http://www.arduino.org/software)es posible encontrar ademas del Arduino IDE,el entorno Arduino Studio que es un ambiente de desarrollo open source basado en eleditor multiplataforma Brackets de Adobe.

2.1.1. GNU/Linux

Los usuarios de GNU/Linux pueden encontrar el entorno Arduino en sus gestoresde paquetes, por ejemplo, si tenemos una distribucion ((Debian)), para instalar todoel entorno unicamente tendremos que poner los siguientes comandos en la terminalcomo ((superusuario)):

apt -get install arduino arduino -core

Es importante anadir a nuestro usuario a los grupos: uucp, lock y dialout, para podermanejar los puertos series y los puertos USB de nuestro sistema GNU/Linux. Paraanadir nuestro usuario a los grupos unicamente ingresaremos el siguiente comando:

gpasswd -a $USER uucpgpasswd -a $USER lock

Con esto ya tendremos nuestro entorno listo.

19

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 arqui-tectura, tendremos que ejecutar el ((.exe)) y nos aparecera una ventana como la de laFigura 2.1 en donde se nos ira guiando para completar la instalacion.

2.2. Probando nuestra placa Arduino

2.2.1. Conexion y configuracion de drivers

Una vez que tenemos instalados los drivers de la placa y el entorno de desarro-llo Arduino, ya podemos probar que nuestro entorno de desarrollo este funcionandocorrectamente con un simple ejemplo de prueba. Lo primero que debemos hacer es co-nectar nuestra placa al ordenador mediante un cable USB A/B. Con este mismo cablepodremos alimentar nuestro Arduino, gracias a los 5V que nos proporciona nuestroordenador por el puerto USB.

Una vez conectado el Arduino en entornos Windows nos aparecera un mensajemostrando el proceso de configuracion y busqueda de drivers, Windows nos informarasi todo va bien y ya estaremos en disposicion de ejecutar nuestro IDE.

En los entornos GNU/Linux podremos saber si nuestro Arduino ha sido reconocidocorrectamente mediante el comando:

dmesg | tail

Este comando en realidad se compone de dos ordenes enlazadas mediante una tuberıao Pipe. La primera orden indica al Kernel que nos informe de todo lo sucedido desdeel arranque, mientras que la orden tail nos permite obtener las ultimas lıneas delregistro.

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 Pro-duct: Arduino XXX donde XXX puede variar en funcion de la placa que tengamos,por ejemplo, para una placa UNO aparecera Product: Arduino UNO.

2.2.2. Primer programa

Nuestro primer programa unicamente nos servira para comprobar que durantetodo el Workflow no se genera ningun error, es decir, podemos programar, compilary ((subir)) sketchs sin ningun problema.

Lo primero que tenemos que hacer es abrir el IDE, una vez que lo tengamos abiertoiremos a: “Archivo, Nuevo” con lo que se nos abrira un editor como el de la Figura 2.2.

A continuacion, copia el codigo Cod. 2.1Este 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 mas basica de dos

funciones: una funcion setup(), donde las variables y los pines a utilizar se declaran e

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 }

Codigo 2.1: Primer ejemplo

inicializan y una funcion loop() donde se define el codigo a implementar. Es importanterecordar que los pines se pueden configurar como Entradas o Salidas dependiendo deluso que se le den. En este caso es necesario definir al pin 13 como de salida (OUTPUT ),como esta indicado en la lınea 3 del codigo. Seguidamente en las lıneas 7 y 9 alternamosel estado del pin pasandolo de estado alto (HIGH) a estado bajo (LOW). La funciondelay() retarda la ejecucion de la instruccion siguiente en la cantidad de milisegundosindicado por el valor entre parentesis, en nuestro caso 1000 ms.

Para ver el funcionamiento de este diseno el lector debera conectar un LED entreel pin 13 y tierra. Generalmente es necesario conectar una resistencia de 470 Ohmspara limitar la corriente y evitar que el LED se queme. En este caso no hara falta yaque este pin ya tiene una resistencia incorporada.

El pin y el led de prueba estan senalados en la Figura 2.3.Ahora unicamente nos resta seleccionar el modelo de placa que estemos usando,

compilar el codigo y subirlo a nuestra placa de desarrollo. Para ello iremos a la pes-tana: “Herramientas, Tarjeta” y seleccionamos nuestra placa, despues tenemos quehabilitar el puerto serie para programar el Arduino, en esta ocasion, navegaremos has-ta“Herramientas, Puerto Serial” y seleccionaremos el puerto USB de nuestra placa.Normalmente unicamente tendremos un puerto conectado, pero si dispusieramos devarios tendrıamos que ver cual corresponde a la placa segun el sistema operativo.

Si en GNU/Linux no aparece ningun puerto serial es probable que el usuario notenga los permisos adecuados. En este caso revise cada uno de los pasos mencionadosen Subseccion 2.1.1.

Una vez hecho esto vamos a compilar y subir el codigo a la placa, para ello vamos ala pestana: “Sketch, Verificar/Compilar” o simplemente pulsamos ((Ctrl-R)) que haraque el IDE primero verifique la sintaxis del codigo y si no hay errores ((suba)) el mismoa la placa.

Si todo va bien podremos ver como el LED de prueba conmuta cada segundo.

2.2. PROBANDO NUESTRA PLACA ARDUINO 23

Figura 2.3: Led y puerto 13 en Arduino Mega 2560

2.2. PROBANDO NUESTRA PLACA ARDUINO 24

CAPITULO 3

MANEJANDOENTRADAS/SALIDAS

3.1. Introduccion

En este capıtulo vamos a trabajar con la placa de desarrollo Arduino enfocando-nos principalmente en el manejo de sus entradas y salidas. En la siguiente seccion deeste capıtulo (Seccion 3.2) abordaremos las diferentes tecnicas para controlar actua-dores desde Arduino. Mas adelante, en la Seccion 3.3 veremos como obtener datos delexterior, tanto analogicos como digitales.

Para simplificar en la medida de lo posible el montaje de los circuitos y centrar-nos la mayor parte del tiempo en la programacion del Arduino, utilizaremos un kitde iniciacion llamado Grove Starter Kit for Arduino de la marca Seeed el cual sepuede adquirir por Internet a un precio relativamente bajo.1. Ademas del kit Grove,utilizaremos el kit de expansion Sidekick Basic Kit for Arduino V2.2

3.2. Utilizando los pines de salida

Normalmente, a la hora de disenar cualquier sistema de control nos encontramoscon una secuencia de ejecucion como la mostrada en Figura 3.1.

Como se observa en la Figura 3.1 una parte esencial en un sistema de control sonlos actuadores, pero ¿Que es un actuador?. La definicion formal de actuador es lasiguiente:

1El kit se puede obtener desde: http://www.seeedstudio.com/depot/

Grove-Starter-Kit-for-Arduino-p-1855.html2Este kit puede se puede obtener desde: http://www.seeedstudio.com/depot/

Sidekick-Basic-Kit-for-Arduino-V2-p-1858.html?cPath=84_13

25

3.2. UTILIZANDO LOS PINES DE SALIDA 26

Figura 3.1: Flujo normal de ejecucion para sistemas de control

Actuador

Dispositivo capaz de transformar la energıa neumatica, electrica o hidraulica enla activacion de un proceso con la finalidad de generar un efecto sobre un procesoautomatizado.

Un ejemplo claro es un motor de un robot. Un motor de un robot cumple con losdos elementos principales de la definicion. Por un lado transforma la energıa electricaen un efecto (movimiento del robot) y por otro lado, este movimiento se realiza sobreun proceso automatizado (logica del robot). Al igual que el ejemplo del robot, existenotros muchos actuadores. Se deja al lector la tarea de buscar en la red mas informacionsobre los diferentes tipos.

El objetivo de esta seccion es controlar estos actuadores, de modo que podamosactuar sobre el entorno desde Arduino.

La manera mas inmediata de controlar un actuador en Arduino es mediante lospines de entrada/salida. La cantidad de pines disponibles depende del tipo de placaque se utilice. Por ejemplo, en la placa ATmega328 que ((montamos)) en la Seccion A.1tenemos un total de 23 pines que pueden ser configurados como entrada o salida.Veamos algunos ejemplos:

3.2.1. Ejemplo 1: Encendiendo un LED

En este primer ejemplo, veremos como encender un LED, aunque ya se vio demanera rapida en Subseccion 2.2.2 en esta seccion lo veremos de manera mas detallada.El LED (Figura 3.2) es un elemento notificador muy utilizado a dıa de hoy. Una de lasrazones que han llevado a la industria a utilizar estos dispositivos es el bajo consumo

3.2. UTILIZANDO LOS PINES DE SALIDA 27

Figura 3.2: Composicion de un LED

que requieren, aproximadamente entre 10 mA y 20 mA. Aunque aquı utilizaremos elLED unicamente con el proposito de notificar o avisar sobre un evento. Hoy en dıa suuso se ha extendido a otras areas como la de la iluminacion e incluso la comunicacion.

En las placas oficiales de Arduino se incorpora un LED notificador conectadoal pin 13. Este LED ya tiene una resistencia limitadora de corriente, por lo queno tendremos que preocuparnos por el consumo de corriente. En el caso de que ellector haya montado el Arduino siguiendo la guıa del Seccion A.1, entonces deberaconectar el LED y la resistencia al pin 13. Para identificar la posicion de cada unode los pines se puede consultar la hoja de datos del ATmega que se puede descargardesde el siguiente enlace: 3. Si esta utilizando una placa con otro microcontrolador,busque la hoja de datos correspondiente. En el apartado 1 del datasheet veremos losdiferentes encapsulados en los cuales el fabricante nos ofrece el microcontrolador. Elencapsulado utilizado en Seccion A.1 es el 28 PDIP (Plastic Dual In-line Package).En este encapsulado el pin 13 se encuentra en la esquina superior derecha teniendoen el norte el semicırculo de referencia. Ademas del datasheet, para saber el mapeadode pines es necesario ir al esquematico de cada placa y buscar a que pin de la librerıacorresponde cada pin fısico.4

En el caso del Arduino realizado en la Seccion A.1, el montaje debe ser parecidoal mostrado en: Figura 3.3, Figura 3.4.

Tabla de entrada/salida

Para obtener unos disenos fiables, seguros y de calidad se deben seguir procedi-mientos y normas de calidad de diseno. Por ello, aunque este ejemplo sea sencillo,seguiremos una metodologıa a la hora de afrontar el problema.

El primer paso en cualquier diseno es analizar los elementos de control en base a lasespecificaciones. 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 buenacostumbre en cualquier diseno. En la Tabla 3.1 se puede ver un ejemplo de analisis deelementos de control. En primer lugar, se indica si el elemento es de entrada o salida.En segundo lugar, se realiza una descripcion sobre el elemento, de modo que siemprese sepa la funcion del mismo. Por otro lado, se indica el nombre de la variable que se

3http://goo.gl/S3oBA24Los esquematicos se pueden encontrar en la pagina de Arduino o en el caso de una placa ensam-

blada por otro fabricante en la pagina del mismo.

3.2. UTILIZANDO LOS PINES DE SALIDA 28

Figura 3.3: Ejemplo 1: Esquema de montaje

5v-12v

16MHzCristal

Azul=TierraRojo=EntradaNaranja=5vestablesCyan=Cristal-------------------Marron=Rx(Arduino)-Tx(FTDI)Morado=Tx(Arduino)-Tx(FTDI)

Figura 3.4: Ejemplo 1: Protoboard, esquema de montaje

3.2. UTILIZANDO LOS PINES DE SALIDA 29

Cuadro 3.1: Ejemplo 1: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Salida Conmutar cada1/2 segundo

led 13

Figura 3.5: Ejemplo 1: Diagrama de flujo

utilizara en el codigo para hacer referencia a dicho elemento y por ultimo el pin fısicoal cual se conectara.

Diagrama de flujo

En base al diagrama general mostrado en la Figura 3.1 al que normalmente seadapta cualquier diseno, vamos a realizar el diagrama correspondiente para nuestrodiseno.

La herramienta utilizada para crear dicho diagrama es DIA una herramienta decodigo abierto, gratuita y que permite realizar diagramas de diversos tipos como porejemplo 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 diseno. Un mismo problemapuede tener diferente soluciones correctas.

Codigo

Ahora que tenemos toda la documentacion lista y se ha analizado el problema esel momento de realizar el diseno de la solucion y su implementacion. Para programarla placa Arduino utilizaremos el entorno de programacion instalado descrito en elCapıtulo 1. Como se adelanto en el capıtulo anterior todo programa que vaya a serejecutado en el entorno Arduino debe tener dos funciones principales y obligatorias:

setup(void): Es la primera funcion que se ejecuta tras cargar el programa. En

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 5v o LOW 0v

12 digitalWrite(led ,HIGH);

13 // delay bloquea el microcontrolador 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 }

Codigo 3.1: Ejemplo 1: Encendiendo y apagando un led

esta funcion se suelen realizar tareas como la inicializacion de variables, pines,comunicaciones, informacion de inicio al usuario, tareas de login. etc.

loop(void): Tambien llamado bucle principal del programa, se ejecuta de for-ma constante. Cada pasada por el bucle se suele llamar un ciclo de scan. Enesta funcion es donde programaremos la logica siguiendo el diagrama generalmostrado en Figura 3.1.

Es importante mantener un orden en la funcion loop. Un mal encapsulamientofuncional puede hacer que nuestro programa sea poco mantenible y finalmente fracase.

El mismo programa mostrado en Cod. 3.1 puede ser reescrito utilizando funcionesde modo que sea mucho mas sencillo de leer tal y como se muestra en: Cod. 3.2.En este nuevo codigo se utilizan dos funciones ademas de las funciones setup() yloop() basicas: la funcion read sensors() y la funcion perform actions() La funcionread sensors() es una funcion que se encarga de la lectura de los sensores para que,de acuerdo a sus valores, ejecutar las actuaciones correspondientes. En este caso esuna funcion vacıa.

La segunda contiene el codigo de encendido y apagado de los LEDs.

Como se puede observar, el codigo es muy simple y legible. Gracias a la librerıa((Arduino.h)), para configurar un pin como entrada o salida unicamente tenemos quellamar a la funcion pinMode() indicando en sus parametros tanto el pin como el modo.Por otro lado, para obtener 5V o 0V en el pin, usaremos la funcion digitalWrite().

3.3. Utilizando los pines de entrada

En la Subseccion 3.2.1 vimos como utilizar los pines de entrada/salida del AT-mega328 para informar al usuario de una accion mediante un LED o de forma mas

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 5v o LOW 0v

16 digitalWrite(led ,HIGH);

17 // delay bloquea el microcontrolador 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 }

Codigo 3.2: Ejemplo 1: Recodificacion utilizando funciones

3.3. UTILIZANDO LOS PINES DE ENTRADA 32

Figura 3.6: Osciloscopio digital

generica como interaccionar con un actuador. En esta seccion, veremos todo lo con-trario, es decir, aprenderemos como captar una accion del usuario (por ejemplo lapulsacion de un boton).

Los pines de entrada salida, como su nombre indica, se pueden utilizar comoentrada o como salida. La seleccion del modo de funcionamiento de cada pin se realizamediante unos registros hardware. En estos primeros ejemplos no se vera el manejo dedichos registros. Sin embargo, en la Seccion B.1 el lector puede estudiar como utilizardichos registros mediante operaciones de desplazamiento de bits.

A la hora de captar una accion o magnitud en un microcontrolador lo primeroque debemos preguntarnos es que tipo de magnitud necesitamos leer o captar. Deforma generica y simplificando, existen dos grandes grupos en los cuales se clasificanlas senales (Figura 3.8):

1. Senales analogicas: Las senales analogicas son continuas en el tiempo, esdecir, se puede representar mediante una funcion matematica continua. ¿Queelementos de la senal analogica se pueden medir? principalmente se analizan dosvariables: la amplitud y el periodo o frecuencia. Ejemplos de senales analogicasson el sonido, senales de radio frecuencia, senales electromagneticas.

En electronica para medir las senales analogicas se utilizan instrumentos comolos osciloscopios (ver Figura 3.6) o frecuencimetro (ver Figura 3.7). Los oscilos-copios nos permiten desde visualizar en una pantalla la forma de onda hastarealizar funciones matematicas como la FFT (transformada rapida de fourier).

2. Senales digitales: Las senales digitales son aquellas en las cuales sus valoresestan completamente discretizados. Aunque cualquier fenomeno electromagneti-co como el producido al realizar contacto en un pulsador es continuo, sus estadospueden ser discretizados en funcion del numero de bits utilizados en la conver-sion, en este caso dos estados (pulsado y no pulsado).

3.3.1. Ejemplo 2: Utilizacion de un boton o interruptor

En este ejemplo nos centraremos en las senales digitales. Mas adelante hablaremossobre las senales analogicas y veremos diferentes casos de uso.

Un boton o mejor dicho y en sentido mas amplio, un interruptor electrico es undispositivo con dos estados: en uno de estos estados (cerrado) se permite la circulacion

3.3. UTILIZANDO LOS PINES DE ENTRADA 33

Figura 3.7: Frecuencımetro digital

0000001001000110100010101100

Señalcontinua

Señaldiscreta

Tiempo

Tiempo

Figura 3.8: Senal digital vs Senal analogica

3.3. UTILIZANDO LOS PINES DE ENTRADA 34

Figura 3.9: Interruptor SPST

1 X 1 0 1 0 X 0 0 0 0 0111V

OL

VIL

VIH

VOH

Rebote

Valor lógico alto

Indeterminado

Valor lógico bajo

Figura 3.10: Rebote de un boton y valores obtenidos

de corriente y en el otro estado se impide su paso (abierto). En los circuitos electronicospuedes encontrarlo representado con diferentes sımbolos. Sin embargo, el mas comunes el que se muestra en la Figura 3.9. Existen muchos tipos de interruptores en funcionde los polos, vıas, etc, pero nosotros utilizaremos los simples, es decir, de un polo yuna vıa.

Los interruptores como cualquier dispositivo mecanico son imperfectos y tienenun desgaste por el propio uso. Aunque parezca que en un diseno esto no es necesariotenerlo en cuenta, una mala eleccion puede que haga que nuestro diseno tenga unaduracion menor a la estimada y por lo tanto, se incremente el coste de mantenimiento.Ademas, hay que tener en cuenta que los interruptores tiene un efecto de rebote,dicho efecto puede provocar que nuestro programa trabaje con valores falsos. Conel objetivo de eliminar estos valores intermedios se utilizan circuitos eliminadores derebotes (debounce) como el que se muestra en la figura Figura 3.10.

Otro metodo es realizar una maquina de estados que detecte varias lecturas con-secutivas con el mismo valor de modo que todos los valores intermedios no se tenganen cuenta. En la Figura 3.10 puedes ver una grafica que ilustra lo comentado.

Para simplificar el diseno y centrarnos en la programacion del microcontrolador,en este ejemplo utilizaremos uno de los modulos ((conectar y listo)) del kit Groove.En Figura 3.11 se puede ver el aspecto del modulo. Dicho modulo nos simplifica elmontaje dado que lleva incorporada la resistencia de pull-down anteriormente citada.

Figura 3.11: Modulo boton Grove

3.3. UTILIZANDO LOS PINES DE ENTRADA 35

Cuadro 3.2: Ejemplo 2: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Salida Led notificador depulsacion

led 13

Entrada Pulsador button 12

Figura 3.12: Ejemplo 2: Diagrama de flujo

Tabla de entrada/salida

En este ejemplo utilizaremos el codigo de la Subseccion 3.2.1 de modo que cuandoel pulsador se encuentre accionado se encendera el LED y cuando el pulsador no seencuentre pulsado el LED se apagara. Como en el ejemplo anterior, lo primero que hayque realizar en el diseno es una tabla con las entradas y salidas que utilizaremos. Comose puede ver en Tabla 3.2 tenemos dos elementos. Por un lado, el LED notificadorque 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, elcondicional. En este caso en funcion del estado de un elemento (condicion) actuaremosde una manera o de otra.

Codigo

Con el diagrama estudiado, trasladar la solucion a codigo es muy sencillo cuan-do se tiene un poco de practica. Como dijimos en la Subseccion 3.2.1 tenemos dos

3.3. UTILIZANDO LOS PINES DE ENTRADA 36

partes diferenciadas, el setup() y el loop(). En el setup() se ha anadido la configu-racion del boton como entrada mediante la funcion pinMode() con el correspondienteparametro INPUT. Un punto importante a tener en cuenta cuando configuramos unpin como entrada es la capacidad del microcontrolador para activar resistencias depull-down internas. Mediante las resistencias de pull-down internas no es necesarioque agreguemos una resistencia para evitar los rebotes. Para habilitar las resistenciasde pull-down, se debe utilizar la funcion digitalWrite() dentro del setup() y sobre unpin configurado anteriormente como entrada.

Una vez configurados los pines de entrada/salida, debemos programar la logicadel bucle. Si revisas el Cod. 3.2 creamos dos funciones dummy5: read sensors() yperform actions(). En este caso utilizaremos tambien la funcion read sensors() enla cual leeremos el valor del boton mediante la funcion digitalRead(). Esta funciondevuelve HIGH o LOW en funcion del valor del pin 5v o 0v.

Una vez que hemos leıdo el valor del pin de entrada mediante la funcion digital-Read() solo falta implementar la toma de decisiones en funcion de ese valor leıdo. Paraello usamos la funcion perform actions() que consulta sobre el valor de la variable ysi es igual a HIGH (boton pulsado) se enciende el LED, de lo contrario se apaga.

Como puedes observar tratar con senales digitales es muy sencillo. Existen sensorescomo el sensor de temperatura DHT11 que aun monitorizando una senal analogica(temperatura) son capaces de ofrecer dicha informacion como un valor digital. Masadelante veremos como se consigue esto y de que manera lo podemos utilizar en losdisenos con Arduino.

3.3.2. Ejemplo 3: Leyendo temperatura y senales analogicas

Hasta ahora hemos trabajado con senales digitales. Normalmente el entorno quenos rodea es continuo y sus efectos se manifiestan de forma analogica. Un ejemplopodrıa ser la energıa que desprendemos cuando tenemos calor o incluso el sonido queproducimos al hablar. Como puedes imaginar la necesidad de analizar y controlarestas magnitudes esta presente en el diseno con microcontroladores. En esta seccionveremos como leer la temperatura ambiente y un ejemplo de actuacion en funcion dedichos valores.

Fundamentos

En el mercado existen sensores de muchos tipos, los hay digitales como por ejemplolos sensores de paso y tambien los hay analogicos como los sensores de temperatura.Normalmente, estos sensores producen senales de un voltaje bajo. Para poder tratarcon dichas senales se suelen realizar diferentes etapas de amplificacion y procesamien-to.

Como seguramente ya sepas, los computadores y los microcontroladores unica-mente trabajan con bits, es decir, dıgitos que unicamente pueden tomar el valor 1o 0. Para convertir una senal analogica a una senal digital se emplean los llamados((Conversores Analogicos Digitales)) o de forma abreviada ADC. Si lo que queremoses producir una senal analogica a partir de una digital se utilizan los ((ConversoresDigitales Analogicos)) o de forma abreviada DAC. La teorıa que hay detras de estos

5Las funciones dummy son aquellas que no tienen ninguna utilidad pero que se incorporan alcodigo por alguna razon, normalmente por estandarizacion

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 5v

21 digitalWrite(led ,HIGH);

22 }else{

23 digitalWrite(led ,LOW);

24 }

25 }

26

27 void loop() {

28 read_sensors ();

29 perform_actions ();

30 }

Codigo 3.3: Ejemplo 2: Codigo para la utilizacion de un boton

3.3. UTILIZANDO LOS PINES DE ENTRADA 38

dispositivos es muy extensa y no es objeto de este libro el desarrollarla en profundi-dad. A continuacion se dara una breve explicacion sobre dos de los parametros clavesa la hora de identificar si nuestro microcontrolador es adecuado para una determinadasenal.

Numero de bits: El numero de bits del conversor se puede ver como la resolu-cion del mismo. Un ejemplo ilustrara de forma clara este concepto. Imaginemosque tenemos una sensor que produce valores desde 0V hasta 5V y que tenemosun ADC de 10 bits (como es el caso del ATmega328) ¿cual es la resolucionmaxima con la que se podrıa trabajar en el diseno?. El calculo es sencillo. Siel conversor codifica los valores con 10 bits, significa que puede tomar valoresentre 0 y 1023 (210 − 1) por lo tanto si dividimos los 5v (rango 5-0) entre los1024 valores (rango 0-1023) tenemos una division de 0.0049 o lo que es lo mismo4,9 mV por cada valor. De este modo si obtenemos una lectura con el valor 53,significara que el sensor ha generado 259 mV.

Velocidad: La velocidad de muestreo es la capacidad del conversor para tratarcon senales de una frecuencia determinada. Segun el teorema de muestreo, lavelocidad de muestreo debe ser como mınimo el doble del ancho de banda de lasenal de entrada.

Aunque estos dos parametros son muy importantes a la hora de seleccionar unconversor, como ya hemos dicho existen muchos parametros estaticos y dinamicosque influyen en la eleccion de los mismos.

Otro de los aspectos que debemos tener en cuenta a la hora de realizar una con-version es el valor de referencia. Como ya hemos explicado mediante el numero debits, la resolucion de nuestra conversion estara dada por el numero de bits del conver-sor pero tambien hablamos de un rango fijo de 5 V. Este rango fijo se llama voltajede referencia. ¿Para que sirve este voltaje?. Imaginemos que tenemos un sensor queunicamente 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 maximo 3.5. Como puedesobservar los voltajes de referencia varıan. La solucion planteada anteriormente deusar un voltaje de referencia de 5 voltios es valida para ambos casos pero estaremosperdiendo resolucion dado que el Arduino esta teniendo en cuenta valores que sonimposibles de alcanzar (desde 1.5 V - 5 V). Para solucionar esta situacion en Arduinoposeemos una funcion llamada analogReference() que nos permite variar el voltaje dereferencia pudiendo pasar los siguientes valores como parametro:

DEFAULT: Voltaje de referencia de 5 voltios para placas de 5V y 3.3 paraplacas de 3.3 voltios.

INTERNAL: Dependiente del microcontrolador. En los microcontroladoresATmega168 y 328 este voltaje es de 1.1, sin embargo, en los microcontroladoresATmega8 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 delArduino

3.3. UTILIZANDO LOS PINES DE ENTRADA 39

Cuadro 3.3: Ejemplo 2: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Salida Led notificador demınima temperatu-ra

led 13

Entrada Sensor de tempera-tura

temperature sensor A1

Si seleccionamos alguno de estos valores es muy importante que se realice lallamada a la funcion analogReference() antes de cualquier llamada a la funcion analo-gRead() de lo contrario podremos danar la placa.

Si el parametro pasado es EXTERNAL se debera aplicar un voltaje de entrada alpin AREF. Existen diferentes maneras de aplicar dicho voltaje. Una manera sencillay estable es un diodo zener del valor requerido. Por otro lado, podemos utilizar undivisor de tension. Hay que tener en cuenta que Arduino posee una resistencia de32 Kohms interna por lo que la formula para el divisor de tension quedara como semuestra en la Ecuacion 3.1. Teniendo en cuenta esto con una resistencia de 75 Khomsy un voltaje de entrada de 5v aproximadamente obtendrıamos un valor de referenciade 1.5V lo que nos permitirıa tener una resolucion de 1,5V/1024 = 1,5mV frente alos 5V/1024 = 4,88mV anteriores.

V Ref = V in ∗ 32K

32K + X(3.1)

Tabla de entrada/salida

Para este ejemplo utilizaremos el modulo conectar y listo de Grove termostato.Existen numerosos sensores de temperatura. Algunos como por ejemplo el DHT11 oel DHT22 permiten leer la temperatura unicamente mediante senales digitales. Eneste caso hemos decidido utilizar el modulo de Grove porque ya tiene incorporado loscondensadores de filtro necesarios y diversas protecciones. Ademas simplifica mucho eldiseno y como ya dijimos en otros ejemplos, nos permite centrarnos en la programaciondel microcontrolador.

Como siempre lo primero que debemos realizar es la tabla de entradas y salidasde tal modo que nuestro diseno este siempre documentado. En este ejemplo vamos autilizar un termostato y un LED notificador Tabla 3.3.

Diagrama de flujo

El diagrama de flujo es fundamental en cualquier diseno, muchas herramientascomo Scratch for Arduino son capaces de generar todo el codigo a partir de un buendiagrama de flujo. En este ejemplo unicamente buscamos encender un LED cuandopase de la temperatura mınima configurada. El diagrama resultante es el mostradoen la Figura 3.13

Como se puede observar en el diagrama de la Figura 3.13 hay dos procesos a lahora de captar la temperatura. El primero obtiene la temperatura en formato RAW,

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 unproceso de conversion (normalmente especificado por el fabricante) mediante el cualel valor RAW se convierte en un valor util para el usuario. En Figura 3.3.2 se veracomo realizar dicha conversion.

Codigo

Esta solucion anade a los demas ejemplos el tratamiento de la senal analogica porlo que unicamente se hara enfasis en dicha parte. En la seccion setup configuramosel pin A1 como entrada. Arduino no necesita especificar que dicho pin se comportaracomo un pin analogico, por lo que este procedimiento es exactamente igual al realizadocon el boton en Subseccion 3.3.1. En la funcion read sensors() se realiza la lectura delsensor conectado a la entrada A1 del Arduino. La funcion analogRead() devuelve unvalor entre 0 y 1023 tal y como se explico en los fundamentos de este ejercicio.

Una vez que tenemos el valor RAW hay que realizar una conversion a un valor engrados celsius. Para ello hay que utilizar una formula especificada por el fabricante enel datasheet. Cada sensor utiliza una conversion diferente por lo que este paso varıade un sensor a otro, incluso aunque el sensor capte la misma magnitud.

La funcion perform actions() unicamente se encarga de comprobar si la tempera-tura es menor al mınimo en cuyo caso activara el led.

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 }

Codigo 3.4: Ejemplo 3: Codigo de ejemplo para senales analogicas

3.3. UTILIZANDO LOS PINES DE ENTRADA 42

CAPITULO 4

COMUNICACIONES

4.1. Introduccion

Uno de los aspectos importantes a considerar en el diseno de sistemas con micro-controladores son las comunicaciones. Si bien hay aspectos teoricos y matematicos enel estudio de las comunicaciones muy importantes y que es menester conocer, en estecapıtulo siguiendo el enfoque practico, aprenderemos a utilizar estas comunicacionessin necesidad de entrar en profundidad en los aspectos puramente teoricos.

La eleccion del modo de comunicacion a utilizar depende de las necesidades denuestro diseno. Aspectos tales como protocolos de comunicacion, medios fısicos, anchode banda, atenuacion, distorsion, ente otros, deben ser considerados a la hora de elegirel modo de comunicacion entre dispositivos.

A continuacion veremos algunos modos de comunicacion que son soportados porArduino.

4.2. Comunicacion serie mediante USART

UART significa Universal Asynchronous Receiver/Transmitter, mientras que USARTsignifica Universal Synchronous/Asynchronous Receiver/Transmitter, y es un dispo-sitivo que trabaja entre dos elementos que se quieren comunicar, tomando bytes dedatos de uno de ellos (transmisor) y transmitiendolos hacia el otro (receptor) de ma-nera secuencial bit por bit a una determinada tasa de transmision. La informacionque transmiten estos bits dependera del protocolo y de la codificacion elegidos. Elprotocolo nos indica la manera de comunicarnos, esto es, la cantidad y el tamano delas tramas de datos que se envıan, los bits de paridad, codigo de error, etc., mientrasque la codificacion determina el contenido de la informacion y la manera en que losbits son ordenados para transmitirla. La tasa de transmision se mide en baudios, bit-s/seg y deben ser configurados de la misma manera tanto en el transmisor como enel receptor para que puedan entenderse.

La unidad USART es igual a la unidad UART con la diferencia que la prime-ra permite la comunicacion sıncrona y asıncrona y la ultima unicamente asıncrona.

43

4.2. COMUNICACION SERIE MEDIANTE USART 44

Data 8b Parity0b

Start1b

Stop1b

Figura 4.1: Trama con formato 8N1

Las diferencias entre la comunicacion asıncrona serie y sıncrona serie se explicaran acontinuacion.

Comunicacion asıncrona serie: Si configuramos la unidad USART en modoasıncrono tendremos que configurar diferentes parametros para que tanto emisorcomo receptor sepan cuando tienen que leer o escribir los datos. Estos parame-tros son el numero de bits de datos, de paridad y de parada, ası como tambienla velocidad en baudios. Existen numerosas configuraciones pero la mas tıpicaes la siguiente: 8N1 que significa 8 bits de datos, ninguno de paridad y unode parada. (ver Figura 4.1) En el entorno Arduino la velocidad de transmisionpuede configurarse entre diferente valores desde 300 hasta 115200 baudios.

Comunicacion sıncrona serie: La unidad USART del ATmega328 se puedeconfigurar para funcionar en modo sıncrono, sin embargo no esta soportado porla librerıa oficial de Arduino. Esto no significa que no pueda ser usado, pero siqueremos usar este modo tendremos que desarrollar nuestra propia librerıa oprogramar la configuracion de la unidad en cada uno de nuestros programas. Lacomunicacion sıncrona se caracteriza por tener una lınea de reloj1 que permiteque ambos dispositivos lean y escriban en el evento de reloj. Esto encarece lasolucion dado que necesitamos un cable mas, sin embargo, permite eliminartodos los bits de control de la comunicacion de tal modo que unicamente setrasmitiran bits con informacion util.

Se ha hablado del modulo UART, sin embargo, hay que tener en cuenta que launidad UART es un modulo hardware y no un protocolo de comunicacion. La unidadUART se encarga de obtener en paralelo y en un unico ciclo de reloj los datos delregistro de datos y los multiplexa en el tiempo segun la configuracion establecida.

Existen diversas configuraciones, simplex, duplex, full-duplex, de dos dispositivoso en bus. Sin embargo, la comunicacion serie mediante UART fue desarrollada con elobjetivo de comunicar unicamente dos dispositivos.

A continuacion veremos diferentes ejemplos de como Arduino implementa la co-municacion serie en sus dispositivos.

Una unidad UART es un modulo hardware y no un protocolo de comunicacion. Launidad UART se encarga de obtener en paralelo y en un unico ciclo de reloj los datosdel registro de datos, y los serializa en el tiempo segun la configuracion establecida.Posee ademas soporte para el control de flujo por hardware. Este control de flujo esrealizado a traves de dos conexiones RTS (Request to Send) y CTS (Clear to Send)que permite a cada lado de la comunicacion indicar al otro que esta listo para manejardatos. El cableado para la comunicacion serie asıncrona es muy sencillo y se puedever 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 encapsuladoDIP28

4.2. COMUNICACION SERIE MEDIANTE USART 45

Dispositivo A Dispositivo B

RTS

CTS CTS

RTS

Tx Tx

Rx Rx

Gnd Gnd

Figura 4.2: Conexion entre dispositivos UART con control de flujo

duplex (en ambos sentidos pero no simultaneamente), y full-duplex (en ambos sentidosal mismo tiempo), con o sin control de flujo.

Una UART se usa comunmente con los estandares de comunicacion RS-232, RS-422 o RS-485 y tiene soporte para el control de flujo mediante hardware.

Dependiendo de la implementacion fısica se utilizara un determinado nivel devoltaje u otro. El protocolo RS-232 utiliza de -3V a -15V para el nivel logico ((1)) yde +3V a +15V para el nivel logico ((0)) Sin embargo, los procolos con logica TTL oCMOS trabajan con otros niveles. Existen circuitos integrados como el MAX232 quepermiten realizar la conversion de niveles.

Figura 4.3: Representacion de comunicacion serie

En la Figura 4.3 se puede ver una representacion de lo explicado anteriormente.

4.2.1. Ejemplo 1: Hola mundo por Serie

Como ya hemos dicho, Arduino tiene soporte para la comunicacion serie, tantohardware como software. A nivel hardware dependiendo del microcontrolador tendre-mos mas o menos unidades UART. La API que proporciona Arduino para el manejo de

4.2. COMUNICACION SERIE MEDIANTE USART 46

Cuadro 4.1: Ejemplo 1: Tabla de entradas/salidas

Entrada/Salida Descripcion Nombre variable Pin

Entrada Boton button 8Entrada Rx0 RX 0Salida Tx0 Tx 1

Cuadro 4.2: Ejemplo 1: Comunicaciones

Interfaz serie

Entrada/Salida Comando Evento Descripcion

Salida Pin 8 ==HIGH

Envıa ”hola mundo”

las comunicaciones es muy sencilla de utilizar y aprenderemos a manejarla mediantelos siguientes ejemplos.

En este primer ejemplo realizaremos un ((hola mundo)) mediante el puerto serie.Cuando se pulse un boton el microcontrolador mandara 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 deentrada/salida. En el enunciado se han senalado dos de los elementos principales. Porun lado la comunicacion serie y por otro lado el boton. Es buena costumbre reservarun apartado ıntegro para las comunicaciones. En un diseno mas complejo, lo masprobable es que tengamos diferentes tipos de comunicaciones con diversos protocolos.En ese apartado, podremos definir todos los protocolos y las interfaces que proporcionanuestro diseno al ((exterior)). Volviendo a las entradas y salidas, si bien en este primerejemplo lo unico que haremos es enviar un string por el puerto Tx (pin 1) dejamos yaindicada la entrada Rx que sera utilizada en los ejemplos que siguen.

Comunicaciones

Este ejemplo es muy sencillo, por lo que no definiremos un protocolo completo, esdecir, sintaxis, semantica y temporizacion. Unicamente describiremos la comunicacionen terminos de los mensajes de salida. En la Tabla 4.2 se puede ver un ejemplo dedicha tabla.

Diagrama de flujo

Las comunicaciones se pueden disenar como eventos asıncronos y sıncronos, tododependera de como implementemos la solucion. En este caso y para no anadir com-plejidad innecesaria en este punto, modelaremos la solucion en base a los ejemplosanteriores, es decir, de forma sıncrona y secuencial.

En la Figura 4.4 puedes ver el diagrama propuesto.

4.2. COMUNICACION SERIE MEDIANTE USART 47

Figura 4.4: Ejemplo 1 - Comunicaciones: Diagrama de flujo

Codigo

En Cod. 4.1 se puede ver una posible solucion a este ejemplo. Lo primero quedebemos hacer es configurar los elementos de entrada salida y las comunicaciones.Cuando el diseno sea mas complejo, puede que sea necesario encapsular cada una deestas partes en otras subfunciones como por ejemplo init communications().

Una de las partes mas importantes de este tutorial se encuentra en la funcionSerial.begin(9600). Esta funcion forma parte de la API de Arduino para la gestion yuso del puerto serie. Como habras podido imaginar, el primer parametro se refiere a lavelocidad en baudios, recuerda que tanto receptor como emisor deben tener la mismavelocidad. Por otro lado, en la Seccion 4.2 se hablo de la configuracion de los bits deparada, datos y paridad. Estos parametros se pueden pasar como segundo argumentoa la funcion Serial.begin(). En la siguiente web se pueden ver los valores que puedetomar este parametro: http://www.arduino.cc/en/pmwiki.php?n=Serial/Begin

En la funcion perform actions() es donde se realiza la actuacion en funcion delestado del boton. Para este primer ejemplo hemos utilizado la funcion Serial.println().Esta funcion envıa al registro de datos de la unidad UART la cadena indicada comoparametro, ademas termina la cadena con los caracteres ((retorno de carro)) y ((saltode lınea)). La funcion devuelve un ((long)) que ındica el numero de bytes escritos en elpuerto serie. Es muy importante que tengas en cuenta que esta funcion no envıalos bytes de ((golpe)) por el ((cable)). Esta funcion unicamente escribe los bytes en elregistro de datos de la UART llamado, en el caso del Atmega328, UDRn.

Si no quieres que finalice la cadena con los caracteres de fin de lınea, puedes utilizarla funcion Serial.print()

4.2. COMUNICACION SERIE MEDIANTE USART 48

1 i n t button = 8 ; //De acuerdo a l a t a b l a de2 // entrada/ s a l i d a s3

4 i n t bu t ton s ta t e = LOW;5

6 void setup ( ) {7

8 //pinMode con f i gura un pin como OUTPUT ( s a l i d a ) o INPUT ( entrada )9 pinMode ( button , INPUT) ;

10 S e r i a l . begin (9600) ;11

12 }13

14 void r e a d s e n s o r s ( ) {15 but ton s ta t e = d ig i ta lRead ( button ) ;16 }17

18 void pe r f o rm ac t i on s ( ) {19 i f ( bu t ton s ta t e == HIGH) {20 S e r i a l . p r i n t l n ( "Hola mundo" ) ;21 }22 }23

24 void loop ( ) {25 r e a d s e n s o r s ( ) ;26 pe r f o rm ac t i on s ( ) ;27 }

Codigo 4.1: Ejemplo 1 - Comunicaciones: Codigo

4.2. COMUNICACION SERIE MEDIANTE USART 49

Cuadro 4.3: Ejemplo 2: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Entrada Rx0 RX 0Salida Led led red 8Salida Led led yellow 9Salida Tx0 Tx 1

4.2.2. Ejemplo 2: Recibiendo informacion

Hasta ahora unicamente hemos enviado informacion al exterior mediante la comu-nicacion serie. En este segundo ejemplo veremos otra de las partes claves en cualquierdiseno, la captacion de informacion mediante el puerto serie. La recepcion de infor-macion al igual que el envıo de la misma normalmente esta ligado a un protocolo deaplicacion y a una serie de parsers que se ocupan de decodificar dicho protocolo.

Protocolos de comunicacion de bajo nivel dependen del medio fısico de comuni-cacion, por ejemplo: punto a punto, por buses, usando Ethernet, etc., y establecen elmodo en que la comunicacion se lleva a cabo. Por ejemplo establece quien inicia lacomunicacion, como se direcciona el elemento receptor, como se envıan los datos, siuno a uno o a rafagas, etc. Protocolos de comunicacion de alto nivel o de aplicacion serefiere al conjunto de comandos de una aplicacion que se utilizan en la comunicacionpara gestionar el funcionamiento de un sistema. El diseno de protocolos de aplicaciones una tarea que requiere de experiencia y no es el objetivo de este libro el entraren detalle, sin embargo, se anima al lector a definir sus propias codificaciones y apracticar con la decodificacion de la informacion, dado que es un problema recurrenteen la soluciones basadas en microcontroladores.

El ejemplo es muy sencillo, en funcion de la cadena recibida se encendera un LEDu otro (se utilizan LEDs para no complicar el diseno de forma innecesaria, el uso deuno u otro actuador normalmente radica en diferencias meramente electricas).

Tabla de entrada/salida

Para este ejemplo utilizaremos dos LEDs, uno de color amarillo y otro de color rojo.En funcion de la cadena que es recibida se encendera 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/salidacon los nombres de las variables y los pines donde se conectaran los actuadores.

Comunicaciones y protocolos de aplicacion

Cuando se disena un protocolo de comunicacion de alto nivel lo primero que nosdeberıamos preguntar es que objetivo tiene. Si no es un elemento crıtico del disenoo unicamente se utiliza como protocolo de gestion, seguramente prefiramos definir elprotocolo mediante caracteres ascii. Sin embargo, si lo que buscamos es una comuni-cacion rapida, de poco tamano, lo mas recomendable es una codificacion binaria.

Un caracter ascii se codifica mediante 8 bits (ascii extendido) ¿En que nos afec-ta este hecho?. Imaginemos que queremos mandar un comando que tiene 8 modos,podrıamos utilizar un caracter ascii y poner cualquier valor desde 0 a 9. Esto su-pondra una carga de 8 bits para la comunicacion. Sin embargo, si utilizamos 3 bits y

4.2. COMUNICACION SERIE MEDIANTE USART 50

Cuadro 4.4: Ejemplo 2 - Comunicaciones: Comunicaciones

Interfaz serie

Entrada/Salida Comando Evento Descripcion

Entrada led,yellow,high Pin 9 = HIGH Enciende el led ama-rillo

Entrada led,yellow,low Pin 9 = LOW Apaga el led amarilloEntrada led,red,high Pin 8 = HIGH Enciende el led rojoEntrada 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, eltamano es un 37.5 % del tamano inicial.

Normalmente para los prototipos y para las comunicaciones de gestion, se utilizancaracteres ascii dado que son mas sencillos de depurar. En este ejemplo nosotrosutilizaremos un protocolo de aplicacion ascii muy sencillo para que el lector puedaapreciar los fundamentos de la recepcion de informacion mediante el puerto serie.

En la Tabla 4.4 se puede ver la tabla de comunicaciones con los comandos y elformato.

Como se puede observar en la Tabla 4.4 los campos estan separados por comas, quese utilizaran para la decodificacion 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 dedisenar nuestro codigo. A medida que vayamos avanzando en los disenos, estos diagra-mas cada vez seran mas genericos y subiran mas el nivel de abstraccion. Un ejemplode este nivel de abstraccion es la funcion check communications() y parse command()donde no se entra en detalle en la implementacion interna de la misma, unicamentese situa dentro del flujo de programa. Si quisieramos detallar una funcion, una opcionserıa la de realizar un diagrama separado para la misma y mas tarde unirlos.

4.2. COMUNICACION SERIE MEDIANTE USART 51

Normalmente, en cada ciclo del bucle de chequeo de comunicacion se realizara lacomprobacion del numero de datos recibido. Si existen datos se guardaran en el bufferde procesado. Una vez detectado el fin de lınea se procesara el comando y realizarala accion.

Codigo

Este ejemplo, aunque sencillo, requiere un poco mas lıneas de codigo que el ante-rior. Un factor clave y determinante para un buen diseno es la modularizacion. Siempreque una parte del codigo se repita debemos plantearnos el convertirlo en una funcion.Vamos a analizar el codigo de ejemplo. En este ejemplo (Cod. 4.2), hemos anadido dosfunciones genericas: check communications() y la funcion parse command(). La pri-mera funcion hace lo siguiente: Primero comprueba que si buffer de recepcion tienedatos. La funcion del buffer de recepcion es la de guardar todo dato que entre al puer-to serie, hasta 64 bytes, de modo que pueda ser consultado en cualquier momento. Lafuncion Serial.available de la librerıa Arduino, nos proporciona un metodo para sabercuantos bytes se encuentran en el buffer de recepcion. Mediante la funcion Serial.readleemos el dato entrante y anadimos el dato al buffer de procesado que hemos creadoy que hemos llamado serial command. Es responsabilidad del programador vaciar elbuffer mediante la llamada a Serial.read. Esta llamada devuelve el byte mas antiguoen 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 dondese hace la comprobacion Serial.available()>0. Aunque esto puede funcionar, hay quetener mucho cuidado. Puede que en el buffer haya mas de 0 bytes, sin embargo, estono significa que toda la trama este en el buffer, por lo que si se hace esta comprobacionhabra que tener especial cuidado a la hora de formar la trama. Otra manera de evitareste comportamiento podrıa ser el comprobar que el tamano del buffer de recepcionsea mayor que el tamano de trama.Otro de los puntos claves a la hora de tratar con protocolos ascii es el detectar el fin delınea. Dependiendo del sistema operativo y de la consola que se utilice para mandarlos datos, se utilizara un caracter de fin de lınea u otro. En el entorno Arduino unopuede abrir un monitor de terminal serie usando (CTRL+Shift+m). En este monitor,por defecto, esta seleccionada la opcion de mandar como fin de lınea los caracteres((\r \n)), sin embargo en el bloque ((if)) de nuestro codigo, se comprueba el fin de lineaunicamente detectando si el valor recibido es igual a \n por lo tanto el caracter \r serainsertado en nuestra trama y seguramente ocasione problemas en el diseno. La maneramas sencilla de modificar este comportamiento es cambiar la opcion en la consola oen este caso el monitor serial para que unicamente envıe el caracter \n como caracterfin 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 funcion que decodifiquela trama y convierta los datos en informacion util para la logica del programa. Coneste fin se invoca a la funcion parse command() (linea 32). Esta funcion se ha creadoapoyandose en las funciones strcpy() y strtok(), que divide y copia los valores leıdos enla estructura Command protocol() (lıneas 9 a 14). Se recomienda al lector que revisela documentacion sobre la funcion strtok().

La ultima fase es la de actuar en funcion de la entrada, en nuestro caso unicamentetenemos que detectar el primer campo y detectar a que tipo de actuador se refiere lacomunicacion. La funcion perform action led() comprueba que led se va a modificar

4.2. COMUNICACION SERIE MEDIANTE USART 52

y en funcion de dicho valor y del estado actua llamado a la funcion digitalWrite().

Como puedes observar el dividir el codigo en modulos con responsabilidad limitada,permite que el codigo sea mucho mas legible y mantenible. Si quisieramos anadir laposibilidad de encender o apagar un motor, unicamente tendrıamos que crear otrafuncion que podrıamos llamar perform action motor() y hacer las comprobacionespertinentes.

4.2.3. Ejemplo 3: Comunicacion entre Arduinos

En los ejemplos anteriores hemos visto como manejar actuadores y como recibirinformacion de los sensores. Ademas se ha practicado con la comunicacion serie conel ordenador. En este ejemplo veremos como comunicar dos Arduinos entre si.

Normalmente en un diseno de complejidad media los microcontroladores no se en-cuentran 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 esel BMW X5. En BMW decidieron anadir un controlador y sensores en cada amorti-guador con el objetivo de recibir informacion de manera inmediata de las condicionesde las ruedas y del sistema de amortiguacion. En funcion del valor de los sensores,el ordenador central del vehıculo modifica diferentes parametros de modo que la con-duccion sea mas suave y comoda.

Como puedes observar estamos ante un sistema distribuido en el que cada mi-crocontrolador es responsable de un conjunto de sensores y un microcontrolador esresponsable de la actuacion.

En este ejemplo vamos a captar informacion en un Arduino y mediante las tecnicasaprendidas en los ejemplos anteriores informaremos a otro Arduino de dichos valoressiendo este ultimo el responsable de actuar en funcion de los valores recibidos.

Tabla de entrada/salida

Para simplificar el montaje en este ejemplo vamos a trabajar con sensores uti-lizados en otros ejemplos. Utilizaremos un boton, un sensor de temperatura comosensores y dos LEDs como actuadores. Un primer Arduino (esclavo) tendra conecta-dos los sensores. Leera los datos del sensor de temperatura y del boton y los enviara aun segundo Arduino (maestro) cuando este lo solicite. Este segundo Arduino tendraconectados dos LEDs. Un led se encendera si el boton ha sido apretado, mientras queel segundo led lo hara de acuerdo al valor de temperatura recibido.

Cuadro 4.5: Ejemplo 3: Tabla de entrada/salida Arduino esclavo

Entrada/Salida Descripcion Nombre variable Pin

Entrada Boton button 8Entrada Sensor de tempera-

turatemperature sensor A1

Entrada Rx0 RX 0Salida Tx0 Tx 1

4.2. COMUNICACION SERIE MEDIANTE USART 53

1 #inc lude <s t r i n g . h>2 #inc lude <s t d l i b . h>3

4 const i n t l e d r e d = 13 ;5 const i n t l e d y e l l o w = 8 ;6

7 char serial command [ 1 9 ] ;8 i n t index = 0 ;9 typede f s t r u c t {

10 char actuator [ 5 ] ;11 char type [ 1 0 ] ;12 char s t a t e [ 2 ] ;13 } Command protocol ;14 Command protocol command protocol ;15

16 void setup ( ) {17 pinMode ( l ed red , OUTPUT) ;18 pinMode ( l ed ye l l ow , OUTPUT) ;19 S e r i a l . begin (9600) ;20 }21

22 void check communications ( ) {23 i n t data = 0 ;24 whi le ( S e r i a l . a v a i l a b l e ( ) ) {25 data = S e r i a l . read ( ) ;26 i f ( data != ’\n’ ) {27 ser ial command [ index ] = data ;28 index++;29 } e l s e {30 ser ial command [ index ] = ’\0’ ;31 index = 0 ;32 parse command ( ) ;33 }34 }35 }36

37 void parse command ( ) {38 char ∗ token ;39 i n t token index = 0 ;40 s t r cpy ( command protocol . actuator , s t r t o k ( serial command , "," ) ) ;41 s t r cpy ( command protocol . type , s t r t o k (NULL, "," ) ) ;42 s t r cpy ( command protocol . s ta te , s t r t o k (NULL, "," ) ) ;43

44 i f ( strcmp ( command protocol . actuator , "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 void p e r f o r m a c t i o n l e d ( char ∗ type , char ∗ s t a t e ) {51 i f ( strcmp ( type , "yellow" ) == 0) {52 i f ( strcmp ( s tate , "on" ) == 0) {53 d i g i t a l W r i t e ( l ed ye l l ow , HIGH) ;54 } e l s e {55 d i g i t a l W r i t e ( l ed ye l l ow , LOW) ;56 }57 } e l s e i f ( type , "red" ) {58 i f ( strcmp ( s tate , "on" ) == 0) {59 d i g i t a l W r i t e ( l ed red , HIGH) ;60 } e l s e {61 d i g i t a l W r i t e ( l ed red , LOW) ;62 }63 }64

65 }66

67 void loop ( ) {68 check communications ( ) ;69 }

Codigo 4.2: Ejemplo 2 - Comunicaciones

4.2. COMUNICACION SERIE MEDIANTE USART 54

Cuadro 4.6: Ejemplo 3: Tabla de entrada/salida Arduino maestro

Entrada/Salida Descripcion Nombre variable Pin

Entrada Rx0 RX 0Salida Tx0 Tx 1Salida Led notificador de

temperaturatemperature led 8

Salida Led notificador deboton

button led 13

Comunicaciones

El protocolo para este ejemplo es muy sencillo. El maestro (Arduino actuador)manda una peticion de informacion. Esta peticion unicamente contiene el id del sensordel cual desea obtener la informacion. Para simplificar los id se definen mediantela directiva de preprocesador ((#DEFINE)). Ambos Arduinos tendran que tener losmismos valores. Una vez que el esclavo detecta la peticion envıa un mensaje con elidentificador del sensor y el valor, separado por una coma. El maestro entonces deberacapturar la trama y analizar de que sensor ha recibido el valor, si el sensor es un boton,esperara un int, si el sensor, por el contrario, es el de temperatura, entonces esperaraun float.

Es importante enviar delimitadores de trama de modo que sepamos en todo mo-mento cuando se ha iniciado una trama y cuando ha finalizado la misma. En nuestrocaso hemos utilizado como inicio de trama el caracter \t y como finalizador de trama\r.

Diagrama de flujo

El diagrama de flujo esta dividido en 3 partes. La primera de ellas es comun atodos los disenos y es el setup() del microcontrolador. Una vez se ha realizado el setupcorrectamente, el siguiente paso es obtener la informacion de los sensores (en nuestrocaso el boton y el sensor de temperatura). Por ultimo comprobaremos si tenemosalguna comunicacion que atender. Si hay una comunicacion por atender haremos unadecodificacion de la misma (mediante el parser) y mandaremos un mensaje u otro enfuncion de la peticion.

En este esquema el Arduino que va a actuar es el que pide los datos. Podrıaplantearse otra situacion en el que el Arduino con los sensores fuera el que envıa deforma constante los valores captados.

Cabe destacar que este diagrama de flujo es el correspondiente al Arduino Esclavo,se deja como ejercicio al lector la creacion del diagrama para el Arduino Maestro.

Codigo

El codigo es muy parecido al realizado en la Subseccion 4.2.2. En Cod. 4.4 puedesver el codigo del Arduino que actua como esclavo. En Cod. 4.3 se encuentra todo elcodigo del Arduino que realiza las peticiones de informacion, es decir, el maestro. Tenen cuenta que se han omitido partes de codigo repetidas como por ejemplo la lecturade valores de temperatura con el objetivo de no hacer muy extenso el codigo.

4.2. COMUNICACION SERIE MEDIANTE USART 55

Figura 4.6: Ejemplo 3 - Comunicaciones: Diagrama de flujo

El codigo del esclavo te deberıa resultar familiar ya que es una recopilacion de losejemplos anteriores. La parte mas importante ahora es la decodificacion del protocoloque es practicamente igual a la realizada en Subseccion 4.2.2. Se comprueba si hayalguna trama sin procesar preguntado por el caracter \n, si hay alguna se obtienentodos los bytes del puerto serie hasta llegar al caracter \n que indica el fin de lınea.

Por otro lado, el maestro solicita los valores enviando una cadena por el puertoserie. Cuando recibe la informacion realiza un parser y en funcion del tipo de infor-macion (temperatura o boton) realiza una accion u otra.

Otra parte importante del codigo es la utilizacion de la librerıa SoftwareSerial.Mediante esta librerıa podemos implementar un emulador de UART en software (co-mo una UART fısica) dandole un nombre a la instancia creada y asignandoles lospines de RX y TX correspondientes (lınea 13 del codigo). Utilizar estos puertos nosayuda mucho a la hora de programar el Arduino dado que si utilizamos los pinescorrespondientes a los puertos RX y TX del puerto serie 0 para la comunicacion entreArduinos, tendremos que desconectar dichos cables cada vez que queramos programaralguno de los Arduinos ya que la programacion tal y como se explico en el Seccion A.1se realiza mediante el puerto serie.

Este tipo de comunicacion aunque muy sencilla puede llevar a problemas muydifıciles de depurar. Uno de los problemas podrıa ser que uno de los Arduinos desbor-dara el buffer del otro. Para evitar esta condicion en la funcion check petition() delArduino maestro se queda bloqueado hasta recibir el caracter fin de trama. Este sim-ple bucle actua como un mecanismo de control de flujo no permitiendo enviar ningunmensaje hasta que no se reciba la contestacion. Aunque esta solucion es muy sencillatiene muchos problemas dado que si el esclavo no contestara el Arduino maestro sequedarıa constantemente en espera. Para evitar estos problemas existen solucionesque utilizan ((ACK’s)) o mensajes de verificacion para mantener un control de flujo.Se deja como ejercicio al lector la implementacion del mecanismo de ((ACK’s)) a lasolucion.

1 #d e f i n e BUTTON 0

4.2. COMUNICACION SERIE MEDIANTE USART 56

2 #d e f i n e TEMPERATURE SENSOR 13 #d e f i n e MAXTEMPERATURE 264 #inc lude <s t d l i b . h>5 #inc lude <s t r i n g . h>6 #inc lude <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 (10 , 11) ;9 const i n t but ton l ed = 13 ;

10 const i n t temperature l ed = 8 ;11 i n t index = 0 ;12 char serial command [ 1 9 ] ;13 typede f s t r u c t {14 i n t s enso r ;15 } p e t i t i o n t ;16

17 void setup ( ) {18 S e r i a l . begin (9600) ; S e r i a l . begin (9600) ;19 pinMode ( button led , OUTPUT) ; pinMode ( temperature led ,OUTPUT) ;20 }21

22 void s e nd pe t i t i o n t e mp e ra tu r e ( ) {23 s e r i a l . p r i n t ( "\t" ) ;24 s e r i a l . p r i n t (TEMPERATURE SENSOR) ;25 s e r i a l . p r i n t ( ’\n’ ) ;26 }27

28 void 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 r i n t ( ’\t’ ) ;30 s e r i a l . p r i n t (BUTTON) ;31 s e r i a l . p r i n t ( ’\n’ ) ;32 }33

34 void c h e c k p e t i t i o n ( ) {35 char data = 0 ;36 whi le ( 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 . read ( ) ;39 i f ( data == ’\t’ ) {40 index = 0 ;41 } e l s e i f ( data == ’\n’ ) {42 ser ial command [ index ] = ’\0’ ;43 index = 0 ;44 parse command ( ) ;45 } e l s e {46 ser ial command [ index ] = data ;47 index++;48 }49 }50 }51 }52

53 void parse command ( ) {54 char ∗ token = s t r t o k ( serial command , "," ) ;55 i n t s enso r = a t o i ( token ) ;56 switch ( s enso r ) {57 case BUTTON:58 per fo rm act i on but ton ( s t r t o k (NULL, "," ) ) ;59 break ;60 case TEMPERATURE SENSOR:61 per fo rm act ion temperature ( s t r t o k (NULL, "," ) ) ;62 break ;

4.2. COMUNICACION SERIE MEDIANTE USART 57

63 }64 }65

66 void per fo rm act i on but ton ( char ∗ value ) {67 i n t s t a t e = a t o i ( va lue ) ;68 d i g i t a l W r i t e ( button led , s t a t e ) ;69 }70

71 void per fo rm act ion temperature ( char ∗ value ) {72 f l o a t temp = a t o f ( va lue ) ;73 i f ( temp > MAXTEMPERATURE) {74 d i g i t a l W r i t e ( temperature led ,HIGH) ;75 S e r i a l . p r i 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 l W r i t e ( temperature led ,LOW) ;79 }80 }81 void loop ( ) {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 nd pe t i t i o n t e mp e ra tu 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 delay (500) ;87 }

Codigo 4.3: Ejemplo 3 - Comunicaciones: maestro

1 #inc lude <s t r i n g . h>2 #inc lude <s t d l i b . h>3 #inc lude <S o f t w a r e S e r i a l . h>4 #d e f i n e BUTTON 05 #d e f i n e TEMPERATURE SENSOR 16 const i n t button = 8 ;7 const i n t t emperature sensor = 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 bu t ton s ta t e = 0 ;

10 char serial command [ 1 9 ] ;11 i n t index = 0 ;12

13 S o f t w a r e S e r i a l s e r i a l (10 ,11) ;14

15 typede f s t r u c t {16 i n t s enso 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 void setup ( ) {20 pinMode ( button , INPUT) ;21 pinMode ( temperature sensor , INPUT) ;22 S e r i a l . begin (9600) ;23 s e r i a l . begin (9600) ;24 }25 void check communications ( ) {26 char 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 . read ( ) ;29 i f ( data == ’\t’ ) {30 index = 0 ;31 } e l s e i f ( data == ’\n’ ) {32 ser ial command [ index ] = ’\0’ ;33 index = 0 ;

4.3. COMUNICACION I2C 58

34 parse command ( ) ;35 } e l s e {36 ser ial command [ index ] = data ;37 index++;38 }39 }40 }41 void parse command ( ) {42 l a s t p e t i t i o n . s enso r = a t o i ( serial command ) ;43 switch ( l a s t p e t i t i o n . s enso r ) {44 case BUTTON:45 s end but ton s ta t e ( ) ;46 break ;47 case TEMPERATURE SENSOR:48 send temperature ( ) ;49 break ;50 }51 }52 void s end but ton s ta t e ( ) {53 s e r i a l . p r i n t ( ’\t’ ) ; s e r i a l . p r i n t ( l a s t p e t i t i o n . s enso r ) ;54 s e r i a l . p r i n t ( "," ) ; s e r i a l . p r i n t ( bu t ton s ta t e ) ;55 s e r i a l . p r i n t ( ’\n’ ) ;56 }57 void send temperature ( ) {58 s e r i a l . p r i n t ( ’\t’ ) ; s e r i a l . p r i n t ( l a s t p e t i t i o n . s enso r ) ;59 s e r i a l . p r i n t ( "," ) ; s e r i a l . p r i 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 r i n t ( ’\n’ ) ;61 }62 void loop ( ) {63 r e a d s e n s o r s ( ) ;64 check communications ( ) ;65 }

Codigo 4.4: Ejemplo 3 - Comunicaciones: esclavo

4.3. Comunicacion I2C

En la Seccion 4.2 vimos un mecanismo de comunicacion muy sencillo (Comuni-cacion mediante UART). En esta seccion veremos otro protocolo de comunicacionllamado I2C.

I2C fue disenado por la empresa Philips en 1992. Actualmente muchos perifericosimplementan este bus de comunicacion por su sencillez, fiabilidad y el bajo numerode cables requerido (unicamente 2).

Es un protocolo de dos cables utilizado para conectar uno o mas maestros a unoo mas esclavos. Los esclavos pueden ser sensores de temperatura, humedad, de mo-vimiento, memorias series, etc. Cada esclavo se A diferencia de la UART, medianteI2C podemos direccionar a varios esclavos, es decir, es un bus con multiples esclavoscada uno de ellos identificados por una direccion de 7 bits o 10 bits en funcion delestandar utilizado.

4.3.1. Hardware

Una de las ventajas de I2C frente a otros buses como SPI (Serial Peripheral Inter-face) es el bajo numero de cables requeridos para la comunicacion. I2C unicamente

4.3. COMUNICACION I2C 59

requiere 2 cables dentro de un mismo circuito y tres cables para la comunicacion entrecircuitos. La 3 senales principales son:

SCL (Serial CLock): La senal de reloj siempre la genera el dispositivo queactua como maestro. Los esclavos pueden forzar al maestro a que el reloj dejede oscilar. En la Subseccion 4.3.2 veremos este caso.

SDA (Serial Data): La senal SDA se utiliza para enviar los datos.

GND (Ground): Esta senal no es obligatoria en comunicaciones dentro de unmismo circuito pero si la comunicacion es entre circuitos, entonces se necesitaracon el objetivo de proporcionar un mismo nivel de referencia.

4.3.2. Protocolo I2C

Figura 4.7: Esquema de comunicacion I2C

El valor de las senales SCL y SDA esta en alto hasta que uno de los componenteslos 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 senal de reloj por la lınea SCL, determinandola tasa de transferencia de datos, y controla o deja controlar por el esclavo la lınea dedirecciones SDA.

La comunicacion entre un maestro y un esclavo consiste en una secuencia detransacciones de datos a traves de SDA controladas por el maestro o por el esclavo,sincronizadas por la senal SCL.

Hay tres tipos de transacciones, todas iniciadas por el maestro: escritura (el maes-tro escribe en la linea SDA, lectura (el esclavo escribe en la linea SDA) y combinadas(ambos escriben). La comunicacion a traves de I2C esta formada por cuatro pasos:

Condicion de Inicio

Trama de Direccionamiento

Trama de Datos

Condicion de Parada

Cada transaccion comienza con una condicion de inicio (S) y termina con unacondicion de Parada (P). Estas condiciones son controladas por el maestro. Lacondicion de inicio la realiza el maestro con una transicion de la senal SDA de 1a 0 en el semiciclo positivo del reloj SCL, mientras que la condicion de parada serealiza con una transicion de la senal SDA de 0 a 1 en el semiciclo positivo de la senalSCL. En I2C la transicion en la lınea SDA durante las transacciones se hace en la

4.3. COMUNICACION I2C 60

Figura 4.8: Trama de direccion I2C

parte baja del ciclo reloj. Unicamente en la condicion de inicio y parada se realiza latransicion en la parte alta del ciclo de reloj.

Una vez que se ha indicado la condicion de inicio el siguiente paso es enviar unatrama con la direccion del dispositivo que se desea seleccionar. Existen dos modosde direccionamiento: de 7 bits o de 10 bits. El uso de 10 bits de comunicacion noes frecuente y no esta cubierto en este libro. Al final de los 7 bits de direccion seagrega un octavo bit que indicara el tipo de transaccion que se hace (0 = escritura,1 = lectura). Hay que tener en cuenta que la direccion del esclavo se colocara enlos 7 bits mas significativos de la trama de direcciones. Una vez enviada la trama dedireccionamiento los esclavos leen cada bit en la transicion del reloj, teniendo en cuentaque el primer bit es el mas significativo de la direccion y que el ultimo bit correspondeal tipo de transferencia. Para comprobar que el esclavo ha detectado la peticion decomunicacion del maestro, el esclavo debe poner la lınea SDA a nivel bajo antes deldecimo pulso de reloj, de lo contrario el maestro puede interpretar que la comunicacionno se ha podido establecer. Una vez que se ha establecido la comunicacion el siguientepaso es enviar la trama de datos. La trama de datos unicamente se trasmite bit por bitmediante codificacion MSB. En funcion de si la transferencia es de lectura o escritura,sera el maestro o el esclavo el encargado de poner los datos en el bus. Es importanteque el receptor en todo momento valide cada byte recibido mediante un ACK. Si elreceptor envıa un NACK despues de recibir un byte, indicara al receptor que quierecerrar la comunicacion.

El ultimo paso consiste en la transicion del estado de transferencia al estado deparada. Una vez que todos los datos han sido enviados y se ha recibido un NACK, elmaestro generara la condicion de parada.

El protocolo I2C admite la comunicacion continuada entre un maestro y un esclavo.Para ello, una vez terminado el primer flujo de informacion (Condicion de inicio +direccion + ack + datos + ack) en vez de enviar la condicion de parada, el maestromantiene ocupado el bus poniendo la senal de datos en alto y manteniendo el relojen alto. De este modo, no se permite que otro maestro adquiera el bus. Una vezcompletada esta fase se puede enviar otra vez la direccion del esclavo (cualquiera) yse procede al envıo de datos.

En la Figura 4.8 se puede ver una ilustracion de la trama de direccion I2C y enFigura 4.7 un esquema donde se ilustra la comunicacion anteriormente explicada. Esimportante tener en cuenta que el ACK lo envıa el receptor y no el emisor. En laFigura 4.9 se puede observar la estructura que presenta una trama de datos, dondeunicamente se mandan los datos y un bit de ACK que envıa el receptor.

En los siguientes ejemplos veremos de manera simplificada como realizar comuni-caciones 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. Me-diante esta librerıa la utilizacion de protocolo se reduce a unas sencillas funciones que

4.3. COMUNICACION I2C 61

Figura 4.9: Trama de datos I2C

Cuadro 4.7: Ejemplo 1 - I2C: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Entrada Linea SCL SCL A5Entrada Linea SDA SDA A4Salida Led notificador notificator led 13

nos abstraeran por completo de detalles de mas bajo nivel.

Los pines I2C variaran en funcion del microcontrolador que estemos utilizando,recuerda que siempre puedes recurrir a la pagina oficial de Arduino o al datasheetdel microcontrolador para mas informacion. En el caso de utilizar el bootloader delAtmega328 los pines correspondientes a SDA y SCL son A4 y A5 respectivamente.Algunas tarjetas de expansion poseen varios conectores I2C que permite expandir elbus para varios esclavos.

En este ejemplo utilizaremos dos placas Arduino de modo que una envıe un men-saje y el otro reciba el mensaje y en funcion del mismo encienda o apague un LED. Denuevo 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 unLED en el nodo esclavo que servira para comprobar que el mensaje ha sido recibidocon exito.

Diagrama de flujo

Los diagramas de flujo que contienen eventos y comunicaciones suelen ser mascomplejos dado que tenemos que atendar a eventos que no son sıncronos. Se reco-mienda que el lector cree el diagrama de flujo correspondiente basandose en el codigomostrado en Tabla 4.3.3.

Codigo

Para evitar tratar con detalles de implementacion del protocolo I2C utilizaremosla librerıa Wire cuyas funciones mas utilizadas se explicaran a continuacion. La docu-mentacion oficial de la librerıa Wire puede ser consultada desde la siguiente direccion:http://playground.arduino.cc/Main/WireLibraryDetailedReference.

begin(): Esta funcion nos permite iniciar la librerıa. Esta funcion es recurrenteen la mayorıa de las librerıas. La implementacion suele reiniciar o poner en elestado inicial la maquina de estados y los registros de trabajo.

4.3. COMUNICACION I2C 62

requestFrom(direccion, tamano): Mediante esta funcion ponemos al maes-tro a la escucha del esclavo hasta que reciba el tamano fijado en el segundoparametro. La direccion tiene que ser de 7 bits.

beginTransmission(direccion): Si queremos enviar datos desde el maestro alesclavo, despues de llamar a la funcion begin, tenemos que utilizar la funcionbeginTransmission. Mediante esta funcion indicamos a la librerıa que pase alestado preparado y configure el buffer de direccionamiento. Ten en cuenta queesta funcion no enviara ningun bit, unicamente hace operaciones a nivel internocon el objetivo de no ocupar el bus de forma innecesaria.

send(buffer): Una vez que se ha establecido la direccion la funcion send nospermite configurar el buffer de datos de la interfaz TWI2.

endTransmission(): La ultima funcion en cada comunicacion es endTransmis-sion() que mandara todos los datos del buffer a la direccion indicada mediantebeginTransmission() y por ultimo enviara el bit de parada.

onReceive((void*)func(int)): Cuando la librerıa detecta que existe un datollama a la funcion handler pasada como parametro. Esta funcion tiene que tenerla ((firma)) void func(int), el parametro que recibe contiene el numero de bytescapturados. El bus no sera liberado hasta que se retorne de la funcion handler.

receive(): Devuelve el siguiente byte en el buffer de recepcion.

onRequest((void*)func(void)): Si el esclavo recibe la senal SLA+R, es decir,la direccion del dispositivo y la opcion de lectura, la librerıa Wire llama a estafuncion handler donde se debera insertar los datos en el buffer de escritura conel objetivo de iniciar el envıo.

available(): Devuelve el numero de bytes del buffer de recepcion.

Una vez que se tiene claro cada una de las funciones de la librerıa, el codigomostrado en Cod. 4.5 y Cod. 4.6 no debe suponer ningun problema. En primer lugarel Arduino maestro envıa un byte de datos con el caracter ascii ((h)) (recuerda que uncaracter ascii extendido es igual a un byte) y espera medio segundo. Por otro lado,el Arduino esclavo unicamente espera los datos mediante la funcion handler. Estafuncion handler se inscribe al evento del bus mediante la funcion onRequest(). Ten encuenta que aunque en este codigo y a modo de ejemplo se ha realizado la operacionde encender el led dentro de la funcion, esto no es recomendable dado que mientraspermanezcamos en dicha funcion el bus se mantendra ocupado.

Por ultimo, se recomienda que el lector modifique el codigo del esclavo y delmaestro con el objetivo de apagar y encender el led mediante algun evento, esteevento puede ser la insercion de una cadena en el puerto serie o un evento temporal.Ademas, se recomienda que se realice la modificacion del pin de salida fuera de lafuncion handler.

2TWI (Two Wire Interface) es otra forma de nombrar al protocolo I2C

4.3. COMUNICACION I2C 63

1 #inc lude <Wire . h>2

3

4 void setup ( ) {5 Wire . begin ( ) ;6

7 }8

9 void loop ( ) {10 Wire . beg inTransmiss ion (1 ) ;11 Wire . wr i t e ( "h" ) ;12 Wire . endTransmission ( ) ;13 delay (500) ;14 }

Codigo 4.5: Ejemplo 1 - Comunicaciones I2C: maestro

1 #inc lude <Wire . h>2 const i n t n o t i f i c a t i o n l e d = 13 ;3

4 void setup ( ) {5 pinMode ( n o t i f i c a t i o n l e d ,OUTPUT) ;6 Wire . begin (1 ) ;7 S e r i a l . begin (9600) ;8 Wire . onReceive ( handler ) ;9

10 }11 void handler ( i n t num bytes ) {12 char va lue = 0 ;13 whi le ( Wire . a v a i l a b l e ( ) > 0) {14 value = Wire . read ( ) ;15 }16 S e r i a l . p r i n t ( "Read: " ) ;17 S e r i a l . p r i n t l n ( va lue ) ;18 i f ( va lue == ’h’ ) {19 d i g i t a l W 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 l W r i t e ( n o t i f i c a t i o n l e d ,LOW) ;22 }23

24 }25 void loop ( ) {26 delay (100) ;27 }

Codigo 4.6: Ejemplo 1 - Comunicaciones I2C: esclavo

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 (unidadde medicion inercial) mediante el protocolo I2C para obtener los angulos de rotacioncon respecto al eje ((X)) y el eje ((Y)).

El uso de estos sensores requiere de practica y de conocimientos trigonometricospara tratar los valores ((crudos))3. En este ejemplo, no nos centraremos en las funcionesmatematicas, la parte mas interesante para este capitulo es la comunicacion con elsensor.

Para este ejemplo hemos utilizado el sensor MPU6050 (ver Figura 4.10) este sen-sor proporciona 6 grados de libertad y posee un controlador DMP (Digital MotionProcessing). Con el objetivo de simplificar las cosas unicamente veremos como ob-tener el valor del giroscopio, aunque tambien obtengamos el del acelerometro parautilizarlo en el filtro complementario4.

Debido a la sencillez del ejemplo y al hecho de que unicamente utilizaremos unsensor, en este caso no realizaremos diagrama de flujo ni tabla de comunicaciones,unicamente mantendremos la tabla de entrada salida con el objetivo de mostrar lasconexiones necesarias.

Tabla de entrada/salida

Como ya hemos comentado, la estructura de este ejemplo es diferente debido a queunicamente estamos utilizando un sensor y no estamos haciendo ningun tipo actuacionen funcion de los valores obtenidos. En la Figura 4.11 se puede observar la conexionde este sensor. Unicamente utilizaremos el bus principal I2C. El MPU6050 tiene otrobus I2C que utiliza para gestionar una cola FIFO.

Es muy importante que te asegures del voltaje de entrada de tu sensor. AlgunosMPU6050 tienen un conversor que permiten conectar el sensor a 5V, pero si dudasconectalo a 3.3V.

Ten en cuenta que en este caso no hace falta una resistencia de pull-up, esto sedebe 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 resultadode una captura

4El filtro complementario es la union de un filtro de paso bajo con un filtro de paso alto

4.3. COMUNICACION I2C 65

Figura 4.11: Conexiones MPU6050

mediante I2C incorporan esta resistencia.

Codigo

Se puede revisar el codigo en Cod. 4.7. A continuacion, describiremos las partesmas importantes del mismo.

1 #inc lude <Wire . h>2 #d e f i n e IMU 0x683 #d e f i n e A R 16384.04 #d e f i n e G R 131 .05 #d e f i n e RAD TO DEG 57.2957796 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 void setup ( ) {11 Wire . begin ( ) ;12 Wire . beg inTransmiss ion (IMU) ;13 Wire . wr i t e (0x6B) ;14 Wire . wr i t e (0 ) ;15 Wire . endTransmission ( t rue ) ;16 S e r i a l . begin (9600) ;

4.3. COMUNICACION I2C 66

17 }18

19 void loop ( ) {20 Wire . beg inTransmiss ion (IMU) ;21 Wire . wr i t e (0x3B) ;22 Wire . endTransmission ( f a l s e ) ;23 Wire . requestFrom (IMU, 6 , t rue ) ;24 AcX = Wire . read ( ) << 8 | Wire . read ( ) ;25 AcY = Wire . read ( ) << 8 | Wire . read ( ) ;26 AcZ = Wire . read ( ) << 8 | Wire . read ( ) ;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 . beg inTransmiss ion (IMU) ;32 Wire . wr i t e (0 x43 ) ;33 Wire . endTransmission ( f a l s e ) ;34 Wire . requestFrom (IMU, 4 , t rue ) ;35 GyX = Wire . read ( ) << 8 | Wire . read ( ) ;36 GyY = Wire . read ( ) << 8 | Wire . read ( ) ;37

38 Gy [ 0 ] = GyX / G R ;39 Gy [ 1 ] = GyY / G R ;40

41 Angle [ 0 ] = 0 .98 ∗ ( Angle [ 0 ] + Gy [ 0 ] ∗ 0 .010 ) + 0.02 ∗ Acc [ 0 ] ;42 Angle [ 1 ] = 0 .98 ∗ ( Angle [ 1 ] + Gy [ 1 ] ∗ 0 .010 ) + 0.02 ∗ Acc [ 1 ] ;43

44 S e r i a l . p r i n t ( "Angle X: " ) ; S e r i a l . p r i n t ( Angle [ 0 ] ) ; S e r i a l . p r i n t ( "\n" ) ;

45 S e r i a l . p r i n t ( "Angle Y: " ) ; S e r i a l . p r i n t ( Angle [ 1 ] ) ; S e r i a l . p r i n t ( "\n------------\n" ) ;

46

47 delay (10) ;48

49 }

Codigo 4.7: Ejemplo 2 - Comunicaciones I2C: Sensor MPU-6050

En la funcion de setup() iniciamos el dispositivo. La inicializacion del sensor de-pendera de cada uno, en este caso tenemos que mandar el comando ((0)) al registro0x6B. Normalmente el procedimiento suele ser el siguiente: En primer lugar se envıaun byte con el registro donde se quiere leer o escribir (en este caos el 0x6B) y segui-damente se envıa o recibe el dato del registro (en este caso enviamos el comando 0que inicia el dispositivo). Una vez que hemos enviado la peticion de inicio cerramosla comunicacion mediante la llamada a la funcion endTransmission() de la librerıaWire.

Por otro lado, en cada ciclo de ((scan)) (funcion loop()) obtenemos los valores decada uno de los registros del sensor5. Para obtener el valor del registro que contiene losvalores del acelerometro tenemos que enviar la peticion a la direccion 0x3B. Este pasoes muy importante y te ayudara a entender cualquier comunicacion I2C medianteArduino. Una vez que hemos escrito el valor 0x3B en el buffer de datos del bus I2C delArduino, el siguiente paso es enviar dicho buffer por el canal SDA. Ten en cuenta quecomo se explico en la Tabla 4.3.3 los datos no se envıan hasta que se llama a la funcion

5Todos las direcciones ası como el valor de los comandos se obtienen del datasheet de cada sensor

4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 67

endTransmission. Al contrario que en el caso del comando de inicializacion, en estecaso vamos a recibir datos del sensor. Cuando se envıa la peticion 0x3B tenemos queactivar el bit de lectura del bus I2C con el objetivo de que el esclavo (sensor) puedaescribir en el maestro (Arduino). Para activar el bit de lectura primero finalizamosla fase de transmision de datos mediante endTransmission(false). Despues indicamosque queremos obtener datos del esclavo y esperamos hasta tener 6 bytes, todo ellomediante la funcion requestFrom(). Los datos se envıan byte a byte. Como los datosson de 16 bits tendremos que utilizar operaciones de manipulacion de bits para formaruna 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 unicadiferencia esta en el numero de bytes que recibimos del esclavo y la direccion delregistro.

En las lıneas 41 y 46 se aplica un filtro complementario que no sera explicadodebido a que no es el objetivo de este libro.

Si abres el monitor serial podras ver los valores obtenidos cada 10ms.

4.4. Protocolo SPI (Serial Peripheral Interface

En esta seccion veremos otro protocolo de comunicacion serial SPI. SPI es unprotocolo de comunicacion serie sıncrono para comunicacion entre microcontroladoresy uno o mas perifericos a corta distancia como displays, tarjetas de memoria y sensores.Cada instancia de SPI tiene un maestro y uno o varios esclavos.

Posee cuatro senales basicas: Tres desde el maestro hacia el esclavo

SS (Slave Select): Senal que se replica para cada esclavo. El maestro pone acero una de las senales SS para seleccionar el esclavo correspondiente

MOSI (Master Out Slave In): Senal por donde se transmite una palabra(byte) desde el maestro hacia el esclavo

SCK (Serial Clock): Senal que sincroniza la comunicacion Y una senal desdeel esclavo hacia el maestro

MISO (Master In Slave Out): Senal por donde se transmite una palabra(byte) desde el esclavo hacia el maestro

El procesador ATmega328 tiene una interfaz SPI para comunicacion. Las senalesSS, MOSI, MISO y SCK corresponden a los pines 16 al 19 respectivamente. En elmodulo Arduino estos pines estan conectados a los pines 10 al 13 respectivamente.(Figura 4.12).

El mecanismo de transferencia se realiza de la siguiente manera: Toda la comunica-cion es controlada por el master que selecciona el esclavo poniendo en bajo la senal SScorrespondiente. Entonces envıa en modo serie una palabra (byte) por la lınea MOSIy simultaneamente acepta un byte proveniente desde el esclavo por la lınea MISO.Esta transferencia se realiza generando 8 pulsos sobre la lınea de sincronizacion SCK

En Arduino existe una librerıa SPI.h que nos proporciona una serie de funcionespara controlar el proceso de comunicacion.

SPIbegin: Inicializa el bus configurando SS como salida con pullup interno enalto y MOSI y SCLK como salida en bajo.

4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 68

Figura 4.12: Pines SPI en el proc. ATmega328

SPISetting: Esta funcion permite configurar algunos parametros de comuni-cacion tales como los siguientes, los cuales se deben poner en el orden que sedescriben:

• Velocidad de comunicacion (generalmente se pone la frecuencia a que operael microcontrolador en HZ).

• Si el primer bit enviado es el mas significativo (MSB) o el menos significa-tivo (LSB)

• El modo de comunicacion SPI, que nos indica en que flanco del reloj losdatos son enviados o recibidos (Fase del reloj) y si el reloj esta inactivocuando esta 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 comunicacion 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 interrup-ciones.

3. SPI.transfer(): Se transfieren 8 bits

4. SPI.endTransaction(): Se libera el bus y se habilitan de nuevo las interrup-ciones.

4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 69

Cuadro 4.8: Modos de comunicacion SPI

Modo Clock Polarity Clock Phase

MODO 0 0 0MODO 1 0 1MODO 2 1 0MODO 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 senal sinusoidal en el procesador yenviarla a un conversor DAC (Conversor Digital/Analogico) a traves del puerto SPI.

El codigo del ejemplo esta en la figura Cod. 4.8. En la primera parte del codigose crea una tabla de consulta (look-up table) que en realidad es un arreglo de me-moria que llamamos Waveform con valores que forman una sinusoide. Este arregloes recorrido y cada valor es enviado a traves del bus SPI, al conversor DAC externoconectado al Arduino para generar la sinusoide. Para este ejemplo se utilizo un modu-lo de conversıon D/A de 8 canales PMODDA4 de DIGILENT que usa el conversorAD5628 de ANALOG DEVICES, 6

La comunicacion se realiza enviando transacciones de 32 bits a traves del puertoMOSI al esclavo (ver figura Figura 4.137 Sin entrar en detalles tecnicos acerca delDAC AD5628 es necesario saber que los bits C3 al C0 se utilizan para indicar elcomando a ejecutar por el AD5628. En el caso de un Reset el valor es el ”0111”, ypara configurar el valor de referencia en la conversion es el ”1000”. El bit DB0 se debeponer a 1 si se utiliza valor de referencia. Los bits A3 a A0 indican la direccion deuno de los 8 canales del conversor desde el ”0000”, hasta el ”0111”. Para seleccionartodos los canales se coloca el valor ”1111”.

El primer paso es inicializar la comunicacion SPI. Para ello hemos creado la fun-cion init spi que pone en alto la senal slave select y llama a la funcion begin de lalibrerıa SPI (SPI.begin). El siguiente paso es configurar el modulo esclavo, segun seindica en la lınea 27 del codigo, donde se indica la frecuencia del reloj, si el primerbit es el mas significativo, y el modo de comunicacion. Para configurar el esclavo esnecesario enviarle un comando de reset y luego otro comando con el setup del va-lor de referencia. Para ello es necesario colocar la senal slave selecta ’0’ durante latransaccion. En este ejemplo se realizan cuatro transacciones de 8 bits cada una (tantoen la funcion config dac como en la funcion Loop), pero se puede utilizar la funcion

6Informacion acerca del conversor PMODDA4 de DIGILENT puede encontrarse en este enlacehttps://reference.digilentinc.com/pmod:pmod:DA4

7tomada de https://reference.digilentinc.com/pmod:pmod:DA4

4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 70

Figura 4.14: Forma de onda obtenida conversion D/A

SPI.Transfer16() de Arduino y realizar dos transacciones de 16 bits.

Una vez configurado se entra en la funcion Loop y se comienzan a enviar los datosque se sacan de memoria al conversor. La salida obtenida en nuestro ejemplo puedeverse en la figura Figura 4.14

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 0x7ff , 0x86a , 0x8d5 , 0x93f , 0x9a9 , 0xa11 , 0xa78 , 0xadd , 0xb40 ,

6 0xba1 , 0xbff , 0xc5a , 0xcb2 , 0xd08 , 0xd59 , 0xda7 , 0xdf1 , 0xe36 ,

7 0xe77 , 0xeb4 , 0xeec , 0xf1f , 0xf4d , 0xf77 , 0xf9a , 0xfb9 , 0xfd2 ,

8 0xfe5 , 0xff3 , 0xffc , 0xfff , 0xffc , 0xff3 , 0xfe5 , 0xfd2 , 0xfb9 ,

9 0xf9a , 0xf77 , 0xf4d , 0xf1f , 0xeec , 0xeb4 , 0xe77 , 0xe36 , 0xdf1 ,

10 0xda7 , 0xd59 , 0xd08 , 0xcb2 , 0xc5a , 0xbff , 0xba1 , 0xb40 , 0xadd ,

11 0xa78 , 0xa11 , 0x9a9 , 0x93f , 0x8d5 , 0x86a , 0x7ff , 0x794 , 0x729 ,

12 0x6bf , 0x655 , 0x5ed , 0x586 , 0x521 , 0x4be , 0x45d , 0x3ff , 0x3a4 ,

13 0x34c , 0x2f6 , 0x2a5 , 0x257 , 0x20d , 0x1c8 , 0x187 , 0x14a , 0x112 ,

14 0xdf , 0xb1 , 0x87 , 0x64 , 0x45 , 0x2c , 0x19 , 0xb , 0x2, 0x0, 0x2 ,

15 0xb , 0x19 , 0x2c , 0x45 , 0x64 , 0x87 , 0xb1 , 0xdf , 0x112 , 0x14a ,

16 0x187 , 0x1c8 , 0x20d , 0x257 , 0x2a5 , 0x2f6 , 0x34c , 0x3a4 , 0x3ff ,

17 0x45d , 0x4be , 0x521 , 0x586 , 0x5ed , 0x655 , 0x6bf , 0x729 , 0x794

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 = 0x07000000;

26 unsigned long reference = 0x08000001;

27 SPI.beginTransaction(SPISettings (12000000 , MSBFIRST , SPI_MODE0));

// gain control of SPI bus

28 digitalWrite(slave_select , LOW);

29 SPI.transfer (( reset & 0xFF000000) >> 24);

30 SPI.transfer (( reset & 0x00FF0000) >> 16);

31 SPI.transfer (( reset & 0x0000FF00) >> 8);

32 SPI.transfer (( reset & 0x000000FF));

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 & 0xFF000000) >> 24);

38 SPI.transfer (( reference & 0x00FF0000) >> 16);

39 SPI.transfer (( reference & 0x0000FF00) >> 8);

40 SPI.transfer (( reference & 0x000000FF) >> 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 = 0x03F00000;

50 unsigned long temp_value = 0x00000000;

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 & 0xFF000000) >> 24));

57 SPI.transfer ((( value2 & 0x00FF0000) >> 16));

58 SPI.transfer ((( value2 & 0x0000FF00) >> 8));

59 SPI.transfer ((( value2 & 0x000000FF) >> 0));

60 digitalWrite(slave_select , HIGH);

61 SPI.endTransaction (); // release the SPI bus

62 temp_value = 0x00000000;

63 }

64 }

Codigo 4.8: Ejemplo - Bus SPI: Codigo

4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 72

CAPITULO 5

INTERRUPCIONES

Hasta ahora toda la obtencion de informacion se ha realizado de manera unidirec-cional y siempre iniciando la peticion desde el Arduino. En este capitulo veremos comotrabajar con las interrupciones en el microcontrolador Atmega328. Las interrupcionesson senales recibidas por el procesador que indican que se ha producido un evento enalguna de sus entradas o que un periferico quiere comunicarse con el procesador.

Las peticiones de interrupcion se pueden implementar de dos maneras diferentes.La primera es utilizando la tecnica de polling que consiste en mirar cada cierto tiempo,de manera regular, si un periferico o el procesador requiere de atencion. Como puedesimaginar este procedimiento es altamente ineficiente dado que el procesador pierdemucho tiempo de procesamiento en comprobaciones, por lo que hoy en dıa no se sueleutilizar. El segundo metodo es a traves de pedidos de interrupcion enviados por elperiferico o componente que quiere ser atendido. Al recibir el pedido de interrupcionel procesador deja de ejecutar su tarea actual y atiende a la interrupcion mediante laejecucion de la rutina de interrupcion correspondiente al tipo de interrupcion que seprodujo..

La rutina de interrupcion es la funcion que se ejecuta cada vez que ocurre unainterrupcion en el procesador y es una pieza de codigo que depende del elementoque interrumpe. Por ejemplo cuando apretamos una tecla el controlador del tecladoenvıa un pedido de interrupcion que hace que el contador de programa en logar decontinuar lo que estaba ejecutando salta a una direccion de memoria donde esta a suvez la direccion de la rutina de atencion al teclado.

Existen diversas maneras de configurar la direccion de salto cuando ocurre unainterrupcion. A continuacion, se nombran las mas importantes:

Direccion fija: Las direcciones se encuentran ((cableadas)) al procesador por loque no se puede modificar la posicion de las interrupciones.

Direccion variable: Las direcciones se pueden encontrar en cualquier lugary pueden variar de un procesador a otro del mismo tipo (configuracion delprocesador).

El metodo que nos interesa a nosotros es el de las direcciones variables, dentro deeste grupo, existen diversas implementaciones. Una de las mas utilizadas y que ademas

73

5.1. INTERRUPCIONES EN EL ATMEGA328 74

Figura 5.1: Ejemplo de indireccion

sera la que utilizaremos, es el direccionamiento vectorizado o indirecto. Medianteesta tecnica una lınea de interrupcion tiene asociada una posicion en el vector deinterrupciones donde se encuentra la direccion de la funcion que finalmente se lanzara.En la Figura 5.1 se puede ver una ilustracion del procedimiento.

En este capıtulo veremos como trabajar con las interrupciones del microcontrola-dor ATmega328 y realizaremos algunos ejemplos con el objetivo de asentar todos losconceptos teoricos.

5.1. Interrupciones en el ATmega328

Cuando afrontamos la tarea de desarrollar una solucion basada en microcontro-ladores, lo primero que debemos realizar es un analisis de caracterısticas del mismo.Algunas de las caracterısticas son el numero de pines de entrada/salida, tipos de co-municaciones soportadas, tipos de timers, interrupciones, etc. . . En este caso vamosa analizar las interrupciones del microcontrolador Atmega328. Este microcontroladorsoporta hasta 27 tipos de interrupciones distintas, cada una con su nivel de priori-dad correspondiente (ver Tabla 5.1). La prioridad va de mayor a menor, es decir, lainterrupcion 1 (reset) tiene la maxima prioridad y la numero 26 (spm ready) tiene lamınima prioridad.

En la Tabla 5.1 podemos ver las interrupciones, internas y externas, soportadaspor el ATmega328. En la tabla se observa claramente lo explicado anteriormente en elejemplo del teclado, la interrupcion asociada al puerto de interrupcion INT0 moverael contador de programa a la direccion 0x0002 y sera en ese lugar en el cual pondremosel codigo para tratar dicha interrupcion.

5.2. Manipulacion software

A la hora de utilizar las interrupciones en el entorno Arduino, tenemos 3 posibili-dades:

Librerıa AVR: Utilizar la librerıa avr que implementa todas las interrupciones.

Librerıa Arduino: Gestionar las interrupciones mediante las herramientas pro-porcionadas por Arduino, aunque muchas interrupciones no se han implemen-tado aun en esta librerıa.

5.2. MANIPULACION SOFTWARE 75

Cuadro 5.1: Tabla de interrupciones

VectorNo. Program Address(2) Source Interrupt Definition1 0x0000(1) RESET External Pin, Power-on Reset, Brown-out Reset and Watchdog System Reset2 0x0002 INT0 External Interrupt Request 03 0x0004 INT1 External Interrupt Request 14 0x0006 PCINT0 Pin Change Interrupt Request 05 0x0008 PCINT1 Pin Change Interrupt Request 16 0x000A PCINT2 Pin Change Interrupt Request 27 0x000C WDT Watchdog Time-out Interrupt8 0x000E TIMER2 COMPA Timer/Counter2 Compare Match A9 0x0010 TIMER2 COMPB Timer/Counter2 Compare Match B

10 0x0012 TIMER2 OVF Timer/Counter2 Overflow11 0x0014 TIMER1 CAPT Timer/Counter1 Capture Event12 0x0016 TIMER1 COMPA Timer/Counter1 Compare Match A13 0x0018 TIMER1 COMPB Timer/Coutner1 Compare Match B14 0x001A TIMER1 OVF Timer/Counter1 Overflow15 0x001C TIMER0 COMPA Timer/Counter0 Compare Match A16 0x001E TIMER0 COMPB Timer/Counter0 Compare Match B17 0x0020 TIMER0 OVF Timer/Counter0 Overflow18 0x0022 SPI, STC SPI Serial Transfer Complete19 0x0024 USART, RX USART Rx Complete20 0x0026 USART, UDRE USART, Data Register Empty21 0x0028 USART, TX USART, Tx Complete22 0x002A ADC ADC Conversion Complete23 0x002C EE READY EEPROM Ready24 0x002E ANALOG COMP Analog Comparator25 0x0030 TWI 2-wire Serial Interface26 0x0032 SPM READY Store Program Memory Ready

Manipulando directamente los registros correspondientes: Mediante las opera-ciones explicadas en Seccion B.1 puedes manipular los registros de interrupcionesy gestionar al nivel mas bajo posible las interrupciones. Hay que tener en cuentaque esta manera es la menos portable, ademas de ser la mas complicada.

5.2.1. Librerıa avr

Como ya se ha comentado a lo largo del libro, el entorno Arduino realmente es unconjunto de librerıas de abstraccion que hacen las cosas mas sencillas aunque puedeocurrir que si queremos realizar tareas especıficas, es probable que en algunos casosesta librerıa no nos proporcione todas las comodidades que necesitemos, dado que hasido pensado para prototipos sencillos principalmente. En esta seccion, veremos comoutilizar la librerıa avr-libc para el manejo de las interrupciones. Esta librerıa es la queutiliza Arduino y por lo tanto no tendremos que instalar ningun paquete adicional.

Para empezar a utilizar las interrupciones lo primero que debemos hacer es incluirel archivo de cabecera que contiene las definiciones de las funciones que vamos autilizar. Este archivo se llama ¡avr/interrupt.h¿, en el podemos encontrar diferentesmacros y definiciones que nos simplificaran la tarea de gestionar las interrupciones.

El ATmega, al igual que la mayorıa de los microcontroladores, utiliza un bit dehabilitacion global de interrupciones. Este bit es el bit 7 del registro de estado y controlSREGdel ATmega. Ademas cada periferico tiene su registro de interrupciones con subit de habilitacion correspondiente. Luego para que una habilitacion sea atendidadebera tener en 1 el bit de habilitacion global y a 1 el bit de habilitacion especıfico.

Para habilitar o deshabilitar el bit de habilitacion global utilizaremos las macrossei() o cli() respectivamente. Estas macros escriben un 1 o un 0 en el bit 7 de ladireccion de memoria 0x3F, que corresponde al registro de estado y control SREG.

Cuando una interrupcion ocurre, se pone a 0 el bit de habilitacion global y sedeshabilitan todas las interrupciones. El software de usuario puede poner a 1 otra

5.2. MANIPULACION SOFTWARE 76

1 #include <avr/interrupt.h>

2 ISR(INT0)

3 {

4 // Procesamiento

5 evento = 1;

6 }

Codigo 5.1: Ejemplo de definicion de ISR

1 #include <avr/interrupt.h>

2 ISR(BADISR_vect)

3 {

4 error =1;

5 }

Codigo 5.2: Captura de interrupcion no esperada

vez el bit de habilitacion para poder atender interrupciones anidadas, de manera quecualquier interrupcion habilitada pueda interrumpir la rutina de interrupcion actual.Luego, cuando se retorna de la interrupcion, el bit de habilitacion global se poneautomaticamente a 1.

Para habilitar las interrupciones correspondientes a cada periferico debemos ponera 1 el bit correspondiente en el registro de mascara de Interrupcion. Si el bit de mascaraesta a 1 y a su vez el bit de habilitacion global esta a 1 entonces el procesador atenderala interrupcion cuando ocurra. Para el caso de interrupciones externas existen registrosque permiten configurar que tipo de eventos se atenderan. Como se detallara masadelante, estos eventos pueden ser un cambio de nivel en un pin, un flanco ascendenteo descendente, etc. Si se desea profundizar mas acerca del manejo de registros deinterrupciones se recomienda al lector la hoja de datos del fabricante.

Ahora que sabemos como habilitar o deshabilitar las interrupciones, el siguientepaso para poder atender a la misma es definir la rutina de interrupcion.

Para definir la rutina debemos utilizar la macro ISR. Esta macro tiene comoparametro una constante que define la interrupcion que se desea atender. Normal-mente el nombre de la constante es el mismo que el especificado en el datasheet delfabricante. Como ejemplo, si quisieramos definir una rutina de interrupcion para INT0el codigo serıa similar al mostrado en Cod. 5.1

Otra practica que es recomendable sobre todo en disenos que hacen uso intensivode las interrupciones, es capturar todas aquellas interrupciones para las cuales no seha definido ninguna rutina de interrupcion (normalmente esto indica un bug). Paracapturar todas las interrupciones se debe pasar la constante BADISR vect a la macroISR. En Cod. 5.2 se puede ver un ejemplo.

Otro caso particular, y para el cual existe una solucion, es cuando se desea utilizaruna misma rutina de interrupcion para dos interrupciones diferentes. Para conseguireste 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 interrupcion dela interrupcion PCINT1 pasa a ser igual a la de PCINT0.

5.2. MANIPULACION SOFTWARE 77

1 #include <avr/interrupt.h>

2 ISR(PCINT0_vect)

3 {

4 // Procesamiento

5 evento = 1;

6 }

7 ISR(PCINT1_vect , ISR_ALIASOF(PCINT0_vect));

Codigo 5.3: Captura de interrupcion no esperada

5.2.2. Librerıa Arduino

Arduino provee de un conjunto de funciones para el tratamiento de interrupcionesen sus placas de desarrollo. La librerıa de Arduino solo da soporte a un numerolimitado de interrupciones, a continuacion explicaremos cada una de las funciones dela librerıa:

noInterrupts(): Esta funcion al igual que cli nos permite deshabilitar las in-terrupciones.

interrupts(): Habilita las interrupciones del mismo modo que sei.

attachInterrupt(): Permite seleccionar una rutina de interrupcion para unainterrupcion determinada. En funcion de la placa de desarrollo el numero de pinvariara, se recomienda ver el datasheet, ası como el esquematico para averiguar elpin correspondiente a la interrupcion. Las interrupciones que estan soportadaspor esta funcion son las siguientes: INT0, INT1, INT2, INT3, INT4, INT5.Ademas, mediante un parametro podemos indicar en que momento queremosque se active la rutina de interrupcion pudiendo ser:

• LOW: Cuando el pin esta en estado ((bajo)).

• CHANGE: Cuando se produce un cambio de estado en el pin (de alto abajo 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 funcion podemos eliminar la rutina de in-terrupcion que se indique como parametro.

En general se recomienda usar la librerıa de AVR dado que aparte de ser maseficiente es mucho mas flexible.

5.2.3. Consideraciones importantes

A la hora de utilizar las interrupciones tenemos que tener algunas consideracionesen cuenta.

Las interrupciones por defecto estan habilitadas desde el inicio, por lo que no esnecesario utilizar la macro sei en un primer instante, aunque si es recomendable.Algunas funciones como delay() o millis() utilizan las interrupciones para su funcio-namiento. Es importante que se tenga en cuenta el uso que hacen las funciones de

5.3. EJEMPLO 1: PRIMERA RUTINA DE INTERRUPCION 78

Cuadro 5.2: Ejemplo 1: Tabla de entrada/salida

Entrada/Salida Descripcion Nombre variable Pin

Entrada Boton button 2Salida Led notification led 13

las interrupciones ya que por ejemplo, para usar una funcion delay() dentro de unarutina de interrupcion debemos habilitar otra vez el bit de habilitacion global, dadoque por defecto al entrar en una rutina de interrupcion se llama de forma implıcitaa la macro cli.

5.3. Ejemplo 1: Primera rutina de interrupcion

En este primer ejemplo veremos un ejemplo de utilizacion de las interrupciones enel procesador ATmega328 usando las librerıas avr. El ejemplo consistira en encendery apagar un led pulsando un boton. Seguramente al lector le resultara familiar puesen la Seccion 3.2 se estudio, sin embargo la principal diferencia es que en este caso noleeremos el valor de un pin, sino que el propio evento sera el encargado de realizar laaccion.

A partir de esta seccion no realizaremos los diagramas de flujo con el objetivo deno hacer tan extensos los ejemplos, pero se recomienda que el lector tenga presentesiempre este paso en sus disenos.

5.3.1. Tabla de entrada/salida

Lo mas importante de la Tabla 5.2 es la eleccion del pin para el boton. Si ob-servamos el datasheet del ATmega328 veremos que los pines que tienen asociadas alas interrupciones externas INT0 e INT1 son los pines PD1 y PD2 los cuales, enel Arduino, estan conectados a los pines fısicos 2 y 3. El pin para el led puede sercualquiera de los de salida digitales.

5.3.2. Codigo

El codigo para manejar las interrupciones en los microcontroladores ATmega esmuy sencillo y se puede resumir en los siguientes pasos:

Habilitar registro global de interrupciones: Mediante las macros sei y clipodemos habilitar y deshabilitar el bit global de interrupciones.

Habilitar interrupcion especıfica: Cada interrupcion tiene asociado un bitde mascara de activacion individual. Mediante este bit podemos permitir in-terrumpir al procesador mediante dicha interrupcion. En nuestro caso y paranuestro procesador el registro se llama EIMSK y el bit que pondremos en 1 esel correspondiente a la INT0 (lınea 13).

Configurar la interrupcion: Algunas interrupciones tienen registros que per-miten configurar cuando lanzar la misma. En nuestro caso, para la interrupcionINT0 seleccionaremos el flanco descendente en el pin 2 poniendo un ’1’ en elbit ISC01.

5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 79

1 #inc lude <avr / i n t e r r u p t . h>2

3 const i n t n o t i f i c a t i o n l e d = 13 ;4 const i n t button = 2 ;5

6

7 void setup ( void )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 l W r i t e ( button , HIGH) ;12 s e i ( ) ;13 EIMSK |= (1 << INT0) ;14 EICRA |= (1 << ISC01 ) ;15 }16

17 void loop ( void )18 {19

20 }21

22 ISR ( INT0 vect )23 {24 d i g i t a l W r i t e ( n o t i f i c a t i o n l e d , ! d i g i t a lRead ( n o t i f i c a t i o n l e d ) ) ;25 }

Codigo 5.4: Ejemplo 1 - Interrupciones

Definir la rutina de interrupcion: Mediante la macro ISR podemos indicarlas acciones que se deberan tomar al atender una interrupcion.

En Cod. 5.4 se puede ver la instanciacion de los pasos anteriormente nombrados.Un procesamiento intensivo dentro de la rutina de interrupcion no es una buena ideadado que durante el tiempo en que el procesador se encuentre en esta funcion norealizara ninguna otra accion. En este ejemplo se ha modificado el estado del pin 13dentro de la funcion unicamente con el objetivo de simplificar el codigo, sin embargose deja como ejercicio al lector modificar el ejercicio para que la modificacion no sehaga dentro de dicha rutina de interrupcion1.

Como puedes observar, el bucle principal esta vacıo, sin embargo si pulsas al botonveras como el led se enciende y apaga. Este comportamiento se llama asincronıa, dadoque no hay ningun lugar donde se llame a la funcion que enciende y apaga el led, esel propio suceso el que llama a dicha funcion.

5.4. Ejemplo 2: Midiendo distancias

En este ejemplo vamos a utilizar las interrupciones con la ayuda de la librerıa deArduino para medir las distancias a la que se encuentran los objetos haciendo usotambien 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. Acontinuacion se explicara cada uno de ellos:

1Como pista se podrıa utilizar algun tipo de ((bandera)) o ((flag))

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 Descripcion Nombre variable Pin

Entrada Pin de recepciondel sensor HC-SR04

echo pin 2

Salida Pin gatillo del sen-sor HC-SR04

tigger pin 3

Salida Transmision puertoserie

Tx 1

VCC: Este sensor trabaja a 5V por lo que podra ser conectado directamenteal Arduino a traves del pin de 5V del mismo.

GND: Comun.

Trigger: El trigger o gatillo nos permite indicar al sensor de que queremoshacer una medicion. Para que el sensor sepa que queremos realizar la mediciony que no es un falso positivo debemos activar este pin como mınimo durante 10microsegundos.

Echo: Este pin n0s indicara la distancia a la que se encuentra el objeto. ¿Como?muy sencillo. Una vez que se recibe la senal del trigger el sensor manda unasenal de 40Khz que cuando rebota contra un objeto y vuelve al sensor de nuevohace que el sensor emita un pulso positivo con una longitud proporcional a ladistancia 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 explicara nada mas al respecto.

5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 81

5.4.2. Codigo

El codigo se puede ver en Cod. 5.5 a continuacion veremos las partes mas impor-tantes del mismo.

Cuando el Arduino entra en la fase de setup configuramos la comunicacion seriepara poder mostrar la distancia por pantalla y ademas configuramos los pines deentrada/salida. En este caso tal y como nombramos en la Tabla 5.3 tenemos un pinde entrada y dos de salida. Los de salida corresponden uno al trigger del sensor y el otroal pin de transmision Tx del puerto serie. El pin de entrada esta conectado a la senalecho del sensor. Es importante en este punto recordar que tenemos lıneas limitadas deinterrupcion en nuestra placa y que estas estan conectadas a unos pines determinados.En este caso, la lınea de interrupcion que hemos utilizado es la conectada al pin 2 delArduino uno, que corresponde a la interrupcion INT0 (ver en el datasheet).

El siguiente paso y seguramente el mas importante en este ejemplo es la habilita-cion de la interrupcion del pin 2. Para ello utilizamos la funcion attachInterrupt dela librerıa de Arduino. Esta funcion tiene tres parametros: Nº de interrupcion, rutinade interrupcion que se ejecutara, y el tipo de evento que disparara la interrupcion(cambio de estado, flanco, etc). Como puedes observar esto es lo mismo que hemoshecho en las lıneas 13 y 14 del ejemplo 1, solo que en lugar de escribir en los registrosutilizando las librerıas avr, lo que hacemos es llamar a esta funcion que se encarga deeso. Por lo tanto no incluimos el archivo de cabecera avr/interrupt en este ejemplo.En este ejemplo el primer parametro es un 0. Este parametro no se refiere al pin alque esta conectado el sensor, sino a la interrupcion del microcontrolador. En este casoel pin 2 esta conectado como ya hemos dicho a la interrupcion 0 por lo que este serael numero que tendremos que utilizar. En las ultimas versiones del entorno Arduinose ha anadido una macro llamada digitalPinToInterrupt(pin) que permite despreocu-parnos de este parametro. Comprueba si tu entorno ha anadido este macro y si esası puedes utilizarla. El otro parametro importante es la funcion que sera llamadacuando recibamos la interrupcion (rutina de Interrupcion). La funcion no debera re-tornar ningun tipo (void). Por ultimo el parametro CHANGE indica que nos aviseen cualquier cambio en el pin, esto es, alto a bajo o bajo a alto. (en Subseccion 5.2.2puedes ver las opciones que se pueden pasar a la funcion).

Como ya comentamos en Seccion 5.4 para saber la distancia a la que se encuentraun objeto debemos medir el tiempo entre el flanco de subida del pin echo y el flanco debajada del mismo. Recordemos que la distancia es proporcional a dicho tiempo. Comola funcion interrupt echo() se llama cada vez que hay una variacion en el pin echo lounico que tenemos que registrar es cuando empieza (flanco de subida) y cuando termi-na (flanco de bajada). Esta comprobacion se hace mediante la funcion digitalRead()que permite saber en que estado estamos y en funcion del mismo registra el tiempoen las variables correspondientes. El tiempo se registra con la funcion micros(). Sepodrıa usar la funcion millis() para medir tiempos en milisegundos pero esto no esposible dentro de una rutina de interrupcion.

En el datasheet del sensor se indica que el tiempo se debe dividir entre 58 paraobtener la distancia en centımetros.

Otro punto a resaltar es la activacion del pin trigger que debe estar en estadoalto durante al menos 10 microsegundos. Aunque esto se podrıa realizar mediante untimer e interrupciones, en este caso hemos optado por un delay en microsegundos.

El retardo final de 100 milisegundos (linea 21) unicamente tiene la funcion demejorar la visualizacion de la distancia.

5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 82

1 const i n t echo p in = 2 ;2 const i n t t r i g g e r p i n = 3 ;3

4 long echo end = 0 ;5 long e c h o s t a r t = 0 ;6 long echo durat ion = 0 ;7

8

9 void setup ( ) {10 S e r i a l . begin (9600) ;11 pinMode ( echo pin , INPUT) ;12 pinMode ( t r i g g e r p i n , OUTPUT) ;13 a t ta ch In t e r rup t ( d i g i t a l P i nT o In t e r r up t ( echo p in ) , i n t e r r u p t e c h o ,

CHANGE) ;14 }15

16 void loop ( ) {17 d i g i t a l W r i t e ( t r i g g e r p i n ,HIGH) ;18 delayMicroseconds (50) ;19 d i g i t a l W 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 ( echo durat ion / 58) ;21 delay (100) ;22 }23

24 void i n t e r r u p t e c h o ( ) {25 i f ( d i g i t a lRead ( echo p in ) == HIGH) {26 echo end = 0 ;27 e c h o s t a r t = micros ( ) ;28 } e l s e {29 echo end = micros ( ) ;30 echo durat ion = echo end − e c h o s t a r t ;31 }32 }

Codigo 5.5: Ejemplo 2 - Midiendo distancias

CAPITULO 6

MULTITASKING Y TIMERS

Hasta ahora cada vez que buscamos repetir una accion en un tiempo determinadohemos utilizado la funcion delay() que nos provoca un retardo en la ejecucion duran-te el tiempo especificado en la misma, en milisegundos. Como se explicara en estecapıtulo la utilizacion de dicha funcion no debe realizarse en todos los casos, es mas,por regla general deberıa evitarse su uso.

Cuando se utiliza la funcion delay() el Arduino se queda en un bucle activo. Porlo tanto, durante ese tiempo el microcontrolador no realizara ninguna otra funcion,esto es, estara consumiendo energıa pero no estara haciendo ningun trabajo util comopor ejemplo podrıa ser registrar diferentes valores de los sensores.

En este capıtulo veremos de manera resumida como utilizar otras funciones comopor ejemplo millis() para evitar los problemas que trae consigo la funcion delay().

6.1. Timers

Los microcontroladores como ya sabras estan compuestos de un conjunto de pe-rifericos como por ejemplo los TIMER, USARTS, WDG, etc que nos permiten expan-dir su funcionalidad. En este caso hablaremos de un periferico muy importante y quesin duda alguna es la base para todas las funciones relacionadas con el tiempo. Esteperiferico es el timer. Un timer es un circuito que nos permite contar eventos, porejemplo ciclos de reloj, eventos externos, etc, para generar formas de ondas, circuitosde reloj, comparadores, etc.

Dependiendo de en que microcontrolador este basado nuestro Arduino tendremosmas o menos timers, en el caso del microcontrolador ATmega328 podemos encontrartres timers: dos de 8 bits y uno mas de 16 bits.

En la Figura 6.1 puedes encontrar un diagrama en bloques del timer de 8 bits delATmega328. A continuacion pasaremos a explicar cada uno de los componentes queforman este modulo de tal modo que podamos tener una vision amplia a la hora deutilizar cualquier funcion relacionada con el tiempo en nuestros disenos.

Basicamente un timer esta formado por un circuito de control (Control Logic) quese encarga de contar de manera ascendente o descendente los eventos que se producen

83

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 decuenta se puede utilizar para generar diferentes funcionalidades tales como compararcon otro valor contenido en otro registro, generar una forma de onda o generar unainterrupcion cuando se llega a un determinado valor, por ejemplo. El circuito selectorde reloj (Clock Select) determina cual es la entrada que se lee, reloj o evento externoy en que flanco del evento se produce la captura, y si se va a tener en cuenta o no unfactor de escala. El factor de escala es un circuito denominado prescaler que genera unevento cada cierta cantidad de ciclos de reloj. Esta generacion de factor de escala esconfigurable desde el registro Clock prescaler del ATMega328, ubicado en la direccionde memoria 0x61. Empezaremos explicando los registros para continuar con los modosde funcionamiento del timer.

6.1.1. Registros

TCNTn: Este registro nos permite saber en todo momento el valor del timercorrespondiente (la letra n indica el numero de timer que puede ser 0 o 1). Estevalor se incrementa o decrementa, segun sea el modo habilitado, cada vez quese produzca un evento de reloj, o de evento externo, a la entrada del circuito decontrol. El registro TCNT0 es un registro de 8 bits por lo que el valor maximoque se podra alcanzar es 255 o 0xFF en hexadecimal.

OCR0a: El valor de este registro se compara cada ciclo del timer con el valordel registro TCNTn. Cuando se alcanza el valor configurado en este registro seactivara el bit OCF0A. El resultado de la comparacion se puede utilizar paragenerar distintas formas de onda o PWM como se vera mas adelante.

OCR0b: El mismo comportamiento que OCR0a.

6.1. TIMERS 85

TCCR0A, TCCR0B : Estos dos registros de control nos permiten configurarel timer de tal modo que se comporte como un comparador, como un generadorde funciones, etc.

TIMSK0 : Mediante este registro podremos habilitar o deshabilitar las diferen-tes interrupciones generadas por el TIMER (ver datasheet).

TIFR0 : Parecido a TIMSK0 la diferencia radica en que este ultimo unicamentenos indica que se ha generado una interrupcion, como por ejemplo cuando sellega al desbordamiento que se activa el bit TOV0.

6.1.2. Modos de funcionamiento

A continuacion veremos los distintos modos de funcionamiento que posee el timer.Estos modos se veran de manera general, lo suficiente como para abordar los ejemplosde este capıtulo, y se deja como tarea para el lector una lectura mas detallada de losmismos.

Modo normal: Es el modo mas simple de utilizacion. Para activar este modose deben poner a 0 los bits WGM0:2 ubicados en los registros TCCR0A yTCCR0B (cuidado, los dos primeros bits se encuentran en el registro TCCR0A,sin embargo el ultimo bit WGM02 se encuentra en el registro TCCR0B). Eneste modo, el registro TCNT0 se incrementa en cada ciclo de reloj hasta llegaral valor 0xFF donde se desborda y vuelve al valor 0x00, activando a la vez lasenal de overflow (TOV0). Esta senal no volvera a 0 de forma automatica, serael programador el encargado de realizar dicho cambio. Si esta habilitado el bitde interrupcion por desbordamiento, cuando se produzca el mismo se generarauna interrupcion.

Modo CTC o Clear Timer on Compare Match: Para activar este mododebemos configurar los bits WGM02:0 al valor 2, esto es, ((010)). En este modoel registro OCR0A se utiliza para variar la resolucion del timer. La diferenciacon el modo normal es que en este modo cuando se alcanza el valor configuradoen el registro OCR0a, el registro TCNT0 se pone de nuevo al valor 0. En laFigura 6.2 se puede ver un esquema que ejemplifica su uso. TCNT0 empiezaen el valor 0 y va ascendiendo hasta llegar al valor configurado en OCR0a(raya horizontal que indica el valor de comparacion). Una vez que se alcanzadicho valor la bandera OC0a del registro TIFR0 se activa de tal modo que siel programador comprueba dicho bit en su programa sabra si se ha alcanzadoel valor de comparacion. La ejecucion repetida del timer en modo CTC nosgenera una forma de onda en el pin de salida OCn. Esta forma de onda sepuede ver en un osciloscopio si conectamos una de las puntas del mismo alpin asociado a la senal OC0 en el caso de que el timer este configurado parapermutar dicho pin en cada acierto de comparacion (esto se configura en elregistro TCCR0A, TCCR0B). En el diagrama de la Figura 6.2 se muestrandiferentes valores del registro de comparacion OCR0a, variando el periodo de lasenal. Segun el datasheet para generar una senal cuadrada con una frecuenciadeterminada podemos utilizar la formula mostrada en la Ecuacion 6.1. Masadelante, en los ejemplos, veremos como utilizar esta formula y como seleccionarlos valores para cada uno de los parametros.

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 WidthModulation) (con el objetivo de simplificar), se habilita mediante la activacionde los bit WGM02:0 = 3 o 7. Con este modo se nos proporciona un manerade generar formas de onda PWM de alta frecuencia y alta precision. El timercuenta 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 registroOCR0A cuando es 7. Posee dos modos, salida de comparacion no-invertida oinvertida. En el modo no-invertida la salida se pone a 0 cuando se alcanza elvalor de comparacion y a 1 cuando se llega al valor BOTTOM. En los ejemplosveremos como se materializa este modo pero para que el lector se haga una idea,este modo nos permite ((indicar)) al microcontrolador que porcentaje de tiempoqueremos que el pin este en alto y cuanto tiempo queremos que este en bajo,pudiendo emular valores analogicos variando el ancho del semiciclo positivo dela senal generada. Si por ejemplo alimentamos un motor con una frecuencia detrabajo ((X)), podremos variar su velocidad modificando el porcentaje de tiempoque se entrega energıa al motor. En la Figura 6.3 se puede ver un ejemplo deuso del modo PWM, a continuacion pasaremos a explicar el mismo aunque enlos ejemplos se vera de manera mas detallada.

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 seleccionadode modo que hasta que el mismo no se selecciona (tercera barra horizontal) nose modifica el estado del pin OCnX. Una vez que se selecciona un valor paraOCRnx (OCR0a,b), cuando el contador alcanza el valor del registro OCRnx sepone a estado bajo el pin OCnx en el caso de la senal normal y se pone en altoen el caso de la senal invertida. El contador continua su incremento hasta quellega al valor top. Sera cuando alcance este valor cuando el pin vuelva a cambiarsu estado.

Como puedes observar, en funcion del valor del registro OCRnx podremos man-tener mas tiempo la senal en alto, o lo que es lo mismo para nuestro ejemplopodremos entregar mas energıa al motor.

La ecuacion para el calculo de la frecuencia se puede consultar en Ecuacion 6.2.

Modo Fast PWM con correccion de fase: Este modo es muy parecidoal Fast PWM con la unica diferencia que la salida esta en fase y ademas lafrecuencia maxima que se puede alcanzar es la mitad con respecto al modoPWM ya que ahora el contador trabaja en modo ascendente y descendente.La formula para calcular el valor de TOP y del prescaler es la mostrada enEcuacion 6.3.

f0Cnx =fclk

2 ∗N ∗ (1 + OCRnX)(6.1)

f0Cnx =fclk

N ∗ (1 + TOP )(6.2)

f0CnxPCPWM =fclk

2 ∗N ∗ TOP(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 registrosque estan implicados en su funcionamiento es el momento de programar el ATmegapara poner en practica lo aprendido.

Como el Arduino utiliza el timer 0 para diferentes funciones como por ejemplo lafuncion delay() dejaremos de lado este timer y utilizaremos en su lugar el timer 1. Ladiferencia entre el timer 0 y el timer 1 es unicamente la cantidad de bits de ancho asıcomo el numero de funciones que poseen cada uno. Tambien se podrıa haber utilizadel timer 2 el cual es de 8 bits.

6.1. TIMERS 88

Ejemplo de uso basico

En este primer ejemplo veremos como podemos configurar el timer unicamentepara 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 estemodo es, como parece logico, el tiempo en el cual queremos que nos avise. En estecaso, vamos a imaginar que queremos que nos avise cada 4 segundos de tal maneraque cada vez que se alcance ese instante de tiempo imprimiremos por el puerto serieun 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 continuacion,explicaremos como configurar cada registro. Es importante tener en cuenta que todasesta informacion esta extraıda del datasheet del microcontrolador. Es imposibleconfigurar bien un microcontrolador sin leerse el datasheet varias veces.

El registro TCCR1A posee diferentes bits que nos permiten modificar el com-portamiento de la unidad de comparacion y dos de los bits que configuran el com-portamiento del propio timer (WGM11 y WGM10 ). La razon por la cual solo sepusieron dos bits de los cuatro necesarios para configurar el timer en este registro esuna decision de diseno pero los bits WGM12 y WGM13 se encuentran en el registroTCCR1B. En este caso buscamos configurar el timer en modo normal y deshabilitarlas unidades de comparacion pero todavıa nos falta un dato mas, el prescaler. Paracalcular el valor del prescaler en primer lugar calculamos el tiempo que tarda nuestroATmega en incrementar el valor del registro TCNT1. Si estamos en un sistema conreloj de 16Mhz, el tiempo por cada ciclo sera igual a 1/16000000 o lo que es lo mismo:62.5 ns. Teniendo en cuenta esto, el tiempo que tardara el timer en desbordarse seraigual a: 216 ∗ 62,5−9 = 4ms. Como puedes observar este valor es muy pequeno y nonos sirve para alcanzar esos 4 segundos que buscamos. La solucion podrıa ser aumen-tar el numero de bits del timer o por otro lado aumentar el tiempo que tarda enincrementar el registro TCNT. Como la primera opcion (aumentar el numero de bits)no depende de nosotros y solo podrıamos tenerla en cuenta si tuvieramos un timercon mas bits (no lo tenemos), abordaremos la segunda solucion, es decir, aumentar eltiempo que tarda el timer en incrementar el registro TCNT. Si utilizamos un pres-caler de 1024 entonces significara que por cada 1024 ciclos de reloj incrementaremosen uno el valor del registro TCNT1 por lo que volviendo a los calculos anteriores eltiempo que tardarıa en desbordarse el timer serıa igual a: 1024∗216 ∗62,5−9 = 4,194s.¡Lo conseguimos! ya tenemos el timer configurado para que tarde 4 segundos. . . Enrealidad esto no es del todo cierto dado que segun nuestros calculos el tiempo es de4.194 s por lo que intentaremos ajustar mas el valor, recuerda, 1 decima de segundopuede significar ganar o perder una carrera en formula uno pero para una primeraaproximacion no esta nada mal. Si queremos un tiempo exactamente igual a 4 segun-dos unicamente tenemos que realizar una ((regla de tres)) sabiendo que el tiempo quetarda en incrementar el registros TCNT es de 1024∗ 1

16000000 obteniendo el resultadode que el numero de incrementos que se realizaran en 4 segundos sera igual a 62500.

Con todos estos calculos ya sabemos que el valor del prescaler es de 1024 y que nosgustarıa que en vez de a los 62536 incrementos el TIMER se desbordara a los 62500.Para esto ultimo unicamente tenemos que modificar el valor de TOP o modificar elvalor en el que empieza el registro TCNT.

En el codigo mostrado en Cod. 6.1 puedes ver como se instancian los pasos ante-riormente mencionados. A continuacion explicaremos las partes mas importantes del

6.1. TIMERS 89

codigo.En el setup() se llama a la macro cli(), como ya se explico en la Capıtulo 5 esta

macro unicamente deshabilita las interrupciones de manera global, de manera quepodamos realizar toda la configuracion sin miedo a ser interrumpidos. Una vez desha-bilitadas las interrupciones, el siguiente paso es configurar tanto el registro TCCR1Acomo el registro TCCR1B. El valor 0 asignado a TCCR1A tiene que ver con lo ex-plicado anteriormente. En la Figura 6.4 puedes la definicion del registro. El registroTCCR1B sin embargo sı que tiene un valor ((util)) asignado. En un primer momentoel lector podrıa preguntarse por que se asigna dicho valor. La razon se encuentra enla configuracion del prescaler. Si echamos un vistazo a la Figura 6.6 y a la Figura 6.5podemos ver que para aplicar un prescaler de 1024 tenemos que asignar el valor ((101))a los bits ((CS1:2-0)). Como todos los demas bits deben estar a cero (ver explicacionanterior y datasheet) el valor ((00-00101)) en decimal es 5.

El siguiente paso consiste en configurar el registro TIMSK1. En este caso hemosoptado por otro modo de asignacion el cual se explica en Seccion B.1. El bit que hayque habilitar es el TOIE1 que nos permitira indicar al microcontrolador que queremosinterrumpir nuestro programa cuando se produzca un overflow.

Por ultimo, en la fase de setup() tenemos que poner el valor inicial de 536 al registrocontador. Es muy importante que siempre que modifiquemos un registro de 16 bitsen la arquitectura AVR, en primer lugar modifiquemos el registro que representa laparte alta (H) y despues el que representa la parte baja (L). En este caso el valoren binario de 536 es ((00000010 00011000)) por lo que la parte alta la escribimos enel registro TCNT1H y la parte baja en el registro TCNT1L. Como puedes observaren este caso hemos utilizado la notacion binaria con el objetivo de que se cubrantodas las posibilidades, aunque la mas portable es la notacion utilizada en el registroTIMSK1.

Lo que resta del codigo ya ha sido explicado en capıtulos anteriores y por lo tantono se volvera a explicar.

Ejemplo de uso modo CTC

En este ejemplo cubriremos el modo de uso CTC. Este modo es el mas utilizadocuando queremos medir tiempos dado que es mas sencillo que el modo normal ypermite actuar sobre un pin de manera inmediata sin necesidad de ninguna instruccionsoftware, ademas de contar con una precision mayor al poder modificar el valor TOP.

Se configurara el timer 1 de modo que cada medio segundo se encienda un led y ca-da segundo se encienda otro. En este caso podremos ver como sin necesidad de ninguncodigo extra podemos realizar acciones cada X segundos y con una precision bastantebuena. Si sustituyes el led por un transistor que sustituya el boton de la camara defotos tendras un temporizador de obturacion. (Solo realizar esta modificacion si setienen los conocimientos suficientes sobre electronica)

El modo de proceder es muy parecido al de Subseccion 6.1.3 con la diferencia queen este caso tendremos que configurar el pin de salida y ademas modificaremos elvalor de ((overflow)) del timer (aunque como ya veremos, realmente no es el valor de((overflow))).

Los calculos para el tiempo en este caso se dejaran como tarea para el lectorde modo que pueda comprobar si ha comprendido los conceptos explicados en laSubseccion 6.1.3

En Figura 6.7 podemos ver los diferentes modos que posee el timer para actuar

6.1. TIMERS 90

1 v o l a t i l e long l a s t u p d a t e = 0 ;2 v o l a t i l e long las t compare update = 0 ;3 i n t l ed = 13 ;4 void setup ( ) {5 c l i ( ) ;6 TCCR1A = 0 ;7 TCCR1B = 5 ; //0b00−−01018 TIMSK1 |= (1 << TOIE1) ;9 TCNT1H = 0b00000010 ;

10 TCNT1L = 0b00011000 ;11 S e r i a l . begin (9600) ;12 s e i ( ) ;13

14 }15

16

17 void loop ( ) {18 i f ( l a s t u p d a t e != last compare update ) {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 compare update = 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 = micros ( ) ;26 TCNT1H = 0b00000010 ;27 TCNT1L = 0b00011000 ;28 }

Codigo 6.1: Ejemplo de uso TIMER

Figura 6.5: Bits de configuracion del presclarer

Figura 6.6: Registro TCCR1B

6.1. TIMERS 91

Figura 6.7: Bits para configuracion del pin de salida

sobre la salida. En nuestro caso elegiremos el modo ((01)) que nos permite permutarla salida en cada comparacion con los registros OCR1A/B.

El siguiente paso consiste en configurar el timer para que actue en modo CTC.En la Figura 6.8 se puede ver la tabla de configuracion en funcion de los bits WGM.A continuacion, explicaremos los referentes al modo CTC.

Si configuramos los bits WGM en el modo ((0100)) el timer empezara a contarhasta llegar al valor configurado en el registro OCRxA y OCRxB, en este momento segenerara una interrupcion siempre y cuando el bit OCIEXa o OCIEXb del registroTIMSKX este habilitado, una vez que ocurra ese evento, el contador seguira ascen-diendo hasta llegar al valor TOP donde se generara la interrupcion de overflow yfinalmente se volvera a contar desde 0.

El lector puede observar que lo que estamos realizando en este ejercicio es generardos frecuencias a partir de un unico timer, esto no siempre sera posible, en estecaso al ser una frecuencia multiplo de la otra no habra ningun problema.

Para ejemplificar todo lo explicado pasaremos a analizar el codigo Cod. 6.2.

En primer lugar, se bloquean las interrupciones con la macro cli() esto mismose podrıa haber hecho con la funcion noInterrupts(), sin embargo, es mas eficientey portable la segunda opcion. Seguidamente se ponen a cero los dos registros deconfiguracion del timer de modo que tengamos certeza de que partimos de inicialmentede 0. Basandonos en el datasheet del ATmega328, configuramos el registro TCCR1A.Como el lector puede observar el unico bit que tiene que estar habilitado es el 0 de loscomparadores, es decir, el COM1B0 y el COM1A0. Observa como en este caso se hautilizado la notacion que se considera mas portable (lınea 5). Con el registro TCCR1Bhacemos lo mismo, solo que en este caso los bits a activar son los del prescaler y losdel modo de funcionamiento del timer.

Para ((permitir)) al timer enviar interrupciones al procesador, debemos activar elbit OCIE1A. Con esto conseguiremos generar dos frecuencias distintas. La frecuenciade dos hercios corresponde al comparador ((A)) mientras que la frecuencia de un herciopertenece al registro ((B)). Por otro lado, el registro ICR1 es el encargado de situar elvalor de TOP. Es importante observar que tanto el valor de ICR1 como el de OCR1Bes el mismo ¿por que?.

Una vez configurados todos los registros se procede a poner los pines de entra-da/salida en modo salida. Hay que tener en cuenta que hemos anadido un pin mas,esto nos permitira generar una senal de referencia para saber que nuestros calculosson correctos. La eleccion de estos pines se debe al mapeo que realiza el Arduinode los pines del ATmega328. Si quieres saber mas sobre esto se recomienda ver elesquematico del Arduino UNO ası como el datasheet del ATmega328.

En la rutina de interrupcion TIMER1 COMPA vect() se comprueba el valor del

6.1. TIMERS 92

Figura 6.8: Configuracion modo CTC

registro y en funcion de si es menor que el registro ((B)) se modifica el valor de lasiguiente comparacion. Se deja al lector el razonar el porque de estas comprobaciones.

En el loop se genera una senal de un hercio con el objetivo de compararla con lagenerada por nosotros mediante el timer.

Ejemplo de uso modo Fast PWM

El ultimo modo que veremos en profundidad sera el modo Fast PWM. Como yase comento anteriormente, con este modo podemos modificar la cantidad de tiempoque pasa una senal en alto y en bajo, es decir, podemos modificar el ciclo de trabajo.

Existen diversas aplicaciones en las que se utilizan este tipo de senales, comoejemplos podemos nombrar los servos de los aviones radio control o los variadores delos motores industriales, etc.

En este ejemplo veremos como utilizar el modo Fast PWM para mover un servocomo el de la Figura 6.10. Este servo esta incluido en el kit ((Grove-Starter Kit forArduino)).

Para mover un servo el primer paso que tenemos que seguir es el de estudiar losfundamentos de los servos. Un servo es un dispositivo electromecanico, que medianteuna logica empotrada consigue decodificar una senal de entrada para generar comoresultado un desplazamiento a una posicion determinada. Estos dispositivos se suelenutilizar en aeromodelismo pero su campo de aplicacion es mucho mas extenso, otroejemplo podrıan ser los robots.

A continuacion pasaremos a explicar cada una de las partes que forman un servo-motor:

Control de posicion: El control de posicion basicamente es un pequeno con-trolador que decodifica la senal de entrada, la compara con el feedback y decidesi mover o no el motor de corriente continua. (Ver Figura 6.9)

Motor de corriente continua: Es la parte fundamental en el apartado mecani-co y unicamente consiste en un motor conectado a un ((puente en H)) y unareductora que permite transformar la mayor parte del giro en torsion.

6.1. TIMERS 93

1 void setup ( ) {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 = 0b11110100 ;11 ICR1L = 0b00100011 ;12

13 OCR1AH = 0b01111010 ;14 OCR1AL = 0b00010001 ;15

16 OCR1BH = 0b11110100 ;17 OCR1BL = 0b00100011 ;18

19 pinMode (9 ,OUTPUT) ;20 pinMode (10 ,OUTPUT) ;21 pinMode (7 ,OUTPUT) ;22

23 S e r i a l . begin (9600) ;24

25 s e i ( ) ;26

27 }28

29 ISR (TIMER1 COMPA vect) {30 // Se 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 = 0b01111010 ;35 OCR1AL = 0b00010001 ;36 }37

38 }39 void loop ( ) {40 d i g i t a l W r i t e (7 ,HIGH) ;41 delay (500) ;42 d i g i t a l W r i t e (7 ,LOW) ;43 delay (500) ;44 }

Codigo 6.2: Ejemplo de uso TIMER modo CTC

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 dife-rencia un servo de otro. En funcion de la calidad de los mismos el movimientosera mas o menos suave. Normalmente el potenciometro de feedback va unidoal sistema de engranajes.

Para mover un servo a una posicion tenemos que enviar una senal con unos parame-tros concretos. Normalmente los servomotores de aeromodelismo utilizan una frecuen-cia de 20 Hz por lo que la senal viajara con un periodo de 50 ms. En la Figura 6.11puedes ver el principio de funcionamiento.

Normalmente se confunden los terminos PWM y PPM, a continuacion intentare-mos clarificar ambos conceptos. Desde el ATmega328 generaremos una senal a unafrecuencia determinada, esa senal la podemos modificar de modo que el ciclo de tra-bajo vaya desde el 0 % hasta 100 %. El servomotor espera un pulso de una duracionconcreta, por ejemplo, siguiendo los tiempos de la Figura 6.11, el servo espera unasenal de 1ms para colocarse en la posicion 0. Si nuestro periodo es de 50 ms, un pulsode 1ms sera equivalente a un 2 % de la senal en alto. Si modificamos el valor de lafrecuencia por ejemplo a 200 hz, el periodo sera 5 ms y un 2 % de dicha senal sera0.1 ms. Con esto llegamos a la conclusion de que para codificar un movimiento elservomotor espera pulsos de un tiempo determinado y no pulsos de un determinadovoltaje (PWM).

En resumidas cuentas, para mover nuestro servo lo primero que tendremos quehacer sera configurar un timer en nuestro caso el timer 1 en modo Fast PWM, luegocalcular el valor del registro TOP para generar una frecuencia de 20 Hz (se deja comotarea para el lector el calculo de dicho valor) y por ultimo anadir la logica de controlpara calcular los ciclos de trabajo en funcion de los grados que se desee mover. (Aquı

6.1. TIMERS 95

1 ms

1,5 ms

2 ms

20 ms

Figura 6.11: Diagrama de tiempos de un servo

1 void setup ( ) {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 = 5000 ;8 pinMode (9 ,OUTPUT) ;9 pinMode (10 ,OUTPUT) ;

10 pinMode (7 ,INPUT) ;11 S e r i a l . begin (9600) ;12 s e i ( ) ;13 OCR1A = 375 ;14 }15

16 void loop ( ) {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 . pa r s e In t ( ) ;19 }20 }

Codigo 6.3: Ejemplo de uso PWM con servo

unicamente anadiremos la logica de control para modificar el ciclo de trabajo a unvalor fijo, se deja como tarea para el lector el anadir al codigo la logica necesaria paragenerar un ciclo de trabajo u otro en funcion de los grados que se desee mover).

En Cod. 6.3 se puede estudiar el codigo implementado. A continuacion explicare-mos como siempre las partes mas interesantes del mismo.

En primer lugar, como siempre, se deshabilitan las interrupciones mediante lamacro cli(), seguidamente se configura el registro TCCR1A siguiendo el datasheet demodo que la senal permute en cada comparacion con OCR1A.

En el bucle loop() unicamente comprobamos si existen datos en el buffer de recep-cion serial. Si existe algun dato lo convertimos a entero y lo escribimos en el registro decomparacion. Esto nunca se debe realizar dado que no hemos realizado ningun controlde entrada y el usuario podrıa ingresar cualquier valor, incluso letras o sımbolos quepodrıan causar serios problemas en un diseno en produccion.

Los valores recomendados son 125,250,375,500 que corresponden a 0,45,90,135,180

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 funcion del servomotor utilizado, en este caso hemos utilizado 2.5porque tras probar hemos visto que es el valor al que mejor responde el servomotor.Ademas hemos utilizado una frecuencia de 50Hz, por lo que tenemos un periodo de 20ms. 2.5 ms es un 12,5 % del periodo. Si el periodo esta determinado por el valor TOPalmacenado en el ICR1, en este caso 5000, un 12,5 de 5000 sera igual a 625. Comoya hemos dicho estos valores hay que probarlos y en el caso del servo de ((Seeed)) estevalor hace que el servo se bloquee porque el potenciometro no tiene tanto recorrido,por esta razon hemos recomendado un valor de 500.

6.2. Multitasking

Hasta ahora hemos visto como funciona el core de tiempos de Arduino, al finaltodo se resume en una palabra, TIMERs. En esta seccion veremos como abstraernosde todos los detalles de implementacion y utilizando las librerıas de Arduino crearnuestros programas ((paralelos)).

La idea que reside en esta implementacion del ((multitasking)) es muy sencilla,nuestro programa principal actuara de ((scheduler)) de tal modo que en su bucle loop()comprobara para cada elemento si es su turno y si es ası le concedera el ((procesador))para que realice las acciones pertinentes.

En la industria se suele trabajar con dispositivos PLC, estos dispositivos se progra-man de una manera diferente a como estamos acostumbrados. La principal diferenciaes que no se programa de forma textual (hoy en dıa se esta incorporando a la industriaesta manera de programar los PLC), por el contrario se programan mediante diagra-mas Ladder. Estos diagramas tienen un elemento llamado lıneas de alimentacion querepresentan la entrada de senales a nuestro elemento de proceso y las salidas del mis-mo. 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 disenosa los entornos industriales y que en la medida de lo posible se minimice el coste demantenimiento o lo que es lo mismo el tiempo requerido para entender nuestro pro-grama. En este libro hemos buscado una solucion estructurada que ayudara bastantea la consecucion de este objetivo.

Apoyandonos en la orientacion a objetos encapsularemos cada elemento en unaclase y dotaremos a la misma de un metodo llamado ((scan)), este metodo para simpli-ficar el diseno nos devolvera un valor ((boolean)) con la informacion del ciclo (correcto,defecto), aunque lo correcto serıa encapsular el valor de retorno en otra clase que nosbrindara mas informacion.

La orientacion a objetos no es el objetivo de este libro, unicamente lo utilizaremoscomo una manera de mostrar al usuario la verdadera importancia de estructurar elcodigo y para que pueda ver los beneficios inmediatos de su utilizacion. El codigoque se utilizara en los ejemplos tratara utilizara los conceptos mas sencillos posiblesen cuanto a orientacion a objetos se refiere intentando evitar aspectos mas complejoscomo el polimorfismo, herencia, etc.

Este es un buen momento para que te animes a leer el Seccion C.1 y preparestu entorno de desarrollo para estos ejemplos, aunque nosotros sigamos utilizando elentorno Arduino te servira para ver las bondades de Eclipse en cuanto tenemos unprograma medianamente complejo.

Como ya se comento en la Seccion 6.1, para medir tiempos podemos utilizar varias

6.2. MULTITASKING 97

Figura 6.12: Ejemplo de programa en ladder

6.2. MULTITASKING 98

funciones de Arduino, nosotros utilizaremos la funcion millis() que nos permite sabercuanto tiempo ha pasado desde que se ha iniciado el programa. Para aplicaciones queestaran activas durante mucho tiempo hay que tener en cuenta que el contador sepondra a 0 en 50 anos, por lo que tendremos que salvar de alguna manera el tiempo(por ejemplo guardandolo en la memoria EEPROM).

6.2.1. Encendiendo y apagando un led de manera profesional

El led ha sido nuestro companero desde el primer capıtulo, con el hemos realizadola mayorıa de ejemplos y como no puede ser de otra manera, en este primer ejemplosobre multitasking recurriremos de nuevo a el. En la mayorıa de los ejemplos quepuedes encontrar por Internet veras que para encender y apagar un led se utiliza lafuncion delay(). En entornos industriales esta funcion es muy temida y la razon esbastante sencilla: Si un programador que esta encargado de mover un motor utiliza lafuncion delay() para programar las esperas en el arranque ((estrella-triangulo)) puedehacer 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 cadasegundo. Para ello en vez de dejar que pase el tiempo vamos a cuantificar este tiempoy mientras tanto aprovecharemos para hacer trabajo util.

En Cod. 6.4 puedes encontrar el codigo que a continuacion comentaremos. Enprimer lugar en el setup() como siempre se inicializan las comunicaciones ası comose configuran los pines en el modo correspondiente (hasta aquı nada nuevo para ellector).

Lo verdaderamente importante se encuentra dentro del bucle loop(), es aquı dondepodemos ver dos funciones: scan led() y scan communications() estas funciones son lasencargadas de encender el led y mandar mensajes por el puerto serie respectivamente.Los ((timing)) de estas acciones seran los siguientes:

Led: Se encendera y apagara cada segundo.

Comunicaciones: Se mandara un mensaje con la cadena .Estoy aquı ”mas eltiempo en milisegundos cada 100 milisegundos.

Empecemos por la funcion scan led(), se utiliza una variable de tipo estatica. Larazon por la cual hemos utilizado este tipo de variable es unicamente educativa demodo que el lector sepa que estas variables solo se inicializan una vez y luego cada vezque se entra a la funcion se mantiene el valor anterior, es decir, es practicamente igualque una variable declarada fuera de todos los metodos tal y como habıamos hechoanteriormente. Mediante esta funcion registramos el valor de la ultima ejecucion de laaccion a controlar. En el ((if)) es donde se encuentra el ((scheduler)). En este ((if)) com-probamos si ha pasado el tiempo mınimo que habıamos programado. Es importanteobservar que solo podemos asegurar un tiempo mınimo y no un tiempo maximo. Si loque buscamos es acotar el tiempo en el cual se debe realizar una determinada accion,en ese caso necesitaremos dotar a nuestro sistema de un ((scheduler)) apropiativo demodo que cuando se alcance un determinado tiempo maximo cancele todas las ta-reas y vuelva a la tarea que requiere atencion. Un sistema operativo muy liviano quepermite dotar al sistema de un ((scheduler)) apropiativo es el sistema FreeRTOS. Siejecutas el codigo podras ver que efectivamente por cada mensaje y permutacion delled, se imprimen 5 mensajes con el mensaje ((I’m here xxxx)).

En la Subseccion 6.2.2 veremos una manera mas elegante de ordenar el codigo comose explico anteriormente, con el objetivo de facilitar el mantenimiento del mismo.

6.2. MULTITASKING 99

1

2 const i n t l e d p i n = 13 ;3

4 #d e f i n e TIME TOOGLE LED 5005 #d e f i n e TIME COMMUNICATION SEND 1006

7 bool s c a n l e d ( ) {8 s t a t i c long 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 l W r i t e ( l ed p in , ! d i g i t a lRead ( l e d p i n ) ) ;12 S e r i a l . p r i 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 turn true ;16 }17

18 bool scan communications ( ) {19 s t a t i c long las t t ime communicat ion = 0 ;20 i f ( ( m i l l i s ( ) − l a s t t ime communicat ion ) >= TIME COMMUNICATION SEND)

{21 l a s t t ime communicat ion = m i l l i s ( ) ;22 S e r i a l . p r i n t ( "I’m here: " ) ;23 S e r i a l . p r i n t l n ( las t t ime communicat ion ) ;24 }25

26 }27

28 void setup ( ) {29 pinMode ( l ed p in , OUTPUT) ;30 S e r i a l . begin (9600) ;31 }32

33 void loop ( ) {34 s c a n l e d ( ) ;35 scan communications ( ) ;36 }

Codigo 6.4: Ejemplo de multitasking

6.2. MULTITASKING 100

6.2.2. Encendiendo y apagando un led de manera mas profe-sional

La orientacion a objetos, como ya se ha comentado, puede reducir muchısimo loscostes de mantenimiento ası como posibles fallos en la implementacion. Mantener unprograma 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 quese le ha asignado la tarea de implementar un modulo ((led)) para proyecto futuro.

La empresa en su guıa de estilos tiene definida una interfaz clara de que metodosse deben exponer. En este caso, para simplificar, supondremos que unicamente estaespecificado el metodo scan.

Para que nuestro trabajo pueda ser reutilizado en los demas proyectos tendremosque encapsular el mismo lo maximo posible.

En Cod. 6.5 puedes ver el ejemplo completamente implementado. A continuacionexplicaremos las partes mas importantes del mismo.

En C++, para declarar una clase, hay que utilizar la palabra reservada class. Eneste caso hemos creado una clase que se llama LED. Esta clase ofrece al exterior unmetodo llamado scan(), tal y como se nos indico en la guıa de estilos. La clase tambientiene un metodo privado update() que es el encargado de comprobar si ha llegado eltiempo de permutacion. Ten en cuenta que este metodo es privado y por lo tanto nole afecta la guıa de estilos. Por otro lado, el constructor recibe el numero de pin y eltiempo en el cual se quiere permutar el led (lınea 16).

Como puedes ver esta manera es mucho mas elegante dado que si queremos crearotro led, unicamente tenemos que anadir otra lınea como LED led1 (13, 500). En esteejemplo tan sencillo puede que no se vean las virtudes de este metodo, pero imaginatener que controlar 200 LEDs, ¿ves ahora la diferencia?, probablemente con el metodotradicional (copia de codigo) y variables globales nos quedarıamos sin memoria en elmicrocontrolador y la tarea de mantenimiento serıa muy tediosa, con el incrementoen el coste asociado.

Se recomienda al lector extender este ejemplo con una nueva clase ServoMotorque controle un motor de manera sencilla apoyandose en el codigo Cod. 6.3 y en todolo aprendido hasta ahora. Una vez creada la clase anadela al proyecto y observa lasbondades del multitasking en Arduino.

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 long l a s t u p d a t e t i m e ;5 long t ime to update ;6

7 void update ( ) {8 i f ( ( m i l l i s ( ) − th i s−> l a s t u p d a t e t i m e ) >= time to update ) {9 th i 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 l W r i t e ( p i n l e d , ! d i g i t a lRead ( p i n l e d ) ) ;11 }12 }13

14 pub l i c :15

16 LED( i n t p in l ed , long t ime to update ) {17 th i s−> p i n l e d = p i n l e d ;18 th i s−> l a s t u p d a t e t i m e = 0 ;19 th i s−>t ime to update = t ime to update ;20 pinMode ( p i n l e d , OUTPUT) ;21 }22

23 bool scan ( ) {24 update ( ) ;25 }26 } ;27

28 LED led1 (13 , 500) ;29

30 void setup ( ) {31

32 }33

34 void loop ( ) {35 l ed1 . scan ( ) ;36

37 }

Codigo 6.5: Ejemplo de multitasking orientado a objetos

6.2. MULTITASKING 102

APENDICE A

CONSTRUYENDO NUESTROPROPIO ARDUINO

A.1. Introduccion

En este apendice se guiara al lector en la construccion de su propio Arduino Uno,el cual sera completamente compatible con un Arduino comprado en la tienda oficial.

Como se comento en el Capıtulo 1, Arduino mantiene la filosofıa del software libre ynos proporciona todos los esquemas necesarios para construir o mejorar su plataforma.El Arduino que vamos a construir en este apendice es muy sencillo y unicamentetendra lo indispensable para que el lector pueda conectarlo al ordenador y ejecutarlos ejemplos realizados en este libro, por lo que eliminaremos muchos elementos deseguridad y filtrado que tiene el Arduino original con el objetivo de simplificar sumontaje.

A.2. Componentes necesarios

A continuacion, se detallara una lista con los componentes que se necesitan parael montaje. Muchos de los componentes tienen equivalentes por lo que en este manualhemos optado por los mas comunes, no obstante, si el lector encuentra un equivalenteal componente no habra problema en sustituirlo, aunque se recomienda escoger en lamedida de lo posible los citados en la siguiente lista.

1. Protoboard

2. Fuente de alimentacion 5-18V CC

3. Cables Jumper

4. LM7805

5. Condensador electrolıtico 10 uF (x2)

103

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 des-de ahora), aunque pueda parecer que la colocacion es algo meramente decorativo y((superfluo)) es muy importante tenerlo en cuenta. La diferencia entre poner el con-densador de desacoplo a 1 centımetro del microcontrolador o ponerlo a 20 centımetrospuede ser determinante en algunos sistemas2. En la Figura A.1 se han dispuesto loscomponentes de modo que al lector le sea sencillo identificar los mismos y situarlosen el circuito por lo que, se recomienda que realice ligeras modificaciones mantenien-do las reglas de conexion (mismo esquema) de modo que los componentes esten masproximos entre ellos.

El circuito se puede dividir en tres partes. A continuacion, describiremos cada unade ellas:

Etapa de alimentacion: El circuito esta preparado para ser alimentado conun voltaje desde 5 V a 18 V de corriente continua, es importante que lacorriente sea continua pues de lo contrario el circuito no funcionara, pudiendollegar a dejarlo inservible. Si el lector no tuviera una fuente de corriente conti-nua con estas caracterısticas, puede montarse una pequena fuente, para lo cualnecesitara entre otras cosas un transformador (fuente con transformador) y unpuente de diodos (en Internet hay mucha informacion a cerca de las fuentes dealimentacion).

Lo siguiente es conectar los terminales a la protoboard. Existe un convenio deconexion por el cual se recomienda conectar el terminal positivo a la primerafila de la protoboard y el negativo a la segunda. El lector podra reconocer estasfilas porque normalmente vienen separadas del resto de la protoboard.

Ahora que la placa esta alimentada, solo queda situar los componentes tal cualaparecen en el esquema Figura A.2 tambien puedes guiarte por la Figura A.1aunque recuerda, esta no es la distribucion optima. Es importante que se respetela polaridad del condensador electrolıtico dado que de lo contrario, si sesupera la tension inversa umbral, podremos romper el mismo. Para saber comoconectar el condensador busca una lınea vertical de color blanco que indica elterminal negativo. (ver Figura A.1)

A la salida del integrado LM7805 (lınea naranja) tendremos 5 V estables quesera la ((fuente de alimentacion)) del microcontrolador ATmega328.

1USBASP es un programador para los microcontroladores AVR libre, por lo que desde la web delautor (http://www.fischl.de/usbasp/) el lector podra acceder tanto al firmware como al circuitopudiendo construir su propio programador.

2Los cables tienen una capacitancia y una inductancia que afecta al circuito, en disenos donde laprecision es muy importante, hay que tener en cuenta la longitud de los cables entre otros factores

A.3. ENSAMBLADO 105

5v-12v

16MHzCristal

Azul=TierraRojo=EntradaNaranja=5vestablesCyan=Cristal-------------------Marron=Rx(Arduino)-Tx(FTDI)Morado=Tx(Arduino)-Tx(FTDI)

Figura A.1: Protoboard de referencia

Figura A.2: Esquema de conexiones

A.3. ENSAMBLADO 106

Figura A.3: Patillaje ATmega328

Microcontrolador: El microcontrolador ATmega328 es el corazon del ArduinoUNO, es en el donde se cargaran los programas. En esta parte del montaje nosencargaremos de que el corazon empiece a ((latir)). Para ello, lo primero que hayque saber es como estan distribuidos los terminales. En la Figura A.3 se puedeobservar que los pines para alimentar el microcontrolador son el 7 y 8, pero...¿Donde empieza la numeracion? para ello se sigue un convenio por el cual todoslos circuitos integrados disponen de una marca que indica el pin con el inicio dela numeracion. En la Figura A.1 se puede observar el punto que indica el inicio.

Una vez que el los terminales de alimentacion del microcontrolador estan co-nectados a la salida del LM7805, ahora solo resta el incorporar un componenteque permita al ATmega328 latir a una frecuencia determinada (siguiendo laanalogıa del corazon humano) este componente es un reloj de cuarzo. El relojde cuarzo genera una onda cuadrada con una frecuencia determinada, normal-mente los microcontroladores poseen un reloj RC3 pero debido a su precisiony a que normalmente oscilan a una velocidad inferior, anadiremos el reloj decuarzo.

Siempre que se va a dotar al microcontrolador de un reloj externo se utiliza elmismo circuito, que consta de un reloj y dos condensadores, cada uno de ellosen paralelo al reloj. Cuanto mas cerca este el reloj de los terminales de conexiondel microcontrolador mayor precision, por lo que de nuevo se recomienda queunicamente se siga la Figura A.2 para guiarse y se modifique la disposicion delos elementos.

Finalmente falta comunicar el pin 20 (AVCC) con VCC, esto se mantendraası hasta que el lector quiera hacer uso del ADC (Digital Analogic Converter)cuando tendra que conectar el pin 20 a VCC a traves de un filtro de paso bajo.

Comunicacion: Ahora que el microcontrolador ya es capaz de oscilar a unafrecuencia determinada (en nuestro caso 16MHz) tenemos que situar el canal decomunicacion entre el computador y el microcontrolador. Si el microcontroladortuviera integrado un chip para USB unicamente tendrıamos que conectar loscables del USB al mismo, pero como el ATmega328 solo posee de comunicacionserial (para la programacion) tendremos que hacer una serie de modificacionesde 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 precision menor frente al reloj decuarzo.

A.4. PROGRAMACION DEL BOOTLOADER 107

no hay ningun problema, unicamente tendras que conectar el pin Tx al Rxdel ATmega328 (pin 2) y el pin Rx al Tx (pin 3).

• Ordenador con USB: Si tu ordenador unicamente dispone de puertos USB,tendras que utilizar un conversor como puede ser FTDI232RL que realizala labor de identificar el dispositivo y gestionar la UART de modo que losdatos que escribamos bajo la pila USB se trasmitan de forma correcta bajoel protocolo serial.

En el caso de estar utilizando el conversor FTDI232RL y estando conectadoslos puertos Rx y Tx a los pines Tx y Rx del ATmega328 respectivamente.Solo queda alimentar el microcontrolador FTDI232RL desde el LM7805 y yatendremos el Arduino listo para ser programado.

A.4. Programacion del Bootloader

Para que el ATmega328 se convierta en un Arduino, necesitamos cargar el bootloa-der, para ello podemos seguir diferentes metodos. En el este libro se explicara el((quemado)) con un programador USBasp el cual es completamente libre.

Antes de empezar a quemar el bootloader en primer lugar se explicara que es yque funciones realiza por nosotros.

El ATmega328 tiene una memoria Flash de 32 KB en la que se podrıa almacenarun programa compilado para este microcontrolador y ejecutarlo posteriormente. Paraprogramar esta memoria se necesitan herramientas como el programador STK-500 porlo que Arduino decidio anadir una interfaz USB de modo que realice la conversion delos datos que llegan por USB a un protocolo serial y los multiplexe en el tiempo delmismo modo en que lo harıa un programador como el STK-500.

El bootloader ocupa un espacio determinado en la memoria flash dado que real-mente es un programa que se inicia cuando alimentamos el microcontrolador eje-cutando un conjunto de instrucciones que permite al ATmega saber si hay algunapeticion para programar el Arduino y si es ası seleccionar la direccion de memoriadonde ((situar)) dicho programa. Si el bootloader no encuentra ningun peticion de pro-gramacion entonces salta a la direccion de memoria del bucle de setup del programay a continuacion empieza el bucle de operacion.

Como el lector habra podido apreciar, no es necesario ((quemar)) este bootloaderpero si no se quema el lector tendra que contar con algun programador AVR para((quemar)) el programa y ademas tendra que realizar la programacion por ISP medianteel conector ICSP4 lo cual es mucho mas incomodo.

A continuacion vamos a proceder a ((quemar)) el bootloader, para ello lo primeroque necesitamos es un programador de AVR, en este libro, como ya se ha dichoanteriormente se utilizara el programador libre USBasp. La instalacion de los driversdel mismo se deja como trabajo para el lector. Una vez que el programador estacorrectamente instalado, tenemos que realizar las conexiones, para ello hay que teneren cuenta que el conexionado ICSP hay que realizarlo con mucho cuidado.

4ICSP son las siglas de In Chip Serial Programming, con este conector, se permite reprogra-mar el microcrocontrolador sin necesidad de desoldarlo y por lo tanto aumentando la vida util delmicrocontrolador

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 ubicacion de cada uno de los pines se puede utilizar un multımetroy medir la diferencia de potencial (voltaje) entre un pin y tierra de tal manera queel pin que devuelva el voltaje de programacion (3.3-5V) sera el VTG o VCC y elpin comun sera tierra. El lector tambien se puede guiar por el cable rojo, de modoque este sera el pin 1. Existen alternativas para facilitar la conexion del programadoral Arduino como por ejemplo el conversor ((ICSP Header)) cuya unica funcionalidades proporcionar una interfaz ((inline)) que se podra conectar a la protoboard siendomucho mas sencilla la conexion final al ATmega.

Una vez que el lector haya localizado cada uno de los pines del conector o hayaadquirido un conversor, ya esta en disposicion de realizar la conexion entre el ICSP yel microcontrolador. Los pines son los proporcionados por la interfaz de comunicacionISP, es decir, GND, VCC, MISO (Master Input Slave Output), MOSI(Master OutputSlave Input), SCK (Clock), RESET. La conexion ISP es muy sencilla gracias a lodescriptivo de los nombres de sus pines, sin embargo, a continuacion se detalla lasconexiones 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 conectarel programador al conversor ICSP o en caso de no haber utilizado el conversor, conec-tar cada uno de los pines del conector ICSP al microcontrolador. Despues conectarel programador al computador, es importante que los drivers esten completamenteconfigurados, de lo contrario el proceso no se completara con exito. El siguiente pa-so 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,despues seleccionamos “Herramientas → programador → USBasp”, por ultimo hay

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 bienel lector podra quemar sus propios sketch.

A.4. PROGRAMACION DEL BOOTLOADER 110

APENDICE B

MANIPULACION DE REGISTROS

B.1. Introduccion

Cuando un disenador se enfrenta a la tarea de disenar una solucion informatica enun microcontrolador, debe manejar una serie de conceptos que normalmente no sonnecesarios o indispensables cuando se programa una solucion en un computador deproposito general. Tener claro donde almacenar una cadena de texto o que espacio dememoria asignar para una determinada tarea son algunos de estos conceptos.

En este apendice se va a guiar al lector en el aprendizaje de las operaciones logicascon bits. Es importante tener en cuenta que la informacion en un microcontroladorse suele almacenar en registros. Estos registros pueden almacenar datos de diferentenaturaleza, por ejemplo, un microcontrolador que forme parte de una impresora puedetener un registro para el control de la misma (calentar rodillo, situar en posicion inicial,iniciar operacion) y otro registro para los datos como por ejemplo la hoja a imprimir.Cuando el disenador programa la logica de la impresora, debera obtener y validar lainformacion contenida en estos registros. En este apendice se mostraran las diferentestecnicas que se utilizan para obtener y tratar los datos de los registros.

B.2. ¿Que es un registro?

Antes de tratar de realizar operaciones con los registros, debemos tener claro quees realmente un registro. Se puede explicar que es un registro desde dos perspectivas:la perspectiva logica y la perspectiva fısica. En este apendice nos centraremos en laprimera dado que buscamos el tratar con la informacion almacenada en los registrosy no el implementar fısicamente el mismo.

El registro es la unidad de almacenamiento que se encuentra en el nivel superiordentro de la jerarquıa de memorias (ver Figura B.1), es decir, es la memoria masrapida y mas cara de todas. Este es el motivo por el cual es un recurso limitado enlos computadores y tan preciado por los compiladores. Como ya hemos nombrado enla introduccion, existen registros de muchos tipos como por ejemplo los registros dedatos que tienen la funcion principal de almacenar datos. Un ejemplo de este tipo de

111

B.2. ¿QUE ES UN REGISTRO? 112

registros es el registro acumulador de la ALU de los microcontroladores PIC. Tambienexisten registros especıficos como el registro SP (stack pointer) de la arquitecturaARM el cual unicamente se utiliza para almacenar la direccion de memoria del((top)) de la pila.

Registros del procesador

Memoria caché(L1,L2,L3)

Memoria RAM(random access memory)

Disco duroAlmacenamiento secundario

Cloud(Copias de seguridad)

Capacidad Velocidad yCoste 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 depen-dera del proposito para el que se haya disenado. Por ejemplo, un registro quemantenga la direccion de la siguiente instruccion a ejecutar IR (instruction regis-ter) debera tener la capacidad suficiente que permita codificar las instruccionesde un programa. Si se quiere un computador con la capacidad de ejecutar 256instrucciones se debera disenar un registro con un ancho de 8 bits.1

2. Tecnologıa: Los registros fısicamente se disenan con una tecnologıa determina-da. Esta tecnologıa sera determinante a la hora de comparar otras caracterısticascomo la latencia, capacidad de integracion. . .

3. Direccion: Un registro queda identificado univocamente por su direccion dememoria como por ejemplo podrıa ser la direccion 0x00001A1B

Podemos imaginar un registro como una caja (ver Figura B.2) que tiene diferentescompartimentos. Cada uno de estos compartimentos es un bit. Dependiendo del tipode registro nos interesara toda la ((caja)) (registro) o unicamente uno de sus ((cajones))(bit). Por ejemplo, si tenemos un registro de datos, seguramente nos interese el registroentero y no uno de sus bit dado que normalmente cuando se accede a un registro dedatos se busca el valor del numero almacenado, esto no implica que no se puedaacceder a un unico bit, por ejemplo para saber si el numero es positivo o negativo enun numero representado en signo magnitud.

1Con 8 bits se pueden codificar 28 instrucciones

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 que es un registro, estamos en disposicion de realizar dife-rentes operaciones con ellos. En esta seccion y con el objetivo de que el lector puedaponer sus conocimientos en practica vamos a utilizar registros reales del microcontro-lador ATmega328, a continuacion se realizara una pequena descripcion de la utilidadde cada uno de estos registros:

DDRC (Data Direction Register C ): Este registro se encuentra en la direc-cion 0x07 y permite configurar los pines del puerto2 como entrada(0) o salida(1),en funcion del bit que se situe en cada uno de las posiciones del registro, porejemplo la configuracion 0b00000001 configura todos los puertos como entra-da(0) y el primero3 como salida(1)4.

PORTC (Port Output Register C ): Este registro sirve para activar lasresistencias de pull-up que el microcontrolador posee en cada uno de los pinesconfigurados como entrada. Si el pin esta configurado como salida (DDRX(n)== 1) este registro determinara el voltaje ((alto)) o ((bajo)) a la salida del pin.

PINC (Pin Input Register C ): Este registro normalmente formara parte delas estructuras ((IF ... THEN)), en cada uno de sus bits obtendremos el valor deun pin configurado como entrada. Si el valor del bit es 1 significara que el pinque representa tiene VCC como entrada, sin embargo, si el valor es 0 entoncessignificara 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, paraello sabemos que tenemos un registro especıfico como es el registro DDRC. Sabemostambien que la direccion de memoria del registro es la 0x07 y que podemos accedera el mediante el puntero DDRC que apunta a dicha direccion5. Si queremos activarel 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 undeterminado numero de pines.

3Es importante tener en cuenta que los bits se empiezan a numerar por 0.4Toda esta informacion se puede obtener del Datasheet del microcontrolador ATmega328.5Estos punteros son mapeados normalmente por la librerıa del fabricante.

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 unvalor esta expresado en binario cuando le antecede el prefijo ((0b)). El tercer elemento(0x01 ) representa el mismo valor pero en hexadecimal, es importante recordar, queun dıgito hexadecimal equivale a cuatro bits (el conjunto de 4 bits se le conoce comonibble). La ultima representacion es la mas comun pero no sera explicada hasta laSubseccion B.3.3

Ahora imaginemos que pasa si ocurrirıa si quisieramos configurar el pin 1 delpuerto ((c)) como salida. En principio actuarıamos de la misma manera, es decir,configurarıamos el valor 0b0000010, pero como el lector habra podido observar, elvalor del pin 0 cambiara y se configurara como entrada (0). La manera mas sencillade solventar este comportamiento es asignar el valor 0b00000011 pero ¿si no se conoceque pines estan como salida en un estado anterior, como se calcula el valor a escribir?esta pregunta sera contestada en la siguiente seccion.

B.3.2. ORing, activacion de un bit

Para evitar el comportamiento observado en la seccion anterior, se hace uso de laoperacion OR. Esta operacion permite activar un bit sin ((afectar)) a los demas bitsdel registro.

Imaginemos el mismo ejemplo que el de la Subseccion B.3.1, es decir, deseamosconfigurar el primer y segundo bit como salida. La solucion planteada en la primeraseccion es incomoda e ineficiente dado que se necesita saber que bits estan activadosanteriormente para poder calcular el valor a escribir. Por lo que vamos a aplicar lafuncion OR de la siguiente manera:

1 DDRC = DDRC | 0b10

De este modo conseguiremos el siguiente valor:

0b00000001

OR 0b00000010

-------------

0b00000011

Como el lector habra observado, el resultado es el esperado, tanto el pin 1 comoel 2 estan configurados como salida. La operacion ORing unicamente afecta a losvalores que poseen un 1 logico, esto es debido a que el 0 actua como operador neutro(A OR 0 = A). El operando que utilizamos para realizar la operacion OR sobre elregistro se llama ((mascara)).

B.3.3. Bit Shifting, movimiento de bits

La aritmetica booleana proporciona herramientas extremadamente utiles al pro-gramador. Por ejemplo, una de las mejoras que un compilador puede realizar es latransformacion de una multiplicacion de potencia 2 que normalmente lleva varios ci-clos (las unidades de multiplicacion suelen ser lentas) en una unica operacion logicallamada ((Bit Shifting)). En este apartado vamos a explotar una de sus utilidades

B.3. OPERACIONES CON REGISTROS 115

01234567

0 0 0 0 0 0 0 1

0 0 0 0 0 0 1 0

Figura B.3: Operacion: 1 << 1

01234567

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: Activacion de pin 3 sobre registro previamente configurado

que nos permitira hacer nuestro codigo mas legible y nos ahorrara mucho codigo in-necesario.

Continuando con nuestro ejemplo de la configuracion del pin 0 y 1 como salida,en este apartado se vera como construir o hallar el valor que debe ser utilizado comooperando extra en la operacion OR.En la Subseccion B.3.2 se utilizo el operando ((extra)) 0b00000010, a este operandocomo ya hemos dicho anteriormente, se le llama mascara. Para obtener este valorse recurre a la operacion Bit Shifting. Si queremos crear una mascara para el pin 1unicamente tenemos que utilizar la siguiente operacion (1 << 1) (en la Figura B.3esta representada graficamente la operacion).

Si quisieramos configurar el pin 3 como salida procederıamos del mismo modo,es decir, calcular la mascara y aplicarla mediante la operacion OR. Para calcular lamascara utilizamos la tecnica del Bit Shifting (1 << 3). En la Figura B.4 se puedever de forma grafica el procedimiento.

En el Cod. B.1 se puede ver un extracto de codigo donde se configura el pin PC6y PC7 como salida. Esta es la forma mas comun para la configuracion de registros. Esimportante observar que la operacion OR se realiza sobre el valor actual del registro(| =) y que se pueden realizar varias operaciones ORing en una unica asignacion.

B.3. OPERACIONES CON REGISTROS 116

1 #include <avr/io.h>

2

3 int main(){

4

5 // Data Direction Register C = 0b01100000

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 }

Codigo B.1: Ejemplo de asignacion mediante oring

01234567

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: Configuracion del pin 3 como entrada sobre registro previamente con-figurado (procedimiento erroneo)

B.3.4. ANDing, desactivando bits

Hasta ahora unicamente hemos configurado bits con el valor 1 pero ¿Se puedeconfigurar del mismo modo el pin con el valor 0?. La mejor manera de ilustrar estasituacion es mediante un ejemplo. Imaginemos que sobre el ultimo valor del registroque hemos utilizado para los ejemplos (0b00001011 ) deseamos configurar el pin 3como entrada (0).

El valor 0 en binario ((ocupa)) un unico bit por lo que para crear la mascarapodrıamos pensar en utilizar el mismo metodo que en apartados anteriores. En lafigura Figura B.5 se ilustra el proceso.

Como se puede observar el resultado obtenido es 0b00001011, es decir, no es elresultado esperado dado que el bit 3 esta a 1. A continuacion, explicaremos el modode realizar este tipo de operaciones.

Si utilizamos la mascara normal, (la utilizada para configurar un bit a 1) y negamosla misma, podemos observar que el resultado tras realizar la operacion AND es elesperado. Se recurre al elemento neutro de la operacion AND, es decir, al 1, de modoque todos los bit que eran 0 al negarse se convierten a 1 consiguiendo que la operacionAND no tengan efecto (A AND 1 = A) sobre los bits que ya esta configurados y sıtenga efecto (configurar con el valor 0) sobre los bits que estan en 1 en el operando

B.3. OPERACIONES CON REGISTROS 117

01234567

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: Configuracion del pin 3 como entrada sobre registro previamente con-figurado

((extra)).En la Figura B.6 se puede ver el procedimiento seguido paso a paso.

B.3. OPERACIONES CON REGISTROS 118

APENDICE C

ENTORNO ECLIPSE CONARDUINO

C.1. Introduccion

En este apendice veremos como configurar el entorno de desarrollo Eclipse demodo que el mismo pueda ser utilizado para programar y grabar programas en laplataforma Arduino.

El lector puede preguntarse por que utilizar otro IDE si ya ha utilizado el oficialde Arduino y no ha tenido ningun problema.

Arduino fue pensado como una plataforma con una curva de aprendizaje muybaja, la ventaja obvia es la rapida adaptacion de cualquier persona con mınimosconocimientos de informatica al entorno. El problema, no tan obvio en un primermomento, es la carencia de herramientas tan importantes como: control de versiones,gestion de proyectos, ((tasks list)), etc.

Con el objetivo de mostrar al lector el potencial de Arduino, en este apendice con-figuraremos un entorno con todas las herramientas necesarias en cualquier ambienteprofesional.

C.2. Que es Eclipse

Eclipse es un entorno de desarrollo integrado de codigo libre y multiplataforma.Eclipse ofrece un ((core)) sobre el que otros desarrolladores realizan sus modificacionespara crear entornos de desarrollo especıficos para cada tecnologıa. Un ejemplo podrıaser el entorno para Java o el entorno de Xilinx para el desarrollo de software sobre suplataforma hardware.

Aunque existen plugins para Eclipse que ofrecen una capa de compatibilidad conArduino, en este apendice trabajaremos sobre Eclipse para C++ con el objetivo depoder tener control sobre cada una de las partes que conforman la construccion deun binario para Arduino.

119

C.3. INSTALACION DEL ENTORNO 120

Figura C.1: Pantalla inicial de Eclipse

La pagina oficial de Eclipse se puede visitar desde el siguiente enlace: https:

//eclipse.org, en ella podras encontrar toda la informacion de la fundacion Eclipse,ademas, desde el apartado ((Download)) podras descargar cada una de las versionesoficiales del entorno.

C.3. Instalacion del entorno

El primer paso sera el descargar nuestro entorno de desarrollo Eclipse sobre el querealizaremos todas las ((modificaciones)) necesarias para que sea compatible compatiblecon la plataforma Arduino.

Para descargar el entorno, en primer lugar, deberemos acceder a la web de Eclipsey luego al apartado de descargas: http://www.eclipse.org/downloads/ una vezdentro deberemos elegir la opcion de ((Eclipse IDE for C/C++ Developers)). Estaversion nos ofrece muchas herramientas de gran utilidad como: Mylyn Task, Git o unsistema de acceso remoto.

Una vez descargado el entorno para nuestra arquitectura, el segundo paso sera ladescompresion del mismo. Eclipse es un programa portable, esto quiere decir que norequiere instalacion por lo que una vez descomprimido estaremos en disposicion deutilizar el mismo. El lugar donde se descomprima no es relevante.

El siguiente paso sera ejecutar el programa, en teorıa si tenemos Java en nuestrocomputador el entorno se ejecutara sin ningun tipo de fallo, en caso de no tener Javael entorno nos avisara y tendremos que instalarlo.

La pantalla inicial tendra la apariencia de la Figura C.1.Ahora deberemos instalar el entorno Arduino tal y como lo hicimos en Seccion 2.1.

Si ya lo tienes instalado no hace falta que vuelvas a hacerlo.Con la instalacion del entorno Arduino tendremos todas las herramientas necesa-

rias, como son:

Compilador: Utilizaremos el compilador ((avr-gcc)) y ((avr-g++)) para compilar

C.4. CONFIGURACION DEL ENTORNO 121

nuestros proyectos.

Conjunto de librerıas Arduino: Las librerıas de Arduino, forman parte delllamado ((ArduinoCore)). Estas librerıas nos simplifican tareas repetitivas comola configuracion del modulo USART, configuracion de pines, etc.

Librerıas AVR: Como ya sabras la librerıa de Arduino utiliza las librerıas deAVR por lo que tambien tendremos estas ultimas en nuestra instalacion.

Programador: Para programar el Arduino necesitamos un programador ((hardware))y un programador ((software)). En el caso del programador ((hardware)) ya lo te-nemos instanciado dentro de la placa Arduino (una vez instalado el bootloader).Por otro lado, el programador ((software)) que utilizaremos sera ((avrdude)) queforma parte del conjunto de herramientas de AVR.

Mapeado de pines: Como ya sabras, el entorno Arduino utiliza su propianumeracion de pines, de tal modo que si pones pin 13 en una funcion, el Arduino((sabe)) que pin es y a que puerto le corresponde (en realidad el encargado dehacer esta conversion como es logico, no es Arduino, es el compilador). Esto serealiza mediante el archivo de variants que veremos mas adelante.

Es importante que anotemos la direccion de instalacion de Arduino para los pasossiguientes. En el caso de los sistemas GNU/Linux, esta direccion suele ser: /usr/sha-re/arduino.

C.4. Configuracion del entorno

Ahora que tenemos todo descargado es el momento de realizar la configuracion delIDE Eclipse. En primer lugar descargaremos un plugin para el desarrollo de solucionesbasadas en microprocesadores AVR dentro de eclipse.

Para descargar un plugin en Eclipse, podemos acceder al menu ((Help, Install newsoftware)) o ((Ayuda, Instalar nuevo software)) en Espanol. Una vez en esa pantalladeberemos pulsar al boton ((Add)) y luego en el apartado de ((Name)) poner el nombreque se desee, por ejemplo avr-descargas, y en location la siguiente direccion: http://avr-eclipse.sourceforge.net/updatesite, debera quedar como en la Figura C.2.

Una vez rellenado el formulario aparecera ante nosotros un plugin llamado AVREclipse Plugin con un ((checkbox)), deberemos seleccionar dicho ((checkbox)), seguida-mente pulsaremos ((Next)) y ((Finish)) segun las instrucciones, hasta finalizar la insta-lacion.

Una vez instalado, el siguiente paso consiste en la configuracion de las variablesque permiten al plugin saber donde se encuentran los binarios del compilador, etc.

Para configurar el plugin hay que ir al menu ((Window, Preferences)) o ((Ventana,Preferencias)). Una vez en el menu deberemos acceder al apartado de ((AVR, Paths)).Hay que configurar cada una de las variables para que apunten al binario dentro delSDK descargado en el paso anterior.

AVR-GCC: <directorioSDK>/hardware/tools/avr/bin

GNU make: Con el entorno descargado para Windows se descarga tambienla herramienta make por lo que el mismo estara en el directorio del SDK, sin

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 formaparte de GNU/Linux y esta de forma nativa para todas las distribuciones por loque en este caso seguramente el propio plugin detecte el lugar donde se encuentrainstalado y aparecera 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 configura-cion, ten en cuenta que en funcion de donde se realice la instalacion, los path y enconsecuencia la configuracion variara.

C.5. Creando el proyecto: ArduinoCore

Como ya hemos comentado, Arduino nos proporciona un conjunto de librerıasque hacen que sea mucho mas sencillo el utilizar algunos de los modulos ((hardware))del microcontrolador. Para poder contar con todas estas comodidades tendremos quecompilar las librerıas en un proyecto a parte (esto nos permitira reutilizar las librerıaen otros proyectos). Esta tarea en el entorno oficial de Arduino se realiza sin quenosotros tengamos que realizarlo de forma explicita, esto hace que sea mucho masrapido para el usuario pero a la vez, hace poco didactico el proceso.

Para compilar las librerıas en primer lugar crearemos un proyecto, para ello va-mos al menu ((New, C++ Project)) y en tipo de proyecto ((AVR Cross Target StaticLibrary)) el nombre que pondremos al proyecto sera ((ArduinoCore)), el nombre noes determinante, sin embargo este nombre simplifica la labor de comprension de lospasos que estamos realizando.

En configuraciones unicamente seleccionaremos la de ((Release)) esto es debido aque esta librerıa no la modificaremos y por lo tanto no necesitamos toda la configu-racion de ((Debug)). En la siguiente pantalla de configuracion se nos preguntara por

C.5. CREANDO EL PROYECTO: ARDUINOCORE 123

Figura C.3: Configuracion de ejemplo

el microcontrolador y por la frecuencia. Ambos parametros deberan ser configuradosen funcion del Arduino sobre el que se vaya a realizar el proyecto. En el caso delArduino construido en este libro en la Seccion A.1 configuraremos estos parametroscon ATmega328 y 16000000 (como se puede observar en este ejemplo la frecuenciaviene determinada en hercios).

Una vez creado el proyecto el siguiente paso consiste en anadir el codigo fuen-te de las librerıas, y configurar los includes. Para esto ultimo daremos ((click)) de-recho sobre el proyecto y buscaremos el menu de propiedades. Una vez dentro delmismo deberemos buscar el apartado ((C/C++ Build, Settings)) y en el apartado((AVR Compiler)) ir a la pestana de ((Directories)) y anadir el directorio: <directo-rioArduino>/hardware/arduino/avr/cores/arduino. Este directorio contiene todos los((headers)) de las librerıas. Por otro lado tenemos que anadir el ((header)) que utilizael entorno Arduino para referirse a sus pines. Este fichero como habras podido adi-vinar varıa en funcion del microcontrolador. El fichero se puede encontrar dentro de<directorioSDK>/hardware/arduino/variants luego selecciona el que necesites, porejemplo, para el microcontrolador ATmega328 deberıamos utilizar el mapeado stan-dard. 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 consisteen importar el codigo fuente de las librerıas, para ello damos boton derecho sobre elproyecto y seleccionamos la opcion de ((Import, File System)). En el cuadro de busque-da hay que ingresar el directorio:<directorioArduino>/hardware/arduino/avr/cores/arduino. Una vez dentro, seleccio-na todos los archivos (.cpp y .h) menos el archivo main.cpp.

Por ultimo ya solo queda compilar el proyecto, para ello cruzamos los dedos ydamos boton derecho ((Build Project)). Si todo va bien ya tendremos las librerıas deArduino compiladas.

C.6. CREANDO EL PROYECTO FINAL 124

Figura C.4: Configuracion de las librerıas

C.6. Creando el proyecto final

Ahora que tenemos compilado el conjunto de librerıas de Arduino para el mi-crocontrolador que estamos utilizando, ya podemos crear un proyecto tal y comolo harıamos en el IDE oficial de Arduino.

Para crear un proyecto en primer lugar accedemos al menu: ((New, C++ Project))y en tipo de proyecto ponemos: ((AVR Cross Target Application)) tal y como en laSeccion C.5. El nombre del proyecto en este caso no es relevante.

Como este proyecto sı que pasara por la fase de ((debug)) y de ((release)) dejaremoshabilitadas ambas configuraciones.

El siguiente paso consiste en anadir los mismos directorios que en la Seccion C.5con el objetivo que se pueda referenciar a los ficheros de cabecera del ((core)). Ademas,hay que anadir al propio proyecto en la lista de directorios. Para ello anadir 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 estemismo proyecto.

Una vez que tenemos las tres direcciones completadas podemos proceder a enlazareste proyecto con el ((core)). Para enlazar los codigo objeto, el primer paso es ir al menudel linker llamado ((AVR C++ Linker)) y en el apartado ((General)) sustituir la cadenadel 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 donde se encuentra el fichero con la librerıa, ası como el nombrede la misma. En nuestro caso el nombre era ((ArduinoCore)) y el ejecutable se puedeencontrar en la carpeta release del proyecto ((ArduinoCore)). Para completar este pasopor tanto, en el primer cuadro (((libraries -l)) debemos poner ArduinoCore y en elcuadro inferior con el nombre ((libraries path -L)) la siguiente cadena que es relativaal workspace: ((${workspace loc:/ArduinoCore/Release})) el aspecto final deberıa serparecido al mostrado en la Figura C.4.

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 proyectoluego ((C/C++ Build, Settings, Additional Tools in Toolchain)) y seleccionar la opcion((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 delArduino, lo primero que tenemos que hacer es configurar la herramienta ((avrdude))que sera el programador software encargado de realizar la comunicacion con el Arduinopara subir el ((.hex)) al mismo.

((avrdude)) utiliza un fichero de configuracion llamado ((avrconf)) para saber losdiferentes tipos de programadores hardware con los que cuenta ası como las configu-raciones que debe realizar para comunicarse con cada uno de ellos.

Para configurar este archivo en ((avrdude)) tenemos que ir a las preferencias genera-les de ((eclipse)) y buscar el menu ((AVR, AVRdude)). Una vez en el menu, el siguientepaso consiste en marcar la casilla Use custom configuration file for AVRDude y buscarel fichero ((avrconf)) en:<directorioSDK>/hardware/tools/avr/etc/avrdude.conf.

Ahora que tenemos preparado el fichero ((avrconf)) lo siguiente consiste en confi-gurar las propiedades del proyecto para indicar que tipo de programador ((hardware))utilizaremos y que acciones debe realizar ((avrdude)) para programar el Arduino. Elprimer paso consiste en ir a las propiedades del proyecto y buscar el menu ((AVR,AVRDude)). Ahora crearemos una nueva configuracion dando al boton ((new)) de lapestana ((Programmer)). El nombre de la configuracion puede ser el que desees, serecomienda utilizar el nombre de tu placa ası te sera mas sencillo tener todo ordena-do. En ((programmer hardware)) deberemos buscar ((Wiring)). En el cuadro de textollamado ((Override default port)) deberemos poner el puerto en el cual se encuentraconectado nuestro Arduino. En el caso de GNU/Linux este suele ser /dev/ttyACMX.En Windows el puerto COM debera escribirse de la siguiente manera ((//./COMX)).En cuanto a la velocidad en baudios variara en funcion del ((bootloader)) y de la placa,los valores tıpicos son 57600 y 115200. El aspecto de esta ventana de configuraciondeberıa ser similar a la Figura C.5.

Con todo configurado ya solo queda crear un archivo ((main.cpp)) en nuestro pro-yecto con el esquema mostrado en Cod. C.1.

Como puedes observar, aquı se puede apreciar como cuando creamos un sketchen el IDE oficial de Arduino realmente estamos creando las dos funciones principalescomo son setup y loop pero nos despreocupamos de iniciar la plataforma (init()) y deasegurarnos de que el programa nunca finaliza (bucle while).

Una vez guardado el programa tendremos que compilar el mismo dando botonderecho sobre el proyecto y sobre la opcion ((Build Project)). Si todo esta bien secreara una carpeta llamada ((Debug)) donde podremos encontrar un fichero llamado((nombreDelProyecto.hex)). Ahora solo nos queda subirlo a nuestra placa, para ellopulsamos boton derecho sobre el proyecto y buscamos ((AVR, Upload Project to TargetDevice)).

C.7. SUBIENDO EL PROYECTO A NUESTRO ARDUINO 126

Figura C.5: Configuracion 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 }

Codigo C.1: Esqueleto de programa

WARRANTY

NO WARRANTYThere 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 entirerisk 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 maydistribute and/or modify the Work as permitted above, be liable to you for damagesarising out of any use of the work (including, but not limited to, loss of data, databeing rendered inaccurate, or losses sustained by anyone as a result of any failure ofthe Work to operate with any other programs), even if the Copyright Holder or saidauthor or said other party has been advised of the possibility of such damages.

127