You are on page 1of 98

Copyright © 2017,Rubén Beiroa Mosquera

Todos los derechos reservados.


Queda prohibida la reproducción total o parcial sin el permiso previo y
por escrito del titular del Copyright.

1
Índice
Introducción 3
Pulsador «debounce» 3
Solución por hardware 4
Ampliación instrucciones de gestión de tiempo 18
Pull-Up debounce 21
Pull-Down debounce 29
Solución por software (programación) 33
Memorias 34
Memoria EEPROM 34
Tarjetas SD 46
Ampliación entradas analógicas 77
Pin AREF 77
Pin IOREF 79
Señales PPM 79

2
Introducción
Este libro es la continuación de los tomos 1 y 2 de Arduino.
Entendiendo el Mundo, nivel básico. En estos volúmenes se trataron
los aspectos fundamentales de la programación, del hardware y del
IDE de Arduino, junto con la electrónica necesaria.

Partiendo de esos conocimientos, se pretende aumentar las


capacidades del público lector, profundizando en las cuatro áreas de
estudio a la hora de trabajar con Arduino. Será necesario haber leído
los libros anteriores para poder seguir el hilo de las explicaciones.

El enfoque del libro no cambia con respecto a los anteriores: se


expondrán casos prácticos para explicar cada nuevo conocimiento y
se propondrán ejercicios para que sean resueltos. Para el mejor
aprovechamiento del libro, será necesario disponer de material para
la realización de todos los casos.

Pulsador «debounce»
Un problema que no resolvimos en los libros anteriores era el
«rebote» de una señal al pulsar un botón. Como vimos, se describía
una gráfica como la siguiente:

Ilustración 1

Este problema tiene dos formas de resolverlo: por programación o


por hardware. Cada solución presenta sus ventajas e inconvenientes;
por lo general, recomendamos solucionar por programación todo lo
posible. No obstante, presentaremos ambas soluciones.

3
Solución por hardware

Solucionar el problema del rebote de una señal nos va a dar pie,


como veremos, a introducir muchos conceptos nuevos. Como en este
apartado definiremos la solución por hardware, empezaremos
hablando de los divisores de tensión.

Ilustración 2. Divisor de tensión

Un divisor de tensión no es más que un circuito formado por una


resistencia conectada a cualquier otro componente electrónico. Su
función es la de reducir la tensión e intensidad aplicada a este
segundo dispositivo, estableciendo un valor seguro para no dañarlo.

En el caso anterior, la resistencia R1 sirve para proteger a la R2,


limitando el valor de tensión e intensidad que llega a esta. La fórmula
de un divisor de tensión es la siguiente:

Vpila = VR1 + VR2

Ilustración 3.

Vpila = I × R1 + I × R2

4
Como la intensidad que circula por ambas resistencias es la misma
podemos sacar la intensidad como factor común:

Vpila = I × (R1 + R2)

I = Vpila / (R1 + R2)

En un divisor de tensión, lo que se busca es fijar una tensión al


segundo elemento, que sería en este caso VR2. Para ello, lo que
debemos hacer es, en función de la fuente de alimentación (en este
caso la pila), calcular el valor de las resistencias, para fijar un valor
VR2 adecuado. Para calcular la relación de las resistencias, sustituimos
y despejamos de las siguientes fórmulas:

I = Vpila / (R1 + R2)

VR2 = I × R2

VR2 = [Vpila / (R1 + R2)] × R2. O lo


Sustituimos la intensidad:
que es lo mismo: VR2 = (Vpila × R2) / (R1 + R2). ¿Para qué
sirve todo esto? Además de para llegar a entender la solución de un
pulsador «debounce», podemos adaptar tensiones que superen la
tensión de trabajo de un Arduino (5 V).

Vamos a suponer que alimentamos nuestro Arduino a través de una


pila y necesitamos conocer el valor de la tensión. La pila es de 9 V, lo
cual no representa un problema para alimentar un Arduino, ya que
disponemos del hardware necesario para regular alimentaciones de
entrada superiores a 5 V.

Ilustración 4.

Sin embargo, para medir el valor de la pila con una entrada


analógica, necesitamos adaptar su tensión de salida, que

5
conseguiremos con un divisor de tensión. A continuación, dividimos la
tensión de la pila por la mitad con el divisor de tensión.

Ilustración 5.

Para adaptar la tensión, debemos calcular la relación entre las


resistencias. Ya hemos razonado la siguiente expresión:

VR2 = (Vpila × R2) / (R1 + R2)

Si despejamos el valor de pila:

VR2 / Vpila = (R2) / (R1 + R2)


La tensión de la pila es de 9 V y queremos una relación de 9 a 4,5,
por lo que sustituimos valores:

4.5 / 9 = (R2) / (R1 + R2)

0,5 = (R2) / (R1 + R2)

Si empezamos a agrupar términos:

0.5 × (R1 + R2) = (R2)

0.5 × R1 + 0.5 × R2 = (R2)

6
0.5 × R1 = (R2) - 0.5 × R2

0.5 × R1 = 0.5 × R2

R1 = R2

En conclusión, para que se cumpla la relación entre la tensión de la


pila (9 V) y la tensión que mida la entrada analógica (4,5 V), las
resistencias deben ser del mismo valor.

Por consiguiente, la tensión que mida va a ser igual si conectamos


dos resistencias en serie de valor 1k, dos de 220 ohmios o dos de
470k. ¿Por qué valor nos decantamos ahora? Recomendamos
optar por el valor más elevado del cual podamos disponer; para
reducir el consumo, no tiene sentido conectar, por ejemplo, dos
resistencias de un ohmio, lo que haría que el consumo fuese de 4,5
amperios. Si es para tomar una medida, vamos a buscar el mínimo
consumo posible; por ejemplo, con dos resistencias de 1k, con lo que
el consumo sería de 0,0045 amperios.

Ilustración 6.

Para comprobar que esto es así vamos a conectar el circuito anterior


y cargaremos el siguiente programa.

7
Ilustración 7.

Ya somos capaces de medir valores de tensión superiores al límite de


tensión de trabajo de un Arduino; esta operación la debemos realizar
con precaución, teniendo en cuenta los consumos e incluso es
recomendable asegurarse con un multímetro de la tensión de salida
del divisor de tensión, así como testear que la medida sea la correcta.

8
Ilustración 8.

Antes de avanzar, vamos a recordar cómo funcionaba un


potenciómetro.

Ilustración 9.

Podemos redefinir lo que es un potenciómetro ahora que sabemos lo


que es un divisor de tensión: un potenciómetro se asemeja a un
divisor de tensión variable.

