Utilizar el Watchdog del Atmega para lanzar una interrupcion cada X tiempo

Lo relacionado con la electronica y arduino.
Responder
dmarofer
Mensajes: 57
Registrado: 02 Feb 2015 15:40

Utilizar el Watchdog del Atmega para lanzar una interrupcion cada X tiempo

Mensaje por dmarofer » 09 Mar 2016 17:42

El WatchDog que llevan muchos microprocesadores es un contador interno que podemos utilizar para resetear el mismo si por algun motivo se bloquea el programa. Basicamente es un contador descendente que reinicia el procesador si llega a cero, por lo que en el caso de usar esta funcionalidad, en nuestro codigo debe haber algo que reestablezca la cuenta antes de que eso ocurra, de manera que si el programa se bloquea no se actualizara y se producira el reinicio.

Por defecto esta funcionalidad esta desactivada en el ATMega del Arduino, pero podriamos usarla. En este caso no lo vamos a usar para esto, ya que otra funcionalidad que le podemos dar a este contador a parte de resetear la CPU, es que en vez de eso nos lance una interrupcion que ejecute una funcion con el codigo que queramos. Os pongo aqui esta explicacion porque he andado con ello en el proyecto RIEGAMATICO y me parece interesante explicarlo y con un ejemplo.

En mi caso quiero que cada X segundos el Arduino ejecute unas rutinas que tengo escritas que comprueban el estado de los sensores, de la bateria y los programas de riego por si es hora de regar. En el antiguo codigo lo hacia a base de llamadas en el loop() del programa, pero cuando a parte de esto el aparato ya hace otras cosas menos importantes (como por ejemplo los menus por donde puede navegar el usuario para configurar cosas y ver informacion), es complejo seguir haciendo esas comprobaciones mientras el usuario anda tocando botones y cambiando cosas. Como estas comprobaciones son importantes las vamos a hacer mediante la interrupcion de este contador, lo cual implicara que se ejecuten si o si este lo que este haciendo arduino en el momento que el contador llega a cero y se dispara dicha interrupcion.

Ademas hacer esto no es muy complejo, para ello solo hay que hacer 2 cosas:

1.- En la parte de setup() (para que solo se haga una vez) configurar los registros internos del procesador para configurar el Watchdog con el tiempo que queramos y para que en vez de reiniciar el procesador cuando llegue a cero, lance la interrupcion:

void ConfigWatchdog () { // Para configurar el WATCHDOG en los registros internos del ATMEGA para disparar interrupcion cara X segundos.

// AQUI SE MANIPULAN BITS INDIVIDUALES DE LOS REGISTROS DE CONFIGURACION INTERNOS DEL PROCESADOR

MCUSR &= ~(1<<WDRF); // Borrar el FLAG WDRF (WatchDogResetFlag). Este Flag se activa si el WTD hace un reset del controlador
// El compound Bitwise And (&=) se suele usar para poner bits individuales a 0
// La operacion Shift (<<) se explica "llevar el 1 para la izquierda hasta la posicion WDRF)

// Configurar el registro WDTCSR (WatchDogTimerControlRegister)
WDTCSR |= (1<<WDCE) | (1<<WDE); // Activar WDCE (WatcDogChangeEnabled) para permitir cambiar la config del WTD
// Y desactivar (creo) el WDE (WatchDog system reset Enabled)


//WDTCSR = 1<<WDP0 | 1<<WDP3; // 1001 para 8 segundos en la config del tiempo del WTD
//WDTCSR = 1<<WDP3; // 1001 para 4 segundos en la config del tiempo del WTD
WDTCSR = 1<<WDP0 | 1<<WDP1 | 1<<WDP2; // 0111 para 2 segundos en la config del tiempo del WTD

WDTCSR |= 1<<WDIE; // Activar la interrupcion por WTD (WatchDogInterruptEnabled) que ejecuta el codigo de la funcion ISR(WDT_vector)

}

En vez de ponerlo directamente en el setup he creado una funcion void ConfigWatchdog () y la llamo desde el mismo.

2.- Poner nuestro codigo a ejecutar dentro de una funcion especial llamada ISR (Iterrupt Service Routine) y pasandole el parametro de la interrupciona gestionar, en este caso WDT_vect (la disparada por el WatchDog). Tambien es importante dentro de la funcion desactivar la interrupcion nada mas entrar para que no se dispare mientras se hace lo que hay dentro, y activarla de nuevo una vez que salimos. O sea:

ISR(WDT_vect){ // Rutina de servicio del WatchDog. Se ejecuta cuando el WTD termina la cuenta. Sirve para hacer las comprobaciones cada X tiempo por interrupcion del WTD

WDTCSR &= ~(1<<WDIE); // Desactiva la interrupcion WTD para que de tiempo a hacer esto de abajo. Si no se dispara de nuevo antes de acabar claro!

HeartBeat(ROJO); // Toy Vivo!!

PruebaSensores(); // Para chequear los sensores que activan los testigos (Activan los Flags de los testigos)

PruebaCarga(); // Para comprobar el ciclo de carga

CompruebaRiegos(); // Para comprobar si hay que regar por programacion horaria

WDTCSR |= 1<<WDIE; // Activar la interrupcion otra vez al terminar.

}


Igual parece un poco lioso pero no lo es tanto, mas aqui que el codigo se ve un poco raro. Pegaroslo en el Notepad++ (si no lo teneis instalaoslo) y ponerlo en lenguage C# que se ve todo mucho mas claro. Si alguien esta interesado en probar o tiene idea de aplicarlo a algo, podemos explicar con mas detalle cualquier cosa por aqui o en persona con cerveza en el local ;)

Un saludo a todos!

Disenator
Mensajes: 66
Registrado: 31 Ene 2015 11:51

Re: Utilizar el Watchdog del Atmega para lanzar una interrupcion cada X tiempo

Mensaje por Disenator » 09 Mar 2016 23:27

Yo por una cerveza, aprendo lo que sea

kurtsik
Mensajes: 124
Registrado: 30 Ene 2015 11:29

Re: Utilizar el Watchdog del Atmega para lanzar una interrupcion cada X tiempo

Mensaje por kurtsik » 10 Mar 2016 14:19

Muy interesante Diego.

Lo he leido un poco por encima pero me parece bastante denso asi que igual una tarde que tengas libre le damos una vuelta si te parece. :)

dmarofer
Mensajes: 57
Registrado: 02 Feb 2015 15:40

Re: Utilizar el Watchdog del Atmega para lanzar una interrupcion cada X tiempo

Mensaje por dmarofer » 10 Mar 2016 23:13

Pues nada hagase la cerveza pues! ;) . Lo explicaremos en persona con un ejemplo sencillo que ya vereis que es una bobada pero muy util.

Responder