最近のマイコン(MCU)をはじめとするLSIは、CMOSロジックで作られていることが多いので、I/Oポートなどの入力バッファも、CMOS入力になっていることが多いです。
一方、多くノイズが乗って到達する信号線の場合、丁度、入力ポートのロジックレベルがLOWとHIGHの境目(閾値、threshold)近辺でノイズを受けると、その期間、LOW⇔HIGHが激しくトグルして、回路やソフトウェアが誤動作する場合があります。
このような時、少しでもノイズの影響を受け難くするため、シュミットトリガ入力と言う入力ポートが用意されているケースがあります。今回は、シュミットトリガ入力への理解を深め、最終的にはオペアンプを使ってシュミットトリガ入力バッファ(インバータ)を作ってみます。
Arduino UNOに搭載されている、ATmega328というMCUのデータシートを検索して確認してみると、どうやら、全てのI/Oポートがシュミットトリガ入力となっているようです(下図、ATmega328データシートからの引用)。
※ATmega328Pデータシートから抜粋して引用
左端上にある「Pxn」と書かれた四角形がATmega328の外部端子です。そこから伸びた線を右下に辿ると、三角形の素子があります(赤○)。これが所謂「入力バッファ」なのですが、内部に変な記号が描かれています。この記号が「シュミットトリガ入力バッフア」の記号となります。
つまり、「ATmega328のI/Oポートはシュミットトリガ入力だよ」と、宣言した図と捉えられます。
ところが、同じデータシートの最後の方に載っている、電気的特性(Electrical Characteristics)のDC特性(DC Characteristics)を確認してみると、普通のCMOSバッファの特性値(Vil,Vih)しか記載されていません(下図)。
※ATmega328Pデータシートから抜粋して引用
どっちが本当なのでしょうか?と、言うことで、ATmega328 I/Oポートの入力特性(DC特性)を、実際に確認してみましょう。
入力特性を測定するためには、入力端子に0〜5[V]までの任意の電圧を印加できる必要があるので、以下図の様な12ビットDA変換器(MCP4725)を使います。
※使い方については、「#7-9 WAVファイルを再生する」を参照してください。
以下にArduinoとMCP4725の接続表(ネットリスト)を示します。
Arduino UNO | 接続 |
12ビットDA変換器 (MCP4725) |
5V | → | VCC |
GND | ⇔ | GND |
A5(SCL) | ⇔ | SCL |
A4(SDA) | ⇔ | SDA |
D2 | ← | OUT |
また、評価用のプログラムをリンクしておきます。
評価用プログラム:test_schmitt.ino
コンパイルしてArduino UNOへ書き込んでください。シリアルモニタから、以下のコマンドを受け付けるようになっています。
これらのプログラムと結線によって、Arduino UNO(ATmega328)のデジタル端子D2の入力特性を測定します。
通常のCMOS入力だと、NMOSとPMOSのバランスによって、閾値電圧VTHが決まり、入力電圧がVTHより高ければ1(HIGH)が読め、低ければ0(LOW)が読めます。シンプルです。
一方、シュミットトリガ入力の場合には、電圧を徐々に上げて行った(0→5[V]など)場合に、読み出したロジックレベルが0→1に変化した点の入力電圧をVt+、逆に、電圧を徐々に下げて行った(5→0[V]など)場合に、読み出したロジックレベルが1→0に変化した点の入力電圧をVt-と呼び、Vt+とVt-は一致しません。
※この先、Vt+,Vt-を使った数式が登場しますが、+/-と言った記号では紛らわしいため、以下、Vt+⇒Vtp、Vt-⇒Vtmと記します。
【eval】コマンドでは、VtpとVtmの両方が測定できるようになっています。
実験サンプル:【 】内はシリアルモニタからの送信データ
【eval】
0,0
2089,0
2090,1
4094,1
4095,1
4095,1
1794,1
1793,0
1,0
0,0
OK: eval
いきなり、測定してみました。4,096点の測定×2回実施するので、測定終了まで10秒程度かかります。上の測定例では、12ビットDA変換器の出力が2,090LSBだった時に、ロジックレベルが0→1に変化しています。この時のDA変換器出力電圧Vdac=Vtp=5V×2,090/4,096≒2.55Vです。
1→0の変化点も同様に、Vtm=1,793LSB(≒2.19V)だとわかります。
データをExcelに貼り付けて、グラフ化した様子が以下図です。
最初に、MCP4725で0→5[V]へ電圧を上げて行った時には、赤破線矢印に沿って、ロジックレベルが0→1に変化しました(この時の電圧がVtp)。続いて、MCP4725で5→0[V]へ電圧を下げて行った時には、オレンジ破線矢印に沿って、ロジックレベルが1→0に変化しました(この時の電圧がVtm)。
※蛇足ですが、シュミットトリガ入力バッファのシンボルに描かれた記号は、上グラフの形状をデザインしたものらしいです。
通常のCMOS入力では、Vtp≒Vtmなのですが、ATmega328の場合は、ちょっと、差分があるみたいですね……。
このように行き(→)と帰り(←)で特性が異なることを「ヒステリシス特性」と呼び、その差分をヒステリシスΔVt(=Vtp−Vtm)と呼びます。シュミットトリガ入力では、ΔVt>>0(十分大きい)となるのが特徴です。ATmega328の場合は、ΔVt≒0.36V程度でした。一般のシュミットトリガ入力バッファの場合は、どのくらいのΔVtがあるものなのでしょうか?
標準ロジックIC(74シリーズ等)では、例えば74HC14と言う型番がシュミットトリガ入力インバータとなっているので、74HC14のデータシートを検索してみます。
74HC14のデータシートを検索(別ウインドウが開きます)
以下、TI社のSN74HC14データシートから引用です。
※SN74HC14データシートから抜粋して引用
丁度良い5Vの条件が抜けているので、Vcc=4.5Vの特性を見てみると、ヒステリシスは、0.4V(MIN)となっていますね。TYPで0.9V、MAXだと2.1Vまで広がっています。
ATmega328の測定値ΔVt≒0.36Vは、74HC14のMINスペックを満たせていないので、ちょっと、大手を振って「シュミットトリガ入力です」と言うのは、はばかられたのかもしれません。とは言え、単なるCMOS入力にしては、ヒステリシスが大きめなので、控えめなシュミットトリガ入力として設計されているのは確かなのかもしれません。
シュミットトリガ入力バッファが、何で、ノイズに対する耐性が高いか?と言うと、その理由は、このヒステリシスΔVt特性にあります。
通常のCMOS入力では、VTH付近でノイズが乗ると、そのまま、ロジックレベルが変化して、0⇔1が定まらなくなってしまいます。シュミットトリガ入力では、Vtpを上回って、一度ロジックレベル1を読み出すと、次にロジックレベル0を読み出すためには、Vtmを下回る必要があります。VtmはVtp−ΔVtなので、ノイズレベルがΔVtを超えない限り、ノイズに引きずられて0⇔1がトグルすることはありません。
すなわち、ノイズへの耐性を上げようとすると、ΔVt→大であることが必要となります。
ATmega328の入力特性について理解したところで、次はオペアンプ(LM358、以下図)を使って、実際にシュミットトリガ入力インバータを設計してみたいと思います。
※以下、オペアンプの非反転入力端子をVp、反転入力端子をVnと表記します。
オペアンプを使ったシュミットトリガ入力インバータは、「ヒステリシス付き比較器」などと呼ばれる場合もあります。「ネガティブフィードバック(Vn端子へ帰還する)」が基本となるオペアンプの応用回路の中で、Vp端子へ正帰還(ポジティブフィードバック)する、応用例となります。
今回は、ちゃんとシュミットトリガ入力の特性である、Vtp,Vtm,ΔVtを任意の値に設定できるような回路を作り、その計算方法をご覧に入れたいと思っています。
まずはいきなり、回路図を示します。以下図がオペアンプLM358を使ったシュミットトリガ入力インバータ回路です。
※上の回路定数だと、実回路を構成した時に、特性が出ないことが判明しました。本ページ最後の「後日のアップデート」をご覧ください。
後でそのままシミュレーションできるように、LTspiceでエントリしました。
※LTspiceやLM358の使い方等については、「逆引き索引」から、LM358やLTspiceの項目をたどって、ご参照ください。複数記事で解説済みなので、重複は避けます。
この回路では、入力信号Vsをシュミットトリガ入力で受け、インバート(論理反転)してVoに出力します。機能的には、74HC14と同じ、シュミットトリガ入力インバータです。繰り返しになりますが、特徴的なのは、VoからのフィードバックがVp端子に入っている(ポジティブフィードバック)ことです。回路の設計ミスではありませんので念のため。
まず、前節の回路に於いて、入力Vsと出力Voの関係を考察します。
回路定数(Vcc,R1,R2,R3,等)を代入してI3が決まると、(3)式より非反転入力端子の電位Vpが決まります。反転入力端子Vnには入力信号Vsが直接接続されているので、オペアンプの特性より、Vs<VpならVo=HIGH、Vs>VpならVo=LOWとなります。すなわち、Vpが入力レベルを決定する閾値となっています。
「何で?この回路がシュミットトリガ入力になるの?」と言う、根本的な疑問への回答も含めて、この回路のヒステリシス特性について考察してみます。
つまり、現在の出力値Voがフィードバックされて、現在の入力閾値(非反転入力Vpの電位)を変化させます。従って、Vo=HIGHの場合と、Vo=LOWの場合で比較対象の閾値(Vtp,Vtm)が異なるため、ヒステリシスΔVtが生じ、シュミットトリガ入力として機能するのです。
前節で示した、Vtp,Vtm,ΔVtは、R1,R2,R3の回路定数によって決定できます。
このままだと、自由度3(R1,R2,R3)なのですが、そもそも、ΔVt=Vtp−Vtmの関係があるので、自由度は2で十分です(∵いずれか2つのパラメータを決定すると、残りの1つは自動的に決まる)。従って、R1=k×R2、R3=j×R2と置いて、自由度を2(kとj)に下げてみます。
いずれか2つのパラメータ、例えばVtmとΔVtの目標値を定める事とすると、Vtmの式とΔVtの式を使って連立方程式を解き、kとjを求めることができます。解いた結果が以下式です。
k=j×(Voh−ΔVt)/(ΔVt×(1+j))
j=Voh×Vtm/(Voh×Vcc−Voh×Vtm−ΔVt×Vcc)
紙とボールペンでゴリゴリと解きました……。あんまし自信無いので、検算して使ってくださいねm(1__)m。
※Pi ZeroWのRaspbianにMathematicaがインストールされていました。Mathematicaで計算させれば一発だったのですが、もう、使い方をすっかり忘れてしまっており、止む無く、紙とボールペンでゴリゴリと……。
※大学時代から、鉛筆(シャーペン含む)は使わない派。「消せるボールペン」も論外。授業のノートもボールペン。間違ったら、見せ消しで書き直せば、消す時間も節約できるし、記録にも残る。お勧め、と、言うよりエンジニアの常識(1^^)/。
※その後、Pi ZeroWのMathematicaを使って検算した記事をアップしました。詳しくは「#8-5 Mathematicaを使ってみよう」をご覧ください。検算結果OKでしたので、安心してお使いください。
パラメータを決定する手順は以下の通りです。
では実際に設計してみましょう。Vtm=1V,ΔVt=2Vとなる、シュミットトリガ入力インバータを設計してみます。Vcc=5V、Voh≒3.95V(LTspiceを使ったLM358のシミュレーションより)、Vol≒0Vを使います。以下が計算結果です。
Vtm=1V (目標値)
ΔVt=2V (目標値)
j=0.681
k=0.395
R2=10,000Ω (抵抗の基準値)
R1=3,950Ω
R3=6,810Ω
ここで、手持ちの抵抗の中から、これらの抵抗値に近い物を選び、以下としました。
R1=4,000Ω=2k+2kΩ
R2=10,000Ω=10kΩ
R3=7,100Ω=5.1k+2kΩ
現実的な抵抗を組み合わせて作るしかないので、多少、計算と異なるのは止む無しです。念のため、決定した抵抗値で各パラメータを逆算したところ、以下となりました。
Vtp≒3.030V
Vtm≒1.019V
ΔVt≒2.012V
まあ、この程度なら、誤差範囲でしょう。
上で示した回路図は、既に、前節で設計したパラメータでエントリしてあるので、そのままシミュレーションしてみます。
入力Vs(緑線)を0→4[V]まで変化させる前半と、4→0[V]まで変化させる後半に分かれています。
前半で出力Vo(赤線)がHIGH→LOWに立ち下がった時のVs(緑線)電位がVtp、後半で出力VoがLOW→HIGHに立ち上がった時のVs電位がVtmとなります。上のタイミング図より、Vtm≒1.08V、Vtp≒2.99V、ΔVt≒1.91Vと読めました。シミュレーションで、ほぼ、設計通りの動作が確認できました。
※ちょうど横軸の真ん中、100msの所で折って、200msのポイントを0msのポイントに重ねると、赤線が、さっきExcelで描画したグラフに相当します(ただし、こっちはインバータなので、論理値の0/1(縦軸)が反転しています)。
入力Vsにノイズを付加してみましょう。回路図で、電圧源V2につながっているVsを、ビヘイビア電圧源B2に繋ぎ変えてシミュレーションします。
上側が通常のインバータを使った入出力特性で、下側がLM358を使ったシュミットトリガ入力インバータです。
ビヘイビア電圧源B2の方には、−0.5〜+0.5[V]のホワイトノイズを加えてみたのですが、通常のインバータでは、VTH近辺で0⇔1がトグルして定まりません(Vnorm、青線)。一方、シュミットトリガ入力インバータでは、ΔVtが2V近くあるので、±0.5Vのノイズ程度では、びくともしません(Vo、赤線)。余裕です(1^−^)。
思い出してみましょう、ATmega328のΔVt実測値はΔVt≒0.36Vでしたので、±0.5Vのノイズが入力されてしまったら、上の青線みたいな状況になります。ΔVt≒0.36Vでは、ちょっと、ノイズ耐性は低いかも?です(もちろん、ただのCMOS入力よりはマシ、なのですが……)。
いよいよ、実際に回路を組んで、実物で評価してみましょう。DIYは、「ブツで動かしてなんぼ」ですから。
シュミットトリガ入力インバータの回路図は、上で示した通りです。オペアンプ(LM358)を使って、構成してください。
評価用に使用するMCP4725との接続は、ATmega328 I/Oバッファの評価とは少し変わりますので、以下の接続表で確認しながら配線してください。
Arduino UNO | 接続 |
12ビットDA変換器 (MCP4725) |
接続 |
シュミットトリガ入力 インバータ(LM358) |
5V | → | VCC | → | Vcc(LM358) |
GND | ⇔ | GND | ⇔ | GND(LM358) |
A5(SCL) | ⇔ | SCL | ||
A4(SDA) | ⇔ | SDA | ||
OUT | → | Vs | ||
D2 | ← | Vo | ||
A0 | ← |
下図に実験中の様子を示します。
この接続だと、ATmega328の評価で使ったプログラム(test_schmitt.ino)がそのまま使えます。コンパイルしてUNOに書き込んだら、シリアルモニタを開いて評価します。
実験サンプル:【 】内はシリアルモニタからの送信データ
【eval】
0,1
2365,1
2366,0
4094,0
4095,0
4095,0
1139,0
1138,1
1,1
0,1
OK: eval
ATmega328の時と同様にグラフ化してみました(下図)。
まず見た目で、ATmega328のグラフと見比べてみて、VtpとVtmの間隔が空いて(ΔVt→大)いることが解ります。
詳しく吟味してみると、Vtm≒1.39V、Vtp≒2.89V、ΔVt≒1.50Vで、……ん?ちょっと、設計値と偏差がありますね。シミュレーションでは、ぴったし設計値通りだったのですが、おかしいですね。
シミュレーションと実物で差があるとすれば、LM358のVohとVolでしょう。設計(計算)に使ったのは、LTspiceのシミュレーションから求めた、Voh=3.95V,Vol=0Vなので、シミュレーションと設計値が一致するのは当たり前です。
実際の回路でも、LM358の出力レベルを測定してみましょう。
実験サンプル:【 】内はシリアルモニタからの送信データ
【daco,0】
OK: DAC=0(0[mV]) port=1 (3647[mV])
【daco,4095】
OK: DAC=4095(4998[mV]) port=0 (703[mV])
注目すべきは、LM358の出力をanalogRead()して実測したカッコ内の電圧値で、Voh=3.647V、Vol=0.703Vでした。おやおや……ブレッドボード+ジャンパ線と言う環境のせいなのか、何なのか、シミュレーションよりもVohが低いし、Volもだいぶ浮いてしまっているようです。
実際の回路で実測したVol,Voh、回路を構成したR1,R2,R3を使って、Vtm,Vtp,ΔVtを逆算して比較してみると:
パラメータ | 設計値 | 逆算値 | 実測値 |
Vtp | 3.0V | 2.876V | 2.89V |
Vtm | 1.0V | 1.379V | 1.39V |
ΔVt | 2.0V | 1.497V | 1.50V |
この通り(↑)、実測値とほぼ一致しました。
設計値と実測値に偏差が生じた理由は、解りました(→Voh,Volがシミュレーション値からずれている)。ブレッドボード+ジャンパ線では、限界かも知れないですね。ちゃんと電源を強化して基板上にはんだ付け実装するとかすれば、対策できるでしょう(あるいは、実測値(Vol,Voh)に合わせて設計(計算)するか?)。
やっぱり、「シミュレーションで確認できたので終了」に、しなくて良かったです。実際に回路を構築する時の留意点を知ることができました。
シュミットトリガ入力が使えるようになると、入力信号のノイズ耐性が上がるだけではなくて、適切な遅延回路(CR回路や積分回路等)と組み合わせて発振させてみたり、任意の電圧でON/OFFするセンサみたいに使えたり、用途が広がります。ご活用ください。
その後、シミュレーションと実回路の結果が、あまりに異なっていたのがモヤモヤしていて、継続調査しておりました。
「#6-2 オペアンプにspice」にて、LTspiceに導入したLM358のspiceモデルですが、コンパイルエラーなしで使えたモデルは、NS社、ST社、TI社の3つでした。本HP(Arduino DIY)では、主にNS社のモデル、LM358_NSを使用しています。
ふと、他のモデルに変更してシミュレーションしてみたところ、LM_358_TI(TI社モデル)で、実回路の現象が再現しました(下図)。
LM358のVoh≒3.79V、Vol≒0.62V、シュミットトリガ入力特性Vtp≒2.93V、Vtm≒1.35V、ΔVt≒1.58Vと、実回路での測定結果に近い値となりました。
LM358_TIモデルは、時々挙動が怪しかったので、いつもは敬遠しているのですが、今回は、このモデルが実回路の動作を再現する結果となりました。
さらに、TI社やNS社のLM358データシートに掲載されていた「ヒステリシス比較器」の回路図にて、フィードバック抵抗値が10MΩになっているのを発見しました(下図)。
※TI社LM358データシートから引用
「もしかしたら、抵抗値(R1〜R3)が小さすぎなのかもしれない」と考えました。すぐに値を変えて試行できるのがシミュレーションの良いところ、R1〜R3の抵抗値を×50倍に大きくしてシミュレーションしたところ、Voh≒3.83V、Vol≒0.02V程度まで向上しました(下図)。
改定後の回路は以下の様になります。
手持ちの抵抗では、100kΩを超える抵抗値にバリエーションが無かったため、データシートのサンプルの様に、R1=10MΩまで大きくできませんでした。本来は、もっと大きい抵抗値にすべきなのかもしれません。
以下がパラメータの設計値です。
R1=200kΩ=100kΩ+100kΩ
R2=500kΩ=1MΩ//1MΩ(並列接続)
R3=350kΩ=100kΩ+100kΩ+100kΩ+100kΩ//100kΩ
Vtp≒2.942V
Vtm≒1.014V
ΔVt≒1.928V
上回路図の通り、実際の回路を構成し、本文中のプログラム(test_schmitt.ino)を使って再評価した結果が以下となります。
実験サンプル:【 】内はシリアルモニタからの送信データ
【daco,0】
OK: DAC=0(0[mV]) port=1 (3818[mV])
【daco,4095】
OK: DAC=4095(4998[mV]) port=0 (53[mV])
【eval】
0,1
2435,1
2436,0
4094,0
4095,0
4095,0
865,0
864,1
1,1
0,1
OK: eval
Voh、Vol共、シミュレーション結果と同等な出力を確認しました。測定データを元にグラフ化すると、以下図の様になります。
Vtp≒2.97V、Vtm≒1.06V、ΔVt≒1.91Vと、ようやく、設計値通りの動作を確認できました。
当初、LTspiceのシミュレーションにて、設計通りの結果が出ていたので、「回路は正しいはず」と、思い込み、「電源とGNDが弱いせいだろう」と、結論を急いでしまいました。確かに、すごい違和感はあったのですけどね……。ケースによっては、spiceモデルが実回路の特性をシミュレートしきれないケースもあるのですね。シミュレーションを過信してしまいました。反省m(1__)m。
※言い訳ですが、回路定数計算用の代数計算(と、イタレーション)がしんどかったので、違和感は感じていたものの、「見て見ぬふり」をしてしまいました……。
オペアンプの電気的特性の中に、同相入力電圧(Input Common-Mode Voltage Range)という規定があります。LM358だと、0〜Vcc−1.5Vです(単電源Vccで使用する場合)。この規定は、「オペアンプが正常動作するのに必要な入力電圧の範囲」と、なっています。
本ページの実験の様に、Vcc=5VでLM358を使用時に、DA変換器(MCP4725)でVcc−1.5Vを超える電圧(3.5V以上)を印加すると、規定に違反していることになります。絶対最大定格は、32Vまで印可可能なチップですので、直ちに破壊するようなことはありませんが、「正常動作しない」というのは気持ち悪いですね。
一応、実測では正常に動作しているように見受けられますが、応用する場合は注意してください。以下いずれかの対策が必要だと考えられます。
いずれにせよ、無保証ですので、応用する場合は、各自、再設計してお使いください。