Volviendo al caso que nos ocupa, para eliminar los rebotes de la


señal, vamos a utilizar un circuito en el cual integraremos un
condensador.

9
Condensador

Ilustración 10.

Un condensador, al igual que un diodo o una resistencia, es un


componente electrónico muy utilizado en electrónica. Existen
diferentes tipos de condensadores, nos centraremos en los
electrolíticos.

¿Para qué sirve un condensador? Un condensador permite


almacenar carga eléctrica; en concreto, almacena culombios.

1 culombio = 6,23 × 1018 electrones

1 culombio = 1 amperio*segundo

Si hacemos un símil, un condensador es como un tanque en el que


almacenamos agua (en este caso, electrones). Una vez que el
condensador está cargado totalmente, se puede comportar como una
pila, puesto que ofrece una diferencia de potencial y carga eléctrica.

Ilustración 11.

Un condensador se carga por una diferencia de potencial que se


aporte entre sus terminales.

10
Ilustración 12.

Los condensadores (electrolíticos), al igual que los diodos, disponen


de dos patillas: una es la patilla positiva y, la otra, la negativa (que
suele venir identificada por una franja de color blanco). Si tenemos
descargado el condensador (0 culombios), la tensión que medimos
entre sus terminales es de 0 V.

Ilustración 13.

Si pulsamos el botón, el condensador empezará a cargarse de


culombios y la tensión en los extremos del condensador irá creciendo
de forma exponencial hasta alcanzar el valor de la pila. Una vez que
el condensador alcance los 9 voltios, dejará de cargarse y mantendrá
esta carga durante un cierto tiempo. Si se conecta al condensador un
elemento como una resistencia o diodo, se descargará.

11
El hecho de que la carga del condensador sea en forma de curva nos
va a permitir filtrar señales. Antes de tratar este tema, vamos a
analizar los tiempos de carga y descarga de un condensador.

La unidad de medida de las resistencias son los ohmios, la de los


condensadores son los faradios. En una resistencia, los ohmios
determinan «una fuerza de oposición al paso de corriente»; los
faradios determinan la capacidad de un condensador de almacenar
carga eléctrica.

Faradio = Culombio / Voltio

O lo que es lo mismo:
C=Q/V

Cuando compramos un condensador, debemos tener en cuenta dos


parámetros: tensión y capacidad. Por ejemplo: un condensador de
100 faradios y 16 voltios. Estos dos valores delimitan la cantidad de
carga que puede almacenar el condensador.

Estos 16 voltios nos indican cuál es la tensión máxima que puede


soportar el condensador. Por lo tanto, en este caso, dentro de ese
margen [0-16], los faradios nos indican la carga que se almacena en
función del voltaje aplicado, por ejemplo, el mismo condensador
conectado a una carga de 10 voltios:

C = Q / V à Q = V*C

Q = 100*10 = 1000 culombios

En resumen, un condensador es un componente similar a una pila, en


el cual su comportamiento en el proceso de carga es de forma
exponencial (define una curva); los parámetros de interés de un
condensador son el voltaje máximo que puede soportar y su
capacidad.

12
Ilustración 14.

En la imagen anterior, observamos el problema que tenemos con los


rebotes. Lo que haremos será conectar un condensador para filtrar
esa señal, puesto que el condensador describe una curva a la hora de
cargarse y descargarse.

Ilustración 15.

Lo que conseguimos con el condensador es cambiar esa señal con


rebotes por una señal en forma de curva, pero estable. En
consecuencia, no tendremos diferentes lecturas ante un cambio de
nivel de una señal.

Por último, lo que nos queda por ver es el tiempo de carga y


descarga de un condensador que, a fin de cuentas, es lo que define
cuánto se prolonga esa curva; es decir, cuánto tiempo tardaría en
cambiar una señal de 5 voltios a 0 voltios(y viceversa).
Si tenemos un condensador cargado, para descargarlo, debemos
conectar, por ejemplo, una resistencia. El tiempo de descarga viene
definido por la siguiente fórmula.

Ilustración 16.

13
En la imagen anterior, apreciamos que, al pulsar el botón, empieza a
circular corriente para cargar el condensador y para que la resistencia
alcance el potencial de la pila.

Ilustración 17.

Una vez que el condensador está cargado (al mismo voltaje que la
pila), ya solo circula intensidad de la pila a la resistencia.

Ilustración 18.

En cuanto se deje de pulsar el botón, el condensador se empezará a


descargarse a través de la resistencia. ¿Cuánto tiempo tarda en
descargarse? Esto viene definido por la siguiente fórmula:

𝜏 = 𝐶 ∗ 𝑅𝑙
En la cual 𝜏 es el tiempo de descarga (en segundos), C es la
capacidad del condensador (en faradios) y Rl es el valor de la
resistencia de descarga (en ohmios). Por ejemplo, si el condensador
es de 100 faradios y la resistencia de 100 ohmios, el tiempo de
descarga es de 𝜏 = 100 ∗ 100 = 10000 𝑠𝑒𝑔𝑢𝑛𝑑𝑜𝑠.

14
No obstante, este tiempo no es el tiempo total, sino el tiempo que
tarda el condensador en descargarse un 63,2 %; por ejemplo, un
condensador de 100 faradios, cargado a 5 voltios, conectado a una
resistencia de descarga de 100 ohmios.

𝜏 = 𝐶 × 𝑅𝑙
La fórmula anterior nos define el tiempo que tarda el condensador en
descargarse un 63,2 %, lo que, en este caso, significaría alcanzar
1,94 voltios.

¿Cuánto tarda en descargarse totalmente? El tiempo que un


condensador tarda en descargarse totalmente es 5 veces el tiempo
de descarga.

𝑡𝑖𝑒𝑚𝑝𝑜 𝑡𝑜𝑡𝑎𝑙 = 5 × 𝜏 = 5 × (𝐶 ×𝑅𝑙)

Esto puede resultar extraño, pero matemáticamente se debe a que el


condensador describe un proceso exponencial de carga y descarga.

Ilustración 19.

!!
𝑉! = 𝑉! ! ∗!
×𝑒 !
!

𝑉! ! : tensión que tiene el condensador.


t: tiempo de descarga.
Rl: resistencia descarga.
C: capacidad del condensador.
e = 2,718.

15
Con la fórmula anterior, podemos saber cuánto tiempo tarda el
condensador en alcanzar una determinada tensión. Sin embargo, lo
que nos interesa es el tiempo que tarda en descargarse.

El proceso de carga se comporta de manera idéntica y viene definido


por las mismas ecuaciones.

Ilustración 20.

