mruby / microcontroller-peripheral-interface-guide

Common I/O API Guidelines for mruby and mruby/c.
https://mruby.org/docs/
22 stars 3 forks source link

How to impliment valiable argument in mrbgems #4

Closed yamori813 closed 11 months ago

yamori813 commented 1 year ago

Sorry use Japanese.

i2cのwriteで

i2c.write( 0x45, 0x30, 0xa2 ) i2c.write( 0x11, [0x30, 0xa2] )

と可変の引数を受け取れるようにしていますが、これはmrbgemsのCではどのように実装するのでしょうか?

mruby-bsdiicというmrbgemsでwriteとwritesに分けているので、この仕様に従いまとめてみようか思い疑問になりました。

HirohitoHigashi commented 1 year ago

Arrayクラスの [] メソッドが参考になるのではないでしょうか。

https://github.com/mruby/mruby/blob/298155d59ed4ed952ce00a8beb0438ab4692fac4/src/array.c#L964

このメソッドでは可変長引数で、ary[n] と ary[n,m] の2パターンあります。 実装では、if (mrb_get_argc(mrb) == 1) で、引数の個数を判定して動作を分けています。

yamori813 commented 1 year ago

コメントありがとうございます。できました。

https://github.com/yamori813/mruby-bsdiic/commit/bb69ad4d4d1d8c3aa8afd8d1db86f7b636c92719

yamori813 commented 1 year ago

この修正をしたところ引数3の場合正しく動かなくなりました。:(

yamori813 commented 1 year ago

こちらのミスでした。お騒がせしてすみません。

yamori813 commented 1 year ago

別の話になりますが、センサーなどはバイナリ値を返しますがread()の返り値が文字列というのはどのような形式になりますか?

また

s = i2c.read( 0x45, 3, 0xf3, 0x2d )

writeの方がarrayなので、

s = i2c.read( 0x45, 3, [0xf3, 0x2d] )

の方が自然ではないですか?

I2C.newでキーワード引数を使用していますが、mrbgemsのCコードでキーワード引数はどのようにして拾えるのでしょうか?

HirohitoHigashi commented 1 year ago

戻り値の件 Rubyは、バイトアレイ型は String と共用する方針なので、read の戻り値もそのように設計してあります。 例)センサーから 0x21, 0x00, 0x3a の3バイトが返る場合、readの戻り値は、"\x21\x00\x3a" が返ることを想定

戻り値を使う方法として、以下の2種類の方法が代表的な使い方だと考えています。

  1. getbyte メソッドを使う

    s = i2c.read( 0x45, 3, 0xf3, 0x2d )
    b0 = s.getbyte(0)
    b1 = s.getbyte(1)
    b2 = s.getbyte(2)
  2. bytes メソッドを使う

    a = i2c.read( 0x45, 3, 0xf3, 0x2d ).bytes
    b0 = a[0]
    b1 = a[1]
    b2 = a[2]

センサーのように数バイトしか送受信がないデバイスならば、戻り値が String でなく Array である方が使いやすいと思います。これは別の方からも指摘をうけました。けれども I2C接続の FlashROM を扱うユースケースがありまして、その場合 Read/Write 単位が数KBになります。数KB 長の Array はマイコンにとってはすごく厳しいので String を規定しました。

readメソッドの第3引数の件

write メソッドのデータは、 Integer, Array, String の3種類を想定しています。 従って read の第3引数も同じ仕様であり、提案された以下2種類の方法は、どちらも同じ結果をもたらすべきです。

s = i2c.read( 0x45, 3, 0xf3, 0x2d )
s = i2c.read( 0x45, 3, [0xf3, 0x2d] )

使う側にとってより自由度が高く、ライブラリを実装する側は苦労する という方針ですね。

yamori813 commented 1 year ago

readの返り値の理解しました。

こちらもmruby-cairoといmrgbemsでメモリバッファに描画して、他のmrbgemsでデバイスに描画するためにデータ渡しするのをArrayでは効率が悪いのでポインター渡しにしていました。

yamori813 commented 1 year ago

私の理解ではキーワード引数はrubyコードでは使えますが、mrbgemsのCコードでは直接処理する事は出来ないと思います。rubyコードでクラスを作って、そこからCコードを呼び出すみたいな事をすればできるのかもしれませんが、あまり現実的ではありません。

HirohitoHigashi commented 1 year ago

Cコードでキーワード引数を取得する事自体は可能のはずですが、mrubyでのオーソライズされた方法を私は知らないです。 だれか詳しい人はいませんかね。

