google-site-verification: google3bd66dd162ef54c7.html
FC2ブログ

Arduino よもやま話-14 (未使用ピンの挙動をプログラムで調査)

◆未使用ピンをどうする
Arduino の未使用ピンの処置について居酒屋ガレージ日記さんが、Arduino やっぱり気になる放置ポートという記事を書かれています。Arduino の世界では未使用ピンは何もしないで放置しておくのが普通ですが、プロの目から見るとそんな危ないことをしてはダメ。ピンのレベルが HIGH あるいは LOW になるようにプログラムで設定、あるいは回路で接続しておくべきなんだそうです。

確かに空きピンの処置は回路設計の行儀作法みたいなもので、言われなくてもちゃんとやっておくものです。私の設計ではありませんが、大昔に誰かが空きピン処置を怠ったために原因不明の誤動作が発生し、2日くらい悩んだ経験があります。

◆マイコンの空きピン処置
Arduino のようなマイコンは、入力ピンがシュミットトリガになっているので空きピンは何も処置しなくても良いという話をどこかで聞いたことがあります(出典不明)。そうは言っても居酒屋ガレージ日記さんが指摘されているように、シュミットトリガ入力の閾値付近で消費電流が増えるのは困ります。

あと、これは個人的な見解ですが、プリント基板の絶縁抵抗は高くても 10^10Ω程度なので、たとえ電荷が存在したとしても時間経過と共に放電してしまうはず、つまり空きピン処理は不要と考えていました。(正確にはその空間の電荷分布に倣う)

◆隣接ピンのスイッチングノイズ
そうは言っても、居酒屋ガレージ日記さんのコメント欄で教えて頂いたPWM出力の隣接ピンへの誘導ノイズの例を見ると、空きピンにははあたかも盗聴器のように信号が漏れている訳で、これはちょっと嫌な状態です。

これは平行配線長が短ければ顕在化しない問題なのかも知れません。でもやはり気になるのでもう少しまじめに調べてみることにしました。何しろ新型コロナウィルス騒動で外出は出来るだけ避けろということで、こういう時は家で暇つぶししているのに限ります。

◆ネタはある
Arduinoのボード
測定に使えそうな基板を引っ張り出してみたら、これだけありました。内容としては、純正品のUNOが2枚、互換品のUNOが2枚、互換品のNANOが1枚あります。なお写真上にA,B,Cなどの文字を書きましたが、これは以下の測定結果と対応しています。

◆調査方法
I/Oピンの電圧を測定することが出来れば手っ取り早いのですが、相手はものすごいハイインピーダンスなので簡単ではありません。それに基板の枚数も多いので1ピンずつ電圧測定なんてやってられません。ということで、調査用のプログラムを書きました。

◆未使用ピンの挙動調査プログラム
全ピンをプルアップ(INPUT_PULLUP)してHIGHの状態にし、その後入力ピンにアサインして一定の時間間隔でピンの状態を読み出して、その結果をシリアルに出力しています。HIGH が終わったら次は全ピンに LOWを出力 (digitalWrite(n,HIGH)) し、同じことを繰り返しています。
このプログラムでは多数のピンの設定と変更を行う必要がありますが、一つづつ pinMode と digitalWrite とやるのは大変です。そこで、居酒屋ガレージさんのコードを流用させていただいて、ポートレジスタ単位でまとめていじることにしました。

