A/D převodník - Měření bez blokování

Zápisník experimentátora

Hierarchy: A/D prevodník

Funkce analogRead je naprogramována tak, že během měření zablokuje mikrokontrolér. Během toho času nemůžete dělat nic jiného. Pokud potřebujete lépe využít čas mikrokontroléru na zpracování jiných úloh, je vhodné upravit analogové měření tak, aby neblokovalo běh programu. V tomto příkladu si takový program navrhneme.

analogRead

Takto vypadá funkce analogRead z aktuální verze Arduina. Budu popisovat pouze podstatné části kódu.

  • if (pin >= 14) pin -= 14; - Na desce má Arduino analogové piny označené symboly A0-A6. Definice se nacházejí v souboru pins_arduino.h. Tento výpočet zajišťuje, aby se symboly posunuli na hodnoty indexu od 0 a dali se následně snadno použít ve funkci na výběr konkrétního pinu, na kterém se bude provádět analogové měření.
  • sbi(ADCSRA, ADSC); - Start konverze A / D převodníku. Samotný start se ale nezačne okamžitě. Pro měření se využívá nastavení děličky vůči hlavní frekvenci mikrokontroléru a běžně je nastavena na hodnotu 128. Analogové měření používá vlastní hodinový signál, který je zpomalen a konverze se začne při změně logické úrovně. Takže se může stát, že ve skutečnosti bude program čekat ještě 128 instrukcí, dokud se konverze opravdu začne. Toto je další důvod k tomu, abychom použili neblokující měření.
  • while (bit_is_set(ADCSRA, ADSC)); - Toto je ten kritický řádek, který blokuje celý program. Dokud není splněna podmínka, program bude čekat. Proto je jasné, že se budeme soustřeďovat v našem zdrojovém kódu zejména na vylepšení tohoto řádku.
int analogRead(uint8_t pin)
{
    uint8_t low, high;

#if defined(analogPinToChannel)
#if defined(__AVR_ATmega32U4__)
    if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#endif
    pin = analogPinToChannel(pin);
#elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    if (pin >= 54) pin -= 54; // allow for channel or pin numbers
#elif defined(__AVR_ATmega32U4__)
    if (pin >= 18) pin -= 18; // allow for channel or pin numbers
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__) || defined(__AVR_ATmega644__) || defined(__AVR_ATmega644A__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__)
    if (pin >= 24) pin -= 24; // allow for channel or pin numbers
#else
    if (pin >= 14) pin -= 14; // allow for channel or pin numbers
#endif

#if defined(ADCSRB) && defined(MUX5)
    // the MUX5 bit of ADCSRB selects whether we're reading from channels
    // 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).
    ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((pin >> 3) & 0x01) << MUX5);
#endif
 
    // set the analog reference (high two bits of ADMUX) and select the
    // channel (low 4 bits).  this also sets ADLAR (left-adjust result)
    // to 0 (the default).
#if defined(ADMUX)
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = (analog_reference << 4) | (pin & 0x07);
#else
    ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif
#endif

    // without a delay, we seem to read from the wrong channel
    //delay(1);

#if defined(ADCSRA) && defined(ADCL)
    // start the conversion
    sbi(ADCSRA, ADSC);

    // ADSC is cleared when the conversion finishes
    while (bit_is_set(ADCSRA, ADSC));

    // we have to read ADCL first; doing so locks both ADCL
    // and ADCH until ADCH is read.  reading ADCL second would
    // cause the results of each conversion to be discarded,
    // as ADCL and ADCH would be locked when it completed.
    low  = ADCL;
    high = ADCH;
#else
    // we dont have an ADC, return 0
    low  = 0;
    high = 0;
#endif

    // combine the two bytes
    return (high << 8) | low;
}

Neblokující měření napětí

Toto je upravený kód pro měření napětí. Příklad je pro pin A0 a Arduino Uno. Ve funkci setup povolíme analogové měření a nastavíme děličku hodinového signálu na 128 (stejně jako to dělá i Arduino). Potom nastavíme referenci napětí na AVCC a pin A0.

Ve funkci loop využíváme proměnnou adc_conversion_working. V ní máme nastaveno, zda právě provádíme analogové měření. Pokud měření není aktivní, spustíme analogové měření. Potom pravidelně kontrolujeme nastavený bit ADSC a v okamžiku, jak není nastaven, je analogové měření skončeno. Odměřenou hodnotu vypíšeme na sériový port a počkáme 500 ms.

Pouze když je podmínka splněna, provedeme akci po skončení měření. Zbytek času se může mikrokontrolér věnovat jiné činnosti.

const byte adcPin = A0; // = 14 (pins_arduino.h)

bool adc_conversion_working = false;

void setup() {
  Serial.begin(115200);
  Serial.println("ADC without blocking");

  ADCSRA = bit(ADEN) // Turn ADC on
           | bit(ADPS0) | bit(ADPS1) | bit(ADPS2); // Prescaler of 128
  ADMUX  = bit(REFS0) // AVCC
           | ((adcPin - 14) & 0x07); // Arduino Uno to ADC pin
}

void loop() {
  if (!adc_conversion_working) {
    bitSet(ADCSRA, ADSC);  // Start a conversion
    adc_conversion_working = true;
  }

  // The ADC clears the bit when done
  if (bit_is_clear(ADCSRA, ADSC)) {
    int value = ADC;  // Read result
    adc_conversion_working = false;
    Serial.println(value);
    delay(500);
  }
}

Zdrojový kód

Zdrojový kód programu se nachází na serveru GitHub.

 


08.10.2018


Menu