PIC ことはじめ ~ 7 セグメント LED を使う

Posted by
ぴろり
Posted at
2012/01/18 21:52
Trackbacks
関連記事 (2)
Comments
コメント (2)
Post Comment
コメントできます
Category
電子工作 カテゴリ
カバーイメージ

 電子工作における標準出力といえば LED ですが(?)、今回は少しステップアップして 7 セグメント LED を使ってみたいと思います。

このエントリーをはてなブックマークに追加  

ポートの節約

 7 セグメント LED には、各セグメントの点灯/消灯を制御するのに夫々 1 本の制御線が必要で、セグメント全体でキャラクタ一桁を表示するには合計 7 本の制御線が必要になります。仮にデジタル温度計の温度表示を、整数部 2 桁、小数点以下 1 桁の 0.0 ℃ から 99.9 ℃ まで表示可能にしたいのであれば、普通に考えると 7 セグメント × 3 桁 = 21 本もの制御線が必要になってしまいます(Fig.1 )。ちょっとしたデジタル温度計を作りたいだけなのに、28 ピンなんてごついチップを使うのはガッカリ(´・ω・`)ですよね。
 少ない制御線でいかにして 7 セグメント LED を駆動するのか? 電子工作の先人たちは様々な方法でこれを実現してきました(大袈裟)


Fig.1 桁数が増えると悲惨なことに…

この記事で登場する 7 セグ LED は、カソード コモン品(=GND がパッケージ内で共通線になっている)としています。電源が共通線のアノード コモン品もあるので、パーツ屋さんで購入する際には注意しましょう。また、解説のため、電流制限抵抗などの部品は盛大に省略しています。実装される際には、各部品の定格内で動作するよう、適当な追加を行ってください。

ダイナミック点灯を使う

 全桁の全セグメントについて、それぞれ点灯/消灯の制御をおこなった場合、点灯すべきセグメントは常に点灯するようになっていますが、この表示方法はスタティック(static; 静的)点灯と呼ばれます。表示すべきセグメントは常に点灯状態のまま変化しない、つまり静的だからです。
 ここで、最初に 1 桁目だけを点灯し、次に 2 桁目だけを点灯、最後に 3 桁目だけを点灯して、そしてまた 1 桁目だけを点灯する...という動作を高速に行うと、人間の目には残像によって、全ての桁が点灯しているように見えます。数値を表示している桁は常に一つで、他の桁は全て消灯しています。これがダイナミック(dynamic; 動的)点灯と呼ばれる方法です。この方法では、表示すべきセグメントを制御する 7 本の制御線と、どの桁を表示するのか制御する桁数分だけの制御線があればよいことになります(Fig.2 )。つまり、3 桁の数値を表示したいのであれば、7 + 3 = 10 本の制御線があればよいのです。素直に全部結線した場合の半分になりました!


Fig.2 表示すべき桁をトランジスタで制御する

 桁数 n の 7 セグ LED を制御したい場合、スタティック点灯では 7n 本の制御線が必要になりますが、ダイナミック点灯では 7+n 本の制御線で済むので、必要なポート数を劇的に減らすことができます。特に、表示したい桁数が多いときにはこうか は ばつぐん だ!
 ダイナミック点灯では、桁数が増えても必要な信号線の増加を抑えられるメリットがありますが、表示がチラつくという問題が出てきます。特に桁数が多い場合に顕著で、表示が波打っているように見えたり、各桁の実質の点灯時間が短くなることから、表示が暗くなるデメリットがあります。桁数が多すぎる場合には、上位桁と下位桁で制御回路を更に分けるなどの対策が必要になってきますが、これらのデメリットを無視できるのなら非常に有用なテクニックには違いありません。

ドライバ IC を使う

 7 セグメント LED では、各セグメントを各個に自由に点灯/消灯させることができますが*1、人間にとって意味のある点灯パターンというのは限られています。特に、今回の温度計のような例であれば、7 セグメント LED に 0 から 9 までの数字シンボルを表示できれば十分ですから、点灯パターンは高々 10 通りしかありません。ここで、24=16 であることから、0 から 9 までの数値を表示したいのであれば、高々 4 本の制御線があれば事足りることがわかります。
 この制御線のオン/オフの組み合わせに応じて、点灯すべきセグメントを適切に制御してくれるのがドライバ IC と呼ばれるものです。ドライバ IC には、74HC451174LS47 などがあります。いずれも、4 本の信号線で 7 セグメント LED に 0 から 9 までの数字を表示できます(Fig.3 )。


Fig.3 7 セグ LED ドライバ IC を使う

もっと多く桁を表示する

 ダイナミック点灯方式を用いて、例えば 8 桁の数値を表示したい場合を考えます。そのまま考えると、追加で 8 本の制御線があればよさそうですが、もっと減らせないでしょうか? ダイナミック点灯では、ある瞬間において点灯している桁は基本的には常に一つだけですので、先のドライバ IC のように、制御線のオン/オフの組み合わせで 1 桁目から 8 桁目を指定できればよいと考えられます。そんな場合、3-8 デコーダ IC というピッタリな IC があります。これを利用すれば、3 本の信号線で 8 通りの出力を制御できるので、8 桁の数値表示でも、たった 3 本の制御線を追加すればよいことになります(Fig.4 )。


Fig.4 3-8 デコーダ IC で点灯する桁を指定する

説明のため簡略化してありますが、3-8 デコーダとセグメント LED のコモン間にはトランジスタなどのドライブ回路が必要です。直結した場合、3-8 デコーダ IC のシンク電流の制限に掛かります。

お財布と相談

 …と、このように、必要な制御線の本数を節約する方法が色々とあるのですが、一方で、マイコンの外に追加で IC が増えてしまう、というデメリットもあります。マイコンの出力ポートが足りない!→ポート数を増やしたいけど、マイコンのランクを上げるとお値段が…→ドライバ IC/デコーダ IC を使って出力ポートを節約しよう→ドライバ IC の追加コストの方が高かった…なんてありそうな気もします。そこら辺はコストやら、基盤の空きスペースやらと相談で。

実験

 さっそくダイナミック点灯による 3 桁の 7 セグメント LED の制御をやってみます。手持ちがないので、ドライバ IC もデコーダ IC も使わず、PIC16F819 だけを使用しました。実行すると、000、001、012、123、234、...、DEF、EF0、F01、...と表示を繰り返します。

#include <htc.h>

__CONFIG (
        FOSC_INTOSCIO   // 内蔵オシレータ
      & WDTE_OFF        // WDT 無効
      & PWRTE_OFF       // PWRT 無効
      & MCLRE_OFF       // MCLR 使用しない
      & BOREN_OFF       // BOD 無効
      & LVP_OFF         // Low-Voltage Programming 使用しない
      & CPD_OFF         // Data memory code protection 無効
      & DEBUG_OFF       // In-Circuit Debugger 使用しない
      & CP_OFF          // Program Memory code protection 無効
);

// 7 セグメント LED の点灯パターンを定義
static int pattern_7seg [] = {
    //abcdefg.
    0b11111100,     // 0
    0b01100000,     // 1
    0b11011010,     // 2
    0b11110010,     // 3
    //abcdefg.
    0b01100110,     // 4
    0b10110110,     // 5
    0b10111110,     // 6
    0b11100100,     // 7
    //abcdefg.
    0b11111110,     // 8
    0b11110110,     // 9
    0b11101110,     // A
    0b00111110,     // B
    //abcdefg.
    0b10011100,     // C
    0b01111010,     // D
    0b10011110,     // E
    0b10001110,     // F
};

static void ResetTimer0 ()
{
    TMR0 = -200;
}

static int timeout;             // Timer0 のタイムアウトをカウントする
static void ResetTimer0_TimeoutCount ()
{
    timeout = -900;
}



static int seg_num = 0;         // 表示する値
static int seg_idx = 1;         // 表示中のセグメント

// メインルーチン
void main ()
{
    // PORTA の入出力設定
    TRISA = 0b00000001;
    ADCON1bits.PCFG = 0b1110;
    // PORTB の入出力設定
    TRISB = 0b00000000;

    // Timer0 の準備
    OSCCONbits.IRCF = 0b111;    // 内蔵クロックを 8MHz に設定
    // OPTION REGISTER
    OPTION_REGbits.T0CS = 0b0;  // Timer0 は CLKO(=Fosc/4) 基準で動作
    OPTION_REGbits.PSA = 0b0;   // プリスケーラを Timer0 にアサイン
    OPTION_REGbits.PS = 0b001;  // プリスケーラ比を 1:4 に設定
    TMR0 = 0;                   // Timer0 を一応クリア
 
    INTCONbits.TMR0IE = 0b1;    // Timer0 の割り込みを許可
    INTCONbits.GIE = 0b1;       // グローバル割り込み許可

    // メインループ
    ResetTimer0 ();
    ResetTimer0_TimeoutCount ();
    while (1)
    {
        switch (seg_idx)
        {
        case 1:
            RA3 = 0;
            PORTB = pattern_7seg [seg_num & 0xf];
            RA1 = 1;
            break;
        case 2:
            RA1 = 0;
            PORTB = pattern_7seg [(seg_num >> 4) & 0xf];
            RA2 = 1;
            break;
        case 3:
            RA2 = 0;
            PORTB = pattern_7seg [(seg_num >> 8) & 0xf];
            RA3 = 1;
            break;
        }
    }
}

// 割り込みルーチン
static void interrupt isr (void)
{
    // Timer0 による割り込み
    if (INTCONbits.TMR0IF)
    {
        // 表示するセグメントを切り替える
        seg_idx = (seg_idx % 3) + 1;

        // タイムアウトしたので表示する数値を更新
        if (!timeout++)
        {
            seg_num = (seg_num << 4) | (seg_num + 1) & 0xf;
            ResetTimer0_TimeoutCount ();
        }

        INTCONbits.TMR0IF = 0;  // 割り込みフラグをクリアするのはプログラムの仕事
        ResetTimer0 ();         // タイマを再スタート
    }
}
  • メイン ループ内では、seg_idx の値に応じて桁に表示すべき値を求め、ポートに点灯パターンを出力します(80~97行目)。
  • Timer0 を利用して割り込みを行い、表示する桁 seg_idx を切り替えます(108 行目)。
  • 適当な時間が経過したら、表示する数値を更新します(113 行目)。

 ...と、こんな感じです。内蔵クロックなので正確な時間はアレですが、計算上は 400 マイクロ秒ごとに桁をくるくる回してダイナミック表示をしています。この状態ではチラツキを感じることはありません。

回路図

回路図

  • PIC の出力ポートは全て正論理動作です。
  • C は発振防止用のパスコン。
  • R1 は 7 セグメント LED の電流制限抵抗。使用した LED の定格は、順電圧 2.1V、順電流 20mA ですので、R = (5.0V - 2.1V) ÷ 20mA = 145 Ω。手持ちが無かったので 1kΩ で。セグメントあたり 3mA。
  • R3 はプルダウン抵抗...って認識でいいのかな? 出力ポートが H レベルの時、逃げ電流は VBE ÷ 10kΩ = 70μA
  • R2 はトランジスタの電流制限抵抗。出力ポートが H レベルの時、電流は (5.0V - VBE) ÷ 10kΩ = 430μATR の増幅率を 200 とすれば、IC = ( 430uA - 70uA )×200 = 72mA

 RB<7:0> で点灯させたいセグメントを H レベルにして制御しています。また、RA<3:1> で、どの桁を表示するかトランジスタ ドライバを駆動しています。たった、これだけの回路ですが、なかなかアツイですね。

このエントリーをはてなブックマークに追加  

  1. *1 理論上 27=128 通りの点灯パターンがある

この記事を読んだ人はこんな記事も読んでいます記事リコメンデーションについて

カバー画像:PIC ことはじめ ~ A/D 変換 + 7 セグメント LED

関連記事/トラックバック (全 2 件中、最新 5 件まで表示しています)

Open MagicVox.net のスクリーンショット
タイトル
PIC ことはじめ ~ LCD を使ってみる
Trackbacked at
2013/05/26 13:02
from
Open MagicVox.net
概要
数字程度の簡単な表示であれば 7 セグメント LED で十分かもしれませんが、アルファベットを表示したり表示する文字が多い場合には、7 セグメント LE...

Open MagicVox.net のスクリーンショット
タイトル
PIC ことはじめ ~ A/D 変換 + 7 セグメント LED
Trackbacked at
2012/01/23 13:43
from
Open MagicVox.net
概要
 さて、7 セグメント LED による表示ができるようになったところで、以前、実験した A/D 変換を組み合わせてみたいと思います。これによって、A/D...

この記事にトラックバックを送るには?

寄せられたコメント (全 2 件中、最新 5 件まで表示しています)

Posted by
ぴろり ◆OLEEi.VOX.ぴろり ◆OLEEi.VOX.
at
2017/02/10 11:34
ID
Gao0beG.
こんにちは
Vdd と Vss の件と、カソード コモンであること(と、回路図を追加したり色々)を追記しておきました
ツッコミありがとうございます(=゚ω゚)ノ
Posted by
加藤 修加藤 修
at
2017/02/10 06:22
ID
O2Av.s/Y
7セグダイレクト表示及びAD変換+7セグ表示の回路図ですが電源回路のVSSと
VDDの位置が逆になってはいませんか?
ところで7セグは回路的にカソードコモンだと思いますが初心者的にはその辺も明示
してほしいと思います。
プログラムはの公開は大変参考になります。ありがとうございます。
これからも見させて頂いて勉強させていただきますので宜しくお願いします。

コメントを投稿する

 
 (必須, 匿名可, 公開, トリップが使えます)
 (必須, 匿名可, 非公開, Gravatar に対応しています)
 (必須)
スパム コメント防止のため「投稿確認」欄に ランダムな数字 CAPTCHAについて を入力してから送信してください。お手数ですがご協力のほど宜しくお願いいたします。