Interrupciones con atmega328

Ante todo un saludo a todos, ya que soy nuevo en este foro.

Bien, hace mucho programaba en ensamblador con un micro 2051, ahora he descubierto arduino y me ha gustado mucho, tanto que me acabo de comprar un Duemilanove con el atmega328, el caso es que he cogido un pequeño ejercicio que tenia por ahí implementando un semáforo y no consigo hacer funcione.

El código esta bien y lo único que no me funciona es un botón que tengo puesto en la interrupción (INT0) pin 2, he probado todo lo que he encontrado en este foro (desde wiring, desde C, etc) y en la red, pero no consigo habilitar este pin para hacer saltar la rutina en el vector de interrupción y es que no se como habilitar y configurar la interrupción. Espero que podáis ayudarme.

Un saludo y muchas gracias por vuestras respuestas.

Hola,

Tienes la funcion attachinterrupt.

Puedes leer acerca de ella en:
http://arduino.cc/en/Reference/AttachInterrupt

Ten cuidado con los rebotes!!! :wink:

Salu2

Igor R.

Muchas gracias por la respuesta Igor, justamente eso es lo primero que hice y no me funciono, seguramente lo implemente mal.

Lo primero fue configurar la interrupcion en void setup() asi:

attachInterrupt(0, mi_funcion, CHANGE);

Pero luego pulsando un interruptor para cambiar el estado del pin 2 (INT0) que es al que entiendo se refiere el primer argumento de attachInterrupt el micro no hacia nada, no ejecutaba mi_función, función que ya tenia probada y se ejecuta perfectamente cuando la llamas por un procedimiento ordinario.

Quizás sepáis de algún código que contenga una interrupción externa sin timer para poder probarlo y ver que pasa.

Saludos y de nuevo gracias.

Hola,

Puede que el problema que tenga sea los rebotes.

En el tutorial de Ladyada puede verlo (Arduino Tutorial - Lesson 5)

Entonces cuando pulsas el boton, en realidad la mecánica oscila y hay pequeños "rebotes" mecánicos que hacen lo que puede ver en la figura.

El problema, que si hace interrupción, entrará tantas veces como cambios de flanco haya detectado.

Para ver si esta ocurriendo esto, puede declarar una variable contador como volatile (http://www.arduino.cc/en/Reference/Volatile).
Incrementa dicha variable cada vez que entre en la interrupción, y dentro del loop() de su programa realiza un Serial.print para ver dicha variable por el puerto serie y realizar un pequeño "depurador" de su programa.

Es una idea.... :wink:

Salu2

Igor R.

Gracias Igor, voy a analizar los rebotes con la variable de contador volatile.

Tambien quizás seria posible inhabilitar las interrupciones como primera instruccion del bucle de INT0 y volver a habilitarlas en la ultima instruccion del mismo.

Seguire estas recomendaciones.

Muchisimas gracias.

Bueno por fin he conseguido hacer funcionar las interrupciones. attachInterrupt no me ha llegado a funcionar nunca, al igual que otros códigos que he encontrado por la red, el problema reside en que no se habilitaban las interrupciones en el registro correspondiente y no encontraba formas de acceso a este registro, muchos de los ejemplos (la mayoría de la red) hacer referencia a la siguiente instrucción

GICR |= (1 << INT0);

como una forma de habilitar el uso de las interrupciones, en este caso INT0, sin embargo solo es válida para el atmega8 y no para el atmega328 que es el mio. Tras buscar por los directorios del IDE de Arduino encontré lo que estaba buscando, en el archivo hardware/cores/arduino/WInterrupts.c se encuentra la forma correcta de inicializar y habilitar o deshabilitar las interrupciones correctamente para el atmega368.

Ahora solo queda solucionar dos problemas, por un lado los rebotes y por otro lado, entender porque el código que esta en el vector de interrupción no admite la instrucción delay, al menos a mi en los pocos ejemplos que he hecho un delay en el vector de interrupción deja mi Arduino colgado (se admiten o mejor se necesitan sugerencias).

Os dejo el siguiente código de ejemplo con la forma que he encontrado para hacer funcionar las interrupciones. El programa imprime una variable de control en la terminal cada vez que hay un flanco en el pin 2 (INT0).

//Cabeceras para la inclusion de interrupciones
#include <avr/io.h>
#include <avr/interrupt.h>

int control=4; //Inicializacion de la variable de control
// Rutina a la que acude el micro cuando detecta la interrupcion
ISR(INT0_vect) 
{
  control=control++; // Incrementa la variable de control
  // Bucle para mantener la variable de control dentro del rango 1 - 4
if (control<0 || control>4) 
{
  control=1; // valor para la variable de control si esta es menor que 0 o mayor que 4
  }
}
void setup()
{
  Serial.begin(9600); // Inicializacion del puerto serie
  // Iniaciliacion de la interrupcion INT0
  EIMSK |= (1 << INT0);
  EICRA |= ( 1 << ISC00);
  EICRA |= ( 0 << ISC01);
  }
  
void loop()
{
  Serial.println(control); /Muestra el valor de la variable de control
  delay(1000);
  }

Espero que pueda servir a alguien. Un saludo a todos.

Se supone que lo que hay detras del fichero WInterrupts es lo que hace cuando llamas a la funcion attachinterrupt…

No lo he probado en un ATMega368, pero en el ATMega168 va bien…

En WInterrups puedes ver que hace segun elijas INT0 ó INT1:

case 0:
      EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);
      EIMSK |= (1 << INT0);
      break;
