Arduinoではシリアル通信はどのように機能しますか?


16

Arduino Uno、Mega2560、Leonardoおよび同様のボードを参照して:

  • シリアル通信はどのように機能しますか?
  • シリアルはどれくらい高速ですか?
  • 送信者と受信者を接続するにはどうすればよいですか?

注:これは参照用の質問です。


次の2つの間の普通のUSBプログラミングケーブルを使用して、ナノの両側にバッファに関するこの興味深いが、Pythonのデータロガーを実行しているRaspianシステムに接続されているかもしれません: arduino.stackexchange.com/questions/11710/...
SDsolar

回答:


16

非同期シリアル(通常はシリアルと呼ばれる)通信は、デバイス間でバイトを送信するために使用されます。デバイスは、次のうちの1つ以上です。

  • Arduino
  • パソコン
  • GPS
  • RFIDカードリーダー
  • 液晶ディスプレイ
  • モデム
  • その他

クロックレートとデータのサンプリング

SPI / USB / I2Cシリアル通信とは異なり、クロック信号はありません。サンプリングクロックは、合意されたサンプルレート(ボーレートと呼ばれます)です。送信者と受信者の両方が同じレートを使用するように設定する必要があります。そうしないと、受信者は無意味なデータを受信します(ビットは送信されたのと同じレートでサンプリングされないため)。

転送は非同期です。つまり、基本的に、バイトはいつでも送信でき、それらの間にギャップがあります。この図は、送信される単一バイトを示しています。

シリアル通信-1バイトの送信

上の図は、送信されている文字「F」を示しています。ASCIIでは、これは0x46(16進数)または0b01000110(バイナリ)です。少なくとも有意な(下位)ビットを使用して、順番に到着したビットを参照してくださいので、上記の図に、最初に送信されています01100010

バイト間の「アイドル」時間は、連続した「1」ビットとして送信されます(事実上、送信ラインは連続的にハイに保持されます)。

バイトの開始を示すために、グラフィックに示されているように、ラインをローにプルすることにより、開始ビットが常に示されます。レシーバは、スタートビットを検出すると、サンプル時間の1.5倍の時間待機してから、データビットをサンプリングします。次のように1.5回待機します。

  • 開始ビットをスキップします
  • 次のビットの途中までのサンプル

たとえば、ボーレートが9600ボーの場合、サンプルレートは1/9600 = 0.00010416秒(104.16 µs)になります。

したがって、9600ボーで、スタートビットを受信した後、レシーバは156.25 µs待機し、その後104.16 µsごとにサンプリングします。

スタートビットタイミング

ストップビットの目的は、各バイト間に1 ビットが確実に存在するようにすることです。ストップビットがなければ、バイトがゼロで終了した場合、ハードウェアがそのビットと次のバイトのスタートビットの違いを知ることは不可能です。

Unoで上記の出力を生成するには、次のコードを記述できます。

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

void loop ()
  {
  }

データビット数

送信時間を節約するために(昔は)、異なるデータビット数を指定することが許可されていました。AtMegaハードウェアは、5〜9のデータビット番号をサポートします。明らかに、データビットが少ないほど、送信できる情報は少なくなりますが、速くなります。


パリティビット

オプションで、パリティビットを持つことができます。これは、必要に応じて、文字の1の数をカウントし、必要に応じてパリティビットを0または1に設定して、この数が奇数または偶数であることを確認することによって計算されます。

たとえば、文字 "F"(または0x46または0b01000110)の場合、3つ(01000110に)あることがわかります。したがって、すでに奇数のパリティがあります。したがって、パリティビットは次のようになります。

  • パリティなし:省略
  • 偶数パリティ:1(3 + 1は偶数)
  • 奇数パリティ:0(3 + 0は奇数)

パリティビットが存在する場合、最後のデータビットの後、ストップビットの前に表示されます。

受信者が正しいパリティビットを取得できない場合、それは「パリティエラー」と呼ばれます。問題があることを示しています。おそらく、送信側と受信側が異なるボー(ビット)レートを使用するように構成されているか、回線でノイズが発生してゼロから1に、またはその逆になった可能性があります。

初期のシステムでは、「マーク」パリティ(パリティビットがデータに関係なく常に1だった場合)または「スペース」パリティ(パリティビットがデータに関係なく常に0だった場合)も使用していました。


