2012年8月17日金曜日

有機ELディスプレイ搭載スイッチIS-C15ANP4をPicoBlazeで制御しよう

先日からピコピコとPicoBlazeを触っていたわけですが、某所で日本開閉器工業社製の有機ELディスプレイ搭載スイッチIS-C15ANP4をPicoBlazeから制御したいと依頼を受けました。


メーカのウェブからダウンロードできる資料には、初期化時に使用するパラメータが列挙されています。このスイッチのディスプレイ・コントローラには、SOLOMON SYSTECH社のSSD1331が搭載されているようです。初期化コードをスイッチに送ってやるだけでディスプレイ・コントローラの初期化は完了です。後は、データを送ればそのまま絵として表示されます。


さて、今回のPicoBlazeのデザインは、先のLEDをピコピコさせたものをそのまま流用しました。
という事はBit Bangで制御するのね、ヒドイ!


ということで、トップのデザインです。

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity main is
    Port (
             oled_ss : out std_logic;
             oled_res : out std_logic;
             oled_dc  : out std_logic;
             oled_sck : out std_logic;
             oled_sdi : out std_logic;
             clk : in std_logic;
             sw1 : in std_logic);
end main;

architecture RTL of main is

  component kcpsm6
    generic(                 hwbuild : std_logic_vector(7 downto 0) := X"00";
                    interrupt_vector : std_logic_vector(11 downto 0) := X"3FF";
             scratch_pad_memory_size : integer := 64);
    port (                   address : out std_logic_vector(11 downto 0);
                         instruction : in std_logic_vector(17 downto 0);
                         bram_enable : out std_logic;
                             in_port : in std_logic_vector(7 downto 0);
                            out_port : out std_logic_vector(7 downto 0);
                             port_id : out std_logic_vector(7 downto 0);
                        write_strobe : out std_logic;
                      k_write_strobe : out std_logic;
                         read_strobe : out std_logic;
                           interrupt : in std_logic;
                       interrupt_ack : out std_logic;
                               sleep : in std_logic;
                               reset : in std_logic;
                                 clk : in std_logic);
  end component;

  component c15anp4
    generic(             C_FAMILY : string := "S6";
                C_RAM_SIZE_KWORDS : integer := 1;
             C_JTAG_LOADER_ENABLE : integer := 0);
    Port (      address : in std_logic_vector(11 downto 0);
            instruction : out std_logic_vector(17 downto 0);
                 enable : in std_logic;
                    rdl : out std_logic;
                    clk : in std_logic);
  end component;

signal address : std_logic_vector(11 downto 0);
signal instruction : std_logic_vector(17 downto 0);
signal port_id : std_logic_vector(7 downto 0);
signal in_port : std_logic_vector(7 downto 0);
signal out_port : std_logic_vector(7 downto 0);
signal read_strobe : std_logic;
signal write_strobe : std_logic;

signal port000 : std_logic_vector(7 downto 0);

begin

  processor: kcpsm6
    generic map (                 hwbuild => X"00",
                         interrupt_vector => X"3FF",
                  scratch_pad_memory_size => 64)
    port map(
                address => address,
               instruction => instruction,
                   port_id => port_id,
               interrupt => '0',
              write_strobe => write_strobe,
                  out_port => out_port,
               read_strobe => read_strobe,
                   in_port => in_port,
                     sleep => '0',
                     reset => '0',
                       clk => clk);

  program: c15anp4
    generic map(             C_FAMILY => "S6",
                    C_RAM_SIZE_KWORDS => 1,
                 C_JTAG_LOADER_ENABLE => 0)
    port map(      address => address,
               instruction => instruction,
                    enable => '1',
                       clk => clk);

   out_ratch : process(clk) is
   begin
       if clk'event and clk='1' then
           if write_strobe='1' then
               if port_id ="00000000" then
                   port000 <= out_port;
               end if;
           end if;
       end if;
   end process out_ratch;

   in_ratch : process(clk) is
   begin
       if clk'event and clk='1' then
           if read_strobe='1' then
               if port_id ="00000000" then
                   in_port(0) <= not sw1;
               end if;
           end if;
       end if;
   end process in_ratch;

   oled_ss  <= port000(0);
   oled_res <= port000(1);
   oled_dc  <= port000(2);
   oled_sck <= port000(3);
   oled_sdi <= port000(4);

