Sei sulla pagina 1di 25

Universidad Nacional Mayor de San Marcos

Facultad de Ingeniería de Sistemas e Informática

Arquitectura de Computadoras

Laboratorio 6 – Interrupciones, Displays LCD(continuación)

I. Interrupciones Externas

Las interrupciones son sucesos que pueden ocurrir en cualquier momento por lo que no
podemos prever exactamente cuándo detectarlas. Por ejemplo, podemos colocar un
sensor en el frente de un vehículo para que detecte a distancia los posibles obstáculos que
encuentre en su camino y lo informe a la tarjeta cambiando el estado de un Pin.

En principio, podemos poner en determinado punto del bucle, leer el estado del pin, de
forma que el cambio de estado nos delatara la existencia del obstáculo y de encontrarlo,
cambiar la trayectoria del vehículo para esquivarlo. Si en recorrer todo el bucle se tarda
milisegundos, la cosa funcionará muy bien, pero imaginemos que en un punto del bucle
hemos puesto una función “delay” que hace detener el funcionamiento del bucle por un
tiempo, igual que hacíamos en el programa “blink”.

Ocurre entonces que el bucle no se recorre en un corto periodo de tiempo, sino que
puede tardar a lo mejor un minuto. Como Arduino tarda un minuto en volver a leer el
estado del pin, no se enterará de la existencia del obstáculo en ese tiempo, tiempo más
que suficiente para que el vehículo choque con el obstáculo. ¿Cómo resolvemos el
problema?.

