2018年04月26日

自作キーボード ソフト編(3)

ソフト編(1)(2) の実装編である。

今まで書いてきたことをコードにしたのが、これ
 ・i2ckbd-0.0.zip
コンパイルが通っただけで、動くはずもない状態だが、現状はサイズの見積もりが最重要。
念のためコンパイル環境を明記しておく
 ・ WINAVR-20100110
である。なんか古いのだが、使うチップも古いし。

    (追記) rm とかのコマンドがエラーになったり、使いにくいので変更
     ・ avr-toolchain-installer-3.4.1.1195-win32

make すると

    avr-gcc -I. -mmcu=attiny2313 -Wall -DF_CPU=8000000UL \
    -Os -fsigned-char -c i2ckbd.c
    avr-gcc -I. -mmcu=attiny2313 -Wall -DF_CPU=8000000UL \
    -Os -fsigned-char -c usi.c
    avr-gcc -mmcu=attiny2313 -Wl,-Map=i2ckbd.map i2ckbd.o usi.o -o i2ckbd.elf
    avr-objcopy -O ihex -R .eeprom i2ckbd.elf i2ckbd.hex

    text data bss dec hex filename
    1966 0 69 2035 7f3 i2ckbd.elf

こうなった。なんと! ATtiny2313 に入ってしまった。しかしかなり微妙。
また、RAM が厳しい。全部で 128 しかないが、69 バイトを消費している。割り込みを使うので、レジスタ全部退避みたいなところがあり、多分スタックオーバーフローを起こす。

プログラムメモリの節約

    ソフト編(1)で、GPIOR を変数として使えば、コードが小さくなるのに・・・ということを書いた。アセンブラ化して頑張らなくとも、実際にそうすれば良いのであった。

    i2ckbd.h で、
    #define USE_GPIOR1
    とすると、
      1966 → 1908 と 58 バイトも節約できる。

    これぐらい空くと、ちょっと手直しできるだけの余裕が出来る。

    さて、今まで書いてきたことの中身だが、ここから機能を外してみよう。

    #define SUPPORT_SCANCODE1
    をコメントすると、

      text data bss dec hex filename
      1656 0 69 1725 6bd i2ckbd.elf

    1908 → 1656 だから、252 バイト使用している。そのうち 変換テーブルが 112 バイト。

    代わりに
    #define SUPPORT_ASCIICODE
    をコメントすると、

      text data bss dec hex filename
      1672 0 69 1741 6cd i2ckbd.elf

    こちらは、236 バイトで、同様に変換テーブル 112 バイト。
    しかし 厳しい RAM は減らない。

    #define FN_EXTEND
    FN 拡張を OFF にすれば、

      #undef SUPPORT_SCANCODE1
      text data bss dec hex filename
      1396 0 55 1451 5ab i2ckbd.elf
      #undef SUPPORT_ASCIICODE
      text data bss dec hex filename
      1412 0 55 1467 5bb i2ckbd.elf
      #undef SUPPORT_SCANCODE1
      #undef SUPPORT_ASCIICODE
      text data bss dec hex filename
      1144 0 55 1199 4af i2ckbd.elf

    とここまで減るのであった。

ATtiny2313 は、HID をサポートできるか?

    さて、ASCII と SCANCODE1 を捨てて HID に対応することは可能か?
    実は HID のキーコードを生成する部分だけ作ったのだ。

      #define FN_EXTEND
      text data bss dec hex filename
      1404 0 69 1473 5c1 i2ckbd.elf
      #define FN_EXTEND
      #define SUPPORT_HIDCODE
      text data bss dec hex filename
      1768 0 69 1837 72d i2ckbd.elf

    FN拡張はどうしても欲しい。キーコード生成に 364 バイトでやはり変換テーブルが 112 バイト。

    あとプロトコルが必要なのだが、全然入らないというわけでもなさそう。こうなるとちゃんと作りこみたくなってくるが、問題は割り込みでのスタック消費。これさえクリアできれば、ATtiny2313 でも随分と遊べそうなのである。

