マンガEG7014C活用講座

第1話 CPLDだけで液晶を動かす(理論編)

戻る
→このマンガは左から右へ読み進んでください


エルシー:「ところで、あえて聞いてみるけど、あなたはこの液晶モジュールを動かすにはどうしたらいいと思う?」
僕:「うーん、データを1個送るごとにXSCLにパルスを入れて、1ライン毎にLP、1画面毎にYDにパルスを入れればよさそうだな、ってのはなんとなくわかるんだけど、具体的にどうしたらいいのかよくわかんないんだよ。さらにCPUやRAMも付けなきゃいけないと思うと、もうこんがらがっちゃって。」
エルシー:「最初はCPLDだけ使って動かしてみましょ。はじめからRAMやCPUまで考えると話がややこしくなるから。」
僕:「うーん。でもCPLDにクロックは必要だよね。CPUボードはあったほうがCPUのクロックが使えていいんじゃない?」
エルシー:「それだと、CPUのクロックが変わるとまたCPLDの設計をやり直しね。やっぱり自前のクロックを持ったほうがいいと思うの。あらっ! こんなところに16MHzのクロックモジュールが転がってるわ。これ使いましょ。」
僕:「な、なんかわざとらしい展開だけど、まぁいいや。じゃそれ使おう。」
エルシー:「で、XSCLはどのくらいの周波数にしたらいいと思う?」
僕:「えーっと、マニュアルで規定されてるのはLPの周期だけだよね。こっからどうやってXSCLの周波数を決めればいいんだろう?」
エルシー:「この液晶は1ライン640ドットよね。データ線は8本だから、1ラインあたりのXSCLの個数は決まるでしょ。そこから求められるわ。」
僕:「640÷8=80個か。LP周期が50μだとするとXSCLの周期は約625ナノ秒、120μ秒だとすると約1500ナノ秒だね。周波数はその逆数だから、0.66MHzから1.6MHzってことか。」
エルシー:「そう。せっかくだから、切りのいい1MHzにしましょ。」
僕:「うん。じゃあ、もとのクロックを16分周すればXSCLになるんだね。」
エルシー:「う〜ん、16分周はいいんだけど、ただ単純に16分周すればいいわけじゃないのよ。」
僕:「どうして?」
エルシー:「タイムチャートをよく見て。LPが出ているときはXSCLは出てないでしょ。」
僕:「そうか。でも、XSCLは80回出さなきゃいけないんだよね? その他にもLPを出すとなると・・・」
エルシー:「1ラインのクロックは80個と考えるより、XSCL80個とLP1個、合計81個と考えたほうが良さそうね。」
僕:「まず元のクロックを16で割って、それをさらに81で割ったのが1ライン分の周波数ってことだね。」
エルシー:「そして縦方向は200ラインあるから、結局液晶の基本タイミングを作るカウンタは前段、X位置、Y位置、の3つが必要ね。前段カウンタをdiv、X位置をx、Y位置をyって信号名にしてカウンタ部分のVHDL書いてみて。」
僕:「信号の定義がこうで」
signal div : std_logic_vector(3 downto 0);
signal x : std_logic_vector(6 downto 0);
signal y : std_logic_vector(7 downto 0);

