lynxeyedの電音鍵盤

MBDとFPGAと車載で使うデバイスの備忘録

職を選ぶということと難儀

過去の話をば

技術的なことから少しだけ離れた記事ですが、よく同じ研究室やサークルだった愛すべき(笑)後輩たちから聞かれるのでここで参考がてら私の話をしましょう。

まず
(1) 今何してるの
溶接、車載機器の開発をする会社の画像処理担当 兼 車載電装事業部長

アクセサリ(カーナビ、カーオーディオ)からASR(スリップ抑制)のための画像処理による運転支援。主に後者の開発が部署のメインです。またタイヤ成分の化学分析も行います。
悪環境な場面で活躍する車載電装が得意なことから、風雨にさらされやすい、街路樹などのLED電飾の依頼もあり今年は3箇所での設計をしています。LEDの点灯パターンを人の流量や挙動で変えたりと、どこかに画像処理が入ってたりします。


(2) そこに至るようになった経緯は

  1. 液晶ドライバが当時世界の80%以上のシェアをもっていた某ファブレス
  2. カーナビで有名な某社
  3. よくわからん迷走してたFPGAとかマイコンとかの謎設計屋
  4. 謎代理店

1,2は同時にやっていました。というか2の会社に1の部門がやってきたわけですが、半年スパンで行ったりきたりしてました。
その部門はリーマンショック、震災と続いたのが影響して部門としては解散してしまいましたが、いまでもお仕事を頂いたりします。(この記事を書いてる時点でも1件頂いてます。ありがとうございます)

3. の会社。ここには正直難儀しました。

(i) 当日になるまで何するか決まってない。社長の気まぐれで始まる
(ii) 言ったことを次の瞬間に撤回、やっぱり別のことをやれと言い出す
(iii) しばらくしたら「アレどうなった?」と撤回前の件の進捗を聞く(当然やってない)
(iv) わめきだす。

さて対策を練るわけです。たぶんぼくの仕事の仕方が悪かったのでしょう。次の日も前日のことを忘れて同じパターンで始まります

