Arduino: Analog ölçümler

Arduino, analog etki alanındaki parametreleri ölçmemizi sağlayan bir dizi analog girişe sahiptir. Bu voltaj, akım, direnç, sıcaklık, ışık vb. olabilir. Bu makale, Arduino’daki analogdan dijitale dönüştürücünün (ADC) kullanımını ve performansını araştırıyor. Testler, boyutundaki en belirgin farkla Arduino Uno’ya çok benzeyen bir Arduino Nano v3.0 üzerinde gerçekleştirilir. Ayrıca kart üzerindeki ATMEGA328 mikrodenetleyicisi de aynı ve 16 MHz saat frekansında çalışıyor.

Analog Okuma;

Standart analogRead() fonksiyonunun dönüşüm süresi aşağıdaki kod ile incelenir. Dönüşümün başlangıcını ve sonunu tespit etmek için kalem 12’de bir işaretleyici çıktı oluşturulur. Bu pin üzerindeki seviye, analogRead() yürütülmeden önce değiştirilir . Çıktıyı değiştirmek için bitSet() ve bitClear()‘ ın yürütülmesi 125 ns sürer.

int marker = 12;   // marker output pin
int analogPin = 3; // analog input pin
int aval = 0;      // analog value

void setup() {                
  pinMode(marker, OUTPUT);  // pin = output
}

void loop() {
  bitSet(PORTB, 4);             // marker high
  aval = analogRead(analogPin); // sample signal
  bitClear(PORTB, 4);           // marker low
  aval = analogRead(analogPin); // sample signal
}

Kod 1: analogRead işlevinin yürütme zamanını zamanlama.

Şekil 2: analogRead işlevinin yürütme süresi. 
(Zaman tabanı: 50 μs/böl)

Analogdan dijitale dönüştürme sırasında, işaretleyici pimindeki seviye 111 μs boyunca yüksek kalır. İşaret sinyalini değiştirmek için yürütme süresi (125 ns) bu nedenle ihmal edilebilir. Bu nedenle analogRead() işleviyle maksimum örnekleme hızı 9,1 kHz’dir. analogRead() kullanmanın bir dezavantajı, Arduino’nun dönüştürme sonucunu beklerken başka bir kod çalıştıramamasıdır.


ADC kesmelerini kullanma

Yukarıda açıklandığı gibi, Arduino analogRead() işlevini yürütürken başka bir kod çalıştıramaz . ATMEGA328, analog sinyallerin alınması için CPU çekirdeğini kullanmadığından, işleme yeteneklerinin boşa gitmesine neden olur. Özellikle bir sinyalin sürekli olarak örneklenmesi gerektiğinde. Bu, kesmeler kullanılarak daha karmaşık bir şekilde yapılabilir. Kesintilerle bir analog dönüştürme kurmak için varsayılan işlevler olmadığından, ADC ile ilişkili kayıtlar manuel olarak değiştirilmelidir.

Tek atış örnekleme

Tek atış örneklemesi aslında Arduino’nun analogRead() işlevini çağırırken yaptığı şeydir . Bu başka yollarla yapıldığında avantaj sağlayacak pek bir şey yoktur. Çünkü ADC başlatılmadan önce ADC-ready bayrağı kontrol edilmelidir. Ve bu, bayrağı bir döngüde yoklamak anlamına gelir ve Arduino’dan farklı değildir.

Serbest çalışan örnekleme

Bir sinyalin sürekli olarak örneklenmesi gerektiğinde, bunu kesintilerle ayarlamak iyi bir fikirdir. ATMEGA328, serbest çalışma modunda yapılandırılabilir. Bu modda, ADC dönüşümü bir öncekinden sonra otomatik olarak başlar. Bir dönüştürme her bittiğinde , ADC sonucunun okunup işlenebildiği kesme rutini ISR(ADC_vect)’ i çağıran bir kesme oluşturulur .

Serbest çalışma modunu kurmak için üç kayıt yapılandırılmalıdır: ADMUX, ADCSRA ve ADCSRB. Bu kayıtların ayrıntılı bir açıklaması ATMEGA328 veri sayfasında bulunabilir. Bu durumda dahili 1,1 volt referans voltajı ve ADMUX ile analog giriş kanalı ADC3 seçilir. Saat frekansı ADCSRA’da seçilmiştir ve bu örnekte ÷16’ya ayarlanmıştır. Tek bir analog dönüştürme 13 saat döngüsü sürer. Örnekleme hızı bu ayardan ve CPU saat frekansından hesaplanabilir: 16 MHz/(16*13) ≈ 77kHz. ADCSRA’da bit 6’yı yüksek yaparak, serbest çalışma dönüşümü başlar.

