Extraño comportamiento del ADC de Attiny

5
#include <avr/io.h>
#include <util/delay.h>

void init(){
    // LED output.
    DDRB |= 1<<4;
    // Turn on ADC on third pin, continuous mode, prescaler 128.
    ADMUX |= (1<<MUX0) | (1<<MUX1);
    ADCSRA |= (1<<ADEN) | (1<<ADSC) | (1<<ADATE) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
}

int main(){
    init();

    uint16_t adc = 0;
    while(1){
        adc = (ADCH << 8) | ADCL;
        // adc = (adc + 1) & 1023;
        // _delay_ms(1);

        if(adc > 512){
            PORTB |= 1<<4;
        }
        else{
            // PORTB = 0;
            PORTB &= ~(1<<4);
        }
    }
}

Este es el código que tengo en mi ATtiny13A. Hay un LED conectado al pin B4, y un potenciómetro a B3. Se supone que el código enciende el LED si el valor analógico es mayor que 512 (la mitad del máximo de 1024). Sin embargo, no funciona y el LED parece estar desactivado todo el tiempo.

Normalmente, asumo que hay algo mal en la configuración de los periféricos, pero están sucediendo algunas cosas muy extrañas. Por ejemplo, si cambio PORTB &= ~(1<<4); por PORTB = 0 (que en este caso debería hacer exactamente lo mismo), el código comienza a funcionar mágicamente como se esperaba. Otra cosa que intenté es cambiar el estado de los LED usando un contador simple al descomentar las dos líneas cerca de _delay_ms , y nuevamente, el LED comienza a parpadear como se esperaba.

El único momento en que este código no funciona es cuando está exactamente en esta forma, por lo que debe haber algún problema con la combinación de lectura de ADC y escritura de bits del puerto de salida.

¿Por qué sucede esto?

    
pregunta akrasuski1

1 respuesta

11
  1. Patrón de acceso de registro de 16 bits incorrecto
      

    Cuando se lee ADCL, el registro de datos ADC no se actualiza hasta que ADCH se   leer.

En C, el orden de evaluación del operador | no está definido, por lo que este código podría leer primero ADCH y luego leer ADCL . Una vez que haya leído ADCL , habrá bloqueado el registro de datos hasta la próxima pasada a través del bucle, que ciertamente no es lo que desea.

Intenta cambiar esta línea a ...

adc = ADC;

(el compilador de C debe generar la codificación de orden de acceso correcta).

  1. Leyendo ADC mientras se está actualizando.

Este diagrama sugiere que el registro de ADC se está actualizando hasta que ADIF se ponga alto ...

Espero que los nuevos bits se coloquen en su lugar con cada aproximación sucesiva, y el retraso adicional del nop podría ser suficiente para que el ADC esté listo para cuando lo toque por casualidad.

Intenta reemplazar ...

adc = (ADCH << 8) | ADCL;

... con ...

while (! ADCSRA & _BV(ADIF) );
adc = ADC;
ADCSRA |= _BV( ADIF );

... y ver si eso resuelve el problema de tiempo.

Informar si esto soluciona el problema. Si no, pasamos al siguiente intento!

    
respondido por el bigjosh

Lea otras preguntas en las etiquetas