huborarduino.com

La web de Hubor para la simulación de Arduino con Proteus

Banner Guia Programacion Arduino Con Proteus

[Proteus versión 8.1]

En la lección anterior aprendimos a gobernar un motor de corriente continua utilizando nuestro ARDUINO. Utilizando los motores de corriente continua somos capaces de lograr que nuestros proyectos gobiernen máquinas con movimiento. Por ejemplo podemos controlar un coche, una grúa o una unidad de CD. Los motores DC nos permiten variar la velocidad y el sentido de giro.

Supongamos ahora que nuestra proyecto nos exige ser capaces de posicionar una pieza movil con una alta precisión. Por ejemplo, pensemos que utilizamos nuestro Arduino con dos motores para controlar la plumilla de un plotter capaz de realizar dibujos de un tamaño DIN A4. Utilizaríamos uno de los motores para generar el desplazamiento de la plumilla en la dirección del eje x y el otro para la dirección del eje y. Podríamos utilizar dos motores de corriente continúa como los vistos en la lección anterior para este trabajo. Pero imaginemos que queremos situar nuestra plumilla exactamente en la esquina superior izquierda de nuestra hoja de papel. La precisión se ha convertido en un asunto importante, porque desplazarnos fuera de la hoja, puede dañar nuestra plumilla. ¿Se imagina el lector, teniendo que ir dando pequeños toques en los pulsadores de control de nuestros dos motores para lograr situar la plumilla en el punto exacto? Seguro que dejamos de utilizar nuestro plotter.

Para dar respuesta a esta problemática disponemos de los servomotores. Un servomotor es un motor con un eje de rendimiento controlado. Esto significa que podemos posicionarlo a nuestra voluntad con una gran precisión. Pero, eso sí, siempre dentro de un rango determinado. En general, los servomotores que podemos utilizar nos permiten usar rangos de 180º, aunque existen versiones de 210º e incluso los de rotación continua.

Lec13 001

Un servomotor es un motor de corriente continua con una serie de engranajes que aprovechan su velocidad para aumentar su torque (fuerza) y un sistema de control basado en un potenciómetro que permite saber constantemente la ubicación del eje. El sistema de control responde a las ordenes generadas con una señal que debemos proporcionarle para establecer la posición del eje deseada.

Lec13 002

La señal de control que introducimos al servomotor, es una señal parecida, que no igual, a la del tipo PWM. Para gestionar un servomotor vamos a generar un tren de pulsos con una frecuencia de 50Hz. Dependiendo del ancho de la zona alta del pulso que le enviemos (he ahí el parecido con las señales PWM), nuestro servo se situará en una posición o en otra. 

Proteus nos facilita un modelo para simular un servomotor, llamado MOTOR_PWMSERVO. Construyamos un primer circuito para poder simularlo hasta entender perfectamente el funcionamiento del mismo. Utilizaremos un servomotor que alimentaremos conectándole los potenciales VDD y GND. Para la señal de control utilizaremos un generador de señal del tipo DPATTERN (Patrón de señales digitales).

Lec13 003

El segundo paso consiste en editar las propiedades del servomotor y de la señal de control. Como siempre, para hacerlo nos pondremos sobre cada uno de ellos y con el botón derecho del ratón abriremos el menú contextual y seleccionaremos la opción de editar sus propiedades. En el caso del servomotor tenemos que parametrizarlo teniendo en cuenta los siguientes aspectos. El rango de giro que nos permite nuestro servo. En nuestro caso vamos a elegir 0º como ángulo mínimo y 180º como el máximo ángulo posible. La velocidad máxima de giro. Vamos a seleccionar 50Hz. Y el ancho de la zona alta del pulso que vamos a utilizar para colocar nuestro servo en el ángulo mínimo y el que usaremos para indicar a nuestro servo que se coloque en el ángulo máximo. Unos valores bastante estandarizados son 0,544 mseg para el mínimo y 2,4 mseg para el máximo. Vamos a introducir estos valores utilizando su equivalente en microsegundos (544u para el mínimo y 2400u para el máximo). Al final de parametrizar nuestro componente debe tener el aspecto recogido en la siguiente imagen.

Lec13 004 

Para el generador de la señal de control, vamos a utilizar los siguientes valores:

Lec13 005