Ilustración 21.

El condensador alcanzará el 63,2 % de su carga transcurrido un


tiempo igual a la constante de tiempo (C*RL); estará cargado
totalmente en 5 veces ese tiempo.

Con todo esto, sabemos que un condensador elimina los rebotes de


una señal y podemos escogerlo en función de nuestras necesidades.
Con lo que hemos aprendido sobre condensadores y divisores de
tensión, estamos en disposición de resolver el problema de los
rebotes de una señal.

16
No obstante, antes de empezar, vamos a verificar todo esto con
nuestro Arduino. Primeramente, vamos a comprobar si el proceso de
carga es así.

Ilustración 22.

Para este caso, utilizaremos un condensador de 10 𝜇𝐹𝑎𝑟𝑎𝑑𝑖𝑜𝑠 y 16


voltios y una resistencia de 1𝐾Ω. Lo que haremos es muestrear los
tiempos de carga y descarga. Para ello, activaremos y
desactivaremos la salida 13 de Arduino.

La carga máxima del condensador será a 5 voltios; el tiempo que


tarde en alcanzar el 63,2 % de esta carga (3,06 voltios) es:

𝜏 = 𝐶 × 𝑅𝑙

𝜏 = 10!! ×1000 = 0,01 𝑠𝑒𝑔𝑢𝑛𝑑𝑜𝑠 = 10 𝑚𝑖𝑙𝑖𝑠𝑒𝑔𝑢𝑛𝑑𝑜𝑠

El tiempo total que tarda en cargarse es:

𝑇𝑖𝑒𝑚𝑝𝑜 𝑡𝑜𝑡𝑎𝑙 = 5×𝜏 = 𝐶 ∗ 𝑅𝑙 ×5


𝑇𝑖𝑒𝑚𝑝𝑜 𝑡𝑜𝑡𝑎𝑙 = 5×10 = 50 𝑚𝑖𝑙𝑖𝑠𝑒𝑔𝑢𝑛𝑑𝑜𝑠

Antes de continuar, necesitamos ampliar las instrucciones de gestión


de tiempo que conocemos.

17
Ampliación instrucciones de gestión de tiempo

millis();

Devuelve el número de milisegundos (ms) desde que la


placa Arduino ejecuta el programa actual. Este número se
reseteará aproximadamente después de 50 días.

Ilustración 23.

Ilustración 24.

Con esta instrucción, podemos saber aproximadamente lo que tarda


en ejecutarse nuestro programa, lo cual puede resultar interesante
para el control de ciertos procesos.

18
micros();

Devuelve el número de microsegundos desde que la placa


Arduino empezó a ejecutar el programa actual. Se restea
cada 70 minutos. Resolución de 4 microsegundos (el valor
retornado siempre es múltiplo de 4).

Ilustración 25.

Ilustración 26.

Si volvemos a nuestro caso, vamos a comprobar los tiempos de carga


y descarga para un condensador de 10 𝜇𝐹 y una resistencia de 1 KΩ.

19
Ilustración 27.

20
Ilustración 28.

Teóricamente, el condensador tardaría 10 milisegundos en alcanzar


un 63 % de su carga y 50 milisegundos en alcanzar un 100 %.
Analizamos que, teniendo en cuenta unos pequeños márgenes de
error, nos aproximamos bastante a estos valores.

Pull-Up debounce

Ilustración 29.

En el circuito anterior, conectamos un pulsador en forma Pull_up


«debounce»; es decir, vamos a eliminar los rebotes. Valores de los
componentes: RCARGA = 1 K Ω , RDESCARGA = 330 Ω , Condensador =
10 𝜇𝐹 (16 voltios).

21
Ilustración 30. Botón no pulsado

Consideramos que la pila es de 5 V para trabajar con el mismo valor


de tensión de operación que nuestro Arduino. Si el botón no se pulsa,
el condensador queda conectado a la pila a través de la resistencia y,
al cabo de un tiempo (5×𝜏), el condensador se carga totalmente.

Nos puede surgir la siguiente duda: ¿por qué el condensador, al


alcanzar aproximadamente 5 voltios, no continúa cargándose?

Ilustración 31.

22
Tal y como observamos en la imagen anterior, en el momento en el
que el condensador alcance 5 V, la diferencia de tensión de la
resistencia es de 0 V. Al ser 0 V, la intensidad que circula por la
resistencia es de 0 A; por lo tanto, el condensador dejaría de
cargarse.

Ilustración 32. Botón pulsado

En caso de pulsar el botón, la resistencia de carga se conecta al resto


del circuito. Organizaremos un poco mejor este circuito con el fin de
analizarlo.

Ilustración 33.

23
En el momento en el que se pulse el botón, el condensador se
empieza a descargar por la resistencia de descarga.

Ilustración 34.

A medida que el condensador se va descargando, la diferencia de


tensión de la resistencia de carga empieza a crecer. Por lo tanto,
empieza a circular corriente proveniente de la pila.

Ilustración 35.

24
Por último, debemos tener en cuenta que, por el modo en el que
quedan conectadas las resistencias, tenemos un divisor de tensión,
que hará que el condensador no se descargue del todo.

Ilustración 36.

𝑅!"#$%&'!()! = 𝑅!"#$" + 𝑅!"#$%&'%

𝑅!"#$%&'!()! = 1000 + 330 = 1330


𝑉 5
𝑉 =𝐼∗𝑅 →𝐼 = = = 0,00376 𝑎𝑚𝑝𝑒𝑟𝑖𝑜𝑠
𝑅 1330

Ilustración 37.

25
En consecuencia, el condensador se descargará hasta que alcance un
valor de 1,24 V .

Ilustración 38.

Ilustración 39. Pull_up debounce

Con este montaje, cargamos el siguiente programa y comprobamos si


nos encontramos rebotes en la señal.

26
Ilustración 40.

Ilustración 41.

Vamos a comprobar si, realmente, al pulsar el botón, el condensador


se descarga hasta 1,24 V, aproximadamente.

Ilustración 42.

27
Ilustración 43.

Ilustración 44.

La entrada analógica nos devuelve un valor de 242 y, si hacemos una


regla de tres:
1023-----------5 V
242------------x
x = 1,23 V

El comportamiento de la señal al pulsarse o no el botón es el


siguiente:

28
Ilustración 45.

Pull-Down debounce

Ilustración 46. Pull-Down debounce

29
Ilustración 47. Pull-Down debounce no pulsado

En caso de que el condensador se hubiese cargado anteriormente, se


descargaría por completo.

Ilustración 48. Pull-Down debounce pulsado

