“Si” se interpreta mal cuando ADC = 1023?

0

Mi código en ATtiny13A:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>

#include "dbg_putchar.h"

void ADC_init()
{
    // Set the ADC input to PB4/ADC2
    ADMUX |= (1 << MUX1);
    //ADMUX |= (1 << ADLAR);
    ADMUX |= (1 << REFS0);

    // Set the prescaler to clock/128 & enable ADC
    // At 9.6 MHz this is 75 kHz.
    // See ATtiny13 datasheet, Table 14.4.
    ADCSRA |= (1 << ADPS1) | (1 << ADPS0) | (1 << ADEN);
}


int adc_read (void)
{
    // Start the conversion
    ADCSRA |= (1 << ADSC);

    // Wait for it to finish
    while (ADCSRA & (1 << ADSC));

    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
    uint8_t high = ADCH; // unlocks both

    int ADC_val = (high<<8) | low;

    return ADC_val;
}

void wyslij_wynik_pomiaru(int wynik)
{
    char str[4];
    itoa(wynik, str, 10);
    dbg_putchar(str[0]);
    if (wynik > 9) dbg_putchar(str[1]);
    if (wynik > 99) dbg_putchar(str[2]);
    if (wynik > 999) dbg_putchar(str[3]);
    dbg_putchar('a');
}


int main(void)
{
    DDRB = _BV(1);
    dbg_tx_init();
    ADC_init();
    int wynik_poczatkowy = adc_read();
    int wynik_nowy;
    while(1)
    {
        wynik_nowy = adc_read();
        if (abs(wynik_poczatkowy-wynik_nowy) > 150) {
            wynik_poczatkowy = wynik_nowy;
            PORTB |= _BV(1); //turn on ESP8266
            _delay_ms(5000);
            wyslij_wynik_pomiaru(wynik_nowy); //send data
            _delay_ms(5000);
            PORTB  &= ~_BV(1); //turn off ESP8266
            _delay_ms(14900);
        }
        _delay_ms(100);
    }
}

Cuando lees el nivel de voltaje del ADC y es menor que 1023, pero constante, entonces todo funciona correctamente: el bucle IF se ejecuta solo una vez, sin embargo, cuando el nivel de voltaje leído en el ADC es igual a 1023, entonces el IF se ejecuta de forma continua (la condición se sigue cumpliendo, pero no debería).

¿Por qué sucede esto y cómo solucionarlo?

    
pregunta Defozo

2 respuestas

3

Si ADMUX: ADLAR está configurado, su código se bloqueará & quemar cuando se fue a la izquierda desplaza high << 8 , porque estás cambiando los datos al bit de signo de " high -promoted-to-int" = comportamiento indefinido. ¿Estás seguro de que ADLAR no está configurado? Lo has comentado. Si ADLAR no está configurado, su código probablemente funcionará, por suerte.

Debes leer sobre la promoción de enteros implícitos. En esta operación de cambio, la variable uint8_t high se promocionará implícitamente a int , que es un int16 firmado (suponiendo que int es de 16 bits), que no es lo que usted quiere o necesita.

Además, para escribir programas deterministas y portátiles, deshacerse del tipo int , nunca debe usarse en la programación de sistemas integrados. Use uint16_t en su lugar.

Además, (si ADLAR no está configurado), ¿qué te hace pensar que los bits 7 a 2 en ADCH contienen datos muy buenos? El manual no menciona qué lectura le darán esos bits.

Para convertir su código en calidad de producción, cámbielo de las siguientes maneras:

uint16_t adc_read (void)
{
    // Start the conversion
    ADCSRA |= 1 << ADSC;

    // Wait for it to finish
    while (ADCSRA & (1 << ADSC))
        ; // semicolon on separate line to show that this was intentional


    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
    uint8_t high = ADCH; // unlocks both

    const uint8_t ADCH_DATA_MASK = 0x03; // the two MSB of 10 bit ADC value
    high = high & ADCH_DATA_MASK; // mask out data, discard junk

    return (uint16_t)high << 8 | (uint16_t)low;
}

Su código main () debe adaptarse para que sea compatible con los cambios anteriores. En main (), es posible que tengas que convertir el uint16_t en int16_t para que tu algoritmo funcione.

    
respondido por el Lundin
1

Esto es solo una corazonada, ¿pero ha intentado usar entradas sin firmar para sus valores de ADC y hacer la comparación de una manera diferente? Dado que 1023 es una potencia de dos menos uno, sospecho que hay un problema aritmético. Intenta algo como esto:

if (wynik_poczatkowy > wynik_nowy + 150 || wynik_nowy > wynik_poczatkowy + 150)
{
    ...
}
    
respondido por el Adam Haun

Lea otras preguntas en las etiquetas