第12回: 実習(3): FPGAへの論理回路の実装(3)

今回の演習は、最後のまとめとして、 10進4桁のカウンタの出力を、4桁の7セグメントLEDに表示させてみましょう。

ダイナミック表示と駆動回路



7セグメントLEDは、数字を構成する「8」の字の7個のLEDと 右下の小数点用のLEDから成っています。 各LED(セグメント)には、↑のようにSG0からSG7の番号がついていて、 それぞれの名前の出力としてFPGAにつながっています。 ただし負論理なので、例えば出力SG0='0'とすると、 一番上のLED(セグメント)が点灯することになります。 例えば「1」の字を表示するためには、SG1とSG2のみ'0'、 他を'1'とすればよいことになります。 この調子で、表示したい値(2進数4桁で"0000"(0)〜"1001"(9))に 応じて、SG0〜SG7の値を設定すれば、好きな数字を表示できることになります。


この調子で4桁もいけばよいように思えますが、残念ながら そう簡単にはいきません。 というのも、4個の7セグメントLEDは、SG0〜SG7が 共通につながっています。 つまりSG0='0'とすると、4桁のすべての7セグメントLEDの 上のLED(セグメント)が点灯してしまうことになります。

これでは、好きな4桁の数値を表示できないわけですが、 その代わり、各桁を点灯させるか消灯させるかを決める SA0〜SA3という信号があり、これもFPGAにつながっています。 これは、(負論理なので)'0'になった桁の7セグメントLEDのみ、 SG0〜SG7に対応して各LED(セグメント)が点灯する、という 機能をもっています。

これをうまく使うと、次の4つの状態を順に変えていけば、 1桁ずつ数値を表示できることになります。

  1. SA0のみ'0'、他を'1'とし、一番右の桁に表示させたい数字に対応するSG0〜SG7を与える
  2. SA1のみ'0'、他を'1'とし、右から2番目の桁に表示させたい数字に対応するSG0〜SG7を与える
  3. SA2のみ'0'、他を'1'とし、右から3番目の桁に表示させたい数字に対応するSG0〜SG7を与える
  4. SA3のみ'0'、他を'1'とし、一番左の桁に表示させたい数字に対応するSG0〜SG7を与える
もちろん、この1.〜4.は、それぞれ1桁ずつしか表示されていませんが、 これを「高速」(例えば毎秒100回程度)に切り替えれば、 残像現象によって、人間の目には、すべてが同時に点灯しているように見えます。 このように、1桁ずつの表示を高速に切り替える方法を ダイナミック点灯(駆動) と呼びます。

4桁10進カウンタ(7セグメントLED表示つき)

このダイナミック駆動を行う回路を用いて、4桁の10進数の数字を表示させる 回路を設計してみましょう。 表示させる値は、例として10進数4桁のカウンタの値とし、 1秒(あるいは0.1秒)ごとにカウンタの値を増やし、 ストップウオッチ的な動作にしてみます。

全体構成



全体の構成を↑この図のようにしてみます。 以下に、各要素の入出力と、機能をまとめてきます。

counter1

counter1は、1桁分の10進カウンタとします。 カウントの基準となるクロックCLK、(非同期)リセットRST、 1桁分(4ビット)の出力Q、出力が9→0になるときに '0'→'1'となる桁上げ出力COをもちます。
entity counter1 is
  Port ( CLK : in  STD_LOGIC;
         RST : in  STD_LOGIC;
         Q : out  STD_LOGIC_VECTOR (3 downto 0);
         CO : out  STD_LOGIC);
end counter1;
参考:設計例

counter4

counter4は、counter1を4桁分まとめた、10進4桁のカウンタとします。 1の桁のcounter1の桁上げ出力COを、次の10の桁のcounter1のクロックCLKに 与えることで、09→10→11→・・・というカウント動作を実現できます。 全体のカウントの基準となるクロックCLK、非同期リセットRST、 1の桁〜1000の桁の出力Q0〜Q3(それぞれ4ビット)、 全体の桁上げ出力COをもちます。
entity counter4 is
  Port ( CLK : in  STD_LOGIC;
         RST : in  STD_LOGIC;
         Q0 : out  STD_LOGIC_VECTOR (3 downto 0);
         Q1 : out  STD_LOGIC_VECTOR (3 downto 0);
         Q2 : out  STD_LOGIC_VECTOR (3 downto 0);
         Q3 : out  STD_LOGIC_VECTOR (3 downto 0);
         CO : out  STD_LOGIC);
end counter4;
参考:設計例

dec7seg

dec7segは、与えられた4ビットの数値を数字として表示する用に、 対応する7セグメントLEDのLED(セグメント)につながるSG0〜SG7を 決定する回路です。 表示するべき数値を与えられる入力D(4ビット)と、 7セグメントLEDにつながる出力SG0〜SG7をもちます。
entity dec7seg is
  Port ( D : in  STD_LOGIC_VECTOR (3 downto 0);
         SG : out  STD_LOGIC_VECTOR (7 downto 0));
