Taller de Interrupciones Organización del Computador 1 Verano 2016 Introducción Para este taller se propone utilizar un ArduinoUNO como unidad de procesamiento principal para controlar un robot que sea capaz de seguir una lı́nea blanca sobre un fondo negro. El desarrollo del mismo se realizará utilizando el simulador web www.123d.circuits.io. El robot El robot que se va a utilizar posee un ArduinoUNO, dos sensores infrarrojos, un pulsador y dos motores controlados por un driver (ver Fig. 1): El ArduinoUNO: es una placa de desarrollo que tiene como unidad de procesamiento un microcontrolador ATmega328. Esta placa cuenta con terminales (o pines) a las que podemos conectar sensores, motores, LEDs, etc. El ArduinoUNO cuenta también con un LED incorporado en la placa que está asociado al pin 13, este LED puede ser utilizado, por ejemplo, para debuguear el código. Los motores: sirven para mover el robot en lı́nea recta (los dos motores con igual velocidad) o doblando en algún sentido (diferentes velocidades en cada motor). El driver: manejan la corriente que necesitan los motores. En nuestra simulación los motores seleccionados cuentan con drivers internos, por lo que no es necesario incluirlos en el circuito. Los sensores infrarrojos: permiten medir cambios de color en el suelo. Cuando detectan un cambio de color estos sensores dispararán una interrupción. El pulsador: es un dispositivo de entrada que se deberá leer por polling. Sensores Infrarrojos Motores Arduino UNO Driver Bumper o Boton Figura 1: El robot El simulador Para simular el robot se utilizarán componentes similares a los del mismo en un el simulador web www.123d.circuits.io. Pero antes es necesario hacer una introducción a los componentes que se utilizarán. 1 Figura 2: Protoboard El Protoboard es una plataforma de pruebas que permite interconectar componentes electrónicos sin la necesidad de utilizar soldadura. Las conexiones se hacen por medio de láminas metálicas internas que ejercen presión sobre los terminales de los componentes. Algunos agujeros se encuentran conectados horizontalmente y otros verticalmente (ver Fig. 2). Los cables son los elementos por los cuales circula la corriente eléctrica. Utilizaremos la siguiente convención de colores: rojo para los cables de 5V negro para los cables de 0V (masa) amarillo para las conexiones de entradas al ArduinoUNO naranja para las conexiones con los motores verde para conexiones internas del protoboard Las resistencias sirven para limitar la corriente que circula por un circuito y ası́ evitar que se queme un componente. Los botones o switches no son más que simples interruptores. Existen dos tipos, dependiendo si mantienen el valor una vez que se sueltan (funcionamiento similar al de una llave de luz) o no (funcionamiento similar al de un timbre). Antes de empezar Para poder utilizar el simulador se debe ingresar a la página web www.123d.circuits.io y crear una cuenta. Al ingresar, hacer click en + New y elegir la opción New Electronics Lab. Esto nos llevará a una página similar a la de la Fig. 3. En dicha Fig. se puede observar la interfaz del simulador. El mismo posee un protoboard y distintos controles que permiten agregar componentes en la pestaña + Components. Para este taller utilizaremos los componentes que figura en la sección Arduino Basic Kit. Cuando se agrega un ArduinoUNO en el simulador se habilita la opción de editar, cargar y ejecutar código desde la pestaña Code Editor. Además en la ventana Code Editor se puede acceder al Serial Monitor donde podremos ver salida de debug. Para comenzar la simulación es necesario tocar el botón Start Simulation. Funciones importantes El lenguaje Arduino está basado en C/C++. Referencia en www.arduino.cc/en/Reference/HomePage. Las funciones relevantes para este taller son: unsigned long millis() devuelve el número de milisegundos desde que el arduino comenzó a correr el programa. void attachInterrupt(digitalPinToInterrupt(pin), ISR, mode) : permite setear una interrupción, pin es la entrada del arduino a la cual está conectada la interrupción. ISR es el nombre de la función que va a atender la interrupción, mode es el tipo de interrupción, en este taller siempre se utilizará CHANGE. void analogWrite(pin, value) es el mecanismo que cuenta el ArduinoUNO para tener una salida analógica. En pin se emite el valor value en el formato PWM (Pulse Width Modulation, en español: Modulación por ancho de pulsos). La señal analógica que se emite puede variar entre el valor mı́nimo 0 (que corresponde a 0V) y el máximo 255 (que corresponde a 5V). En el caso del taller el 0 corresponde a el motor detenido y el 255 a máxima potencia. 2 Figura 3: Interfaz del simulador int digitalRead(pin) lee el valor de una entrada digital pin, puede ser un 1 ó un 0. Serial.begin(speed) configura e inicializa la comunicación serial a la velocidad dada por speed. En el taller utilizaremos speed=9600 Serial.println(‘‘Esto es una salida de debug’’) imprime texto en el puerto serial. Consigna 1) a) Construir el circuito que se muestra en la Fig. 4 y testear que los LEDs se prendan cuando se pulsan o activan los botones (no hace falta cargar código en el ArduinoUNO para esto). Nota: no usar el protoboard que viene originalmente. ArduinoUNO led pulsador motor left resistencia motor right botón 1 botón 2 Figura 4: Circuito completo Observaciones: las resistencias deben ser configuradas como de 220Ω (220 Ohms). el pulsador solo mantiene el valor mientras lo tenemos presionado los botones 1 y 2 mantienen su valor una vez que los soltamos algunos componentes tienen polaridad, con lo cual no son reversibles. Prestar especial atención a como se deben conectar. 3 Utilizaremos el pulsador para simular el pulsador del robot y los botones 1 y 2 para simular el comportamiento de los sensores infrarrojos. b) Utilizar el código de ejemplo anexo a este enunciado para testear la simulación. 2) Se deberán programar 3 comportamientos distintos de un robot siguiendo una lı́nea. En todos los casos el robot siempre deberá tener guardado lo que está haciendo, llamaremos a esto estado interno. Las configuraciones posibles de los motores son: Avanzando ambos motores deben ser configurados con potencia 75 Girando izquierda se debe configurar el motor izquierdo con potencia 35 y el derecho con potencia 130 Girando derecha igual al anterior pero intercambiando los motores Detenido ambos motores reciben cero como potencia Los pines del ArduinoUNO están conectados de la siguiente manera: pin 2 3 5 6 9 10 12 Conexión Sensor izquierdo Sensor derecho Potencia motor derecho Masa motor derecho Masa motor izquierdo Potencia motor izquierdo Pulsador Los pines que van a masa de un motor deben setearse inicialmente en cero y no modificarse. Por último, el control de los motores se debe realizar en la rutina principal y no en una rutina de atención de una interrupción. Comportamiento 1 Utilizando 1 sensor infrarrojo (Sensor izquierdo) y el pulsador programar el ArduinoUNO para lograr el siguiente comportamiento: El robot deberá alternar entra las dos configuraciones de los motores: Girando izquierda y Girando derecha para poder seguir la lı́nea. Se inicia en Girando derecha y se mantiena hasta que el sensor detecte un cambio (y produzca una interrupción). En ese momento debe cambiar a Girando izquierdahasta la próxima interrupción. Y ası́ sucesivamente. El pulsador debe comportarse como interruptor ON/OFF, es decir, cuando el pulsador esté apretado el robot debe detenerse (configurado como Detenido) y cuando se suelta el robot debe seguir en el estado que estaba (siguiendo la lı́nea). A continuación se muestran las dos máquinas de estados (ver Fig. 5) que reflejan el comportamiento esperado. Figura 5: Comportamiento 1 Comportamiento 2 Utilizando los 2 sensores infrarrojos y el pulsador programar el ArduinoUNO para lograr el siguiente comportamiento: 4 El robot arrancará configurado en Avanzando. Cuando detecte que se desvı́a de la lı́nea deberá corregir su rumbo. Para ello, si el sensor izquierdo empieza a sensar blanco (lo cual produce una interrupción), se debe modificar a girando izquierda hasta que el mismo sensor vuelve a sensar negro (produciendo nuevamente una interrupción). Lo mismo sucede si el sensor que provoca una interrupción es el sensor derecho. El pulsador debe comportarse como interruptor ON/OFF, es decir, cuando el pulsador esté apretado el robot debe detenerse (configurado como Detenido) y cuando se suelta el robot debe seguir en el estado que estaba (siguiendo la lı́nea). A continuación se muestra las dos máquinas de estados (ver Fig. 6) que reflejan el comportamiento esperado. Figura 6: Comportamiento 2 Comportamiento 3 Utilizando los 2 sensores infrarrojos y el pulsador programar el ArduinoUNO para lograr el siguiente comportamiento: El robot arrancará configurado en Avanzando. Cuando detecte que se desvı́a de la lı́nea deberá corregir su rumbo. Para ello, si el sensor izquierdo empieza a sensar blanco (lo cual produce una interrupción), se debe modificar a girando izquierda hasta que el mismo sensor vuelve a sensar negro (produciendo nuevamente una interrupción). Lo mismo sucede si el sensor que provoca una interrupción es el sensor derecho. el pulsador debe comportarse como una pausa de 3 segundos, es decir, que cuando el pulsador es activado el robot configurarse como Detenido por 3 segundos y luego continuar con su comportamiento anterior. A continuación se muestra las dos máquinas de estados (ver Fig. 7) que reflejan el comportamiento esperado. Figura 7: Comportamiento 3 Anexo Código de ejemplo 1 2 3 4 5 / * Constantes que no cambian y son usadas para setear el numero de los pines * / # define motorPinA 5 // el numero del pin de uno de los cables de motor # define motorPinB 6 // el numero del pin del otro cable del motor # define sensorInt 3 // el numero del pin del sensor que i n t e r r u m p e 5 6 # define sensorPoll 2 // el numero del pin del p u l s a d o r 7 8 9 10 11 / * variables que cambian su valor : * / bool andando = true ; bool nitro = false ; 12 13 14 15 16 17 18 19 / * setup inicial * / void setup () { pinMode ( sensorPoll , INPUT_PULLUP ) ; / / i n i c i a l i z o e l p i n p u l s a d o r c o m o u n a e n t r a d a pinMode ( motorPinA , OUTPUT ) ; // i n i c i a l i z o el pin A del motor como una salida analogWrite ( motorPinA ,0) ; // pongo en 0 el pin A del motor pinMode ( motorPinB , OUTPUT ) ; // i n i c i a l i z o el pin B del motor como una salida Serial . begin (9600) ; // c o n f i g u r o e i n i c i a l i z o la c o m u n i c a c i o n serial a 9600 baudios // c o n f i g u r o la i n t e r r u p c i o n del pin meInterrumpieron // cuando se p r o d u c e un cambio 20 21 para que llame a la f u n c i o n attachInterrupt ( digi talP inToI nter rupt ( sensorInt ) , meInterrumpieron , CHANGE ) ; 22 23 sensorInt } 24 25 26 27 28 29 / * loop del programa * / void loop () { if ( andando ) { if ( digitalRead ( sensorPoll ) ) { nitro = true ; // P r e g u n t o si estoy en estado a n d a n d o // P r e g u n t o si el p u l s a d o r esta a p r e t a d o // Si estoy en estos e s t a d o s pongo seteo el estado nitro Serial . println ( " nitro = true " ) ; / / } else { // nitro = false ; // Serial . println ( " nitro = false " ) ; / / } 30 31 32 33 34 35 } actualizarEstado () ; 36 37 motores 38 dependiendo del estado M u e s t r o por serial que cambie de estado Si el p u l s a d o r no esta a p r e t a d o p o n g o n i t r o c o m o false M u e s t r o por serial que cambie de estado // llamo a la f u n c i o n que manda definido potencia a los } 39 40 41 42 43 44 45 46 47 / * funcion que actualiza la velocidad de los motores dependiendo del estado * / void actualizarEstado () { if ( andando ) { // Si el estado es a n d a n d o if ( nitro ) { // y nitro es v e r d a d e r o analogWrite ( motorPinB ,250) ; // Seteo la v e l o c i d a d de el motor a 250 } else { analogWrite ( motorPinB ,30) ; // Si nitro == 0 e n t o n c e s pongo v e l o c i d a d de 30 a los motores } } else { analogWrite ( motorPinB ,0) ; } 48 49 50 51 52 53 // Si a n d a n d o no es v e r d a d e r o // freno el motor p o n i e n d o v e l o c i d a d 0 } 54 55 56 57 58 59 60 61 62 63 64 65 66 / * funcion que se llama cuando se produce una interrupcion * / void meInterrumpieron () { Serial . println ( " Interrupcion " ) ; // m u e s t r o por serial que hubo una i n t e r r u p c i o n if ( andando ) { // si estoy a n d a n d o andando = false ; // e n t o n c e s freno Serial . println ( " andando = false " ) ; / / y m u e s t r o p o r s e r i a l q u e f r e n e } else { // Si estoy f r e n a d o andando = true ; // c o m i e n z o a andar Serial . println ( " andando = true " ) ; / / y m u e s t r o p o r s e r i a l q u e c o m e n z e a a n d a r } } 6