5 votos

Comportamiento extraño del ADC de Attiny

#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 funcionando en mi ATtiny13A. Hay un LED conectado al pin B4, y un potenciómetro a B3. El código se supone que 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 asumiría que hay algo mal en la configuración de los periféricos, pero están ocurriendo cosas muy extrañas. Por ejemplo, si cambio PORTB &= ~(1<<4); a PORTB = 0 (que en este caso debería hacer exactamente lo mismo), el código empieza a funcionar mágicamente tal y como se esperaba. Otra cosa que probé es cambiar el estado del LED usando un simple contador descomentando las dos líneas cerca de _delay_ms - y de nuevo, el LED comienza a parpadear como se esperaba.

La única vez que este código no funciona es cuando está exactamente en esta forma - por lo que debe haber algo mal con la combinación de la lectura del ADC y la escritura de bits del puerto de salida.

¿Por qué ocurre esto?

0 votos

Una cosa más - incluso con el código como se muestra, parece como si el LED podría encenderse UNA vez - por lo que si el potenciómetro comenzó bajo (con el LED apagado), podría encender el LED, y luego apagarlo, pero la próxima vez que traté de girar el potenciómetro, falla. Esto es tan raro...

0 votos

OK, tengo una buena pista - si uso PORTB = 0 (la línea que ha funcionado), pero luego añade un nop después, se vuelve a romper. Parece ser un problema de sincronización por alguna razón - añadiendo 4 nop s lo arregla de nuevo...

11voto

chews Puntos 1507
  1. Patrón de acceso al registro de 16 bits incorrecto

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

En C, el orden de evaluación del | es indefinido, por lo que este código podría estar leyendo ADCH primero, y luego leer ADCL . Una vez que lea ADCL entonces has bloqueado el registro de datos hasta la siguiente pasada por el bucle, lo que ciertamente no es lo que quieres.

Intenta cambiar esta línea por...

adc = ADC;

(el compilador de C debería generar la codificación correcta del orden de acceso).

  1. Lectura del CAD mientras se actualiza.

Este diagrama sugiere fuertemente que el registro ADC se está actualizando hasta que ADIF se pone en alto...

enter image description here

Yo esperaría que los nuevos bits se desplazaran en su lugar con cada aproximación sucesiva, y el retraso extra del nop podría ser suficiente para que el ADC esté listo en el momento en que lo golpees 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 sincronización.

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

0 votos

Aha, eso resolvió el problema. ¡Buena captura! El ensamblaje generado efectivamente leyó ADCH primero (aunque el orden de las operaciones generalmente no está garantizado por C).

6 votos

"En C, [...] el | el operador se evalúa de izquierda a derecha" - esto no es correcto. De hecho, el orden de evaluación no está especificado (a diferencia de, por ejemplo, para || donde efectivamente es de izquierda a derecha para permitir la evaluación del cortocircuito).

0 votos

No he comprobado la hoja de datos por ahora, pero ¿funciona la comprobación de ADIF cuando el ADC está en modo de funcionamiento libre? Comienza una nueva conversión justo después de la anterior, así que o bien el registro de datos tiene que estar completamente almacenado en el buffer, o debería ser ilegible en todo momento...

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