end RTL;

回路はLX-9 MicroBlazeのPMODのピンに超適当に配線です。

NET "oled_ss"  LOC = D18;
NET "oled_res" LOC = D17;
NET "oled_dc"  LOC = G14;
NET "oled_sck" LOC = F14;
NET "oled_sdi" LOC = F15;


さて、後はI/Oをバンバン打つだけです。

          ; =========================================================
          ; Port000[0] : SS- : Slave Select (Low Active)
          ; Port000[1] : RES : Reset        (Low Active)
          ; Port000[2] : D/C : Data/Command (Data=High, Command=Low)
          ; Port000[3] : SCK : Serial Clock (Up Edge Trigger)
          ; Port000[4] : SDI : Serial Data
          ; =========================================================
          ; ---------------------------------------------------------
          ; Register Name
          ; ---------------------------------------------------------

          ; SPI Data Register
          NAMEREG s0, SPIDATA
          ; Port Data Register
          NAMEREG s1, PORTDAT

          ; Delay Counter No.1
          NAMEREG s2, DLYCNT1
          ; Delay Counter No.2
          NAMEREG s3, DLYCNT2
          ; Delay Counter No.3
          NAMEREG s4, DLYCNT3

          ; Send Counter No.1
          NAMEREG s5, SNDCNT1
          ; Send Counter No.2
          NAMEREG s6, SNDCNT2

          ; Temporary Register No.1
          NAMEREG s7, TEMP_I
          ; Temporary Register No.2
          NAMEREG s8, TEMP_J

          ; ---------------------------------------------------------
          ; Constant Variables
          ; ---------------------------------------------------------
          CONSTANT PORT000, 00
          CONSTANT OLED_SS, 01
          CONSTANT OLED_RES, 02
          CONSTANT OLED_DC, 04
          CONSTANT OLED_SCK, 08
          CONSTANT OLED_SDI, 10
MAIN:
          CALL INIT
TESTLOOP:
          CALL INIT_ADDR
          CALL FILLR
          CALL LONGDELAY

          CALL INIT_ADDR
          CALL FILLG
          CALL LONGDELAY

          CALL INIT_ADDR
          CALL FILLB
          CALL LONGDELAY

          CALL INIT_ADDR
          CALL FILLW
          CALL LONGDELAY

          JUMP TESTLOOP
INIT_ADDR:
          ; ---------------------------------------------------------
          ; Setup Column Address
          ; ---------------------------------------------------------
          LOAD SPIDATA, 15
          CALL SEND_CMD
          LOAD SPIDATA, 10
          CALL SEND_CMD
          LOAD SPIDATA, 4F
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Setup Row Address
          ; ---------------------------------------------------------
          LOAD SPIDATA, 75
          CALL SEND_CMD
          LOAD SPIDATA, 00
          CALL SEND_CMD
          LOAD SPIDATA, 2F
          CALL SEND_CMD

          RETURN

FILLR:
          LOAD TEMP_I, 30
FILLR1:
          LOAD TEMP_J, 40
FILLR2:
          ; ---------------------------------------------------------
          ; Byte0 = B[4:0] + G[5:3]
          ; Byte1 = G[2:0] + R[4:0]
          ; ---------------------------------------------------------
          LOAD SPIDATA, 00
          CALL SEND_DAT
          LOAD SPIDATA, 1F
          CALL SEND_DAT
          SUB TEMP_J, 01
          JUMP NZ, FILLR2
          SUB TEMP_I, 01
          JUMP NZ, FILLR1
          RETURN
FILLG:
          LOAD TEMP_I, 30
FILLG1:
          LOAD TEMP_J, 40