AD dönüşümünün sonucu, kesme rutini ISR(ADC_vect) içinde okunur . Analogdan dijitale dönüştürmenin sonucu 10 bit uzunluğunda olduğundan, iki bayt genişliğindeki kayıtlara bölünmüştür: ADCL ve ADCH. Doğru bir okuma için, önce ADCL’nin okunması ve sadece daha sonra ADCH kaydının okunması önemlidir. Örnek kodda ADC sonucu “aval” int değişkenine kopyalanmıştır.

int marker = 12;   // marker output pin
int aval = 0;      // analog value

void setup() {
  pinMode(marker, OUTPUT); // pin = output
  DIDR0 = 0x3F;            // digital inputs disabled
  ADMUX = 0xC3;            // measuring on ADC3, use the internal 1.1 reference
  ADCSRA = 0xAC;           // AD-converter on, interrupt enabled, prescaler = 16
  ADCSRB = 0x40;           // AD channels MUX on, free running mode
  bitWrite(ADCSRA, 6, 1);  // Start the conversion by setting bit 6 (=ADSC) in ADCSRA
  sei();                   // set interrupt flag
}

void loop() {
}

/*** Interrupt routine ADC ready ***/
ISR(ADC_vect) {
  bitClear(PORTB, 4); // marker low
  aval = ADCL;        // store lower byte ADC
  aval += ADCH << 8;  // store higher bytes ADC
  bitSet(PORTB, 4);   // marker high
}

Kod 2: ADC değerini okumanın yürütme süresini ölçün.

Şekil 3: Bölme faktörü 16’ya ayarlanmış serbest çalışan analog dönüştürme. (Zaman tabanı: 5 μs/böl)

Yürütme süresini ölçmek için, işaretleyici ADC’yi okumadan önce düşük yapılır ve ondan sonra tekrar yüksek yapılır. Kesinti rutininin toplam yürütme süresinin daha uzun olduğunu unutmayın.

“Döngünün” artık tamamen ücretsiz olduğuna ve diğer kodları yürütmek için kullanılabildiğine dikkat edin.


Hangi referans?

Bir analog sinyali ölçmek için, onunla karşılaştırılacak bir voltaj seviyesi olmalıdır. Bu gerilime referans denir. Arduino’nun ATMEGA328’inde bu referans voltajı aynı zamanda ölçülebilen maksimum voltajdır. Gerilimler her zaman toprağa göre ölçülür. Arduino’nun üç referans voltaj seçeneği vardır: Dijital 5 V güç hattına bağlı AVcc, dahili 1,1 V ve harici voltaj referansı kullanma seçeneği. Giriş voltajlarının ölçümleri referans voltajına göre yapıldığından, referans voltajındaki dalgalanmalar sonucu etkileyecektir.

Referans voltajı, analogReference() işleviyle veya ADMUX kaydındaki REFS[1:0] bitleriyle ayarlanabilir.

AVcc referansı

AVcc, varsayılan referans voltajıdır ve yalnızca doğrudan besleme voltajına bağlı voltajları ölçerken kullanışlıdır. Bu, yarım direnç köprüsünün voltajının şekil 4’te gösterildiği gibi ölçülmesi gereken durumdur. Herhangi bir nedenle besleme voltajı düşerse, iki direncin bağlantısındaki voltaj orantılı olarak düşecektir. Artık referans ve giriş voltajı orantılı olarak değiştiğinden, ADC sonucu hala aynı olacaktır.

Dahili 1,1 V referans

Harici voltajların kesin ölçümleri için dahili 1,1 V referansını kullanın. 1,1 V referansı daha kararlıdır ve besleme gerilimine veya sıcaklığa pek bağlı değildir. Bu nedenle mutlak ölçümler yapılabilir. Şekil 5’teki örnek, 0 ile 11 V arasındaki harici voltajları ölçmek için 1,1 V referansı ve 10:1 direnç bölücüyü kullanır.

Şekil 4: Yarım köprüde bir direnci ölçerken 5 V referansını kullanın.
Şekil 5: Harici voltajları ölçerken dahili 1,1 V veya harici referansı kullanın.

Kesinlik