Como se puede observar estamos definiendo un tren de pulsos con forma de secuencia continua que empieza en una señal baja, con el primer flanco en el instante cero. El tiempo que permanecerá en alto el pulso será de 2.4 mseg (el que hemos definido como posicionarse en el ángulo máximo de 180º y el tiempo que permanecerá bajo será de 17,6 mseg. Para obtener esta segunda cifra hemos realizado el siguiente cálculo. Puesto que la señal de control es de 50Hz, el tiempo de cada pulso debe ser de 20 mseg (1 seg / 50 = 20 mseg). Si el pulso debe estar en alto 2,4 mseg, el resto del tiempo debe estar bajo (20 mseg - 2,4 mseg = 17,6 mseg).

Ahora que ya hemos explicado cómo se configura la señal de control, podemos arrancar la simulación y comprobaremos que el servomotor se coloca en la posición deseada (180º).

Lec13 006

En general, podemos fijar el ángulo deseado de nuestro servomotor, calculando el tiempo del flanco alto y bajo de nuestra señal de tren de pulsos de control con estas dos fórmulas:

tiempo flanco alto = 0,544 + n ( 1,856 / 180 ) donde n es el ángulo en grados.

tiempo flanco bajo = 20 - tiempo flanco bajo.

Probemos a colocar nuestro servo en la posición 0º (0,544 y 19,456), en la posición 45º (1,008 y 18,992) y en la posición 90º (1,472 y 18,528). Realmente es sencillo colocar nuestro servomotor en la posición que deseemos.

Ahora que ya entendemos completamente como funciona un servomotor, vamos a realizar el siguiente circuito para controlarlo con nuestro Arduino.

Lec13 007

Y nos enfrentaremos ahora a la tarea de programar la señal de control. En un primer momento podría parecer que generar nuestra señal de control desde Arduino no va a resultar una tarea sencilla. Afortunadamente contamos con una librería (servo.h) que nos pondrá las cosas muy sencillas. Podemos consultar todas sus funciones en el siguiente enlace (http://arduino.cc/es/Reference/Servo). Vamos a utilizar el siguiente código de ejemplo para controlar nuestro servomotor y, como siempre, iremos explicando cada línea de nuestro programa.

Lec13 008

La sentencia #include  <servo.h> sirve para enlazar con la librería servo. A continuación definimos dos variables. Una llamada myservo del tipo servo (este tipo está definido en la propia librería) que utilizaremos para controlar nuestro servomotor. Si nuestro Arduino controlara más de un servomotor, tendríamos que definir una variable de este tipo por cada uno de los servomotores que deseamos controlar. Además usaremos una segunda variable entera llamad ángulo que usaremos para fijar el ángulo al que deseamos situar nuestro servomotor.

En la función de configuración (setup), definiremos los pines que vamos a utilizar para generar nuestra salida de control (pin IO9) y el que vamos a utilizar para controlar un led que nos ayudará a visualizar en que fase del programa nos encontramos (el pin IO0).

En el bucle principal del programa (loop) realizaremos la siguiente secuencia de control. Primero encendemos el led, luego vamos aumentando el valor del ángulo de posición de nuestro servomotor desde 0º a 180º en saltos de 10 en 10 grados, dejando un retardo de 1 segundo entre cada nuevo posicionamiento. A continuación apagamos el led y vamos disminuyendo el ángulo de posición desde 180º a 0º pero esta vez en saltos de 5 en 5 grados. La función de la librería servo.h utilizada para posicionar el servomotor en el ángulo que deseemos es write().

Arranquemos nuestra simulación y comprobemos que todo funciona como deseamos.

Lec13 009 

Utilizar un servomotor desde nuestro ARDUINO es muy sencillo.

Construyamos ahora un segundo circuito donde utilizaremos nuestro terminal virtual para poder enviar órdenes a nuestro ARDUINO y que éste nos porporcione información usándolo como consola.

Lec13 010

Para que el terminal virtual funcione bien, debemos configurarlo como se muestra en la imagen siguiente.

Lec13 011

El código de nuestro programa de control será el siguiente:

Lec13 012

En este caso esperamos que el usuario pulse un número entre 0 y 9 y colocamos el servomotor en el ángulo correspondiente (el número multiplicado por 10). Dejamos al usuario que estudie el programa para entenderlo bien. Es importante tener en cuenta cuando arranquemos la simulación, que para poder enviar teclas desde nuestro teclado por el terminal virtual tenemos que poner el ratón encima y pulsar sobre él para activarlo. Si no se visualizara el terminal virtual al arrancar la simulación podemos ordenar que se muestre desde el menú depuración (debug). El resultado si pulsamos la tecla 8 sería:

Lec13 013

 

De esta manera, en este ejemplo, no tenemos un servomotor cambiando constantemente de posición como en el primer caso. En este nuevo ejemplo, el uso del servomotor es más parecido a la realidad. El usuario cada vez indica desde el terminal la posición en la que deseamos colocar el servomotor, ARDUINO se encarga de generar la orden y el servomotor permanece en esta posición hasta que se recibe una nueva orden.

Si no podemos gobernar nuestro ARDUINO desde la consola, podemos utilizar un potenciometro para indicar la posición a la que deseamos colocar nuestro servomotor. A continuación mostramos el circuito y el código del programa. A estas alturas del curso seguro que el lector no encuentra problemas para comprender ambos.

Lec13 014

Lec13 015

En este caso, moviendo el potenciometro, recolocamos nuestro servomotor según deseemos. Si giramos el potenciometro en uno u otro sentido, el ángulo de posicionamiento del servomotor se incrementa o decremente. Seguro que el lector encontrará muchas aplicaciones para los servomotores.

 

 

[Proteus versión 8.1]

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.

Lec17 001

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 un micro que funcione a 20Mhz se incrementará cada 0.2 microsegundos o 5 veces en 1 usec. De todas formas, el lector podrá comprobar que con el método propuesto en esta lección puede olvidarse de estas consideraciones.

Lec17 002

El segundo concepto que necesitamos conocer para construir nuestras funciones de tiempo, es el de interrupción de timer. Básicamente, todos los microprocesadores nos permiten generar interrupciones asociadas a los timer. 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. Como sabemos la frecuencia con la que se produce cada tic, evidentemente, podemos decir que se genere una interrupción cada vez que hayan transcurrido una serie de "unidades de tiempo" determinadas. Estas "unidades de tiempo" pueden ser microsegundos, milisegundos, centésimas, décimas o segundos. La gran ventaja de utilizar interrupciones, 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. Por ello la precisión obtenida es muy grande.

Lec17 003

Por lo tanto, 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.

Lec17 004

Por defecto, Arduino utiliza el timer0 para las funciones de tiempo incorporadas por el software báse: delay(), millis() y micros(). El timer1 lo utiliza la librería servo. Y el timer2 lo utiliza la función tone(). Por lo tanto, 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. Para evitarlo, la solución será aprovechar la función millis() para implementar nuestro reloj maestro en el que se basan las funciones de tiempo.

Básicamente, 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 Arduino ya nos facilita el reloj maestro. La función millis() nos devuelve un unsigned long, es decir un número de 32 bits. Así que todos nuestros relojes secundarios deben utilizar variables de este mismo tipo. Puesto que la función millis() cuenta milisegundos, la precisión de nuestros relojes secundarios será, precisamente de un milisegundo (más que suficiente para la mayoría de los proyectos habituales).

Lec17 005

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. La segunda función será chkTemp() y nos dirá los milisegundos que  restan para que el cronómeto alcance el tiempo deseado.

El código de ambas funciones es el siguiente:

Lec17 006

Antes de utilizar estas dos funciones y ver con detalle cómo están escritas, vamos a construir una tercera, a la que llamaremos parpadeo() y que utiliza las dos anteriores para generar una intermitencia de una frecuencia determinada. Su código es el siguiente:

Lec17 007

Ahora veamos un ejemplo de su uso. En primer lugar, el esquema electrónico que vamos a utilizar en nuestra lección es muy sencillo. Nuestro Arduino y dos leds para hacer las pruebas.

Lec17 008

Y el código de nuestro programa:

Lec17 009

Lec17 010

Cada vez que utilicemos la función intermitencia() necesitamos pasarle cuatro parámetros. El primero es una señal que indica cuando debe ejecutarse la función (in) y llevar a cabo la intermitencia. En nuestro ejemplo, como deseamos que la intermitencia se esté generando sobre el led todo el tiempo, escribimos directamente un 1. Si quisiéramos que la intermitencia se produjera sólo cuando se cumpliera una determinada condición, utilizaríamos este primer parámetro para llevar a cabo esta tarea sólo cuando se cumpla la condición deseada.

El segundo parámetro es el tiempo en milisegundos que permanecerá alto (y, por lo tanto, también bajo) nuestra cadena de pulsos intermitentes. 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.

El tercer parámetro es una variable auxiliar de trabajo que utilizaremos para almacenar nuestro cronómetro. Debe ser del tipo unsigned long como explicamos antes. Hemos creado las variables temporizador1 y temporizador2 para este fin. La primera la utilizamos para un led y la segunda para el otro.

El cuarto y último parámetro es una variable auxiliar que utilizamos para indicar el estado de nuestra salida de la intermitencia. Es la variable que usamos para indicar si el led se iluminará o apagará, según el momento de la intermitencia en que nos encontremos. Hemos utilizado las variables intermitencia1 e intermitencia2 para este fin.  La primera la utilizamos para un led y la segunda para el otro.

El código de nuestro bucle principal, loop(), no puede ser más sencillo. 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. Ejecutamos la compilación y simulamos nuestro programa. Podemos comprobar que hay dos intermitencias a frecuencias de 1 y 2,5 segundos que no se interfieren una con la otra.

Lec17 011

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. 

Lec17 012

En primer lugar, utilizamos AND y NOT en lugar de && y !. La razón es que en la zona superior del código hemos utilizado tres sentencias #define para fijar las definiciones de AND, NOT y OR y utilizar estos nombres más intuitivos que &&, || o !.

Lec17 018

En segundo lugar las variables auxiliares crono y out las hemos utilizado precedidas de un asterisco. Esto signfica que estamos utilizando punteros a las variables en lugar de las propias variables. El puntero indica la dirección que ocupa una variable. Así, 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 &.

Lec17 013

El código de la función realiza lo siguiente. 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-. Si no es verdad ponemos bajo el parámetro OUT para indicar el estado de la intermitencia.

Para arrancar y comprobar el estado de nuestro cronómetro hemos utilizado las otras dos funciones auxiliares que hemos creado initTemp() y chkTemp(). La primera asigna a la variable que utilizamos como cronómetro el valor actual devuelto por la función millis() -nuestro reloj maestro- más el tiempo que deseamos controlar y que le pasamos a la función como parámetro.

Lec17 014

La segunda comprueba si el valor almacenado en nuestro cronómetro es mayor que el reloj maestro -la función millis()- y devuelve un cero si ya ha transcurrido el tiempo o el valor en milisegundos que falta, en caso contrario.

Lec17 015

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, se muestra a continuación como otro ejemplo de las funciones temporales que podemos escribir. También se apoya en el uso de las funciones iniTemp y chkTemp, que siempre son la base de todas las demás.

Lec17 016

Dejamos como problema para el usuario, corregir el programa de la lección anterior para que los botones de nuestro reloj ya estén activos todo el tiempo. Esperamos que vuestras soluciones las compartáis en nuestro facebook (https://www.facebook.com/pages/Hubor-Proteus/294446180592964?ref=hl).

A continuación, vamos a mostrar, como ejemplo final, 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. De la misma forma no nos resultará difícil construir otras que se adapten a nuestras necesidades concretas de cada proyecto.

Lec17 017

No queremos terminar sin hacer dos consideraciones importantes. La función millis() utiliza, como ya mencionamos, números de 32 bits. Como cada unidad es un milisegundo, eso significa que nuestro cronómetro puede contar hasta 4.294.967.296 mseg = 4.294.967 seg = 71.582 min = 1.193 horas = 49 días.

Es decir que cada 49 días nuestro reloj maestro se reiniciará a cero. En la práctica, suele ser suficiente y no tendremos problemas. Pero es posible que en ciertos proyectos tengamos que tener esta circunstancia en cuenta porque un retraso, un pulso o un parpadeo que se produzca justo en el momento en que se reinicia el reloj maestro podría no funcionar correctamente. Si en nuestro proyecto se diera este caso, 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. 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(). En este caso la limitación máxima temoral es de unos 70 minutos. 

Esperamos que esta lección le haya resultado útil e interesante.

 

[Proteus versión 8.1]

En la lección de hoy vamos a aprender a mostrar información utilizando un display LCD de texto. Un display LCD (siglas del inglés liquid crystal display) o pantalla de cristal líquido, es una pantalla delgada y plana formada por un número de píxeles, en color o monocromos, colocados delante de una fuente de luz o reflectora. Su principal ventaja es que utiliza cantidades muy pequeñas de energía eléctrica.

Lec16 001

Existen pantallas muy variadas en el mercado. Las hay de colores diferentes (monocromo verde, monocromo ámbar o color). Unas tienen capacidad para mostrar sólo caracteres de  texto y otras admiten gráficos. Las hay que se controlan utilizando varios pines de nuestro Arduino (normalmente dos para el control y otros 4 para los datos) o que se comunican con él mediante comunicaciones vía serie. Y, además, las podemos encontrar de  tamaños muy distintos.

Lec16 002

En esta lección, nos vamos a centrar en aquellas que son monocromo y sólo pueden mostrar caracteres de texto. Proteus pone a nuestra disposición una amplia variedad de pantallas de este tipo para que nuestra simulación se adapte lo más precisamente posible a las necesidades de nuestros proyectos.

Lec16 003

En este proyecto vamos a utilizar el modelo LM016L que nos permite simular una pantalla de dos filas de 16 caracteres cada una. Para controlarla utilizaremos 6 pines de nuestro Arduino (dos para el control y cuatro para los datos). El esquema de conexionado se muestra en la siguiente figura.

Lec16 004

Al estar utilizando un dispositivo virtual no tenemos que preocuparnos de gestionar la luminosidad de nuestra pantalla (normalmente se puede graduar para que luzca con más o menos intensidad mediante la utilización de un potenciómetro).

Además, para simplificar aún más las cosas, en Arduino disponemos de una librería llamada LiquidCrystal.h que nos permite gestionar de forma muy sencilla nuestra pantalla. De esta manera nos podemos olvidar de las complejidades derivades de todas las escrituras a bajo nivel con la correspondiente gestión de activación y desactivacióin de los pines. 

Vamos a escribir un sencillo programa que muestra en la pantalla un contador de segundos. Con la ayuda de la librería el código es así de sencillo:

Lec16 005

Al principio se realiza una llamada a la librería LiquidCrystal.h. A continuación utilizamos dos variables que nos ayudan a definir las filas y columnas de nuestra pantalla. Configuramos los pines de Arduino que vamos a utilizar para controlar nuestra pantalla con la función lcd() de la librería.

En la función setup() aprovechamos para inicializar nuestra pantalla con 2 filas y 16 caractéres y escribir en la primera fila de nuestra pantalla un mensaje que muestre el rótulo "segundos:"

En la función loop() situamos el cursor en el primer carácter de la segunda fila (tener en cuenta que siempre empezamos a contar desde la posición 0, por eso el primer carácter ocupa la posición 0 y la segunda línea la posición 1). A continuación escribimos en dicha posición la lectura del contador interno del Arduino de los milisegundos que han transcurrido desde que lo encendemos (utilizando la función millis() ) y para obtener la cuenta en segundos dividimos por 1000 ese valor.

Si compilamos y generamos la simulación, tendremos un contador de segundos en nuestra pantalla de cristal líquido.

Lec16 006

El control de una pantalla no puede ser más sencillo. Simplemente hay que configurarlo al principio y luego, cada vez que deseamos refrescar la información presentada al usuario, tenemos que colocar el cursor en el lugar deseado y escribir la cadena de texto correspondiente.

Para completar la lección, vamos a realizar un segundo proyecto. Vamos a añadir tres pulsadores a nuestro anterior montaje. Así que ahora tenemos el siguiente diseño electrónico:

Lec16 007

El programa que vamos a escribir ahora, nos permitirá tener un reloj en nuestra pantalla y los tres pulsadores nos servirán para configurar la hora, minuto y segundo que deseemos. El código es el siguiente:

Lec16 008

Lec16 009

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

El esquema es el mismo: configuramos nuestra pantalla y escribimos el mensaje de la primera línea que permanece siempre fijo. Como el bucle se ejecuta cada 1 segundos, cada iteración vamos sumando un nuevo segundo. Cuando llegamos a 60 segundos, incrementamos en una unidad los minutos. Cuando llegamos a 60 minutos, incrementamos en una unidad las horas. Por último, controlamos la acción del usuario sobre los pulsadores para incrementar los segundos, minutos u horas.

El resultado de compilar y ejecutar la simulación se muestra a continuación.

Lec16 010

Observe que el programa no reacciona de forma inmediata al uso de los pulsadores. La razón es que tenemos la mayor parte del tiempo a nuestro programa parado en el delay situado al final del código. Por eso hay que tener pulsado el botón y ver que tarda un segundo en reaccionar. Más adelante en otra lección veremos cómo podemos solventar este problema de forma satisfactoria.

Esperamos que la utilización de pantallas LCD de texto ya sea un asunto sin secretos para el usuario.

 

[Proteus versión 8.1]

En la lección 16 montamos como práctica un reloj digital. En aquel momento nuestro objetivo era aprender a utilizar un display LCD de texto monocromo y, por esa razón, no nos preocupó realizar una serie de simplificaciones que, en la práctica, hacían que nuestro reloj fuera inservible. De hecho, nuestro reloj era un prodigio de inexactitud y de ineficacia. Inexacto, porque contábamos los segundos mediante la utilización de lapsos de tiempo de inactividad (lo que significa que cada segundo se produce un desfase igual al tiempo que tarda el micro en ejecutar el resto del código de programa). Ineficaz porque teníamos a nuestro microprocesador ocupado la mayor parte del tiempo en un bucle de espera inoperativo.

Lec18 001

En la lección 17 vimos como podíamos utilizar una serie de funciones de tiempo que nos permitían evitar el uso de la función delay() con los problemas que nos acarreaba. En esta lección vamos a construir un reloj un poco más profesional. Utilizaremos un display LCD de dos filas y 40 caracteres. Aprovecharemos para aprender a utilizar un integrado RTC (reloj de tiempo real) que proporcionará a nuestro reloj una adecuada precisión. Incluiremos el uso de las funciones temporales para que los pulsadores de mando estén activos en todo momento. Y veremos la utilización de un función que nos permita detectar el flanco de subida de una señal. Además utilizaremos el bus i2c que vimos en las lecciones 910 y 11. Por supuesto todo ello simulado desde nuestro entorno Proteus.

Nuestro montaje se muestra en las imagénes siguientes. Como es un poco mayor de lo habitual, presentamos una versión global y varios detalles:

Lec18 002

 El detalle del integrado RTC se muestra a continuación:

Lec18 003

El conexionado del display LCD es el siguiente:

Lec18 004

Y, por último, el conjunto de pulsadores de mando, se muestra a continuación:

Lec18 005

El DS3232 es un integrado de la firma Maxim (http://www.maximintegrated.com/en/products/digital/real-time-clocks/DS3232.html) que nos proporciona un reloj de gran precisión con corrección de las derivas que se producen por los cambios de temperatura. Se conecta con nuestro ARDUINO utilizando el puerto i2c. Proteus incluye un modelo que nos permite llevar a cabo su simulación. Para ello sólo tenemos que unirlo a nuestro ARDUINO utilizando dos pines (los del puerto i2c). Además, aprovecharemos una ventaja que nos proporciona Proteus. Si modificamos las propiedades del DS3232 podemos indicarle que tome de forma automática la hora de nuestro PC al arrancar la simulación. Así tendremos un reloj en hora sin esfuerzo.

Lec18 006

Una vez que tenemos listo nuestro hardware, podemos pasar a ocuparnos del programa que se ejecutará en nuestro Arduino. Esta vez es un poco más largo de los que habitualmente utilizamos en el curso. Pero es que, a medida que el curso avanza, los programas se hacen más interesantes, más sofisticados y, por lo tanto, más largos. Por ese motivo, lo vamos a analizar por trozos.

En la primera parte (líneas 1 a 25) vamos a enlazar con las dos librerías que vamos a utilizar. Wire.h nos posibilita la utilización del bus i2c y LiquidCrystal.h el uso del display LCD (ya vimos el uso de las dos en lecciones anteriores). Luego definimos unas constantes que utilizaremos a lo largo del programa (la dirección de nuestro integrado DS3232 y los operadores lógicos AND, OR y NOT). A continuación, dos constantes para indicar el tamaño de nuestro display LCD (dos filas y cuarenta columnas). Por último una serie de variables de trabajo, de las cuales sólo destaca la variable del objeto que usamos para controlar nuestro display LCD donde definimos los pines con los que se conecta a nuestro ARDUINO y que vimos con detalle en lecciones anteriores.

Lec18 007

El segundo trozo de nuestro código contiene la función setup() que sirve para inicializar nuestro equipo Arduino. La utilizamos para escribir la primera línea de nuestro display LCD, definir como entradas los pines a los que conectamos los pulsadores de mando e inicializar el puerto serie que utilizamos para el bus i2c. Observe que hemos dejado comentadas una serie de líneas al final en las que se incluye un pequeño trozo de código que sirve para escribir la hora actual en el dispositivo DS3232. Como explicamos antes, Proteus nos facilita esta tarea e inicializa por defecto el dispositivo DS3232 con la hora actual del reloj de nuestro PC por lo que en simulación no es necesaria utilizarla.

Lec18 008

La tercera parte del programa contiene la función loop() que es el código que se ejecuta de forma cíclica en nuestro equipo Arduino.

En la primera parte utilizamos las funciones chkTemp e iniTemp para ejecutar una serie de instrucciones sólamente una vez cada 100ms. De esa forma no tenemos que utilizar el poco práctico delay() y descargamos a nuestro Arduino de la tarea de estar refrescando de forma infructuosa cada ciclo de programa el display. Por otro lado 100ms es tiempo suficiente para que si se usan los pulsadores de mando, la información se refresque en el display cuando el usuario los utilice.

La tarea que se ejecuta cada 100ms es la encargada de refrescar la hora actual en el display LCD utilizando la función auxiliar llamada MostrarHoraEnDisplay() cuyo código veremos más adelante.

El resto de la función es una serie de trozos de código iguales, cada uno de los cuales controla un pulsador y responde aumentando la parte del reloj correspondiente (segundo, minuto, hora, etc) una unidad y controlando los desbordes. Destacamos el uso de la nueva función FlancoSub() cuyo código veremos más adelante. Con ella posibilitamos que cuando el usuario pulse el botón, sólo se incremente una unidad con el flanco de subida. Si no lo hiciéramos así, cada vez que el usuario actúa sobre el pulsador se incrementarían muchas unidades de un golpe. La función flanco de subida puede resultar muy útil en muchos otros proyectos en los que necesitemos utilizar pulsadores.

Lec18 009

Lec18 010

El siguiente trozo de código contiene la función MostrarHoraEnDisplay() que ya dijimos que se ocupa de escribir en el display LCD la hora actual. Recibe como parámetros las unidades que componen nuestro reloj: segundo, minuto, hora, día de la semana, día, mes y año. Formatea de forma adecuada el mensaje para presentarlo en nuestro display.

Lec18 011

Lec18 012

La función LeerHoraDS3232() se encarga de recibir de la información de la hora actual desde el dispositivo DS3232. Puesto que es un dispositivo con el que nos conectamos a través del bus i2c, el flujo de control es muy similar al que ya vimos en lecciones anteriores para leer información procedente de un dispositivo que cumple el estandar de comunicación i2c. Primero hacemos una llamada a su dirección dentro de la red, luego escribimos el comando adecuado para indicarle que queremos que nos devuelva los datos de la hora actual y, por último, leemos los diferentes bytes que nos trasmite. Guardamos cada trozo de informacióin en la variable destinada a tal fin y utilizamos la función bcdToDec(), que veremos con detalle más adelante, para transformarla al formato decimal.

Lec18 013

FijarHoraDS3232() es una función que utilizamos para cambiar la hora guardada en este momento en el dispositivo DS3232. Su funcionamiento es muy similar a la que acabamos de ver, pero en este caso escribimos en lugar de leer los datos.

Lec18 014

Las funciones iniTemp() y chkTemp() ya las vimos en lecciones anteriores. Sirven para arrancar un temporizador de un determinado tiempo de duración y para comprobar si ese tiempo ya se ha cumplido o cuanto queda todavía para hacerlo. Es una técnica muy útil para poder manejar varios temporizadores diferentes en nuestro Arduino basándose en un único reloj maestro (en este caso el que usa la función millis().

Lec18 015

decToBcd() es una función que convierte un número en formato decimal a un número en formato BCD. La función bcdToDec() hace la función inversa. El código BCD ( Binary Coded Decimal) es un código estándar de 6 bits usado por algunos ordenadores para almacenar caracteres alfanuméricos y signos de puntuación. Cada carácter está compuesto por 6 bits (2 carácteres en formato octal). Con estos 6 bits se pueden definir un total de 64 caracteres (2^6). Como el dispositivo DS3232 almacena su información utilizando este formato, estas funciones son útiles para hacer las transformaciones adecuadas al formato decimal que utilizamos en nuestro Arduino.

Lec18 016

En una señal digital, se denomina flanco a la transición del nivel bajo al alto (flanco de subida) o del nivel alto al bajo (flanco de bajada). En este caso, incorporamos la función FlancoSub() que detecta el flanco de subida. De esta forma, nos resulta sencillo determinar el momento en que se actúa sobre el pulsador ignorando el resto del tiempo que dicho botón permanece actuado. Así, permitimos que se incremente una unidad cada vez que se produce una actuación sobre el pulsador. 

Esta función es muy utilizada en dispositivos de mando de máquinas móviles para reducir el riesgo de que una botonera caída en el suelo (con tal mala suerte de que el pulsador de marcha quede pulsado de forma constante) o humedades producidas por condensación en el interior de la botonera, puedan provocar de forma extemporánea arranques no intencionados con riesgo para las personas.

Lec18 017

Una vez desentrañado todo el código, ya sólo resta arrancar la simulación y disfrutar de nuestro reloj.

Lec18 018

Esperamos que les haya resultado interesante la lección de hoy y como siempre terminamos proponiendo una práctica. ¿Se anima el lector a completar el proyecto logrando que nuestro reloj se convierta en un despertador? Para ello es necesario poder fijar una hora con ayuda de los pulsadores y hacer sonar una alarma cuando se cumpla dicha hora.

 

 

[Proteus versión 8.1]

En la lección de hoy vamos a aprender a controlar un teclado matricial desde nuestro equipo Arduino. Un teclado matricial es un dispositivo compuesto de una serie de pulsadores conectados de forma matricial (en filas y colúmnas).

lec15 001

La ventaja de disponerlos de esta manera es que se pueden leer la información de múltiples botones utilizando un número reducido de pines de nuestro microprocesador. Así, por ejemplo, un teclado matricial de 4×4 que se compone de 16 pulsadores, solamente necesita 4 pines para las filas y otros 4 pines para las colúmnas. De esta forma se pueden leer las 16 teclas utilizando solamente 8 pines, en lugar de los 16 que serían necesarios utilizando otra configuración.

lec15 002

lec15 003

El sistema en el que se basa el funcionamiento de un teclado matricial es el multiplexado. Configuramos cuatro pines de nuestro microprocesador como entradas y otros cuatro como salidas. Conectamos las entradas a las columnas y las salidas a las filas (o viceversa porque el funcionamiento sería el mismo). Empezamos con las cuatro salidas en nivel bajo. Activamos la salida de la primera fila y leemos las cuatro entradas. Cada una de ellas nos informará del estado de los cuatro pulsadores de esa fila. La entrada 1 el pulsador de la columna 1, la entrada 2 el pulsador de la columna 2 y así sucesivamente. A continuación, desactivamos la salida 1 y activamos la 2. En este momento en las cuatro entradas podemos leer el estado de los pulsadores situados en la segunda fila. Haremos lo mismo con las salidas 3 y 4 para obtener el estado de los pulsadores situados en las filas 3 y 4.

A continuación, vemos un esquema del momento en que hemos activado nuestra salida 2 (para leer el estado de los pulsadores de la segunda fila) y vemos como, si estuviera actuado el pulsador 6 (fila 2, columna2), tendríamos la entrada de la columna 2 activa.

lec15 004

Afortunadamente, no tenemos que preocuparnos de la programación de nuestro multiplexor, porque Arduino ya nos facilita librerías para ello. Así que el uso de un teclado matricial es muy sencillo. Proteus, también nos facilita varios modelos de teclados matriciales para poder llevar a cabo la simulación. Un teclado de calculadora reducido (keypad-smallcalc), un teclado de calculadora más amplio (keypad-calculator) y uno de teléfono (keypad-phone).

lec15 005

Cada uno de ellos difiere de los otros, no solo en el serigrafiado de los botones, sino también en el número de pulsadores y, consecuentemente, en el número de pines de entrada y salida necesarios para gobernarlos. Nosotros vamos a utilizar en nuestra práctica el teclado telefónico, aunque al lector no le costará nada aplicar luego los conocimientos adquiridos para gestionar cualquier otro tipo de teclado. El esquema que vamos a utilizar se muestra en la imagen siguiente:

lec15 006

La librería que viene en nuestra ayuda se llama keypad.h. Esta librería no viene de forma estándar con la distribución del compilador de Arduino. Así que tenemos que descargarla desde la web. La podemos encontrar en el siguiente enlace:

http://playground.arduino.cc/code/Keypad

Descargaremos el fichero comprimido zip a nuestro ordenador y lo desempaquetamos. Con esta operación obtendremos una carpeta llamada keypad. Esta carpeta la copiaremos en la carpeta 'Libraries' del directorio donde se encuentra instalado el compilador Arduino en nuestro equipo. En nuestro caso la ruta de acceso es:

Archivos de programa /(x86)/Arduino/libraries. 

lec16 006

Una vez hecho esto ya podemos escribir nuestro código. Vamos a realizar una aplicación que nos permite introducir con ayuda de nuestro teclado una clave de cuatro dígitos que nos posibilitará abrir o cerrar una caja fuerte. Si la clave introducida coincide con el número almacenado, encenderemos un led verde (simulamos que abrimos la caja). En caso contrario, encenderemos un led rojo (simulamos que cerramos nuestra caja fuerte).

lec16 007

Lo primero que hacemos es enlazar con la librería keypad. A continuación definimos una serie de constantes que utilizamos para indicar el número de filas y columnas de nuestro teclado. Y luego dos matrices que usamos para indicar los pines a los que conectaremos cada fila y cada columna.

lec16 008

A continuacíón creamos una serie de variables que utilizamos para diversas operaciones de control.

lec16 009

La variable position la utilizaremos para ir ordenando las cifras que componen nuestra clave. La variable redPin almacena el pin de salida donde conectamos nuestro led rojo. La variable greenPin almacena el pin de salida donde conectaremo nuestro led verde. La variable secretCode contiene nuestra clave válida. Nosotros hemos puesto 2468. Luego generamos una matriz para identificar cada una de las teclas con el símbolo que tiene rotulado. Por último creamos un objeto del tipo keypad (este tipo se define en la librería keypad.h) que nos permite inicializar todos los datos del objeto que controla nuestro teclado. Como se ve, para inicializar el objeto, se llama a la función keypad() y se le pasa cinco parémetros. El primero es una llamada a la función makeKeymap() que, a su vez, recibe como parámetro la matriz con los símbolos serigrafiados. El segundo y tercer parámetro son las matrices que contienen los pines a los que conectamos las columnas y filas. Por último, el cuarto y quinto parámetro son las variables con el número de filas y columnas de nuestro teclado.

Como se ha visto, la librería es muy fácil de adaptar a diversas configuraciones de teclados matriciales, simplemente cambiando las definiciones de las matrices keys, rowPins, colPins y de las variables rows y cols.

El siguiente paso es inicializar nuestro equipo utilizando la función setup().

lec16 010

En este caso, sólo configuramos los pines a los que hemos conectado los dos leds, como pines de salida y realizamos una llamada a la función setLocked() pasando como parámetro la constante true. La función setLocked() se encuentra situada al final de nuestro código.

lec16 011

Su funcionamiento es muy sencillo. Cuando la llamamos pasando como parámetro true, encendemos el led rojo y apagamos el verde (no se abrió la puerta). Si pasamos como parámetro false, encendemos el led verde y apagamos el rojo (se abrió la puerta). Por eso, de mano, la hemos llamado pasando como parámetro true, para indicar que empezamos con la puerta de nuestra caja fuerte cerrada.

Veamos ahora el último trozo de código que está contenido en la función loop().

lec16 012

Primero asignamos el resultado del método getKey() de nuestro objeto keypad a la variable key. De esta manera tan sencilla la librería nos indica qué botón de nuestro teclado matricial ha pulsado el usuario. A continuación evaluamos si se ha pulsado las teclas '*' o '#'. En caso afirmativo, ponemos el contador de cifras introducidas a cero y cerramos la caja llamando a la función setLocked() con el parámetro true.

Si la tecla introducida es una diferente a estas dos (es decir, cualquiera que tenga una cifra serigrafiada), comprobamos si el valor de la tecla introducida coincide con la el valor del código secreto de esa misma posición. En caso afirmativo pasamos a la segunda cifra y así sucesivamente hasta llegar a la cuarta cifra.

Cuando hemos acertado con las cuatro cifras de nuestra clave secreta, abrimos la caja llamando a la función setLocked() pasándole el parámetro false.

Vamos a compilar nuestro programa para ver como funciona. Si es la primera vez que utilizamos nuestra librería keypad.h encontraremos un mensaje de error porque Proteus no sabe que esa carpeta se ha añadido a nuestro proyecto. Para solventarlo tenemos que utilizar la opción de menú Construir->reconstruir el proyecto.

lec16 013

Ahora ya se habrá compilado correctamente si no nos hemos equivocado al escribir el código de nuestro programa. Ya podemos arrancar la simulación y observar que nuestra caja empieza cerrada (led rojo encendido).

lec16 014

Ahora ya podemos probar a meter la clave secreta en nuestro teclado pulsando sobre las teclas 2, 4, 6 y 8. Si lo hacemos en el orden correcto veremos cómo se abre nuestra caja fuerte (led verde encendido).

lec16 015

Para volver a cerrar la caja o para poner el contador de cifras a cero, sólo tenemos que pulsar los botones '*' o '#'.

Es evidente que no iremos muy lejos con nuestra caja fuerte si el funcionamiento real fuera como el que se muestra en esta lección. El programa sólo pretende mostrar el funcionamiento de un teclado matricial. 

Como ejercicio, proponemos en esta ocasión utilizar cualquiera de los otros teclados y realizar algún proyecto controlándolos desde nuestro Arduino.

Esperamos que la lección os haya resultado interesante.

 


Esta página es propiedad de Hubor.

2014.

Guardar
Preferencias de usuario para las Cookies
Estamos utilizando las cookies para asegurarnos de que le podemos ofrecer el mejor servicio posible en este sitio web. Si rechaza el uso de las cookes, es posible que esta web no funcione como estaba previsto por el equipo de programadores.
Aceptar todo
Rechazar todo
Leer más
Estadísticas
Las cookies de preferencias permiten a la página web recordar información que cambia la forma en que la página se comporta o el aspecto que tiene, como su idioma preferido o la región en la que usted se encuentra.
Google Analytics
Aceptar
Rechazar