case 1:
      EICRA = (EICRA & ~((1 << ISC10) | (1 << ISC11))) | (mode << ISC10);
      EIMSK |= (1 << INT1);
      break;

¿Que version de software tienes?

La función delay usa en su interior la función millis, que a su vez usa interrupción del Timer 0. No se como estará el tema de prioridades de interrupciones…Ya que como estas dentro de INT0, no sé si atiende a la del Timer… Me imagino que millis() no se actualizará y por eso no te funciona delay. Pero todo esto son suposiciones…

void delay(unsigned long ms)
{
      unsigned long start = millis();
      
      while (millis() - start <= ms)
            ;
}

Salu2

Igor R.

Igor, de nuevo muchas gracias por el interés que te estas tomando en el asunto.

Bien, ya me había dado cuenta que WInterrupts es lo que se debe ejecutar cuando llamas a attachinterrupt (bueno realmente lo intuía nada más), así que se supone que debería funcionar y lo más probable es que sea así y sea yo el que haga algo mal, pues las interrupciones no funcionan. En cualquier caso estoy contento de poder acceder un poco más directamente a las interrupciones ya que así será algo más rápido el proceso.

Contestando a tu pregunta, la versión que tengo del IDE de Arduino es la 16 (creo que la última). Bueno aún me queda mucho por aprender pues tengo mi Arduino tan solo desde el miercoles pasado.

En cuanto a la función delay muchas gracias por la aclaración, en cualquier caso mientras no necesite parar un tiempo en concreto puedo anidar unos cuantos bucles de variables para simular un delay, esto es lo que hacía en ensamblador, claro que cuando programas en ensamblador tienes la ventaja de que sabes exactamente cuanto tardas en ejecutar una instrucción, lo que te permite anidar bucles y saber exactamente cuanto va a tardar el micro en ejecutarlos.

Bueno Igor, muchas gracias y ya veras algún post que otro mio por el foro, ya que el motivo de trabajar con Arduino es un proyecto que tengo en mente, en el que habrá lectura de sensores, lectura de instrucciones NMEA del GPS y escritura por el puerto SPI. Ahora eso sí, igual que preguntare cosas también posteare las soluciones que encuentre por si a alguien le sirven en un futuro.

Un saludo a todos