FILLG2:
          ; ---------------------------------------------------------
          ; Byte0 = B[4:0] + G[5:3]
          ; Byte1 = G[2:0] + R[4:0]
          ; ---------------------------------------------------------
          LOAD SPIDATA, 07
          CALL SEND_DAT
          LOAD SPIDATA, C0
          CALL SEND_DAT
          SUB TEMP_J, 01
          JUMP NZ, FILLG2
          SUB TEMP_I, 01
          JUMP NZ, FILLG1
          RETURN
FILLB:
          LOAD TEMP_I, 30
FILLB1:
          LOAD TEMP_J, 40
FILLB2:
          ; ---------------------------------------------------------
          ; Byte0 = B[4:0] + G[5:3]
          ; Byte1 = G[2:0] + R[4:0]
          ; ---------------------------------------------------------
          LOAD SPIDATA, F8
          CALL SEND_DAT
          LOAD SPIDATA, 00
          CALL SEND_DAT
          SUB TEMP_J, 01
          JUMP NZ, FILLB2
          SUB TEMP_I, 01
          JUMP NZ, FILLB1
          RETURN
FILLW:
          LOAD TEMP_I, 30
FILLW1:
          LOAD TEMP_J, 40
FILLW2:
          ; ---------------------------------------------------------
          ; Byte0 = B[4:0] + G[5:3]
          ; Byte1 = G[2:0] + R[4:0]
          ; ---------------------------------------------------------
          LOAD SPIDATA, FF
          CALL SEND_DAT
          LOAD SPIDATA, FF
          CALL SEND_DAT
          SUB TEMP_J, 01
          JUMP NZ, FILLW2
          SUB TEMP_I, 01
          JUMP NZ, FILLW1
          RETURN
INIT:
          ; =========================================================
          ; SS- : Slave Select (Low Active)
          ; RES : Reset        (Low Active)
          ; D/C : Data/Command (Data=High, Command=Low)
          ; SCK : Serial Clock (Up Edge Trigger)
          ; SDI : Serial Data
          ; =========================================================

          ; ---------------------------------------------------------
          ; Reset Active
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OUTPUT PORTDAT, PORT000
          CALL DELAY

          ; ---------------------------------------------------------
          ; Reset Inactive
          ; ---------------------------------------------------------
          LOAD PORTDAT, OLED_RES
          OUTPUT PORTDAT, PORT000
          CALL DELAY

          ; ---------------------------------------------------------
          ; Setup Column Address
          ; ---------------------------------------------------------
          LOAD SPIDATA, 15
          CALL SEND_CMD
          LOAD SPIDATA, 10
          CALL SEND_CMD
          LOAD SPIDATA, 4F
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Setup Row Address
          ; ---------------------------------------------------------
          LOAD SPIDATA, 75
          CALL SEND_CMD
          LOAD SPIDATA, 00
          CALL SEND_CMD
          LOAD SPIDATA, 2F
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Setup Contrast Color A
          ; ---------------------------------------------------------
          LOAD SPIDATA, 81
          CALL SEND_CMD
          LOAD SPIDATA, 19
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Setup Contrast Color B
          ; ---------------------------------------------------------
          LOAD SPIDATA, 82
          CALL SEND_CMD
          LOAD SPIDATA, 14
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Setup Contrast Color C
          ; ---------------------------------------------------------
          LOAD SPIDATA, 83
          CALL SEND_CMD
          LOAD SPIDATA, 24
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Master Current Control
          ; ---------------------------------------------------------
          LOAD SPIDATA, 87
          CALL SEND_CMD
          LOAD SPIDATA, 0F
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Display Start Line
          ; ---------------------------------------------------------
          LOAD SPIDATA, A1
          CALL SEND_CMD
          LOAD SPIDATA, 00
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Display Offset
          ; ---------------------------------------------------------
          LOAD SPIDATA, A2
          CALL SEND_CMD
          LOAD SPIDATA, 10
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Display Mode
          ; ---------------------------------------------------------
          LOAD SPIDATA, A4
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Multiplex Ratio
          ; ---------------------------------------------------------
          LOAD SPIDATA, A8
          CALL SEND_CMD
          LOAD SPIDATA, 2F
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Dim Mode Setting
          ; ---------------------------------------------------------
          LOAD SPIDATA, AB
          CALL SEND_CMD
          LOAD SPIDATA, 12
          CALL SEND_CMD
          LOAD SPIDATA, 0C
          CALL SEND_CMD
          LOAD SPIDATA, 14
          CALL SEND_CMD
          LOAD SPIDATA, 12
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Master Configuration
          ; ---------------------------------------------------------
          LOAD SPIDATA, AD
          CALL SEND_CMD
          LOAD SPIDATA, 8E
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Phase Period Adjustment
          ; ---------------------------------------------------------
          LOAD SPIDATA, B1
          CALL SEND_CMD
          LOAD SPIDATA, 44
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Display Clock Divider
          ; ---------------------------------------------------------
          LOAD SPIDATA, B3
          CALL SEND_CMD
          LOAD SPIDATA, A0
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Enable Linear Gray Scale
          ; ---------------------------------------------------------
          LOAD SPIDATA, B9
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Precharge Level
          ; ---------------------------------------------------------
          LOAD SPIDATA, BB
          CALL SEND_CMD
          LOAD SPIDATA, 12
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set VCOMH
          ; ---------------------------------------------------------
          LOAD SPIDATA, BE
          CALL SEND_CMD
          LOAD SPIDATA, 28
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Color Depth = 64K
          ; ---------------------------------------------------------
          LOAD SPIDATA, A0
          CALL SEND_CMD
          LOAD SPIDATA, 70
          CALL SEND_CMD

          ; ---------------------------------------------------------
          ; Set Display Normal
          ; ---------------------------------------------------------
          LOAD SPIDATA, AF
          CALL SEND_CMD

          RETURN
