私は5〜10人のArduinosの間でコマンドとデータを送るためにコミュニケーションを探しています。 "Arduino"とは、Arduinoプログラミングフレームワークを使ってプログラムされたATmega328を意味します。
各デバイスは同じPCB上にあります。
私はI2CとSPIがある程度通信できることは知っていますが、実際には、複数のマイクロコントローラを相互に接続するのではなく、マイクロコントローラに接続された周辺機器のデバイスを意味し、通常は一度に数バイトのデータしか送信しません。一方、私はLCDディスプレイがそれらを使用して駆動できることを知っています、そして、私はそれらのケースで転送されているデータがたくさんあると思います。
マイクロコントローラ間の通信にI2C(マルチマスタ、マルチスレーブ、シングルエンド、シリアルコンピュータバス)のようなものがありますか?それぞれのデバイスが任意の他のデバイスにデータを送信することを許可する。たとえば、スイッチがデバイスAで押された場合、デバイスBは、スイッチが押されたときにデバイスBが常にAに尋ねることなく、デバイスBに通知します。およびその逆。
Edit: I found some libraries that may do what I want with RS-485. Is this the only viable option for ~1k bps transmission between 5-10 devices? I would imagine TCP/IP over Ethernet would work as well, but that seems like too much. Are there any I2C libraries that allow sending that amount of data between only master devices?
あなたの夢を実現するのに役立ついくつかの概念があります。私は、この答えの中で、あなたが望むものをどのように実装するのかを正確に伝えることはできませんが、実装するのに役立つ概念を示すことができます。
まず、バスマスタというコンセプトがあります。これは必ずしも通信をインスタンス化するデバイスではありません。代わりに、これはバスを所有し制御するデバイスです。
バスマスタではないデバイスがバス上で通信したい場合、まずバスマスタからの許可を求めます。古いZ80(まあ、私は「古い」と言っていますが、今日でもいろいろな形でまだ使用されています)は、他のチップがデータバスとアドレスバスを使用できるようにするためにこのコンセプトを使用しました。 BUSRQとBUSACKの2つの信号で構成されています。デバイスはまず、BUSRQまたはBUSACKのいずれかがアクティブであるかどうかを調べ、どちらもアクティブでない場合は、BUSRQをアクティブにします。バスマスタが他のデバイスにバスを放棄しようとしている場合(その時点でそれを使用していない場合)、バスマスタはBUSACKを起動し、他のデバイスはバスを使用できることを認識します。 BUSRQとBUSACKの両方がリリースされるまで、それ以外のものは使用できません。ニースとシンプルでエレガント。
しかし完璧ではない。 2つのデバイスが非常に同じ瞬間にバスを要求すると決定した場合、衝突が発生します。これは、このような共有バスシステムの共通の問題であり、正しく処理する方法がわからない限り、未解決の問題を引き起こします。
listen-while-you-talk のコンセプトを入力します。これは、別の受信機を介してバス上に送信されているものを聴いているバスを送信しているデバイスを含む。その後、バスで送られたものが実際にバスで終わったかどうかを知ることができます。たとえば、2つのデバイスが同時に発言し、1つが 10011001
を送信し、もう1つが 11001100
を送信した場合、バスに表示される結果は実際にはバス信号の作成方法に応じて 11011101
、または 10001000
だからあなたが送ったものが壊れているのを知ったら、あなたは今それについて何かできます。
次のコンセプト:後退。これは、両方の送信者が短期間待ってから再試行して送信する場所です。彼らが両方の時間の異なる時間のために遅延する限り、しようとする最初のものは、バスを取得し、通信することができます。しかし、あなたはどうやって彼らが異なる時間に両方を遅らせることを保証しますか?答えは簡単だと思うかもしれません: rand()
や random()
のような乱数を使用してください。しかし、それも問題である。
Another concept: The pseudo random number generator
Arduinoは乱数を生成しません。複雑な数式を使って、私たちに無作為に見える数列を作成するだけです。彼らはそうではありません。シリアルを通して10の乱数を印刷し、それを複数回実行するための小さなプログラムを書く(リセットボタンを押す)。毎回同じ順序で同じ「ランダム」番号が見つかるでしょう。別のArduinoで試してみて、同じ数字をもう一度得る。いつも同じ。
じゃあ何をすればいいの?答えは乱数ジェネレータをシードすると呼ばれます。 rand()
などが生成する次の数字は、最後に生成された番号に依存します。 最初のの数字を変更すると、残りの数字はすべて変更されます。しかし、キャッチ22の状況があります。乱数ジェネレータをシードする乱数が必要です。乱数を生成して乱数ジェネレータ...無限にシードするには、乱数ジェネレータを乱数にする必要があります。あなたはそれがどこにあるのかを見ますか? rand()
はランダムなソースからシードされるまでランダムではないので、 rand()
からシードすることはできません。だからあなたはランダムな情報源を見つける必要があります。
それは簡単なことではありません。知られているエントロピーの最良のソースはホワイトノイズです。これは、ダイオード接合部の破壊から、抵抗器の熱変動の非常に高い利得増幅まで、さまざまな回路を使用してさまざまな方法で生成することができます。
すべてはあなたが本当に望むものにはかなり複雑ですが、何かに接続されていないアナログ入力を読み込む方が、ほんの僅かでランダムな方法では簡単です。適切なエントロピージェネレータほどの範囲はありませんが、各デバイスが異なるシードを取得する妥当な確率を与えるのに十分なランダム性を提供する必要があります。
もう一つの便利な概念は割り込みです。
これは、すべての衝突などを伴うマルチマスターバスの複雑さを望まない状況では有効です。バス上のすべての作業を行う単一のマスターがあり、スレーブデバイスが何か重要なことを言うときは割り込みでマスタをナッジします。マスターは次に「はい、あなたは何をしたいですか?スレーブが「誰かが私のボタンを押した」と返答します。
そうすれば、マスターはスレーブを常にポーリングして、ボタンが押されたかどうかを確認することはありません。これはSPIのようなバス構成でよく使用され、入力ピンの1つが変化したときに割り込みをアサートできるIO拡張チップなどのチップが多数あります。
しかし、20個のデバイスを持っていると、20個の割り込みピンがあることになりますか?必ずしも。新しいコンセプト:有線OR
複数の異なるスレーブがすべて同じ割り込みピンを使用することは完全に可能です。このピンは通常、抵抗(内部プルアップ抵抗でも可)でHIGHに保持され、各スレーブにはそのピンに接続されたオープンドレイン出力があります。オープンドレイン出力は、「オフ」のときは何も接続されていません。ピンが入力モードにあるようなものです(実際には入力モードと出力モードを切り替えることでオープンドレインのないチップでエミュレートできます)。出力が「オン」のときは、ピンと同じようにピンをグランドに接続し、IOピンをプルダウンします。
それから、誰が注意を必要としているかを見るために、その中断に付随していることを知っている奴隷の周りを回るのは、主人までです。もちろん、それぞれのスレーブの異なるグループを持つ複数の異なる割り込みピンを実装することができます。たとえば、デバイスが1つの場合は優先度が高く、それぞれのデバイスが複数ある場合は優先度が低くなります。
wired OR と open drain という同じ概念を使用して、複数のデバイスがすべて同じ物理配線を共有できるようにすることができます。つまり、2つのバスラインは抵抗によってプルアップされ、その上のデバイスはオープンドレイン出力を使用してラインをローにプルダウンし、ハイレベルに戻して異なるロジックレベルを作り出します。 2つのデバイスが共に低くなると、それはちょうど低くなります。オープンドレイン方式がなければ、1つのデバイスが1を出力し、もう1つが0を出力すると、基本的に2つの間の短絡が発生し、破損したチップになります。
もちろん、同期と非同期のコミュニケーションのコンセプトはありますが、それはまったく異なる魚です。簡単に言えば、そのクロックを生成するマスタを持つSPIやI2Cのようなクロックを持つプロトコルは同期しています。 UARTやRS-232、RS-485などのプロトコルは非同期です。信号の到着時に信号をどのように解釈するかを知るために、データの転送速度(ボーレート)を両者が頼りにしています。
PJONをチェックしてください - https://github.com/gioblu/PJON これは単線の代替品です。
@ Majenkoの優れた答えに加えて、あなたはコミュニケーションしているものについて考える必要があります。
それはあなたが単一のビットを伝えたいと思うように聞こえる - おそらくあなたは個々のピンで逃げることができますか?マスターと最大13台のスレーブがある場合は、スレーブ上の1つのピンに(抵抗を介して)接続されたマスター上の1つのピンを持つことができます。スレーブAが信号を送りたいと思ったら、ピンを上げるでしょう。マスタは次に各ピンを順番にポーリングして、誰が何を言ったかを理解することができます。
別のオプションはSPIです。
SPIは、リング内の任意の数のデバイスで使用できます。それは私に次のように説明されました:どの参加者もクロックラインを8回トグルして自分のバッファ内のバイトをリング内の次のデバイスのバッファに移動できます。デバイスは前のデバイスからバイトを取得します。他の誰かがクロックを8回トグルすると、新しいバイトが準備完了であることが通知されます(これはハードウェアで行われます)。
あなたは、 "アドレス"、 "長さ"、 "コマンド"、そして任意の数のパラメータのためのプロトコルについて注意深く考える必要があります。送信側は "アドレス"バイトを送信し、バッファ内の "長さ"バイトを設定し、残りを準備します。次のデバイスは「アドレス」バイトを受信し、それが意図された受信者であるかどうかを見る。そうでない場合は、これをメモし、チェーンを丸めて、長さをメモし、ELSEがそのバイト数をプッシュするまで待つ。それがあなたのものであれば、残りのデータを読んで、データをプッシュして行きます(戻るのを止めるために)。空白のアドレス(例えば0)は予約されている。つまり、「これで何もしないで、誰かがそれを押して、次のバイトを次のアドレスとして読む」。すべてのデバイスは、起動時に0に初期化する必要があります。
アドレスを動的に設定する必要がある場合は、「あなたのデバイス番号がXで、次のデバイスにデバイス番号がX + 1であることを通知する」というコマンドで、特別なバイト「すべてのステーション」を設定します。もちろん、誰かがこれを開始する必要があります。
2つの端末が同時に送信を開始した場合や、何かが中継されていない場合(たとえば、チップの電源が切れているなど)、衝突の危険性があります。あなたはこれをどのように検出/回復するかについて考える必要があります。