ATMEGA328 veri sayfasına göre referans voltajı 1,1 ±0,1 V’tur. Bu oldukça büyük bir toleranstır. Test edilen Arduino Nano’nun ölçülen referans voltajı 21 °C ortam sıcaklığında 1.089 V ve paket sıcaklığı 29 °C idi. ATMEGA328’in paketi dondurarak sprey ile -18 °C’ye soğutulduğunda, referans voltajı 1.084 V olarak ölçülür. Bu nedenle sıcaklık kayması 100 ppm/°C’dir.

Yanında kod bulunan bir test, sıcaklık değişikliği nedeniyle hafif bir sonuç kaymasını doğrular. Analog pin 3, 0,545 V’luk bir kaynağa bağlanır. 29 °C’lik bir kasa sıcaklığında sonuç: (0.455/1.089)*1024 = 512 (511 okuma) olmalıdır ve -18 °C sıcaklıkta şu olur: (0.545/1.084)*1024 = 515 ( 515 okuma)

Bununla birlikte, sıcaklık kayması düşüktür, kesin bir ölçüm için bir Arduino uygulamasının %10’luk büyük genel belirsizliği nedeniyle kalibre edilmesi gerekir.

int analogPin = 3;   // analog input pin

void setup() {
  analogReference(INTERNAL);    // select internal 1.1 volt reference
  Serial.begin(9600);
}

void loop() {
  int aval = analogRead(analogPin); // sample analog input
  Serial.println(aval);
  delay(300);
}

Kod 3: Ölçülen değerleri saniyede üç kez okuyun.


Gürültü

Gürültü seviyesini ölçmenin bir yolu, ADC’den gelen değerlerdeki yayılmaya bakmaktır. Bunu yapmak için analog girişlerden birine kararlı bir DC seviyesi sunulur ve dönüştürülen değerler bir histogram oluşturmak için gruplanır.

Şekil 6: Bu devre, Arduino analog girişine ayarlanabilir bir DC voltajı sağlar.
Test devresi

Şekil 6’daki devre test voltajını Arduino’ya iletir. Kaynak, 0,55 V’luk bir voltajda ayarlanmış, 1,1 V referans voltajını yarıya indiren oldukça kararlı, düşük gürültülü ayarlanabilir bir güç kaynağıdır. Sinyal, R1, C1,2 tarafından ekstra filtrelenir ve 100 Ω direnç R2 ile Arduino’nun analog giriş A3’e bağlanır. Toprak aynı sırada GND pinine bağlanır.

Bindirme

ATMEGA328 ADC, 2 10  = 1024 bit çözünürlüğe sahiptir . Binning fikri, belirli bir değerin ne sıklıkla meydana geldiğini saymaktır. Bu nedenle, olası ADC değerlerinin her birini temsil eden bins adı verilen 1024 yer ile bir dizi oluşturulur. Kullanılabilir bellek sınırlı olduğundan, yalnızca bayt boyutundaki kutular oluşturulabilir. Bu nedenle sayım sayısı 255 ile sınırlıdır.

iki program

Gürültü, analogRead işlevi ve ayrıca kesme yöntemi kullanılarak test edilir . İki program özünde aynı şeyi yapar: Bir dizi 1024 bin ile tanımlanır. Kurulumda tüm kutular 0’a ayarlanır ve dahili 1,1 V referans voltajı seçilir.

Her iki program da 10000 sahte okuma kullanır. Bundan sonra gruplama başlar ve her ADC sonucunda ilgili kutu bir artar. 1024 binden biri maksimum 255 sayıma ulaşırsa, alım durur ve tüm 1024 bin değerleri bilgisayara gönderilir.

int analogPin = 3;   // analog input pin
int sendStatus = 0;  // send status
int startDelay = 0;
byte valueBin[1024]; // value bins

void setup() {
  analogReference(INTERNAL);    // select internal 1.1 volt reference
  for (int i=0; i<=1023; i++) { // clear bins
    valueBin[i] = 0;
  }
  Serial.begin(9600);
  Serial.println("Start");
}

void loop() {
  int aval = analogRead(analogPin); // sample analog input
  if (sendStatus == 0) { // do nothing the first x samples
    if (startDelay < 10000) {
      startDelay++;
    }
    else {
      valueBin[aval] += 1; // increase value bin
      if (valueBin[aval] == 255) { // stop if a bin is full
        sendStatus = 1;
      }
    }
  }
  if (sendStatus == 1) {
    for (int i=0; i<=1023; i++) { // output bin values
      Serial.print(i);
      Serial.print("\t");
      Serial.println(valueBin[i]);
    }
    Serial.println("Done");
    sendStatus = 2;
  }
}

Kod 4: Ölçülen değerleri analogRead yöntemiyle gruplama.