C で記述する割り込み

    SIGNAL(SIG_OUTPUT_COMPARE0A) {
    set_F_TIMER_TICK(); (1命令)
    }

    この 1命令だけ実行したい。AVR の場合は、お手軽に記述できるのある。だが、割り込みを使えばレジスタの退避をするので、スタックが消費される。どうなっているのか確認しないと。

    __vector_13:
    push __zero_reg__
    push r0
    in r0,__SREG__
    push r0
    clr __zero_reg__

    sbi 51-32,0 # set_F_TIMER_TICK()

    pop r0
    out __SREG__,r0
    pop r0
    pop __zero_reg__
    reti

    あれ?コードも別に悪いというほどでもない。
    いや問題は、USI の割り込みでどれだけスタックを消費するかということだ、安心してはいけない。

    /* epilogue start */
    pop r24
    pop r0
    out __SREG__,r0
    pop r0
    pop __zero_reg__
    reti
    .size __vector_15, .-__vector_15

    /* epilogue start */
    pop r31
    pop r30
    pop r27
    pop r26
    pop r25
    pop r24
    pop r19
    pop r18
    pop r0
    out __SREG__,r0
    pop r0
    pop __zero_reg__
    reti
    .size __vector_16, .-__vector_16

    USI では、2つの割り込みを使っている。それぞれの、epilogue を見ると、そんなには悪くない。
    といっても、69 バイト使っているから 残りは全部で 59 バイト。これで、main のスタックと割り込みでのスタックを賄わなければならない。割り込みの epilogue だけで 13 バイト の pop ?

    なんか微妙ですな。ひょっとして行けるのか?やっぱり無理なのか?

スタックを使わないところで割り込みを受ける

    ちょっと思いついたのが、割り込みを受ける場所を限定するということ。割り込みを禁止しておいて、アイドルになったら 割り込み許可というアイディア。ただし、main 関数で inline 関数を動かすとスタックフレームを割り当てしまうので、main が call する関数から先で inline を使う。

    こうすることで、とりあえず・・・・・プログラムメモリが増える。

      text data bss dec hex filename
      1978 0 69 2047 7ff i2ckbd.elf

    1908 → 1978 か。これは ... 厳しい。

HID について、全くわかっていないのが Report Descriptor これの実装例を見てみる。

    /* USB report descriptor */
    const PROGMEM char usbHidReportDescriptor[35] = {
    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    0x09, 0x06, // USAGE (Keyboard)
    0xa1, 0x01, // COLLECTION (Application)
    0x05, 0x07, // USAGE_PAGE (Keyboard)
    0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00, // LOGICAL_MINIMUM (0)
    0x25, 0x01, // LOGICAL_MAXIMUM (1)
    0x75, 0x01, // REPORT_SIZE (1)
    0x95, 0x08, // REPORT_COUNT (8)
    0x81, 0x02, // INPUT (Data,Var,Abs)
    0x95, 0x01, // REPORT_COUNT (1)
    0x75, 0x08, // REPORT_SIZE (8)
    0x25, 0x65, // LOGICAL_MAXIMUM (101)
    0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00, // INPUT (Data,Ary,Abs)
    0xc0 // END_COLLECTION
    };
    /* We use a simplifed keyboard report descriptor which does not support the
    * boot protocol. We don't allow setting status LEDs and we only allow one
    * simultaneous key press (except modifiers). We can therefore use short
    * 2 byte input reports.
    * The report descriptor has been created with usb.org's "HID Descriptor Tool"
    * which can be downloaded from http://www.usb.org/developers/hidpage/.
    * Redundant entries (such as LOGICAL_MINIMUM and USAGE_PAGE) have been omitted
    * for the second INPUT item.
    */

    なるほど ... いや分からないのだが、35 バイトということは分かった。

    (追記) これは、レポートが、2 バイトの場合で、作りたいものとは違う。8 バイトの形式は、たぶん次の 41 バイト。

    0x05, 0x01, // USAGE_PAGE (Generic Desktop)
    0x09, 0x06, // USAGE (Keyboard)
    0xa1, 0x01, // COLLECTION (Application)
    /* modifier E0 - E7 */
    0x05, 0x07, // USAGE_PAGE (Keyboard)
    0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00, // LOGICAL_MINIMUM (0)
    0x25, 0x01, // LOGICAL_MAXIMUM (1)
    0x75, 0x01, // REPORT_SIZE (1)
    0x95, 0x08, // REPORT_COUNT (8)
    0x81, 0x02, // INPUT (Data,Var,Abs)
    /* Reserved Byte */
    0x95, 0x01, // REPORT_COUNT (1)
    0x75, 0x08, // REPORT_SIZE (8)
    0x81, 0x01, // INPUT (Constant); Reserved Byte
    /* KeyCode Array [6] */
    0x95, 0x06, // REPORT_COUNT (6)
    0x75, 0x08, // REPORT_SIZE (8)
    0x25, 0x9A, // LOGICAL_MAXIMUM (0x9A)
    0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x9A, // USAGE_MAXIMUM (Keyboard SysRq)
    0x81, 0x00, // INPUT (Data,Ary,Abs)
    0xc0 // END_COLLECTION