En caso de pulsarse el botón, el condensador se cargaría hasta los


5 V. Una vez alcanzados, se dejaría de cargar.

30
Ilustración 49. Proceso de carga

Ilustración 50. Condensador cargado

Ilustración 51.

31
Ilustración 52.

Ilustración 53.

Ilustración 54.

Con este último caso, acabamos de ver cómo eliminar por hardware
los rebotes de una señal. Tanto las resistencias como el condensador
se escogen en función de la atenuación de la señal que busquemos.

32
Solución por software (programación)

El «debounce» por software tiene la ventaja de no requerir


componentes adicionales. Se resuelve el rebote mediante un
programa. Se incrementa levemente el tiempo de ejecución y la
complejidad del código. Se comprueba el tiempo entre «disparos» de
la interrupción; si el tiempo es menor a un umbral, se descarta la
interrupción.

Ilustración 55.

Ilustración 56. Debounce por software

En función del botón, tendremos que ajustar el tiempo (150).

33
Memorias
En el nivel inicial, explicamos tres memorias existentes en el MCU del
Arduino UNO. Para recordar, disponemos de las siguientes memorias:

• FLASH: para almacenar el programa que debe ejecutar el


Arduino (los Arduinos vienen de fábrica con el gestor de
arranque cargado).

• SRAM: para trabajar con datos temporales.

• Memoria EEPROM: memoria permanente en la que se


almacenan los datos que se deseen que permanezcan grabados
una vez apagado el MCU, para así poder recuperarlos al
encender el MCU.

En este capítulo, nos centraremos en la memoria EEPROM y en el


módulo de tarjetas SD.

Memoria EEPROM

En el caso del Arduino UNO (ATmega328P), esta memoria tiene una


capacidad de 1 Kb, lo cual quiere decir que disponemos de una
«estantería» con 1024 apartados, que tienen una capacidad de 8 bits
(8 celdas en las que puede haber en cada una un 0 o 1). Al igual que
ocurre con las otras memorias del Arduino, podemos ampliar su
capacidad con memorias independientes conectadas al MCU mediante
algún protocolo de comunicación.

Con la memoria EEPROM debemos tener cuidado a la hora de


utilizarla, pues tenemos un número limitado de escrituras, después
de las cuales no operaría correctamente. En caso del Arduino UNO, la
limitación de la memoria EEPROM es de 100000 escrituras. La
memoria FLASH tiene la limitación de 10000 escrituras; la única que
no tiene limitación de escrituras es la memoria SRAM. Para operar
con la memoria EEPROM, necesitamos la librería correspondiente:

34
Ilustración 57. Incluir librería EEPROM

Ilustración 58.

EEPROM.read(parámetro);

Lee un byte de la posición especificada por parámetro; como


tiene 1024 posiciones, los valores con los que opera son del 0 a
1023.

35
Ilustración 59. Lectura de datos de una memoria EEPROM

Ilustración 60.

Las posiciones que no se hayan escrito nunca, por defecto, tienen


almacenado el valor 255.

EEPROM.write(parámetro1,parámetro2);

Escribimos, en la posición definida por el parámetro1, el valor


definido por el parámetro2. Este valor no puede superar el tamaño de
1 byte, lo que en números naturales equivale a un valor de entre 0 y
255 o, si hablamos de caracteres, 1 carácter.

36
Ilustración 61. «Limpiar» memoria EEPROM

Ilustración 62.

37
Vamos a tratar de almacenar en cada posición el valor de esta con
respecto al total; es decir, en la posición 50, almacenaremos el valor
50 o, en la posición 1023, el valor 1023.

Ilustración 63.

.
.
.
.

38
.
.
.

.
.
.
.

.
.
.
.
.

Ilustración 64.

Observamos que, al llegar al valor 255, se resetea y vuelve a


empezar la cuenta; esto se debe a que la instrucción
«EEPROM.write(,);» solo escribe datos de 1 byte de tamaño, lo que
significa que el valor máximo que puede almacenar es 255. Al
superar este valor, se resetea y vuelve empezar.

39
EEPROM.put(parámetro1,parámetro2);

Esta instrucción funciona igual que «EEPROM.write(,);». La


única diferencia es que permite almacenar valores que ocupen un
tamaño superior a 1 byte.

Ilustración 65.

Si cargamos este programa, vemos que se vuelve a repetir el mismo


problema.

.
.
.

40
.
.
.
.

.
.
.

.
.
.
.
.

Ilustración 66.

El conflicto radica en que la instrucción «EEPROM.read();» solo puede


leer un byte; por lo tanto, no solucionamos nada si escribimos datos
con un tamaño superior a un byte, pero luego solo leemos un byte.

41
EEPROM.get(parámetro1,parámetro2);

Funciona igual que «EEPROM.read();». Añade un segundo


parámetro que hace referencia a la variable en la que se escribe el
valor que se está leyendo. Es importante que esta variable concuerde
con el tipo de dato almacenado.

Ilustración 67.

Guardamos el valor 2000 en la posición 20 para luego cargar otro


programa que lea el valor de esa posición y nos lo devuelva por el
monitor serie.

Ilustración 68.

42
Ilustración 69.

Antes de continuar, debemos tener en cuenta que, si queremos


almacenar un valor que ocupe más de un byte, como es este caso, el
valor ocupa más de una posición en la memoria EEPROM.

Ilustración 70.

43
Por consiguiente, debemos considerar las posiciones que puede
ocupar una variable.

Ilustración 71.

Al cargar el programa anterior, se almacena incorrectamente el


primer valor.

Ilustración 72.

Ilustración 73.

Observamos que el valor 2000 no se almacena correctamente, pero si


el valor uno. Esto se debe a lo que ilustramos a continuación:

44
Ilustración 74.

En la imagen anterior, apreciamos con qué valores quedan,


finalmente, las posiciones 20 y 21.

Ilustración 75.

En resumen, debemos considerar el tamaño de los datos que


almacenamos en la memoria EEPROM para evitar errores de
sobrescribir sin ser nuestra intención.

45
Tarjetas SD

Para finalizar este capítulo, vamos a tratar el tema de trabajar con


tarjetas SD. Como ya aprendimos en el Tomo I del nivel inicial,
disponemos de Arduinos con el hardware específico para operar con
tarjetas SD y, como también ya sabemos, el hecho de que un Arduino
no incorpore una serie de periféricos no nos va a limitar su uso, ya
que podemos ampliar sus funciones con módulos.

Como el Arduino UNO no incorpora un periférico para tarjetas SD,


utilizaremos un módulo para ello. Disponemos de diferentes módulos
en función del tamaño (no capacidad) de las memorias.