(i) 当日になるまで何するか決まってない。社長の気まぐれで始まる
(ii) 言ったことを次の瞬間に撤回、やっぱり別のことをやれと言い出す
(iii) 撤回前の件と撤回後の件、そのプロセスに必要な要素を見つけ、どこから最優先にすべきか聞く
(iv) 返事「全部」
(v) 「全部」の真意を聞く
(vi) わめ(ry


コントです。お笑いやりにここに来たつもりはありません。
だいたい、優先度を聞いて「全部」という返事はなんですか、組み込みRTOSだってそんなのディスパッチ処理しないでしょう。
で、わめいたり、嫌味をならべたてて騒ぐわけです。40歳目前のオトナが何をしてるんだ。



こっちも仕事中は録音機を四六時中作動させるようになりました。*1
理由はメモするな、といわれたので思い出せなくなると困るからですが、今録音聞き直したら、院時代の研究内容を聞いてそれをバカにしたりなどしてましたね。技術士の倫理規範を逸脱した行為です。


複数のタスクがある場合はスケジューリングをするのは当然です。


この社長、昼過ぎに出社して、3時過ぎに帰る。
まぁ別にそれは1,2の職場でもあった事のなので別にかまわないでしょう。外部の仕事もあるはずです。仕事と金とってくるのが仕事です。

が、家に帰って夜中に起きて、散々スタッフにメール出して、真夜中に反応なくてイラついたのか知りませんが「明日AM9:30までに仕上げてください」ってそれどうなんですか。


はー。スカイプID教えなくてよかった。夜中にかかってくるぞ(笑


計画性のなさは仕事中にもよく現れました。あるプログラムを共同で作る必要がありました。gitもsvnも使わないとか言い出すわけです。
まぁいいでしょう。開発は2,3人です。diffとって慎重にやればできなくはないでしょう。

とおもったら、目で見て判断してただけだったようです。私の作業全部ぶっつぶれてました。

そんなわけのわからんもの使えるか、とでもいいたかったようですがやってることが訳分からん。
それの繰り返し。いい加減苦言を呈したところ、「技術がないものがいわゆる便利ツールにたよるのは御法度」などとのたまう。

まぁいいでしょう。向こうも僕に言いたいことは山ほどあるでしょう。

さて、とあるとき、AndroidWindows Phone(iOSはJS関連の制約でやめたと記憶)でのサービスを提供するという話になったときがあり、端末側のアプリを担当することになっていました。
当時はAndroid StudioもありませんでしたのでEclipse for JavaやVS、Blenderxnaをインストールするわけです。


その後、別の案件が舞い込んできて2ヶ月ほど離れていましたが、その後そのPCの上記インストール物をみて「会社で遊ばないでください」


何いってんだホント。。。


Androidアプリが完成したときにもうこれ以上やりたくない。元気があるうちにここから去りたい、と心底思っていました。
そのころ、胃に穴があくことが度々あり、医者に20代の今はいい、40代以降になったときの癌の確率が大幅に上がる、いい加減身の振り方を考えろ。と助言をされていました。
また結婚を控えていて、これも少なからぬストレスにはなっていたと思います。

大幅なモチベーションの低下は仕事にも現れるわけで、当然できたことすら出来なくなっていくのを実感しました。
数社から(1,2含む)、「来いよ。待ってる」というオファがあったこともあり、会社都合によるドロップアウトをしました。

そして

で、就職活動を控えている後輩たちと、面接を控えてる皆さん。こんな脅しの文章書いて申し訳ない。
でも入ってみないと分からないのは事実です。面接官だって採用してしばらくたたないとその人が有用かどうか分からないはず。裏返せば、みんなも社内の風潮や面子を知りえることなどできない。
多くの約束をせよ。だがそれを果たすな。これが君主として振舞える条件である ― ナポレオン・ボナパルト (が言ったとされている)

ボーナス出る出るとか、待遇上がるよ、などとうそにならない程度にほのめかすと社員はがんばる、上手に半殺しにしておけ的な内容が「経営の上手な社長になるには」みたいな意識高い()啓蒙本に書いてありますね。なんなんですかねこれ。


ただ、言えることは「まじめ」の定義を曲じまげて皆さんに仕事を強要する組織にいる必要はありません。それこそ不真面目な組織なのです。

必ずその者たちは「常識」という言葉を使ってくるでしょう。なんですかそれは。
「説明できないけど、俺がお前たちにやらせたいことをお前の良心に訴えて無理やりやらせる口実」くらいに思っておいたほうがよいです。


お前は技術がない、お前は常識がない、お前は(ry

まぁ耳にタコができるくらいききましたわ。自分が知らないことを提示される焦りなのか知りませんが…これ、新人を蹴落とす洗礼のようですね。


看過できなくなる前に相談すべきと思う。研究室の教授、先輩、もし親がご健在なら親(もちろん、引退されてる方が多いでしょう。情勢は今と異なるので参考程度に)相談すべきです。
どこかで答えが返ってくるはず。後輩のみなさん、わたしも協力できるならその一助を担おうと思います。


今に至るにはもうワンステップあります、その話はまた別のときに。

*1:当然ですが、今も会議や商談では必ず録音します

PSoC4 Pioneer Kitでtelnet

何か作ろう

PSoC4 Pioneer KitキャンペーンPSoC 4 Pioneer Kitキャンペーン PSoC 4でデザインしよう!使ってみたいデザイン案を投稿して、無償でキットをゲット!の応募に当選しちゃいました。
f:id:Lynx-EyED:20131019230059j:plain
ありがとうございます!!(中野の方角向きながら

Cortex-M0ということで、すこし遊べるでしょう。

UARTの出力がUSBからでてこない

動かなくてうんうん唸ってましたが、マニュアル見ろってはなしでしたw
http://www.cypress.com/?docID=43713のp.61~
f:id:Lynx-EyED:20131019232510p:plain
ジャンパでPSoC5LPと配線してあげる必要があるみたいです。
ArduinoのD0(P0.4),D1(P0.5)以外にも接続可能な箇所があります。

Systickの割り込みってどうやれば

OSやらフレームワークやら移植するのにCortex-MシリーズはSystickから1msecタイマーを作ることが多いですが、PSoC4でどうやれば良いのか分からなかったので調べてみました。
PSoC4 SysTick interrupt-サイプレス
ここによるとVector#15がsystickの割り込みなので、1msecごとに割り込みを発生させてmsTicksをカウントアップするには以下の様にすれば良いはずです。メインルーチンからsetup_systickをコールしてやります。

int msTicks;
void SysTick_Handler(void) {
  msTicks++;                        /* increment counter necessary in Delay() */
}

void setup_systick (void) {

         CyIntSetSysVector(15,SysTick_Handler);

        // 1msecごとに割り込み
        if (SysTick_Config( (システムクロックの値) / 1000)) { 
                while (1);                                  /* Capture error */
        }
        CyGlobalIntEnable; 

}

W5100のEthernetシールドで遊ぶ

Element14のページにEthernet shieldのWebServerのサンプルがあります。
PSoC 4 Pioneer Kit Community Project#031 – Ethernet Shield - element14
さくっと動かそうとしたのですが、動きません。動かなくてうんうん唸ってましたが、写真にあったw
イーサーネットシールドは6ピンのICSP端子からしかSPIは接続されていない模様。
イーサーネットシールドの回路図
なので、6ピンを半田付けします。
f:id:Lynx-EyED:20131019235233p:plain
さて、Element14のサンプルそのままでも使えない事は無いのですが、やたらめったらArduinoライブラリがくっついてくるので収拾がつかなく、他のプロジェクトに転用できない感じです。
ちなみに純正ドライバ↓
Innovative Embedded Networking : Device Server, WiFi, Ethernet | WIZnet Co. Ltd.


Wiznetの純正ドライバを元に、低レイヤーをC、アプリケーションに近いレイヤーをC++で記述しました。
lynxeyed-atsu/W5100_PSoC4_telnet_test · GitHub

他のデバイスに移植したい場合、device_depend.cを該当デバイス用ドライバに書き換えれば動作するはずです。

main.cppのIPアドレスはご自分の環境に書き換えてください。自分の場合は169.254.22.31にしました。

// Network Settigs
uint8 mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xA0, 0x8A };
uint8 ip[] = { 169, 254, 22, 31 };

// use port 23:Telnet
Server server = Server(23);

動作中の図。I2Cポートから何やらジャンパーが生えてますが、PSoC4のUARTです。この箇所にUARTを割り当ててます。
f:id:Lynx-EyED:20131020000823j:plain

■telnetループバック
TeraTermを立ち上げまして、
f:id:Lynx-EyED:20131020002951p:plain

telnetでループバックします。キーボードの入力をディスプレーに出力します
f:id:Lynx-EyED:20131020003222p:plain
はいバッチィです。
おしまい。

◆次回
PSoC4のCY8C4100シリーズはPSoCの特徴でもあったUDBが無く、今回のPioneer Kitに載っている4200も大幅に削減されてコストを抑える思い切ったデバイスになっています。UDBはVerilogでカスタムロジックの開発もできる尖ったデバイスですが、4200ではすぐにロジックが枯渇するでしょう。ここでCypressはシフト、加減算、バイトスワップなど単純なロジックはdatapath、それ以外の複雑なロジックはPLDベースのグルーロジックを使いわけることによってこの問題に対処することを提案しています。
次回はdatapath話題でも。

◆参考
PSoC CreatorでC++を使う方法
http://www.element14.com/community/servlet/JiveServlet/download/79763-114371/C%2B%2B%20In%20Creator.doc

ネットワークインターフェース部品の電子工作(W5100,ENC28J60,etc)
http://www.kako.com/neta/2009-003/2009-003.html

マイコン風雲録: Wiz820io(W5200)のドライバ (バグ?対応)

mbed LPC1768でJVM

RAMを豪快に使うだけのあそび

前回(RunnableインターフェースでマルチスレッドLチカ - Lynx-EyEDの電音鍵盤 新館)の続き。

JVMをmbedに移植しました。らくちん。mbedいいよmbed。
mbed LPC11U24/LPC1768には2MBのフラッシュがついています。classファイルをTest.claという名前にリネームしてmbedにコピー(←ファイル名が8.3形式にしか対応していない模様)すると、これを逐次解釈します。

↓こちらからフォークしてください。対応mbedはLPC11U24,LPC1768です*1
https://mbed.org/users/lynxeyed_atsu/code/FRDM_RaVem_JVM/


mallocとreallocがメモリーを取得できていないにもかかわらず、NULLを返さないコンパイラが一部にあったので、スタック領域に動的メモリを擬似的に確保するメモリプールマネージメントを実装しています。pool.hをご覧下さい。

今回作ったJVMはスレッドをnewするたびにメモリを動的に確保します。したがってメモリが少ないLPC812、LPC11U24、LPC1114だと1スレッド動作が現実的と言えます。

で、超ご無沙汰してるmbed LPC1768。LPC1768のCPUが使用できるRAMは比較的多いので4スレッドスーバーエコジャナイシステムを作りました。

上記のコードを2箇所修正します。
[device_depend.cpp]の5行目を

#define bc_str_length 2048 // from 32 to 1024

に変更(2048にする)。これはバイトコードをフラッシュからRAMにコピーするための領域です。(2048 = 2キロバイト)

[pool.h]の19行目。

#define pool_size           512 // 256 * (sizeof(int)) = 1kByte

に変更する。これはJVMが使うRAMのヒープ領域です*2。int型(=4バイト)アラインメントしてるので512 x 4 = 2キロバイトです。
コンパイルしてmbedに書き込みます。

Javaのコードの作成
(前回)のSoftBlinkLEDTest.javaを修正します。lpc800.javaも前回を参考に用意しておいてください。

public class SoftBlinkLEDTest implements Runnable{
    private int x,y;
    
    public SoftBlinkLEDTest(int bit_num, int sleep_time_ms){
        
        x = bit_num;
        y = sleep_time_ms;
       
    }
    
    public static void main(String[] args){
        
        SoftBlinkLEDTest LED1 = new SoftBlinkLEDTest(0, 90);     // LED1, half-cycle = 90msec
        SoftBlinkLEDTest LED2 = new SoftBlinkLEDTest(1, 100);    // LED2, half-cycle = 100msec
        SoftBlinkLEDTest LED3 = new SoftBlinkLEDTest(2, 500);    // LED3, half-cycle = 500msec
        SoftBlinkLEDTest LED4 = new SoftBlinkLEDTest(3, 1000);    // LED4, half-cycle = 1000msec
        
        
        Thread th1 = new Thread(LED1);
        Thread th2 = new Thread(LED2);
        Thread th3 = new Thread(LED3);
        Thread th4 = new Thread(LED4);
        
        th1.start();
        th2.start();
        th3.start();
        th4.start();
        
    }
    
    public void run(){
        
        int port_bit = x, time = y;
        
        while(true){
            try{
                Thread.sleep(time);
            }catch(Exception e){}
            
            lpc800.portWrite(port_bit, 1);
            //System.out.println("LED_on");
            
            try{
                Thread.sleep(time);
            }catch(Exception e){}
            
            lpc800.portWrite(port_bit, 0);
            //System.out.println("LED_off");
        }
    }
}

javacコンパイル。

$ javac SoftBlinkLEDTest.java

SoftBlinkLEDTest.classをリネーム

$ mv SoftBlinkLEDTest.class Test.cla

Test.claをmbed LPC1768にコピー。
mbedのマスストレージはこんな感じになってるはず。
f:id:Lynx-EyED:20130830001204p:plain
リセットボタンを押してスタート。ロードに2,3秒かかります。
↓こんな感じに動作するはず。

*1:最近mbedに追加された他のデバイス、たとえばLPC1114,KL25Zなどは今までLPC812で行っていた時と同様にバイトコードを配列変換すれば動作するはずです。余力のある方はraven.cpp/device_depend.cppを変更してください

*2:前述した通り、スタック領域に擬似的に形成してます

RunnableインターフェースでマルチスレッドLチカ

LPC800 JVMとRunnableクラスの実装

前回(LPC800にJVM載せてUARTループバック - Lynx-EyEDの電音鍵盤 新館)の続きです。

なお、JVMソースコード一式は以下を参照ください
lynxeyed-atsu/RaVem · GitHub

マルチスレッドで複数のLEDを別々の周期で点滅させようと思います。
そうなると、マイコンのIOポート番号や周期をThreadクラスに引数として持たせたい訳ですが、そうするにはJavaではRunnable実装クラスをインスタンス化したオブジェクト参照を、Threadクラスのコンストラクタ引数として渡す必要があります。

JVMレベルで考えると、オペランドスタックに引数をスタック → newされたオブジェクトのインスタンス初期化メソッドが呼び出された際にローカルレジスタにコピー → Public Field領域に引数を書き込みます。

このJVMの一連の動作をどのようにLPC812に実装するかが課題だったのですが、漸くできました。

とりあえず深い事は考えず、まず、以下のJavaのコードを SoftBlinkLEDTest.java という名前で保存します。

public class SoftBlinkLEDTest implements Runnable{
    private int x,y;
    
    public SoftBlinkLEDTest(int bit_num, int sleep_time_ms){
        
        x = bit_num;
        y = sleep_time_ms;
       
    }
    
    public static void main(String[] args){
        
        SoftBlinkLEDTest LED1 = new SoftBlinkLEDTest(7, 40);       // PIO0_7, half-cycle = 40msec
        SoftBlinkLEDTest LED2 = new SoftBlinkLEDTest(16, 1000);    // PIO0_16, half-cycle = 1000msec
        SoftBlinkLEDTest LED3 = new SoftBlinkLEDTest(17, 500);    // PIO0_17, half-cycle = 500msec
        
        
        Thread th1 = new Thread(LED1);
        Thread th2 = new Thread(LED2);
        Thread th3 = new Thread(LED3);
        
        th1.start();
        th2.start();
        th3.start();
    }
    
    public void run(){
        
        int port_bit = x, time = y;
        
        while(true){
            try{
                Thread.sleep(time);
            }catch(Exception e){}
            
            lpc800.portWrite(port_bit, 1);
            System.out.println("LED_on");
            
            try{
                Thread.sleep(time);
            }catch(Exception e){}
            
            lpc800.portWrite(port_bit, 0);
            System.out.println("LED_off");
        }
    }
}

ここで、GPIOにアクセスするためのportWrite(bit_num, value) というメソッドをlpc800クラスに用意しました。もちろんこれは独自のクラスです。そんなクラスもメソッドも無いとコンパイラに怒られてしまいますので、コンパイラを騙すフェイクを作ります。以下を lpc800.java という名前で保存します。

public class lpc800{
    
    public static void portWrite(int bit, int value){}
    
}

コレだけです。LPC812本体がこのクラスとメソッドを正しく認識すれば良く、Javacがこのクラスが何をするか知っていてもあまり意味はないので、何も実動作を記述する必要はありません。
二つのファイルを同一のフォルダに入れて、コンパイラバイトコードを生成してもらいます。

$ javac SoftBlinkLEDTest.java

2つのclassファイルができますが、必要なのは SoftBlinkLEDTest.class だけです。このクラスの実態バイトコードです。これをカンマ区切りデータで表現すると、以下の様になります。

0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x32, 0x00, 0x41, 0x0A, 0x00, 0x10, 
	0x00, 0x21, 0x09, 0x00, 0x04, 0x00, 0x22, 0x09, 0x00, 0x04, 0x00, 0x23, 0x07, 0x00, 0x24, 0x0A, 
	0x00, 0x04, 0x00, 0x25, 0x07, 0x00, 0x26, 0x0A, 0x00, 0x06, 0x00, 0x27, 0x0A, 0x00, 0x06, 0x00, 
	0x28, 0x0A, 0x00, 0x06, 0x00, 0x29, 0x07, 0x00, 0x2A, 0x0A, 0x00, 0x2B, 0x00, 0x2C, 0x09, 0x00, 
	0x2D, 0x00, 0x2E, 0x08, 0x00, 0x2F, 0x0A, 0x00, 0x30, 0x00, 0x31, 0x08, 0x00, 0x32, 0x07, 0x00, 
	0x33, 0x07, 0x00, 0x34, 0x01, 0x00, 0x01, 0x78, 0x01, 0x00, 0x01, 0x49, 0x01, 0x00, 0x01, 0x79, 
	0x01, 0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x05, 0x28, 0x49, 0x49, 0x29, 
	0x56, 0x01, 0x00, 0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 
	0x75, 0x6D, 0x62, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x04, 0x6D, 0x61, 0x69, 
	0x6E, 0x01, 0x00, 0x16, 0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 
	0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 
	0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x0D, 0x53, 0x74, 0x61, 0x63, 0x6B, 0x4D, 0x61, 
	0x70, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x07, 0x00, 0x2A, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 0x72, 
	0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x15, 0x53, 0x6F, 0x66, 0x74, 0x42, 0x6C, 0x69, 
	0x6E, 0x6B, 0x4C, 0x45, 0x44, 0x54, 0x65, 0x73, 0x74, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 
	0x15, 0x00, 0x1C, 0x0C, 0x00, 0x12, 0x00, 0x13, 0x0C, 0x00, 0x14, 0x00, 0x13, 0x01, 0x00, 0x10, 
	0x53, 0x6F, 0x66, 0x74, 0x42, 0x6C, 0x69, 0x6E, 0x6B, 0x4C, 0x45, 0x44, 0x54, 0x65, 0x73, 0x74, 
	0x0C, 0x00, 0x15, 0x00, 0x16, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 
	0x67, 0x2F, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x0C, 0x00, 0x15, 0x00, 0x35, 0x0C, 0x00, 0x36, 
	0x00, 0x1C, 0x0C, 0x00, 0x37, 0x00, 0x38, 0x01, 0x00, 0x13, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 
	0x61, 0x6E, 0x67, 0x2F, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x07, 0x00, 0x39, 
	0x0C, 0x00, 0x3A, 0x00, 0x16, 0x07, 0x00, 0x3B, 0x0C, 0x00, 0x3C, 0x00, 0x3D, 0x01, 0x00, 0x06, 
	0x4C, 0x45, 0x44, 0x5F, 0x6F, 0x6E, 0x07, 0x00, 0x3E, 0x0C, 0x00, 0x3F, 0x00, 0x40, 0x01, 0x00, 
	0x07, 0x4C, 0x45, 0x44, 0x5F, 0x6F, 0x66, 0x66, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 
	0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x12, 0x6A, 0x61, 
	0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x52, 0x75, 0x6E, 0x6E, 0x61, 0x62, 0x6C, 0x65, 
	0x01, 0x00, 0x17, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x52, 
	0x75, 0x6E, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x3B, 0x29, 0x56, 0x01, 0x00, 0x05, 0x73, 0x74, 0x61, 
	0x72, 0x74, 0x01, 0x00, 0x05, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x01, 0x00, 0x04, 0x28, 0x4A, 0x29, 
	0x56, 0x01, 0x00, 0x06, 0x6C, 0x70, 0x63, 0x38, 0x30, 0x30, 0x01, 0x00, 0x09, 0x70, 0x6F, 0x72, 
	0x74, 0x57, 0x72, 0x69, 0x74, 0x65, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 
	0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 0x00, 0x03, 0x6F, 0x75, 0x74, 0x01, 
	0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 
	0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 0x00, 0x13, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 
	0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x01, 0x00, 0x07, 
	0x70, 0x72, 0x69, 0x6E, 0x74, 0x6C, 0x6E, 0x01, 0x00, 0x15, 0x28, 0x4C, 0x6A, 0x61, 0x76, 0x61, 
	0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x00, 
	0x21, 0x00, 0x04, 0x00, 0x10, 0x00, 0x01, 0x00, 0x11, 0x00, 0x02, 0x00, 0x02, 0x00, 0x12, 0x00, 
	0x13, 0x00, 0x00, 0x00, 0x02, 0x00, 0x14, 0x00, 0x13, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 
	0x15, 0x00, 0x16, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x33, 0x00, 0x02, 0x00, 0x03, 0x00, 
	0x00, 0x00, 0x0F, 0x2A, 0xB7, 0x00, 0x01, 0x2A, 0x1B, 0xB5, 0x00, 0x02, 0x2A, 0x1C, 0xB5, 0x00, 
	0x03, 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x12, 0x00, 0x04, 0x00, 0x00, 
	0x00, 0x04, 0x00, 0x04, 0x00, 0x06, 0x00, 0x09, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x09, 0x00, 0x09, 
	0x00, 0x19, 0x00, 0x1A, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x90, 0x00, 0x04, 0x00, 0x07, 
	0x00, 0x00, 0x00, 0x54, 0xBB, 0x00, 0x04, 0x59, 0x10, 0x07, 0x10, 0x28, 0xB7, 0x00, 0x05, 0x4C, 
	0xBB, 0x00, 0x04, 0x59, 0x10, 0x10, 0x11, 0x03, 0xE8, 0xB7, 0x00, 0x05, 0x4D, 0xBB, 0x00, 0x04, 
	0x59, 0x10, 0x11, 0x11, 0x01, 0xF4, 0xB7, 0x00, 0x05, 0x4E, 0xBB, 0x00, 0x06, 0x59, 0x2B, 0xB7, 
	0x00, 0x07, 0x3A, 0x04, 0xBB, 0x00, 0x06, 0x59, 0x2C, 0xB7, 0x00, 0x07, 0x3A, 0x05, 0xBB, 0x00, 
	0x06, 0x59, 0x2D, 0xB7, 0x00, 0x07, 0x3A, 0x06, 0x19, 0x04, 0xB6, 0x00, 0x08, 0x19, 0x05, 0xB6, 
	0x00, 0x08, 0x19, 0x06, 0xB6, 0x00, 0x08, 0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 
	0x00, 0x2A, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x0C, 0x00, 0x0E, 0x00, 0x19, 0x00, 0x0F, 
	0x00, 0x26, 0x00, 0x12, 0x00, 0x30, 0x00, 0x13, 0x00, 0x3A, 0x00, 0x14, 0x00, 0x44, 0x00, 0x16, 
	0x00, 0x49, 0x00, 0x17, 0x00, 0x4E, 0x00, 0x18, 0x00, 0x53, 0x00, 0x19, 0x00, 0x01, 0x00, 0x1B, 
	0x00, 0x1C, 0x00, 0x01, 0x00, 0x17, 0x00, 0x00, 0x00, 0x98, 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 
	0x00, 0x39, 0x2A, 0xB4, 0x00, 0x02, 0x3C, 0x2A, 0xB4, 0x00, 0x03, 0x3D, 0x1C, 0x85, 0xB8, 0x00, 
	0x09, 0xA7, 0x00, 0x04, 0x4E, 0x1B, 0x04, 0xB8, 0x00, 0x0B, 0xB2, 0x00, 0x0C, 0x12, 0x0D, 0xB6, 
	0x00, 0x0E, 0x1C, 0x85, 0xB8, 0x00, 0x09, 0xA7, 0x00, 0x04, 0x4E, 0x1B, 0x03, 0xB8, 0x00, 0x0B, 
	0xB2, 0x00, 0x0C, 0x12, 0x0F, 0xB6, 0x00, 0x0E, 0xA7, 0xFF, 0xD4, 0x00, 0x02, 0x00, 0x0A, 0x00, 
	0x0F, 0x00, 0x12, 0x00, 0x0A, 0x00, 0x20, 0x00, 0x25, 0x00, 0x28, 0x00, 0x0A, 0x00, 0x02, 0x00, 
	0x18, 0x00, 0x00, 0x00, 0x26, 0x00, 0x09, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x0A, 0x00, 0x21, 0x00, 
	0x0F, 0x00, 0x22, 0x00, 0x13, 0x00, 0x24, 0x00, 0x18, 0x00, 0x25, 0x00, 0x20, 0x00, 0x28, 0x00, 
	0x25, 0x00, 0x29, 0x00, 0x29, 0x00, 0x2B, 0x00, 0x2E, 0x00, 0x2C, 0x00, 0x1D, 0x00, 0x00, 0x00, 
	0x11, 0x00, 0x05, 0xFD, 0x00, 0x0A, 0x01, 0x01, 0x47, 0x07, 0x00, 0x1E, 0x00, 0x54, 0x07, 0x00, 
	0x1E, 0x00, 0x00, 0x01, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00

上記バイトコードを、MDK-ARMプロジェクトの中のravem.cにvm_array[]という配列名で記述し、コンパイルします。

LPCXpressoだとPIO0_7(赤色LED)が80msec周期、PIO0_16(青色LED)が2000msec周期、PIO0_17(緑色LED)が1000msec周期で点滅するのが確認できると思います。

f:id:Lynx-EyED:20130823203623j:plain

LPC800にJVM載せてUARTループバック

Java VMのせてみた

f:id:Lynx-EyED:20130802144748p:plain
とある必要にかられて、Flash ~32kB/RAM ~4kBクラスの小規模コントローラ向けにJVMを実装しました。
Javaを使いたい場合Linuxを搭載した組み込み機器にJavaを載せるのが通常ですがそれが困難な容量の少ないデバイス向けです。

ここではCortex-M0+のLPC812にJVMの移植を試みました。
lynxeyed-atsu/RaVem · GitHub
解釈できるニーモニックは限られてます。
上記のリンクからダウンロードできるのはMDK-ARMプロジェクト一式です。開発環境はMDK-ARMです。32kBまでなら無償のMDK-ARM Liteが使用できます。(要登録)
https://www.keil.com/demo/eval/arm.htm

なお、LPCXpresso LPC812ボード単体だと書き込み、デバッグが出来ません。LPC-Link2をCMSIS-DAPにして使う必要があります。
(参考) LPCマイコン情報:LPC-Link2ノート1-LPC-Link2を各デバッガにしてデバッグする その1 〜Redlink & LPCXPresso, CMSIS-DAPデバッガ & Keil, SEGGER J-Link & Keil〜


Java中間言語生成にはjavac-1.6.0_51を使用しています。(なにか特殊なコードを吐く様にコンパイラを細工したりはしていません)

SDカードなどからバイトコードをブートしても良いですが、始めてやる作業なので不確定要素が多いと困ります。まず力弱くバイトコードを配列にしてソースコードに貼付け、まるごとMDK-ARMでコンパイルます。const char配列にするのでcsv形式で出力ができるバイナリエディタで、コンパイルした*.classファイルをリードすると良いでしょう。

つまり、javacでコンパイル → バイナリをCの配列にする → JVM本体ごとMDK-ARMでコンパイル → LPC812に書き込み
という流れです。

とりあえず、こんなJavaコードを書きました。UARTから1文字受信して、それを表示します。

import java.io.*;
class hoge1{
    public static void main(String[] args){
        int  i;
        System.out.println( "enter any key" );
        while(true){
            try{
                i = System.in.read();
                System.out.print((char)i);
            }
            catch(IOException e){
                System.err.println( "error" );
            }
        }
    }
}

上記をhoge1.javaとして保存。
コンパイルします。

javac hoge1.java

生成されたhoge1.classは下に示すバイトコードが保存されています。

0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x32, 0x00, 0x34, 0x0A, 0x00, 0x0C, 
0x00, 0x17, 0x09, 0x00, 0x18, 0x00, 0x19, 0x08, 0x00, 0x1A, 0x0A, 0x00, 0x1B, 0x00, 0x1C, 0x09, 
0x00, 0x18, 0x00, 0x1D, 0x0A, 0x00, 0x1E, 0x00, 0x1F, 0x0A, 0x00, 0x1B, 0x00, 0x20, 0x07, 0x00, 
0x21, 0x09, 0x00, 0x18, 0x00, 0x22, 0x08, 0x00, 0x23, 0x07, 0x00, 0x24, 0x07, 0x00, 0x25, 0x01, 
0x00, 0x06, 0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 
0x04, 0x43, 0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 
0x65, 0x72, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x04, 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 
0x16, 0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 
0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x01, 0x00, 0x0D, 0x53, 0x74, 0x61, 0x63, 0x6B, 0x4D, 
0x61, 0x70, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x07, 0x00, 0x21, 0x01, 0x00, 0x0A, 0x53, 0x6F, 0x75, 
0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x2E, 
0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x0D, 0x00, 0x0E, 0x07, 0x00, 0x26, 0x0C, 0x00, 0x27, 0x00, 
0x28, 0x01, 0x00, 0x0D, 0x65, 0x6E, 0x74, 0x65, 0x72, 0x20, 0x61, 0x6E, 0x79, 0x20, 0x6B, 0x65, 
0x79, 0x07, 0x00, 0x29, 0x0C, 0x00, 0x2A, 0x00, 0x2B, 0x0C, 0x00, 0x2C, 0x00, 0x2D, 0x07, 0x00, 
0x2E, 0x0C, 0x00, 0x2F, 0x00, 0x30, 0x0C, 0x00, 0x31, 0x00, 0x32, 0x01, 0x00, 0x13, 0x6A, 0x61, 
0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x49, 0x4F, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 
0x6E, 0x0C, 0x00, 0x33, 0x00, 0x28, 0x01, 0x00, 0x05, 0x65, 0x72, 0x72, 0x6F, 0x72, 0x01, 0x00, 
0x05, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 
0x6E, 0x67, 0x2F, 0x4F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 
0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 0x00, 0x03, 0x6F, 
0x75, 0x74, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 
0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 0x00, 0x13, 0x6A, 0x61, 0x76, 
0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 
0x01, 0x00, 0x07, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x6C, 0x6E, 0x01, 0x00, 0x15, 0x28, 0x4C, 0x6A, 
0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 
0x29, 0x56, 0x01, 0x00, 0x02, 0x69, 0x6E, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 
0x69, 0x6F, 0x2F, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 
0x00, 0x13, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x49, 0x6E, 0x70, 0x75, 0x74, 0x53, 
0x74, 0x72, 0x65, 0x61, 0x6D, 0x01, 0x00, 0x04, 0x72, 0x65, 0x61, 0x64, 0x01, 0x00, 0x03, 0x28, 
0x29, 0x49, 0x01, 0x00, 0x05, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x01, 0x00, 0x04, 0x28, 0x43, 0x29, 
0x56, 0x01, 0x00, 0x03, 0x65, 0x72, 0x72, 0x00, 0x20, 0x00, 0x0B, 0x00, 0x0C, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00, 
0x1D, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, 0xB7, 0x00, 0x01, 0xB1, 0x00, 0x00, 
0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x09, 
0x00, 0x11, 0x00, 0x12, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x02, 0x00, 0x03, 
0x00, 0x00, 0x00, 0x26, 0xB2, 0x00, 0x02, 0x12, 0x03, 0xB6, 0x00, 0x04, 0xB2, 0x00, 0x05, 0xB6, 
0x00, 0x06, 0x3C, 0xB2, 0x00, 0x02, 0x1B, 0x92, 0xB6, 0x00, 0x07, 0xA7, 0xFF, 0xF1, 0x4D, 0xB2, 
0x00, 0x09, 0x12, 0x0A, 0xB6, 0x00, 0x04, 0xA7, 0xFF, 0xE5, 0x00, 0x01, 0x00, 0x08, 0x00, 0x17, 
0x00, 0x1A, 0x00, 0x08, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x07, 0x00, 0x00, 
0x00, 0x06, 0x00, 0x08, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x0A, 0x00, 0x17, 0x00, 0x0E, 0x00, 0x1A, 
0x00, 0x0C, 0x00, 0x1B, 0x00, 0x0D, 0x00, 0x23, 0x00, 0x0E, 0x00, 0x13, 0x00, 0x00, 0x00, 0x07, 
0x00, 0x02, 0x08, 0x51, 0x07, 0x00, 0x14, 0x00, 0x01, 0x00, 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 
0x16, 0x00, 0x00, 0x00

上記バイトコードを、MDK-ARMプロジェクトの中のravem.cにvm_array[]という配列名で記述し、コンパイル。
USB-UARTとLPC812を接続します。

PIO0_4 -> UART0_TXD
PIO0_0 -> UART0_RXD

無事動作すれば、下の様になります。
f:id:Lynx-EyED:20130802152912p:plain


開発風景(笑
f:id:Lynx-EyED:20130802154506j:plain

おまけ:マルチスレッドっぽい何かを実装してみた

ノンプリエンプティブ型でマルチスレッドを実装してみました。
このご時世にノンプリエンプティブとか何考えてるんですかwと言われかねませんが、、そもそも、すでにCortex-M0+にとってJVMが高負荷であり、メモリが限られていて、かつ10も20もスレッドを持つ意義が感じられない・持つことができない*1デバイスに複雑なTCBを持つプリエンプティブ型を採用するのは利点がないように思えたからです。このデバイスでマルチスレッドを持つとしてもメソッドの負荷によりますが2ないし3個が現実的でしょう。それならばノンプリエンプティブ型のほうが、トータルで見た開発時間が少なくてすみます。
Javacで以下のコードをコンパイルします

class hoge1 extends Thread{
    
        public void run() {
            while(true) {
                System.out.print("thread Number=");
                System.out.println(getName());
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }
    
    
        public static void main(String[] args){
            System.out.println("Thread test");
            
            hoge1 thread1 = new hoge1();
            hoge1 thread2 = new hoge1();
            
            thread1.start();
            thread2.start();
        }
    
}

ここで、

System.out.println("thread Number=" + getName());

と記述した方が処理が高速になるよね?と考えた方、正解です。
すみません。StringBuilderを実装していないので、Stringの連結ができないんですよ。(ぉぃ
そのうちしますよそのうち
バイトコードは以下のようになります。

0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x32, 0x00, 0x35, 0x0A, 0x00, 0x0F, 
	0x00, 0x1B, 0x09, 0x00, 0x1C, 0x00, 0x1D, 0x08, 0x00, 0x1E, 0x0A, 0x00, 0x1F, 0x00, 0x20, 0x0A, 
	0x00, 0x0B, 0x00, 0x21, 0x0A, 0x00, 0x1F, 0x00, 0x22, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x03, 0xE8, 0x0A, 0x00, 0x0B, 0x00, 0x23, 0x07, 0x00, 0x24, 0x07, 0x00, 0x25, 0x0A, 0x00, 0x0B, 
	0x00, 0x1B, 0x0A, 0x00, 0x0B, 0x00, 0x26, 0x08, 0x00, 0x27, 0x07, 0x00, 0x28, 0x01, 0x00, 0x06, 
	0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E, 0x01, 0x00, 0x03, 0x28, 0x29, 0x56, 0x01, 0x00, 0x04, 0x43, 
	0x6F, 0x64, 0x65, 0x01, 0x00, 0x0F, 0x4C, 0x69, 0x6E, 0x65, 0x4E, 0x75, 0x6D, 0x62, 0x65, 0x72, 
	0x54, 0x61, 0x62, 0x6C, 0x65, 0x01, 0x00, 0x03, 0x72, 0x75, 0x6E, 0x01, 0x00, 0x0D, 0x53, 0x74, 
	0x61, 0x63, 0x6B, 0x4D, 0x61, 0x70, 0x54, 0x61, 0x62, 0x6C, 0x65, 0x07, 0x00, 0x24, 0x01, 0x00, 
	0x04, 0x6D, 0x61, 0x69, 0x6E, 0x01, 0x00, 0x16, 0x28, 0x5B, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 
	0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 0x3B, 0x29, 0x56, 0x01, 0x00, 
	0x0A, 0x53, 0x6F, 0x75, 0x72, 0x63, 0x65, 0x46, 0x69, 0x6C, 0x65, 0x01, 0x00, 0x0A, 0x68, 0x6F, 
	0x67, 0x65, 0x31, 0x2E, 0x6A, 0x61, 0x76, 0x61, 0x0C, 0x00, 0x10, 0x00, 0x11, 0x07, 0x00, 0x29, 
	0x0C, 0x00, 0x2A, 0x00, 0x2B, 0x01, 0x00, 0x0E, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x20, 0x4E, 
	0x75, 0x6D, 0x62, 0x65, 0x72, 0x3D, 0x07, 0x00, 0x2C, 0x0C, 0x00, 0x2D, 0x00, 0x2E, 0x0C, 0x00, 
	0x2F, 0x00, 0x30, 0x0C, 0x00, 0x31, 0x00, 0x2E, 0x0C, 0x00, 0x32, 0x00, 0x33, 0x01, 0x00, 0x1E, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x72, 
	0x75, 0x70, 0x74, 0x65, 0x64, 0x45, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x01, 0x00, 
	0x05, 0x68, 0x6F, 0x67, 0x65, 0x31, 0x0C, 0x00, 0x34, 0x00, 0x11, 0x01, 0x00, 0x0B, 0x54, 0x68, 
	0x72, 0x65, 0x61, 0x64, 0x20, 0x74, 0x65, 0x73, 0x74, 0x01, 0x00, 0x10, 0x6A, 0x61, 0x76, 0x61, 
	0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x54, 0x68, 0x72, 0x65, 0x61, 0x64, 0x01, 0x00, 0x10, 0x6A, 
	0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x01, 
	0x00, 0x03, 0x6F, 0x75, 0x74, 0x01, 0x00, 0x15, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 
	0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6D, 0x3B, 0x01, 0x00, 0x13, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x69, 0x6F, 0x2F, 0x50, 0x72, 0x69, 0x6E, 0x74, 0x53, 0x74, 0x72, 
	0x65, 0x61, 0x6D, 0x01, 0x00, 0x05, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x01, 0x00, 0x15, 0x28, 0x4C, 
	0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 0x69, 0x6E, 0x67, 
	0x3B, 0x29, 0x56, 0x01, 0x00, 0x07, 0x67, 0x65, 0x74, 0x4E, 0x61, 0x6D, 0x65, 0x01, 0x00, 0x14, 
	0x28, 0x29, 0x4C, 0x6A, 0x61, 0x76, 0x61, 0x2F, 0x6C, 0x61, 0x6E, 0x67, 0x2F, 0x53, 0x74, 0x72, 
	0x69, 0x6E, 0x67, 0x3B, 0x01, 0x00, 0x07, 0x70, 0x72, 0x69, 0x6E, 0x74, 0x6C, 0x6E, 0x01, 0x00, 
	0x05, 0x73, 0x6C, 0x65, 0x65, 0x70, 0x01, 0x00, 0x04, 0x28, 0x4A, 0x29, 0x56, 0x01, 0x00, 0x05, 
	0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x20, 0x00, 0x0B, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x11, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1D, 0x00, 
	0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x2A, 0xB7, 0x00, 0x01, 0xB1, 0x00, 0x00, 0x00, 0x01, 
	0x00, 0x13, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x14, 
	0x00, 0x11, 0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x60, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 
	0x00, 0x1F, 0xB2, 0x00, 0x02, 0x12, 0x03, 0xB6, 0x00, 0x04, 0xB2, 0x00, 0x02, 0x2A, 0xB6, 0x00, 
	0x05, 0xB6, 0x00, 0x06, 0x14, 0x00, 0x07, 0xB8, 0x00, 0x09, 0xA7, 0xFF, 0xE8, 0x4C, 0xA7, 0xFF, 
	0xE4, 0x00, 0x01, 0x00, 0x12, 0x00, 0x18, 0x00, 0x1B, 0x00, 0x0A, 0x00, 0x02, 0x00, 0x13, 0x00, 
	0x00, 0x00, 0x1A, 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x06, 0x00, 0x12, 0x00, 
	0x08, 0x00, 0x18, 0x00, 0x0A, 0x00, 0x1B, 0x00, 0x09, 0x00, 0x1C, 0x00, 0x0A, 0x00, 0x15, 0x00, 
	0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x5A, 0x07, 0x00, 0x16, 0x00, 0x09, 0x00, 0x17, 0x00, 0x18, 
	0x00, 0x01, 0x00, 0x12, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 
	0xBB, 0x00, 0x0B, 0x59, 0xB7, 0x00, 0x0C, 0x4C, 0xBB, 0x00, 0x0B, 0x59, 0xB7, 0x00, 0x0C, 0x4D, 
	0x2B, 0xB6, 0x00, 0x0D, 0x2C, 0xB6, 0x00, 0x0D, 0xB2, 0x00, 0x02, 0x12, 0x0E, 0xB6, 0x00, 0x06, 
	0xB1, 0x00, 0x00, 0x00, 0x01, 0x00, 0x13, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x06, 0x00, 0x00, 0x00, 
	0x11, 0x00, 0x08, 0x00, 0x12, 0x00, 0x10, 0x00, 0x14, 0x00, 0x14, 0x00, 0x15, 0x00, 0x18, 0x00, 
	0x17, 0x00, 0x20, 0x00, 0x18, 0x00, 0x01, 0x00, 0x19, 0x00, 0x00, 0x00, 0x02, 0x00, 0x1A, 0x00, 
	0x00, 0x00

コンパイルして実行すると、UARTでマルチスレッドっぽくなっているところが確認できると思います。
f:id:Lynx-EyED:20130809213540p:plain


◆参考
Chapter 4. The class File Format
Javaのhello worldの読み方 | mwSoft




*1:LPC81xは省メモリで安価なデバイスですので機能ごとに複数のデバイスに分けて平行動作させたほうがデバッグ、機能安全の観点からもメリットがありますし、省電力デバイスなのでパワーマネージメントの設計に注力した方が良いと思われます

画像認識におけるフーリエ変換とフーリエ記述子(その1)

はじめに

一連のFPGA関連ネタとして、画像認識を扱おうと思います。
ここでは周波数スペクトルを推定する離散フーリエ変換からはじまり、フーリエ記述子、ウェーブレット変換、ニューラルネットワークに至るまで取り扱うつもりです。

画像の特徴を抽出する

 入力画像から特定の形状を抽出して、基準画像と比較することは画像認証ではよく行う方法です。そのためには、入力画像データから図形を抽出し、数式化する必要があります。
 例えば一辺の長さが2の正方形を画像の中から抽出し数式化しなければならないとします。もし図1の様に、画像の縦横に対して図形が平行なら、数式化は難しくありません。
f:id:Lynx-EyED:20130612191453p:plain
図1:一辺が2の正方形

この正方形は以下の式で表せます。
f:id:Lynx-EyED:20130612235544p:plain

しかし、図2の様に正方形に傾きがある場合、数式化は難しいかもしれません。不可能ではないですが、その数式から正方形であるかどうか判別するのに時間がかかるはずです。

f:id:Lynx-EyED:20130612191505p:plain
図2:傾きのある正方形

このように傾きがある場合でも、画像中に正方形が存在する事を数式として表現できるでしょうか。
一つの答えは、図3のように正方形上を移動する点Pを用意する事です。
f:id:Lynx-EyED:20130612191514p:plain
図3:正方形上を移動する点P

言葉で表現すると以下の様になります。

  1. 距離2だけ進み、開始時のベクトル方向より正の方向(反時計回り)に90度回転
  2. 距離2だけ進み、開始時のベクトル方向より正の方向(反時計回り)に180度回転
  3. 距離2だけ進み、開始時のベクトル方向より正の方向(反時計回り)に270度回転
  4. 距離2だけ進み、開始時のベクトル方向より正の方向(反時計回り)に360度回転


このように表現すると正方形の傾きは関係なくなります。
グラフ化してみましょう。点Pが移動する距離をs、開始時のベクトルと現時点でのベクトルのなす角度をθとします。先ほどdegreeで角度を表現しましたが、便宜上radian表示に直しました。
f:id:Lynx-EyED:20130612191530p:plain
図4:点Pの軌跡をグラフ化

少し違和感のあるグラフになりました。点Pはそもそも正方形上を移動しているので2x4=8だけ進むと周期的に元の場所に戻って来ているはずです。

そこで、点Pの軌跡を「正規化」します。*1
先ほどの様に言葉で表現するとこうなります。

  1. 距離2だけ進み、正の方向(反時計回り)に90度回転
  2. (1)を4回繰り返す


グラフで表現しましょう。「正規化」しているので少し複雑になります。

θの正規化偏角関数をθN(s)、
正方形の全長をL、
開始時のベクトル方向をθ(0)、
現在の角度とθ(0)とのなす角をθ(s)

とすると、
f:id:Lynx-EyED:20130612192029p:plain
となり、グラフ化すると図5の様になります。
f:id:Lynx-EyED:20130612191540p:plain
図5:正方形に対する正規化偏角関数

図5から、正方形を傾きに依存せず周期的な関数で表せる事がわかります。*2

このように、図形の持つ角度(または周波数成分)から、どのような形状をしているかを知る(推定する)ことが可能であり、情報圧縮や特徴抽出に利用されます。

 取得したデータにどの周波数成分がどの程度含まれているかを知る上でフーリエ変換は有効と言えます。

フーリエ変換

周期T[s]の周期関数の基本角周波数ω[rad/s]は
f:id:Lynx-EyED:20130612234001p:plain…(1)

となります。
フーリエ級数における基本的な概念は、
「全ての周期信号はバイアス成分とω[rad/s]およびその整数倍の角周波数をもつ三角関数の和として表現できる」
というものです。
ある時間変化する周期信号 s(t)に対して、
f:id:Lynx-EyED:20130612234014p:plain…(2)

が成り立ちます(実フーリエ級数展開)。
ただし三角関数の直交性から
f:id:Lynx-EyED:20130612234023p:plain…(3)

となります。
式(2)を複素指数関数で表現すると、以下の様になります。
f:id:Lynx-EyED:20130612234031p:plain…(4)


式(4)は、分解して考えると
f:id:Lynx-EyED:20130612234040p:plain…(5)


式(5)と式(2)の係数をオイラーの公式を考慮しつつ対応させて考えると、以下の様になります。
f:id:Lynx-EyED:20130612234048p:plain…(6)


式(3)でも示しましたが、フーリエ級数展開は周期関数に対して有効な手がかりです。しかし非周期信号や孤立波を対象にする場合はこの方法は有効ではありません。どうすれば良いでしょうか?

非周期信号を周期無限大の周期関数とみなせばよいのです。*3

具体的には積分を±無限大の範囲で行う事になります。
しかし数学的にはその手法をとる事は可能ですが、コンピュータでは離散化された信号が対象であり、有限回で計算を終える必要があります。そのために

時間Tのあいだにサンプリングした全データNが周期的に繰り返されるものとみなす

という工夫をして、この問題を解決*しようと*しています。(なぜ「解決しています」では無いのかは後述) これが離散フーリエ変換の考え方になります。

離散フーリエ変換(DFT)

前述のフーリエ級数展開では、信号s(t)は連続時間tの間に変化する信号であったので時間軸に対して積分を行っていました。ここではN[個]のサンプリングデータに対して演算を行います。従って複素フーリエ係数を求める際には時間tではなく、i=0番目から(N-1)番目のサンプリングデータに対して積分(正確に言うと総和)を求めます。

アナログ入力信号: s(t)
サンプリング周期: τ[s/回]
総サンプリングデータ: N[回]またはN[個]
サンプリング時間: T[s]

とする。s(t)を標本化したデータのうちi番目のものs[i]は、

f:id:Lynx-EyED:20130614223033p:plain …(7)

時間t[s]は標本化されたiτ[s]に置き換えられます。
また、
f:id:Lynx-EyED:20130614223108p:plain …(8)

f:id:Lynx-EyED:20130614223119p:plain …(9)


前述した通りN[個]のサンプリングデータに対して演算を行うため、式(5)で求めた複素フーリエ係数は離散化したデータにおいて、
f:id:Lynx-EyED:20130614223132p:plain …(10)

式(10)が離散フーリエ変換と呼ばれるものになります。また、式(4)を直接離散化すると、 逆離散フーリエ変換(IDFT)が得られ、

f:id:Lynx-EyED:20130614223144p:plain …(11)


となります。しかしT=Nτはサンプリング時間であり、信号の周期と必ずしも一致しません。例えば図6のようにサンプリング周期Tと実際の波形の周期T’が異なっている場合を考えましょう。
f:id:Lynx-EyED:20130614223205p:plain
図6:サンプリング周期と実際の周期が異なる場合

この波形に対してDFTを行い、その後IDFTを行うと、図7の様に不連続な波形となってしまい、スペクトルには多くの高次成分が現れてしまいます。
f:id:Lynx-EyED:20130614223231p:plain
図7:不連続な波形として再現されてしまい、多くの高次成分が現れる

この様な現象を抑えるためハミングや、ハニング関数が用いられます。

ちょっと長いので位相回転因子やFFTの話題、ハニング窓関数のお話はまた次回に。


◆参考文献
酒井幸市 (2003);ディジタル画像処理入門 OpenDesignBooks, pp.151-156. CQ出版社
酒井幸市 (2007);改訂版 ディジタル画像処理の基礎と応用 ディジタル信号処理シリーズ, pp.119-125. CQ出版社
後藤尚久 (2001);なっとくする電気数学, pp.99-119. 講談社
佐野理 (1992);キーポイント 微分方程式, pp.108-138. 岩波書店
昌達慶仁 (2010);圧縮処理プログラミング, pp.318-319. ソフトバンククリエイティブ

*1:詳細は後述しますが、不連続点が多いと収束が遅くなるのでなるべく単純な周期関数になるようにします

*2:詳細は後述する、有限個のフーリエ係数で図形の特徴を記述するフーリエ記述子の項で扱います

*3:厳密に言うと周期無限大=非周期関数

Avalon-MM Master Bridgeを探す

USB付きで簡単に使えるBridge

Qsysを使ってAvalonバスにモジュールをぶら下げて使えるシステムを開発中です。
Alteraのデバイスの以下のシリーズで検討しています。

  • Cyclone IV E / GX
  • Cyclone V E
  • MAX V CPLD

Nios IIは規模が500LE未満と軽量なソフトマクロなのでこれをAvalon-MM マスタとしてインプリメントするのが正解かもしれませんが、以下の理由からこれを見送りました。

・CPUとロジックの各部を物理的に分けておきたい(問題発生時の切り分け、USBによる外部との通信)
CPLDの場合はNios IIは通常インプリメントできない
・システム動作中に適宜、リコンフィグレーションする用途を考えているので、CPUは外部に欲しい

そこで外部にUSBデバイスorOTG対応マイコンの採用を考えました。Avalonバスを選択してもバスマスタが別個に選択できるのは便利な点です。マスタ候補は以下の通りです。

  • Vinculum II
  • LPC11U37
  • PIC32MX250F128/220F032

ここから、安価で、部品点数が減らせて、開発が容易でそこそこ演算能力がある(配列を持たせ、その積和を求める事があるので)デバイスを探しました。

  • Vinculum IIはおなじみFTDIのチップでUSBホストにもなるフレキシブルなデバイスですが、開発が謎のRTOSベースでちょっとよくわかんないし思考回路が爆発四散したのでやめ(ぉぃ
  • LPC11U37はNXPUSBLibなど豊富なライブラリに支えられており、とても使いやすい環境を提供されているのですが、BOMが増大する傾向にあったので今回は採用を取りやめました。残念。
  • PIC32MX250Fは(ESDプロテクションを考慮しないなら)そのままUSBバスに直結が可能な32bit MIPS M4KコアPICです。ライブラリが豊富でUSB OTGにも対応しているので今回採用しました。高速ADコンバータも魅力です。

PIC32MXでUSB

PIC32MXでLチカ - Lynx-EyEDの電音鍵盤 新館で触れていたので、MPLABXで開発しました。USB-CDCで動作確認をします
ハードは以前作ったものに、USB mini-Bを追加工したもの。
おもて↓
f:id:Lynx-EyED:20130527232550j:plain

うら↓
f:id:Lynx-EyED:20130527232530j:plain


必要なソフト

基本的にこのライブラリにはいっている"Device - CDC - Serial Emulator"で良いのですが、PIC32MX220F/250Fベースのものがないのと、パイロットランプ用LEDやプッシュスイッチなどは不要、またifdefで宣言されているPIC16/18/24のコードは不要ですので、ばっさり削除しました。

githubにそのときのコードを上げてあります
lynxeyed-atsu/USB_CDC_on_PIC32MX220 · GitHub

ファイルツリー構成は以下の図の様にしてください。
f:id:Lynx-EyED:20130527231144p:plain

メイン動作はUSB_CDC.cのProcessIO()関数が行っています。
この関数の後半部を

if(USBUSARTIsTxTrfReady()) {
		numBytesRead = getsUSBUSART(USB_Out_Buffer,64);
                putUSBUSART("unko",4);
	}
	CDCTxService();

とかきかえて、PIC32MX220F/250Fに書き込み。
Macでは

$  screen /dev/tty.usbmodem621

のようにオープンすると、怒濤のunkoが出迎えてくれる*1
f:id:Lynx-EyED:20130527232241p:plain
うん。もういい。

ちゃんと認識されてる事も確認。
f:id:Lynx-EyED:20130527234646p:plain

というわけで、サクッと動くことがわかったので、コレから作るデバイスのブロック図を作ってみました。以下の図はCyclone Vのものですが、他のデバイスもおおよそ同じ構成となります。
f:id:Lynx-EyED:20130527233200j:plain

設計途中の基板の3Dビュー(下図はMAX V(5M2210)+PIC32MX)
PIC32MXはボード裏側に配置しました。
f:id:Lynx-EyED:20130527233249p:plain
f:id:Lynx-EyED:20130527233258p:plain



◆参考
no crystal usb
PIC32MX220F032B クリスタル無し、内蔵FRCでUSB|marumonのページ

*1:githubに上げてあるコードはちゃんとエコーバックのコードなのであしからず