Arduino'da seri iletişim nasıl çalışır?


16

Arduino Uno, Mega2560, Leonardo ve benzeri kartlara referansla:

  • Seri iletişim nasıl çalışır?
  • Seri ne kadar hızlı?
  • Bir gönderen ve alıcı arasında nasıl bağlantı kurabilirim?

Lütfen dikkat: Bu referans bir soru olarak düşünülmüştür.


Python veri kayıt cihazı çalıştıran bir Raspian sistemine bağlı bir Nano'nun her iki tarafındaki tamponlar hakkında bu ilginç olabilir, ikisi arasında sadece normal bir USB programlama kablosu kullanarak: arduino.stackexchange.com/questions/11710/…
SDsolar

Yanıtlar:


16

Asenkron seri (genellikle seri olarak adlandırılır) iletişim, bir cihazdan diğerine bayt göndermek için kullanılır. Bir cihaz aşağıdakilerden biri veya daha fazlası olabilir:

  • Arduino
  • PC
  • Küresel Konumlama Sistemi
  • RFID kart okuyucu
  • LCD ekran
  • Modem
  • Diğer

Saat hızı ve verilerin örneklenmesi

SPI / USB / I2C'nin aksine seri iletişimde saat sinyali yoktur. Örnekleme saati, üzerinde anlaşmaya varılmış bir numune oranıdır (baud hızı olarak bilinir). Hem gönderenin hem de alıcının aynı hızı kullanacak şekilde yapılandırılması gerekir ya da alıcı anlamsız veriler alacaktır (bitlerin gönderildikleri hızda örneklenmemesi nedeniyle).

İletim eşzamansızdır, bu da temel olarak baytların her zaman aralarında değişen boşluklarla gönderilebileceği anlamına gelir. Bu grafik gönderilen tek bir baytı göstermektedir:

Seri haberleşme - bir bayt gönderme

Yukarıdaki grafik iletilen 'F' harfini göstermektedir. ASCII'de bu 0x46 (onaltılık) veya 0b01000110 (ikili olarak) şeklindedir. En az anlamlı bir (düşük değerli) biraz sırayla gelen bit görmek, böylece grafik yukarıdaki birinci iletilir: 01100010.

Baytlar arasındaki "boşta" süre sürekli "1" bit olarak iletilir (etkin bir şekilde iletim hattı sürekli olarak yüksek tutulur).

Bir baytın başlangıcını belirtmek için, Başlangıç ​​Biti daima çizgiyi grafikte gösterildiği gibi aşağı çekerek gösterilir. Alıcı başlangıç ​​bitini gördükten sonra, numune süresinin 1.5 katı kadar bekler ve sonra veri bitlerini örnekler. 1.5 kez bekler, böylece:

  • Başlangıç ​​bitini atlar
  • Sonraki bitin yarısında örnekler

Örneğin, baud hızı 9600 baud ise, örnekleme hızı 1/9600 = 0.00010416saniye (104.16 µs) olacaktır.

Böylece, 9600 baud'da, bir başlangıç ​​biti alındıktan sonra alıcı 156.25 µs bekler ve sonra her 104.16 µs'de numune alır.

Bit zamanlamasını başlat

Stop Bit'in amacı, her bayt arasında kesinlikle 1 bit olmasını sağlamaktır. Durdurma biti olmadan, bir bayt sıfırla biterse, donanımın bu ile bir sonraki baytın başlangıç ​​biti arasındaki farkı anlatması imkansızdır.

Yukarıdaki çıktıyı bir Uno'da üretmek için bu kodu yazabilirsiniz:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Veri biti sayısı

İletim süresinden tasarruf etmek için (eski günlerde, heh) farklı sayıda veri biti belirtmenize izin verildi. AtMega donanımı, 5'ten 9'a kadar olan veri bitlerini destekler. Açıkça daha az veri, daha az bilgi gönderebilir, ancak daha hızlı olacaktır.


Eşlik bitleri