Independientemente del módulo, las conexiones son las mismas,


puesto que utilizan un protocolo de comunicaciones (SPI), que
trataremos en su apartado correspondiente.

Ilustración 76. Modulo SD

Al lado de los pines del módulo, aparece una referencia para cada
pin. Al igual que sucede con los pines de nuestro Arduino, los
nombres de estos pines tienen que ver con el protocolo de
comunicaciones (SPI).

46
Ilustración 77. Arduino módulo SD

Pines SD Pines Arduino


GND GND
3,3V 3V3
- 5V
10 CS
11 MOSI
13 SCK
12 MISO
GND GND

Este módulo dispone de dos columnas de pines y ocho filas de pines;


los pines de una misma fila están unidos. Es importante conectar los
dos pines de GND para evitar posibles errores(interferencias). Por
otro lado, si alimentamos por el pin de 3,3 voltios, no es necesario
conectar el de 5 V.

47
En principio, debemos respetar la tabla de conexiones anterior,
aunque veremos que, según el programa, podemos realizar
modificaciones.
Una vez establecido el esquema anterior, debemos conectar nuestra
tarjeta SD, pero es necesario asegurarse de que el sistema de
ficheros de la tarjeta sea el adecuado (FAT16, FAT32). Para ello y,
con ayuda de nuestro PC, vamos a formatearlas.

Ilustración 78.

Si trabajamos con un MAC, no tenemos más que irnos a la utilidad de


discos, seleccionar la tarjeta y seleccionar el botón de borrar.

Ilustración 79.

Seleccionamos el formateo adecuado:

48
Ilustración 80.

Ilustración 81.

Además, antes de continuar, podemos renombrar la tarjeta. Al


finalizar el proceso, nos mostrará un mensaje como el de la imagen
anterior. Podemos encontrar por internet diferentes tutoriales de
cómo formatear una tarjeta SD con otros sistemas operativos.

Con nuestra tarjeta formateada y conectada al módulo, podemos


empezar a trabajar con archivos o tarjetas: creando, borrando o
modificando. Para trabajar con estos módulos, debemos utilizar una
nueva librería:

Ilustración 82.

49
Ilustración 83.

Esta librería aporta una serie de funciones, todas ellas con el


«prefijo» SD. Antes de continuar, debemos establecer las normas
para usar correctamente esta librería:

• Los ficheros deben tener una longitud máxima de 8 caracteres,


más 3 caracteres para su extensión.

• No se distingue entre mayúsculas y minúsculas, es decir, es lo


mismo «mifichero.txt» que «MIFICHERO.TXT».

SD.begin(parámetro);

Al igual que ocurría con el «Serial.begin();», que permitía


inicializar la comunicación serie, esta función permite inicializar la
comunicación con el módulo SD.

Si conectamos el pin CS del módulo al pin 10 del Arduino, no es


necesario definir ningún parámetro, puesto que, por defecto, se
utiliza este pin por el protocolo SPI. La función «SD.begin();»
devuelve dos valores: un 1 si se ha establecido correctamente la
comunicación y un 0 de no ser así.

50
Ilustración 84. Programa detección tarjeta

Ilustración 85.

Si no hemos cometido ningún error, deberíamos poder visualizar el


mensaje de la imagen anterior en nuestro Monitor Serie.

Ilustración 86.

Si reiniciamos nuestro Arduino (pulsando el botón), nos volvería a


mostrar el mensaje «Tarjeta detectada». ¿El «SD.begin()» detecta
que el módulo está conectado al Arduino o que también está
conectada la tarjeta? Detecta que la tarjeta está conectada; si
mantenemos el módulo conectado, retiramos la tarjeta y volvemos a
reiniciar el Arduino, nos mostrará el mensaje «Tarjeta no detectada».

51
Ilustración 87.

Por lo tanto, lo que detecta es que la tarjeta se encuentra conectada


y, por ende, que el módulo se encuentra correctamente conectado.

Ilustración 88.

Si, por el motivo que sea, no tenemos la posibilidad de conectar el


pin CS al pin 10 del Arduino y mantenemos el mismo programa, nos
mostrará el siguiente mensaje.

Ilustración 89.

52
Ilustración 90.

Debemos indicar por parámetro, en la función «SD.begin()», el pin al


que esté conectado CS. Ahora sí debería aparecer el mensaje
«Tarjeta detectada».

Ilustración 91.

Para entender mejor el programa, vamos a crear funciones que


realicen operaciones simples.

Ilustración 92.

53
La estructura de esta función se realizará en una ventana que
llamaremos «Funciones_sd», en la que, a partir de ahora, iremos
definiendo todas las funciones que creemos.

Ilustración 93.

Ilustración 94.

Ilustración 95.

Ilustración 96.

SD.mkdir(parámetro);

Permite crear una carpeta, con el nombre o ruta pasado por


parámetro. Si crea la carpeta, nos devuelve un 1; de no ser así, nos
devuelve un 0.

54
Para crear carpetas debemos seguir las siguientes reglas:

• Su nombre no debe superar los 8 caracteres.

• No debe haber espacios.

• Tampoco debe haber símbolos especiales.

Ilustración 97.

Ilustración 98.

55
Ilustración 99.

Aunque reiniciemos el Arduino y nos vuelva a decir que se ha creado


la carpeta, si esta ya existe, no creará una nueva. Vamos a reagrupar
código para dejar simplificada la parte principal del programa.

Ilustración 100.

56
Ilustración 101.

Podemos crear una ruta con carpetas; es decir, crear carpetas dentro
de otras.

Ilustración 102.

Ilustración 103.

Quizás encontremos un inconveniente en esta función, puesto que, si


queremos automatizar la creación de carpetas, según las
circunstancias, debemos saber si existe o no una carpeta para tomar
la decisión de crear otra con un nombre diferente.

57
SD.exists(parámetro);

Esta función comprueba si existe la carpeta o ruta que se pase


por parámetro. De existir, devuelve un 1 y, de no ser así, un 0.

Ilustración 104.

Como la ruta anterior la hemos creado antes de cargar este nuevo


programa, nos muestra el mensaje «Carpeta Existente».

Ilustración 105.

A continuación, probaremos con el nombre de una carpeta que


sabemos que no hemos creado.

Ilustración 106.

58
Ilustración 107.

Ilustración 108.

Con las funciones que hemos visto hasta ahora, podemos crear y
buscar carpetas. Por último, quizás necesitemos borrar carpetas.

SD.rmdir(parámetro);

Permite eliminar carpetas, siempre y cuando no tengan


