Lección 17: Funciones de tiempo

En la lección anterior (Lección 16) descubrimos los problemas que resultaban de utilizar la
función delay(). Cuando tenemos un código de varias líneas que contiene una instrucción
delay(1000) para esperar a que pase un segundo, nos encontramos que nuestro Arduino está
inoperativo la mayor parte del tiempo. Ejecutar el resto del código le puede llevar apenas unos
milisegundos y tiene que estar esperando el resto del tiempo sin capacidad de responder a
ninguno de los eventos que suceden a su alrededor. Por esa razón, nuestros pulsadores para
cambiar la hora, minuto y segundo no respondian de forma satisfactoria.
Además, en la lección 2 tuvimos ocasión de ver cómo se usaba también una instrucción delay()
para lograr intermitencias de leds. Seguro que el lector ha visto ejemplos similiares en muchos
otros cursos de Arduino. Sin embargo esta técnica tiene un segundo problema añadido al
mencionado. Supongamos que queremos generar intermitencias en varios leds con frecuencias
diferentes (uno parpadea cada un segundo y otro cada dos y medio, por ejemplo).
Evidentemente utilizando la técnica de la función delay() es imposible.

Por eso, en esta lección vamos a abordar la construcción de funciones de tiempo con una
filosofía diferente que solucione los dos problemas anteriores. Empezaremos, como es habitual
en este curso, con un mínimo de teoría. El primer concepto importante para entender las
funciones de tiempo es el de "timer". En general, todos los microprocesadores tienen disponibles
varios temporizadores (timer). En concreto el Arduino Uno tiene tres: timer0, timer1, timer2. Un
timer no es más que un contador cuya entrada está conectada al reloj del sistema y que nos
permite "contar" los tics (ciclos de reloj) que han transcurrido.
Nota aclaratoria: Por defecto la señal que van a contabilizar los timers corresponde a la
frecuencia del oscilador dividida por cuatro. Por lo tanto, en realidad cuentan ciclos máquina, no
ciclos de reloj. Con un reloj de 20 Mhz tendríamos una frecuencia de ciclos máquina de 20/4 = 5
MHz, por lo que un ciclo máquina corresponde a 0.2 usec. En principio, el contador del timer de

Por lo tanto. décimas o segundos. el lector podrá comprobar que con el método propuesto en esta lección puede olvidarse de estas consideraciones. es el de interrupción de timer. La gran ventaja de utilizar interrupciones. centésimas.un micro que funcione a 20Mhz se incrementará cada 0. Básicamente. es que todo el código que se esté ejecutando se detiene cada vez que se produce una interrupción y se ejecuta el código que hayamos escrito para "atender" a la interrupción. Estas "unidades de tiempo" pueden ser microsegundos. Como sabemos la frecuencia con la que se produce cada tic. Por ello la precisión obtenida es muy grande. todos los microprocesadores nos permiten generar interrupciones asociadas a los timer. con los timer y las interrupciones podemos lograr construir un reloj (con la precisión que necesitemos acorde a nuestras necesidades) independiente del código de nuestro programa y no sujeto a que el micro esté con más o menos carga de trabajo. La técnica propuesta consiste en mantener un "reloj maestro" que sepa exactamente el tiempo transcurrido en cada momento y utilizar tantos "relojes secundarios" como sea necesario para cada una de las tareas temporizadas que queramos llevar a cabo de forma independiente. Eso significa que podemos configurar nuestro Arduino para que se genere una "interrupción" cada vez que un timer ha contado un número concreto de tics.2 microsegundos o 5 veces en 1 usec. podemos decir que se genere una interrupción cada vez que hayan transcurrido una serie de "unidades de tiempo" determinadas. De todas formas. milisegundos. . evidentemente. El segundo concepto que necesitamos conocer para construir nuestras funciones de tiempo.