HID over I2C 再読

    他に SET_POWER だとか RESET だのコマンドがどうなっているか? もう一度読んでみる・・・と、誤解しているところが見つかった。まずそこから。

    コマンド:GET_REPORT の例

    ホスト: レジスタ(2B:コマンド) コマンド(2B)  レジスタ(2B:データ)
    デバイス: レングス(2B) レポ−ト(8B)

    必ずレジスタのアドレスを WRITE してから READ するのだが、コマンドに続いてもアドレス指定が 来るのであった。
    レポートを送るレジスタは、データレジスタであった。なお、READ での レングスは 全体のサイズで、この場合 10 になる。

    コマンド:SET_REPORT の例

    ホスト: レジスタ(2B:コマンド) コマンド(2B)  レジスタ(2B:データ) レングス(2B) レポ−ト(XB)

    同様に、データレジスタ を指定して 続いてレングス付きのデータが来る。

    コマンド:SET_IDLE

    ホスト: レジスタ(2B:コマンド) コマンド(2B)  レジスタ(2B:データ) レングス(2B:4) レポ−ト(2B)

    レポートは 2 バイトで、レングスは 4 である。

    コマンド:GET_IDLE

    ホスト: レジスタ(2B:コマンド) コマンド(2B)  レジスタ(2B:データ)
    デバイス: レングス(2B:4) レポ−ト(2B)

    コマンドのレスポンスは、すべて データレジスタのようである。
    また、ホストは、レングスだけ READ して、再度 READ するようなことが書いてある。残りを読むみたいなことが書いてあるが、頭から読むような気がする。

    さて、読み込みは全部 レングス付きなのだろうか?
    有効なデータは、すべてそのようである。しかしデータがない場合 0 を返すようである。
    例えば

    コマンド:RESET

    多分本当にリセットすれば良い。
    リセット後、Input Register を READ すると 0x0000 の 2 バイトが読めるようになる。
    ということだそうだ。

    コマンド:SET_POWER

    これは、なにもしない。

現在のサイズ

    いろいろ小変更を繰り返した。いまどうなっているか?チェック。

    HID がターゲットに入ってきたので、意識していく。また、FN 拡張は是非入れたい。その上、スタックのチューニングをありにしないと動く気がしない。この条件でどうなっているか?

      #define STACK_TUNE
      #define USE_GPIOR1
      #define FN_EXTEND

    これが標準

      #define SUPPORT_HIDCODE

      text data bss dec hex filename
      1952 0 68 2020 7e4 i2ckbd.elf

      #define SUPPORT_ASCIICODE
      #define SUPPORT_SCANCODE1

      text data bss dec hex filename
      1976 0 68 2044 7fc i2ckbd.elf

    どちらも一応入っている。2048 までだから、まだ僅かに余裕がある。

    で、すっかり忘れていた機能があった。オートリピートである。HID は関係ないのだが、ASCIICODE と SCANCODE1 の両方をサポートするのは難しくなってきた。

    逆に HID のみとして、ローカルスキャンコードなどもサポートしないのであれば、随分とコードを減らせる。

      1952 → 1750 (202 バイト減)

    i2c 側でのプロトコル解析の部分が減らせると思ったのだが、そこまでしなくとも減ってしまった。なにかの間違いかと思ったがそうではなかった。key_make() という関数でコードを生成するのだが、それが実質空になる。そうすると、引数のために使った key_pressed_ex() という関数が不要になる。
    安易に使った key_pressed_ex() の効率がひどく悪いようだ。

     ・i2ckbd-0.1.zip

    そのあたりを見直しチューニングした。

      ASCII + SCANCODE1 + LOCAL
        1976 → 1798
      HID + LOCAL
        1952 → 1760
      HID only
        1610

    ここまでサイズが減った。これぐらい余裕があるのであれば、オートスキャンぐらいは入れられる。HID についても足りない機能を入れていけそうだ。