9ビット伝送

一部の通信機器は9ビットデータを使用するため、これらの場合、パリティビットは9番目のビットに変換されます。この9番目のビットを送信するための特別な手法があります(レジスタは8ビットのレジスタであるため、9番目のビットは別の場所に配置する必要があります)。


ストップビット数

初期の機器は電子的に多少遅くなる傾向があったため、受信バイトに着信バイトを処理する時間を与えるために、送信側が2つのストップビットを送信するように指定されることがありました。これは基本的に、次の開始ビットが現れる前にデータラインがハイに保たれる時間を増やします(もう1ビット時間)。この余分なビット時間により、受信者は最後の着信バイトを処理する時間が得られます。

ストップビットが想定されているときにレシーバが論理1を取得しない場合、それは「フレーミングエラー」と呼ばれます。問題があることを示しています。おそらく、送信者と受信者は異なるボー(ビット)レートを使用するように構成されています。


表記法

一般に、シリアル通信は、次のように、速度、データビット数、パリティの種類、およびストップビット数を示すことで示されます。

9600/8-N-1

これは私たちに言っています:

  • 9600ビット/秒
  • 8データビット
  • パリティなし(代わりに表示される場合があります:E =偶数、O =奇数)
  • 1ストップビット

送信者と受信者が上記に同意することが重要です。同意しないと、通信が成功しない可能性があります。


ピン配列

Arduino Unoには、ハードウェアシリアルに使用可能なデジタルピン0および1があります。

Arduino Unoシリアルピン

2つのArduinoを接続するには、次のようにTxとRx を交換します。

2つのArduinoを接続する


速度

幅広い速度がサポートされています(下の図を参照)。「標準」速度は通常、300ボーの倍数です(例:300/600/1200/2400など)。

その他の「非標準」速度は、適切なレジスタを設定することで処理できます。HardwareSerialクラスがこれを行います。例えば。

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

経験則として、8ビットデータを使用していると仮定すると、ボーレートを10で除算することにより、1秒あたりに送信できるバイト数を推定できます(開始ビットと停止ビットのため)。

したがって、9600ボーでは、9600 / 10 = 9601秒あたり960バイト()を送信できます。


ボーレートエラー

Atmegaのボーレートは、システムクロックを分周し、事前に設定した数までカウントアップすることで生成されます。データシートのこの表は、16 MHzクロック(Arduino Unoのクロックなど)のレジスタ値とエラー率を示しています。

ボーレートエラー

U2Xnビットはクロックレート除数に影響します(0 = 16で除算、1 = 8で除算)。UBRRnレジスタには、プロセッサがカウントアップする数が含まれています。

したがって、上の表から、次のように16 MHzクロックから9600ボーを取得することがわかります。

16000000 / 16 / 104 = 9615

カウンターはゼロ相対であるため、103ではなく104で割ります。したがって、ここでのエラーは15 / 9600 = 0.0016、上記の表に近い(0.02%)です。

ボーレートの中には、他のボーレートよりもエラー量が多いことに気づくでしょう。

データシートによると、8データビットの最大エラー率は1.5%から2.0%の範囲です(詳細についてはデータシートを参照)。


アルドゥイーノ・レオナルド

Arduino LeonardoとMicroは、シリアルポートではなくUSBを介してホストコンピューターに直接接続するため、シリアル通信に対して異なるアプローチを採用しています。

このため、次のような追加の行を追加して、シリアルが(ソフトウェアがUSB接続を確立する際に)「準備完了」になるまで待つ必要があります。

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

void loop ()
  {
  }

ただし、(USBケーブルではなく)ピンD0およびD1を介して実際に通信する場合は、シリアルではなくSerial1を使用する必要があります。次のようにします。

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

void loop ()
  {
  }

電圧レベル

Arduinoはシリアル通信にTTLレベルを使用することに注意してください。これは、次のことを期待することを意味します。

  • 「ゼロ」ビットは0Vです
  • 「1」ビットは+ 5Vです

PCのシリアルポートに接続するように設計された古いシリアル機器は、おそらくRS232電圧レベルを使用します。

  • 「ゼロ」ビットは+3〜+15ボルトです
  • 「1」ビットは-3〜-15ボルトです