La primera función que vamos a escribir se llamará iniTemp() y se encarga de arrancar un cronómetro (reloj secundario) indicando el tiempo que deseamos que se cuente. Arduino utiliza el timer0 para las funciones de tiempo incorporadas por el software báse: delay(). Así que todos nuestros relojes secundarios deben utilizar variables de este mismo tipo. la solución será aprovechar la función millis() para implementar nuestro reloj maestro en el que se basan las funciones de tiempo. la precisión de nuestros relojes secundarios será. El código de ambas funciones es el siguiente: . precisamente de un milisegundo (más que suficiente para la mayoría de los proyectos habituales). la función millis() nos devuelve el tiempo en milisegundos transcurridos desde que se arranco la placa Arduino con el programa actual. Por lo tanto. millis() y micros(). Y el timer2 lo utiliza la función tone(). Básicamente.Por defecto. es decir un número de 32 bits. La función millis() nos devuelve un unsigned long. Puesto que la función millis() cuenta milisegundos. La segunda función será chkTemp() y nos dirá los milisegundos que restan para que el cronómeto alcance el tiempo deseado. Para evitarlo. el usuario que no profundice más puede llegar a pensar que su utilización para los fines que pretendemos está comprometida a menos que sacrifiquemos alguna de las funcionalidades mencionadas. El timer1 lo utiliza la librería servo. Por lo tanto Arduino ya nos facilita el reloj maestro.

Nuestro Arduino y dos leds para hacer las pruebas.Antes de utilizar estas dos funciones y ver con detalle cómo están escritas. vamos a construir una tercera. el esquema electrónico que vamos a utilizar en nuestra lección es muy sencillo. En primer lugar. Su código es el siguiente: Ahora veamos un ejemplo de su uso. . a la que llamaremos parpadeo() y que utiliza las dos anteriores para generar una intermitencia de una frecuencia determinada.

Y el código de nuestro programa: .

.

