USBデバイスをつくってみたい(1) シリアルポートのようなデバイス編



AVR-CDC
-------
http://www.recursion.jp/avrcdc/cdc-ioj.html

* AVRをシリアルポートへの読み書きによって制御できるデバイス。
* 各ポートのON/OFF、Digital/Analogの読み込みなど。
* パソコンとの接続はUSBケーブル。電源もUSB。
* AVRのソフトウエアでUSBプロトコルを処理するのでUSB用のchipが不要。部品がとても少ない。
 * AVR
 * クリスタル
 * コンデンサ3個
 * 抵抗3本
 * ダイオード2個
* BLINK(1)みたいな"USBデバイス"が自作できる！！
* Arduinoでいいのではないか
 * 2-3000円ぐらいするのが負けている感
 * 完成品なのが負けている感
 * USB-シリアル変換の部品が高くついている感
* うちにあったのは88pと20MHzのxtalなのでこれを使いたい



コード修正
--------
cdcio.2009-07-15/mega48/defaultの中のファイルをちょっと変える。

* Makefile
 * MCU = atmega88p
 * F_CPU 12000000UL → 20000000UL
* mega48/main.c
 * "#if _AVR_IOMX8_H_" → "#if _AVR_IOMX8_H_ || _AVR_IOM88P_H_"


make
----
```
% PATH=$PATH:/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin


cdcio.2009-07-15/mega48/default% make
avr-gcc -I".." -I"../../usbdrv"  -mmcu=atmega88p -Wall -gdwarf-2 -DF_CPU=20000000UL -Os -fsigned-char -MD -MP -MT main.o -MF dep/main.o.d  -c  ../main.c
avr-gcc -I".." -I"../../usbdrv"  -mmcu=atmega88p -mmcu=atmega88p -Wall -gdwarf-2 -DF_CPU=20000000UL -Os -fsigned-char -MD -MP -MT usbdrvasm.o -MF dep/usbdrvasm.o.d  -x assembler-with-cpp -Wa,-gdwarf2 -c  ../../usbdrv/usbdrvasm.S
avr-gcc -I".." -I"../../usbdrv"  -mmcu=atmega88p -Wall -gdwarf-2 -DF_CPU=20000000UL -Os -fsigned-char -MD -MP -MT oddebug.o -MF dep/oddebug.o.d  -c  ../../usbdrv/oddebug.c
avr-gcc -I".." -I"../../usbdrv"  -mmcu=atmega88p -Wall -gdwarf-2 -DF_CPU=20000000UL -Os -fsigned-char -MD -MP -MT usbdrv.o -MF dep/usbdrv.o.d  -c  ../../usbdrv/usbdrv.c
avr-gcc -mmcu=atmega88p  main.o usbdrvasm.o oddebug.o usbdrv.o     -o cdcio.elf
avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  cdcio.elf cdcio.hex
avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex cdcio.elf cdcio.eep || exit 0
avr-objdump -h -S cdcio.elf > cdcio.lss
```



fuse
----
どんな設定にすればいいのか。Readme.txtに書いてある。

```
Fuse bits
                   ext  H-L
ATmega48/88/168    FF CE-FF

SPIEN=0, WDTON=0, CKOPT(mega8)=0,
Crystal: Ex.8MHz/PLL(45,461), BOD: 1.8-2.7V
```

* ext=0xFF
bit3-7は未使用なので0xFFも0x07も同じ。

* H=0xCE
bit7:RSTDISBL = 1 RESETピン有効
bit6:DWEN = 1 デバッグWIRE不許可
bit5:SPIEN = 0 直列プログラミング許可
bit4:WDTON = 0 WDTは常時有効
bit3:EESAVE = 1 EEPROMは未保護
bit2:BODLEVEL2 = 1 
bit1:BODLEVEL1 = 1
bit0:BODLEVEL0 = 0 => 1.8Vでリセット

* L=0xFF -> 0xF7
bit3210=1111は12MHzの設定。
ほかは
http://qiita.com/takeru@github/items/b6bd851ed8727d6c8862
と一緒なので0xF7にする。
bit7:CKDIV8 = 1 8分周しない
bit6:CKOUT = 1 クロック出力しない
bit5:SUT1 = 1 
bit4:SUT0 = 1 => 遅い起動(=安定)
bit3:CKSEL3 = 0
bit2:CKSEL2 = 1
bit1:CKSEL1 = 1 => 0.4-20MHz 全振幅発振器
bit0:CKSEL0 = 1 => 外部クリスタル

* まとめ ext=0x07 H=CE L=F7