end dec7seg;

sel4

sel4は、ダイナミック駆動を制御する、この回路の中心的な回路です。 各桁の表示の切り替えクロックCLK、非同期リセットRSTをもち、 各桁に表示するべき4ビットの値Q0〜Q3のうちの1つを選んで出力Qへわたし、 それにあわせて、表示するべき桁のSA0〜SA3のいずれか1つのみ'0'とします。 例えば、まず最初はQ=Q0かつSA0='0'とし、 その次のタイミングではQ=Q1かつSA1='0'とすればよいことになります。
entity sel4 is
  Port ( CLK : in  STD_LOGIC;
         RST : in  STD_LOGIC;
         Q0 : in  STD_LOGIC_VECTOR (3 downto 0);
         Q1 : in  STD_LOGIC_VECTOR (3 downto 0);
         Q2 : in  STD_LOGIC_VECTOR (3 downto 0);
         Q3 : in  STD_LOGIC_VECTOR (3 downto 0);
         Q : out  STD_LOGIC_VECTOR (3 downto 0);
         SA : out  STD_LOGIC_VECTOR (3 downto 0));
end sel4;
※ヒント:これは、1の桁〜1000の桁を表示している状態4つの状態 SA0_ST〜SA3_STをもつステートマシンとして設計できそうです。

div1s

div1sは、1kHzの基準クロックCLKをもとに、カウンタを動かす 1Hzのクロックを作る分周回路です。
entity div1s is
  Port ( CLK : in  STD_LOGIC;
         CLK1S : out  STD_LOGIC;
         RST : in  STD_LOGIC);
end div1s;

div3ms

div3msは、1kHzの基準クロックCLKをもとに、ダイナミック駆動の制御回路 sel4を動かすクロックを作る分周回路です。 ダイナミック駆動の制御クロックは、適当な値でよいのですが、 例として3msごと(333Hz)としてみましょう。
entity div3ms is
  Port ( CLK : in  STD_LOGIC;
         CLK3MS : out  STD_LOGIC;
         RST : in  STD_LOGIC);
end div3ms;
※3msごとに桁が切り替わり、4桁でひとまわりですから、 表示の点滅の周期は3ms×4=12ms、つまり約80Hzということになります。

top_counter4

以上の要素回路を、まとめている全体の回路です。 各要素回路をcomponent宣言し、インスタンス呼び出しして接続するだけでよいでしょう。
entity top_counter4 is
  Port ( CLK : in  STD_LOGIC;
         RST : in  STD_LOGIC;
         SG : out  STD_LOGIC_VECTOR (7 downto 0);
         SA : out  STD_LOGIC_VECTOR (3 downto 0);
         LED0 : out  STD_LOGIC);
end top_counter4;

ピン定義ファイル

top_counter4をつかって論理合成する際に、 EDX-001用のピン定義ファイル(top_counter4.ucf)が必要ですが、それには↓の内容を 使ってください。 LED0には、counter4の桁上げ出力COをつないでおきましょう。
NET "CLK" LOC = "P18";
NET "RST" LOC = "P50";
NET "LED0" LOC = "P65";
NET "SG<7>" LOC = "P29";
NET "SG<6>" LOC = "P30";
NET "SG<5>" LOC = "P31";
NET "SG<4>" LOC = "P40";
NET "SG<3>" LOC = "P41";
NET "SG<2>" LOC = "P42";
NET "SG<1>" LOC = "P43";
NET "SG<0>" LOC = "P44";
NET "SA<3>" LOC = "P46";
NET "SA<2>" LOC = "P47";
NET "SA<1>" LOC = "P48";
NET "SA<0>" LOC = "P49";

レポート

上記の4桁10進カウンタ、あるいはそれに独自の機能を加えたり改造した回路を 設計し、シミュレーションやEDX-001での動作を行う。

レポートの採点基準

補足

他のスイッチやLEDなどを使う際には、以下から使うスイッチ・LEDの分のみを 作った回路の*.ucfにコピーして使ってください。 (vector形式の信号名を使う場合は、適宜NETの名称を修正すること。 例:LED0→LED<0>) ちなみにBUZはブザーで、4kHz程度のクロックを与えると音が鳴ります。 またCLK0は18.432MHz、CLK1は72kHzのクロック信号です。
NET "LED0" LOC = "P65";
NET "LED1" LOC = "P64";
NET "LED2" LOC = "P63";
NET "LED3" LOC = "P62";
NET "LED4" LOC = "P60";
NET "LED5" LOC = "P59";
NET "LED6" LOC = "P58";
NET "LED7" LOC = "P57";
NET "SWA"  LOC = "P50";
NET "SWB"  LOC = "P51";
NET "SWC"  LOC = "P54";
NET "SWD"  LOC = "P56";
NET "BUZ"  LOC = "P96";
NET "CLK0" LOC = "P88";
NET "CLK1" LOC = "P91";

戻る