クイズ:一つのスイッチを押すと全部のLEDが消灯する

この前の日曜日、FPGAにつなごうと思ってこんなコントローラーを作っていたんです。左半分(十字ボタン)の配線が終わったところで、動作をチェックしようと思い、DE0のGPIOに接続して入力をLEDに出してみたのですが、なぜか「一つのスイッチを押すと全部のLEDが消灯する」という現象が起きました。その理由が面白かったのでクイズ形式でブログの記事にしました。

出題編

スイッチは、信号線とGNDの間に入っていて、信号線とVccをプルアップ抵抗でつないである、というオーソドックスなやり方です。4つのスイッチがブレッドボード上に実装されていて、ブレッドボードのGNDとVccを、それぞれDE0ボードのGNDおよび3.3Vのピンにジャンパーケーブルで接続しています。各スイッチの信号線も同様にDE0のGPIOピンに接続しています。

FPGA上ではGPIOのピンの値を読んで、それをそのままLEDの点灯消灯で出力しています。スイッチが押されると信号線がGNDと接続されるので入力の値がLOになり、LEDが消灯するという仕組みです。もちろん、押したスイッチに対応するLEDだけが消灯することを期待していました。しかし実際には全部のLEDが消滅します。

問:なぜこのような現象が起きたのでしょう?

ヒント1

ヒントは問題の解決に関係ないものも含めて全部で9つあります。

抵抗は4.7KΩのものが手元になかったので7.5KΩのを使いました。これは問題ないはず。

ヒント2

7.5KΩだと思っている抵抗が実は何を間違えたか75Ωくらいで、1個スイッチを押しただけでVccの電圧が低下してしまう…とか考えてカラーコードを確認してみましたが、紫緑赤金だったので抵抗値が間違っているという線は消えました。

ヒント3

スイッチを引きぬいて確実に開放状態にした上でも、1つのスイッチを押すとすべてのLEDが消灯します。信号線自体を引き抜くとそれに対応したLEDだけ消灯しなくなります。というわけでソフト側ではなくハード側の問題だとは思いましたが…

ヒント4

4つのスイッチを十字に配置するために配線がグネグネしているので、そこで何か間違っている可能性を考え、シンプルに2個のスイッチX、Yをつないでみましたが、やはりXを押してもYを押しても、XとY両方のLEDが消灯します。

諦めてジャンパーケーブルを抜いて片付けました。

ヒント5

「信号線の電圧をテスターで測ってみたら?」という提案を受けて、もう一度GNDとVccをそれぞれジャンパーケーブルをで接続してGNDと抵抗の足元との電位差を計測。スイッチを押していない状態で3.3V、スイッチを押した状態で0V、その時押されていない隣のスイッチは3.3V、と至極まっとうな状態でした。

ヒント6

ところがこの状態から信号線をジャンパーケーブルでDE0のGPIOに接続すると、スイッチXを押した時にはLED Xだけが消灯します。おやおや?「何もしてないのに直った」だなんて結論では満足出来ませんよ…。

ヒント7

スイッチYを押した時には何も消灯しません。スイッチYをつないでいるジャンパーケーブルを別のものに交換すると、スイッチYを押した時にLED Yだけが消灯する、本来の期待通りの挙動になりました。

ヒント8

元の十字スイッチを復元してみたところ、問題なく4つのボタンそれぞれを押した時にだけそのスイッチに対応するLEDが消灯するようになりました。

ヒント9

試しにその状態からVccをつないでいるジャンパーケーブルを抜いてみたところ、当初のような「どのスイッチを押しても4つのLED全部が消灯する」という現象が再現しました。

解答編

最初に作った十字スイッチ回路の、Vccをつなぐのに使ったジャンパーケーブルは実は断線していました。そのためブレッドボードのVccはどこにも繋がっていない状態でした。スイッチが一つでも押されると、そのスイッチの信号線がGNDに接続されてLOになるだけではなく、プルアップ抵抗と「Vccだと思い込んでいるただの配線」を通ってすべての信号線がGNDに接続されてLOになるわけです。

感想

ソフトウェアとハードウェアの大きな違い: ソフトウェアの部品は、一つのインスタンスが仕様通りに動くことを確認したら、どのインスタンスでも仕様通りに動く。ハードウェアの部品は、たまに仕様通りに動かないインスタンスが混ざっている。

ソフトウェアでは「あ、この代入演算子、断線してますね」なんてことがありえないので、そこを疑うコストを割かずに実装ができるわけです。とても恵まれています。