HOME > 技術談話 > PIC® microcontroller > セグメントLED制御
はじめに 7セグメントLED テーマ 回路 配置例 サンプルプログラム
ポート割り付け LEDの点灯と消灯 ダイナミック制御 イメージ処理 フローチャート
桁数を増やす際の注意点
ダイナミック制御を利用してセグメントLEDを制御するサンプルプログラムについて解説します。 今回のサンプルでは2桁の7セグメントLEDを制御しますが、制御内容の理解を補足する意味で、 |
・LEDのPWM制御 | … 点灯周期と点灯比率について解説しています。 | |
・複数LED制御 | … スタティック制御とダイナミック制御について解説しています。 | |
・1つのスイッチ | … 1つのスイッチで複数の事象を認識する方法について解説しています。 |
を予めチェックすることをお勧めします(リンク先は新しいウィンドウで開きます)。 今回のテーマを「試食」でと思いましたが、技術としては枯れた(?)内容なので中級編の位置付けにしました。 注意事項 |
▲ |
電卓や時計で良く目にすると思いますが…。言葉よりも写真の方がイメージが伝わりやすいですね。 |
左の写真は2桁が内蔵された7セグメントLEDです。 各LED素子はセグメントと呼ばれ、7つ(厳密には8つ)のLED素子を予め数字の「8」風に配置しています。 一般的にはピリオドを含め「0」~「9」の数字をを表示しますが、各LEDは個別に制御できるので数字以外の形を表現することもあります。 写真では発光していない部分が目立ちますが、製品に組み込む際は表面に濃い色のフィルターを被せて発光している部分だけを強調します。 |
各セグメントLEDには番号、もしくは英字が割りつけられています。そして複数個のLEDを点灯することで英数字を表現します。 数字の「1」 … b,cを点灯 数字の「3」 … a,b,c,d,gを点灯 数字の「8」 … a,b,c,d,e,f,gを点灯 |
デバイスにはアノードコモンとカソードコモンがあり、1桁の場合は最低9本の端子が必要です。 2桁の数字の場合は18本の端子が必要ですが、デバイスによっては2桁を1つのデバイスに内蔵して都合10本の端子で済む場合があります。この場合は各桁の同時点灯が不可能なのでダイナミック制御を利用します。 |
今回のサンプルプログラムで利用した7セグメントLEDは2桁タイプですが「2桁のアノードコモン、兼用型」を利用しています。スタティック制御「も」できる構造なので18本の端子があります。スタティック制御を前提とするならば良いのですが、ダイナミック制御を利用する場合はデバイスの外で配線を補います。 たまたま手元にあったからなのですが…。 兼用型は桁数が多くなると配線が面倒です。ダイナミック制御専用を用意しているデバイスもあるので、デバイス選択時に検討してみてはいかがでしょう。 |
▲ |
PICマイコンに PIC16F648A を利用して2桁の7セグメントLEDをダイナミック制御します。 スイッチを3つ用意します。 ・左 … 上位桁を更新します。1秒以上押下すると連続(200ms周期)して更新を行います。 ・中 … 下位桁を更新します。2秒以上押下すると連続(500ms周期)して更新します。 ・右 … 通常は高輝度(点灯比率50%)で表示しますが、押下中は低輝度(点灯比率25%)で表示します。 数字の更新は「0」、「1」、…「9」、最後に「.」を表示して再び「0」に戻ります。 タイマ割り込みの中でダイナミック制御を行います。タイマ0を使用し、その基準時間は約1msです。 |
▲ |
(1)回路図 これまでのサンプルプログラムと異なり、今回は PIC16F648A を利用します。 このPICマイコンは 3V~5V で動作することができるので便利です。 |
図をクリックすると大きな画像を見ることができます(新しいウィンドウで開きます)。 大きな画像の解像度が荒い場合は画像の上にマウスカーソルをかざすと画像右下に拡大ボタンが表示されます。 電源電圧に 3.3V を推奨しています。今回使用したPICマイコンでは 3.0V~5.0V で動作することができますが、電池駆動では3.0Vを下回ることが予想されるので、3.3Vの安定した電圧が供給されることを想定しています。 3.3Vを想定し電流制限抵抗の値を算出しているので、3.3Vを超える電圧を加えないでください。 なお…個体差というか、今回の実験では表示が暗くなるものの 3.0V 以下でも動作してしまいました。 トランジスタ等の電流を増幅する手段は使用していません。 ダイナミック制御で利用すること、使用した PIC16F648A の出力ポートが大電流を取り扱えることから、電流制限抵抗を接続しただけで他の部品は不要です。しかし利用する7セグメントLEDの特性、PICマイコン側の特性に注意しないとPICマイコンが永久破壊するので注意してください。 今回のサンプルプログラムでは、セグメント1つあたり 約2.01mA(3.3V時) を流しています。ダイナミック制御を使用しますが各桁の中では同時点灯するので、同時点灯時には 約16.08mA の電流が「アノード側の出力ポート」に加わります。更に、消費電力を計算すると 約53.1mW(3.3V時) になります。この条件がPICマイコンで実現できることが大前提になります(単純計算で済ませてますが御容赦)。 |
(2)用意した部品 ブレッドボード上で実験することを目標としているので、まずはブレッドボードに関する部材です。 初期投資が必要ですが、再利用できるので一式持っていると何かと便利です。 ジャンパーワイヤーが足りない場合は買い足すこともできますが、手持ちのワイヤーを使用しても可です。 |
名称 | 通販コード | 金額 | 備考 | |
ブレッドボード | P-00285 | 700 | 大きい種類もあります | |
ジャンパワイヤー | P-00289 | 800 | ワイヤーが足りなかったので | |
ジャンパワイヤー15cm(白) | P-02936 | 300 | 空中配線用(慣れると手放せない) | |
ジャンパワイヤー15cm(緑) | P-02934 | 300 | 空中配線用(慣れると手放せない) | |
ジャンパワイヤー15cm(青) | P-02935 | 300 | 空中配線用(慣れると手放せない) |
直接制御に関わる部材です。 抵抗は…1本売りはしていませんが、いくつか種類を持っていると便利です |
名称 | 通販コード | 金額 | 備考 | |
7セグメントLED表示器 A-552SR | I-00214 | 150 | 2桁のアノードコモンです | |
PICマイコン 16F648A | I-00466 | 180 | DIP形状です | |
カーボン抵抗 1/4W 820Ω | R-25821 | 100 | 100本入っています | |
カーボン抵抗 1/4W 10KΩ | R-25103 | 100 | 100本入っています | |
積層セラミックコンデンサー 0.1uF 50V | P-00090 | 100 | 10本入っています | |
タクトスイッチ(緑) | P-03651 | 10 | 混色100個セットもあります P-01282 | |
タクトスイッチ(黄) | P-03650 | 10 | ||
タクトスイッチ(赤) | P-03646 | 10 |
電源装置については記載しませんでしたが、目標電圧3.3Vを出力することのできる電源を用意してください。 PICマイコンの実力としては乾電池でも大丈夫そうです。 紹介した部材は総て秋月電子通商様で調達することができます(リンク先は新しいウィンドウで開きます)。 通販コードを検索すると詳細を観ることができます。上記の価格は、2010/04/30現在の価格です。 紹介した部材は一例です。新規に購入せず手持ちの部材を利用しても問題ありません。ただ、回路図とプログラムの修正が必要になるので、このあたりは…各自で検討してください。 |
▲ |
(1)ブレッドボード配置例 ユニバーサル基板に組み付けても良いのですが、時間と手間を節約するためにブレッドボードを利用します。 以下に配置の例を載せます。使用した部材の都合により、ひとまわり大きなブレッドボードを用意しました。 |
図をクリックすると大きな画像を観ることができます(新しいウィンドウで開きます)。 大きな画像の解像度が荒い場合は画像の上にマウスカーソルをかざすと画像右下に拡大ボタンが表示されます。 7セグメントLEDを配置する部分(右端)の配線が大変なことになっています。ダイナミック制御専用の7セグメントLEDを利用すればもう少しすっきりすると思います。 赤い線はジャンパ線になりますが、良く見ると交差していても接続していない箇所があります。少しだけ赤い線の位置がズレているのですが…わかりますか?注意点は A21、E49、F49 でしょうか。回路図と見比べてみると分かりやすいのですが、縦線を先に差し込んでから横線を上にバイパス配線します。 |
(2)実装例 部品とジャンパ線を組み付けると以下の様な感じに出来上がります(電源入ってます)。 |
スイッチは左から SW0(緑)、SW1(黄)、SW2(赤) の順で並んでいます。 ブレッドボードの上部にある「+/-」のラインは電源に利用していますが、下部にある「+/-」のラインは7セグメントLEDのアノードコモンで利用しています。 左下に設置する _MCLR 用のプルアップ抵抗を忘れないで下さい。設置しないとリセット誤動作を繰り返します。 |
右端の7セグメントLEDの下は… 余り見せたくない配線です orz 重ねてジャンパできるのは3本が限度かな(汗 高さが厳しい場合は横に並べると低くなります。 ジャンパ線の配線スペースがない場合、届かない場合は空中配線にすると楽です。が、数が多いとジャングル状態になり、表示が見えなくなるので注意しましょう。 |
▲ |
PIC16F648Aを利用したサンプルプログラムを公開します → Test7seg010x.zip 開発環境は MPLAB IDE V8.40 と HI-TECH C V9.70 を利用しています(開発環境の準備)。 HI-TECH C v9.70 で作成されたプログラムを v9.81 以降のコンパイラで再コンパイルする際の注意点 → 開発環境による修正 2011/03/09 今回のサンプルプログラムでは解説済みのスイッチ汎用ライブラリを使用しています。 |
・main.c | … 制御プログラム本体です。割り込み処理も含めて記述されています。 | |
・DrvSw.c | … スイッチ汎用ライブラリです。詳細については「1つのスイッチ」を参照願います。 |
スイッチ汎用ライブラリはユーザ環境(DrvSw.u)のみ変更しています。連続スイッチの認識が簡単になります。 ポインタを利用する方式なので関数のプログラム容量が大きくなりましたが、連続スイッチを3種取り扱うので、3種を個別に作成するよりは必要とする総プログラム容量は少ないです。 |
▲ |
今回使用するPICマイコン、PIC16F648Aのポート割り付けを以下にまとめます。 |
ポート | In/Out | 制御対象 | 備考 | |
RA0 | In | SW0 | スイッチ(左)押下でL。 | |
RA1 | Out | (未使用) | 常時L。 | |
RA2 | Out | DEG 0 COM | 上位桁(左)用アノード共通、Hで点灯可能。 | |
RA3 | Out | DEG 1 COM | 下位桁(右)用アノード共通、Hで点灯可能。 | |
RA4 | Out | (未使用) | 常時L。 | |
RA5 | In | _MCLR | リセット時はL印加 | |
RA6 | In | SW2 | スイッチ(右)押下でL。 | |
RA7 | In | SW1 | スイッチ(中)押下でL。 | |
RB0 | Out | SEG A | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB1 | Out | SEG B | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB2 | Out | SEG C | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB3 | Out | SEG D | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB4 | Out | SEG E | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB5 | Out | SEG F | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB6 | Out | SEG G | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) | |
RB7 | Out | SEG .(DP) | 各桁カソード共通、Lで点灯可能(要アノードとの組み合わせ) |
今回は空中ジャンパ線の助けがあったのでカソードの並びをポートの並びに合わせました。 一般的にカソードデータは値と表示の間で有意味な連続性がないため、固定長のテーブルデータを利用してセグメントの組み合わせを取得することが多いと思います。このあたりは配線上の都合と各自の好みです。 |
▲ |
前述のポート割り付けに基づき、7セグメントLEDを制御する場合の組み合わせは以下の様になります。 RA2は上位桁用のアノード、RA3は下位桁用のアノードに割り付けています。 SEGはLED素子を示します。u?で上位桁が点灯、d?で下位桁が点灯します。 上位桁と下位桁の同時点灯(RA2 = RA3 = H)は禁止です。 |
RA | RB | SEG | ||||||||||||||||
3 | 2 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | . | G | F | E | D | C | B | A | |
L | L | - | - | - | - | - | - | - | - | × | × | × | × | × | × | × | × | |
L | H | - | - | - | - | - | - | - | L | - | - | - | - | - | - | - | uA | |
- | - | - | - | - | - | L | - | - | - | - | - | - | - | uB | - | |||
- | - | - | - | - | L | - | - | - | - | - | - | - | uC | - | - | |||
- | - | - | - | L | - | - | - | - | - | - | - | uD | - | - | - | |||
- | - | - | L | - | - | - | - | - | - | - | uE | - | - | - | - | |||
- | - | L | - | - | - | - | - | - | - | uF | - | - | - | - | - | |||
- | L | - | - | - | - | - | - | - | uG | - | - | - | - | - | - | |||
L | - | - | - | - | - | - | - | u. | - | - | - | - | - | - | - | |||
H | L | - | - | - | - | - | - | - | L | - | - | - | - | - | - | - | dA | |
- | - | - | - | - | - | L | - | - | - | - | - | - | - | dB | - | |||
- | - | - | - | - | L | - | - | - | - | - | - | - | dC | - | - | |||
- | - | - | - | L | - | - | - | - | - | - | - | dD | - | - | - | |||
- | - | - | L | - | - | - | - | - | - | - | dE | - | - | - | - | |||
- | - | L | - | - | - | - | - | - | - | dF | - | - | - | - | - | |||
- | L | - | - | - | - | - | - | - | dG | - | - | - | - | - | - | |||
L | - | - | - | - | - | - | - | d. | - | - | - | - | - | - | - |
消灯方法はアノードを共に「L」にします。これはカソード側を総て「H」にすることでも実現できます。 注目桁の任意LED素子を消灯する場合は対応するカソードを「H」にします。 表を作ってみましたが…実のところ7セグメントLEDなんですよね。要は形。各素子の組み合わせで意味を持ちます。 桁を示すアノードは別として、カソードに関しては表示する数字に1対1で対応する制御データを作ります。 すると、以下の様な変換テーブルが出来上がります(割り付けは上表の綺麗な並び?に基づきます)。 |
数値 | SEG | 値 | |||||||||
. | G | F | E | D | C | B | A | ||||
0 | * | * | * | * | * | * | 0x3F | ||||
1 | * | * | 0x06 | ||||||||
2 | * | * | * | * | * | 0x5B | |||||
3 | * | * | * | * | * | 0x4F | |||||
4 | * | * | * | * | 0x66 | ||||||
5 | * | * | * | * | * | 0x6D | |||||
6 | * | * | * | * | * | * | 0x7D | ||||
7 | * | * | * | * | 0x27 | ||||||
8 | * | * | * | * | * | * | * | 0x7F | |||
9 | * | * | * | * | * | * | 0x6F | ||||
. | * | 0x80 |
今回のサンプルプログラムの場合、ポート出力データは上表の値を反転します(カソード側のため)。 反転操作はプログラムでも実現できますが、単に出力するだけなのでテーブル側で反転したほうが便利です。 自己診断、アニメーション等で各素子を狙い撃ちする場合のデータの並びについては各自で工夫してみてください。 |
▲ |
今回のサンプルプログラムではダイナミック制御を利用し、上位桁と下位桁を高速に切り替えることで見かけ上、同時点灯している様に見せています。 ダイナミック制御を管理するタイマの基準時間は1ms(厳密には1024us)です。この1msを基準に上位桁、もしくは下位桁の点灯(消灯)を行いますが、今回のサンプルプログラムでは高輝度モード(点灯比率50%)と低輝度モード(点灯比率25%)を取り扱うので、少々工夫しています。 具体的には以下のような制御を行ってます。 |
これを採用 |
1ms周期の処理を4回実行することで制御が一巡します。 もう少し簡単にできそうですが、「輝度モードの切り替え」という考えをダイナミック制御の実処理に持ち込まないような構成にしています。 考え方によっては別の方法もあります。 |
この方法も間違いではないのですが、個人的にはバランスが悪いかな… この辺りはハードウェア担当者から指示があるかもしれませんが、私個人の趣味としては前者を選びます。 いずれにせよ「制御の決め!」は結果次第です。結果が駄目なら制御方法を変更しなければなりません。 しかし…まぁ今回は特別かもしれません。 輝度の切り替えにハードウェアPWM制御を利用し、ソフトウェアの負荷を減らす方が得策です(これは本音)。 |
▲ |
別ページにある複数LED制御と同様、今回のサンプルプログラムでも ・表示イメージ … スイッチの挙動により更新される情報 ・制御イメージ … ハードウェアに依存したダイナミック制御用の情報 の2つのイメージを取り扱います。 今回のサンプルプログラムではタイマ割り込みを利用してダイナミック制御を行いますが、タイマ割り込み処理の中で制御イメージを生成するのではなく、メイン処理で制御イメージを生成し、タイマ割り込み処理では制御イメージを単純に参照&反映する方式を採っています。 |
制御イメージは全部で4要素。個々の要素にはアノード用、カソード用の情報が格納されます。 この制御イメージはメイン処理と割り込み処理で共用されるので、メイン処理で制御イメージを更新する際は局所的に割り込み禁止にします。組み合わせで意味を持つ要素を単位として排他的に制御することは必須です。 イメージ変換(表示イメージから制御イメージの生成)はスイッチによる事象が発生した時のみ行われます。 イメージ変換をタイマ割り込み処理の中で行った場合。行っても問題はありませんが、ダイナミック制御の制御周期に比べ、スイッチ事象発生周期が非常に長い為、結果として処理効率が悪くなるのでるのでイメージ変換をメイン処理で行っています。 この方式は処理効率が良いのですが、制御イメージ用のメモリが必要になるのが難点です。が、今回のケースでは情報量が少ないのでベターな方法と考えます。 |
▲ |
サンプルプログラムをフローチャートを交えて簡単に解説します。. サンプルプログラムは大きく分けてメイン処理と割り込み処理に分かれます。 |
処理 | 概要 | |
メイン処理 | スイッチの認識とスイッチ事象に応じた表示イメージ、制御イメージの更新。 | |
割り込み処理 | 制御イメージに基づいたダイナミック制御。時間管理。 |
今回のサンプルプログラムはメイン処理の負荷が小さく、表示桁数も2桁だけです。メイン処理が1msで巡回できるのであれば割り込み処理の中身をメイン処理の中に埋め込むことができますが、メイン処理の負荷を気にせず安定したダイナミック制御を行うため、割り込み処理を導入しています。 各処理の流れを概略フローにしてみました。 |
これまでのサンプルプログラムを見慣れていれば解析は難しくないと思います(私の癖が出ている?)。 メイン処理では初期設定を行い、タイマ割り込みを起動して無限巡回に入ります。 今回のサンプルプログラムではスイッチのチャタリング除去を行うため、タイマ割り込みで作成される10msを基準に動作します。スイッチは汎用ライブラリを利用することで簡単に事象を取得することができ、その事象に基づいて表示イメージを更新(厳密には制御イメージも同時)します。 割り込み処理はタイマ割り込みだけを対象にします。が、作法としては割り込み要因を確認した方が無難です。今回のサンプルプログラムでは1ms周期のタイマ割り込みを確認してから制御を行います。 これまでのサンプルプログラムと同様、準備されたデータを出力し、その後で制御バッファの内容に基づき、次に出力するデータを準備します。ちょうど1周期遅れて出力内容が更新されますが…問題はないはずです。 割り込み処理が妙な構成になっていますが、気がつきましたか? 複数の割り込みに対応した構成です。が、それだけでなく、割り込み処理の中でも処理の優先度を分けています。 時間的にシビアな処理、安定動作を求める処理を冒頭に配置し、条件により変動する処理を後半に配置しています。 今回の規模であれば厳しく時間を意識する必要はありませんが、一つの方法として紹介しました。 |
▲ |
今回は2桁の制御でしたが、実運用では4桁以上の桁数を取り扱う場合が多いと思います。 (今回のサンプルプログラムの低輝度モードが4桁制御に相当します) 多くの桁数を扱う場合の注意点をいくつか列挙したいと思います。 ちらつく。 桁数を増やすとダイナミック制御の巡回周期が伸びて「ちらつき」が気になります。この場合は巡回周期を短くすることで対応します。なお、巡回周期を短くすると単位時間当たりのダイナミック制御の割合が多くなり、他の処理時間が圧迫されてしまうので注意してください。静止状態で使うのであれば、それほど気にすることはないですが、念のため。 出力ポートが足りない。 結果的な話です。出力ポートの多いマイコンを選択するか、シフトレジスタデバイスを利用します。 輝度が低下する。 巡回周期が伸びるので桁数が少ない時に比べると輝度が低下します(単位時間当たりの点灯時間の減少)。 この場合は高輝度タイプのLEDを選択するか、LEDに流す電流量を増やすことで対応できる場合があります。また、ダイナミック制御に限り定格値を超えて電流を流すことのできるLEDもあります。いずれの場合もデータシートの確認が必要です。 大電流に伴いプログラムを停止できない。 プログラムを停止するとスタティック制御扱いになります。LEDの定格を超えて電流を流すシステムの場合はLEDの破損に注意が必要です。 大電流に出力ポートが対応できない。 シフトレジスタデバイスや高性能なマイコンを利用した場合に注意する点です。デバイス、マイコンのデータシートに記載されていますが、出力ポートに流せる電流量に制限があります。ダイナミック制御の様なコモン端子(共通端子)を利用する場合、複数の電流が合流するので、時としてデバイス、マイコンの出力ポートに流せる電流量の定格を超えてしまう場合があります。この場合は出力ポートにトランジスタ等の電流を増幅する手段を適用する必要があります。 ダイナミック制御はハードウェアとファームウェアが密接に絡んできます。時としてLED前面に置くフィルタの有無も関わってくるのでスタティック制御に比べると悩みどころが多いかもしれません。 可能であればファンクションジェネレータ等を利用し、使用するLEDを任意の時間で高速点滅、すなわち疑似的にダイナミック制御して輝度と点灯イメージを確認してから設計を進めると良い結果が得られます。これはダイナミック制御だけでなくスタティック制御でも大事な実験です。 |
▲ |