MES – 1 V1.6
Minimal Experimental Shield – 1 Versión 1.6
Para Arduino Duemilanove y compatiblesSalvador García Bernal
M.C. en Electrónica y Sistemas Digitales Lic. en Ing. Electrónica y Computadoras
Primera Revisión: Noviembre 2009 Segunda Revisión: Diciembre 2009
Todas las marcas son propiedad de sus respectivos dueños.
Attribution Non-Commercial Share Alike
This license lets others remix, tweak, and build upon your work non-commercially, as long as they credit you and license their new creations under the identical terms. Others can download and redistribute your work just like the by-nc-nd license, but they can also translate, make remixes, and produce new stories based on your work. All new work based on yours will carry the same license, so any derivatives will also be non-commercial in nature.
2 - Ejemplos Prácticos A continuación el lector podrá realizar diversas prácticas que le darán idea de los posibles experimentos realizables con la tarjeta MES-1. Al final de cada ejemplo se propone una práctica para reafirmar lo discutido. Para realizar estos ejemplos requiere del entorno de desarrollo Arduino , así como una tarjeta compatible con Arduino Duemilanove.
2.1 - Contador Binario de 4 Bits (1 Nible) 2.1.1- Manejo de puertos y bits
En Arduino es posible configurar puertos usando la instrucción: DDRX
La cual tomará el puerto X para configurarlo de: salida 0 ó entrada 1. Así, suponga que el puerto B se desee de salida tendríamos:
DDRB = B11111111 ó DDRB = OxFF
Claro esta, que si se requiere que cada dos puertos sean entrada ó salida tendríamos: DDRB = B11001100 ó DDRB = 0xCC
Si se requiere leer todo el puerto la instrucción: PINX Se usaría como:
C = PINB
Si se requiere leer sólo un bit, el bit 3 del puerto C, se puede recurrir a: D = digitalRead(17);
Como sabemos, el uso de 1 ó 0 nos darán infinidad de combinaciones dependiendo de la longitud de la palabra. Para un número definido en Arduino como por ejemplo:
Su representación binaria interna, en termino de registros será representada como:
A 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Suponga que el registro A, desea recorrerse 4 posiciones a la izquierda, por lo que el resultado será:
A <<= 3
A 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Considere que el registro A sufre una transformación de incremento, tal como: A += 1
A 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Para leer el bit 4 del registro A tendremos que:
bitRead(A, 4); De igual manera para escribir un 1 en el bit 2 del registro A:
bitWrite(A, 2, 1);
A 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
2.1.2 – Diseño del código
El pseudo-código a realizar está basado en algunas de las operaciones binarias que se mencionaron anteriormente. Para ello podemos realizar:
1. Configuramos Puerto D de salida D7 – D4
2. Aprovechando el loop(), no requeriremos de otro ciclo 3. Reviso si la cuenta a llegado a su límite
4. Si puedo contar, continua 5. Envío datos al Puerto D 6. Incremento registro 7. Espero un tiempo
8. Si no se puede contar, limpio registro. 9. Regreso a 2
De esta forma, tenemos a grandes rasgos lo que se requiere para realizar el programa principal, por lo que podemos escribir el código 2.1, como se muestra a continuación.
int x = 0; void setup(){ DDRD = B00001111; } void loop(){ if (x == 0x10){ x = 0; }else{ PORTD = x; x+=1; delay (500); } }
Código 2.1 – Contador Binario de 1 nible.
2.1.3 Uso de funciones para manejo de puertos
En Arduino tenemos funciones para manejar puertos tal como: pinMode (3,OUTPUT);
pinMode (4,INPUT);
Los números de los puertos pueden ser definidos con nombres, esto antes del setup(), tal como:
int PDSal = 3; int PDIn = 4;
Con lo que indicamos que el pin 3 de la tarjeta será de salida y el pin 4 será de entrada. Para configurar todo el puerto D que corresponde a los pines: 0 – 7, de salida tendremos:
int PD [] = {0,1,2,3,4,5,6,7}; void setup(){
for (int i = 0; i<=7; i++) { pinMode(PD[i], OUTPUT); } } void loop(){ }
En caso de leer o escribir a un pin, para escribir se usa: digitalWrite(3,1); Para leer un pin tenemos:
digitalRead(PDIn);
2.1.4 – Propuesta de práctica
– Diseñe un programa de contador binario usando un ciclo for, el cual repita la cuenta sólo 3 veces de manera ascendente - descendente.
2.2 - Contador de números
2.2.1 – Representación de números en display
La tarjeta MES-1 cuenta con un display de 7 segmentos (pin 13 – 10) que fácilmente puede usarse con el diseño de una función que contiene las combinaciones necesarias para mostrar el dígito actual. En la figura 2.1 puede verse el código binario necesario para crear la función. p4 p5 p6 p7 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 0 0 1 1 1 1 0 0 0 1 0 0 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 1 1 1 1 0 1 2 3 4 5 6 7 8 9 c ! u c t
La función se implementa usando digitalWrite(), para poner en 1 ó 0 los pines del microcontrolador que serán usados por el decodificador. En el caso de poner el número 5 tendremos: int p4 = 13; int p5 = 12; int p6 = 11; int p7 = 10; void disp(int k){ switch (k) { case 5: digitalWrite (p4,0); digitalWrite (p5,1); digitalWrite (p6,0); digitalWrite (p7,1); break; } }
Código 2.3 – Configuración del display. Otra forma de realizar lo mismo se muestra en el código 2.4.
int pDisp[] = {13,12,11,10}; void disp(int k){ switch (k) { case 5: PORTB = 0X28; break; } }
Código 2.4 – Configuración del diaplay.
2.2.2 – Diseño del programa
Para este caso, consideremos que se requiere contar del número: 0 – 9 , los cuales deben mostrarse en el display. Para ello se puede usar dos ciclos for para la parte ascendente – descendente. El código principal se muestra en el código 2.5. Para la función disp() puede usar cualquiera de los código 2.3 ó 2.4.
void loop(){ int i;
for (i = 0; i<= 9 ; i++){ disp(i);
delay(250);
}
for (i = 9; i>=0; i--){ disp(i);
delay(250);
} }
Código 2.5 – Código principal del contador numérico.
2.2.3 – Propuesta de práctica
– Diseñe un contador de números pares (ascendente) e impares (descendente) usando el código anterior.
2.3 - Simulando interrupción con un PushButton 2.3.1 – Simulando interrupción
Una interrupción es una llama a una sub-rutina especial cuando un proceso externo inesperado pero considerado se presenta. Para simular este comportamiento, se requieren guardar manualmente las variables actuales que se encontraban en uso, para que una vez finalizada la rutina de interrupción retome los datos anteriores para continuar con el algoritmo que se estaba ejecutando.
Para ejemplificar esto, usaremos el PushButton del MES-1 (SW1, pin 8) que actuará como una acción externa. Se diseña un programa que genere los primeros 6 números de la sucesión Fibonacci . Cuando el botón se active, realizará una acción visual en los LEDs. 2.3.2 – Diseño del programa
La sucesión Fibonacci se define como:
fi=
!
01 ii=0=1f!i"2 #$ f!i"1# i%1
#
Como se observa implementarla es relativamente sencilla. Cuando el botón este en 0 lógico entra a una rutina que genera un efecto luminoso en los LED tal como se ilustra en la figura 2.2.
Figura 2.2 – Secuencia de encendido.
Para esto puede usarse una función similar al código 2.3 o 2.4. Aquí se tiene que usar el puerto D entre: 4 – 7 (pin 7 - 4). Una manera interesante de realizar esto se ilustra en el código 2.6. Como puede verse se usa una variable que tanto sirve para entrar en el case así como para seleccionar los LED a usar.
El código principal para realizar la sucesión se observa en el código 2.7. Observe que se usan vectores para almacenar los valores anteriores. Esto es la propia naturaleza de un filtro básico usando la sucesión Fibonacci.
int pLED[] = {7,6,5,4}; void leds(int k){ int i; if ( k == 1) { k = 1; i = 0; }else if(k == 2) { k = 1; i = 1; } switch (k) { case 0: for ( i = 4; i <= 7; i++ ) { digitalWrite(pLED[i],0); } break; case 1: digitalWrite(pLED[4+i],1); digitalWrite(pLED[7-i],1); break; } }
int F[]={0,0,0,0,0,0}; int S = 0; void loop() { if (Pbutton == LOW) { if (S == 6){ S = 0; }
for (int i = S; i <= 6; i++){ if (i < 2) { disp(i); F[i] = i; S = i; delay (500); } else {
F[i] = F[i-2] + F[i-1]; S = i; disp(F[i]); delay (500); } } } else { for (int j = 0; j <= 2; j++){ leds(j); delay (500); } } }
2.3.3 – Propuesta de práctica
– Diseñe un programa que cuente de 0 – 4, al presionar el botón de interrupción genere un efecto correspondiente al numero actual de la interrupción usando la idea del código 2.6, así como el código 2.7. Las secuencias para los LED se ilustran en la figura 2.3.
Figura 2.3 – Secuencias para los LEDs.
2.4 - Nible Vumeter con Potenciómetro 2.4.1 – Manejo de puertos Analógicos
La tarjeta MES-1 tiene una conexión directa al puerto analógico 0, llamado An0. Para configurar el puerto tenemos:
Data = analogRead(An0); Previamente An0 se define como:
El potenciómetro tiene la opción de usar dos voltajes: 3.3 V (Fig. 2.4 - 1) ó 5 V (Fig. 2.4 - 2). En la figura 2.4 se ilustran las posiciones del jumper para seleccionar el voltaje adecuado. Se debe tener cuidado de nunca conectar el jumper en la posición cuatro (Fig. 2.4 - 4), esto puede provocar un corto circuito, cuando no se use algún voltaje con poner el jumper en la posición tres (Fig. 2.4 - 3) es adecuado.
Figura 2.4 – Posiciones para el jumper. 2.4.2 – Diseño del programa para el Vumeter
La acción de los LED se basa en el código 2.3 – 2.4. Para ello obtenemos el código 2.8. int pLED[] = {7,6,5,4}; void vleds(int k){ switch (k) { case 0:
for (int i = 4; i <= 7; i++ ) { digitalWrite(pLED[i],0); } break; case 1: digitalWrite(pLED[4],1); break; case 2:
for (int i = 4; i <= 5; i++ ) { digitalWrite(pLED[i],1); }
break; case 3:
for (int i = 4; i <= 6; i++ ) { digitalWrite(pLED[i],1); }
break; case 4:
for (int i = 4; i <= 7; i++ ) { digitalWrite(pLED[i],1); }
break; } }
Código 2.8 – Configuración de los LEDs para Vumeter.
Para leer el valor analógico, tenemos que adecuar los valores entre 0 – 255, el convertidor tiene una resolución de 1024 , para ello con la instrucción:
Data = map (Data,0,1024,0,255); Con estos valores umbralizamos para los cuatro posibles estados:
Um = 255 / 4;
Así obtenemos la siguiente tabla 2.1, con estos valores podemos generar el código principal como el mostrado en el código 2.9. Para este caso se usa el jumper con 5 Volts de alimentación.
Tabla 2.1 – Valores para umbral.
Umbral Estado 0 – 63 64 – 127 128 – 191 192 – 255 1 2 3 4
void loop(){
int San0 = analogRead(An0); San0 = map(San0,0,1024,0,255); if ((San0 >= 0) & (San0 <= 63) ) { vleds(0);
vleds(1); delay (250);
}else if ((San0 >= 64) & (San0 <= 127) ) { vleds(0);
vleds(2); delay (250);
}else if ((San0 >= 128) & (San0 <= 191) ) { vleds(0);
vleds(3); delay (250);
}else if ((San0 >= 192) & (San0 <= 255) ) { vleds(0);
vleds(4); delay (250); }
}
Código 2.9 – Programa principal para Vumeter. 2.4.3 - Propuesta de práctica
- Considere el programa del código 2.9, pero usando un voltaje de 3.3 V.
2.5 - Control de un servo con Potenciómetro 2.5.1 Manejo de un servo
Un motor ampliamente usado por algunos robots pequeños son los servos. Estos dispositivos vienen con una circuitería interna lista para ser conectados a cualquier microcontrolador. Estos vienen con 3 hilos, uno de ellos es conocido como señal principal (S1), normalmente de color amarillo o naranja. Para controlarlo, se requiere la librería:
#include <Servo.h>.
Para configurar el servo , se usa la función: MiServo.attach(), la cual recibe el pin de conexión de la señal principal del servo. Esta señal puede ser cualquier pin que soporte PWM. La tarjeta MES usa el pin 9. En la figura 2.6 se observa la zona para conectar el servo.
Figura 2.6 – Conector para el servo en la tarjeta MES. 2.5.2 Diseño del programa para el servo
Se modifica el código 2.9, que usa el potenciómetro; con el cual controlaremos las posiciones del motor. Para ello observe el código 2.10. Como puede verse el attach() se realiza en el setup(). Con la función:
MiServo.write(x);
Dónde (x) indica los grados que se moverá el motor, este es un valor entero comprendido entre 0 – 180. #include<Servo.h> Servo MiServo; int S1 = 9; void setup(){ MiServo.attach(S1); } void loop(){
int San0 = analogRead(An0); San0 = map(San0,0,1024,0,255); if ((San0 >= 0) & (San0 <= 63) ) { MiServo.write(0);
delay (250);
}else if ((San0 >= 64) & (San0 <= 127) ) { MiServo.write(45);
delay (250);
}else if ((San0 >= 128) & (San0 <= 191) ) { MiServo.write(90);
delay (250);
}else if ((San0 >= 192) & (San0 <= 255) ) { MiServo.write(135);
delay (250); }
}
2.5.3 Propuesta de práctica
- Diseñe un programa similar al expuesto, que use el PushButton como contador: llegue a 90 grados, llegue a 135 grados, llegue a 0 grados.
2.6 - Medidor básico de intensidad lumínica 2.6.1 - Circuito para intensidad lumínica
Para este ejemplo se usa una fotorresistencia que servirá como sensor para detectar los cambios de intensidad en un cuarto. Para ello se usa el circuito de la figura 2.7. Para el programa se usará el display que indicará la intensidad entre 0 – 9. Para ello usaremos el siguiente valor para el umbral:
Um = 255 / 9
Este programa puede realizarse usando el código 2.9 para sensar el estado del LDR y el código 2.3 ó 2.4, para mostrar los dígitos en el display.
Figura 2.7 – Circuito para usar la fotorresistencia.
2.6.2 – Propuesta de práctica
2.7 - Medidor de temperatura con diodo 2.7.1 Diseño del Circuito
Se usa un diodo 1N4001 que puede ser usado como sensor de temperatura. Esto es gracias a la respuesta lineal con la que cuenta así como al coeficiente de temperatura de 2.3 mV/C. En pruebas extensas se ha demostrado que la respuesta del diodo es satisfactoria como medidor de temperatura. Para lograr ello, se requiere tener una corriente constante usando un resistor o potenciómetro. El circuito que puede usarse se muestra en la figura 2.8.
Figura 2.8 – Circuito para conectar el diodo.
2.7.2 Diseño del programa
Para diseñar el programa tendremos que obtener valores referentes a temperatura – voltaje, esta caracterización nos servirá para encontrar la relación que describa el comportamiento del diodo. En la tabla 2.2 se observa el comportamiento a un voltaje de 5.1 Volts. Gráficamente puede verse el comportamiento lineal del diodo.(Fig. 2.9). Con está información es posible encontrar una ecuación característica usando una regresión lineal.
Tabla 2.2 – Valores obtenidos de Temperatura – Voltaje. Temperatura (°C) Voltaje en Diodo 2 12 22 32 42 52 62 72 82 92 0.727 0.709 0.693 0.673 0.655 0.638 0.617 0.599 0.581 0.563
Figura 2.9 – Gráfica Voltaje – Temperatura para el diodo. Considerando:
y=y2" y1
x2"x1
! x"bx#$by (2)
Aplicando a los datos obtenidos de la tabla 2.2, se obtiene:
y="552.9! x"0.563#$92 (3)
Gratificando la ecuación (3), obtenemos una aproximación al comportamiento del diodo como el mostrado en la figura 2.10.
Figura 2.10 – Gráfica obtenida de la regresión lineal junto con los datos obtenidos del diodo.
Con estos datos es posible obtener un voltaje por medio del ADC del microcontrolador para obtener su correspondiente valor de temperatura.
Para esto el dato obtenido se mapea a 1024 de resolución con 1 Volt de base. Por lo que la ecuación (3) resulta en una nueva ecuación definida como:
y="0.539! x"576#$92 (4)
Si aplicamos está ecuación a valores conocidos mapeados a 1V, obtenemos la tabla 2.3, estos valores se redondean con la función:
round(); Para la obtención de los datos, tenemos:
Data = analogRead(An0); Data = map(Data,0,1,0,1024);
Tabla 2.3 – Valores de prueba del ADC. Voltaje en Diodo Temperatura (°C) 744 645 576 2 54 92
Una vez que se obtienen los valores de temperatura, es posible desplegar estos datos en un solo display , para ello usaremos el siguiente algoritmo.
DF1 = D1 / 10; DF2 = D1 % 10;
Como puede verse la segunda operación involucra el modulo del número, esto es: DF2 = D1 – (DF1*10);
DF1,DF2 son variables de tipo int , para el caso de 54 grados tendremos: DF1 = 54 / 10 = 5
DF2 = 54 – (5 * 10) = 4 2.7.3 – Propuesta de práctica
2.8 - Envío - recepción de procesos físicos por puerto USB 2.8.1 – Comunicación serial.
Uno de los aspectos importantes que tienen los microcontroladores, es poder comunicarse entre ellos o con alguna computadora. Una vez que está conectada con una computadora las posibilidades son infinitas. Los datos recolectados por el microcontrolador podrán ser visualizados, de manera local o remota; pudiendo estar conectado a internet o con algún otro dispositivo móvil.
Para lograr una comunicación serial, se tiene que considerar una buena tasa de transferencia. El microcontrolador Atmel soporta velocidades de 2400 a 57600 kbps. Uno de los primeros pasos para configurar el puerto serial está dado por la función: Serial.begin(), el cual cuenta como argumentos la velocidad, que pueden ser: 300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 ó 115200.
Otra de las funciones involucradas, es cuando un dato sea recibido, este se encontrará en el buffer de llegada, cuando pasa esto se activa una bandera que indica que existe un nuevo dato de entrada. Para monitorear esto se usa la función: Serial.available(). Cuando existe un dato nuevo, este recibe una bandera en alto, por lo que el valor del buffer será mayor que cero. Una vez que el dato está en el buffer, este se puede leer, para ello se usa la función: Serial.read(), que recibirá un dato de tipo int. Cuando se requiere enviar un dato del microcontrolador a la computadora, la función:
Serial.print() o Serial.println() pueden usarse.
En la tabla 2.4 se observa los tipos de datos soportados por ambas funciones. Para ejemplificar lo anterior, suponga que cuenta con un sensor conectado al pin0, antes de enviar el dato, este debe ser condicionado entre 0 – 255, luego debe enviarse por puerto serial a la computadora cada segundo. Para ello observe el código 2.11.
Tabla 2.4 - Datos soportados por la función Serial.print() y Serial.println(). Datos soportado Descripción
DEC HEX OCT BIN BYTE Dato Decimal Dato Hexadecimal Dato Octal Dato Binario Palabra ASCII int SenADC =0; int temp; void setup(){ Serial.begin(9600); } void loop(){ temp = analog.Read(SenADC); temp = map (temp,0,1024,0,255); Serial.println(temp);
delay (1000); }
Para visualizar los datos provenientes del microcontrolador, en el entorno de desarrollo de Arduino, presionamos el botón: Serial Monitor. con ello obtendremos una ventana como la mostrada en la figura 2.11, en la cual se puede seleccionar la tasa de transferencia, para el ejemplo anterior: 9600 bauds. La selección del puerto es automática, la cual corresponde al puerto que se usa para descargar el programa a la tarjeta.
Figura 2.11 – Ventana de monitoreo serial de Arduino.
2.8.2 – Propuesta de práctica
- Diseñe un programa basado en el código 2.11 y el código que diseño en la sección 2.7.2, para enviar datos seriales de temperatura tanto en grados centígrados – grados kelvin.
Bibliografía
Banzi, Massimo, Getting Started with Arduino. 2008, O !Reilly, USA. García, Bernal. S. Arduino Diseño y Aplicaciones. 2009, México. Internet: Arduino Reference, http://arduino.cc/en/Reference/Extended Internet: Processing Reference, http://processing.org/reference/
Salvador García Bernal © 2009