SEND_DAT:
          ; =========================================================
          ; SS- : Slave Select (Low Active)
          ; RES : Reset        (Low Active)
          ; D/C : Data/Command (Data=High, Command=Low)
          ; SCK : Serial Clock (Up Edge Trigger)
          ; SDI : Serial Data
          ; =========================================================
SEND_DAT_SLAVE_ACTIVE:
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
SEND_DAT_SETUP:
          LOAD SNDCNT1, 08
          LOAD SNDCNT2, SPIDATA
SEND_DAT_LOOP:
          SL0 SNDCNT2
          JUMP C, SEND_DAT_H
          JUMP SEND_DAT_L
SEND_DAT_H:
          ; ---------------------------------------------------------
          ; SDI=HIGH, SCK=LOW
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
          OR PORTDAT, OLED_SDI
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; SDI=HIGH, SCK=HIGH
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
          OR PORTDAT, OLED_SCK
          OR PORTDAT, OLED_SDI
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; NEXT
          ; ---------------------------------------------------------
          JUMP SEND_DAT_NEXT
SEND_DAT_L:
          ; ---------------------------------------------------------
          ; SDI=LOW, SCK=LOW
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; SDI=LOW, SCK=HIGH
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
          OR PORTDAT, OLED_SCK
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; NEXT
          ; ---------------------------------------------------------
          JUMP SEND_DAT_NEXT
SEND_DAT_NEXT:
          SUB SNDCNT1, 01
          JUMP NZ, SEND_DAT_LOOP
SEND_DAT_END:
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_DC
          OR PORTDAT, OLED_SS
          OUTPUT PORTDAT, PORT000
          RETURN
SEND_CMD:
          ; =========================================================
          ; SS- : Slave Select (Low Active)
          ; RES : Reset        (Low Active)
          ; D/C : Data/Command (Data=High, Command=Low)
          ; SCK : Serial Clock (Up Edge Trigger)
          ; SDI : Serial Data
          ; =========================================================
SEND_CMD_SLAVE_ACTIVE:
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
SEND_CMD_SETUP:
          LOAD SNDCNT1, 08
          LOAD SNDCNT2, SPIDATA
