El lenguaje de programación, aparte de las instrucciones ya conocidas como <<delay()>> y <<delayMicrosenconds()>>, dispone de otras para la gestión de tiempo.
•millis()
•Esta instrucción no necesita parámetros y nos devuelve el tiempo que lleva encendido el Arduino; este tiempo viene expresado en milisegundos.
•Nos devuelve un valor tipo unsigned long, lo que quiere decir que el valor devuelto varía entre 0 y 4.294.967.295.
•Si hacemos la operación: 4.294.967.295 / (1000 * 60 * 60 * 24), el resultado aproximado será de 50 días.
•Por lo tanto, esta instrucción al cabo de 50 días se resetea (su valor interno) y vuelve a contar desde cero.
•micros()
•Esta instrucción no necesita parámetros y nos devuelve el tiempo que lleva encendido el Arduino; este tiempo viene expresado en microsegundos.
•Nos devuelve un valor de tipo unsigned long, lo que quiere decir que el valor devuelto varía entre 0 y 4.294.967.295.
•Si hacemos la operación: 4.294.967.295 / (1000000 * 60) el resultado aproximado será de 70 minutos.
•Por lo tanto, esta instrucción al cabo de 70 minutos se resetea (su valor interno) y vuelve a contar desde cero.
Nos ayudaremos de las instrucciones anteriores para comprobar las reglas que fijan el comportamiento de un condensador en el proceso de carga; de lo que se trata es de comprobar el tiempo que tarda el condensador en alcanzar los valores clave, que son los de la carga al 63,2 % y al 100 %.
1.Para medir los tiempos, es necesario muestrear el valor del condensador; esto se hará con la entrada A1 .
2.Según las ecuaciones vistas en el capítulo anterior y utilizando los mismos valores de resistencia y condensador, este alcanza el 63,2 % de la carga en 1 segundo y el 100 % en 5 segundos.
3.En el programa pondremos una pequeña tolerancia, de 0,05 V (10); por eso se considera que, cuando el condensador alcanza 4,95 (1013), se ha cargado totalmente y, con 0,05 V, está totalmente descargado.
4.El valor del 63,2 % de la carga lo alcanza cuando llega a un valor de tensión de 3,16 V cuyo valor de lectura equivale a 646.
5.Cuando visualizamos el resultado, vemos que se aproxima al resultado esperado y las pequeñas desviaciones que encontramos son normales.
En el capítulo anterior estudiamos cómo se comportaba un condensador en el proceso de carga y, en este capítulo, estudiaremos el proceso de descarga. Un condensador, cuando se descarga, se comporta del mismo modo que cuando se carga, es decir, se cumplen las mismas leyes y utilizaremos las mismas ecuaciones matemáticas, pero, antes de ver esto, tenemos una ecuación más compleja que las vistas anteriormente con la cual podemos obtener el valor de carga del condensador para cada instante o, lo que es lo mismo, saber en qué momento alcanzará un determinado valor de tensión.
Como el proceso de descarga de un condensador es similar al de carga, rigen las mismas ecuaciones, pero definirán un comportamiento inverso:
•Seguimos manteniendo el margen de tolerancia (0,05 - 4,95 V).
•El condensador tardará R * C segundos en descargarse un 63,2 % de su carga o, lo que es lo mismo que se descarga hasta el 36,8 %.
•Ese 36,8 % supone que el condensador tiene una carga de 1,84 V, que, si lo traducimos a la lectura de un Arduino, es un valor de 376.
•Teniendo esto en cuenta, cargamos el siguiente programa y analizaremos los resultados.
A vista de los resultados, podemos comprobar que el comportamiento del condensador, cuando se carga y cuando se descarga, es el mismo y se rige por los mismos principios. Con este capítulo, ya hemos visto el funcionamiento básico de un condensador.
En el siguiente capítulo veremos un uso práctico de un condensador, el cual nos va a permitir solucionar el problema que tenemos con los rebotes de las señales ya que, como los condensadores tienen un proceso no lineal de carga y descarga, aprovecharemos eso para diseñar un circuito que filtre estos rebotes. Los condensadores tienen diversas aplicaciones, como pueden ser:
•Baterías
•Filtros
•Energía solar
•Memorias
•Osciladores
•Compensación del factor de potencia
•…
Si analizamos el hardware del Arduino UNO , encontramos varios condensadores que se utilizan en la etapa de potencia; para evitar que la alimentación se corte bruscamente, se utilizan condensadores.
El término debounce significa rebotar en inglés y es el problema con el que partimos en este libro: la eliminación de los rebotes en una señal; para ello, tenemos dos posibles soluciones: por hardware o por software. Cada una tendrá sus ventajas e inconvenientes que, en función de las circunstancias, nos hará decantarnos por uno u otra.
Empecemos por la solución por hardware; como ya sabemos, tenemos varias opciones a la hora de conectar un pulsador: Pull-Up y Pull-Down. Solución antirrebote pulsador Pull-Down:
1.Conectamos el siguiente circuito .
2.El circuito estará formado por dos resistencias en serie y un condensador en paralelo con una de ellas .
3.Es importante el valor de cada componente y el criterio de conexión.
4.Si dejamos a un lado el condensador, vemos que lo que tenemos es un divisor de tensión con dos resistencias; en el primer tomo, ya vimos cómo solucionar este tipo de circuito.
5.Atendiendo a los valores de las resistencias, en el momento en el que se pulse el botón en la primera resistencia, la caída de tensión será de 1,24 V y, en la segunda 3,76 V.
6.Si recordamos los umbrales de tensión para los niveles lógicos de estado alto (> 60% de la tensión de trabajo 3 V) y estado bajo(<30 % de la tensión de trabajo 1,5 V), podemos estar seguros de que, como el condensador se encuentra en paralelo con la segunda resistencia y se cargue hasta los 3,75 V, la lectura de esta entrada será a nivel alto.
7.Por lo tanto como al condensador le llevará un tiempo cargarse eliminará de la señal los rebotes que produce el pulsador.
8.En el momento en el que dejemos de pulsar, el condensador se empezará a descargar por la segunda resistencia hasta alcanzar los 0 V.
9.Con este circuito montado, podemos cargar el siguiente programa y comprobar que el contador ahora sí contará adecuadamente el número de pulsaciones.
10. Lo que sí que podemos apreciar es que, si pulsamos muy rápido, no se llegarán a contabilizar todas las pulsaciones: eso es por el tamaño del circuito RC, que hemos diseñado para filtrar las pulsaciones entre las que no ha transcurrido un tiempo demasiado elevado.
11. Si, en la práctica, vemos que su funcionamiento no se ajusta a las características del sistema, no tenemos más que rediseñar el circuito RC.
12. En nuestro caso, los pulsadores que se suelen utilizar en las protoboards suelen tener unos rebotes de unos 150 ms de duración; con el circuito que hemos realizado, debería ser suficiente como para eliminar esos rebotes.
13. En caso de rediseñar el circuito para que la respuesta del filtro sea más rápida o más lenta, se tiene que mantener la relación entre las resistencias que forman el divisor de tensión.
En el caso del pulsador Pull-Up, debemos hacer una serie de cambios en el circuito: primero en relación con las resistencias y, en segundo lugar, mover el condensador, tal y como se ve en el esquema .
•Como el comportamiento del pulsador Pull-Up es inverso al Pull-Down, tanto el circuito como el programa se «invertirán».
•En este caso , cuando no se pulsa el botón, la tensión de lectura será de 5 V y, en el momento en el que se pulse, bajará hasta 1,24 V ya que el condensador se quedaría conectado en paralelo con la segunda resistencia.
Vistas las soluciones por hardware, pasemos a la solución por software. La solución por software consistirá en medir el tiempo que transcurre entre diferentes flancos de subida (ya que se utilizará un pulsador Pull-Up) despreciando las que, por tiempo, se puedan considerar un rebote.
1.Conectamos un circuito con un pulsador Pull-Up .
2.Con este programa , primero comprobamos si hay un flanco de subida, de ser así, comprobamos si, al menos, han transcurrido 150 ms desde el último flanco; de ser así, se contabiliza como pulsación y, en caso contrario, no se tendrá en cuenta.
3.En el caso de que el pulsador fuese Pull-Down, solo habría que controlar flancos de bajada.
Conclusiones:
•La solución por hardware simplifica la programación, pero implicaría la conexión de más componentes.
•Por otro lado, la solución por software consume más recursos de programa, pero es más fácil de adaptar a cada posible caso ya que, simplemente, se cambia un valor en el programa.
•Posiblemente la solución por software, en este caso, resulte más conveniente.
Otro uso que podemos darle a un condensador es convertir una señal PWM en una señal analógica. Con una señal PWM, podemos regular infinidad de procesos, pero habrá ciertos casos en los que no será adecuado utilizar una señal PWM:
•A la hora de regular componentes cuya tensión de trabajo sea inferior a 5V .
•Cuando sea necesario disponer de un nivel de tensión constante diferente a 3,3 o 5V.
•Para general señales que serán leídas por entradas analógicas.
Para estos casos, podemos utilizar un filtro de paso bajo para modular la señal PWM y convertirla en una salida analógica. Existen diferentes tipos de filtros que, en resumen, se diferencian por el rango de frecuencias con el que trabajan. Para el caso que nos atañe, basta con decir que el filtro de paso bajo será el adecuado para este tipo de aplicación, ya que no es objeto del libro profundizar en este tema.
1.Conectamos una resistencia de 4,7 kΩ y un condensador de 10 μF(35 V) en serie .
2.Este circuito se conectará a una salida PWM.
3.La señal analógica la obtendremos del polo positivo del condensador, así que lo conectamos a una entrada analógica para comprobar que realmente se genera una salida analógica.
4.Por último, conectamos un potenciómetro, el cual se utilizará como valor de consigna y se comparará si se consigue el mismo nivel de tensión en el filtro que en la salida del potenciómetro.
5.Programamos el Arduino para que genere una salida PWM proporcional al valor del potenciómetro y muestre los valores tanto del potenciómetro como del filtro, para así poder compararlos.
6.Con ayuda de la herramienta Serial Plotter, podemos graficar los valores .
Conclusiones:
•Si analizamos la gráfica, encontramos que la señal analógica (azul) se asemeja a la entrada analógica (roja). Resulta normal que la señal no sea exactamente igual, ya que hay que tener en cuenta que el condensador está continuamente cargándose y descargándose.
•Esta señal que acabamos de graficar tiene factores importantes que definen su comportamiento, el tiempo de respuesta (lo que tarda en alcanzar el valor de consigna) y el rizado (esa oscilación que apreciamos en la gráfica). Estos factores están relacionados ya que, si queremos reducir el rizado, aumentará el tiempo de respuesta y viceversa.
•También es importante el dimensionamiento del filtro, para lo cual podemos buscar calculadoras online que nos permitan afinar el diseño.
•La aplicación que se le puede dar a esta solución no sería efectiva si pretendemos conectar una carga, ya que esto influye en el comportamiento del filtro; para ello, lo adecuado sería utilizar, además del filtro, un amplificador operacional.
Las interrupciones son un tipo de «mecanismo» que interrumpe la ejecución normal de un programa para atender un evento. Los eventos que puede atender un Arduino son:
•Evento por señal (evento externo)
•Tiempo
•Comunicación
Las interrupciones son similares a una función; la diferencia estriba en que las funciones que hemos programado tenían que ser «llamadas» desde una parte del programa. Una interrupción no es más que una función que se ejecutará, no por ser «llamada», sino porque se configura para que, ante un evento se ejecute.
El primer tipo de interrupciones que estudiaremos son las interrupciones ante un evento externo. Estas interrupciones están pensadas para dar una respuesta rápida ante un evento crítico o relevante.
Como se mencionaba al principio, las interrupciones son funciones que se ejecutan por haber sido previamente configuradas; por lo tanto, es por donde tenemos que empezar.
1.Este tipo de interrupciones necesitan configurar tres parámetros:
•El pin en donde se puede producir el evento.
•El nombre de la función que se ha de ejecutar si ocurre el evento.
•El tipo de evento que ha de ocurrir.
2.Por ejemplo, un evento crítico puede ser una parada de emergencia. Para que se ejecute una interrupción por evento externo, tenemos que tener una señal que nos indique que ha ocurrido algo, como es lógico, esa señal se ha de conectar a uno de los pines del Arduino, con lo que tenemos que indicar por programación en qué pin está conectada esa señal.
3.Como lo que, al fin y al cabo, lo que se va a ejecutar es una función, será necesario indicar el nombre de la función que se ha de ejecutar.
4.El último parámetro es el tipo de evento, cuando recibimos una señal tenemos que saber que comportamiento de esta nos indica que hay que atender a un evento: señal a nivel alto, a nivel bajo, flanco de subida…
5.La instrucción <<attachInterrupt(,,)>> permite programar interrupciones por evento externo; necesita los tres parámetros que se han mencionado en el primer punto (siguiendo el mismo orden).
Antes de continuar, hay que revisar ciertas peculiaridades de este tipo de interrupciones:
•No se pueden atender eventos en todos los pines: en el caso del Arduino UNO, disponemos de dos pines que pueden atender eventos por interrupción y son los pines 2 y 3 que en el parámetro correspondiente de la función anterior, se configurarán como 0 y 1, respectivamente.
•El nombre de la función que programamos tiene que respetar las mismas reglas que cualquier otra función.
•Tenemos una serie de palabras reservadas para indicar el tipo de interrupción:
•LOW: si configuramos la interrupción con este parámetro, se ejecutará la función cuando el pin reciba una señal a nivel bajo.
•CHANGE: si configuramos la interrupción con este parámetro, se ejecutará la función cuando el pin cambie de estado, tanto si es de 0 a 5 voltios (flanco de subida) como de 5 a 0 voltios (flanco de bajada).
•RISING: si configuramos la interrupción con este parámetro, se ejecutará la función cuando el pin cambie de 0 a 5 voltios (flanco de subida).
•FALLING: si configuramos la interrupción con este parámetro, se ejecutará la función cuando el pin cambie de 5 a 0 voltios (flanco de bajada).
•HIGH: si configuramos la interrupción con este parámetro, se ejecutará la función cuando el pin reciba una señal a nivel alto. No obstante, el Arduino UNO no dispone de este tipo de evento.
La programación de este tipo de interrupciones es fácil de probar: con el siguiente montaje , programaremos el Arduino para que, cada vez que haya una pulsación, se contabilice y se muestre el valor total de pulsaciones por el monitor serie
.
Si, como en el caso del capítulo anterior, utilizamos variables que pueden ser modificadas dentro de la interrupción, estas deben tener una declaración especial, que no es más que empezar por la palabra reservada <<volatile>> y, a continuación, el tipo de variable, el nombre de esta y, si fuese necesario, se le asigna un valor .
En el caso del anterior, también tenemos que tener presente que se pueden dar rebotes en la señal, por lo que tenemos que solucionarlo por hardware o software , tal y como hemos visto en capítulos anteriores.
Las interrupciones están pensadas para dar una respuesta rápida, en ellas se deben programar unas pocas líneas o, en todo caso, que el tiempo de ejecución sea corto. Es más, dentro de una interrupción no funcionan los delay; como decía, el código que se programe se ha de ejecutar rápidamente.
Disponemos de más instrucciones para trabajar con este tipo de interrupciones:
•noInterrupts() desactiva cualquier interrupción del programa.
•Interrupts() para los casos en los que se han deshabilitado las interrupciones con la instrucción anterior y queremos que se vuelvan a activar.
•detachInterrupt() desactiva la interrupción indicada: detachInterrupt ( interrupción )
•detachInterrupt (digitalPinToInterrupt (pin));
•detachInterrupt (pin) (solo Arduino DUE y CERO).
Lo que tenemos que tener presente es que, si se ejecuta una interrupción, el programa «saltará» desde la línea de código en donde se encuentre, ejecutará las líneas de código que haya dentro de la función de la interrupción y volverá a la línea del programa desde donde ha saltado y continuará con la ejecución normal del programa.
¿Qué ocurre si se da la circunstancia de que dos interrupciones suceden al mismo tiempo? Primero se debe decir que ese caso no es muy habitual, pero, dada esa circunstancia, existe lo que se conoce como «prioridades»; esto quiere decir que hay interrupciones que son más prioritarias que otras, por lo que se iría atendiendo los eventos de las más prioritarias para dejar para el final las menos prioritarias.
Cada Arduino (en concreto, cada MCU) tiene su propia lista de prioridades. De hecho, la interrupción más prioritaria es la que atiende al reseteo de Arduino; esa es la más prioritaria de todas.
También cabe mencionar que cada Arduino tiene más o menos pines, que permiten la programación de más o menos interrupciones que otros; puede llegar a ser una condición importante para decantarrnos entre un Arduino u otro ya que, si tenemos que atender a varios eventos críticos, lo recomendable es buscar un Arduino que, por hardware, permita programar todos esos eventos como interrupciones:
•Uno, Nano, Mini o basados en el MCU 328 tienen 2 interrupciones, pines: 2, 3.
•Micro, Leonardo o basados en el MCU 32u4 tienen 5 interrupciones, pines:0, 1, 2, 3, 7.
•UNO WiFi y DUE tienen interrupciones para todos sus pines.
•Mega, Mega2560 y MegaADK tienen 6 interrupciones, pines: 2, 3, 18, 19, 20, 21.
En cuanto a la gestión de tiempo, ya hemos trabajo con las instrucciones delay, delayMicroseconds, millis y micros.
Las interrupciones por tiempo vienen a sustituir a un delay o a un delayMicrosenconds; como ya es sabido, estas instrucciones lo que hacen es detener la ejecución de un programa durante un tiempo determinado: hasta ahora era la única forma que teníamos de ejecutar tareas temporales.
El inconveniente de esta solución es que no se puede ejecutar otra tarea y, para muchas aplicaciones, será necesario la ejecución de tareas temporales pero también atender a otras tareas; pues bien, con las interrupciones temporales, conseguimos esto mismo.
Al igual que ocurría con las interrupciones por señal, tenemos una función que se va a ejecutar sin ser llamada desde otra línea del programa y que, en este caso, lo hará cada cierto tiempo. El <<mecanismo>> que utiliza este tipo de interrupciones son los Timers; estos mecanismos son capaces de llevar el contaje de tiempo y podemos trabajar con ellos gracias a las diferentes librerías de las que disponemos.
Cada Arduino tiene un determinado número de Timers; en concreto, el Arduino UNO tiene tres Timers. En este capítulo haremos un caso práctico utilizando el Timer 1 así que vayamos al gestor de librerías e instalemos la librería correspondiente
:
•La instrucción <<Timer1.attachInterrupt()>> define la función que se tiene que ejecutar.
•Con la instrucción <<Timer1.initialize()>>, definimos cada cuánto tiempo se ejecuta la función del punto anterior. El tiempo se mide en microsegundos.
•Con estas instrucciones ya podemos hacer, por ejemplo, que un led parpadee cada segundo sin necesidad de bloquear el programa .
Otras instrucciones de la librería TimerOne:
•<<Timer1.setPeriod(microseconds)>> establece un nuevo tiempo de interrupción.
•<<Timer1.start()>> arranca la interrupción con el nuevo tiempo.
•<<Timer1.stop()>> detiene la temporización.
•<<Timer1.restart()>> reinicia el temporizador.
•<<Timer1.pwm(pin, duty)>> configura las PWM asociadas a este Timer.
•<<Timer1.setPwmDuty(pin, duty)>> establece un nuevo tiempo para las señales PWM.
•<<Timer1.disablePwm(pin)>> deshabilita la PWM.
•<<Timer1.detachInterrupt()>> deshabilita la interrupción.
No para todos los Timers tenemos las mismas instrucciones, ya que usamos diferentes librerías para trabajar con ellos y también existen diferencias entre ellos.
Con un buen uso de este tipo de interrupciones, conseguiremos mejorar el rendimiento de nuestros programas.
Der kostenlose Auszug ist beendet.