2012年8月29日水曜日

リアルな操作画面のキャプチャが欲しい! ~ドキュメント制作時に便利なLCD画像生成ツールを作ってみた~

リアルな操作画面のキャプチャが欲しい!

ドキュメント制作時、意外に面倒なのが操作画面のキャプチャです。
たかがドキュメント、されどドキュメント。

意外に適当な文書作成ツールで作ったドキュメントが多いのが実状ではないでしょうか?
テキストLCDの画面が情けない通常のフォントを使ったものになっている事も少なくありません。

今回はドキュメント制作時に便利なLCD画像生成ツールを作ってみました。
まずは、その出力をご覧ください。



上記は8文字x2行のLCDを模擬した出力です。
バックライトは青色ですね。

少し実物と見比べてみましょう。


上記の画面を仮に説明するドキュメントを作る場合、以下のような出力を得る事になります。


適当なフォントでドキュメントを制作するよりも当然ながらリアリティがあります。
少し「ちゃんとドキュメントも書こうかな?」なんていう気持ちにもなります。

実行方法

出力BMPファイル名、1行目の表示、2行目の表示を与えて実行させるようにしました。
シェルから実行させればまとめて出力を得る事ができます。

外字も組み込んでおけば外字込みでの出力を得る事も可能。
bashを使えばコマンドラインから16進数を与える事ができます。

#!/bin/bash
LCDTOOL=./lcdtool
$LCDTOOL page001.bmp $'\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F' "TEST"
$LCDTOOL page002.bmp "BlueTank" " BF592  "
$LCDTOOL page003.bmp "Effect:0" "Volume:0"
$LCDTOOL page004.bmp "MT:  OK " "InitDone"

ソースコードのダウンロード

こちらからダウンロードして適当にビルドして使用して下さい。
ライセンスはMITです。


2012/09/01追記
改良したバージョンがhttp://shinta-main-jp.blogspot.jp/2012/09/lcdlcd-tool.htmlにあります。

2012年8月17日金曜日

オーディオ・プラットフォームBlueTankの始まりとUZUMEオーディオ・フレームワーク

まえがき

先日からBlueTankだとか何とか書いていて、経緯を知らない人にとっては「何を言っているの?」という感じです。この記事では、オーディオ・プラットフォームBlueTankの始まりとUZUMEオーディオ・フレームワークについて触れたいと思います。

前々からオーディオ装置の開発をやりたいなぁと考えているわけですが、それっぽい入口を仕立てる度にシェルを作るのに忙しくなったり、デバッグ用ツールを作るのに忙しくなったりと何をやっているんだという状態です。

そうは言えども、徐々に何をどう進めようかという指針が明確になってきました。

まずは下回りを固めようという事でオーディオを簡単に扱うためのフレームワークを構築しようと考えるわけですが、実はDSP空挺団をホストされている酔漢さんが既に完成度の高いフレームワークを構築されています。

ということで、後はお手頃なハードウェア・プラットフォームを構築して、その上でフレームワークを使って楽しめば良いじゃないかとなるわけなのです。

ハードウェア・プラットフォーム

以前にBlackTankを設計した時に面倒な思いをして、「これを続けるのは大変だなぁ」と考えていました。
もっと手軽なプラットフォームで開発したいと考えたわけです。

そこに現れたのが金子システム社のDSP基板です。
お値段とサイズがコンパクトなのでほぼリスクなし。


こいつを載せるベース基板を作りさえすればスタートできるなぁと考えていました。
で、作ってみたのが以下の写真・・・。(え?)


基板マニアの私としては、この手作り感がどうしても許せません。
そして、複数個作れと言われても作る気になれません。
要するに、手作業でユニバーサル基板に配線する事に手軽さを感じませんでした。

結局のところ簡単なベース基板を設計する事にしました。
それがBlueTankとなったのです。
とはいえ、DSP基板上にほとんど必要な部品が搭載されているため、ベース基板の回路はスカスカです。


BlueTankで実現するかどうかわかりませんが、装置モノとして筺体設計も考えています。
BlueTankの基板はTAKACHIのLCS135-Hに収まるように設計してあります。