RAMの削減

    コードは、随分と楽にはなった。しかし RAM が減るわけではない。GPIOR0-2 は普通に使ってしまったが、他にも使わない機能のレジスタを メモリとして使える。

    随分とせせこましいが、背に腹は代えられぬ。減らさないとオートリピートとか機能の追加が厳しくなる。

      タイマ0 比較Bレジスタ OCR0B
      タイマ1 タイマレジスタ TCNT1L (16bit: TCNT1)
      タイマ1 比較Aレジスタ OCR1AL (16bit : OCR1A)
      タイマ1 比較Bレジスタ OCR1BL (16bit : OCR1B)
      USART ボーレートレジスタ UBRRL (12bit : UBRR)

    このあたりを変数に使ってみよう。

      HID + LOCAL
      text data bss dec hex filename
      1766 0 68 1834 72a i2ckbd.elf
      HID + LOCAL
      #define USE_IOR_AS_MEM
      text data bss dec hex filename
      1708 0 62 1770 6ea i2ckbd.elf

    結果はこう。対応するために、通常コードを少し変更したら 6 バイト増えた。が、割り当てると、メモリが 6バイト 減るだけではなく、コードまで 58 バイト減った。

    これは、確認だけしておいてディスエーブルにしておく。
    #undef USE_IOR_AS_MEM

オートリピート

    RAM を削減できることが確認できたので、 オートリピートを入れることにした。
    追加になる変数
     ・idle_tick 状態が変わらない時間

      main() から 10ms 間隔で keystat_scan() という関数が call される。とりあえず インクリメントするが、なにかキーを押したり・離したりすれば 0 にする。ただし、モデファイアは対象外。

     ・last_key 最後に押された キーコード

      最後に押された ローカルのキーコードを覚えておく。ただし、モデファイアは対象外。
      これは、keystat_scan() の処理で、key_make() という関数が call されるのだが、その処理で行う。
      オートリピートを実行するのは、keystat_scan() の最後で、last_key を元に key_make()を call して行う。

    オートリピートの条件

      ・モディファイア以外のキーが 1つしか押されていない。
      ・最後にキーの状態が変化してから REPEAT_TICK の時間が経過した
      ・あるいは、繰り返しリピートしている場合は、REPEAT2_TICK の時間が経過した

    REPEAT_TICK, REPEAT2_TICK は、define で決め打ちにする。変数にして、設定できるようにするとかは、全部が動いた後考える。

    i2ckbd-0.2.zip
    ここまでを 0.2 とした。

      ASCII + SCANCODE1 + LOCAL
      text data bss dec hex filename
      1752 0 68 1820 71c i2ckbd.elf
      ASCII + SCANCODE1 + LOCAL + AUTO_REPEAT
      text data bss dec hex filename
      1814 0 70 1884 75c i2ckbd.elf

    最後にサイズ確認。これぐらいなら、多少の修正に耐えられそう。

    なお、HID は、関係ない。.... というより関係ないようにしないといけない。あぁダメそう。
    HID では、ASCII , SCANCODE1 , LOCAL のどれもを抑止しないといけない。これは、未着手なのは分かっているのだが、オートリピートもまた抑止しないといけない -- 今気が付いた。

(続く)
posted by すz at 23:36| Comment(0) | TrackBack(0) | I2CKEYBOARD
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント: [必須入力]

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
この記事へのトラックバックURL
http://blog.sakura.ne.jp/tb/183063407
※ブログオーナーが承認したトラックバックのみ表示されます。

この記事へのトラックバック