Professional Documents
Culture Documents
Gazzano.
Reservados todos los derechos. No se permite la reproduccion total o parcial de esta obra, ni su
incorporacion 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 los
titulares del copyright. La infraccion de dichos derechos puede constituir un delito contra la propiedad
intelectual.
Digital edition
This book includes illustrations and index.
ISBN 978-84-608-7339-6
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 a
mis padres, cuyos consejos y compresion han sido un apoyo fundamental en el desarrollo tanto de mi
vida profesional como personal. Agradecer tambien a Julio Daniel Dondo y al grupo de investigacion
ARCO la confianza depositada en m.
7
8
INDICE GENERAL
1. Que es Arduino? 13
1.1. Elementos de Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3. Manejando entradas/salidas 25
3.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2. Utilizando los pines de salida . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.1. Ejemplo 1: Encendiendo un LED . . . . . . . . . . . . . . . . . 26
3.3. Utilizando los pines de entrada . . . . . . . . . . . . . . . . . . . . . . 30
3.3.1. Ejemplo 2: Utilizacion de un boton o interruptor . . . . . . . . 32
3.3.2. Ejemplo 3: Leyendo temperatura y senales analogicas . . . . . 36
4. Comunicaciones 43
4.1. Introduccion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.2. Comunicacion serie mediante USART . . . . . . . . . . . . . . . . . . 43
4.2.1. Ejemplo 1: Hola mundo por Serie . . . . . . . . . . . . . . . . . 45
4.2.2. Ejemplo 2: Recibiendo informacion . . . . . . . . . . . . . . . . 49
4.2.3. Ejemplo 3: Comunicacion entre Arduinos . . . . . . . . . . . . 52
4.3. Comunicacion I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.1. Hardware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.3.2. Protocolo I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.3.3. Ejemplo 1: Hola mundo mediante I2C . . . . . . . . . . . . . . 60
4.3.4. Ejemplo 2: Obteniendo datos de un IMU . . . . . . . . . . . . 64
4.4. Protocolo SPI (Serial Peripheral Interface . . . . . . . . . . . . . . . . 67
9
INDICE GENERAL 10
5. Interrupciones 73
5.1. Interrupciones en el ATmega328 . . . . . . . . . . . . . . . . . . . . . 74
5.2. Manipulacion software . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2.1. Librera avr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.2.2. Librera Arduino . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.2.3. Consideraciones importantes . . . . . . . . . . . . . . . . . . . 77
5.3. Ejemplo 1: Primera rutina de interrupcion . . . . . . . . . . . . . . . . 78
5.3.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 78
5.3.2. Codigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.4. Ejemplo 2: Midiendo distancias . . . . . . . . . . . . . . . . . . . . . . 79
5.4.1. Tabla de entrada/salida . . . . . . . . . . . . . . . . . . . . . . 80
5.4.2. Codigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
6. Multitasking y Timers 83
6.1. Timers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6.1.1. Registros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.1.2. Modos de funcionamiento . . . . . . . . . . . . . . . . . . . . . 85
6.1.3. Ejemplos de uso . . . . . . . . . . . . . . . . . . . . . . . . . . 87
6.2. Multitasking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.1. Encendiendo y apagando un led de manera profesional . . . . . 98
6.2.2. Encendiendo y apagando un led de manera mas profesional . . 100
11
INDICE DE FIGURAS 12
En este libro vamos a describir una serie de disenos y desarrollos practicos para
automatizar el control de sistemas utilizando el entorno Arduino. El entorno Arduino
es una plataforma electronica de desarrollo de codigo abierto (open source) que ofre-
ce un conjunto de herramientas tanto software como hardware que nos permitiran
realizar 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 elementos
necesarios para programar el microcontrolador, y un ambiente de desarrollo software
con una serie de libreras para construir nuestros disenos.
Hardware
13
1.1. ELEMENTOS DE ARDUINO 14
avr-libc
1.1. ELEMENTOS DE ARDUINO 18
CAPITULO 2
PRIMEROS PASOS EN ARDUINO
2.1.1. GNU/Linux
Los usuarios de GNU/Linux pueden encontrar el entorno Arduino en sus gestores
de paquetes, por ejemplo, si tenemos una distribucion ((Debian)), para instalar todo
el entorno unicamente tendremos que poner los siguientes comandos en la terminal
como ((superusuario)):
Es importante anadir a nuestro usuario a los grupos: uucp, lock y dialout, para poder
manejar los puertos series y los puertos USB de nuestro sistema GNU/Linux. Para
anadir nuestro usuario a los grupos unicamente ingresaremos el siguiente comando:
gpasswd -a $USER uucp
gpasswd -a $USER lock
19
2.2. PROBANDO NUESTRA PLACA ARDUINO 20
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 la
Figura 2.1 en donde se nos ira guiando para completar la instalacion.
Este comando en realidad se compone de dos ordenes enlazadas mediante una tubera
o Pipe. La primera orden indica al Kernel que nos informe de todo lo sucedido desde
el arranque, mientras que la orden tail nos permite obtener las ultimas lneas del
registro.
2.2. PROBANDO NUESTRA PLACA ARDUINO 21
3.1. Introduccion
En este captulo vamos a trabajar con la placa de desarrollo Arduino enfocando-
nos principalmente en el manejo de sus entradas y salidas. En la siguiente seccion de
este captulo (Seccion 3.2) abordaremos las diferentes tecnicas para controlar actua-
dores desde Arduino. Mas adelante, en la Seccion 3.3 veremos como obtener datos del
exterior, 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 kit
de iniciacion llamado Grove Starter Kit for Arduino de la marca Seeed el cual se
puede 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
25
3.2. UTILIZANDO LOS PINES DE SALIDA 26
Actuador
Dispositivo capaz de transformar la energa neumatica, electrica o hidraulica en
la activacion de un proceso con la finalidad de generar un efecto sobre un proceso
automatizado.
Un ejemplo claro es un motor de un robot. Un motor de un robot cumple con los
dos elementos principales de la definicion. Por un lado transforma la energa electrica
en un efecto (movimiento del robot) y por otro lado, este movimiento se realiza sobre
un proceso automatizado (logica del robot). Al igual que el ejemplo del robot, existen
otros muchos actuadores. Se deja al lector la tarea de buscar en la red mas informacion
sobre los diferentes tipos.
El objetivo de esta seccion es controlar estos actuadores, de modo que podamos
actuar sobre el entorno desde Arduino.
La manera mas inmediata de controlar un actuador en Arduino es mediante los
pines de entrada/salida. La cantidad de pines disponibles depende del tipo de placa
que se utilice. Por ejemplo, en la placa ATmega328 que ((montamos)) en la Seccion A.1
tenemos un total de 23 pines que pueden ser configurados como entrada o salida.
Veamos algunos ejemplos:
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 metodologa a la hora de afrontar el problema.
El primer paso en cualquier diseno es analizar los elementos de control en base a las
especificaciones. En nuestro caso unicamente tenemos un unico elemento a controlar
(el LED) por lo que este paso se podra obviar, sin embargo documentar es una buena
costumbre en cualquier diseno. En la Tabla 3.1 se puede ver un ejemplo de analisis de
elementos 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 siempre
se sepa la funcion del mismo. Por otro lado, se indica el nombre de la variable que se
3 http://goo.gl/S3oBA2
4 Los esquematicos se pueden encontrar en la pagina de Arduino o en el caso de una placa ensam-
5v-12v
Azul = Tierra
Rojo = Entrada
Naranja = 5v estables
Cyan = Cristal
-------------------
Marron = Rx(Arduino) - Tx(FTDI)
Morado = Tx(Arduino) - Tx(FTDI)
16MHz Cristal
utilizara en el codigo para hacer referencia a dicho elemento y por ultimo el pin fsico
al cual se conectara.
Diagrama de flujo
En base al diagrama general mostrado en la Figura 3.1 al que normalmente se
adapta cualquier diseno, vamos a realizar el diagrama correspondiente para nuestro
diseno.
La herramienta utilizada para crear dicho diagrama es DIA una herramienta de
codigo abierto, gratuita y que permite realizar diagramas de diversos tipos como por
ejemplo diagramas de clases UML o diagramas de flujo.
En la Figura 3.5 se puede ver un ejemplo de diagrama de flujo para nuestro caso.
Este ejemplo no es la unica manera de representar nuestro diseno. Un mismo problema
puede tener diferente soluciones correctas.
Codigo
Ahora que tenemos toda la documentacion lista y se ha analizado el problema es
el momento de realizar el diseno de la solucion y su implementacion. Para programar
la placa Arduino utilizaremos el entorno de programacion instalado descrito en el
Captulo 1. Como se adelanto en el captulo anterior todo programa que vaya a ser
ejecutado en el entorno Arduino debe tener dos funciones principales y obligatorias:
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 la
pulsacion de un boton).
Los pines de entrada salida, como su nombre indica, se pueden utilizar como
entrada o como salida. La seleccion del modo de funcionamiento de cada pin se realiza
mediante unos registros hardware. En estos primeros ejemplos no se vera el manejo de
dichos registros. Sin embargo, en la Seccion B.1 el lector puede estudiar como utilizar
dichos registros mediante operaciones de desplazamiento de bits.
A la hora de captar una accion o magnitud en un microcontrolador lo primero
que debemos preguntarnos es que tipo de magnitud necesitamos leer o captar. De
forma generica y simplificando, existen dos grandes grupos en los cuales se clasifican
las senales (Figura 3.8):
1. Senales analogicas: Las senales analogicas son continuas en el tiempo, es
decir, se puede representar mediante una funcion matematica continua. Que
elementos de la senal analogica se pueden medir? principalmente se analizan dos
variables: la amplitud y el periodo o frecuencia. Ejemplos de senales analogicas
son el sonido, senales de radio frecuencia, senales electromagneticas.
En electronica para medir las senales analogicas se utilizan instrumentos como
los 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 hasta
realizar funciones matematicas como la FFT (transformada rapida de fourier).
2. Senales digitales: Las senales digitales son aquellas en las cuales sus valores
estan completamente discretizados. Aunque cualquier fenomeno electromagneti-
co como el producido al realizar contacto en un pulsador es continuo, sus estados
pueden ser discretizados en funcion del numero de bits utilizados en la conver-
sion, en este caso dos estados (pulsado y no pulsado).
Seal
continua
Tiempo
Seal
discreta
1100
1010
1000
0110
0100
0010
0000 Tiempo
Rebote
VOH
Valor lgico alto
VIH
Indeterminado
VIL
Valor lgico bajo
VOL
1 1 1 1 X 1 0 1 0 X 0 0 0 0 0
Tabla de entrada/salida
En este ejemplo utilizaremos el codigo de la Subseccion 3.2.1 de modo que cuando
el pulsador se encuentre accionado se encendera el LED y cuando el pulsador no se
encuentre pulsado el LED se apagara. Como en el ejemplo anterior, lo primero que hay
que realizar en el diseno es una tabla con las entradas y salidas que utilizaremos. Como
se puede ver en Tabla 3.2 tenemos dos elementos. Por un lado, el LED notificador
que vimos en el ejemplo anterior y por otro lado, el pulsador como entrada.
Diagrama de flujo
En el diagrama de flujo mostrado en Figura 3.12 se muestra un elemento nuevo, el
condicional. En este caso en funcion del estado de un elemento (condicion) actuaremos
de 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
Fundamentos
En el mercado existen sensores de muchos tipos, los hay digitales como por ejemplo
los sensores de paso y tambien los hay analogicos como los sensores de temperatura.
Normalmente, estos sensores producen senales de un voltaje bajo. Para poder tratar
con 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, dgitos que unicamente pueden tomar el valor 1
o 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 queremos
es producir una senal analogica a partir de una digital se utilizan los ((Conversores
Digitales Analogicos)) o de forma abreviada DAC. La teora que hay detras de estos
5 Las funciones dummy son aquellas que no tienen ninguna utilidad pero que se incorporan al
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. Imaginemos
que tenemos una sensor que produce valores desde 0V hasta 5V y que tenemos
un ADC de 10 bits (como es el caso del ATmega328) cual es la resolucion
maxima con la que se podra trabajar en el diseno?. El calculo es sencillo. Si
el conversor codifica los valores con 10 bits, significa que puede tomar valores
entre 0 y 1023 (210 1) por lo tanto si dividimos los 5v (rango 5-0) entre los
1024 valores (rango 0-1023) tenemos una division de 0.0049 o lo que es lo mismo
4,9 mV por cada valor. De este modo si obtenemos una lectura con el valor 53,
significara que el sensor ha generado 259 mV.
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 o
el DHT22 permiten leer la temperatura unicamente mediante senales digitales. En
este caso hemos decidido utilizar el modulo de Grove porque ya tiene incorporado los
condensadores de filtro necesarios y diversas protecciones. Ademas simplifica mucho el
diseno y como ya dijimos en otros ejemplos, nos permite centrarnos en la programacion
del microcontrolador.
Como siempre lo primero que debemos realizar es la tabla de entradas y salidas
de tal modo que nuestro diseno este siempre documentado. En este ejemplo vamos a
utilizar un termostato y un LED notificador Tabla 3.3.
Diagrama de flujo
El diagrama de flujo es fundamental en cualquier diseno, muchas herramientas
como Scratch for Arduino son capaces de generar todo el codigo a partir de un buen
diagrama de flujo. En este ejemplo unicamente buscamos encender un LED cuando
pase de la temperatura mnima configurada. El diagrama resultante es el mostrado
en la Figura 3.13
Como se puede observar en el diagrama de la Figura 3.13 hay dos procesos a la
hora de captar la temperatura. El primero obtiene la temperatura en formato RAW,
3.3. UTILIZANDO LOS PINES DE ENTRADA 40
es decir, un valor sin tratar. Una vez que tenemos dicho valor tenemos que realizar un
proceso de conversion (normalmente especificado por el fabricante) mediante el cual
el valor RAW se convierte en un valor util para el usuario. En Figura 3.3.2 se vera
como realizar dicha conversion.
Codigo
Esta solucion anade a los demas ejemplos el tratamiento de la senal analogica por
lo que unicamente se hara enfasis en dicha parte. En la seccion setup configuramos
el pin A1 como entrada. Arduino no necesita especificar que dicho pin se comportara
como un pin analogico, por lo que este procedimiento es exactamente igual al realizado
con el boton en Subseccion 3.3.1. En la funcion read sensors() se realiza la lectura del
sensor conectado a la entrada A1 del Arduino. La funcion analogRead() devuelve un
valor 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 en
grados celsius. Para ello hay que utilizar una formula especificada por el fabricante en
el datasheet. Cada sensor utiliza una conversion diferente por lo que este paso vara
de 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 mnimo en cuyo caso activara el led.
3.3. UTILIZANDO LOS PINES DE ENTRADA 41
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 en
el estudio de las comunicaciones muy importantes y que es menester conocer, en este
captulo siguiendo el enfoque practico, aprenderemos a utilizar estas comunicaciones
sin necesidad de entrar en profundidad en los aspectos puramente teoricos.
La eleccion del modo de comunicacion a utilizar depende de las necesidades de
nuestro diseno. Aspectos tales como protocolos de comunicacion, medios fsicos, ancho
de banda, atenuacion, distorsion, ente otros, deben ser considerados a la hora de elegir
el modo de comunicacion entre dispositivos.
A continuacion veremos algunos modos de comunicacion que son soportados por
Arduino.
43
4.2. COMUNICACION SERIE MEDIANTE USART 44
Se ha hablado del modulo UART, sin embargo, hay que tener en cuenta que la
unidad UART es un modulo hardware y no un protocolo de comunicacion. La unidad
UART se encarga de obtener en paralelo y en un unico ciclo de reloj los datos del
registro de datos y los multiplexa en el tiempo segun la configuracion establecida.
Existen diversas configuraciones, simplex, duplex, full-duplex, de dos dispositivos
o en bus. Sin embargo, la comunicacion serie mediante UART fue desarrollada con el
objetivo 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. La
unidad UART se encarga de obtener en paralelo y en un unico ciclo de reloj los datos
del registro de datos, y los serializa en el tiempo segun la configuracion establecida.
Posee ademas soporte para el control de flujo por hardware. Este control de flujo es
realizado 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 manejar
datos. El cableado para la comunicacion serie asncrona es muy sencillo y se puede
ver en la Figura 4.2. Existen diversas configuraciones, simplex (en un solo sentido),
1 En el ATmega328 la lnea de control se llama XCK0 y se encuentra en el pin 6 del encapsulado
DIP28
4.2. COMUNICACION SERIE MEDIANTE USART 45
RTS RTS
CTS CTS
Dispositivo A Dispositivo B
Tx Tx
Rx Rx
Gnd Gnd
duplex (en ambos sentidos pero no simultaneamente), y full-duplex (en ambos sentidos
al 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 fsica se utilizara un determinado nivel de
voltaje u otro. El protocolo RS-232 utiliza de -3V a -15V para el nivel logico ((1)) y
de +3V a +15V para el nivel logico ((0)) Sin embargo, los procolos con logica TTL o
CMOS trabajan con otros niveles. Existen circuitos integrados como el MAX232 que
permiten realizar la conversion de niveles.
Interfaz serie
Entrada/Salida Comando Evento Descripcion
Salida Pin 8 == Enva hola mundo
HIGH
Tabla de entrada/salida
Como en los anteriores ejemplos, lo primero que vamos a hacer es la tabla de
entrada/salida. En el enunciado se han senalado dos de los elementos principales. Por
un lado la comunicacion serie y por otro lado el boton. Es buena costumbre reservar
un apartado ntegro para las comunicaciones. En un diseno mas complejo, lo mas
probable es que tengamos diferentes tipos de comunicaciones con diversos protocolos.
En ese apartado, podremos definir todos los protocolos y las interfaces que proporciona
nuestro diseno al ((exterior)). Volviendo a las entradas y salidas, si bien en este primer
ejemplo lo unico que haremos es enviar un string por el puerto Tx (pin 1) dejamos ya
indicada la entrada Rx que sera utilizada en los ejemplos que siguen.
Comunicaciones
Este ejemplo es muy sencillo, por lo que no definiremos un protocolo completo, es
decir, sintaxis, semantica y temporizacion. Unicamente describiremos la comunicacion
en terminos de los mensajes de salida. En la Tabla 4.2 se puede ver un ejemplo de
dicha tabla.
Diagrama de flujo
Las comunicaciones se pueden disenar como eventos asncronos y sncronos, todo
dependera 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 ejemplos
anteriores, es decir, de forma sncrona y secuencial.
En la Figura 4.4 puedes ver el diagrama propuesto.
4.2. COMUNICACION SERIE MEDIANTE USART 47
Codigo
En Cod. 4.1 se puede ver una posible solucion a este ejemplo. Lo primero que
debemos hacer es configurar los elementos de entrada salida y las comunicaciones.
Cuando el diseno sea mas complejo, puede que sea necesario encapsular cada una de
estas partes en otras subfunciones como por ejemplo init communications().
Si no quieres que finalice la cadena con los caracteres de fin de lnea, puedes utilizar
la funcion Serial.print()
4.2. COMUNICACION SERIE MEDIANTE USART 48
1 i n t b ut to n = 8 ; //De a c u e r d o a l a t a b l a de
2 // e n t r a d a / s a l i d a s
3
4 i n t b u t t o n s t a t e = LOW;
5
6 void setup ( ) {
7
8 // pinMode c o n f i g u r a un p i n como OUTPUT ( s a l i d a ) o INPUT ( e n t r a d a )
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 b u t t o n s t a t e = d i g i t a l R e a d ( bu tt on ) ;
16 }
17
18 void perform actions ( ) {
19 i f ( b u t t o n s t a t e == HIGH) {
20 S e r i a l . p r i n t l n ( " Hola mundo " ) ;
21 }
22 }
23
24 void loop ( ) {
25 read sensors () ;
26 perform actions () ;
27 }
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/salida
con los nombres de las variables y los pines donde se conectaran los actuadores.
Diagrama de flujo
En la Figura 4.5 se puede ver el diagrama en el que nos apoyaremos a la hora de
disenar 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 ejemplo
de 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, unicamente
se situa dentro del flujo de programa. Si quisieramos detallar una funcion, una opcion
sera la de realizar un diagrama separado para la misma y mas tarde unirlos.
4.2. COMUNICACION SERIE MEDIANTE USART 51
Codigo
Este ejemplo, aunque sencillo, requiere un poco mas lneas de codigo que el ante-
rior. Un factor clave y determinante para un buen diseno es la modularizacion. Siempre
que 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 dos
funciones genericas: check communications() y la funcion parse command(). La pri-
mera funcion hace lo siguiente: Primero comprueba que si buffer de recepcion tiene
datos. 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. La
funcion Serial.available de la librera Arduino, nos proporciona un metodo para saber
cuantos bytes se encuentran en el buffer de recepcion. Mediante la funcion Serial.read
leemos el dato entrante y anadimos el dato al buffer de procesado que hemos creado
y que hemos llamado serial command. Es responsabilidad del programador vaciar el
buffer mediante la llamada a Serial.read. Esta llamada devuelve el byte mas antiguo
en el buffer, es decir, el primero trasmitido (cola FIFO). Un detalle a tener en cuenta:
en Internet y en la bibliografa sobre Arduino se pueden ver muchos ejemplos donde
se hace la comprobacion Serial.available()>0. Aunque esto puede funcionar, hay que
tener mucho cuidado. Puede que en el buffer haya mas de 0 bytes, sin embargo, esto
no significa que toda la trama este en el buffer, por lo que si se hace esta comprobacion
habra que tener especial cuidado a la hora de formar la trama. Otra manera de evitar
este comportamiento podra ser el comprobar que el tamano del buffer de recepcion
sea mayor que el tamano de trama.
Otro de los puntos claves a la hora de tratar con protocolos ascii es el detectar el fin de
lnea. Dependiendo del sistema operativo y de la consola que se utilice para mandar
los datos, se utilizara un caracter de fin de lnea u otro. En el entorno Arduino uno
puede abrir un monitor de terminal serie usando (CTRL+Shift+m). En este monitor,
por defecto, esta seleccionada la opcion de mandar como fin de lnea los caracteres
((\r \n)), sin embargo en el bloque ((if)) de nuestro codigo, se comprueba el fin de linea
unicamente detectando si el valor recibido es igual a \n por lo tanto el caracter \r sera
insertado en nuestra trama y seguramente ocasione problemas en el diseno. La manera
mas sencilla de modificar este comportamiento es cambiar la opcion en la consola o
en este caso el monitor serial para que unicamente enve el caracter \n como caracter
fin de lnea.
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 decodifique
la trama y convierta los datos en informacion util para la logica del programa. Con
este fin se invoca a la funcion parse command() (linea 32). Esta funcion se ha creado
apoyandose en las funciones strcpy() y strtok(), que divide y copia los valores ledos en
la estructura Command protocol() (lneas 9 a 14). Se recomienda al lector que revise
la documentacion sobre la funcion strtok().
La ultima fase es la de actuar en funcion de la entrada, en nuestro caso unicamente
tenemos que detectar el primer campo y detectar a que tipo de actuador se refiere la
comunicacion. La funcion perform action led() comprueba que led se va a modificar
4.2. COMUNICACION SERIE MEDIANTE USART 52
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 como
sensores 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 a
un segundo Arduino (maestro) cuando este lo solicite. Este segundo Arduino tendra
conectados dos LEDs. Un led se encendera si el boton ha sido apretado, mientras que
el segundo led lo hara de acuerdo al valor de temperatura recibido.
1 #i n c l u d e < s t r i n g . h>
2 #i n c l u d e < s t d l i b . h>
3
4 const int led red = 13;
5 const int led yellow = 8;
6
7 char serial command [ 1 9 ] ;
8 i n t index = 0 ;
9 typedef struct {
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 e d r e d , OUTPUT) ;
18 pinMode ( l e d y e l l o w , OUTPUT) ;
19 S e r i a l . begin (9600) ;
20 }
21
22 void check communications ( ) {
23 i n t data = 0 ;
24 while ( S e r i a l . available () ) {
25 data = S e r i a l . r e a d ( ) ;
26 i f ( data != \ n ) {
27 s e r i a l c o m m a n d [ i n d e x ] = data ;
28 i n d e x ++;
29 } else {
30 s e r i a l c o m m a n d [ i n d e x ] = \0 ;
31 index = 0 ;
32 parse command ( ) ;
33 }
34 }
35 }
36
37 v o i d parse command ( ) {
38 char token ;
39 int token index = 0;
40 s t r c p y ( command protocol . a c t u a t o r , s t r t o k ( s e r i a l c o m m a n d , " ," ) ) ;
41 s t r c p y ( command protocol . type , s t r t o k (NULL, " ," ) ) ;
42 s t r c p y ( command protocol . s t a t e , s t r t o k (NULL, " ," ) ) ;
43
44 i f ( strcmp ( command protocol . a c t u a t o r , " led " ) == 0 ) {
45 p e r f o r m a c t i o n l e d ( command protocol . type , command protocol . s t a t e )
;
46 }
47
48 }
49
50 v o i d p e r f o r m a c t i o n l e d ( c h a r type , c h a r s t a t e ) {
51 i f ( strcmp ( type , " yellow " ) == 0 ) {
52 i f ( strcmp ( s t a t e , " on " ) == 0 ) {
53 d i g i t a l W r i t e ( l e d y e l l o w , HIGH) ;
54 } else {
55 d i g i t a l W r i t e ( l e d y e l l o w , LOW) ;
56 }
57 } e l s e i f ( type , " red " ) {
58 i f ( strcmp ( s t a t e , " on " ) == 0 ) {
59 d i g i t a l W r i t e ( l e d r e d , HIGH) ;
60 } else {
61 d i g i t a l W r i t e ( l e d r e d , LOW) ;
62 }
63 }
64
65 }
66
67 void loop ( ) {
68 check communications ( ) ;
69 }
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 sensor
del cual desea obtener la informacion. Para simplificar los id se definen mediante
la directiva de preprocesador ((#DEFINE)). Ambos Arduinos tendran que tener los
mismos valores. Una vez que el esclavo detecta la peticion enva un mensaje con el
identificador del sensor y el valor, separado por una coma. El maestro entonces debera
capturar 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 esperara
un 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 nuestro
caso 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 a
todos los disenos y es el setup() del microcontrolador. Una vez se ha realizado el setup
correctamente, el siguiente paso es obtener la informacion de los sensores (en nuestro
caso el boton y el sensor de temperatura). Por ultimo comprobaremos si tenemos
alguna comunicacion que atender. Si hay una comunicacion por atender haremos una
decodificacion de la misma (mediante el parser ) y mandaremos un mensaje u otro en
funcion de la peticion.
En este esquema el Arduino que va a actuar es el que pide los datos. Podra
plantearse otra situacion en el que el Arduino con los sensores fuera el que enva de
forma constante los valores captados.
Cabe destacar que este diagrama de flujo es el correspondiente al Arduino Esclavo,
se deja como ejercicio al lector la 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 puedes
ver el codigo del Arduino que actua como esclavo. En Cod. 4.3 se encuentra todo el
codigo del Arduino que realiza las peticiones de informacion, es decir, el maestro. Ten
en cuenta que se han omitido partes de codigo repetidas como por ejemplo la lectura
de valores de temperatura con el objetivo de no hacer muy extenso el codigo.
4.2. COMUNICACION SERIE MEDIANTE USART 55
El codigo del esclavo te debera resultar familiar ya que es una recopilacion de los
ejemplos anteriores. La parte mas importante ahora es la decodificacion del protocolo
que es practicamente igual a la realizada en Subseccion 4.2.2. Se comprueba si hay
alguna trama sin procesar preguntado por el caracter \n, si hay alguna se obtienen
todos los bytes del puerto serie hasta llegar al caracter \n que indica el fin de lnea.
Por otro lado, el maestro solicita los valores enviando una cadena por el puerto
serie. 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 librera SoftwareSerial.
Mediante esta librera podemos implementar un emulador de UART en software (co-
mo una UART fsica) dandole un nombre a la instancia creada y asignandoles los
pines de RX y TX correspondientes (lnea 13 del codigo). Utilizar estos puertos nos
ayuda mucho a la hora de programar el Arduino dado que si utilizamos los pines
correspondientes a los puertos RX y TX del puerto serie 0 para la comunicacion entre
Arduinos, tendremos que desconectar dichos cables cada vez que queramos programar
alguno de los Arduinos ya que la programacion tal y como se explico en el Seccion A.1
se realiza mediante el puerto serie.
Este tipo de comunicacion aunque muy sencilla puede llevar a problemas muy
difciles de depurar. Uno de los problemas podra ser que uno de los Arduinos desbor-
dara el buffer del otro. Para evitar esta condicion en la funcion check petition() del
Arduino 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 ningun
mensaje hasta que no se reciba la contestacion. Aunque esta solucion es muy sencilla
tiene muchos problemas dado que si el esclavo no contestara el Arduino maestro se
quedara constantemente en espera. Para evitar estos problemas existen soluciones
que utilizan ((ACKs)) o mensajes de verificacion para mantener un control de flujo.
Se deja como ejercicio al lector la implementacion del mecanismo de ((ACKs)) a la
solucion.
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 1
3 #d e f i n e MAX TEMPERATURE 26
4 #i n c l u d e < s t d l i b . h>
5 #i n c l u d e < s t r i n g . h>
6 #i n c l u d e <S o f t w a r e S e r i a l . h>
7
8 S o f t w a r e S e r i a l s e r i a l (10 , 11) ;
9 const int button led = 13;
10 const int temperature led = 8;
11 i n t index = 0 ;
12 char serial command [ 1 9 ] ;
13 typedef struct {
14 int sensor ;
15 } petition t ;
16
17 void setup ( ) {
18 S e r i a l . begin (9600) ; S e r i a l . begin (9600) ;
19 pinMode ( b u t t o n l e d , OUTPUT) ; pinMode ( t e m p e r a t u r e l e d ,OUTPUT) ;
20 }
21
22 void send petition temperature ( ) {
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 c h a r data = 0 ;
36 w h i l e ( data != \ n ) {
37 i f ( s e r i a l . a v a i l a b l e ( ) > 0) {
38 data = s e r i a l . r e a d ( ) ;
39 i f ( data == \ t ) {
40 index = 0 ;
41 } e l s e i f ( data == \ n ) {
42 s e r i a l c o m m a n d [ i n d e x ] = \0 ;
43 index = 0 ;
44 parse command ( ) ;
45 } else {
46 s e r i a l c o m m a n d [ i n d e x ] = data ;
47 i n d e x ++;
48 }
49 }
50 }
51 }
52
53 v o i d parse command ( ) {
54 c h a r t o k e n = s t r t o k ( s e r i a l c o m m a n d , " ," ) ;
55 i n t s e n s o r = a t o i ( token ) ;
56 switch ( sensor ) {
57 c a s e BUTTON:
58 p e r f o r m a c t i o n b u t t o n ( s t r t o k (NULL, " ," ) ) ;
59 break ;
60 c a s e TEMPERATURE SENSOR:
61 p e r f o r m a c t i o n t e m p e r a t u r e ( s t r t o k (NULL, " ," ) ) ;
62 break ;
4.2. COMUNICACION SERIE MEDIANTE USART 57
63 }
64 }
65
66 void perform action button ( char value ) {
67 int state = atoi ( value ) ;
68 digitalWrite ( button led , state ) ;
69 }
70
71 void perform action temperature ( char value ) {
72 f l o a t temp = a t o f ( v a l u e ) ;
73 i f ( temp > MAX TEMPERATURE) {
74 d i g i t a l W r i t e ( t e m p e r a t u r e l e d , 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 }else{
78 d i g i t a l W r i t e ( t e m p e r a t u r e l e d ,LOW) ;
79 }
80 }
81 void loop ( ) {
82 send petition button () ;
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 send petition temperature () ;
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 }
1 #i n c l u d e < s t r i n g . h>
2 #i n c l u d e < s t d l i b . h>
3 #i n c l u d e <S o f t w a r e S e r i a l . h>
4 #d e f i n e BUTTON 0
5 #d e f i n e TEMPERATURE SENSOR 1
6 c o n s t i n t b ut ton = 8 ;
7 c o n s t i n t t e m p e r a t u r e s e n s o r = A1 ;
8 float temperature celsius ;
9 int button state = 0;
10 char serial command [ 1 9 ] ;
11 i n t index = 0 ;
12
13 SoftwareSerial s e r i a l (10 ,11) ;
14
15 typedef struct {
16 int sensor ;
17 } petition t ;
18 petition t last petition ;
19 void setup ( ) {
20 pinMode ( button , INPUT) ;
21 pinMode ( t e m p e r a t u r e s e n s o r , 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 c h a r data = 0 ;
27 i f ( s e r i a l . a v a i l a b l e ( ) > 0) {
28 data = s e r i a l . r e a d ( ) ;
29 i f ( data == \ t ) {
30 index = 0 ;
31 } e l s e i f ( data == \ n ) {
32 s e r i a l c o m m a n d [ i n d e x ] = \0 ;
33 index = 0 ;
4.3. COMUNICACION I2C 58
34 parse command ( ) ;
35 } else {
36 s e r i a l c o m m a n d [ i n d e x ] = data ;
37 i n d e x ++;
38 }
39 }
40 }
41 v o i d parse command ( ) {
42 l a s t p e t i t i o n . sensor = a t o i ( serial command ) ;
43 switch ( l a s t p e t i t i o n . sensor ) {
44 c a s e BUTTON:
45 send button state () ;
46 break ;
47 c a s e TEMPERATURE SENSOR:
48 send temperature () ;
49 break ;
50 }
51 }
52 void send button state ( ) {
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 e n s o r ) ;
54 s e r i a l . p r i n t ( " ," ) ; s e r i a l . p r i n t ( b u t t o n s t a 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 e n s o 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 read sensors () ;
64 check communications ( ) ;
65 }
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 entre
circuitos. La 3 senales principales son:
SDA (Serial Data): La senal SDA se utiliza para enviar los datos.
El valor de las senales SCL y SDA esta en alto hasta que uno de los componentes
los ponga en bajo. Los dos resistores de pull-up fuerzan el valor de los cables a VCC
(3,3 - 5 volts). El maestro controla una senal de reloj por la lnea SCL, determinando
la tasa de transferencia de datos, y controla o deja controlar por el esclavo la lnea de
direcciones SDA.
La comunicacion entre un maestro y un esclavo consiste en una secuencia de
transacciones 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 una
condicion de Parada (P). Estas condiciones son controladas por el maestro. La
condicion de inicio la realiza el maestro con una transicion de la senal SDA de 1
a 0 en el semiciclo positivo del reloj SCL, mientras que la condicion de parada se
realiza con una transicion de la senal SDA de 0 a 1 en el semiciclo positivo de la senal
SCL. En I2C la transicion en la lnea SDA durante las transacciones se hace en la
4.3. COMUNICACION I2C 60
parte baja del ciclo reloj. Unicamente en la condicion de inicio y parada se realiza la
transicion en la parte alta del ciclo de reloj.
Una vez que se ha indicado la condicion de inicio el siguiente paso es enviar una
trama con la direccion del dispositivo que se desea seleccionar. Existen dos modos
de direccionamiento: de 7 bits o de 10 bits. El uso de 10 bits de comunicacion no
es frecuente y no esta cubierto en este libro. Al final de los 7 bits de direccion se
agrega 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 en
los 7 bits mas significativos de la trama de direcciones. Una vez enviada la trama de
direccionamiento los esclavos leen cada bit en la transicion del reloj, teniendo en cuenta
que el primer bit es el mas significativo de la direccion y que el ultimo bit corresponde
al tipo de transferencia. Para comprobar que el esclavo ha detectado la peticion de
comunicacion del maestro, el esclavo debe poner la lnea SDA a nivel bajo antes del
decimo pulso de reloj, de lo contrario el maestro puede interpretar que la comunicacion
no se ha podido establecer. Una vez que se ha establecido la comunicacion el siguiente
paso es enviar la trama de datos. La trama de datos unicamente se trasmite bit por bit
mediante 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 importante
que el receptor en todo momento valide cada byte recibido mediante un ACK. Si el
receptor enva un NACK despues de recibir un byte, indicara al receptor que quiere
cerrar la comunicacion.
El ultimo paso consiste en la transicion del estado de transferencia al estado de
parada. Una vez que todos los datos han sido enviados y se ha recibido un NACK, el
maestro 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 maestro
mantiene ocupado el bus poniendo la senal de datos en alto y manteniendo el reloj
en alto. De este modo, no se permite que otro maestro adquiera el bus. Una vez
completada esta fase se puede enviar otra vez la direccion del esclavo (cualquiera) y
se procede al envo de datos.
En la Figura 4.8 se puede ver una ilustracion de la trama de direccion I2C y en
Figura 4.7 un esquema donde se ilustra la comunicacion anteriormente explicada. Es
importante tener en cuenta que el ACK lo enva el receptor y no el emisor. En la
Figura 4.9 se puede observar la estructura que presenta una trama de datos, donde
unicamente se mandan los datos y un bit de ACK que enva el receptor.
En los siguientes ejemplos veremos de manera simplificada como realizar comuni-
caciones mediante el protocolo I2C en Arduino.
Tabla de entrada/salida
La tabla de entrada salida es muy simple (ver Tabla 4.7), unicamente tenemos un
LED en el nodo esclavo que servira para comprobar que el mensaje ha sido recibido
con exito.
Diagrama de flujo
Los diagramas de flujo que contienen eventos y comunicaciones suelen ser mas
complejos dado que tenemos que atendar a eventos que no son sncronos. Se reco-
mienda que el lector cree el diagrama de flujo correspondiente basandose en el codigo
mostrado en Tabla 4.3.3.
Codigo
Para evitar tratar con detalles de implementacion del protocolo I2C utilizaremos
la librera Wire cuyas funciones mas utilizadas se explicaran a continuacion. La docu-
mentacion oficial de la librera Wire puede ser consultada desde la siguiente direccion:
http://playground.arduino.cc/Main/WireLibraryDetailedReference.
begin(): Esta funcion nos permite iniciar la librera. Esta funcion es recurrente
en la mayora de las libreras. La implementacion suele reiniciar o poner en el
estado inicial la maquina de estados y los registros de trabajo.
4.3. COMUNICACION I2C 62
Una vez que se tiene claro cada una de las funciones de la librera, el codigo
mostrado en Cod. 4.5 y Cod. 4.6 no debe suponer ningun problema. En primer lugar
el Arduino maestro enva un byte de datos con el caracter ascii ((h)) (recuerda que un
caracter 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. Esta
funcion handler se inscribe al evento del bus mediante la funcion onRequest(). Ten en
cuenta que aunque en este codigo y a modo de ejemplo se ha realizado la operacion
de encender el led dentro de la funcion, esto no es recomendable dado que mientras
permanezcamos en dicha funcion el bus se mantendra ocupado.
Por ultimo, se recomienda que el lector modifique el codigo del esclavo y del
maestro con el objetivo de apagar y encender el led mediante algun evento, este
evento 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 la
funcion handler.
2 TWI (Two Wire Interface) es otra forma de nombrar al protocolo I2C
4.3. COMUNICACION I2C 63
1 #i n c l u d e <Wire . h>
2
3
4 void setup ( ) {
5 Wire . b e g i n ( ) ;
6
7 }
8
9 void loop ( ) {
10 Wire . b e g i n T r a n s m i s s i o n ( 1 ) ;
11 Wire . w r i t e ( " h " ) ;
12 Wire . e n d T r a n s m i s s i o n ( ) ;
13 delay (500) ;
14 }
1 #i n c l u d e <Wire . h>
2 const int 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 . b e g i n ( 1 ) ;
7 S e r i a l . begin (9600) ;
8 Wire . o n R e c e i v e ( h a n d l e r ) ;
9
10 }
11 v o i d h a n d l e r ( i n t num bytes ) {
12 char value = 0 ;
13 w h i l e ( Wire . a v a i l a b l e ( ) > 0 ) {
14 v a l u e = Wire . r e a d ( ) ;
15 }
16 S e r i a l . p r i n t ( " Read : " ) ;
17 S e r i a l . p rin tln ( value ) ;
18 i f ( v a l u e == 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 }else{
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 }
Tabla de entrada/salida
Como ya hemos comentado, la estructura de este ejemplo es diferente debido a que
unicamente estamos utilizando un sensor y no estamos haciendo ningun tipo actuacion
en funcion de los valores obtenidos. En la Figura 4.11 se puede observar la conexion
de este sensor. Unicamente utilizaremos el bus principal I2C. El MPU6050 tiene otro
bus I2C que utiliza para gestionar una cola FIFO.
Es muy importante que te asegures del voltaje de entrada de tu sensor. Algunos
MPU6050 tienen un conversor que permiten conectar el sensor a 5V, pero si dudas
conectalo a 3.3V.
Ten en cuenta que en este caso no hace falta una resistencia de pull-up, esto se
debe a que el sensor trae una resistencia interna. Muchos sensores que se comunican
3 Los valores ((crudos)) o ((RAW)) son aquellos valores que no han sido tratados y que son resultado
de una captura
4 El filtro complementario es la union de un filtro de paso bajo con un filtro de paso alto
4.3. COMUNICACION I2C 65
Codigo
Se puede revisar el codigo en Cod. 4.7. A continuacion, describiremos las partes
mas importantes del mismo.
1 #i n c l u d e <Wire . h>
2 #d e f i n e IMU 0 x68
3 #d e f i n e A R 1 6 3 8 4 . 0
4 #d e f i n e G R 1 3 1 . 0
5 #d e f i n e RAD TO DEG 5 7 . 2 9 5 7 7 9
6 i n t 1 6 t AcX , AcY , AcZ , GyX, GyY, GyZ ;
7 f l o a t Acc [ 2 ] ;
8 f l o a t Gy [ 2 ] ;
9 f l o a t Angle [ 2 ] ;
10 void setup ( ) {
11 Wire . b e g i n ( ) ;
12 Wire . b e g i n T r a n s m i s s i o n (IMU) ;
13 Wire . w r i t e ( 0 x6B ) ;
14 Wire . w r i t e ( 0 ) ;
15 Wire . e n d T r a n s m i s s i o n ( t r u e ) ;
16 S e r i a l . begin (9600) ;
4.3. COMUNICACION I2C 66
17 }
18
19 void loop ( ) {
20 Wire . b e g i n T r a n s m i s s i o n (IMU) ;
21 Wire . w r i t e ( 0 x3B ) ;
22 Wire . e n d T r a n s m i s s i o n ( f a l s e ) ;
23 Wire . requestFrom (IMU, 6 , t r u e ) ;
24 AcX = Wire . r e a d ( ) << 8 | Wire . r e a d ( ) ;
25 AcY = Wire . r e a d ( ) << 8 | Wire . r e a d ( ) ;
26 AcZ = Wire . r e a d ( ) << 8 | Wire . r e a d ( ) ;
27
28 Acc [ 1 ] = atan (1 (AcX / A R ) / s q r t ( pow ( ( AcY / A R ) , 2 ) + pow ( (
AcZ / A R ) , 2 ) ) ) RAD TO DEG ;
29 Acc [ 0 ] = atan ( ( AcY / A R ) / s q r t ( pow ( ( AcX / A R ) , 2 ) + pow ( ( AcZ /
A R ) , 2 ) ) ) RAD TO DEG ;
30
31 Wire . b e g i n T r a n s m i s s i o n (IMU) ;
32 Wire . w r i t e ( 0 x43 ) ;
33 Wire . e n d T r a n s m i s s i o n ( f a l s e ) ;
34 Wire . requestFrom (IMU, 4 , t r u e ) ;
35 GyX = Wire . r e a d ( ) << 8 | Wire . r e a d ( ) ;
36 GyY = Wire . r e a d ( ) << 8 | Wire . r e a d ( ) ;
37
38 Gy [ 0 ] = GyX / G R ;
39 Gy [ 1 ] = GyY / G R ;
40
41 Angle [ 0 ] = 0 . 9 8 ( Angle [ 0 ] + Gy [ 0 ] 0 . 0 1 0 ) + 0 . 0 2 Acc [ 0 ] ;
42 Angle [ 1 ] = 0 . 9 8 ( Angle [ 1 ] + Gy [ 1 ] 0 . 0 1 0 ) + 0 . 0 2 Acc [ 1 ] ;
43
44 S e r i a l . p 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 }
SS (Slave Select): Senal que se replica para cada esclavo. El maestro pone a
cero 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 desde
el 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 senales
SS, MOSI, MISO y SCK corresponden a los pines 16 al 19 respectivamente. En el
modulo 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 SS
correspondiente. Entonces enva en modo serie una palabra (byte) por la lnea MOSI
y simultaneamente acepta un byte proveniente desde el esclavo por la lnea MISO.
Esta transferencia se realiza generando 8 pulsos sobre la lnea de sincronizacion SCK
En Arduino existe una librera SPI.h que nos proporciona una serie de funciones
para controlar el proceso de comunicacion.
SPI.endTransaction
SPI.transfer
Los pasos para establecer una comunicacion SPI son los siguientes,
https://reference.digilentinc.com/pmod:pmod:DA4
7 tomada de https://reference.digilentinc.com/pmod:pmod:DA4
4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 70
Una vez configurado se entra en la funcion Loop y se comienzan a enviar los datos
que se sacan de memoria al conversor. La salida obtenida en nuestro ejemplo puede
verse en la figura Figura 4.14
4.4. PROTOCOLO SPI (SERIAL PERIPHERAL INTERFACE 71
73
5.1. INTERRUPCIONES EN EL ATMEGA328 74
Librera AVR: Utilizar la librera avr que implementa todas las interrupciones.
vez el bit de habilitacion para poder atender interrupciones anidadas, de manera que
cualquier interrupcion habilitada pueda interrumpir la rutina de interrupcion actual.
Luego, cuando se retorna de la interrupcion, el bit de habilitacion global se pone
automaticamente a 1.
Para habilitar las interrupciones correspondientes a cada periferico debemos poner
a 1 el bit correspondiente en el registro de mascara de Interrupcion. Si el bit de mascara
esta a 1 y a su vez el bit de habilitacion global esta a 1 entonces el procesador atendera
la interrupcion cuando ocurra. Para el caso de interrupciones externas existen registros
que permiten configurar que tipo de eventos se atenderan. Como se detallara mas
adelante, estos eventos pueden ser un cambio de nivel en un pin, un flanco ascendente
o descendente, etc. Si se desea profundizar mas acerca del manejo de registros de
interrupciones se recomienda al lector la hoja de datos del fabricante.
Ahora que sabemos como habilitar o deshabilitar las interrupciones, el siguiente
paso para poder atender a la misma es definir la rutina de interrupcion.
Para definir la rutina debemos utilizar la macro ISR. Esta macro tiene como
parametro 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 del
fabricante. Como ejemplo, si quisieramos definir una rutina de interrupcion para INT0
el codigo sera similar al mostrado en Cod. 5.1
Otra practica que es recomendable sobre todo en disenos que hacen uso intensivo
de las interrupciones, es capturar todas aquellas interrupciones para las cuales no se
ha definido ninguna rutina de interrupcion (normalmente esto indica un bug). Para
capturar todas las interrupciones se debe pasar la constante BADISR vect a la macro
ISR. En Cod. 5.2 se puede ver un ejemplo.
Otro caso particular, y para el cual existe una solucion, es cuando se desea utilizar
una misma rutina de interrupcion para dos interrupciones diferentes. Para conseguir
este comportamiento debemos utilizar la macro ISR ALIASOF junto a la macro ISR.
En Cod. 5.3 se puede ver un ejemplo de uso, en el cual la rutina de interrupcion de
la interrupcion PCINT1 pasa a ser igual a la de PCINT0.
5.2. MANIPULACION SOFTWARE 77
noInterrupts(): Esta funcion al igual que cli nos permite deshabilitar las in-
terrupciones.
En general se recomienda usar la librera de AVR dado que aparte de ser mas
eficiente es mucho mas flexible.
las interrupciones ya que por ejemplo, para usar una funcion delay() dentro de una
rutina de interrupcion debemos habilitar otra vez el bit de habilitacion global, dado
que por defecto al entrar en una rutina de interrupcion se llama de forma implcita
a la macro cli.
5.3.2. Codigo
El codigo para manejar las interrupciones en los microcontroladores ATmega es
muy sencillo y se puede resumir en los siguientes pasos:
Habilitar registro global de interrupciones: Mediante las macros sei y cli
podemos habilitar y deshabilitar el bit global de interrupciones.
Habilitar interrupcion especfica: Cada interrupcion tiene asociado un bit
de mascara de activacion individual. Mediante este bit podemos permitir in-
terrumpir al procesador mediante dicha interrupcion. En nuestro caso y para
nuestro procesador el registro se llama EIMSK y el bit que pondremos en 1 es
el correspondiente a la INT0 (lnea 13).
Configurar la interrupcion: Algunas interrupciones tienen registros que per-
miten configurar cuando lanzar la misma. En nuestro caso, para la interrupcion
INT0 seleccionaremos el flanco descendente en el pin 2 poniendo un 1 en el
bit ISC01.
5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 79
1 #i n c l u d e <a v r / i n t e r r u p t . h>
2
3 const int n o t i f i c a t i o n l e d = 13;
4 c o n s t i n t b ut ton = 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 sei () ;
13 EIMSK |= ( 1 << INT0 ) ;
14 EICRA |= ( 1 << ISC01 ) ;
15 }
16
17 void loop ( void )
18 {
19
20 }
21
22 ISR ( INT0 vect )
23 {
24 digitalWrite ( notification led , ! digitalRead ( n o t i f i c a t i o n l e d ) ) ;
25 }
VCC: Este sensor trabaja a 5V por lo que podra ser conectado directamente
al Arduino a traves del pin de 5V del mismo.
GND: Comun.
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 una
senal de 40Khz que cuando rebota contra un objeto y vuelve al sensor de nuevo
hace que el sensor emita un pulso positivo con una longitud proporcional a la
distancia del objeto por el pin echo.
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 serie
para poder mostrar la distancia por pantalla y ademas configuramos los pines de
entrada/salida. En este caso tal y como nombramos en la Tabla 5.3 tenemos un pin
de entrada y dos de salida. Los de salida corresponden uno al trigger del sensor y el otro
al pin de transmision Tx del puerto serie. El pin de entrada esta conectado a la senal
echo del sensor. Es importante en este punto recordar que tenemos lneas limitadas de
interrupcion en nuestra placa y que estas estan conectadas a unos pines determinados.
En este caso, la lnea de interrupcion que hemos utilizado es la conectada al pin 2 del
Arduino 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 de
la librera de Arduino. Esta funcion tiene tres parametros: N de interrupcion, rutina
de 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 hemos
hecho en las lneas 13 y 14 del ejemplo 1, solo que en lugar de escribir en los registros
utilizando las libreras avr, lo que hacemos es llamar a esta funcion que se encarga de
eso. 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 al
que esta conectado el sensor, sino a la interrupcion del microcontrolador. En este caso
el pin 2 esta conectado como ya hemos dicho a la interrupcion 0 por lo que este sera
el numero que tendremos que utilizar. En las ultimas versiones del entorno Arduino
se ha anadido una macro llamada digitalPinToInterrupt(pin) que permite despreocu-
parnos de este parametro. Comprueba si tu entorno ha anadido este macro y si es
as puedes utilizarla. El otro parametro importante es la funcion que sera llamada
cuando recibamos la interrupcion (rutina de Interrupcion). La funcion no debera re-
tornar ningun tipo (void). Por ultimo el parametro CHANGE indica que nos avise
en cualquier cambio en el pin, esto es, alto a bajo o bajo a alto. (en Subseccion 5.2.2
puedes 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 encuentra
un objeto debemos medir el tiempo entre el flanco de subida del pin echo y el flanco de
bajada del mismo. Recordemos que la distancia es proporcional a dicho tiempo. Como
la funcion interrupt echo() se llama cada vez que hay una variacion en el pin echo lo
unico 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 tiempo
en las variables correspondientes. El tiempo se registra con la funcion micros(). Se
podra usar la funcion millis() para medir tiempos en milisegundos pero esto no es
posible dentro de una rutina de interrupcion.
En el datasheet del sensor se indica que el tiempo se debe dividir entre 58 para
obtener la distancia en centmetros.
Otro punto a resaltar es la activacion del pin trigger que debe estar en estado
alto durante al menos 10 microsegundos. Aunque esto se podra realizar mediante un
timer e interrupciones, en este caso hemos optado por un delay en microsegundos.
El retardo final de 100 milisegundos (linea 21) unicamente tiene la funcion de
mejorar la visualizacion de la distancia.
5.4. EJEMPLO 2: MIDIENDO DISTANCIAS 82
Hasta ahora cada vez que buscamos repetir una accion en un tiempo determinado
hemos 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 este
captulo la utilizacion de dicha funcion no debe realizarse en todos los casos, es mas,
por regla general debera evitarse su uso.
Cuando se utiliza la funcion delay() el Arduino se queda en un bucle activo. Por
lo tanto, durante ese tiempo el microcontrolador no realizara ninguna otra funcion,
esto es, estara consumiendo energa pero no estara haciendo ningun trabajo util como
por ejemplo podra ser registrar diferentes valores de los sensores.
En este captulo veremos de manera resumida como utilizar otras funciones como
por 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 que
sin duda alguna es la base para todas las funciones relacionadas con el tiempo. Este
periferico es el timer. Un timer es un circuito que nos permite contar eventos, por
ejemplo ciclos de reloj, eventos externos, etc, para generar formas de ondas, circuitos
de reloj, comparadores, etc.
Dependiendo de en que microcontrolador este basado nuestro Arduino tendremos
mas o menos timers, en el caso del microcontrolador ATmega328 podemos encontrar
tres 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 del
ATmega328. A continuacion pasaremos a explicar cada uno de los componentes que
forman este modulo de tal modo que podamos tener una vision amplia a la hora de
utilizar cualquier funcion relacionada con el tiempo en nuestros disenos.
Basicamente un timer esta formado por un circuito de control (Control Logic) que
se encarga de contar de manera ascendente o descendente los eventos que se producen
83
6.1. TIMERS 84
6.1.1. Registros
TCNTn: Este registro nos permite saber en todo momento el valor del timer
correspondiente (la letra n indica el numero de timer que puede ser 0 o 1). Este
valor se incrementa o decrementa, segun sea el modo habilitado, cada vez que
se produzca un evento de reloj, o de evento externo, a la entrada del circuito de
control. El registro TCNT0 es un registro de 8 bits por lo que el valor maximo
que se podra alcanzar es 255 o 0xFF en hexadecimal.
OCR0a: El valor de este registro se compara cada ciclo del timer con el valor
del registro TCNTn. Cuando se alcanza el valor configurado en este registro se
activara el bit OCF0A. El resultado de la comparacion se puede utilizar para
generar distintas formas de onda o PWM como se vera mas adelante.
Modo normal: Es el modo mas simple de utilizacion. Para activar este modo
se deben poner a 0 los bits WGM0:2 ubicados en los registros TCCR0A y
TCCR0B (cuidado, los dos primeros bits se encuentran en el registro TCCR0A,
sin embargo el ultimo bit WGM02 se encuentra en el registro TCCR0B ). En
este modo, el registro TCNT0 se incrementa en cada ciclo de reloj hasta llegar
al valor 0xFF donde se desborda y vuelve al valor 0x00, activando a la vez la
senal de overflow (TOV0). Esta senal no volvera a 0 de forma automatica, sera
el programador el encargado de realizar dicho cambio. Si esta habilitado el bit
de interrupcion por desbordamiento, cuando se produzca el mismo se generara
una interrupcion.
Modo CTC o Clear Timer on Compare Match: Para activar este modo
debemos configurar los bits WGM02:0 al valor 2, esto es, ((010)). En este modo
el registro OCR0A se utiliza para variar la resolucion del timer. La diferencia
con el modo normal es que en este modo cuando se alcanza el valor configurado
en el registro OCR0a, el registro TCNT0 se pone de nuevo al valor 0. En la
Figura 6.2 se puede ver un esquema que ejemplifica su uso. TCNT0 empieza
en el valor 0 y va ascendiendo hasta llegar al valor configurado en OCR0a
(raya horizontal que indica el valor de comparacion). Una vez que se alcanza
dicho valor la bandera OC0a del registro TIFR0 se activa de tal modo que si
el programador comprueba dicho bit en su programa sabra si se ha alcanzado
el valor de comparacion. La ejecucion repetida del timer en modo CTC nos
genera una forma de onda en el pin de salida OCn. Esta forma de onda se
puede ver en un osciloscopio si conectamos una de las puntas del mismo al
pin asociado a la senal OC0 en el caso de que el timer este configurado para
permutar dicho pin en cada acierto de comparacion (esto se configura en el
registro TCCR0A, TCCR0B ). En el diagrama de la Figura 6.2 se muestran
diferentes valores del registro de comparacion OCR0a, variando el periodo de la
senal. Segun el datasheet para generar una senal cuadrada con una frecuencia
determinada podemos utilizar la formula mostrada en la Ecuacion 6.1. Mas
adelante, en los ejemplos, veremos como utilizar esta formula y como seleccionar
los valores para cada uno de los parametros.
6.1. TIMERS 86
Modo Fast PWM: El modo Fast PWM o desde ahora PWM (Pulse Width
Modulation) (con el objetivo de simplificar), se habilita mediante la activacion
de los bit WGM02:0 = 3 o 7. Con este modo se nos proporciona un manera
de generar formas de onda PWM de alta frecuencia y alta precision. El timer
cuenta desde el valor indicado en BOTTOM hasta el valor indicado en TOP,
cuando se alcanza el valor TOP se vuelve a iniciar la cuenta desde BOTTOM.
El valor de TOP es 0xFF cuando WGM02:0 es 3, sino toma el valor del registro
OCR0A cuando es 7. Posee dos modos, salida de comparacion no-invertida o
invertida. En el modo no-invertida la salida se pone a 0 cuando se alcanza el
valor de comparacion y a 1 cuando se llega al valor BOTTOM. En los ejemplos
veremos como se materializa este modo pero para que el lector se haga una idea,
este modo nos permite ((indicar)) al microcontrolador que porcentaje de tiempo
queremos que el pin este en alto y cuanto tiempo queremos que este en bajo,
pudiendo emular valores analogicos variando el ancho del semiciclo positivo de
la senal generada. Si por ejemplo alimentamos un motor con una frecuencia de
trabajo ((X)), podremos variar su velocidad modificando el porcentaje de tiempo
que se entrega energa al motor. En la Figura 6.3 se puede ver un ejemplo de
uso del modo PWM, a continuacion pasaremos a explicar el mismo aunque en
los ejemplos se vera de manera mas detallada.
6.1. TIMERS 87
Modo Fast PWM con correccion de fase: Este modo es muy parecido
al Fast PWM con la unica diferencia que la salida esta en fase y ademas la
frecuencia maxima que se puede alcanzar es la mitad con respecto al modo
PWM 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 en
Ecuacion 6.3.
f clk
f 0Cnx = (6.1)
2 N (1 + OCRnX)
f clk
f 0Cnx = (6.2)
N (1 + T OP )
f clk
f 0CnxP CP W M = (6.3)
2 N T OP
codigo.
En el setup() se llama a la macro cli(), como ya se explico en la Captulo 5 esta
macro unicamente deshabilita las interrupciones de manera global, de manera que
podamos realizar toda la configuracion sin miedo a ser interrumpidos. Una vez desha-
bilitadas las interrupciones, el siguiente paso es configurar tanto el registro TCCR1A
como el registro TCCR1B. El valor 0 asignado a TCCR1A tiene que ver con lo ex-
plicado anteriormente. En la Figura 6.4 puedes la definicion del registro. El registro
TCCR1B sin embargo s que tiene un valor ((util)) asignado. En un primer momento
el lector podra preguntarse por que se asigna dicho valor. La razon se encuentra en
la configuracion del prescaler. Si echamos un vistazo a la Figura 6.6 y a la Figura 6.5
podemos ver que para aplicar un prescaler de 1024 tenemos que asignar el valor ((101))
a los bits ((CS1:2-0)). Como todos los demas bits deben estar a cero (ver explicacion
anterior y datasheet) el valor ((00-00101)) en decimal es 5.
El siguiente paso consiste en configurar el registro TIMSK1. En este caso hemos
optado por otro modo de asignacion el cual se explica en Seccion B.1. El bit que hay
que habilitar es el TOIE1 que nos permitira indicar al microcontrolador que queremos
interrumpir nuestro programa cuando se produzca un overflow.
Por ultimo, en la fase de setup() tenemos que poner el valor inicial de 536 al registro
contador. Es muy importante que siempre que modifiquemos un registro de 16 bits
en la arquitectura AVR, en primer lugar modifiquemos el registro que representa la
parte alta (H) y despues el que representa la parte baja (L). En este caso el valor
en binario de 536 es ((00000010 00011000)) por lo que la parte alta la escribimos en
el registro TCNT1H y la parte baja en el registro TCNT1L. Como puedes observar
en este caso hemos utilizado la notacion binaria con el objetivo de que se cubran
todas las posibilidades, aunque la mas portable es la notacion utilizada en el registro
TIMSK1.
Lo que resta del codigo ya ha sido explicado en captulos anteriores y por lo tanto
no se volvera a explicar.
sobre la salida. En nuestro caso elegiremos el modo ((01)) que nos permite permutar
la 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 contar
hasta llegar al valor configurado en el registro OCRxA y OCRxB, en este momento se
generara una interrupcion siempre y cuando el bit OCIEXa o OCIEXb del registro
TIMSKX 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 y
finalmente se volvera a contar desde 0.
El lector puede observar que lo que estamos realizando en este ejercicio es generar
dos frecuencias a partir de un unico timer, esto no siempre sera posible, en este
caso 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 mismo
se podra haber hecho con la funcion noInterrupts(), sin embargo, es mas eficiente
y portable la segunda opcion. Seguidamente se ponen a cero los dos registros de
configuracion del timer de modo que tengamos certeza de que partimos de inicialmente
de 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 los
comparadores, es decir, el COM1B0 y el COM1A0. Observa como en este caso se ha
utilizado la notacion que se considera mas portable (lnea 5). Con el registro TCCR1B
hacemos lo mismo, solo que en este caso los bits a activar son los del prescaler y los
del modo de funcionamiento del timer.
Para ((permitir)) al timer enviar interrupciones al procesador, debemos activar el
bit OCIE1A. Con esto conseguiremos generar dos frecuencias distintas. La frecuencia
de dos hercios corresponde al comparador ((A)) mientras que la frecuencia de un hercio
pertenece al registro ((B)). Por otro lado, el registro ICR1 es el encargado de situar el
valor de TOP. Es importante observar que tanto el valor de ICR1 como el de OCR1B
es el mismo por 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 calculos
son correctos. La eleccion de estos pines se debe al mapeo que realiza el Arduino
de los pines del ATmega328. Si quieres saber mas sobre esto se recomienda ver el
esquematico 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
El ultimo modo que veremos en profundidad sera el modo Fast PWM. Como ya
se comento anteriormente, con este modo podemos modificar la cantidad de tiempo
que 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, como
ejemplos podemos nombrar los servos de los aviones radio control o los variadores de
los motores industriales, etc.
En este ejemplo veremos como utilizar el modo Fast PWM para mover un servo
como el de la Figura 6.10. Este servo esta incluido en el kit ((Grove-Starter Kit for
Arduino)).
Para mover un servo el primer paso que tenemos que seguir es el de estudiar los
fundamentos de los servos. Un servo es un dispositivo electromecanico, que mediante
una logica empotrada consigue decodificar una senal de entrada para generar como
resultado un desplazamiento a una posicion determinada. Estos dispositivos se suelen
utilizar en aeromodelismo pero su campo de aplicacion es mucho mas extenso, otro
ejemplo podran ser los robots.
A continuacion pasaremos a explicar cada una de las partes que forman un servo-
motor:
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 = 0 b11110100 ;
11 ICR1L = 0 b00100011 ;
12
13 OCR1AH = 0 b01111010 ;
14 OCR1AL = 0 b00010001 ;
15
16 OCR1BH = 0 b11110100 ;
17 OCR1BL = 0 b00100011 ;
18
19 pinMode ( 9 ,OUTPUT) ;
20 pinMode ( 1 0 ,OUTPUT) ;
21 pinMode ( 7 ,OUTPUT) ;
22
23 S e r i a l . begin (9600) ;
24
25 sei () ;
26
27 }
28
29 ISR ( TIMER1 COMPA vect ) {
30 // S e r i a l . p r i n t l n ( Entro ) ;
31 i f (OCR1A < OCR1B) {
32 OCR1A = OCR1B;
33 }else{
34 OCR1AH = 0 b01111010 ;
35 OCR1AL = 0 b00010001 ;
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 }
1 ms
1,5 ms
2 ms
20 ms
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 = 5 0 0 0 ;
8 pinMode ( 9 ,OUTPUT) ;
9 pinMode ( 1 0 ,OUTPUT) ;
10 pinMode ( 7 ,INPUT) ;
11 S e r i a l . begin (9600) ;
12 sei () ;
13 OCR1A = 3 7 5 ;
14 }
15
16 void loop ( ) {
17 i f ( Serial . available () ){
18 OCR1A = S e r i a l . p a r s e I n t ( ) ;
19 }
20 }
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.5
porque 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 20
ms. 2.5 ms es un 12,5 % del periodo. Si el periodo esta determinado por el valor TOP
almacenado en el ICR1, en este caso 5000, un 12,5 de 5000 sera igual a 625. Como
ya hemos dicho estos valores hay que probarlos y en el caso del servo de ((Seeed)) este
valor hace que el servo se bloquee porque el 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 final
todo se resume en una palabra, TIMERs. En esta seccion veremos como abstraernos
de todos los detalles de implementacion y utilizando las libreras de Arduino crear
nuestros 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 diferencia
es que no se programa de forma textual (hoy en da se esta incorporando a la industria
esta manera de programar los PLC), por el contrario se programan mediante diagra-
mas Ladder. Estos diagramas tienen un elemento llamado lneas de alimentacion que
representan 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 disenos
a los entornos industriales y que en la medida de lo posible se minimice el coste de
mantenimiento o lo que es lo mismo el tiempo requerido para entender nuestro pro-
grama. En este libro hemos buscado una solucion estructurada que ayudara bastante
a la consecucion de este objetivo.
Apoyandonos en la orientacion a objetos encapsularemos cada elemento en una
clase 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 sera encapsular el valor de retorno en otra clase que nos
brindara mas informacion.
La orientacion a objetos no es el objetivo de este libro, unicamente lo utilizaremos
como una manera de mostrar al usuario la verdadera importancia de estructurar el
codigo y para que pueda ver los beneficios inmediatos de su utilizacion. El codigo
que se utilizara en los ejemplos tratara utilizara los conceptos mas sencillos posibles
en cuanto a orientacion a objetos se refiere intentando evitar aspectos mas complejos
como el polimorfismo, herencia, etc.
Este es un buen momento para que te animes a leer el Seccion C.1 y prepares
tu entorno de desarrollo para estos ejemplos, aunque nosotros sigamos utilizando el
entorno Arduino te servira para ver las bondades de Eclipse en cuanto tenemos un
programa medianamente complejo.
Como ya se comento en la Seccion 6.1, para medir tiempos podemos utilizar varias
6.2. MULTITASKING 97
funciones de Arduino, nosotros utilizaremos la funcion millis() que nos permite saber
cuanto tiempo ha pasado desde que se ha iniciado el programa. Para aplicaciones que
estaran activas durante mucho tiempo hay que tener en cuenta que el contador se
pondra a 0 en 50 anos, por lo que tendremos que salvar de alguna manera el tiempo
(por ejemplo guardandolo en la memoria EEPROM).
1
2 const int led pin = 13;
3
4 #d e f i n e TIME TOOGLE LED 500
5 #d e f i n e TIME COMMUNICATION SEND 100
6
7 bool scan led () {
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 last time led = millis () ;
11 digitalWrite ( led pin , ! digitalRead ( led pin ) ) ;
12 S e r i a l . p r i n t ( " Led toogle at : " ) ;
13 Serial . println ( last time led ) ;
14 }
15 return true ;
16 }
17
18 bool scan communications ( ) {
19 s t a t i c long last time communication = 0;
20 i f ( ( m i l l i s ( ) l a s t t i m e c o m m u n i c a t i o n ) >= TIME COMMUNICATION SEND)
{
21 last time communication = 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 . println ( last time communication ) ;
24 }
25
26 }
27
28 void setup ( ) {
29 pinMode ( l e d p i n , OUTPUT) ;
30 S e r i a l . begin (9600) ;
31 }
32
33 void loop ( ) {
34 scan led () ;
35 scan communications ( ) ;
36 }
1 c l a s s LED {
2 private :
3 int pin led ;
4 long last update time ;
5 long time to update ;
6
7 v o i d update ( ) {
8 i f ( ( m i l l i s ( ) t h i s > l a s t u p d a t e t i m e ) >= t i m e t o u p d a t e ) {
9 t h i s > l a s t u p d a t e t i m e = m i l l i s ( ) ;
10 digitalWrite ( pin led , ! digitalRead ( pin led ) ) ;
11 }
12 }
13
14 public :
15
16 LED( i n t p i n l e d , l o n g t i m e t o u p d a t e ) {
17 t h i s > p i n l e d = p i n l e d ;
18 t h i s > l a s t u p d a t e t i m e = 0 ;
19 t h i s >t i m e t o u p d a t e = t i m e t o u p d a t e ;
20 pinMode ( p i n l e d , OUTPUT) ;
21 }
22
23 bool scan ( ) {
24 update ( ) ;
25 }
26 };
27
28 LED l e d 1 ( 1 3 , 5 0 0 ) ;
29
30 void setup ( ) {
31
32 }
33
34 void loop ( ) {
35 led1 . scan ( ) ;
36
37 }
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 Captulo 1, Arduino mantiene la filosofa del software libre y
nos proporciona todos los esquemas necesarios para construir o mejorar su plataforma.
El Arduino que vamos a construir en este apendice es muy sencillo y unicamente
tendra lo indispensable para que el lector pueda conectarlo al ordenador y ejecutar
los ejemplos realizados en este libro, por lo que eliminaremos muchos elementos de
seguridad y filtrado que tiene el Arduino original con el objetivo de simplificar su
montaje.
1. Protoboard
3. Cables Jumper
4. LM7805
103
A.3. ENSAMBLADO 104
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 centmetro del microcontrolador o ponerlo a 20 centmetros
puede ser determinante en algunos sistemas2 . En la Figura A.1 se han dispuesto los
componentes de modo que al lector le sea sencillo identificar los mismos y situarlos
en el circuito por lo que, se recomienda que realice ligeras modificaciones mantenien-
do las reglas de conexion (mismo esquema) de modo que los componentes esten mas
proximos entre ellos.
El circuito se puede dividir en tres partes. A continuacion, describiremos cada una
de ellas:
Etapa de alimentacion: El circuito esta preparado para ser alimentado con
un voltaje desde 5 V a 18 V de corriente continua, es importante que la
corriente sea continua pues de lo contrario el circuito no funcionara, pudiendo
llegar a dejarlo inservible. Si el lector no tuviera una fuente de corriente conti-
nua con estas caractersticas, puede montarse una pequena fuente, para lo cual
necesitara entre otras cosas un transformador (fuente con transformador) y un
puente de diodos (en Internet hay mucha informacion a cerca de las fuentes de
alimentacion).
Lo siguiente es conectar los terminales a la protoboard. Existe un convenio de
conexion por el cual se recomienda conectar el terminal positivo a la primera
fila de la protoboard y el negativo a la segunda. El lector podra reconocer estas
filas porque normalmente vienen separadas del resto de la protoboard.
Ahora que la placa esta alimentada, solo queda situar los componentes tal cual
aparecen en el esquema Figura A.2 tambien puedes guiarte por la Figura A.1
aunque recuerda, esta no es la distribucion optima. Es importante que se respete
la polaridad del condensador electroltico dado que de lo contrario, si se
supera la tension inversa umbral, podremos romper el mismo. Para saber como
conectar el condensador busca una lnea vertical de color blanco que indica el
terminal negativo. (ver Figura A.1)
A la salida del integrado LM7805 (lnea naranja) tendremos 5 V estables que
sera la ((fuente de alimentacion)) del microcontrolador ATmega328.
1 USBASP es un programador para los microcontroladores AVR libre, por lo que desde la web del
autor (http://www.fischl.de/usbasp/) el lector podra acceder tanto al firmware como al circuito
pudiendo construir su propio programador.
2 Los cables tienen una capacitancia y una inductancia que afecta al circuito, en disenos donde la
precision es muy importante, hay que tener en cuenta la longitud de los cables entre otros factores
A.3. ENSAMBLADO 105
5v-12v
Azul = Tierra
Rojo = Entrada
Naranja = 5v estables
Cyan = Cristal
-------------------
Marron = Rx(Arduino) - Tx(FTDI)
Morado = Tx(Arduino) - Tx(FTDI)
16MHz Cristal
cuarzo.
A.4. PROGRAMACION DEL BOOTLOADER 107
mar el microcrocontrolador sin necesidad de desoldarlo y por lo tanto aumentando la vida util del
microcontrolador
A.4. PROGRAMACION DEL BOOTLOADER 108
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 multmetro
y medir la diferencia de potencial (voltaje) entre un pin y tierra de tal manera que
el pin que devuelva el voltaje de programacion (3.3-5V) sera el VTG o VCC y el
pin comun sera tierra. El lector tambien se puede guiar por el cable rojo, de modo
que este sera el pin 1. Existen alternativas para facilitar la conexion del programador
al Arduino como por ejemplo el conversor ((ICSP Header)) cuya unica funcionalidad
es proporcionar una interfaz ((inline)) que se podra conectar a la protoboard siendo
mucho mas sencilla la conexion final al ATmega.
Una vez que el lector haya localizado cada uno de los pines del conector o haya
adquirido un conversor, ya esta en disposicion de realizar la conexion entre el ICSP y
el microcontrolador. Los pines son los proporcionados por la interfaz de comunicacion
ISP, es decir, GND, VCC, MISO (Master Input Slave Output), MOSI(Master Output
Slave Input), SCK (Clock), RESET. La conexion ISP es muy sencilla gracias a lo
descriptivo de los nombres de sus pines, sin embargo, a continuacion se detalla las
conexiones a realizar:
Pin MISO programador Pin 18 ATmega328
Pin MOSI programador Pin 17 ATmega328
Pin SCK programador Pin 13 ATmega328
Pin RESET programador Pin 1 ATmega328
Ahora solo queda cargar el bootloader al ATmega328, para ello hay que conectar
el programador al conversor ICSP o en caso de no haber utilizado el conversor, conec-
tar cada uno de los pines del conector ICSP al microcontrolador. Despues conectar
el programador al computador, es importante que los drivers esten completamente
configurados, 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
que pulsar sobre Herramientas Grabar secuencia de inicio. Si todo ha ido bien
el 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 en
un microcontrolador, debe manejar una serie de conceptos que normalmente no son
necesarios o indispensables cuando se programa una solucion en un computador de
proposito general. Tener claro donde almacenar una cadena de texto o que espacio de
memoria asignar para una determinada tarea son algunos de estos conceptos.
En este apendice se va a guiar al lector en el aprendizaje de las operaciones logicas
con bits. Es importante tener en cuenta que la informacion en un microcontrolador
se suele almacenar en registros. Estos registros pueden almacenar datos de diferente
naturaleza, por ejemplo, un microcontrolador que forme parte de una impresora puede
tener un registro para el control de la misma (calentar rodillo, situar en 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 la
informacion contenida en estos registros. En este apendice se mostraran las diferentes
tecnicas que se utilizan para obtener y tratar los datos de los registros.
111
B.2. QUE ES UN REGISTRO? 112
Registros del
procesador
Memoria cach
(L1,L2,L3)
Capacidad Velocidad y
Coste por bit
Memoria RAM
(random access memory)
Disco duro
Almacenamiento secundario
Cloud
(Copias de seguridad)
Podemos imaginar un registro como una caja (ver Figura B.2) que tiene diferentes
compartimentos. Cada uno de estos compartimentos es un bit. Dependiendo del tipo
de registro nos interesara toda la ((caja)) (registro) o unicamente uno de sus ((cajones))
(bit). Por ejemplo, si tenemos un registro de datos, seguramente nos interese el registro
entero y no uno de sus bit dado que normalmente cuando se accede a un registro de
datos se busca el valor del numero almacenado, esto no implica que no se pueda
acceder a un unico bit, por ejemplo para saber si el numero es positivo o negativo en
un numero representado en signo magnitud.
1 Con 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))
Los cuatro elementos de la lista anterior hacen referencia al mismo valor, es decir,
al 1 en decimal. El primer elemento representa el 1 en decimal. Se sabe que un
valor esta expresado en binario cuando le antecede el prefijo ((0b)). El tercer elemento
(0x01 ) representa el mismo valor pero en hexadecimal, es importante recordar, que
un dgito hexadecimal equivale a cuatro bits (el conjunto de 4 bits se le conoce como
nibble). La ultima representacion es la mas comun pero no sera explicada hasta la
Subseccion B.3.3
Ahora imaginemos que pasa si ocurrira si quisieramos configurar el pin 1 del
puerto ((c)) como salida. En principio actuaramos de la misma manera, es decir,
configuraramos el valor 0b0000010, pero como el lector habra podido observar, el
valor del pin 0 cambiara y se configurara como entrada (0). La manera mas sencilla
de solventar este comportamiento es asignar el valor 0b00000011 pero si no se conoce
que pines estan como salida en un estado anterior, como se calcula el valor a escribir?
esta pregunta sera contestada en la siguiente seccion.
0b00000001
OR 0b00000010
-------------
0b00000011
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 1
0 0 0 0 0 0 1 0
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 1
1 << 3
0 0 0 0 1 0 0 0
OR
0 0 0 0 0 0 1 1
=
0 0 0 0 1 0 1 1
que nos permitira hacer nuestro codigo mas legible y nos ahorrara mucho codigo in-
necesario.
7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 0
0 << 3
0 0 0 0 0 0 0 0
OR
0 0 0 0 1 0 1 1
=
0 0 0 0 1 0 1 1
Figura B.5: Configuracion del pin 3 como entrada sobre registro previamente con-
figurado (procedimiento erroneo)
7 6 5 4 3 2 1 0
0 0 0 0 1 0 0 0
NOT
1 1 1 1 0 1 1 1
AND
0 0 0 0 0 0 1 1
=
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 CON
ARDUINO
C.1. Introduccion
En este apendice veremos como configurar el entorno de desarrollo Eclipse de
modo que el mismo pueda ser utilizado para programar y grabar programas en la
plataforma Arduino.
El lector puede preguntarse por que utilizar otro IDE si ya ha utilizado el oficial
de Arduino y no ha tenido ningun problema.
Arduino fue pensado como una plataforma con una curva de aprendizaje muy
baja, la ventaja obvia es la rapida adaptacion de cualquier persona con mnimos
conocimientos de informatica al entorno. El problema, no tan obvio en un primer
momento, 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 ambiente
profesional.
119
C.3. INSTALACION DEL ENTORNO 120
nuestros proyectos.
AVR-GCC: <directorioSDK>/hardware/tools/avr/bin
embargo para GNU/Linux esta herramienta no viene incluida dado que forma
parte de GNU/Linux y esta de forma nativa para todas las distribuciones por lo
que en este caso seguramente el propio plugin detecte el lugar donde se encuentra
instalado y aparecera algo como ((system)) en esta variable.
AVRDude: <directorioSDK>/hardware/tools/avr/bin
Una vez configuradas las libreras deberemos ((decir)) a eclipse que genere el ((.hex))
para el Arduino. Para ello hay que ir a las propiedades de AVR dentro del proyecto
luego ((C/C++ Build, Settings, Additional Tools in Toolchain)) y seleccionar la opcion
((Generate HEX file for flash memory)).
NO WARRANTY
There is no warranty for this work. Except when otherwise stated in writing, the
Copyright Holder provides the work as is, without warranty of any kind. The entire
risk as to the quality and performance of the Work is with you. The Copyright Holder,
or any author named in the components of the work, or any other party who may
distribute and/or modify the Work as permitted above, be liable to you for damages
arising out of any use of the work (including, but not limited to, loss of data, data
being rendered inaccurate, or losses sustained by anyone as a result of any failure of
the Work to operate with any other programs), even if the Copyright Holder or said
author or said other party has been advised of the possibility of such damages.
127