Arduinoベースの蛍光表示管(VFD)コントローラーキット
必要な部品と材料
| Arduino UNO | ×1 |
| VFDディスプレイ | ×1 |
| CD4094 (DIP IC) | ×2 |
| CD4017 (DIP IC) | ×2 |
| BC557トランジスタ(または同等品:2N3906, 2SA733等、または高電圧PNP) | ×17 |
| BC547トランジスタ(または同等品:2N3904, 2SC945等) | ×2 |
| PC817 オプトカプラ | ×1 |
| 1N4007 ダイオード | ×4 |
| 電解コンデンサ | ×4 |
| 470nF コンデンサ | ×1 |
| ユニバーサル基板 | ×1 |
| 1/4W 抵抗 | ×24 |
| デュポンコネクタ/ジャンパー | ×1 |
| 配線用ワイヤー | ×1 |
必要な工具と機器
- はんだごて(汎用)
- はんだ線(0.5mm推奨、安物は避ける)
- ニッパー
このプロジェクトについて
何の役に立つのか?
VFD(蛍光表示管)はガラス製で白熱フィラメントを持ち、真空管の一種です。非常に明るく、時にはカラー発光し、暗色フィルターが必要なほどです。現在はトレンドではありませんが、独特の魅力があります。
通常、VFDを駆動するには専用チップが必要で、多くのモジュール/機器にはI2CやSPIで制御可能な専用チップが搭載されています。しかし、そのチップがないVFDを入手した場合、「つないですぐ動く」わけではありません。理想は専用チップを購入することですが(通常SMD)、この回路はArduinoのようなマイコンとVFDの間にかなり良いインターフェースを提供します。かなりの種類のVFD、大型の「チューブ型」ビンテージVFDにも対応可能ですが、セグメント数が非常に多いマトリックス表示には向きません(拡張は可能ですが、数千セグメントは現実的ではありません)。
メリットとデメリット
メリット
- ほぼゼロコスト(部品が揃っていれば)
- 部品が入手しやすい
- 高電圧対応(アノード側で数百Vまで)
- グリッドとアノードの電圧を独立制御可能
- デジタルデータ線はわずか3本で済む
- 拡張可能(CPU負荷は増えるが)
- 全8ビットArduinoで動作するコードが完成済み
デメリット
- 複数の異なる電圧/電源が必要(VFDでは一般的)
- 調光機能なし(電圧駆動による調整のみ)
- ArduinoのSPIとタイマー1リソースを使用
- マイコンがハングするとスキャンが1グリッドで止まり、セグメント焼損の恐れ
- 単なる抵抗とLEDではないため、それなりの工作が必要
動作原理
VFDは通常、多重化(マルチプレクス)方式で動作します。X-Yマトリックスのような構造で、フィラメント(カソード、前面の細いワイヤー)を加熱し、そこにグラウンドを接続。グリッド(制御グリッド、X軸の一点)に正電圧、セグメントピン(アノード、Y軸の一点)に正電圧をかけると、1セグメントだけが発光します。
任意のセグメント組み合わせを少ない配線で点灯させるため、多重化では1度に1グリッドを選択し、同時にそのグリッド配下のセグメントに対応するアノードを設定します。少し後に次のグリッドを選択し、アノードをそのグリッド用に再設定。この連続スキャンを高速化すると、残像効果(POV:Persistence Of Vision)により全グリッドが同時に駆動されているように見えます。
本回路では同系のICを2種類使用します:CD4094を2個(アノード駆動用)、CD4017を2個(グリッド駆動用)。CD4094は出力にハイ/ロー設定を保持できるためアノード側に適し、CD4017は10段のシーケンシャル出力を持つためグリッド側に最適です。CD4094に瞬間的なアノード設定をロードし、「ストローブ」信号で適用と同時にCD4017を1ステップ進めることで、自動シーケンシングを実現しています。
電力部は基本的にBC557トランジスタ(または同等品)のみで、アノード側の電圧スイングを広げています。VFDはArduinoの5Vより高い電圧を必要とするためです。グリッドはCD4017が直接駆動し、PC817オプトカプラによりCD4017周りにより高い電圧(5V以上)を扱えるほか、CD4094とは異なる電圧レベルも可能で、回路が大幅に簡素化されています。
Arduinoが全オーケストレーションを担当します。全セグメント設定を記憶し、グリッド切り替えごとにアノード設定を回路にロードし続ける必要があり、ユーザー操作以外のバックグラウンドで賢いコードが動作し続ける必要があります。作成したコードはタイマー割り込みベースで、約1000回/秒の頻度でCD4094をリロードします。10グリッドならリフレッシュレート100Hzとなり、十分実用的です。セグメント設定を格納するデータ配列があり、これをコード内で変更するだけで、特別な手順なしに割り込みルーチンが自動的にデータをアップロードします。
電源
グリッド電圧(CD4017側): 5V〜18V DC、最低50mA。安定化不要。通常12Vで十分。電圧を上げても輝度はそれほど向上しない(真空管ならグリッドに巨大電圧は不要)。
アノード電圧: 0V〜トランジスタ耐圧まで(BC557なら50V DC)。通常、完好なVFDなら20〜30V DCで完璧。安定化不要。通常セットアップなら50mA以上で十分。
デジタル電源: 5V DC(3.3V Arduino/MCUなら3.3V DCも可、未検証)。約100mA必要。CD4094の動作が怪しい場合、コードでSPIクロックを下げるか、最初の4094の「Q'S」出力を使ってより安定した通信にできます。
フィラメント電源: 最低5V 200〜300mA。既にDC電源があるならブリッジ整流器と1000μFコンデンサは省略可能。ただし、Arduino用デジタル電源とは共用不可。実際のフィラメントは小型機器のVFDで約3V、150mA程度で動作します。