archivos u otras carpetas dentro. Si elimina la carpeta pasada por
parámetro, devuelve un 1; de no ser así, un cero. Antes de continuar,
vamos a volver a organizar nuestro programa:

Ilustración 109.

59
Ilustración 110.

Ilustración 111.

60
La carpeta que indicamos por variable existe, por lo tanto, esto es lo
primero que nos va a indicar y, a continuación, la borramos y nos
muestra el mensaje correspondiente.

Ilustración 112.

Con el mismo programa cargado, si cerramos y abrimos el monitor


serie, nos muestra los siguientes mensajes:

Ilustración 113.

Al reiniciar el Arduino, nos indica que la carpeta no existe, puesto que


la hemos borrado con anterioridad. A continuación, vuelve a crear la
carpeta y se vuelve a eliminar.

Ejercicios propuestos:

1. Programa que permita crear carpetas con nombres que se


envíen por el puerto serie desde el monitor serie e indique si se
han creado.
2. Programa que permita buscar carpetas con nombres que se
envíen por el puerto serie desde el monitor serie e indique si
existen.
3. Programa que permita borrar carpetas con nombres que se
envíen por el puerto serie desde el monitor serie e indique si ha
sido posible borrarlas.

61
1. Programa que permita crear carpetas con nombres que
se envíen por el puerto serie desde el monitor serie e
indique si se han creado.

62
2. Programa que permita buscar carpetas con nombres
que se envíen por el puerto serie desde el monitor serie e
indique si existen.

63
3. Programa que permita borrar carpetas con nombres que
se envíen por el puerto serie desde el monitor serie e
indique si ha sido posible borrarlas.

64
Con las funciones anteriormente vistas, podemos realizar cualquier
tipo de operación con carpetas. Sin embargo, lo que realmente nos
interesa, a la hora de trabajar con una tarjeta SD, es crear archivos.

SD.open(parámetro1, parámetro2);

Permite «abrir» un fichero; «abrir» significa que puede ser leído


o modificado según el modo en el que se abra (lectura/escritura).
Para crear un fichero que no existe se debe de abrir en modo
escritura.

Como primer parámetro, se escribe el nombre del fichero o ruta en el


que se encuentra o queremos que se encuentre el fichero. El segundo
parámetro indica el modo en el que se abre el archivo (escritura
/lectura): FILE_READ o FILE_WRITE. Para trabajar con archivos es
necesario crear una instancia.

El nombre de la instancia debe cumplir las mismas reglas que los


nombres de variables. La función «SD.open(,)» devuelve un 1 si crea
correctamente el fichero o un 0 de lo contrario; este resultado debe
almacenarse en la instancia creada.

Ilustración 114.

65
Ilustración 115.

Podemos comprobar si se creó el archivo con nuestro PC.

Ilustración 116.

Si abrimos el archivo, nos encontramos con que está vacío.

Ilustración 117.

Para crear un archivo dentro de una carpeta o carpetas debemos


crear esa carpeta o carpetas antes de crear el archivo.

66
Ilustración 118.

Ilustración 119.

Ilustración 120.

Si, por el contrario, tratamos de crear un archivo en una ruta no


existente, no se creará.

67
Ilustración 121.

En el programa anterior, cambiamos la ruta en la que creamos el


fichero, por una no existente: «ficha/test.txt».

Ilustración 122.

SD.remove(parámetro);

Elimina un fichero de la tarjeta SD, utiliza un único parámetro


para indicar qué archivo (o ruta del archivo) debe borrar.

68
Ilustración 123.

Ilustración 124.

Si pulsamos el botón de reinicio del Arduino, por el monitor serie


veremos dos mensajes más:

Ilustración 125.

Al tratar de volver a borrar el archivo, nos indicará que ya no existe,


ya que lo hemos borrado anteriormente.

69
Con carpetas para poder agrupar archivos y sabiendo crear archivos,
nos encontramos en disposición de almacenar datos en esos archivos.

Nombre_Instancia.print();

Una vez que tenemos el archivo abierto, la función anterior


empieza por el nombre de la instancia que hemos creado (en los
casos anteriores nombrada «archivo»). Si, por ejemplo, creamos un
archivo de texto, esta función opera de forma idéntica a
«Serial.print()». Lo que hace es escribir el texto pasado por
parámetro después del último texto (si lo hubiera) escrito sin realizar
saltos de línea.

Nombre_Instancia.println();

Opera de forma similar a la función anterior; la diferencia es


que añade un salto de línea al finalizar la escritura del texto.

Nombre_Instancia.close();

Cierra el fichero abierto, pero antes, se asegura de que se haya


escrito el último mensaje en la tarjeta SD. Por lo tanto, lo
utilizaremos siempre después de cada escritura para comprobar que
se ha escrito correctamente.

70
Ilustración 126.

Ilustración 127.

Si comprobamos en nuestro PC, los datos están escritos en la tarjeta


SD:

Ilustración 128.

Por último, podemos leer los valores almacenados en algún archivo.


Para ello, necesitamos funciones diferentes a las usadas
anteriormente.

Nombre_Instancia.available();

Permite comprobar si el archivo abierto tiene datos y cuántos le


quedan por leer. Funciona igual, en caso de la comunicación serie,
que el «Serial.available();».

71
Nombre_Instancia.read();

Siguiendo con la comparativa de la comunicación serie, para


recuperar los datos del buffer de entrada, teníamos la función
«Serial.read();», que leía byte a byte o, lo que es lo mismo, carácter
a carácter. Esta función hace lo mismo, pero con los datos de un
fichero.

Con estas dos nuevas funciones, podemos tratar de recuperar los


datos escritos en el caso anterior. Antes de continuar, es importante
resaltar que el fichero ya no debe abrirse como escritura, sino como
lectura: «SD.open(“test.txt,FILE_READ”);».

Ilustración 129.

72
Ilustración 130.

Ejercicios propuestos:

4. Programa que permita crear un fichero con cada encendido,


para luego escribir en él un chat entre dos Arduinos.

5. Otro programa para recuperar el archivo anterior y mostrar los


mensajes enviados entre dos Arduinos.

73
4. Programa que permita crear un fichero con cada
encendido, para luego escribir en él un chat entre dos
Arduinos.

74
75
5. Otro programa para recuperar el archivo anterior y
mostrar los mensajes enviados entre dos Arduinos.

76
Ampliación entradas analógicas

Pin AREF

Ofrece un voltaje de referencia para poder adaptar la resolución de


las entradas analógicas. El valor del voltaje por defecto es de
5 voltios, lo que, unido al hecho de que las entradas analógicas
tienen una resolución de 10 bits, nos permite cuantificar incrementos
de tensión de 5 mV (5/1023).

