Tengo un problema en el que ejecutar una secuencia de desactivación de vigilancia en un AVR ATtiny84A en realidad está restableciendo el chip a pesar de que el temporizador debería tener suficiente tiempo. Esto sucede de manera inconsistente y cuando se ejecuta el mismo código en muchas partes físicas; algunos se reinician cada vez, algunos se reinician a veces y otros nunca.
Para demostrar el problema, he escrito un programa simple que ...
- Permite al perro guardián con un tiempo de espera de 1 segundo
- Restablece el perro guardián
- Parpadea el LED blanco durante 0,1 segundos.
- Parpadeó el LED blanco apagado durante 0.1 segundos
- Deshabilita el perro guardián
El tiempo total entre la activación y desactivación de watchdog es inferior a 0.3 segundos, aunque a veces se produce un reinicio de watchdog cuando se ejecuta la secuencia de desactivación.
Aquí está el código:
#define F_CPU 1000000 // Name used by delay.h. We are running 1Mhz (default fuses)
#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>
// White LED connected to pin 8 - PA5
#define WHITE_LED_PORT PORTA
#define WHITE_LED_DDR DDRA
#define WHITE_LED_BIT 5
// Red LED connected to pin 7 - PA6
#define RED_LED_PORT PORTA
#define RED_LED_DDR DDRA
#define RED_LED_BIT 6
int main(void)
{
// Set LED pins to output mode
RED_LED_DDR |= _BV(RED_LED_BIT);
WHITE_LED_DDR |= _BV(WHITE_LED_BIT);
// Are we coming out of a watchdog reset?
// WDRF: Watchdog Reset Flag
// This bit is set if a watchdog reset occurs. The bit is reset by a Power-on Reset, or by writing a
// logic zero to the flag
if (MCUSR & _BV(WDRF) ) {
// We should never get here!
// Light the RED led to show it happened
RED_LED_PORT |= _BV(RED_LED_BIT);
MCUCR = 0; // Clear the flag for next time
}
while(1)
{
// Enable a 1 second watchdog
wdt_enable( WDTO_1S );
wdt_reset(); // Not necessary since the enable macro does it, but just to be 100% sure
// Flash white LED for 0.1 second just so we know it is running
WHITE_LED_PORT |= _BV(WHITE_LED_BIT);
_delay_ms(100);
WHITE_LED_PORT &= ~_BV(WHITE_LED_BIT);
_delay_ms(100);
// Ok, when we get here, it has only been about 0.2 seconds since we reset the watchdog.
wdt_disable(); // Turn off the watchdog with plenty of time to spare.
}
}
Al inicio, el programa verifica si el reinicio anterior fue causado por un tiempo de espera de vigilancia y, de ser así, enciende el LED rojo y borra la bandera de reinicio de vigilancia para indicar que se produjo un reinicio de vigilancia. Creo que este código nunca debería ejecutarse y el LED rojo nunca debería encenderse, pero a menudo lo hace.
¿Que esta pasando aqui?
Respuestas:
Hay un error en la rutina de la biblioteca wdt_reset ().
Aquí está el código ...
La cuarta línea se expande para ...
La intención de esta línea es escribir un 1 en WD_CHANGE_BIT, lo que permitirá que la siguiente línea escriba un 0 en el bit de habilitación de vigilancia (WDE). De la hoja de datos:
Desafortunadamente, esta asignación tiene el efecto secundario de establecer también los 3 bits inferiores del Registro de control de vigilancia (WDCE) en 0. Esto establece inmediatamente el preescalador en su valor más corto. Si el nuevo preescalador ya se ha activado en el momento en que se ejecuta esta instrucción, el procesador se reiniciará.
Dado que el temporizador de vigilancia funciona con un oscilador de 128 kHz físicamente independiente, es difícil predecir cuál será el estado del nuevo preescalador en relación con el programa en ejecución. Esto explica la amplia gama de comportamientos observados en los que el error puede correlacionarse con el voltaje de suministro, la temperatura y el lote de fabricación, ya que todas estas cosas pueden afectar la velocidad del oscilador de vigilancia y el reloj del sistema de forma asimétrica. ¡Fue un error muy difícil de encontrar!
Aquí hay un código actualizado que evita este problema ...
La
wdr
instrucción adicional restablece el temporizador de vigilancia, por lo que cuando la siguiente línea cambia potencialmente a un preescalador diferente, se garantiza que aún no ha excedido el tiempo de espera.Esto también se puede solucionar ORinging los bits WD_CHANGE_BIT y WDE en WD_CONTROL_REGISTER como se sugiere en las hojas de datos ...
... pero esto requiere más código y un registro de memoria virtual adicional. Desde el mostrador de vigilancia se restablece cuando se trata de todos modos desactivado, el reajuste adicional no clobber nada y no tiene efectos secundarios no intencionales.
fuente