おーい、ておくれのみんな、聞こえてるか?
最近IT業界は元気ないって?
単価がヤスイ、仕事がオワラナイ、
イマイチなニュースばかりだもんな!
でもクヨクヨしててもしょうがないだろ?
最近IT業界は元気ないって?
単価がヤスイ、仕事がオワラナイ、
イマイチなニュースばかりだもんな!
でもクヨクヨしててもしょうがないだろ?
いまはローレイヤー弄って遊ぶのさ!
カーネル/VM探検隊から、みんなのテンションアガりまくる
最高にホットなAdvent Calendarエントリだ!
「次世代GPIOインターフェイスの製作」
ておくれろ、エンジニアよ
ておくれ狂っちまえよ!
カーネル/VM探検隊から、みんなのテンションアガりまくる
最高にホットなAdvent Calendarエントリだ!
「次世代GPIOインターフェイスの製作」
ておくれろ、エンジニアよ
ておくれ狂っちまえよ!
今年もカーネル/VM Advent Calendarの時期がやってきました。というわけで、この記事は2013年のカーネル/VM Advent Calendar の12/24の記事でーす。恥ずかしながら最近はそれほどローレイヤいじってないので、記事のネタ用に下記の真新しい何かを製作しました。
■ 次世代I/Oインターフェイスの検討経緯
ところで皆さん Ejectコマンドユーザー会ってご存じですかね。OSからejectコマンド一発でCD-ROMドライブのアクチュエータ動かして動物に餌やったり ボタンを操作したり 鐘をついたりするアレです。
最近は「シンプルに動く物を作れる新たな取り組み(でもあたまおかしい)」とRaspberry Pi本家で何度も取り上げられるほどに盛り上がりが続いているテクノロジーで、今年は Advent Calendar も走っています。
Ejectのよいところは、 た ぶ ん 利用するOS上で特定のドライバやインターフェイスに依存せず、今時ならUSBなどでカジュアルにデバイスを接続でき、さらに制御コマンドがOSに最初からついている点にあります。 Raspberry Pi でも Linux で USB スタックが動いているし GPIO があるじゃないと言っても、GPIO がついてないような Windows/Linux/MacOS X マシンなどへも簡単に移植できてしまうという CD-ROM ならではのスゴさがあります。
ただ Eject コマンドを実装したヘルメットはけっこう重いらしく頭おかしく見えてしまうという欠点もあります。CD-ROMドライブは12V電源供給が必要なため単三電池を大量に積んでおり、それだけでも相当な重さに見えます。
ごくごく一般的な Eject おじさん(写真はヘルメット実装前)
RPi blog より拾ってきました
そこで、Ejectの流れに一石を投じてこの @Akkiesoft モテモテな状況を打破するべく、 (☝ ՞ਊ ՞)☝ウイーン に代わる次世代のI/Oインターフェイスを検討/製作することにしました。
3年前に一度USB HIDクラスのデバイスはPIC18F2550で実装したことがあるので、それを久々に引っ張りだしてきました。PIC向けのUSBスタックは Microchip Libraries for Applications に様々な材料があるので、この中のUSBデバイスサンプルをベースに USB 経由にて GPIO ライクな操作が可能なデバイスを製作することにします。
■ day 1 - December 21, Saturday
PICマイコンの選定
MSL の USB Device - Mass Storage - Intl Flash ライブラリ(PIC内のフラッシュメモリを使ったUSB Mass Storageデバイスのサンプル)をコンパイルしてみたところ、メモリ消費量が2000バイトと余裕で超えることに気づきました。残念ながら引っ張り出してきたPIC18F2550のメモリサイズに収まらないようなので、あきらめて上位モデルのPICを購入することにします。
探してみた結果、PIC18F25J50なら問題なさそうで、秋月電子でカジュアルに手に入りそうだという結論に到りました。メモリ量が倍増しているのでUSB Mass Storageサンプルの苦労せずに載りそうですし、USB 2.0も喋れるようです。PIC18F2550の時はUSBを利用する際に外部からクロックを供給する必要がありましたが、PIC18F25J50はMPU内の内蔵クロックだけでUSBも利用できるそうで大変お手軽です!一点玉にキズなのは、駆動電圧が5V→3.3VとなりUSBのバスパワーを使うならレギュレータを通す必要がありそうです。
プロトコルの勉強、というかスニファの準備
既存のUSBプロトコルの通信内容をモニタリングして参考にしたかったので、以下のふたつの方法のスニファを準備しました。
- 手許の VMware Fusion で Windows 7 仮想マシンの USB プロトコルロギングを有効化、 tail -f vmware.log しておく。1KB/分 までなら問題なく sniff できますが、これを超えるスレッショルドがかかるのが難点です。
- Windows 7 VM と Windows 7 実機に USBPcap と Wireshark をインストール。 USBPcapCMD.EXEを実行して sniff 対象の USB ハブを指定すると tcpdump のごとく生ファイルに生データをダンプしてくれます。 Wireshark はこのダンプデータの表示に使います
これらの手段があれば、とりあえずUSBプロトコルで何がおきてるか等は追跡できそうです。
仕事納めした金曜日の夜にここまでの感触を掴んだ私は、土曜日に秋葉原へと繰り出したのでした。
- PIC18F25J50
- I-00534 低損失三端子レギュレータ TA48033S \100
ほかにブレットボードやジャンパなどを購入していますが忘れた。申し訳ねぇ申し訳ねぇ
秋葉原から帰る前に、はんだづけカフェに一時間ちょっと寄って、ブレットボードの上でPIC18F25J50がPICKit2に認識されるところまで組みました。
帰宅後、USB Mass StorageのサンプルをPIC上にロードして実効してみるも「不明なデバイス」としてしか表示されません。こまった。でも眠いのでまた明日。
■ day2 - December 22, Sunday
このまま動かなかったらどうしよう、Advent Calendar向けに違うネタ準備しないとダメかもという脅迫観念に追われながら試行錯誤。
配線上の問題などを潰した結果、ちゃんとデバイスが認識されるようになりました。
サンプルのmain.cにある#pragmaのリストをレビュー。内蔵クロックでUSBが駆動できる設定に見直し
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -117,12 +119,12 @@ | |
#elif defined(PIC18F46J50_PIM) || defined(PIC18F_STARTER_KIT_1) || defined(PIC18F47J53_PIM) | |
#pragma config WDTEN = OFF //WDT disabled (enabled by SWDTEN bit) | |
- #pragma config PLLDIV = 3 //Divide by 3 (12 MHz oscillator input) | |
+ #pragma config PLLDIV = 2 | |
#pragma config STVREN = ON //stack overflow/underflow reset enabled | |
#pragma config XINST = OFF //Extended instruction set disabled | |
#pragma config CPUDIV = OSC1 //No CPU system clock divide | |
#pragma config CP0 = OFF //Program memory is not code-protected | |
- #pragma config OSC = HSPLL //HS oscillator, PLL enabled, HSPLL used by USB | |
+ #pragma config OSC = INTOSCPLL | |
#pragma config FCMEN = OFF //Fail-Safe Clock Monitor disabled | |
#pragma config IESO = OFF //Two-Speed Start-up disabled | |
#pragma config WDTPS = 32768 //1:32768 |
REQUEST SENSEでされたらメディア状態にかかわらずとりあえず PASSED を返してみる(usb_function_msd.c)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -735,8 +837,12 @@ | |
//request. Since this is a removable media device, and the media isn't | |
//present, we need to indicate an error to let the host know (to | |
//check the sense keys, which will tell it the media isn't present). | |
- msd_csw.bCSWStatus = MSD_CSW_COMMAND_FAILED; | |
- MSDCommandState = MSD_COMMAND_WAIT; | |
+ | |
+ // This devicealways notify as PASSED | |
+ // Option: MSD_CSW_COMMAND_PASED(0x00), MSD_CSW_COMMAND_FAILED(0x01) | |
+ msd_csw.bCSWStatus = MSD_CSW_COMMAND_PASSED; | |
+ | |
break; | |
case MSD_INQUIRY: | |
//The handling code for this state is the same with or without media. |
READ CAPACITYの値をハードコードする(usb_function_msd.c)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
case MSD_READ_CAPACITY: | |
{ | |
//The host asked for the total capacity of the device. The response | |
//packet is 8-bytes (32-bits for last LBA implemented, 32-bits for block size). | |
// hard code | |
msd_buffer[0] = 0x02; | |
msd_buffer[1] = 0x01; | |
msd_buffer[2] = 0x00; | |
msd_buffer[3] = 0x00; | |
msd_buffer[4] = 0x82; | |
msd_buffer[5] = 0x00; | |
msd_buffer[6] = 0x00; | |
msd_buffer[7] = 0x00; | |
//Compute and load proper csw residue and device in number of byte. | |
TransferLength.Val = 0x08; //READ_CAPACITY always has an 8-byte response. | |
MSDComputeDeviceInAndResidue(0x08); | |
MSDCommandState = MSD_COMMAND_RESPONSE; | |
break; | |
} |
MODE SENSEも(usb_function_msd.c)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@@ -896,11 +1031,16 @@ | |
MSDCommandState = MSD_COMMAND_RESPONSE; | |
break; | |
case MSD_MODE_SENSE: | |
- msd_buffer[0]=0x02; | |
- msd_buffer[1]=0x00; | |
- msd_buffer[2]=0x00; | |
- msd_buffer[3]=0x00; | |
- | |
+ // Hard code | |
+ msd_buffer[0]=0x02; | |
+ msd_buffer[1]=0x01; | |
+ msd_buffer[2]=0x00; | |
+ msd_buffer[3]=0x00; | |
+ msd_buffer[4]=0x82; | |
+ msd_buffer[5]=0x00; | |
+ msd_buffer[6]=0x00; | |
+ msd_buffer[7]=0x00; | |
+ | |
//Compute and load proper csw residue and device in number of byte. | |
TransferLength.Val = 0x04; | |
MSDComputeDeviceInAndResidue(0x04); |
ResetSenseData()関数でそれっぽい返事を返す(usb_function_msd.c)
INQUIRY のレスポンスを調整(main.c)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Standard Response to INQUIRY command stored in ROM */ | |
const ROM InquiryResponse inq_resp = { | |
0x05, // CD-ROM Device | |
0x80, // removable | |
0x00, // EJECT-IO/ version = 00=> does not conform to any standard, 4=> SPC-2 | |
0x32, // EJECT-IO/ response is in format specified by SPC-2 | |
0x20, // EJECT-IO/ n-4 = 36-4=32= 0x20 | |
0x00, // sccs etc. | |
0x00, // bque=1 and cmdque=0,indicates simple queueing 00 is obsolete, | |
// but as in case of other device, we are just using 00 | |
0x00, // 00 obsolete, 0x80 for basic task queueing | |
{//'M','i','c','r','o','c','h','p' | |
'K','e','r','n','e','l','V','M' | |
}, | |
// this is the T10 assigned Vendor ID | |
{//'M','a','s','s',' ','S','t','o','r','a','g','e',' ',' ',' ',' ' | |
'E','j','e','c','t','-','i','o',' ','v','H','e','l','m','e','t' | |
}, | |
{'0','0','0','1' | |
} | |
}; |
よしよし、意図どおり認識されるようになりました。では今日はランニングに行かないといけないのでまた明日。
■ day3 - December 23, Sunday
イベント発生時のコードを追加。トレイが操作されたらRA0 LEDをチカチカさせて、RA1/RA2 LEDをトグルさせる(usb_function_msd.c)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void cdrom_StartStopUnit() | |
{ | |
int i; | |
// LED! | |
TRISAbits.TRISA0 = 0; | |
TRISAbits.TRISA1 = 0; | |
TRISAbits.TRISA2 = 0; | |
for (i = 0; i < 6; i++) | |
{ | |
PORTAbits.RA0 = 1 - PORTAbits.RA0; | |
Delay1KTCYx(0); | |
Delay1KTCYx(0); | |
Delay1KTCYx(0); | |
} | |
PORTAbits.RA2 = PORTAbits.RA1; | |
PORTAbits.RA1 = 1 - PORTAbits.RA1; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void MSDProcessCommandMediaAbsent(void) | |
{ | |
//Check what command we are currently processing, to decide how to handle it. | |
switch(MSDCommandState) | |
{ | |
case MSD_CDROM_STARTSTOP_UNIT: | |
cdrom_StartStopUnit(); | |
msd_csw.dCSWDataResidue=0x00; | |
MSDCommandState = MSD_COMMAND_WAIT; | |
break; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
void MSDProcessCommandMediaPresent(void) | |
{ | |
BYTE i; | |
BYTE NumBytesInPacket; | |
//Check what command we are currently processing, to decide how to handle it. | |
switch(MSDCommandState) | |
{ | |
case MSD_CDROM_STARTSTOP_UNIT: | |
cdrom_StartStopUnit(); | |
msd_csw.dCSWDataResidue=0x00; | |
MSDCommandState = MSD_COMMAND_WAIT; | |
break; |
実際には細かい変更をもっといれてたりしますが、だいたいこんな感じですね。
最初はLEDひとつでテストしていたので、その時のビデオをご覧ください。
よし、とりあえず完成です。しかし、しかし何かを忘れていますね?
そう、 これだけではいけません。イベントがトリガされた結果何か有益な結果を得ることが重要なのです。ただ単に LED が反応するだけでは片手落ちで、仮にEjectの貴公子から「目的と手段が入れ替わっている」などと苦言を呈されても仕方がないでしょう。というわけで何かのアウトプットを作ることにします。
秋月にPICを買いに行ったときにアッ、これもっと思って買ってきたキットです。ふふふ、まさかこんなに早く出番がくるとはね。こういうキット組み立てるのって小学生か、中学生かぶりですね。たぶん。手順書も親切だったし、がんがんハンダ付けして通電したら一発で動いてくれました。
これをどうやって制御するかですが、、、いやー、キットってやさしいですね。付属されていた回路図によると START/STOP のジャンパーピンは、ジャンパーしていない状態だと入力に電圧がかかっているようです。で、ジャンパーをつなぐとGNDに落ちて電圧が0Vになるんですかね。
ためしにジャンパーピンを一本GNDに落としてみると、たしかに音が鳴ったり止まったりします。
ここでどうするかちょっと悩んだんだけど、電気工作が判らない私としてはトランジスタやリレーを挟むことを考えたけど、PIC18F25J50のデジタル出力ピンから3.3V?が出ていて、しかもこのキット回路も同じ電源を利用しているので、18F25J50側のデジタル出力を直接つないで1/0切り替えればいいんじゃね?と思って試してみたらうまくいったので、それで対応することにします。抵抗とか挟まないといけないんですかね。私にはわかりませんが今晩動いているのでヨシとします。
大変"実用的"となった次世代デバイス Eject-io の全容をご覧下さい。
とりあえずOKそうです。では今日は神田にお買い物にいくのでまた明日。
■ day4 - December 24, Sunday
さて、ここまでの作業は全て Windows の USB スタック相手にやってきたので他のホストともきちんと接続できるか確認する必要があると言っても過言ではありません。試してみたところ Linux でも(多少エラーは出てますが)次世代I/Oが可能なことが確認できました。MacOS XのUSBスタックはちょっとチェックがキビシイようで、もう少しファームウェアそれっぽく書かないとダメなようです。Advent Calendarの当番が12/24でありながら実際に作業にとりかかったのが4日前の夜だったのでさすがにキレイにデバッグしきるまでには到りませんでした。許してください。
また、エネループから電源供給されたRaspberry Piのバスパワーで本デバイスが動作することも確認できました。USBスタックもLinuxベースなので動作上問題なさそうです。
がんばってビデオを撮影しましたのでご鑑賞いただければと。
Windows 7 での動作チェック
CentOS 6.2 の仮想マシン (VMware Fusion 上)での動作チェック
Raspberry Pi + Eject-io で軽量化された Eject ヘルメット
これなら無駄に大きな5インチCD-ROMやバッテリがなくても機器の制御ができるのでヘルメットが軽くなるし、あと12Vのバッテリに苦心することもありませんね!
2014年もEjectは明るいとおもいます。
では、今日は東京ドームに Perfume のライブにみにいくのでまた明日。