TTLレベルに関して「反転」(「1」は「ゼロ」よりも負)であるだけでなく、Arduinoは入力ピンの負電圧(5Vを超える正電圧)も処理できません。

したがって、このようなデバイスと通信するためのインターフェース回路が必要です。(Arduinoへの)入力のみの場合、単純なトランジスタ、ダイオード、およびいくつかの抵抗がそれを行います。

反転バッファー

双方向通信では、負電圧を生成できる必要があるため、より複雑な回路が必要です。たとえば、MAX232チップは、4つの1 µFコンデンサと組み合わせてチャージポンプ回路として機能します。


ソフトウェアシリアル

SoftwareSerialというライブラリがあり、ハードウェアではなくソフトウェアで(ある程度まで)シリアル通信を行うことができます。これには、シリアル通信に異なるピン構成を使用できるという利点があります。欠点は、ソフトウェアでシリアルを実行すると、よりプロセッサに負荷がかかり、エラーが発生しやすくなることです。詳細については、ソフトウェアシリアルを参照してください。


Mega2560

Arduino "Mega"には、3つのハードウェアシリアルポートが追加されています。ボード上でTx1 / Rx1、Tx2 / Rx2、Tx3 / Rx3としてマークされています。可能であれば、SoftwareSerialよりも優先して使用する必要があります。これらの他のポートを開くには、次のようにSerial1、Serial2、Serial3という名前を使用します。

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

割り込み

HardwareSerialライブラリを使用して、送信と受信の両方で割り込みを使用します。

送信

を実行するSerial.printと、印刷しようとしているデータは内部の「送信」バッファに配置されます。1024バイト以上のRAM(Unoなど)がある場合は、64バイトのバッファーを取得します。そうでない場合は、16バイトのバッファーを取得します。バッファに空きがある場合、Serial.printすぐに戻り、コードを遅延させません。スペースがない場合は、バッファーが空になるのを待って「ブロック」し、スペースができるようにします。

次に、各バイトがハードウェアによって送信されると、割り込みが呼び出され(「USART、データレジスタエンプティ」割り込み)、割り込みルーチンがバッファから次のバイトをシリアルポートから送信します。

受信中

着信データが受信されると、割り込みルーチンが呼び出され(「USART Rx Complete」割り込み)、着信バイトが「受信」バッファー(上記の送信バッファーと同じサイズ)に配置されます。

呼び出すSerial.availableと、その「受信」バッファーで使用可能なバイト数がわかります。呼び出すとSerial.read、バイトが受信バッファーから削除され、コードに返されます。

1000バイト以上のRAMを備えたArduinosでは、データをいっぱいにしない限り、受信バッファからデータを削除する必要はありません。いっぱいになると、それ以降の着信データは破棄されます。

このバッファのサイズのために、非常に多くのバイトが到着するのを待つ意味がないことに注意してください。例えば:

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

バッファがそれだけ保持できないため、これは機能しません。


チップ

  • 読む前に、常にデータが利用可能であることを確認してください。たとえば、これは間違っています:

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

    Serial.availableテストは、あなたが持っている保証します1つのしかし、コードは2つを読み取ろうと、バイトが利用可能。バッファに2バイトある場合は動作する可能性があり、そうでない場合は-1が返され、印刷すると「ÿ」のように見えます。

  • データの送信にかかる時間に注意してください。上記のように、9600ボーでは1秒あたり960バイトしか送信できないため、9600ボーでアナログポートから1000の読み取り値を送信しようとしても、あまり成功しません。


参照資料


1番目の図では、矢印でストップビットが最初に送信されているように見えます。Rx / Txと矢印の方向を交換すると、混乱が少なくなると思います。
ott--

この文のように左から右に読むことを意図していたため、左のこと​​が最初に起こります。次のように入力します。オシロスコープでトレースを確認します。
ニックギャモン

オシロスコープの説明でそれを購入します。:-)
ott--

しかし、私はあなたの主張が多くの意味をなすと考えてきました。他の人はどう思いますか?矢印が逆になり、Rx / Txを交換した場合、より明確になりますか?
ニックギャモン

1
@ linhartr22「意味のないデータ」を読むように修正しました。
ニックギャモン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.