SLEEP_MODE_ADC interfiriendo con USART (y pitido)

En un AVR ATmega328P, una vez por segundo estoy haciendo 3 conversiones AD que se suceden inmediatamente con un sobremuestreo de 16x usando SLEEP_MODE_ADC así:

EMPTY_INTERRUPT(ADC_vect);

uint16_t getVoltage(uint8_t pin) {
    ADMUX = (0b11110000 & ADMUX) | pin;

    uint32_t overValue = 0;
    for (uint8_t i = 0; i < 16; i++) {
        sleep_mode();
        overValue += ADC;
    }
    int16_t mV = (((overValue >> 2) * AREF_MV) >> 12);

    return mV;
}

Funciona bien.

Al mismo tiempo, estoy generando un pitido de 4 kHz con un temporizador que alterna un pin de salida, y estoy leyendo y escribiendo datos a través de USART.

El pitido tiene un clic corto una vez por segundo que se ve así:ingrese la descripción de la imagen aquí

Y de vez en cuando, un carácter enviado por USART se distorsiona.

Si bien esperaba que el temporizador de pitido se suspendiera durante la conversión de AD para reducir el ruido interno, no esperaba que se perturbara el USART tx/rx.

Cuando no uso el modo de suspensión y hago conversiones de AD como esta:

    // sleep_mode();
    ADCSRA |= (1 << ADSC);
    loop_until_bit_is_clear(ADCSRA, ADSC);

el pitido es "limpio", no hay más problemas de USART tx/rx, y después de algunas mediciones exhaustivas, estoy bastante seguro de que los resultados de conversión de AD son tan precisos como lo son con el modo de suspensión.

Entonces me pregunto dos cosas:

  1. ¿Se espera que SLEEP_MODE_ADC perturbe el USART tx/rx?
  2. ¿El modo de suspensión realmente tiene sentido en combinación con el sobremuestreo? ¿Es posible que algo de ruido ni siquiera ayude a aumentar la precisión (difuminación) o se aplica solo al ruido proveniente de los sensores?
¿Tenemos que adivinar qué hardware estás usando?
Uy, lo siento - agregó.
¿Está utilizando las rutinas de Arduino Serial o algo más? Si es Arduino, ¿puedes probar Serial.flush() antes de dormir? Si tiene sus propias rutinas, intente esperar a que se envíe cualquier carácter en el búfer antes de irse a dormir. El modo de suspensión, según recuerdo, deshabilitará el reloj del generador de baudios USART, lo que significa que si un personaje está en medio de ser enviado, se corromperá.
Estoy usando mis propias rutinas y probaré lo que sugieres...
Lo intenté loop_until_bit_is_set(UCSR0A, UDRE0);(USART Data Register Empty) antes de irme a dormir, pero no funcionó. Un _delay_ms(10)antes de entrar en el bucle ayuda, pero supongo que no es muy bueno. Además, recibo datos corruptos enviados al AVR y no tengo idea de cómo evitar que se reciban datos durante esas llamadas 3*16 a sleep_mode().

Respuestas (1)

  1. ¿Se espera que SLEEP_MODE_ADC perturbe el USART tx/rx?

Absolutamente. Tanto el USART como los temporizadores usan clk IO para la operación, y esto está deshabilitado en el modo de reducción de ruido ADC.

  1. ¿El modo de suspensión realmente tiene sentido en combinación con el sobremuestreo? ¿Es posible que algo de ruido ni siquiera ayude a aumentar la precisión (difuminación) o se aplica solo al ruido proveniente de los sensores?

En realidad, desea algo de ruido cuando realiza el sobremuestreo y la aniquilación tal como está, por lo que no creo que el modo ANR tenga sentido aquí; Restríjase al modo inactivo ( SLEEP_MODE_IDLE) para mantener activos el USART y los temporizadores.

Lindo. Con SLEEP_MODE_IDLE mi código original funciona bien, sin hacer clic en el pitido y sin problemas de USART RX/TX. Y no es necesario usar loop_until_bit_is_clear(ADCSRA, ADSC). ¡Gracias!