参考情報として、mruby/c の方は、MRBCKW*** マクロを用意して対応できるようにしています。 サンプルは、PIC32の mruby/c 実装だと以下。 https://github.com/YoshihiroOgura/pic32mx170_mrubyc/blob/758b664af4045ae51a52b03e53ba8e73030accae/pwm.c#L180

mimaki commented 1 year ago

@yamori813 mrbgemのCコードでキーワード引数を扱うには、mrb_get_args()のフォーマット文字に “:” を指定して、mrb_kwargs構造体で受け取ります。

使用方法は mruby.h に記載がありますので、こちらを参照ください。 mrb_get_args()を呼び出す前に、mrb_kwargsの変数を初期化する必要があるところが注意点です。

実装例は mrbgem/mruby-io/src/io.c が参考になると思います。 ご参考まで。

yamori813 commented 1 year ago

@mimaki ありがとうございます。

処理が複雑ですね。デバイス周りは制約があったりするので、あまり複雑にならないのが嬉しいです。

HirohitoHigashi commented 1 year ago

@yamori813 リファレンス実装があると良いですね。それがあれば、皆がそれを元にデバイス依存の部分のみ書けば良いですし。 全体の設計思想として、以下の事を考えて設計しています。

その結果として

といった構成になっているはずです。キーワード引数の対応は、ライブラリ作成者にとっては面倒ですけどね。

yamori813 commented 1 year ago

i2cのwriteのエラー時には0が返り、readのエラー時にはmrb_nil_value()が返るという認識でよかったですか?

あと

s = i2c.read( 0x45, 3, 0xf3, 0x2d )

は2バイト送信後受信になり

s = i2c.read( 0x45, 3, 0xf3,)

は1バイト送信後受信で、intの引数は2バイトまで直接指定が可能という仕様ですか?

HirohitoHigashi commented 12 months ago

エラー時の挙動や戻り値は、今のところ規格では明確に定めていないものが多いです。 とりあえず正常系をきちんと定めてレビュー・試験実装してもらい、異常系(エラー時)の挙動はむしろ提案して欲しいと思っています。どうしたらよいと思いますか? Ruby的には例外を発生させるのが本筋かと思い、Rboard+mruby/c では例外を発生させていますが、ちょっと大げさと思う面もあり悩みます。

read の引数の件

intの引数は2バイトまで直接指定が可能という仕様ですか?

readの引数の数、具体的には送信できるバイト数ですが、制限はありません。

s = i2c.read( 0x45, 3, 0xf3, 0x2d, 0x03, 0x04, 0x05 )  # 5bytes送信後 3bytes 受信

この仕様は、rubygem の i2c とコンパチブルになっています。 https://www.rubydoc.info/gems/i2c/0.4.2/I2C/Dev#read-instance_method

yamori813 commented 12 months ago

readの引数ですが、mrubyのmrbgemsで制限無しの引数は実装できるのでしょうか?

返り値も引数も複数要素がある場合はArrayを基本として、最適化が必要な場合はStringsでの実装も可能とするのが、現実的な気がします。

yamori813 commented 12 months ago

FreeBSD上のmruby用のi2c mrbgemsをちょっと修正してみました。

https://github.com/yamori813/mruby-bsdiic

i2cはバイトオペレーションで操作できるslave(device)が多いので、バイトオペレーションとarrayでのオペレーションにしてみました。

yamori813 commented 11 months ago

頑張らないと実装できないのはmrubyらしくないと思うんですが。

yamori813 commented 11 months ago

ここを見るとESP32のmruby/cでもreadの返り値はarrayみたいですね。 https://www.gfd-dennou.org/arch/sugiyama/IoTeX_2018/es/mrubyc_2020/lecture_mrubyc_04-I2C.htm.ja

HirohitoHigashi commented 11 months ago

そうですね。この記事ではまだ規格策定前なので、自分なりの方法で実装されたのですね。今回こういったケースを統一させて、可能な限り規格に沿って実装してもらえば、利用者にとって嬉しいことが多いだろうという目論見です。 ですので実装を頑張るのは、ライブラリ作成者です。その分利用者は楽をするというバランスです。もちろんライブラリ作成が難しすぎてもうまく回らないので、現実的なところを考えたつもりです。

yamori813 commented 11 months ago

可変引数は、mrubyのソースを見ると、MRB_ARGS_ANY()を指定してArrayで受け取ることが出来るようです。

しかし返り値はコストを削減のためにStringsで、引数はいろいろなタイプを可能にしてコストを増やすのはちぐはぐな感じがします。

HirohitoHigashi commented 11 months ago

直接お話しする機会を得たので、そのサマリーを追記して、この件はクローズしたいと思います。