UZUMEオーディオ・フレームワーク

UZUMEオーディオ・フレームワークは、オーディオ処理に必要なリアルタイム制御部分を遮蔽し、オーディオの処理のみに注力できるように考えられているフレームワークです。


フレームワークでは、予め想定された用途の範囲でAPIが整備されています。

一部はBlueTankに適用できないので、対応はこれから考えますが、UZUMEオーディオ・フレームワークの比較的小さなプラットフォームの部類にBlueTankを該当させたい考えです。

「コンパクトなハードウェアで各種オーディオ処理の実験が可能!」というのがBlueTankの位置付けでしょうか。

プロジェクト・ページ


オーディオ・プラットフォームBlueTankにも、UZUMEオーディオ・フレームワークにも、プロジェクト・ページが存在します。興味のある人は訪れてみて下さい。


有機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]です。


2012年8月15日水曜日

PicoBlazeでピコピコしよう (Xilinx社のデバイスが搭載されたFPGAボードをジャンク箱で眠らせている人にもお勧め)

PicoBlazeってなぁに?

PicoBlazeとは、Xilinxが提供しているソフト・マクロで構成されたマイクロ・コントローラです。
最大4K命令の実行が可能で、命令長は18ビット固定、1命令あたり2クロック・サイクルで処理されます。

PicoBlazeは、非常にシンプルな8ビット・プロセッサです。
I/Oは自分で構成する事ができ、入出力それぞれ独立した8ビットのポート番号を持ちます。
割り込みなんて1系統しかありません。

最大クロック周波数は、デバイスとデザイン(配置配線、タイム・スペック)に依存するものの、Spartan-6(-2)で105MHz、Spartan-6(-3)で138MHz、Vertex-6(-3)で240MHzが得られます。

とはいえ、パフォーマンスはさほど重要なポイントではありません。
というか、パフォーマンス目当てでこんな物を使ってはいけません。


FPGAとプロセッサを並行して使う事で、論理記述言語のみで処理する場合に比べて、格段に設計が楽になったりします。(当然ですが、全てのケースでそうだなんて言いません。そういう場合もあるというだけです。)

プロセッサの場合、シーケンシャルな処理を記述するのは簡単で、単に処理を上から順に書くだけで済みます。(実行はFPGAに比べて格段に遅いけどね。)
FPGAでシーケンシャルな処理を構成する場合、ステートマシンを設計したり、ジェネリックで色々なパラメータを上位から渡せるようにした後でそれらを上位でパイプラインを構成して組み合わせたりと、何だかんだを手作業でやる事になるのが通例です。

「FPGAで構成したクロック単位でキビキビ動作する回路と、PicoBlaze上で動作するゆるーいソフトウェアを組み合わせる事で、従来にないメリットが得る」という使い方が、PicoBlazeの美味しいところです。

PicoBlazeはEDKなど有償ライセンスなしで開発できるのも嬉しいところ。
自宅のプチ・プロジェクトでも安心して使えるのがPicoBlazeなのです。

PicoBlazeのリソースをダウンロードする

PicoBlazeのリソースは、Xilinx社のウェブからダウンロードする事ができます。
ダウンロードにはサイン・インが必要です。
http://www.xilinx.com/ipcenter/processor_central/picoblaze/member/index.htm

初めにダウンロードする場合、登録が必要になっています。


ターゲットデバイスを選択して、住所などを入力します。


ライセンス情報が表示されます。


登録が完了するとラウンジに入室です。


ここからお目当てのデバイスのリソースをダウンロードします。
既に7シリーズ用のリソースもあります。


今回はLX-9 MicroBoardで使用するので、KCPSM6をダウンロードしました。
Release4の中身は以下のようになっていました。


どんな風に開発するの?

PicoBlazeの開発フローを簡単に説明します。
  1. PicoBlaze向けのソフトウェアをアセンブラで記述する。
  2. 記述したソフトウェアをアセンブルして、プログラムROM用のモジュールを得る。
  3. プログラムROM用のモジュールとPicoBlazeソフト・コアをプロジェクトに追加する。
  4. シンセサイズとインプリメンテーションを実行する。