少し長いですが、コードを見える形で置いておきます。
* Arduino I/O pin leak test
force High(INPUT_PULLUP) and relese, then watch level by digitalread
force LOE(digitalWrite(LOW) and relese, the watch level bay digital read
2020/04/29 radiopench
*/
#include <MsTimer2.h>

#define TIMESTEP 1000 // 測定周期(ms)(適当に設定)
#define N_TEST 10 // 測定回数(適当に設定)

long t = 0; // 開始からの時刻(ms)
char buff[10]; // 文字列操作バッファ
volatile boolean tUp = false; // 時刻同期フラグ

void setup() {
Serial.begin(115200);
Serial.println();
MsTimer2::set(TIMESTEP, MsTimer2IRQ); // 指定時間間隔で割り込み
MsTimer2::start();
}

void loop() {
Serial.println();
Serial.println("Set all pin High and relese. then watch dicharge of pins");
Serial.println(" time, D2,D3,D4,D5,D6,D7, D8,D9,10,11,12, A0,A1,A2,A3,A4,A5");
allPullUp(); // 全ピンハイレベル(INPUT_PULLUP)
delay(20);
allPinInput(); // 全ピン入力
t = 0;
syncTimer(); // タイマー同期
pinDump(t); // 初期値測定(t=0)
for (int n = 0; n < N_TEST; n++) { // 指定回数繰り返し
syncTimer(); // タイマー同期
t += TIMESTEP; // 時刻更新
pinDump(t); // 状態をシリアルに出力
}

Serial.println();
Serial.println("Set all pin Low and release. then watch charge up of pins");
Serial.println(" time, D2,D3,D4,D5,D6,D7, D8,D9,10,11,12, A0,A1,A2,A3,A4,A5");
allPinLow(); // 全ピンLowレベル(OUTPUT, LOW)
delay(20);
allPinInput(); // 全ピン入力
t = 0;
syncTimer(); // タイマー同期
pinDump(t);
for (int n = 0; n < N_TEST; n++) { // 指定回数
syncTimer(); // タイマー同期
t += TIMESTEP; // 時刻更新
pinDump(t); // 状態をシリアルに出力
}
}

void pinDump(long t) { // pinの情報を出力
dtostrf((t / 1000.0), 6, 1, buff); Serial.print(buff);
Serial.print(", "); printC(digitalRead(2));
Serial.print(", "); printC(digitalRead(3));
Serial.print(", "); printC(digitalRead(4));
Serial.print(", "); printC(digitalRead(5));
Serial.print(", "); printC(digitalRead(6));
Serial.print(", "); printC(digitalRead(7));
Serial.print(", "); printC(digitalRead(8));
Serial.print(", "); printC(digitalRead(9));
Serial.print(", "); printC(digitalRead(10));
Serial.print(", "); printC(digitalRead(11));
Serial.print(", "); printC(digitalRead(12));
// Serial.print(", "); printC(digitalRead(13));
Serial.print(", "); printC(digitalRead(A0));
Serial.print(", "); printC(digitalRead(A1));
Serial.print(", "); printC(digitalRead(A2));
Serial.print(", "); printC(digitalRead(A3));
Serial.print(", "); printC(digitalRead(A4));
Serial.print(", "); printC(digitalRead(A5));
Serial.println();
}

void printC(int k) { // 状態を指定文字で出力
if (k == 0) { // ゼロなら、
Serial.print(" "); // スペース出力
} else { // ゼロ以外なら
Serial.print(k); // その値(1しか来ないので1を出力)
}
}

void allPullUp() { // 全ピンプルアップ
// 居酒屋ガレージ日記さんの書き方を真似した
PORTB = 0b00111111; // data/pull up
DDRB = 0b00000000; // I/O (0:in 1:out)
// |||||+---- PB0 IO8 in
// ||||+----- PB1 IO9 in
// |||+------ PB2 IO10 in
// ||+------- PB3 IO11 in
// |+-------- PB4 IO12 in
// +--------- PB5 IO13 in
PORTC = 0b00111111; // data/pull up
DDRC = 0b00000000; // I/O (0:in 1:out)
// |||||+---- PC0 AD0 in
// ||||+----- PC1 AD1 in
// |||+------ PC2 AD2 in
// ||+------- PC3 AD3 in
// |+-------- PC4 AD4 in
// +--------- PC5 AD5 in
PORTD = 0b11111111; // data/pull up
DDRD = 0b00000010; // I/O (0:in 1:out)
// |||||||+---- PD0 IO0 in RXD
// ||||||+----- PD1 IO1 out TXD
// |||||+------ PD2 IO2 in
// ||||+------- PD3 IO3 in
// |||+-------- PD4 IO4 in
// ||+--------- PD5 IO5 in
// |+---------- PD6 IO6 in
// +----------- PD7 IO7 in
}

void allPinLow() { // 全ピン LOW出力
PORTB = 0b00000000; // D13-8 data/pull up
DDRB = 0b00111111; // I/O (0:in 1:out)
PORTC = 0b00000000; // A5-0 data/pull up
DDRC = 0b00111111; // I/O (0:in 1:out)
PORTD = 0b00000000; // D7-0 data/pull up
DDRD = 0b11111110; // I/O (0:in 1:out)
}

void allPinInput() { // 全ピン 入力
PORTB = 0b00000000; // D13-8 data/pull up
DDRB = 0b00100000; // I/O (0:in 1:out) Pin13だけ出力
PORTC = 0b00000000; // A5-0 data/pull up
DDRC = 0b00000000; // I/O (0:in 1:out)
PORTD = 0b00000000; // D7-0 data/pull up
DDRD = 0b00000010; // I/O (0:in 1:out) Txだけ出力
}

void syncTimer() { // タイマー同期
while (tUp == false) {
}
tUp = false;
}

void MsTimer2IRQ() { // MsTimer2 割り込みハンドラ
tUp = true;
}
◆結果
以下に測定結果を画面のハードコピーの形で示します。実際の測定は60秒行っているのですが、意味のある部分だけ抜き出しています。なお1行は1秒に相当しています。
Arduino UNO -A
UNO-A
事前に全ピン HIGH を書いているので2秒までは全ピンが1(HIGH)になっています。その後は櫛の歯が抜けるようにビットが落ちていって、12秒後には全ビットが0(LOW)になっています。(見づらいので 0 の時はスペースを出力しています)
後半は全Pin にLOWを書いているので、時間が経っても LOWのままです。

よしよし、この結果は予想通りです。A0 ピンがHIGHの状態を11秒維持しているのは、このピンの絶縁が優秀なんだろうな、と思ってました。

Arduino UNO-B
UNO-B
こっちはもっと優秀で、D10 と A4 はこの先60秒経っても 1 のままでした。優秀なんだけどこんなに長持ちするのはちょっと変かも?

Arduino-UNO-C
UNO-C
これは AliExpress で買った互換品で、CPUは32ピンのQFPです。
前半の HIGH を書いた後の挙動は予想通りなのですが、後半の LOW を書いた後の挙動がなんだか変です。そのまま LOW の状態で寝ていてくれれば良いのですが、D10とD11が何故かHIGHに変化しています。ゾンビかおまえは?

・Arduino UNO-D
UNO-D
これはもっと変で、LOWを書いた後多くのピンで電圧が上昇しています。D11なんて1秒も経たないうちにHIGHになっています。これは変です。何か予想外の現象が起きているようです。

・Arduino NANO-1
NANO-1
HIGHを書いても保持時間が短いようです。パチ物なので絶縁が悪いのかも?なんて思っていました。LOWを書いた後の挙動は普通で、勝手にHIGHになったりはしていません。なお、この基板のCPUも32ピンQFPです。

◆ここまでの整理
プログラムを書いたおかげで、多くのチップの全てのピン (5チップ×17ピン) の特性を効率良く測定することが出来ました。これで問題が大きそうなピンや、優秀(そう)なピンに目星を付けることが出来ました。

私の頭の中では、ピンの静電容量に溜まった電荷が、基板などの部品の絶縁抵抗を通して徐々に放電していくモデルを想定していました。多くの測定結果はそういうモデルで説明出来そうですが、LOWを書いたのに徐々に電圧が上がって行く現象はそのモデルでは説明が付きません。なんだか電荷が染み出している感じなので、CMOS入力の上下のゲートのリークが見えているのかも知れません。

こうなるとデジタルな測定では手に負えないので、電位の測定が必要になります。こういう測定は、プロならエレクトロメーターの出番ですが、アマチュアならCMOSオペアンプのボルテージフォロワーを使った測定でやるのが定番だと思います。

実はその測定は終わっていて結果は整理中なので、次回の記事で公開予定です。

あと、こういう場合の測定方法や現象の解説などで情報があれば教えて頂けると幸いです。

#コロナウィルスで外出できない日は、家でコードを書こう
関連記事

コメントの投稿

管理者にだけ表示を許可する

大拍手!!!

次のレポート、待ってまぁ~っす。
むちゃおもしろい。

これこそ「トラ技」ネタなんですが、今の編集方針、ちょいと「未来」に向かい過ぎのような感じなんです。
もっと、電子回路のこまかい所をほじくっても良いような気がしてるんですが・・・

re:大拍手!!!

応援ありがとうございます。

確かにこういう基本的なところはトラ技でもっと扱って欲しいですね。

ん、トラ技は半年以上買って無いので、偉そうなことは言えなかったです。
カレンダー
05 | 2020/06 | 07
- 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 - - - -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード