37 votos

¿Por qué mi AVR reset cuando llamo wdt_disable() para intentar activar el temporizador de vigilancia?

Estoy teniendo un problema en la ejecución de un deshabilitar el guardián de secuencia en un AVR ATtiny84A en realidad es resetear el chip, incluso a pesar de que el temporizador debe tener un montón de tiempo a la izquierda. Esto sucede de forma incoherente y cuando se ejecuta el mismo código en muchas partes físicas; algunos reset cada vez, algunos de reinicio a veces, y algunos nunca.

Para demostrar el problema, he escrito un sencillo programa que...

  1. Permite que el organismo de control con 1 segundo de tiempo de espera
  2. Restablece el watchdog
  3. Parpadea el LED blanco en 0,1 segundos
  4. Brilló el blanco LED apagado durante 0,1 segundos
  5. Desactiva el watchdog

El tiempo total entre el organismo de control habilitar y deshabilitar es menos de 0.3 segundos, sin embargo, a veces, un perro guardián de restablecimiento se produce cuando el deshabilitar la secuencia se ejecuta.

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.

    }
}

En el inicio, el programa comprueba si la última puesta a cero fue causada por un organismo de control de tiempo de espera, y si es así, se enciende el LED rojo y se borra el guardián de restablecimiento de la bandera para indicar que un organismo de control reset sucedido. Creo que este código debe nunca ser ejecutado y el LED rojo debe nunca llegan, sin embargo, con frecuencia lo hace.

¿Qué está pasando aquí?

43voto

chews Puntos 1507

Hay un error en el wdt_reset() de la biblioteca de la rutina.

Aquí está el código...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

La cuarta línea se expande...

out _WD_CONTROL_REG, _BV(_WD_CHANGE_BIT) | _BV(WDE)

La intención de esta línea es para escribir un 1 a la WD_CHANGE_BIT, que permitirá a la siguiente línea para escribir un 0 a la watchdog bit de habilitación (WDE). De la hoja de datos:

Para desactivar activado un Temporizador de vigilancia, el siguiente procedimiento debe ser seguido: 1. En la misma operación, escribir un uno lógico a WDCE y WDE. Un uno lógico debe ser por escrito para WDE independientemente del valor anterior de la WDE bit. 2. Dentro de los próximos cuatro ciclos de reloj, en la misma operación, escribir la WDE y WDP bits como se desee, pero con la WDCE bits desactivada.

Por desgracia, esta misión tiene el efecto secundario de la configuración de la parte inferior 3 bits de la Watchdog Registro de Control (WDCE) a 0. Esto inmediatamente establece el prescaler a su menor valor. Si el nuevo prescaler ya ha sido despedido en el momento en que se ejecuta esta instrucción, el procesador será de restablecimiento.

Desde el temporizador de vigilancia se ejecuta en un físicamente independiente de 128 kHz oscilador, es difícil predecir lo que el estado de la nueva prescaler está en relación con el programa de ejecución. Esto explica la amplia gama de comportamientos observados en donde el error puede ser correlacionada con la tensión de alimentación, la temperatura, y el lote de fabricación puesto que todas estas cosas pueden afectar a la velocidad de la watchdog oscilador y el reloj del sistema de forma asimétrica. Este fue un muy duro error para encontrar!

Aquí se actualizó el código que evita este problema...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "wdr" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

El extra wdr instrucción restablece el temporizador de vigilancia, para cuando la siguiente línea potencialmente cambia a otra prescaler, que está garantizado que no se han agotado todavía.

Esto también podría ser fijado por la junta tórica de la WD_CHANGE_BIT y WDE bits en el WD_CONTROL_REGISTER como se sugiere en las hojas de datos...

; Write logical one to WDCE and WDE
; Keep old prescaler setting to prevent unintentional Watchdog Reset
in r16, WDTCR
ori r16, (1<<WDCE)|(1<<WDE)
out WDTCR, r16

...pero esto requiere más código y un extra de cero registrar. Desde el organismo de control contador se restablece cuando se está deshabilitado de todos modos, el extra de reset no darle una paliza a nada y no tiene efectos secundarios no intencionales.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X