```
% avrdude -C /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf -P /dev/tty.usbserial-A9005bvI -c avrisp -p m88p -b 19200 -U lfuse:w:0xF7:m -U hfuse:w:0xCE:m

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.06s

avrdude: Device signature = 0x1e930f
avrdude: reading input file "0xF7"
avrdude: writing lfuse (1 bytes):

Writing | ################################################## | 100% 0.06s

avrdude: 1 bytes of lfuse written
avrdude: verifying lfuse memory against 0xF7:
avrdude: load data lfuse data from input file 0xF7:
avrdude: input file 0xF7 contains 1 bytes
avrdude: reading on-chip lfuse data:

Reading | ################################################## | 100% 0.02s

avrdude: verifying ...
avrdude: 1 bytes of lfuse verified
avrdude: reading input file "0xCE"
avrdude: writing hfuse (1 bytes):

Writing | ################################################## | 100% 0.02s

avrdude: 1 bytes of hfuse written
avrdude: verifying hfuse memory against 0xCE:
avrdude: load data hfuse data from input file 0xCE:
avrdude: input file 0xCE contains 1 bytes
avrdude: reading on-chip hfuse data:

Reading | ################################################## | 100% 0.02s

avrdude: verifying ...
avrdude: 1 bytes of hfuse verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.
```



hex書き込み
---------
```
% avrdude -C /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/avrdude.conf -P /dev/tty.usbserial-A9005bvI -c avrisp -p m88p -b 19200 -U flash:w:cdcio.hex

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.06s

avrdude: Device signature = 0x1e930f
avrdude: NOTE: FLASH memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "cdcio.hex"
avrdude: input file cdcio.hex auto detected as Intel Hex
avrdude: writing flash (3686 bytes):

Writing | ################################################## | 100% 6.47s

avrdude: 3686 bytes of flash written
avrdude: verifying flash memory against cdcio.hex:
avrdude: load data flash data from input file cdcio.hex:
avrdude: input file cdcio.hex auto detected as Intel Hex
avrdude: input file cdcio.hex contains 3686 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 4.27s

avrdude: verifying ...
avrdude: 3686 bytes of flash verified

avrdude: safemode: Fuses OK

avrdude done.  Thank you.
```


だめ
---
"コンソール"に
```
2014/05/15 13:42:16.000 kernel[0]: USBF:    87733. 18    The IOUSBFamily is having trouble enumerating a USB device that has been plugged in.  It will keep retrying.  (Port 4 of Hub at 0x1d110000)
2014/05/15 13:42:16.000 kernel[0]: USBF:    87733. 18    The IOUSBFamily was not able to enumerate a device.
```

こんなログが繰り返しでている。なにかだめっぽい。




ビルド済みのcdcio88.hexでやってみる
---------------------
クリスタルは12MHzに変更。fuseもL=0xFFで、全てデフォルトの設定。

```
2014/05/15 14:21:35.000 kernel[0]: AppleUSBCDCACMData: Version number - 4.2.1b5, Input buffers 8, Output buffers 16

2014/05/15 14:21:35.000 kernel[0]: AppleUSBCDC: Version number - 4.2.1b5

2014/05/15 14:21:35.645 UserEventAgent[289]: add service for USB-PIO

```
OKっぽい反応。



```
% ls -l /dev/tty.*
crw-rw-rw-  1 root  wheel   18,   0  5 14 01:01 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw-  1 root  wheel   18,   2  5 14 01:01 /dev/tty.Bluetooth-Modem
crw-rw-rw-  1 root  wheel   18,  42  5 15 14:26 /dev/tty.usbserial-A9005bvI

% ls -l /dev/tty.*
crw-rw-rw-  1 root  wheel   18,   0  5 14 01:01 /dev/tty.Bluetooth-Incoming-Port
crw-rw-rw-  1 root  wheel   18,   2  5 14 01:01 /dev/tty.Bluetooth-Modem
crw-rw-rw-  1 root  wheel   18,  56  5 15 14:30 /dev/tty.usbmodem1d1141
crw-rw-rw-  1 root  wheel   18,  42  5 15 14:26 /dev/tty.usbserial-A9005bvI
```

接続すると、"/dev/tty.usbmodem1d1141"というのが増える。


しかし、コマンドには反応なし。
screen /dev/cu.usbmodem1d1141 9600
とか
screen /dev/tty.usbmodem1d1141 19200
して"@"を送っても反応しない。




ドライバ入れ替え
-------------

「AppleUSBCDC tty」で検索すると
http://www.omiya-giken.com/?page_id=1196
が見つかった。