İsteğe bağlı olarak bir eşlik bitiniz olabilir. Bu, gerekirse, karakterdeki 1'lerin sayısını sayarak ve sonra bu sayının tek olduğundan emin olunarak veya hatta eşlik bitini gerektiği gibi 0 veya 1'e ayarlayarak hesaplanır.

Örneğin, "F" harfi (veya 0x46 veya 0b01000110) için orada 3 tane olduğunu görebilirsiniz (01000110'da). Böylece zaten garip paritemiz var. Yani, parite biti aşağıdaki gibi olacaktır:

  • Eşlik yok: atlandı
  • Eşlik eşitliği: 1 (3 + 1 eşittir)
  • Tek parite: 0 (3 + 0 garip)

Eşlik biti, varsa, son veri bitinden sonra ancak durdurma bitinden önce görünür.

Alıcı doğru eşlik bitini alamazsa buna "eşlik hatası" denir. Bazı problemler olduğunu gösterir. Muhtemelen gönderici ve alıcı farklı baud (bit) hızları kullanacak şekilde yapılandırılmıştır veya hatta sıfıra bire dönüşen veya tam tersi bir gürültü vardı.

Bazı erken sistemlerde ayrıca "işaret" paritesi (verilerden bağımsız olarak parite bitinin her zaman 1 olduğu) veya "boşluk" paritesi (verilerden bağımsız olarak parite bitinin her zaman 0 olduğu) kullanıldı.


9 bit iletim

Bazı iletişim ekipmanı 9 bit veri kullanır, bu nedenle parite biti 9. bite dönüştürülür. Bu 9. biti göndermek için özel teknikler vardır (kayıtlar 8 bit kayıtlardır, bu nedenle 9. bit başka bir yere konulmalıdır).


Durdurma biti sayısı

İlk ekipman elektronik olarak biraz daha yavaş olma eğilimindeydi, bu nedenle alıcıya gelen baytı işlemek için zaman vermek için bazen gönderenin iki durdurma biti göndereceği belirtildi. Bu, temel olarak bir sonraki başlangıç ​​bitinin görünmesi için veri hattının yüksek tutulduğu (daha bir bit zamanı) daha fazla zaman ekler. Bu ekstra bit süresi alıcıya son gelen baytı işlemek için zaman tanır.

Eğer durdurma bitinin olması gerektiği zaman alıcı mantıksal 1 almazsa buna "çerçeveleme hatası" denir. Bazı problemler olduğunu gösterir. Büyük olasılıkla gönderici ve alıcı farklı baud (bit) hızlarını kullanacak şekilde yapılandırılmıştır.


Gösterim

Genellikle, seri iletişim size hızı, veri bitlerinin sayısını, eşlik tipini ve durdurma bitlerinin sayısını söyleyerek gösterilir:

9600/8-N-1

Bu bize şunu söylüyor:

  • Saniyede 9600 bit
  • 8 veri biti
  • Eşlik yok (bunun yerine görebilirsiniz: E = çift, O = tek)
  • 1 durdurma biti

Gönderenin ve alıcının yukarıdakiler üzerinde anlaşması önemlidir, aksi takdirde iletişimin başarılı olması olası değildir.


Pim-çıkışları

Arduino Uno, donanım seri için 0 ve 1 dijital pinlerine sahiptir:

Arduino Uno seri pinleri

İki Arduino'yu bağlamak için Tx ve Rx'i şu şekilde değiştirebilirsiniz :

İki Arduino'yu birbirine bağlama


hız

Çok çeşitli hızlar desteklenir (aşağıdaki grafiğe bakın). "Standart" hızlar genellikle 300 baudun katlarıdır (örn. 300/600/1200/2400 vb.).

Diğer "standart dışı" hızlar, uygun kayıtlar ayarlanarak işlenebilir. HardwareSerial sınıfı bunu sizin için yapar. Örneğin.

Serial.begin (115200);  // set speed to 115200 baud

Pratik bir kural olarak, 8 bit veri kullandığınızı varsayarsak, baud hızını 10'a bölerek saniyede iletebileceğiniz bayt sayısını tahmin edebilirsiniz (başlangıç ​​biti ve durdurma biti nedeniyle).

Böylece, 9600 baud'da 9600 / 10 = 960saniyede 960 bayt ( ) iletebilirsiniz .


Baud hızı hataları

Atmega'daki baud hızı, sistem saatini bölerek ve sonra önceden ayarlanmış bir sayıya kadar sayarak üretilir. Veri sayfasındaki bu tablo, 16 MHz'lik bir saat için (Arduino Uno'daki gibi) kayıt değerlerini ve hata yüzdelerini gösterir.

Baud hızı hataları

U2Xn biti saat hızı bölücüsünü etkiler (0 = 16'ya bölün, 1 = 8'e bölün). UBRRn kaydı, işlemcinin saydığı sayıyı içerir.

Yukarıdaki tablodan, 16 MHz'lik bir saatten 9600 baud aldığımızı görüyoruz:

16000000 / 16 / 104 = 9615

Sayaç sıfır göreli olduğundan 103'e değil 104'e böleriz. Dolayısıyla, buradaki hata, 15 / 9600 = 0.0016yukarıdaki tabloda söylediklerine yakındır (% 0.02).

Bazı baud hızlarının hata miktarının diğerlerinden daha yüksek olduğunu fark edeceksiniz.

Veri sayfasına göre 8 veri biti için maksimum hata yüzdesi% 1.5 ila% 2.0 arasındadır (daha fazla ayrıntı için veri sayfasına bakın).


Arduino Leonardo

Arduino Leonardo ve Micro, seri bağlantı noktasına değil, doğrudan USB üzerinden ana bilgisayara bağlandıklarından, seri iletişim için farklı bir yaklaşıma sahiptir.

Bu nedenle, Seri'nin "hazır" olmasını beklemelisiniz (yazılım bir USB bağlantısı kurarken), aşağıdaki gibi fazladan birkaç satırla:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // wait for Serial comms to become ready
      Serial.print("Fab");
  }

void loop ()
  {
  }

Ancak, D0 ve D1 pinleri aracılığıyla (USB kablosu yerine) gerçekten iletişim kurmak istiyorsanız, Seri yerine Serial1 kullanmanız gerekir. Bunu şöyle yapıyorsun:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Voltaj seviyeleri

Arduino'nun seri iletişim için TTL seviyelerini kullandığını unutmayın. Bu beklediği anlamına gelir:

  • "Sıfır" bir bit 0V'dir
  • "Bir" bit + 5V

Bir bilgisayarın seri portuna takılmak üzere tasarlanmış daha eski seri ekipman muhtemelen RS232 voltaj seviyelerini kullanır:

  • Bir "sıfır" bit +3 ila +15 volttur
  • "Bir" bit −3 ila −15 volttur

Bu sadece TTL seviyelerine ("bir" bir "sıfır" dan daha negatif) "ters" değil, Arduino giriş pinleri üzerindeki negatif gerilimleri (5V'dan daha yüksek pozitif gerilimleri) kaldıramaz.

Bu nedenle, bu tür cihazlarla iletişim kurmak için bir arayüz devresine ihtiyacınız vardır. Yalnızca (Arduino'ya) giriş için, basit bir transistör, diyot ve birkaç direnç bunu yapar:

Ters tampon

İki yönlü iletişim için negatif voltaj üretebilmeniz gerekir, bu nedenle daha karmaşık bir devre gereklidir. Örneğin, MAX232 yongası bunu dört adet 1 µF kapasitör ile birlikte şarj pompası devresi olarak görev yapar.


Yazılım Seri

SoftwareSerial adı verilen ve donanımdan ziyade yazılımda seri iletişim (bir noktaya kadar) yapmanızı sağlayan bir kütüphane vardır. Bunun avantajı, seri iletişim için farklı pin yapılandırmalarını kullanabilmenizdir. Dezavantajı, yazılımda seri yapmanın daha fazla işlemci yoğunluğu ve hataya daha yatkın olmasıdır. Daha fazla bilgi için Yazılım Seri Numarası'na bakın.


Mega2560

Arduino "Mega" 3 ek donanım seri bağlantı noktasına sahiptir. Tahtada Tx1 / Rx1, Tx2 / Rx2, Tx3 / Rx3 olarak işaretlenmiştir. Mümkünse SoftwareSerial yerine kullanılmalıdır. Bu diğer bağlantı noktalarını açmak için Serial1, Serial2, Serial3 adlarını aşağıdaki gibi kullanırsınız:

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Kesmeler

HardwareSerial kitaplığını kullanarak hem gönderme hem de alma kesintileri kullanır.

gönderme

A yaptığınızda Serial.print, yazdırmaya çalıştığınız veriler dahili bir "iletim" arabelleğine yerleştirilir. 1024 bayt veya daha fazla RAM'iniz varsa (Uno'da olduğu gibi) 64 baytlık bir tampon alırsınız, aksi takdirde 16 baytlık bir tampon alırsınız. Tamponda yer varsa, Serial.printhemen geri döner, böylece kodunuzu geciktirmez. Eğer yer yoksa, tamponun oda olması için yeterince boşaltılmasını bekleyen "engeller".

Daha sonra, her bayt donanım tarafından iletildikçe bir kesme denir ("USART, Veri Kaydı Boş" kesme) ve kesme rutini tampondan seri porttan sonraki baytı gönderir.

kabul

Gelen veriler alındıkça bir kesme rutini denir ("USART Rx Complete" kesme) ve gelen bayt bir "alma" tamponuna (yukarıda belirtilen iletim tamponu ile aynı boyutta) yerleştirilir.

Aradığınızda Serial.availablebunu mevcuttur kaç bayt öğrenmek "alma" tampon. Aradığınızda Serial.readbir bayt alma arabelleğinden kaldırılır ve kodunuza geri döner.

1000 bayt veya daha fazla RAM içeren Arduinos'ta, veriyi doldurmasına izin vermemeniz koşuluyla, alma arabelleğindeki verileri kaldırmak için acele yoktur. Doldurulursa, gelen diğer veriler atılır.

Bu arabellek boyutu nedeniyle, çok sayıda baytın gelmesini beklemenin bir anlamı olmadığını unutmayın, örneğin:

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

Bu asla işe yaramaz, çünkü arabellek bu kadar tutamaz.


İpuçları

  • Okumadan önce verilerin daima mevcut olduğundan emin olun. Örneğin, bu yanlış:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

    Serial.availableTest Sadece sahip sağlayan bir ancak kod ikisini okumaya çalışır, bayt kullanılabilir. Tamponda iki bayt varsa işe yarayabilir, değilse -1 döndürülür ve yazdırılırsa 'ÿ' gibi görünür.

  • Veri göndermenin ne kadar sürdüğünün farkında olun. Yukarıda belirtildiği gibi, 9600 baud'da saniyede sadece 960 bayt iletirsiniz, bu nedenle 9600 baud'da bir analog bağlantı noktasından 1000 okuma göndermeye çalışmak çok başarılı olmaz.


Referanslar


1. grafikte: oklarla ilk önce durdurma biti iletilmiş gibi görünüyor. Eğer Rx / Tx ve okların yönünü değiştirdiyseniz, bunun daha az kafa karıştırıcı olduğunu düşünürdüm.
ott--

Soldan sağa okunması amaçlanmıştır (bu cümle gibi) ve böylece soldaki şeyler önce gerçekleşir. Şöyle koyun: bir osiloskopta, izi bu şekilde görürdünüz.
Nick Gammon

Tamam osiloskop açıklaması ile satın alıyorum. :-)
ott--

Bununla birlikte, amacınızın çok mantıklı olduğunu düşünüyorum. Diğerleri ne düşünüyor? Okların ters çevrilmesi daha açık olur muydu ve ben Rx / Tx değiştirdim mi?
Nick Gammon

1
@ linhartr22 Muhtemelen daha yakın olan "anlamsız verileri" okumak için değiştirdim.
Nick Gammon
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.