En el Arduino UNO no podemos variar la resolución, pero si adaptarla.


Si, por ejemplo, tenemos una señal analógica que tenemos que leer y
sabemos que, en ningún caso, superará los 3 voltios, sí podemos
adaptar nuestra resolución a un rango de entre 0 y 3 voltios,
conseguimos detectar incrementos más pequeños (3/1023 = 3 mV,
aprox.).

analogReference(parámetro);

Con esta función, podemos configurar la tensión de referencia


de las entradas analógicas. El parámetro permite tres posibles
valores:

• DEFAULT: configura todas las entradas analógicas entre un


rango de 0 a 5 voltios. Si no hemos cambiado la referencia de
las entradas, no tendría sentido, puesto que este estado es en
el cual se encontraría por defecto.

• INTERNAL: voltaje de referencia 1,1 voltios; por lo tanto, las


entradas analógicas no pueden superar esta tensión y la
resolución sería de: 1,1/1023 = 1 mV, aprox.

• EXTERNAL: permite referenciar las entradas en función de la


tensión del pin AREF (tensión que debe estar entre 0 y
5 voltios). Por lo tanto, si a la entrada del pin AREF conectamos
una señal de 2,5 voltios y configuramos el
«analogReference(EXTERNAL);», los valores de las entradas
analógicas deben encontrarse entre 0 y 2,5 voltios, con una
resolución de: 2,5/1023 = 2,5 mV, aprox.

77
Ilustración 131.

Con el circuito anterior, podemos variar la tensión de referencia del


pin AREF. No obstante, esto no basta y debemos configurar por
programa que se tenga en cuenta esa tensión.

Ilustración 132.

Con el programa anterior, configuramos la referencia de las entradas


analógicas en función del valor de la tensión del pin AREF. Antes de
continuar, es necesario resaltar que no cambiamos la resolución, sino
que lo que hacemos es adaptar esa resolución a un determinado
rango de tensiones.

analogReadResolution(parámetro);

Esta función sí que permite definir la resolución de las entradas


analógicas, pero, en el caso del Arduino UNO, no podemos modificar
la resolución.

78
analogWriteResolution(parámetro);

Modifica la resolución de las señales PWM (por defecto, 8 bits),


pero, al igual que el caso anterior, no es posible modificarlo en el
Arduino UNO.

Pin IOREF
Este pin se utiliza para indicar a placas supletorias a qué tensión
opera nuestra placa.

Señales PPM
Este tipo de señales se basan en las señales PWM, que permitían
regular los tiempos de activación de las señales. Las señales PWM
permitían «codificar» el valor de una señal analógica a una señal
digital.

Ilustración 133.

El problema con este tipo de señales aparece cuando tenemos que


«codificar» varias señales analógicas, puesto que, por cada una,
debemos generar su correspondiente señal PWM.

79
Ilustración 134.

En ciertos casos, esta solución no será la más favorable. Sin


embargo, tenemos la opción de usar señales PPM; estas señales son
muy comunes en sistemas de radiocontrol.

Ilustración 135.

De lo que se trata es de que, por una salida, se envíe toda la


información. No obstante, como se aprecia en la imagen anterior, si
tratamos se enviar todas las señales PWM por una misma línea, se
formaría una confusión, puesto que el receptor no tendría forma de
saber qué tramo de la señal se corresponde con la señal analógica
asociada.

De alguna forma, debemos indicarle qué parte de la señal que recibe


se corresponde con una determinada entrada analógica. Esto lo
conseguimos con: señal de sincronización, canales y tiempo entre
canales.

80
Antes de continuar con las señales PWM, vamos a trabajar con
joysticks.

Ilustración 136.

Los módulos más comunes que nos encontramos en el mercado


disponen de cinco pines:

• 5v: alimentación

• X: Desplazamiento joystick en el eje X

• Y: Desplazamiento joystick en el eje Y

• Button: estado botón.

• GND: alimentación.

81
Ilustración 137.

Internamente, un joystick analógico no está formado por más que


dos potenciómetros asociados al desplazamiento de este en sus dos
ejes y un pulsador(puesto que podemos realizar pulsaciones con el
joystick); estos tres componentes comparten alimentación.

Ilustración 138.

82
Ilustración 139.

Ilustración 140.

Si el joystick se encuentra en la posición media, los valores de las


lecturas de las entradas analógicas rondan el valor 512 y, al mover el
joystick, deben variar entre 0 y 1023. Una vez que sabemos la
posición en cada eje del joystick, debemos regular los leds de otro
Arduino de la siguiente forma:

83
Ilustración 141.

Los leds se montan en rombo o, lo que es lo mismo, marcan los


extremos de cada uno de los ejes. Por lo tanto, en función de cómo
se desplace el joystick, los leds se encenderán de la siguiente
manera:

• Desplazamiento en el eje X hacia la izquierda: solo se


encenderá el led azul y, cuanto más al extremo llegue, más se
iluminará.

• Desplazamiento en el eje X hacia la derecha: solo se encenderá


el led amarillo y, cuanto más al extremo llegue, más se
iluminará.

• Desplazamiento en el eje Y hacia arriba: solo se encenderá el


led blanco y, cuanto más al extremo llegue, más se iluminará.

• Desplazamiento en el eje Y hacia abajo solo se encenderá el led


rojo y, cuanto más al extremo llegue, más se iluminará.

• Desplazamiento en diagonal hacia arriba e izquierda: se


encenderán, proporcionalmente, los leds azul y blanco.

84
• Desplazamiento en diagonal hacia arriba y derecha: se
encenderán, proporcionalmente, los leds amarillo y blanco.

• Desplazamiento en diagonal hacia abajo y derecha: se


encenderán, proporcionalmente, los leds amarillo rojo.

• Desplazamiento en diagonal hacia abajo e izquierda: se


encenderán, proporcionalmente, los leds rojo y azul.

En consecuencia, lo que se pretende es que, en función de cómo se


enciendan los leds, conozcamos en qué posición se debe encontrar el
joystick del otro Arduino.

Ilustración 142.

Se trata de algo similar a lo que ocurre con los dispositivos de


radiocontrol que se utilizan en drones. En radiocontrol, disponemos
de un dispositivo que toma la lectura de todos los controles del
mando (botones, deslizadores, joysticks...) y genera una señal que se
envía por radiofrecuencia al receptor del dron, que debe procesarla.

85
De las señales PPM, debemos entender un par de conceptos; el
primero de ellos se refiere a los canales.

Ilustración 143.