```
(入れ替える前)
# kextstat | grep USBCDC
  183    0 0xffffff7f8264d000 0x4000     0x4000     com.apple.driver.AppleUSBCDC (4.2.1b5) <35 4 3>
  184    0 0xffffff7f826c8000 0x4000     0x4000     com.apple.driver.AppleUSBCDCACMControl (4.2.1b5) <35 4 3>
  185    0 0xffffff7f826cc000 0x7000     0x7000     com.apple.driver.AppleUSBCDCACMData (4.2.1b5) <119 35 5 4 3>

(入れ替えた後)
% kextstat | grep USBCDC
  189    0 0xffffff7f8264d000 0x3000     0x3000     com.apple.driver.AppleUSBCDC (3.1.4) <119 35 5 4 3>
  190    0 0xffffff7f826c8000 0x7000     0x7000     com.apple.driver.AppleUSBCDCACMControl (3.1.4) <119 35 5 4 3>
  191    0 0xffffff7f826cf000 0x9000     0x9000     com.apple.driver.AppleUSBCDCACMData (3.1.4) <119 35 5 4 3>
  192    0 0xffffff7f826d8000 0x5000     0x5000     com.apple.driver.AppleUSBCDCECMData (4.2.1b5) <41 35 5 4 3 1>

```

結局だめ。元に戻した。





Macのドライバが悪いの？ → ハブでした。
--------------------------------
Raspberry PI + ハブでOK.
MacBook Airの直接接続でOK.

ハードの組み合わせでOK/NGがある。
iMac Direct : OK
iMac + Hub(U2H-TM410BWH) : NG
MacbookAir Direct : OK
Raspberry PI + Hub(U2H-TM410BWH) : OK

http://www.recursion.jp/avrcdc/faqj.html
ここにちゃんと書いてありました。
USB2.0のポートにUSB2.0のハブをつけてAVR-CDCはうまく動かない。
直接接続 or USB1.1のハブを挟むといいらしい。
でも、Raspberry PIはハブ経由でOK直接はNGだ。

信号の電圧の違いだったりするのか、ソフトウエア的な問題なのか？よくわからないなあ。


```
lsusb -v

...
Bus 001 Device 006: ID 16c0:05e1 VOTI CDC-ACM class devices (modems)
Couldn't open device, some information will be missing
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               1.10
  bDeviceClass            2 Communications
  bDeviceSubClass         0
  bDeviceProtocol         0
  bMaxPacketSize0         8
  idVendor           0x16c0 VOTI
  idProduct          0x05e1 CDC-ACM class devices (modems)
  bcdDevice            1.00
  iManufacturer           1
  iProduct                2
  iSerial                 0
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           67
    bNumInterfaces          2
    bConfigurationValue     1
    iConfiguration          0
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           1
      bInterfaceClass         2 Communications
      bInterfaceSubClass      2 Abstract (modem)
      bInterfaceProtocol      1 AT-commands (v.25ter)
      iInterface              0
      CDC Header:
        bcdCDC               1.10
      CDC ACM:
        bmCapabilities       0x02
          line coding and serial state
      CDC Union:
        bMasterInterface        0
        bSlaveInterface         1
      CDC Call Management:
        bmCapabilities       0x03
          call management
          use DataInterface
        bDataInterface          1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x83  EP 3 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval             255
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        1
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass        10 CDC Data
      bInterfaceSubClass      0 Unused
      bInterfaceProtocol      0
      iInterface              0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               0
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            2
          Transfer Type            Bulk
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0008  1x 8 bytes
        bInterval               0
```


ソフトウエア
----------
とりあえず、動くハードウエアがわかったのでソフトウエア側を進める。
* 何も変えずにmega88でmake 88pに書き込み → OK
* 最新のv-usb + mega88でmake → OK
* 最新のv-usb + mega88pでmake → OK
* 最新のv-usb + mega88p + 20MHzでmake → OK




規格外
------
host側が/dev/tty*を読み書きするだけなのでお手軽なのだがハブやポートの組み合わせで動かないというのはなんだかなあ。

* http://www.recursion.jp/avrcdc/indexj.html
 * 「Low-Speed での ３つのエンドポイント生成とバルク転送は規格外の動作となる」
 * 「ロースピードのバルク転送モードは新 OS では動かせなくなりつつあります。USB 技術の実験材料として楽しんだ後は、HID モードで使うか、コントローラ内蔵マイコンに移行してください。」
* http://vusb.wikidot.com/usb-device-classes
 * "Consumes ca. 90% or more of the AVR's CPU time because bulk endpoints are polled aggressively by the host. Real-time applications on the AVR are close to impossible."
* https://github.com/larskanis/libusb
 * LIBUSB is a Ruby binding that gives Ruby programmers access to arbitrary USB devices.


HIDを試してみよう。


まとめ
-----
* 遠回りしたけどUSBデバイスをつくることに成功
* 規格外なので動いたり動かなかったりするのがいまいち
* HID+Ruby+libusbを試してみたい


つづき→ http://qiita.com/takeru@github/items/42873e1a7e0aef830eea