MiMicRemoteMCUのファームフェア自作の関数を追加して、それをJavascriptAPIなどの高レベルAPIで呼び出すことが出来るようになりました。この機能は、MiMic/1.1.0(MiMicRemoteMCU/1.1)から使用できます。
何が出来るのか
例えば、以下の様なC言語で実装した関数を、Javascriptから呼び出すことが出来ます。この例では、ストリームから受信した32bit Int値を、そのままエコーバックします。
C言語のユーザ関数(Native function)
NyLPC_TBool jp_nyatla_native_sample(NyLPC_TcMiMicVM_t* i_vm) { NyLPC_TUInt32 v; if(!NyLPC_cMiMicVM_sget(i_vm,&v)){ return NyLPC_TBool_FALSE; } //write uint32 value from stream. if(!NyLPC_cMiMicVM_sput(i_vm,v)){ return NyLPC_TBool_FALSE; } return NyLPC_TBool_TRUE;//OK }
呼び出す為のJavascript(Caller)
function init(){ var mcu=new LPCXpresso1769.Mcu("192.168.128.39"); var ni=new LPCXpresso1769.Ni(mcu); var ret={stream:null}; ni.call(0x39000000,{stream:[31319]},ret); alert(ret.stream[0]); }
ネイティブ関数の仕様
ネイティブ関数は、入力ストリーム、又はVMのワークレジスタから引数を受け取ります。また、出力ストリーム、又はVMのワークレジスタに戻り値を返します。値の型は、32bit int型のみが扱えます。
どのネイティブ関数を呼び出すかは、ファンクションIDによって決定します。ファンクションIDは、32bitのint値です。ファンクションIDと関数のマッピングは、ファームウェアの中に定義します。
ネイティブ関数のスタックサイズは、約256バイトです。
実装方法
ネイティブ関数は、次のステップで実装します。
- ファームフェアに関数を実装する
- 関数テーブルに関数を登録する
- ファームウェアの更新
- JavascriptAPIのLPCXpresso1769.Niクラスからの実行テスト
1.ファームウェアに関数を定義する
ネイティブ関数のプロトタイプは、次の通りです。
typedef NyLPC_TBool (*NyLPC_TNativeFunction)(NyLPC_TcMiMicVM_t* i_vm);
戻り値は、関数の正常終了/異常終了のフラグです。FALSEを返すと、呼出元のMiMicVMは後続のバイトコードの実行を放棄し、エラーコードMVM_RUNTIME_NG_CALLを返却して終了します。
i_vmは、この関数を呼び出したMiMicVMのインスタンスです。関数は、i_vm->wm[]の値を操作することが出来ます。
次のコードは、wm0に1を加算して返す関数を定義します。
NyLPC_TBool sample(NyLPC_TcMiMicVM_t* i_vm) { i_vm->wm[0]+=1; return NyLPC_TBool_TRUE;//OK }
関数は、native_function.cに定義してください。自分で関数を追加する場合は、native_function.hをインクルードするようにしてください。
2.関数テーブルに関数を登録する
関数テーブルは、native_function.cのnaitive_function_tableで定義します。次の様に登録します。
const struct TNativeFunctionItem naitive_function_table[]= { {0x39000000,"jp.nyatla.native_sample",jp_nyatla_native_sample}, {0x00000001,"your sample function",sample}, // // add your native function here! // {0x39FFFFFF,NULL,NULL} //This is terminator. };
1番目のフィールドはファンクションIDです。関数を識別するID値になります。2番目のフィールドは関数の文字列名です。現在は、この値に意味はありません。3番目のフィールドが、関数ポインタになります。
ファンクションIDにはビットフィールドがあります。基本的にLocalIDを使用してください。他の予約値は使用しないでください。ビットフィールドの説明を以下に示します。
bit field | name | value |
---|---|---|
31-24 | Address type |
|
23-0 | Number | Address Typeの値で意味が異なる。
|
グローバルIDのために、MiMicDoamainのIDが必要な方はお知らせください。発行します。
3.ファームウェアの更新
実装が完了したら、ファームウェアをLPCXpresso1769へ書き込んで更新してください。ファームウェアを更新すると、設定値がリセットされるので注意して下さい。
4.JavascriptAPIからの実行テスト
新しいファームウェアのMiMicに接続できることを確認したら、次のHtmlを作成して、実行します。IPアドレスの部分は、適切に変更してください。
LPCXpresso1769.Niクラスについては、こちらのドキュメントを参照してください。
<head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <script src="../MiMicCore.js"></script> <script src="../LPCXPresso1769.All.js"></script> <script type="text/javascript"> function init(){ var mcu=new LPCXpresso1769.Mcu("192.168.0.39"); var ni=new LPCXpresso1769.Ni(mcu); var ret={wm0:null};//create a result holder. ni.call(0x00000001,{wm0:38},ret); alert(ret.wm0);//show result } </script> </head> <body onload="init();"><h1>native call test</h1></body>
応用編
MiMicのネイティブコール関数は、WMレジスタ(0-7)のほかに、HTTPストリームから値を得ることも出来ます。HTTPストリーム関数とのデータの交換には、NyLPC_cMiMicVM_sget関数と、NyLPC_cMiMicVM_sput関数を使います。
NyLPC_TUInt32 v; //read uint32 value from stream. if(!NyLPC_cMiMicVM_sget(i_vm,&v)){ return NyLPC_TBool_FALSE; } //write uint32 value from stream. if(!NyLPC_cMiMicVM_sput(i_vm,v)){ return NyLPC_TBool_FALSE; }
NyLPC_cMiMicVM_sget関数は、httpストリームから32bit int値を1個読み出します。この値は、call関数の第2引数の入力パラメタのstreamに、配列で与えます。1回目のコールで1番目、2回目のコールで2個目のパラメータが得られます。
LPCXpresso1769.Ni.call(FUNCTION_ID,{stream:[1,2,3...]},OUTPUT)
NyLPC_cMiMicVM_sput関数は、httpストリームへ32bit int値を1個書き出します。この値は、call関数の第3引数の出力パラメタにstreamが有る場合に、配列で返されます。1回目のコールで1番目、2回目のコールで2個目の要素の値を設定します。
var r={stream:null}; LPCXpresso1769.Ni.call(FUNCTION_ID,INPUT,r);
現在は32bit int以外の値を関数に引き渡すことはできません。文字列などは、32bit値に変換して渡す必要があります。
Inside MiMic Native Function Interface
Mimicのネイティブ関数コールは、MiMicRemoteMCU/1.1 から追加された、MiMicVMのCALL命令を使用して実装されています。
CALL命令は、IDに一致する組み込み関数を、VMのインスタンスを引数にして呼び出します。
ここで紹介したLPCXpresso1769.Niクラスは、CALL命令の前後にVMの状態設定コードで挟んで実装したものです。実行されているMiMicILは、以下の構造をしています。
;WMレジスタの初期化 LD #0 (WM0の値) LD #1 (WM1の値) ; : WMの初期化を必要な数だけ実行 CALL #0 ;WM値の出力 SPUT #0 SPUT #1 ; : WMの出力を必要な数だけ実行 EXIT .END .D32 (streamの値0) .D32 (streamの値1) ; :
ネイティブ関数コール自体はMiMicILレベルで行われるので、必要なら、複数のネイティブ関数コールを組み合わせて実行することも出来ます。
Comments are closed, but trackbacks and pingbacks are open.