ヒントと注意点
- 配線前にピン配置が不明な場合、まずVFD単体でテストを。単3電池2本直列でフィラメント(通常は側面端子)、9V電池2本直列でグリッドとアノードに正極性を与えると良い。グリッドとアノードはグループ化されていることが多い。「アノード0」を見つけると良い(多くの場合、数字/英数字部分の最初のセグメントで、全グリッドで同一順序のことが多い)。誤って逆順に配線するとソフトウェアが面倒になる。グリッドはピン順に並んでいる傾向あり。
- フィラメント電流は少しずつ上げる。暗所でフィラメントがわずかに白熱し始めたら少し戻す。パワーポテンショメーターが理想的(古いテレビにあったが、今は入手困難)。
- グリッド/アノード電圧は慎重に上げる。蛍光体は焼損すると修復不能。回路はスキャン駆動なので、システムがハングすると単一グリッドに継続的に過電力がかかる。
- グリッド/アノード電圧の妥協点を探る。グリッドも電流を消費するため、無理に上げずアノード側を重視。
- プロジェクト全体で単一トランスを使うのが理想。スイッチング電源、特にアース付きはトラブルの元。実験ではクラシックなトランス電源が安全。
- CD4094と/or CD4017を追加して拡張可能。8アノード以下なら2個目の4094は不要。グリッドも同様。でも手元に置くならフル構成で作るのが無難。
- アノード側トランジスタのベース抵抗はなし。超高速駆動が可能で部品点数削減だが、CD4094が多少電力を消費。発熱はほぼないが、チップによっては出力が強すぎる場合あり。電流消費を確認のこと。チップは破損しない(許容範囲内)。5V電源でピンあたり4mA程度。
- 古く暗くなったVFDは、フィラメントに電流ショックを与えると復活することがある。全体が暗い場合(セグメント焼損でない場合)、フィラメントを肉眼で白熱(黄色)レベルまで5回、各数秒間行うとクリーニング効果あり。定格電圧の2倍必要な場合も。非常に古い/アンティーク向け。リスクあり(ワイヤー切断の恐れ)。
コード
Arduino code with working routine (Arduino)
/* Versatile VFD Display Hardware Interface Arduino Program
Copyright (C) 2019 Genny A. Carogna
GNU GPL v3 license */
// このコードはArduinoのリソースを「裏方」で使用:
// タイマー1とSPIモジュールの両方を占有するため、
// SPI、SPIピン、タイマー1依存の機能と併用すると問題発生の可能性大
// 提案回路にオンボードメモリがないため、セグメント設定を
// Arduinoのセグメント配列から毎グリッド、毎秒100回「スキャン」し続ける必要あり
// これは「オーバーヘッド」CPU作業だが、通常のマイコン使用レベルで約3%程度
#define ledLed 13
#define strobePin 4
#define strobeHold 10 // マイクロ秒
// 表示デバイスごとのグリッド/アノード数定義例
#define JVC
#define mitsubishiGrids 10
#define mitsubishiAnodes 9
#define marantzGrids 9
#define marantzAnodes 14
#define JVCGrids 11
#define JVCAnodes 19
#if defined mitsubishi
#define gridAmount mitsubishiGrids
#define anodeAmount mitsubishiAnodes
#elif defined marantz
#define gridAmount marantzGrids
#define anodeAmount marantzAnodes
#elif defined JVC
#define gridAmount JVCGrids
#define anodeAmount JVCAnodes
#endif
// セグメントビット格納配列
// segments[0] = グリッド0、LSB = そのグリッドのセグメント0
// この配列をどこからでもアクセス/変更可能。効果は即座に表示に反映
#if anodeAmount > 16
volatile uint32_t segments[gridAmount] = {0}; // 16超セグメント(32bit必要)
#elif anodeAmount > 8
volatile uint16_t segments[gridAmount] = {0}; // 8超セグメント(16bit、二重転送)
#else
volatile uint8_t segments[gridAmount] = {0}; // 8以下セグメント(8bit)
#endif
void setup() {
// SPIピンと機能ピンを高速/直接に出力設定
*portModeRegister(digitalPinToPort(PIN_SPI_SS)) |= digitalPinToBitMask(PIN_SPI_SS);
*portModeRegister(digitalPinToPort(PIN_SPI_MOSI)) |= digitalPinToBitMask(PIN_SPI_MOSI);
*portModeRegister(digitalPinToPort(PIN_SPI_SCK)) |= digitalPinToBitMask(PIN_SPI_SCK);
*portModeRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin);
*portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin); // LOW
*portModeRegister(digitalPinToPort(ledLed)) |= digitalPinToBitMask(ledLed);
*portOutputRegister(digitalPinToPort(ledLed)) &= ~digitalPinToBitMask(ledLed); // LOW
delay(800); // CD4017リセット完了待ち(4017通電後にコード実行必要、一括通電時自動化)
cli(); // 割り込み無効化
// タイマー1設定:「グリッド数 × 100」回/秒の割り込み = 総リフレッシュ100Hz
TCCR1A = 0; TCCR1B = 0; TCNT1 = 0;
OCR1A = 160000 / gridAmount;
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS10);
TIMSK1 |= (1 << OCIE1A);
// SPI設定:Arduino SPIツールは割り込み依存のため使用不可
// 割り込み内で通信する必要あり
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0); // 1MHz標準SCK
// SPSR = (1 << SPI2X); // 半分クロックにする場合はこちらを有効化
sei(); // 割り込み有効化
// 追加のsetup()コードはここから
}
void loop() {
show(); // デモ表示、削除可
}
// 割り込みサービスルーチン:グリッド切り替えごと、秒100回実行
ISR(TIMER1_COMPA_vect) {
static uint8_t turn = 1; // 現在のグリッド位置
#if anodeAmount > 24
SPDR = ~uint8_t(segments[turn] >> 24);
while (!(SPSR & (1 << SPIF))) ;
#endif
#if anodeAmount > 16
SPDR = ~uint8_t(segments[turn] >> 16);
while (!(SPSR & (1 << SPIF))) ;
#endif
#if anodeAmount > 8
SPDR = ~uint8_t(segments[turn] >> 8);
while (!(SPSR & (1 << SPIF))) ;
#endif
SPDR = ~uint8_t(segments[turn]);
while (!(SPSR & (1 << SPIF))) ;
// ストローブ(ついに!)これもCD4017のグリッド切り替え
*portOutputRegister(digitalPinToPort(strobePin)) |= digitalPinToBitMask(strobePin);
delayMicroseconds(strobeHold);
*portOutputRegister(digitalPinToPort(strobePin)) &= ~digitalPinToBitMask(strobePin);
turn++;
if (turn == gridAmount) turn = 0;
}
// 以下、デモ用コード(削除可能)
void show() {
// 数字フリップ、セグメントフリップ、全点滅などのデモパターン
// 実装詳細は元コード参照
}
回路図
すべてのBC557トランジスタは描画を省略(CD4094出力すべてに同一接続)。
ジャンパーセレクター(赤線上)をCD4017チップで設定必須。2個の4017をグリッドスキャン完了でリセットするため、最後のグリッドの次の出力ピンにフライングジャンパーを接続(またはハンダ付け)。例:10グリッドなら11番目出力にジャンパー。
Arduino接続は赤線で示す。ArduinoにはUSB電源可だが、残り電源には古いタイプのトランス使用を強く推奨(PCのようなアース付き電源を使っている場合)。これら電源はすべてDC(フィラメント電源のみACの可能性あり、ダイオードを追加)。フィラメント電源は通常3V 150mA程度、5V ACトランスで十分。「バラスト」は100Ω程度の巻線パワーポテンショメーター、固定抵抗、または1N4007ダイオードで電圧降下。
BC557トランジスタがアノードをアノード電圧までプルアップ、100kΩ抵抗でセグメントOFF時に電圧を落とす。CD4094は通常5Vのまま。
CD4017はグリッド電圧で駆動、追加トランジスタ不要。「ゲットー」リセット回路(約0.1秒)で4017をリセット、落ち着くまでコード実行不可。カスケード接続の「正統」回路はロジックゲートIC必要だが、NPNトランジスタとオプトカプラでANDゲートを模倣、クローズは十分高速、リリースは遅いが問題なし(4017クロック入力はシュミットトリガー、要求速度は「怖くない」)。
チップ類のグラウンドが複数あるので注意深く確認を。

製造プロセス
- Arduino UNOで作る『イカゲーム』テーマのインタラクティブ人形 - DIYプロジェクト
- ILI9341 2.4インチTFTタッチシールドでのビットマップアニメーション実装ガイド
- Web制御DMX照明システム:Arduino Megaマスターコントローラー
- Arduino UNOとOLEDディスプレイでクラシックPongゲームを自作:ステップバイステップチュートリアル
- DIY Arduino USBゲームコントローラー - 高性能ゲームパッドを自作
- Unopad: Ableton Live連携シームレスなArduino MIDIコントローラー
- Arduino Dueと3.2インチTFTで作る温度監視付きリアルタイムクロック
- Arduino MegaとNextion HMIで作る高機能ウェザーステーション
- ArduinoとNokia 5110ディスプレイで自作電圧計:ステップバイステップガイド
- BME280 温湿度・気圧データを Nextion タッチスクリーンに表示
- Arduino超音波センサーとProcessingで作るリアルタイム距離測定ソナー