int sendStatus = 0;  // send status
int startDelay = 0;
byte valueBin[1024]; // value bins

void setup() {
  TIMSK0 = 0x00;           // disable timer (causes anoying interrupts)
  DIDR0 = 0x3F;            // digital inputs disabled
  ADMUX = 0xC3;            // measuring on ADC3, right adjust, internal 1.1V ref
  ADCSRA = 0xAC;           // AD-converter on, interrupt enabled, prescaler = 128
  ADCSRB = 0x40;           // AD channels MUX on, free running mode
  bitWrite(ADCSRA, 6, 1);  // Start the conversion by setting bit 6 (=ADSC) in ADCSRA
  sei();							// Set global interrupt flag
  for (int i=0; i<=1023; i++) { // clear bins
    valueBin[i] = 0;
  }
  Serial.begin(9600);
  Serial.println("Start");
}

void loop() {
  if (sendStatus == 1) {
    for (int i=0; i<=1023; i++) { // output bin values
      Serial.print(i);
      Serial.print("\t");
      Serial.println(valueBin[i]);
    }
    Serial.println("Done");
    sendStatus = 2;
  }
}

/*** Interrupt routine ADC ready ***/
ISR(ADC_vect) {
  int aval = ADCL;    // store lower byte ADC
  aval += ADCH << 8;  // store higher bytes ADC
  if (sendStatus == 0) {
    if (startDelay < 10000) { // do nothing the first x samples
      startDelay++;
    }
    else {
      valueBin[aval] += 1; // increase value bin
      if (valueBin[aval] == 255) { // stop if a bin is full
        sendStatus = 1;
      }
    }
  }
}

Kod 5: Ölçülen değerleri kesme yöntemiyle bindirme.

Test frekansları

Test, analogRead işlevi ve sürekli örnekleme yöntemi ile gerçekleştirilir. Son durumda örnek frekansı ayarlanabilir olduğu için ADCSRA = 0xAC satırındaki değer değiştirilerek dört farklı frekans test edilir ; . Bunlar: 9,6 kHz (clk÷ 128), 19,2 kHz (clk÷64), 38,4 kHz (clk÷32) ve 76,9 kHz (clk÷16). AnalogRead ile örnek frekansı yaklaşık 9,1 kHz’dir.

Sonuçlar

Her iki yöntemin sonuçları ve tüm örnek frekansları benzerdir. Numuneler iki bitişik kutuya bölünmüştür ve istisnai olarak bazı örnekler üçüncü bir kutuda bulunabilir. Bu, gürültü seviyelerinin her durumda kabul edilebilir derecede düşük olduğu anlamına gelir.

Şekil 7: Her ADC örnek frekansında, ölçülen DC voltajı, beklendiği gibi histogramda dar bir artış verir.

Girişler ve referans gerilimler arasında geçiş

Arduino Nano’nun 8 analog girişi vardır. ADC aynı anda sadece bir sinyali sayısallaştırabildiğinden, dönüştürülecek analog giriş ADC başlamadan önce seçilmelidir. Ayrıca öncelikle gerekli referans voltajı seçilmelidir.

Analog girişlerin seçilmesi

Analog giriş seçimi analogPin = n ile yapılır ; burada n , analog pin numarasıdır veya ADMUX kaydındaki Analog Kanal Seçim Bitleri MUX[3:0] değiştirilerek. Serbest çalışma modu kullanılıyorsa özellikle dikkat edilmelidir: Yeni bir analog dönüştürme başlamadan önce analog kanal seçilmelidir. Kesinti rutininde, bir sonraki kesintide sonucun okunacağı bir analog giriş seçilir.

Girişler değiştirildiğinde gürültü seviyelerini ve doğruluğu test etmek için, aşağıdaki ek kodlar kod 4 ve 5’e dahil edilmiştir. Analog giriş 5’e ikinci bir voltaj uygulanır. Ayrıca şimdi ölçülen değerler tıpkı gürültü testinde olduğu gibi bindirilir.