El concepto de canal es un término extendido en radiocontrol, pero


no debemos confundirlo con que cada canal se genere por una salida
diferente. La señal se envía por una única salida; en nuestro caso,
enviamos dos canales, puesto que vamos a enviar la posición del
joystick del eje X en un canal y la posición del eje Y por otro canal.

Por consiguiente, un canal aporta la información referente a un


elemento de control de una estación de mando.

Ilustración 144.

Si volvemos al planteamiento inicial, la señal que estamos emitiendo


se genera por la misma salida y está formada por tres canales, cada
uno con información referente a cada entrada analógica.

86
Sean los canales dos, tres, cuatro, etc., el problema radica en que el
receptor, de la forma en la que enviamos la señal, no sabe distinguir
cuál es cuál. Por lo tanto, vamos a utilizar una señal de
sincronización:

Ilustración 145.

Las señales se sincronización se envían antes que los canales; por lo


tanto, es una señal que nos permite diferenciar cuál es el primer
canal, puesto que, a continuación de esta, siempre va a venir el
primero.

A continuación, desempeñan un papel importante los tiempos de la


señal de sincronismo y de los canales. ¿Cómo hacemos para
encriptar la información de un canal? Al igual que las señales
PWM, modificamos los tiempos de cada uno de los dos estados
(activado o desactivado), con una pequeña diferencia: vamos a fijar
un tiempo mínimo de duración.

87
Ilustración 146.

Fijaremos un tiempo mínimo para cada canal de 1000 𝜇segundos y


un tiempo variable de 1023, que se corresponderá con el valor de
lectura de la entrada analógica.

Una vez determinados estos tiempos, debemos establecer el tiempo


de la señal de sincronismo. Para que sea inequívoca, la haremos muy
superior al tiempo máximo de cada canal (2023), por ejemplo, de
4000 𝜇segundos.

Ilustración 147.

De esta forma, cuando el receptor mida los tiempos de la PPM, no


habrá confusión sobre si está recibiendo la señal de sincronismo o los
canales.

88
Por último, es necesario establecer unos tiempos de separación entre
cada una de las señales que intervienen en la PPM; en nuestro caso,
pondremos un tiempo de 300 𝜇segundos.

Ilustración 148.

Los tiempos entre canales y señal de sincronismo se realizan con el


estado de la señal a nivel bajo (desactivado). A modo de resumen
visual, la señal que se genera será de la siguiente forma:

Ilustración 149.

Tanto el emisor como el receptor deben seguir todas las reglas que
hemos establecido; el emisor es el encargado de tomar la lectura de
las señales generadas por el joystick y encriptar esa información en
una señal PPM.

89
Esa señal PPM será recibida y procesada por el receptor. El procesar
la señal, en este caso, consiste en saber distinguir los canales y la
información que aporta cada uno de ellos, para luego actuar,
proporcionalmente, sobre los leds.

Ilustración 150.

Con el programa anterior, vamos a comprobar cuánto tardamos en


calcular los tiempos de cada canal, para contabilizarlos en la señal de
sincronismo.

Ilustración 151.

Aproximadamente, se tarda 224 𝜇segundos en calcular los tiempos


de cada canal. Por lo tanto, en vez de fijar el tiempo en 4000, lo
haremos en 3776 𝜇segundos .

90
Ilustración 152.

Con esta pequeño programa, estamos generando continuamente la


señal de sincronismo.

Ilustración 153.

91
Ilustración 154.

Ilustración 155.

92
Ilustración 156.

Con el emisor de la señal PPM resuelto, ya podemos empezar a


trabajar con la programación del emisor. El emisor tendrá que ser
capaz de cuantificar los tiempos de la señal PPM para diferenciar
señal de sincronismo y canales (atendiendo al tramo relevante del
canal).

pulseIn(parámetro1,parámetro2,parámetro3);

Esta función permite cuantificar los tiempos de una señal,


atendiendo a una serie de características que se definen en función
de los parámetros de la función:

• Parámetro1: indica por qué pin se va a recibir la señal, para


que la función cuantifique los tiempos.

93
• Parámetro2: el estado de la señal que interesa cuantificar; en
nuestro caso, el tiempo que permanece la señal a nivel alto
(HIGH).

• Parámetro3: este parámetro fuerza un tiempo de espera, tras


el cual, si la señal no alcanzó el nivel fijado como segundo
parámetro, la ejecución del programa continua y la función
devuelve un 0.

Por ejemplo:

pulseIn(13,HIGH,8000);

Con los parámetros anteriores, configuramos la función para que,


cuando se ejecute el programa, se detenga a la espera de que, por el
pin 13, se reciba una señal a nivel alto. Si no se recibe una señal a
nivel alto en el transcurso de 8000 𝜇segundos, el programa continúa
con su ejecución y la función devuelve un 0.

Si, por el contrario, antes de acabar el tiempo se recibe una señal a


nivel alto, se empieza a contabilizar el tiempo hasta que cambie de
estado, devolviendo el tiempo en 𝜇segundos. Lo recomendable es
usar esta función para tiempos entre 10 𝜇segundos y 3 minutos.

Disponemos de más formas para muestrear los tiempos de la señal


PPM, ayudándonos de «digitalRead()» y funciones como «micros(),»
o, incluso, con interrupciones, pero quizás, la forma más sencilla sea
usando «pulseIn(,,);».

94
Ilustración 157.

Las variables de «tiempo_sincronización», «canal_uno» y


«canal_dos» sirven para almacenar los tiempos muestreados de cada
parte de la señal PPM. La variable PPM nos va a permitir saber, en
cada momento, qué parte de la PPM tendríamos que recibir (lo que
nos va a permitir sincronizar el muestreo).

Ilustración 158.

95
Ilustración 159.

Nos moveremos dentro de la estructura switch en función de la parte


de la señal PPM que debamos muestrear; eliminamos los break para
no salir de la estructura y atender lo más rápido posible al siguiente
canal.

Fijamos unos márgenes de error, la señal de sincronismo se


considerará válida si se supera un valor de 3700 y, cada canal, si se
encuentra entre unos valores de 950 y 2200.

Con este programa, disponemos de los tiempos de cada canal para


actualizar el valor de los leds. Por lo tanto, si llegamos al último case
y la señal del canal es la adecuada, definiremos una función para
actualizar el estado de los leds.

96
Ilustración 160.

Lo primero que vamos hacer es crear unos márgenes entre los cuales
no vamos a encender los leds, puesto que el punto medio del joystick
puede ser crítico si existen pequeñas variaciones.

Ilustración 161.

97
Ilustración 162

98

You might also like