AT89C51マイコンを使用して複数桁の7セグメントLEDを制御する際、表示がちらついたり、一部の桁だけ点灯したり、意図しないパターンが表示されることがあります。これは「ダイナミックスキャン」のタイミングや論理に誤りがあることが原因です。本稿では、Proteus環境で共陽極6桁7セグメントLED(7SEG-MPX6-CA)を安定して駆動するための「セグメント選択(セグ選)」と「ディジット選択(位選)」の仕組みを解説し、実装例を示します。
7セグメントLEDの基本構造
7セグメントLEDはa~gの7つのセグメントと小数点(dp)の計8つのLEDで構成されます。内部接続方式により以下の2種類があります:
- 共陽極(Common Anode):全LEDのアノードがVCCに接続され、カソード側をLOWにすることで点灯。
- 共陰極(Common Cathode):全LEDのカソードがGNDに接続され、アノード側をHIGHにすることで点灯。
Proteusでは7SEG-MPX6-CAが共陽極6桁タイプです。「CA」はCommon Anodeを意味します。
セグメントコードの定義
共陽極の場合、点灯させるセグメントにはLOW(0)、消灯にはHIGH(1)を出力します。数字「0」を表示するにはa~fを点灯(gを消灯)するため、ビットパターンは11000000b = 0xC0となります。以下に共陽極用のセグメントテーブルを示します:
// 共陽極用セグメントコード(0~9)
unsigned char code digit_pattern[10] = {
0xC0, // 0
0xF9, // 1
0xA4, // 2
0xB0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xF8, // 7
0x80, // 8
0x90 // 9
};
共陰極を使用する場合は、各値をビット反転(~digit_pattern[n])する必要があります。
ディジット選択のハードウェア設計
6桁のLEDを個別に制御するには、各桁のCOM端子を独立してON/OFFできる必要があります。共陽極の場合、COM端子は通常VCCに接続されますが、実際にはトランジスタスイッチで一時的に接地することで「非選択状態」にします。しかし、これでは動作しません。
正しいアプローチは、COM端子をVCCに直結し、NPNトランジスタでカソード側をGNDに接続する方法です。つまり、共陽極LEDの各セグメント出力をP0ポートに接続し、各桁のカソードをNPNトランジスタ(例:2N2222)を介してGNDに接続します。このとき、トランジスタのベースにHIGHを出力するとその桁が点灯します(共陰極駆動と同様の論理)。
ただし、多くのシミュレーション回路では簡略化のため、位選信号をLOWアクティブとして扱い、ソフトウェアで論理を調整します。つまり、選択したい桁に対応するポートピンをLOWに設定します。
ダイナミックスキャンの実装
人間の視覚持続時間(約16ms)を利用し、1桁ずつ高速に切り替えることで、すべての桁が同時に点灯しているように見せます。重要なのは消隠処理(Blanking)で、セグメントデータを変更する前に一旦すべてのセグメントを消灯させることです。これにより「ゴースト表示」を防ぎます。
void update_display() {
static unsigned char idx = 0;
P0 = 0xFF; // 消隠:全セグメント消灯
P2 |= 0x3F; // 全桁非選択(P2.0~P2.5をHIGH)
P0 = digit_pattern[buffer[idx]]; // 表示データ設定
P2 &= ~(1 << idx); // 対象桁を選択(LOWアクティブ)
idx = (idx + 1) % 6; // 次の桁へ
}
この関数を1ms間隔で呼び出すことで、6桁全体のリフレッシュ周期は6ms(約167Hz)となり、ちらつきなく表示できます。
Proteus回路接続のポイント
- P0ポート → LEDのa~dpセグメント(10kΩのプルアップ抵抗必須)
- P2.0~P2.5 → 各桁のカソードを駆動するNPNトランジスタのベース(1kΩのベース抵抗推奨)
- LEDのCOM端子 → VCC
- トランジスタのエミッタ → GND
より高信頼性を求める場合は、ULN2003などのドライバICを使用すると、電流駆動能力とノイズ耐性が向上します。
タイマ割り込みによる非同期更新
メインループで表示更新を行うとCPUがブロッキングされるため、タイマ割り込みを使用したバックグラウンド更新が推奨されます:
unsigned char display_buf[6] = {1,2,3,4,5,6};
unsigned char current_pos = 0;
void timer0_init() {
TMOD = (TMOD & 0xF0) | 0x01; // モード1(16ビットタイマ)
TH0 = 0xFC; // 12MHz晶振で1msタイマ(65536-1000=64536=0xFC18)
TL0 = 0x18;
ET0 = 1;
TR0 = 1;
EA = 1;
}
void timer0_isr() __interrupt(1) {
P0 = 0xFF;
P2 |= 0x3F;
P0 = digit_pattern[display_buf[current_pos]];
P2 &= ~(1 << current_pos);
current_pos = (current_pos + 1) % 6;
}
これにより、メイン処理はセンサ読み取りや通信処理など他のタスクに専念でき、表示は自動で維持されます。
よくあるトラブルシューティング
- ゴースト表示:セグメント切り替え前の消隠漏れ → 必ず
P0 = 0xFFを挿入 - ちらつき:スキャン周期が長すぎる → タイマ割り込みで1桁あたり≤1.5msを確保
- 特定桁の輝度低下:トランジスタの飽和電圧 or ベース抵抗過大 → 抵値を1kΩ以下に調整
- 表示が逆(例:0が8に見える):共陽/共陰の混同 → セグメントテーブルの確認