まぁ、ざっくり言ってしまうと、FPGAの開発フローの前段に余計な物がくっついたという感じです。
PicoBlazeプロセッサ向けに追加されたフローは、アセンブラの記述とアセンブルの実行です。

このプロセスで得られるのは、PicoBlazeソフト・コア本体と、自分で記述したソフトウェアのインストラクションが格納されたモジュールです。後は、ROMとソフト・コア本体、そして自分が欲しい周辺回路を論理記述言語で接続して使用するだけです。

命令格納モジュール(下図で言うyour_program)とPicoBlaze(下図で言うkcpsm6)の関係は以下です。プロセッサ・コアからアドレスを与えられた命令格納モジュールが、命令をプロセッサ・コアに引き渡す構成です。


使用イメージを簡単に掴む為に、実際にインプリメンテーションを通した後の様子を回路図で見てみましょう。


上記を見ておわかりのように、ユーザ回路の一部にインストラクション格納モジュールとPicoBlaze本体が挟まっただけの構成です。

本当に適当な例(さっきの回路)

ここで、本当に適当な例(さっきの回路)を挙げてみましょう。
トップはsw1の状態を入力ポートのビット0に渡し、出力ポートのビット3から0をport_idが0の時にラッチしてLEDを点灯させているだけの回路です。

入力ポートをどのように使用するのか、出力ポートをどのように使用するのか、についてはプロセッサ上で動作するプログラムに任されています。

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