僕:「カウンタの記述はこうかな?」
process(clk)
begin
  if(clk'event and clk='1') then
    if(div="1111") then			-- divは16分周して1マイクロ秒をつくる
      if(x="1010000") then		-- 1ライン81μ秒
        if(y="11000111") then		-- yが199なら
          y <= "00000000";		--   0にする
        else				-- そうでなければ
          y <= y + 1;			--   yに1を足す
        end if;
        x <= "0000000";			-- xが80ならxを0にする
      else				-- そうでなければ
        x <= x + 1;			--   xに1を足す
      end if;
    end if;
    div <= div + 1;
  end if;
end process;

エルシー:「そうね。じゃ今度はXSCL、LP、YDを出してみましょ。」
僕:「LPってデータ80個送った後に出すの?」
エルシー:「タイムチャート見るとそう考えてもよさそうね。ついでにYDは最初のラインのLPを出すときに出せばいいみたい。」
僕:「するってぇと、こんな感じかな?」
process(clk)
begin
  if(clk'event and clk='1') then
    if(div="0111") then			-- divが8〜15のとき
      if(x="1010000") then		-- xが80ならLPを1にする
        lp <= '1';
      else				-- それ以外ならXSCLを1にする
        xscl <= '1';
      end if;
    elsif(div="1111") then		-- divが0〜7のとき
      xscl <= '0';			-- XSCLもLPも0にする
      lp <= '0';
    end if;
  end if;
end process;

process(clk)
begin
  if(clk'event and clk='1') then
    if((x="1010000")and(y="00000000")) then	-- xが80でyが0のとき
      if(div="0000") then			-- divが1〜15の間
        yd <='1';				-- YDを1にする
      elsif(div="1111") then
        yd <='0';
      end if;
    end if;
  end if;
end process;

エルシー:「ちょっと待って。LPとYDが同時に0に落ちてるわね。データシートよく見て。LPの立下りに対してYDのホールドタイムが必要なはずよ。」
僕:「YDを引き伸ばせばいいのかな?」
エルシー:「それよりXSCLとLPを前にずらしたほうがいいわ。混乱するから今は説明しないけど、あとでRAMやCPUをつなぐことを考えるとその方がいいの。」
僕:「ふーん、そうなのか。タイムチャート見ると、LPはYDの真ん中あたりにきているね。それを真似すればいいのかな。というわけで、LPとXSCLのタイミングを直してみたよ。」
process(clk)
begin
  if(clk'event and clk='1') then
    if(div="0011") then			-- divが4〜11のとき
      if(x="1010000") then		-- xが80ならLPを1にする
        lp <= '1';
      else
        xscl <= '1';			-- それ以外ならXSCLを1にする
      end if;
    elsif(div="1011") then		-- divが0〜3、12〜15のとき
      xscl <= '0';			-- XSCLもLPも0にする
      lp <= '0';
    end if;
  end if;
end process;

エルシー:「そうそう。じゃ、今度はデータを出して、パターンを表示しましょ。8×8ドットのパターンなら、yの下位3ビットをデコードしてXDに適当なデータを出せばいいから簡単ね。」


僕:「データっていつ出せばいいの?」
エルシー:「基本的にはXSCLの立下りでとり込まれるのね。RAMをつなぐときにもう一度しっかりタイミングを検討するけど、今はとりあえずXSCLの立ちあがりと同時に出せば、余裕で間に合うわね。」
僕:「こうかな?」
process(clk)
begin
  if(clk'event and clk='1') then
    if(div="0011") then			-- XSCLの立ちあがりと同じタイミング
      case y(2 downto 0) is
        when "000" => xd <= "00000000";	-- yの下位3ビットが0のときのパターン
        when "001" => xd <= "00000001";	--     〃    1のときのパターン
        when "010" => xd <= "00000011";	--     〃    2のときのパターン
        when "011" => xd <= "00000111";	--     〃    3のときのパターン
        when "100" => xd <= "00001111";	--     〃    4のときのパターン
        when "101" => xd <= "00011111";	--     〃    5のときのパターン
        when "110" => xd <= "00111111";	--     〃    6のときのパターン
        when "111" => xd <= "01111111";	--     〃    7のときのパターン
        when others => xd <= "00000000";-- これを書かないとコンパイラが通らない
      end case;
    end if;
  end if;
end process;

エルシー:「そう、それでいいの。以外と簡単でしょ。」
僕:「本当にこれで動くのかな?」
エルシー:「大丈夫よ。おっと、doff信号を忘れちゃいけないわね。とりあえず、doffiという入力をそのままdoffoという出力に出しましょ。

doffo <= doffi;


こんな感じで。まとめたVHDLはここね。」


続く。