void loop() {
  if (analogPin == 3) {
    analogPin = 5;}
  else {
    analogPin = 3;}
  int aval = analogRead(analogPin);
  ...

Kod 6: Analog girişler arasında geçiş yapmak için analogRead işlevinin modifikasyonu.

ISR(ADC_vect) {
  if (ADMUX == 0xC3) {
    ADMUX = 0xC5;}
  else {
    ADMUX = 0xC3;}
  int aval = ADCL;
  ...

Kod 7: Analog girişler arasında geçiş yapmak için sürekli örnekleme modu için değişiklik.

Sonuçlar

Ölçülen her iki voltaj da histogramlarda iki ani yükselme olarak görülebilir. Şekil 8, beş testin histogramlarını gösterir: AnalogRead işlevi, ÷128, ÷64, ÷32 ve ÷16’da serbest çalışma. İlk voltajın ölçülen değerleri (ADC değeri 511) önceki gürültü testinden farklı değil. Yani ölçüm hala doğrudur. Çevredeki çöp kutuları çok küçüktür, bu da gürültü seviyesinin artmadığı anlamına gelir.

Şekil 8: Beş histogramın her biri, ölçülen iki voltajı temsil eden iki ani artışı gösterir.

Referans voltajının değiştirilmesi

Referans voltajının değiştirilmesi, analog girişler arasında geçiş yapmak kadar hızlı yapılamaz. Bunun nedeni Arduino kartındaki ATMEGA328’in AREF pinine bağlı 100 nF kapasitördür. Bu kapasitörün şarj olması ve boşalması için zamana ihtiyacı vardır. Şekil 9, bu yerleşme gecikmelerinin bir osiloskop grafiğini göstermektedir. Referans voltajı 1,1 V’tan 5 V’a değiştirmek için gereken süre oldukça hızlıdır: 15 μsn, ancak ters yönde 5 V’tan 1,1 V’a değişiklik en az 5 ms sürer.

void loop() {
  if (swr == true) {
    analogReference(DEFAULT);
    swr = false;
  }
  else {
    analogReference(INTERNAL);
    swr = true;
  }
  delay(8);
  int aval = analogRead(analogPin);
  ...

Kod 8: Referans voltajları arasında geçiş yapmak için analogRead işlevi için değişiklik.

Şekil 9: Referans voltajının oturma süresi (Zaman tabanı: 2 ms/böl, Dikey: 2 V/böl)

Sürekli örnekleme yöntemi kullanılırken referans voltajlar arasında değişiklik yapmaktan kaçınılmalıdır. Karışık girişler olduğunda: Şekil 4’teki gibi harici voltajları ve direnç köprülerini ölçmek, direnç köprüsünü 1.1 referans voltajından beslemek tercih edilir. Referans voltajının yalnızca küçük akımlar iletebileceğini unutmayın. 4,7 kΩ direnç yükü, referans voltajının 1,0880’den 1,0857’ye düşmesine izin verir. Bu durumda AREF voltajını bir opamp ile tamponlamak ve direnç yüklerini opamp çıkışına bağlamak en iyisidir.


Örnek frekans ve çözünürlük

Çözünürlüğü örnekleme frekansına karşı analiz etmek için aynı iki gruplama kodu 4 ve 5 kullanılır. Bu test için Arduino analog girişine şekil 10’da gösterildiği gibi bir fonksiyon üreteci bağlanır. Fonksiyon üreteci 25 mV üst-üst gerilim ve 0,55 V ofset gerilim (= ortalama değer) ile üçgen dalga şekli verir. ölçüm için dalga biçimi frekansı, örnek frekansı 163 kat daha yüksek olacak şekilde seçilir.

Her değer (kuantize edildiğinde) eşit derecede ortak olduğu için bir üçgen dalga biçimi seçilir. Böyle bir sinyal bindirildiğinde, minimum ve maksimum voltaj içindeki her bir bin değeri aynı sayıya sahip olmalıdır.

Şekil 10: Arduino analog girişine bir fonksiyon üreteci bağlanmıştır.

Sonuçlar

Şekil 11’deki sonuçlar düşük frekanslı örneklemeyi gösterdiği için analogRead ve clk÷128’de devam eden örnekleme makul bir düz tepeye sahiptir: örneğin, aralık içindeki tüm değerler aynı sayı ile oluşur. Ancak daha yüksek örnekleme frekanslarında (clk÷64, clk÷32 ve clk÷16) gruplama bloğunda frekansla daha da kötüleşen boşluklar görünür.

ATMEGA328’in veri sayfası bu fenomen hakkında uyarır: 200 kHz’in üzerindeki bir ADC saatinde çözünürlük düşer. 16 MHz’lik bir saat frekansında ve 64’lük bir bölme faktöründe, ADC saati 250 kHz olur, bu, ölçümler tarafından onaylandığı üzere çözünürlüğün daha az olduğu eşiğin hemen üzerindedir.

Şekil 11: Bir üçgen voltajı düz bir tepe histogramı vermelidir. 
Örnek frekansı ne kadar yüksek olursa, o kadar fazla boşluk görünür.