entity main is
    Port (
             led1 : out std_logic;
             led2 : out std_logic;
             led3 : out std_logic;
             led4 : 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 led_toggle
    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_rom: led_toggle
    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;

   led1 <= port000(0);
   led2 <= port000(1);
   led3 <= port000(2);
   led4 <= port000(3);

end RTL;

次にプロセッサ上のプログラムです。

レジスタは16レジスタが2バンクある構成になっています。



デフォルト・バンクは'A'です。
これらのレジスタは、ユーザが自由に使用する事ができます。
単にs0とかs1とかだと分かりにくいのですが、NAMEREGを使う事で名前でレジスタにアクセスする事ができます。


        NAMEREG s0, TEMP
        NAMEREG s1, DLYCNT1
        NAMEREG s2, DLYCNT2
        NAMEREG s3, PVAL000
        CONSTANT PORT000, 00
INIT:
        LOAD PVAL000, 00
LOOP:
        CALL DELAY
        INPUT TEMP, PORT000
        COMPARE TEMP, 01
        JUMP Z, LOOP
LOOP1:
        CALL DELAY
        INPUT TEMP, PORT000
        COMPARE TEMP, 00
        JUMP Z, LOOP1
LOOP2:
        ADD PVAL000, 01
        OUTPUT PVAL000, PORT000
        JUMP LOOP
DELAY:
        LOAD DLYCNT1, FF
DELAY1:
        LOAD DLYCNT2, FF
DELAY2:
        SUB DLYCNT2, 01
        JUMP NZ, DELAY2
        SUB DLYCNT1, 01
        JUMP NZ, DELAY1
        RETURN

上記の例では、スイッチを押すたびにプロセッサ上のレジスタの値をインクリメントし、インクリメントした値を8ビットのポートに出力するようにしました。



今回は非常に単純な例を示したので、「そんなのVHDLだけで良い」と言われかねませんが、プロセッサによる並列処理を介在させる事でハードウェア側の設計を単純にする事ができる例があります。I2CやSPIなど、同じプロトコルでも複数の種類があるような場合や、そもそもステートがあまりに多い場合にも、全体制御をプロセッサにさせる事で、ハードウェア側の設計が楽になる事は間違いありません。(ここでは性能について一切触れませんが。)

また、ソフト・コア・プロセッサが動作するというだけで、少しワクワクした気分になるのは自分だけではないでしょう。無償で得られる開発環境で、チクチクピコピコPicoBlazeいじりなんていうのも、たまには面白いのではないでしょうか。

2012年8月9日木曜日

BlueTank ACB-BF592 (ACB-BF592とUMB-SSM2603のベース基板)

「ACB-BF592とUMB-SSM2603のベース基板を設計する」とか「外装から考えるDSPオーディオプラットフォーム - BlueTank ACB-BF592」で検討した金子システム社製DSP基板を使ったオーディオ実験用プラットフォームBlueTankの進捗状況です。

金子システムさんのコンパクトで使いやすいDIP型モジュールのお陰で、殆ど何も無いベース基板を作ればDSPを使ったオーディオ実験ができるようになりました。

組み立てた様子を示したのが以下の写真です。
転がっているSDカードと比較して頂くと、そのコンパクトな感じが伝わるのではないでしょうか。


USBのバスパワーで動作させる設計なので、これ1台を持てば色々な実験ができます。
単に2つのモジュールを接続して「使えます」と言うだけでは面白くないので、空きピンを使ってSDカードやLED内蔵ロータリーエンコーダーや赤外線受光素子なども搭載してあります。


SDカードから読み込んだ波形データを使ってオーディオ信号を発生させるアプリケーションを赤外線リモコンを使って操作するとか、そんな物をやってみると面白いかもと考えています。

ちなみに、「BlueTank ACB-BF592の印象に残るロゴを考える」でロゴまで考え、お洒落な?アイコンも基板に記されているわけですが、名称に反してどこも全然青くありません・・・。


あえて言うと、液晶とDSP基板が青い。
ちなみに、この名称はBlackTank LPC1769から始まりました。

2012年8月1日水曜日

パッケージングの事とかその思考過程とか

今日は少し感じを変えて、パッケージングの事とかその思考過程について触れてみようと思う。
今回の投稿は坂井さんの「技術系の個人活動ってこういうのが大切なんじゃないかなあとぼくが勝手におもっている4つのこと」にかなり影響を受けている。

電気式炊飯器

僕自身、昔は暇さえあれば電気屋に足を運んで色々な製品やパッケージを舐めるように眺めていたのだけど、そういえばそんな時に自分は何を見ていたんだ?と改めて考えてみると、機能とかそれそのものがどうとかそういう事ではなくて、単に目の前にあるものの質感だったり、違和感がある箇所が無いかとか、そういうことを探していたように思う。

本来どうあるべきか?とかそんな難しい事は考えないで、「これは気持ち良い」とか「この箇所のこういうところが好き」とかそんな分類を自分の中で繰り返し行なっていた。
だから電気屋に行くと、だいたいいつも同じようなコースを辿って2時間くらいすると出てくるっていうパターンだった。

まだガスでお米を炊く装置なんかもあった時代の電気式炊飯器とかは、結構「マイコン使ってます」って感じのインターフェースで子供心に少しワクワクした。その頃は7セグメントLEDなんかが付いているだけでも「すっげー!」って感じたんだよ。粗大ごみの日なんて、炊飯器が落ちてたら拾って分解してたもんね。もう完全に制御基板とかお宝状態だったもん。もちろんその頃は「どうやったら7セグメントLEDって光るんだろう」とかそんな小学生だったけど。


ワクワクするようなアイデア

今、これだけ多くのテクノロジが急速に広まっていく中で、どうやったら昔味わったようなワクワクに出会えるかというと、これが少し難しくて中々そういう感覚に陥る事ができない。
もう沢山の人が優れたアイデアを試してしまっているし、そういったアイデアはもう十分に世の中に広まって「あたかも当たり前」になっている。

じゃあ、そんな中で何もする事がないかというとそうでもなくて、世の中で優れたアイデアを生み出してきた人達の思考過程を探索したり、自分ならどうなっちゃうんだろう?と試してみるという手もある。

CuBeatSystemsというのは、現在のところ会社でもないし、何でもないのだけど、最近は「普通の人が明かそうとしない思考過程を共有するのが仕事なんじゃないか?」と考えるようになっている。
思考過程を明かしてしまうと、それは手品師の仕込みをオープンにしているのと一緒で、思考過程の結果得られる成果物の相対的価値を下げる可能性があるし、そういう事を普通はみんな望まない。

どうしてあえてそんな事をやっているのか?と聞かれると微妙で、そうしたくてそうしているわけではないように思う。言い回しが難しいけど、あえて言うなら、「世の中は持ちつ持たれつで成り立っている」ということかもしれない。

ただ単に誰かのアイデアを持ってきて何かをしている人というのは、僕は個人的にあまり関心しない。「この人は凄いなぁ」という人は、必ずその人でなければ出てこないようなアイデアを持っていて、それが世の中の人に新しいアイデアや刺激をもたらすからこそ魅力的に見える。それらは真似してできるようなことではないし、一見簡単そうでも自分なら着想しない可能性が高い。

そして、そういう凄いなぁと感じさせる人達は、往々にして他人に対してとても大らかだったり、敷居が低かったりして、それがまた彼らを魅力的に見せる。僕は時折こういった「技術的な事と一見関係のない物事」にとても興味を持つのだけど、近年出会った沢山の優れたエンジニアに共通するのは、何かそういう持ちつ持たれつの感覚を持っている人という事かもしれない。ここでは具体的に誰だとかどのブログだとかそういう事は書かないけれど、彼らのブログには多くの示唆に富んでいて、純粋に見て、読んで、眺めて楽しい。


パッケージングとクリエイティビィティ

話は主題の(そしてまだ何も書いていない)パッケージの事に戻るのだけど、パッケージングというのは単に製品を良く見せたいとかっていう事では無くて、「僕はこう考えました」とか「私はこう考えています」とか、そういう事を表現する一番身近な存在なんだと考えるようになった。

実際に、最近のモノ作りはコストコストコストでこうやって繰り返して書くとコストコ、コストコみたいになってしまうけど、そんな風にして「自分達は一体何をしたいんだっけ?」っていう状態になっている。
確かに「良い物を他より安く作る」事はそれその物を売るために必要な要素かもしれないけど、必ずしもそうでなくてはいけないというわけではない。

明らかに他よりもべらぼうに高い製品だって、世の中には沢山ある。
で、そのメーカが売れずに潰れているかというとそんな事はない。
きちんと顧客を持っていて、常にその顧客がメーカの製品を支持している。

ざっくり言うと、安くないといけない場合というのは、他の物と何も変わらない時に限られる。
そういう物は、先に書いた「その人でなければ出てこないようなアイデア」がその物にない事が多く、必然的に他と変わらないから安い方が良いという事になる。

クリエイティブな人達が本当にやりたいのは、安く作るでもなく、安く売るでもなく、沢山売るでもなく、本当は自分が納得する物を作るという事だと思う。
それは度が過ぎれば全く続かない事は過去の歴史が証明しているし、失敗した例は山ほどあるけど、それでもその歴史に少しだけ抵抗して理想的な状態を考えてみる。
そんな少しの抵抗が製品が市場に受け入れられるかそうでないかの境目ではないかと考えているところだ。

パッケージングで何かを表現するという意図は無いにせよ、それを手に取った人のニヤリとする顔を考えてみるとか、誰かが「おぉ!」と思うような瞬間を考えてみるとか、そういう普段と違う頭の使い方をする事こそが、もしかしたらモノ作りには必要な事かもしれない。

CuBeatSystemsは「第14回 組込みシステム技術に関するサマーワークショップ SWEST14: 14th Summer Workshop on Embedded System Technologies」に参加します。

CuBeatSystemsは「第14回 組込みシステム技術に関するサマーワークショップ SWEST14: 14th Summer Workshop on Embedded System Technologies」に参加します。


ポスター発表と同時に開発成果物のデモンストレーションも実施予定です。

  • Natural Tiny Shell (NT-Shell)
  • Natural Tiny Logger (NT-Logger)
  • その他
会場は「下呂温泉 水明館」ということで、気持ちの良い旅になる事は間違いありません。


今回の参加は「小規模組み込みシステムプラットフォーム研究開発温泉プレビュー」の流れとも関係しそうです。組み込みシステムを愛してやまない人達の温泉合宿です。

CuBeatSystemsは、小規模組み込みシステムの開発に関する情報を数多く提供しています。
近年は、ちょっと便利なNatural Tinyシリーズや、KOZOS用ユティリティ(kz_h8write, kz_xmodem)など、開発のサポート用ツールにも力を入れています。

今回は、参加者の皆様とこういった活動成果の一部を共有できればと考えています。