編集履歴一覧に戻る
jeffyのアイコン画像

jeffy が 2021年02月07日07時39分01秒 に編集

コメント無し

本文の変更

# はじめに

-

光で信号を伝える

+

光で信号を伝えるデバイスをつくる

今回の目標はこれです.これを達成するために色々な試行錯誤を行ったので,この記事ではそれを順に説明していきます. 最初に簡単に説明すると,

-

LED,PD(フォトトランジスタ)を使用した光通信により,通常有線で実装されるシリアル通信を1cmの距離で無線化した

+

LED,PD(フォトトランジスタ)を使用した光通信により,通常有線で実装されるシリアル通信を1cmの距離で無線化したデバイスを作った

ことが今回達成したことです. [以前に書いた記事](https://elchika.com/article/cbf45ae7-db8e-4afe-9866-b486e472c518/)ではLEDを受光素子として使い,PD(フォトダイオード)が無くてもなんとか信号を送信できることを確認していましたが,今回はPD(実際にはフォトトランジスタ)を受光素子として使用して,Arduinoでよく使われる(はずの),シリアル通信を光通信に置き換えています. この記事は以下,大まかに - 基本構成 - 受光側での信号波形 - 信号の補完,波形の整形 の順に構成されています. # 全体の構成 ![構成図](https://camo.elchika.com/9ac5e3db2f4340988564c2fd8f3660ca8fd5666d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f38313735323561382d373865382d343239642d386431652d623632323031353661323537/) 信号の送受信には1台のArduinoを使用します.LEDにソフトウェアシリアル通信用のTxピンから信号を供給し,信号に合わせて点滅させます.その光信号をPDで受光し電圧信号として取り出します.取り出した光信号はオペアンプにより増幅し,その後電圧レベルを調節するためにロジックICを通して,最後にArduinoのシリアルRxに入力します. Arduinoはシリアル信号を受け取ると,PCに対してシリアル通信でメッセージを送信します.系全体でシリアル通信は9600bpsとしています. 今回はPDが手元にあるので,前回のような奮闘をしなくても素直に受光すれば信号が取り出せるはずです.さらにいいことに,今回使用するPDは実際にはフォトトランジスタなので,素子から出力される電流があらかじめ増幅されています.なので別にアンプ等を用意する必要がないので受光側がシンプルになる,と思っていました. 今回の実験を始めたときには,”まあそれほど苦労せずにいけるかな”,と思ってたのですが,やってみたら通信ができなくて原因を探して解決していかなければならなかったので,ここからはその説明になります. # シリアル通信の波形 ![シリアル通信の波形](https://camo.elchika.com/184a67a7f1a1f3c254f1a5a6cc3b8c7f7d2d32b6/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f32376331653866332d626131312d346230322d383536382d353163333331376130616265/) 回路の説明等の前に,最初に軽く行った実験結果について説明します. 実際に送信されているシリアル通信の波形をUSBオシロで取得したのが上のスクリーンショットになります.赤色のCH1が光となったシリアル信号をPDで受光して取り出した電圧波形,青色のCH2がLED側のシリアルTxの電圧波形です. この波形を見てもらえばわかりますが,本来シリアル通信の信号が5VからLOWレベルに落ちるべきなのですが,受光側の波形では4V程までしか落ちていません.比較として送信側の信号はGNDレベルまで落ちています.(オシロの適当さによりGNDを微妙に下回っているが) つまりPDに光が入っていないときもLOWレベルではない電圧が出力されてしまっているのが原因でシリアル通信ができていないことがわかりました.信号の形は変わりなく伝わっているので,受光側で何かしらの処理をしてやれば上手く通信ができそうです. # 波形の補正 簡単に達成できると思っていたので,最初の段階で躓いてショックですが,なんとか解決するために順に見ていくしかありません. すでに原因が,十分にLOWレベルまで落ちていないこと,とわかっているので,受信信号をLOWレベルに落としていきます. ## 受光回路 まず再考したのはPD受光回路部分です. 今回はちょっと前に入手したフォトトランジスタ,[TPS601A](https://toshiba.semicon-storage.com/jp/semiconductor/product/optoelectronics/detail.TPS601A(F).html)を使用しました.受光回路はこのPDのデータシートにある回路を参考に以下の(a)のように実装していました. ![受光回路](https://camo.elchika.com/e12593771fd7086a895bc4226c91512f79cb2ef5/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f30623037373036302d333331322d346463352d613737612d636432386330633832636436/) (a)の回路だと,光入力があるときに約5Vが出力され,入力がないときには約4Vが出力されます.これを図中の(b)の回路に変更しました. 変更点は見てはっきりとわかるように,PDと抵抗の順序を逆にしただけです.この構成だと,光が入射しているときにGNDレベルに,光が入射していないときに電源レベルになります.つまり,信号が逆になってしまうのですが,とりあえず信号の振幅レベルは解決できました. 以下のスクリーンショットが改善された波形になります. ![変更した受光回路の出力波形](https://camo.elchika.com/2b005ea37ec4215d72512c2b66be4ef3dd26e4aa/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f33353261353239302d343130302d343331382d613431332d646162643066386139356436/) 実際にはこの後に増幅したり波形整形したりするので,この順序の入れ替えってのは無駄になりそうですが,以前に受光回路を作ったときの経験から,PDがGND側にいる方がよい気がします.今回の目標では通信速度は考慮していませんが,Mbpsを超える光通信を行った場合,発光・受光素子の周波数特性が問題となってきます.先に挙げた2つの回路をネットワーク・アナライザで測定すると(以前に行った別の実験で),(b)の方が安定していたために,今回もこの(b)の回路で進めていきます. ## バッファと増幅 インピーダンスの高い低い問題により,受光回路の出力をそのまま回路上で取り回したりすると信号が変形します.低周波回路では一般に,低インピーダンス信号を高インピーダンスの何かで受けて次の何かに信号を回します.高周波ではインピーダンス・マッチングが基本となりますが,今回はそれほど高周波でないので,前者の手法でなんとかします. これについては何となく悪い予感がしていたので,オペアンプをだいぶ事前に用意してあり,それにより解決しました. まずバッファですが,これはボルテージフォロワで解決です.これにより次段は気軽に信号線を取り回せるようになります. 次に増幅ですが,ここで少し問題が発生します.問題だらけですが.とりあえず以下がバッファ,増幅部の回路図となります. ![オペアンプ部分回路図](https://camo.elchika.com/442cb94e423d049b2372897f69c52f71674e0ead/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f36646264626565332d626231632d343964322d626634342d323633336337366435383931/) ボルテージフォロワの前にあるコンデンサは直流カットの働きをしています.コンデンサの直後にある抵抗は交流となった信号にバイアスを持たせるためについています. ボルテージフォロワの次段には反転増幅回路があります.増幅率は10倍になっています. このオペアンプはArduino Unoと同じ電源電圧である5Vで動いています.なのでオペアンプからの出力は5Vとはなりません. 回路図ではNJM4580となっていますが,シミュレーションでも同じものを使用して出力を見てみました. ![シミュレーション結果](https://camo.elchika.com/4d978ed4e423ad982d2e626220f41f865774a790/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f30626630313037332d393964382d343131642d386263652d363735663862313866333733/) シミュレーションにはLTspiceを使用しました.出力波形の線がかなり見にくいですが,左上のグラフが反転増幅回路のもので,出力は1.4V~3.5Vとなっています.[NJM4580のデータシート](https://www.njr.co.jp/electronic_device/PDF/NJM4580_J.pdf)を見ても,電源電圧プラマイ2.5Vのときにはプラマイ2V出てません.Spiceモデルは新日本無線の製品ページから入手したものなので,シミュレーションがデータシートと合っていて当然とも言えます. なんにせよこのままではシリアル通信の信号としては使えないので,さらに信号の整形が必要です.そのためオペアンプの後段にバッファが入りますが,その入力信号としてこの段階でもう少し手を加えておきます. 次に来るバッファのHIGH,LOWの基準を上手く超えられるように,出力信号の振幅を大きくできるようにします.具体的には単電源オペアンプに変更します.NJM4580は両電源オペアンプですが,単電源オペアンプを使うともう少し出力の幅が広がります.今回はNJM13404Dを使用しました.回路自体は変更なく,載せ替えるだけで済みます. ## さらにバッファ ここまでの試行錯誤で,オペアンプだけでは信号が0V~5Vとならないことがわかりました.そのため標準ロジックICを使って信号波形を整形します.使用したのは手元にあった[MN74HC00(NAND)](https://industrial.panasonic.com/jp/eol/semiconductors/other-semiconductors/other-semiconductors/models/MN74HC00)です.NANDなので,入力の一つをHIGHに固定し,もう一方に信号を入力します.他のICは試していませんが,NOT,AND,ORでも代わりになるはずです. ただしここまでで信号は, PDで反転 → 反転増幅回路で元に戻る → NANDでまた反転 となっているので,NANDをもう一度通します. これでついに信号は元のシリアル信号とほぼ同じになりました. # 光通信の結果 ![受信信号,結果](https://camo.elchika.com/1a6bc6a83ad46c1675de148a47b1f7b8e5029b7e/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f64663939353861322d663062622d343931322d393034632d373431336237366662383432/) 赤い波形が最終的な信号です.5Vとはなっていませんが,USBオシロの微妙な誤差とArduino Unoのシリアル通信しきい値の範囲内にはなっているようです. 下側にあるシリアルモニタはArduino UnoのRxへの入力があるときにシリアル出力しているデータが表示されています. ![全体の様子](https://camo.elchika.com/da0646738af54118a702b8b2dd25deb91bbd1a0d/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f64373230643161362d303138372d346538382d396438332d6436616663373764613736652f33356133383139662d313161612d346637342d396264302d346462373239663932393064/) 光通信が行われているのは右上のブレッドボード上の約1cmの距離です.今回はブレッドボード上で,Arduino Uno一つで実験を行い,主に信号の整形がメインとなったので,通信距離や通信速度についてはできるだけ考えないようにしています.しかしこれらは現在考えている限りでは容易に改善できるはずです. 通信距離はLEDの光出力と,PDの感度,受光面積,アンプの増幅率を変更することで改善できます. さらに通信速度は単純にシリアル通信のビットレートを上げる以外に,LED,PD,アンプに高周波特性の良いものを使用することで改善できます.LED,PDは発光部分の面積,受光部分の面積が小さいものほど高周波での特性が良くなることが多いですが,特性の良いものを使用すると光出力,感度が悪くなることがあるのでどこかで線引が必要です. PDは実際に使用して確認している限りでは500MHzとか1GHzとかいう特性のものがありますが,LEDの方は割と低く,100MHzほどの特性が現在の限界ではないかと思います.今まで実際に実験を行った結果からは50Mbpsぐらいで通信ができます. これ以上の速度が必要となるなら通信の多重化を考えるか,光源をレーザーダイオード(LD)にしてしまう手があります.LDなら特性がLED・PDより良かったり,光出力が高いので通信距離と通信速度を稼げます.ただしコヒーレント光なので複数の光源があると光が干渉したり,単一の光源であっても照射面上でスペックル(ゆらいでいるやつ)が発生したりするので,一概に良いとは言えなかったりします. 結局のところどうするのかと言うと,その都度一番良いものを選んで,出てくる問題を一つひとつ解決するしかないです.

+

# 何が嬉しいのか・何に使えるのか 今回作った物がどういうときに嬉しいのか,役立つのかを考えてみます. 単純に有線が無線になるので,シリアル通信を手軽にある程度の距離まで伸ばせます.導線を使うとどうしても電圧降下が発生しますが,このシステムを使うと受光素子が光信号を捉えられなくなるまでは距離を離しても通信できます. GNDを別々にとって良いことも嬉しい点です.何かしらの通信対象と同じ電源に繋ぐと問題がある場合に,それらと独立して信号を取得できるので,影響を与えず,与えられないはずです. 通信のプロトコルを考えなくていいことも挙げられます.オンオフの2値状態で信号を伝送するときに,単純な信号だけを送るならそれでいいのですが,ある程度の規模になるとしっかりしたプロトコルが必要になります.今回はシリアル通信が使えるので,その辺りを考えずに通信できます.

# まとめ

-

通常有線で使用されるシリアル通信を,LEDとPDにより光通信化しました.PDにより受光した後の信号をシリアル通信で使用される信号と同一にするため,いくつかの処理により整形しました.現在のところ通信距離は1cmですが,いくつかの改善により容易にパワーアップできるはずです.

+

通常有線で使用されるシリアル通信を,LEDとPDにより光通信化したデバイスを作りました.PDにより受光した後の信号をシリアル通信で使用される信号と同一にするため,いくつかの処理により整形しました.現在のところ通信距離は1cmですが,いくつかの改善により容易にパワーアップできるはずです.

+

# 使用した物 - 測定器 - USBオシロスコープ(OSC001 PCB SCOPE) - テスター(SANWA SP20) - Arduino Uno - LED(OSW54L5111P) - PD(TPS601A) - オペアンプ(NJM13404D) - 標準ロジックIC(MN74HC00) # コード ``` #include <SoftwareSerial.h> SoftwareSerial sSerial(10, 11); int count = 0; void setup() { Serial.begin(9600); sSerial.begin(9600); } void loop() { sSerial.println(100); if(Serial.available()){ count += 1; Serial.print("got signal !! "); Serial.print(count); Serial.print(" times !! read >>"); Serial.println(Serial.read()); } delay(10); } ```