En nuestro caso para el led situado en el pin IO2 hemos fijado como tiempo 1000 mseg y para el led situado en el pin IO3 hemos fijado como tiempo 2500mseg. utilizaríamos este primer parámetro para llevar a cabo esta tarea sólo cuando se cumpla la condición deseada. como deseamos que la intermitencia se esté generando sobre el led todo el tiempo.Cada vez que utilicemos la función intermitencia() necesitamos pasarle cuatro parámetros. también bajo) nuestra cadena de pulsos intermitentes. El tercer parámetro es una variable auxiliar de trabajo que utilizaremos para almacenar nuestro cronómetro. El segundo parámetro es el tiempo en milisegundos que permanecerá alto (y. por lo tanto. Hemos creado las variables . Debe ser del tipo unsigned long como explicamos antes. En nuestro ejemplo. escribimos directamente un 1. Si quisiéramos que la intermitencia se produjera sólo cuando se cumpliera una determinada condición. El primero es una señal que indica cuando debe ejecutarse la función (in) y llevar a cabo la intermitencia.

. El código de nuestro bucle principal. La primera la utilizamos para un led y la segunda para el otro. según el momento de la intermitencia en que nos encontremos. Hemos utilizado las variables intermitencia1 e intermitencia2 para este fin.5 segundos que no se interfieren una con la otra. El cuarto y último parámetro es una variable auxiliar que utilizamos para indicar el estado de nuestra salida de la intermitencia. Dos llamadas a la nueva función intermitencia() y dos sentencias para escribir el resultado que nos devuelven para activar o desactivar las salidas donde se conectan nuestros leds. no puede ser más sencillo. Podemos comprobar que hay dos intermitencias a frecuencias de 1 y 2.temporizador1 y temporizador2 para este fin. Es la variable que usamos para indicar si el led se iluminará o apagará. Ejecutamos la compilación y simulamos nuestro programa. La primera la utilizamos para un led y la segunda para el otro. loop().

.Conviene aquí que dediquemos un momento a estudiar el código de la función parpadeo() y algunas técnicas de programación utlizadas en él.

. La razón es que en la zona superior del código hemos utilizado tres sentencias #define para fijar las definiciones de AND.más el tiempo que deseamos controlar y que le pasamos a la función como parámetro.En primer lugar. NOT y OR y utilizar estos nombres más intuitivos que &&. La primera asigna a la variable que utilizamos como cronómetro el valor actual devuelto por la función millis() -nuestro reloj maestro. En segundo lugar las variables auxiliares crono y out las hemos utilizado precedidas de un asterisco. || o !. El código de la función realiza lo siguiente. Esto signfica que estamos utilizando punteros a las variables en lugar de las propias variables. utilizamos AND y NOT en lugar de && y !. Para arrancar y comprobar el estado de nuestro cronómetro hemos utilizado las otras dos funciones auxiliares que hemos creado initTemp() y chkTemp(). Si no es verdad ponemos bajo el parámetro OUT para indicar el estado de la intermitencia. Si el parámetro IN es verdad (lanzador de la función) y el cronométro ya ha cumplido su tiempo (la primera vez que se utiliza la función es siempre es así por el reloj maestro siempre es mayor que 0 que es el valor del cronómetro en ese momento) arrancamos nuestro cronómetro con el tiempo establecido en el parámetro TIEMPO y ponemos alto el parámetro utilizado para indicar el estado de la intermitencia -OUT-. Así. El puntero indica la dirección que ocupa una variable. el código de la misma función puede ser utilizada diversas veces con temporizadores diferentes sin que entren en conflicto unos con otros. Por eso cuando utilizamos la función parpadeo la llamamos utilizando como parámetros las variables precedidas del símbolo &.

También se apoya en el uso de las funciones iniTemp y chkTemp. en caso contrario.y devuelve un cero si ya ha transcurrido el tiempo o el valor en milisegundos que falta. se muestra a continuación como otro ejemplo de las funciones temporales que podemos escribir. El código de una nueva función retraso() que sustituya a la estándar delay() pero que no paraliza a nuestro Arduino mientras se ejecuta. . que siempre son la base de todas las demás.La segunda comprueba si el valor almacenado en nuestro cronómetro es mayor que el reloj maestro -la función millis().

el código de una nueva función llamada pulso() que permite generar un pulso de una duración determinada siempre que se cumpla una determinada condición. De esta manera tenemos otro ejemplo de funciones de tiempo que podemos construir con ayuda de nuestra técnica de cronómetro maestro y cronómetros derivados. Esperamos que vuestras soluciones las compartáis en nuestro facebook (https://www.com/pages/HuborProteus/294446180592964?ref=hl). corregir el programa de la lección anterior para que los botones de nuestro reloj ya estén activos todo el tiempo. como ejemplo final. vamos a mostrar. De la misma forma no nos resultará difícil construir otras que se adapten a nuestras necesidades concretas de cada proyecto.facebook. A continuación.Dejamos como problema para el usuario. .

.294. Pero es posible que en ciertos proyectos tengamos que tener esta circunstancia en cuenta porque un retraso.294. Como cada unidad es un milisegundo.582 min = 1. como ya mencionamos.193 horas = 49 días. podemos solucionar el problema utilizando dos variables unsigned long combinadas. La segunda consideración es que la precisión de nuestro cronómetro es de un milisegundo.967. Es decir que cada 49 días nuestro reloj maestro se reiniciará a cero. un pulso o un parpadeo que se produzca justo en el momento en que se reinicia el reloj maestro podría no funcionar correctamente. números de 32 bits. Si necesitáramos precisiones mayores (de hasta un microsegundo) podemos utilizar la función micros() que devuelve microsengundos transcurridos en lugar de milisegundos como hace la función millis(). Esperamos que esta lección le haya resultado útil e interesante. Si en nuestro proyecto se diera este caso.967 seg = 71. En este caso la limitación máxima temoral es de unos 70 minutos.296 mseg = 4.No queremos terminar sin hacer dos consideraciones importantes. suele ser suficiente y no tendremos problemas. En la práctica. eso significa que nuestro cronómetro puede contar hasta 4. La función millis() utiliza.