Para resolver este tipo de problemas existe varios medios, pero el más sencillo es el
previsto para el manejo de interrupciones y para ello Arduino Uno tiene dos de los pines
específicos, el Pin 2 y el Pin 3, (como se explicó en el capitulo 5 de la Introducción a
Arduino , capaces de detectar interrupciones en cualquier instante, independientemente
de lo que en ese momento esté haciendo la función principal “loop”, Cuando Arduino
detecta un cambio en el estado de uno de esos pines puede parar el funcionamiento del
bucle “loop”donde se encuentre y lanzar una función asociada a esa interrupción
inmediatamente, (en el modelo Arduino UNO, para continuar posteriormente el proceso
del bucle “loop” desde donde lo parara.
Así pues en nuestro vehículo, podemos asociar el detector de obstáculos al Pin2, (o al
Pin3), y tan pronto como se detecte un obstáculo y cambie el estado del Pin2, se puede
lanzar una función que por ejemplo haga retroceder al vehículo y cambie el rumbo con el
fin de eludir el obstáculo.

Ejemplo1

Es una especie de costumbre en Arduino, usar un pulsador para ilustrar el concepto de


Interrupción, así que nos plegáramos a ello. Vamos a utilizar un típico circuito para leer un
pulsador con un pullup.

Hasta ahora habríamos escrito el programa para leerlo así,

void setup()
{ pinMode(2, INPUT);
Serial.begin(9600);
}

void loop()
{ bool p = digitalRead(2);
Serial.println(p);
}
El resultado seria normalmente 1, por el pull up y la lectura bajaría a 0, al pulsar el botón.
Nada nuevo en esto.

Pero vamos a reescribir el programa para  establecer una interrupción en el pin 2


(Interrupción 0) .El programa quedara más o menos así:

int contador = 0;
int n = contador ;

void setup()
{   Serial.begin(9600);
attachInterrupt( 0, ServicioBoton, FALLING);
}
void loop()
{
if (n != contador)
{     Serial.println(contador);
n = contador ;
}
}

void ServicioBoton()
{ contador++ ;
}

En primer lugar fijaros que hemos eliminado la definición de entrada del pin 2, porque no
vamos a usarlo como input estrictamente. Con definir la interrupción es suficiente.

En segundo lugar usamos attachInterrupt() pasándole como parámetros la interrupción 0,


que es el pin2 de Arduino UNO. SI fuese la Interrupción 1, la conectaríamos al pin 3 (Anda
que…)

Le pasamos el nombre de la función CallBack ServicioBoton, que es de lo más sencilla. Un


variable global contador, guarda el número de pulsaciones. Lo único que hace la función
de servicio es aumentar contador en uno cada vez que se pulsa y volver.

Y por último el trigger es FALLING porque el estado es normalmente HIGH y baja a LOW al
pulsar el botón,  utilizaremos el disparo con el flanco de bajada, o sea FALLING o LOW.

 Un disparador en inglés es trigger y es el nombre que encontrareis en la jerga


técnica.
 

El loop comprueba si el número de pulsaciones ha cambiado y si es así lo imprime, pero


puede dedicarse a hacer cualquier cosa, porque no vamos a perder ninguna pulsación.

Cuando veamos la salida en la consola tendremos una sorpresa esperada:

Cuando pulsamos el botón, el número que aparece no aumenta de uno en uno si no a


golpes. ¿Por qué?

Pues como ya vimos en su día, se debe a los rebotes del pulsador que para eliminar el
rebote de los botones, tenemos que hacer el debouncing y para eso se puede hacer con
un delay de 250 ms.

Pero vamos a tener un problema. No podemos usar un delay dentro de una interrupción.
No funciona. ¿Cómo dice?

Hay varias consideraciones a tener en cuenta con las interrupciones: 

 Haz lo que quieras pero no te demores. Acaba cuanto antes y lárgate.


 Hay cosas que no funcionan, como las funciones delay (), millis () y cualquier cosa
que dependa de interrupciones o timers.
 Ni se te ocurra meter un Serial. Nada en una interrupción, son muy lentos (Y
además tampoco funcionan porque también dependen de interrupciones).
 Debes entender que una interrupción es como un estado de excepción, que se
puede usar sin reparos, pero entendiendo que hay que hacer el trabajo y salir
cuanto antes.
 De hecho, una ISR o función CallBack, no puede devolver parámetros ni tampoco
recibirlos

Además cuando definimos una variable global como contador, de la que depende una
función ISR, se recomienda definirla como volatile y no como una global normal. 

 Estrictamente hablando, volatile no es un tipo de variable, sino una directiva para


el compilador.
 Eso significa que la variable en cuestión, debe ser almacenado de un cierto modo
que evite algunos problemas raros que pueden surgir cuando una variable puede
ser cambiada por el ISR o por el programa (algo que no ocurre en nuestro ejemplo).
 Bajo ciertas circunstancias puede surgir un conflicto y volatile lo evita y por eso se
recomineda hacerlo así siempre.

Si se necesita un retraso para algo, o sea , un delay, siempre es mejor, comprobar el


tiempo transcurrido y decidir si se toma la acción o no. Veamos otro ejemplo

volatile int contador = 0; // Somos de lo mas obedientes


int n = contador ;
long T0 = 0 ;  // Variable global para tiempo

void setup()
{ pinMode(2, INPUT);
Serial.begin(9600);
attachInterrupt( 0, ServicioBoton, LOW);
}
void loop()
{   if (n != contador)
{   Serial.println(contador);
n = contador ;
}
}
void ServicioBoton()
{
if ( millis() > T0  + 250)
{   contador++ ;
T0 = millis();
}
}
En primer lugar definimos contador como volátil, por prescripción médica, y definimos
otra variable global T0 para almacenar el tiempo a partir del cual contaremos.

En la ISR la diferencia es que comprobamos si el valor actúan de millis es mayor en 250 ms


a la última vez que paso por la interrupción. Si no es así, lo consideramos un rebote y lo
ignoramos olímpicamente. Por el contrario si ha pasado un tiempo prudencial
incrementamos contador.

La ventaja d este sistema es que no congelamos el procesador con un delay, si no que le


dejamos seguir con su trabajo, atendiendo otras interrupciones, por ejemplo.

Pero se había dicho que millis() no funciona en las interrupciones.

Así es. Mientras una interrupción esta activa millis está congelada y su valor no cambiará,
pero sigue pudiéndose leer. 

 Mientras estas dentro de una interrupción, todas las demás interrupciones, son
ignoradas, por eso, nada que dependa de otras interrupciones funciona.
 Por eso es importante salir pronto, para garantizar que no nos perdemos nada de
interés.
 Mientras una interrupción está activa, millis() y micros()se congelan. Eso quiere
decir que si tienes unos cuantos miles de interrupciones por segundo (Como si estas
midiendo la frecuencia de una onda de audio) tu medida de tiempo con millis o
micros se puede ver distorsionada. 
Por ultimo conviene saber que existen algunas otras instrucciones relativas a las
interrupciones:

 noInterrupts(), Desactiva la ejecución de interrupciones hasta nueva orden.


 Interrupts(), Reinicia las interrupciones definidas con attachInterrupt().
 detachInterrupt( num Interrupt), Anula la interrupción indicada.

 
Ejemplo2

Como se ha indicado hay dos pines capaces de detectar en cualquier momento una
interrupción los pines 2 y 3 y lanzar instantáneamente una tarea diseñada para hacerla
frente. Por poner otro ejemplo, en un proyecto de vigilancia domótica, puedes poner un
sensor en la puerta de la calle, asociado al Pin2 y un detector de humo, asociado al Pin3 y
según se abran la puerta de la calle o se detecte humo, lanzar dos tareas diferentes. En
tarjetas de la familia Arduino más potentes existen más pines que controlan
interrupciones, por lo que en nuestro caso podrías por otras alarmas, como para
inundaciones, viento etc.

El montaje comporta dos  resistencias de 270 ohmios, un al lado del botón pulsador (así
si se pone por error el pin 2 OUPUT, no habrá desastres), la otra es la resistencia en
serie con el LED  que le permite funcionar a la tensión adecuada. Los hilos verdes están
conectados a tierra (GND).
El codigo para el uso de las interrupciones será:

/* ¿Las interrupciones estan activas a pesar de usar delay()


Cuando pulsamos el botón:
Cambia el estado del Pin 2 (pasa a baja)
La función de interrupción se activa
El estado cambia y el LED cambia de estado y se apaga
Cuando suelte el botón:
El Pin 2 cambia de estado
(Pasa a alta debido la efecto de recordar a más, el pull-up)
La función de interrupción se activa
El estado cambia y el LED cambia de estado y se enciende
*/

// Pin correspondiente al botón


int bzero = 2; // El Pin 2 corresponde al botón
// Pin correspondiente al LED
int ledPin = 4;
// La variable tipo volatile que comunica entre la
// función de interrupción y bucle principal (loop)
volatile int etat = HIGH;
void setup () {

// El pin conectado al botón se abre en modo INPUT


pinMode(bzero, INPUT); // Pin en modo entrada
// Activación de la resistencia interna pull-up
digitalWrite(bzero, HIGH);
// El pin está preparado para encender el LED
pinMode(ledPin, OUTPUT);
// La interrupción 0 monitorea los cambios de estado del pin 2.
attachInterrupt(0, inter0, CHANGE);

} // fin de setup

void loop () {

// No hacemos nada más que esperar …


delay (30000);

} // fin del bucle

void inter0 () { // Se ejecuta cuando se detecta un cambio de estado del Pin 2


etat =!etat; // Cambia el estado. De HIGH a LOW y de LOW a HIGH
digitalWrite (ledPin, etat); // encender o apagar el LED

} // fin inter0

Ejemplo3: Con 2 pulsadores

El programa anterior introduce varios conceptos nuevos. No hay variables "volátiles"


definidos o necesarios. Tanto INTR0 (DP2) y INTR1 (DP3) ambos tienen su propio rutinas
ISR llamadas ISR0 () y ISR1 () respectivamente. Hay subrutina no interrupción toggle ().

En este caso, ambas rutinas ISR simplemente llamaran a toggle () y dejaran que ella haga
el trabajo.

Ciertas funciones tales como delay () no funcionarán dentro de una función ISR así que es
una buena idea utilizar subrutinas independientes, siempre y cuando no sean demasiado
complejos o tomen mucho tiempo - otra interrupción rodando sobre una llamada de
subrutina ISR que no ha terminado podía ser interesante.

#define LED1 9
#define LED2 10

#define SW1 2

#define SW2 3

void toggle(byte pinNum) {

byte pinState = !digitalRead(pinNum);

digitalWrite(pinNum, pinState);

void setup() {

pinMode(LED1, OUTPUT);

pinMode(LED2, OUTPUT);

digitalWrite(LED1, 0); // LED off

digitalWrite(LED2, 0); // LED off

pinMode(SW1, INPUT);

pinMode(SW2, INPUT);

attachInterrupt(0, ISR0, FALLING);

// interrupt 0 digital pin 2 connected SW0

attachInterrupt(1, ISR1, RISING);

// interrupt 1 digital pin 3 connected SW1

void loop() {

// do nothing
}

// can't use delay(x) in IRQ routine

void ISR0() {

toggle(LED1);

void ISR1() {

toggle(LED2);

En adición de funciones que utilizan las interrupciones tales como delay () no funcionará si
se llama desde una rutina ISR. Por ejemplo, la versión modificada de toggle () que sigue no
va a funcionar si es llamado por un ISR, pero funciona bien si se llama desde loop () u otra
subrutina no interrupción. El LED seleccionado se encenderá durante dos segundos y
luego apagarse.

La rutina ISR mantiene el control de las interrupciones hasta que esté terminado y ejecuta
un "retorno" de comando de interrupción.

void toggle(byte pinNum) {

byte pinState = !digitalRead(pinNum);

digitalWrite(pinNum, pinState);

delay(2000);

pinState = pinState ^ 1;

digitalWrite(pinNum, pinState);

} // last brace is understood as return.


La verdadera solucion no es usar la funcion delay() sino hacer una propia rutina. El
delayMicroceconds() no requiere el uso de interrupciones, pero está limitado a unos
16,000 - 1000uSec. = 1mSec.; 1,000,000uSec. - 1 Sec. Simplemente usaro en loop:

void myDelay(int x) {

for(int i=0; i<=x; i++)

delayMicroseconds(1000);

Usese myDelay(5000) por delay(5000) y el programa trabajara.

Un ultimo ejercicio para el mismo circuito seria el siguiente codigo:

#define LED1 9

#define LED2 10

#define SW1 2

#define SW2 3

volatile byte flag0 = LOW; // declare IRQ flag

volatile byte flag1 = LOW; // declare IRQ flag

// change state of an output pin

void toggle(byte pinNum) {

byte pinState = !digitalRead(pinNum);

digitalWrite(pinNum, pinState);

}
void setup() {

pinMode(LED1, OUTPUT);

pinMode(LED2, OUTPUT);

digitalWrite(LED1, 0); // LED off

digitalWrite(LED2, 0); // LED off

pinMode(SW1, INPUT);

pinMode(SW2, INPUT);

attachInterrupt(0, ISR0, FALLING);

attachInterrupt(1, ISR1, RISING);

void loop() {

noInterrupts();

toggle(LED2);

myDelay(2000);

toggle(LED2);

myDelay(2000);

interrupts();

// no other interrupt work except from here to begin loop.

} // end loop

void myDelay(int x) {

for(unsigned int i=0; i<=x; i++)

{
delayMicroseconds(1000);

void ISR0()

toggle(LED1);

void ISR1(){

toggle(LED2);

myDelay(5000); // 5 sec.

toggle(LED2);

flag1 = 0; // clear flag

II. Concurrencia

Ejemplo1

Usando variables booleanas y millis().

#define ledPin1 7

#define ledPin2 9

#define led1Cycle 300U

#define led2Cycle 775U

unsigned long led1LastMillis = 0;

unsigned long led2LastMillis = 0;


boolean led1State = false;

boolean led2State = false;

boolean cycleCheck(unsigned long *lastMillis, unsigned int cycle)

unsigned long currentMillis = millis();

if(currentMillis - *lastMillis >= cycle)

*lastMillis = currentMillis;

return true;

else

return false;

void setup()

pinMode(ledPin1, OUTPUT);

pinMode(ledPin2, OUTPUT);

void loop()

{
if(cycleCheck(&led1LastMillis, led1Cycle))

digitalWrite(ledPin1, led1State);

led1State = !led1State;

if(cycleCheck(&led2LastMillis, led2Cycle))

digitalWrite(ledPin2, led2State);

led2State = !led2State;

Ejemplo2

Usaremos leds en los pines 7 y 9 para mostrar la concurrencia.

#include "mthread.h"

#define LED1 7

#define LED2 9

class Led;

Led *led1, *led2;

class Led : public Thread {

public:
Led(int pin, int p1);

void blink(int blinks);

void switchOn(bool isOn);

bool isOn;

int blinks;

protected:

bool loop();

int pin;

};

Led::Led(int pin, int p1) {

this->pin = pin;

this->blinks = p1;

this->isOn = false;

pinMode(pin, OUTPUT);

digitalWrite(pin, LOW);

//pause();

void Led::switchOn(bool isOn) {

digitalWrite(pin, isOn ? HIGH : LOW);

this->isOn = isOn;

}
void Led::blink(int blinks) {

this->blinks = blinks;

resume();

bool Led::loop() {

if(blinks > 0) {

switchOn(!isOn);

sleep_milli(blinks);

} else {

pause();

return true;

void setup(){

led1 = new Led(LED1, 700);

led2 = new Led(LED2,350);

main_thread_list->add_thread(led1);

main_thread_list->add_thread(led2);

}
II. LCD Touch Screen

SainSmart Mega2560+3.2LCD TFT Touch Screen SD Reader For Arduino mega1280 R3


Robot

Attention please:SD card supports Mega 2560, ARM and 51 MCU.

SD Memory Supports:2GB
Overview

Ø Low-cost LCD display graphics user interface solution.

Ø 240 x 320 QVGA resolution, 65K true to life colors, TFT screen.

Ø Integrated 4-Wire resistive Touch Panel.

Ø Easy 5 pin interface to any host device: VCC, TX, RX, GND, RESET.

Ø Asynchronous hardware serial port, TTL interface, with 300 baud to 256K baud.

Ø Powered by the 4D-Labs PICASO-SGC processor

Ø On-board micro-SD memory card adapter for multimedia storage and data logging
purposes. HC card support is also available for cards 4Gb and larger.

Ø DOS compatible file access (FAT16 format) as well as low level access to card memory.
Ø Dedicated PWM Audio pin supports FAT16 audio WAV files and complex sound
generation.

Ø On-board audio amplifier with a tiny 8 Ohms speaker for sound generation and WAV
file playback.

Ø Comprehensive set of built in high level graphics functions and algorithms that can
draw lines, circles, text, and much more.

Ø Display full color images, animations, icons and video clips - supports all available
Windows fonts and characters (imported as external fonts).

Ø 16x General Purpose I/O pins. Upper 8 bits can be used as an I/O Bus for fast 8-bit
parallel data transfers.

Ø 2x 30 pin headers for I/O expansion and future plug-in daughter boards.

Ø 4.0V to 5.5V range operation (single supply).

Ø 4 x snap-off mounting tabs with 3mm holes for mechanical support.

Library: UTFT

This library is the continuation of my ITDB02_Graph, ITDB02_Graph16 and RGB_GLCD


libraries for Arduino and chipKit. As the number of supported display modules and
controllers started to increase I felt it was time to make a single, universal library as it will
be much easier to maintain in the future.

2.4" display module 3.2" display module 1.5" display shield

Basic functionality of this library was origianlly based on the demo-code provided by ITead
studio (for the ITDB02 modules) and NKC Electronics (for the RGB GLCD module/shield).
This library supports a number of 8bit, 16bit and serial graphic displays, and will work with
both Arduino and chipKit boards.

The library works great with the ITDB02 series of display modules from ITead Studio, the
TFT01 series of display modules from ElecFreaks, and the RGB LCD Shield and module
from NKC Electronics.

III. Salida Analógica (Música)

El diagrama esquemático para este circuito es conectar el parlante (piezo eléctrico, si se


dispone) en los pines D9 y GND como se indica en la figura:

Teniendo esta conexión, se puede generar sonidos simplemente controlando la frecuencia


y duración de la señal, por ejemplo con el siguiente código:

int speakerOut =9;

void setup(){
pinMode (speakerOut, OUTPUT);
}
void loop() {
for (int i = 0; i 100; i++){
digitalWrite(speakerOut,HIGH);
delayMicroseconds(1136);
digitalWrite(speakerOut, LOW);
delayMicroseconds(1136);
}
}

b. Uso de la función tone().

Arduino tiene 2 funciones diseñadas para emitir sonidos de manera correcta. La primera
es tone() la cual requiere como parametros lo siguiente:

tone(pin, frequency, duration)

tone(pin, frequency)

Ambos realizan la misma operación, dependiendo de la duración que se proporcione. Si


no se incluye duración, el sonido se escuchara hasta que se envíe un nuevo comando
tone() o hasta que se llame a la función noTone(). (Por lo que puede requerir el uso de la
función delay si se toca un tono determinado) La duración será en milisegundos.

Un ejemplo de código es el siguiente:

#define NOTE_C4 262

#define NOTE_D4 294

#define NOTE_E4 330

#define NOTE_F4 349

#define NOTE_G4 392

#define NOTE_A4 440

#define NOTE_B4 494

#define NOTE_C5 523


const int kPinSpeaker = 9;

void setup(){

pinMode(kPinSpeaker, OUTPUT);

void loop() {

tone(kPinSpeaker, NOTE_C4, 500);

delay(500);

tone(kPinSpeaker, NOTE_D4, 500);

delay(500);

tone(kPinSpeaker, NOTE_E4, 500);

delay(500);

tone(kPinSpeaker, NOTE_F4, 500);

delay(500);

tone(kPinSpeaker, NOTE_G4, 500);

delay(500);

tone(kPinSpeaker, NOTE_A4, 500);

delay(500);

tone(kPinSpeaker, NOTE_B4, 500);

delay(500);

tone(kPinSpeaker, NOTE_C5, 500);

delay(500);
noTone(kPinSpeaker);

delay(2000);

Potrebbero piacerti anche