SEND_CMD_LOOP:
          SL0 SNDCNT2
          JUMP C, SEND_CMD_H
          JUMP SEND_CMD_L
SEND_CMD_H:
          ; ---------------------------------------------------------
          ; SDI=HIGH, SCK=LOW
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_SDI
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; SDI=HIGH, SCK=HIGH
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_SCK
          OR PORTDAT, OLED_SDI
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; NEXT
          ; ---------------------------------------------------------
          JUMP SEND_CMD_NEXT
SEND_CMD_L:
          ; ---------------------------------------------------------
          ; SDI=LOW, SCK=LOW
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; SDI=LOW, SCK=HIGH
          ; ---------------------------------------------------------
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_SCK
          OUTPUT PORTDAT, PORT000
          CALL DELAY
          ; ---------------------------------------------------------
          ; NEXT
          ; ---------------------------------------------------------
          JUMP SEND_CMD_NEXT
SEND_CMD_NEXT:
          SUB SNDCNT1, 01
          JUMP NZ, SEND_CMD_LOOP
SEND_CMD_END:
          LOAD PORTDAT, 00
          OR PORTDAT, OLED_RES
          OR PORTDAT, OLED_SS
          OUTPUT PORTDAT, PORT000
          RETURN
DELAY:
          LOAD DLYCNT1, 01
DELAY1:
          LOAD DLYCNT2, 01
DELAY2:
          SUB DLYCNT2, 01
          JUMP NZ, DELAY2
          SUB DLYCNT1, 01
          JUMP NZ, DELAY1
          RETURN
LONGDELAY:
          LOAD DLYCNT1, 80
LONGDELAY1:
          LOAD DLYCNT2, FF
LONGDELAY2:
          LOAD DLYCNT3, FF
LONGDELAY3:
          SUB DLYCNT3, 01
          JUMP NZ, LONGDELAY3
          SUB DLYCNT2, 01
          JUMP NZ, LONGDELAY2
          SUB DLYCNT1, 01
          JUMP NZ, LONGDELAY1
          RETURN

もう完全にPicoBlazeの使い方としてはおかしいですが、第一段階としては気にしません。
上記のコードで「赤」、「緑」、「青」、「白」と順に表示を繰り返す事ができます。




リソースの使用率ですが、以下のようになりました。


Bit Bangはあんまりなので、SPIモジュールを作ってやってデータを渡すのが良いでしょう。
データを渡して、転送開始入力信号と転送完了出力信号が出てくるようなモジュールです。

フォントなどを内蔵すればもっと手軽に表示が楽しめそう。
上記を雛型にすればこういった拡張も簡単です。

ちなみに、今回はBit Bangの基本動作を外部のロジック・アナライザで確認した後、セットアップとホールド、クロックサイクルを見ながらアセンブラ・コードをチューニングし、波形が期待通りに出力されている事を確認した後で、最後にIS-C15ANP4を接続するという形で作業しました。(ChipScope使えよという感じです。)


PicoBlazeのアセンブラも、最初のバージョンより気が利くようになっていて、一度立ち上げてプログラム・ファイル名を入力しておけば「R」をタイプするだけで再コンパイルできるようになっています。


なので、アセンブルの後で生成されたVHDLををプロジェクトにコピーするバッチファイルを作っておけば、「アセンブル→再インプリメンテーション」の流れをスムーズにこなす事ができます。


こんな感じで(超適当だけど)半田付けを含めて小1時間ほどでPicoBlazeからIS-C15ANP4に絵が出せるようになりました。


念のために書いておくと、IS-C15ANP4には決められた電源シーケンスがあります。
製品に使用する場合、きちんとシーケンスを守るように設計しましょう。


最後にグリーンな表示。

ちなみに、先ほどの初期化コードの場合、2バイトで1ピクセルを表現するようになっています。
BとRが5ビット、Gが6ビットで、1バイト目がB[4:0] + G[5:3]、2バイト目がG[2:0] + R[4:0]です。


0 件のコメント:

コメントを投稿