日本海の寒冷前線に沿って綺麗に強い雨の地域が並んでいます。
一方で天気図は5時間前のものですが、
といった感じ。日本海の真ん中辺りにあった低気圧が今は石狩湾辺りにあるんですかね。
画像は気象庁のホームページからのものです。誰でも居ながらにしてこういう情報が手に入る。便利な世の中です。
<追伸>
上記は5時間前の午前3時のものでしたが、2時間前の午前6時のものを追加しておきます。
趣味の電子工作などの記録。時にLinuxへ行ったり、ガジェットに浮気したりするので、なかなかまとまらない。
余っていたDIP版のPIC18F14K50とユニバーサル基板でテスト用のボードを作ってみました。
このボードを作ってみたのは、部品が余っていたから・・・というのもありますが、秋月の800円PIC18F14K50ボードとブレッドボードの組み合わせでは不便な点があったからです。それは、
というところからです。双方向でのデータのやりとりを試してみたいので、余っていた気圧センサとI2Cのキャラクタ液晶も載せてあります。気圧センサは液晶の下に置いてあります。(最初の写真は気圧センサから読み取った値を液晶に表示しています)
裏面はこんな感じです。(自分用です。回路図を作っていないのでその代用(^^;。クリックすると特大画像が開きます。)
各部分の説明(自分用です)
ポートアサインは以下の通りとしています。
I2Cバスのアドレスアサインは以下の通りです。
ソフトウェアはとりあえず以下の通りです。
これでLinux環境下でデータのやりとりをUSB経由で好きなようにできそうです。
例によって Microchip Solutionsの中のサンプルプログラムの中にマウスのデモがあったので、これをベースにアナログジョイスティックで操作するマウスエミュレータを作ってみました。
写真が完成した状態です。アナログジョイスティックでマウスカーソルを上下左右に移動させることができ、その下のタクトスイッチ2つはマウスの左クリック・右クリックに対応します。中央やや上のタクトスイッチはBootloaderの起動スイッチです。押しながらUSBケーブルを挿すことでHID Bootloaderが起動します。
USBバスは様々な要因で電圧が変動するので、USBの5Vから以前作った3.3Vレギュレータ小基板で3.3Vを生成してADCの基準電圧とアナログジョイスティックの入力電圧としています。ADCの基準電圧をRB4(AN4=Vref+)に加えて、X軸方向の出力をRB5(AN5)に、Y軸方向の出力をRB6(AN7)に加えています。左右クリックの代わりのタクトスイッチはRB6とRB7に接続し、内部プルアップを有効にしています。RB4にはLEDを接続、RB5にはBootloader用のタクトスイッチを接続していますが、RB5は外部プルアップを行っています。
XY軸はアナログジョイスティックモジュールを基板に載せるときの結線とXYの正負の関係が合わなかったため、内部で読み替えています。
ソフトウェアは上記のハードウェアに合わせた変更と、LEDやSWなどで評価ボードに載っているものに関する制御を取っ払っているのみです。参考になるかわかりませんが、ソースコードをアップロードしておきます。ライセンスは修正部分を除いてオリジナルに従ってください。修正箇所はご自由にどうぞ。なお当然無保証です。PIC18F14K50以外では余分なソースコードは取り除いていませんが一方でコンパイルすらしていませんので、そのつもりで。(修正したソースコード:mouse.c)
そうそう、USBのベンダID、デバイスIDも設定しているファイルを含んでいない=当然変わりませんので、これまたそのつもりでお願いします。
コンパイルはMPLAB-X v1.90とMPLAB C18の組み合わせで、もちろんLinux上です。
設計において今回苦労したのは、コンパイルして生成されたHEXファイルが1000H番地以降にロードするようにリンクされていない、というものです。
もちろん、見た目では
という感じで rm18f14k50_g.lkr ファイルがプロジェクトに取り込まれていますし、ソースコード自身にも384行目付近からベクタを再配置するためのマクロ定義がされていて問題なさそうにみえるのですが、生成されるHEXファイルは0番地付近から生成されている・・・というものです。
・・・・で、悩んで悩んで・・・・ふとMPLAB-Xを再起動したところ、Linker Filesからrm18f15k50_g.lkrが抜けているではないですか・・・。改めてプロジェクトに取り込ませたところ、無事に1000H番地付近からのオブジェクトが生成されて、HID Bootloaderでダウンロード、実行することができました。
しかし、(Linux版だけかもしれませんが)MPLAB-Xはまだこの手の「再起動すると治る」という不具合がたくさんあります。何とかならないもんですかね。趣味で使う分にはまだ納期もないのでいいですが、プロフェッショナル用途では致命的だと思うのですが。
PIC18F4550でブートローダが動いたので、次に本来のターゲットであるPIC18F14K50で動かしてみます。
PIC18F4550のプロジェクトを開きます(自分の場合にはディレクトリごとコピーしてから開いてリネームしています)。次にProjectのPropertyを開き、PIC18F14K50の設定をActiveにして、PICkit3から電源供給するように設定変更します。
ブートローダ用のSWとLEDは io_cfg.h の131行目付近からに記述がありますので、
/** LED ************************************************************/ // #define mInitAllLEDs() LATC &= 0xFE; TRISC &= 0xFE; // #define mLED_1 LATCbits.LATC0 // #define mLED_1_On() mLED_1 = 1; // #define mLED_1_Off() mLED_1 = 0; // #define mLED_1_Toggle() mLED_1 = !mLED_1; #define mInitAllLEDs() LATBbits.LATB4 = 0; TRISBbits.RB4 = 0; #define mLED_1 LATBbits.LATB4 #define mLED_1_On() mLED_1 = 1; #define mLED_1_Off() mLED_1 = 0; #define mLED_1_Toggle() mLED_1 = !mLED_1; /** SWITCH *********************************************************/ // #define mInitAllSwitches() mInitSwitch2(); // #define mInitSwitch2() {ADCON1 = 0x0F;} // #define sw2 PORTAbits.RA3 // #define mDeInitSwitch2() {ADCON1 = 0x07;} #define mInitAllSwitches() mInitSwitch2(); #define mInitSwitch2() {TRISBbits.RB5 = 1; ANSELHbits.ANS11=0;} #define sw2 PORTBbits.RB5 #define mDeInitSwitch2() {TRISBbits.RB5 = 1; ANSELHbits.ANS11=1;}
という感じで、修正します。ここではLEDはポートC0、SWがポートA3に接続する前提だったのを、LEDはポートB4、SWはポートB5に接続するように修正しています。そして、main.cの325行目にある ENABLE_FLASH_SIGNATURE_VERIFICATION の定義はコメントアウトして、SIGNATUREのチェックはしないようにします。
これでビルドを行い、PIC18F14K50に書き込みを行うと、無事にブートローダアプリから認識されるようになりました。
サンプルは例によってCDC Basic Demoを使いました。同様にProjectのPropertyを開き、PIC18F14K50の設定をActiveにします。プロジェクトのディレクトリ内に「microchip_solutions_v2013-06-15/USB/Device – Bootloaders/HID/Firmware – PIC18 Non-J/Linker files for applications」の下にある「rm18f14k50_g.lkr」をコピーして、Projectsの「Linker files」を右クリックして「Add Existing Item…」を選択してプロジェクトに加えます。他にもリンカファイルは存在しますが、今回加えたファイル以外は薄文字になっていることを確認します。
さらに、このままビルドすると「p18f14k50.libが見つからない」というエラーに遭遇します。これはライブラリとして実在するのは「p18F14K50.lib」なのに対して、探しているのが「p18f14k50.lib」と全て小文字のファイル名のためです。ですので、
$ cd /opt/microchip/mplabc18/v3.40/lib /opt/microchip/mplabc18/v3.40/lib $ sudo ln -s p18F14K50.lib p18f14k50.lib
として、大文字でも探せるようシンボリックリンクを張って誤魔化します。(Windowsでは問題にならず、Linuxでは問題になるケース)
これでビルドすると無事に通るようになりました。
ビルドしたアプリケーションは「microchip_solutions_v2013-06-15/USB/Device – CDC – Basic Demo/Firmware/MPLAB.X/dist/LPC_USB_Development_Kit_PIC18F14K50/production」の下に置かれますので、ここにできたHEXファイルをブートローダアプリケーションで指定して書き込んでやると無事にCDC Basic Demoとして動作しました。
PIC18F14K50は割り切って使うにはなかなか便利なデバイスなのですが、MPLAB-X(Linux版)とPICkit3の組み合わせで毎回書き換えて使うには、書き込む度に
というなかなか面倒くさい手順が必要です。理由は、
というためです。
そこで調べていたところ、HIDプロトコルによるブートローダがMicrochipから提供されていることがわかりました。これを使うと、
ということがわかりました。
どこから手をつけるか考えていたところ、CDCクラスのBasic Demoのlkrファイルが「rm18f4550 – HID Bootload.lkr」となっていていかにも対応していそうなファイル名です。中身も、
LIBPATH . FILES c018i.o FILES clib.lib FILES p18F4550.lib CODEPAGE NAME=bootloader START=0x0 END=0xFFF PROTECTED CODEPAGE NAME=vectors START=0x1000 END=0x1029 PROTECTED CODEPAGE NAME=page START=0x102A END=0x7FFF CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED
102A番地から7FFF番地までに配置されそうな感じです。
そこで、手始めにPIC18F4550(AE-18F4550ボード)でブートローダを動かしてみることにしました。
ブートローダのプロジェクトは microchip_solutions_v2013-06-15/USB/Device – Bootloaders/HID/Firmware – PIC18 Non-J/MPLAB.X に収められています。これを開いて、ProjectのPropertyを開いてPIC18F4550の設定をActiveにすることと、PICkit3から電源供給するように設定変更します。
ブートローダ用のSWとLEDは io_cfg.h を見ると68行目付近から記述があり、LEDはPORT D0、SWはPORT B4に接続する前提になっていることがわかります。そこで、ブレッドボード上で適当にLEDとSW(プルアップ抵抗をつけておきます)を配線します。
ビルド自体はこれで難なく通り、書き込みもできました。(コンパイラは例によってMPLABC18です)
18F4550のPORT B4をGNDに落とした状態でUSBポートをホストPCと接続するとLEDがチカチカ点滅します。そして、「microchip_solutions_v2013-06-15/USB/Device – Bootloaders/HID/Firmware – PIC18 Non-J/HIDBootloader(Linux)」を起動すると「Device Attatched」という表示が出て、無事に認識されました。
次にアプリケーションですが、先に調査した通り、CDC Basic Demoで試してみます。
microchip_solutions_v2013-06-15/USB/Device – CDC – Basic Demo/Firmware/MPLAB.X に格納されたプロジェクトを開いて、ProjectのPropertyを開いてPIC18F4550の設定をActiveにしてビルドします。
ビルドして出来上がったHEXファイルはmicrochip_solutions_v2013-06-15/USB/Device – CDC – Basic Demo/Firmware/MPLAB.X/dist/PICDEM_FSUSB/productionの下に格納されています。これをHIDBootloader(Linux)で File → Import Firmaware Image で読み込ませた後、Program → Erase/Program/Verify Device で書き込みます。書き込み後、PORT B4をHレベルになるようにした後、Program → Reset Device でリセットを掛けます。・・・が、再びBootloaderに認識されてしまいました。
うーん、HID Bootloader上のアプリケーション・・・動きません。
ブートローダのファームウェア側main.cを見ると、421行目近辺に
DoFlashSignatureCheck: //Check if the application region flash signature is valid #ifdef ENABLE_FLASH_SIGNATURE_VERIFICATION if(*(rom unsigned int*)APP_SIGNATURE_ADDRESS == APP_SIGNATURE_VALUE) { //The flash signature was valid, implying the previous //erase/program/verify operation was a success. //Go ahead and jump out of bootloader mode into the application run mode _asm goto REMAPPED_APPLICATION_RESET_VECTOR _endasm } //else the application image is missing or corrupt. In this case, we //need to stay in the bootloader mode, so the user has the ability to //try (again) to re-program a valid application image into the device. //We should stay in bootloader mode _asm goto BootMain _endasm #else //Ideally we shouldn't get here. It is not recommended for the user to //disable both the I/O pin check and flash signature checking //simultaneously. Doing so would make the application non-recoverable //in the event of a failed bootload attempt (ex: due to power loss). _asm goto REMAPPED_APPLICATION_RESET_VECTOR _endasm #endif
というコードがあって、APP_SIGNATURE_ADDRESS番地にAPP_SIGNATURE_VALUEが書かれていないと、BootMain(ブートローダのMain)に飛んでいくことがわかります。具体的には、0x1006番地が0x600Dでなければなりません。
そこで、MPLAB IPEでROMの内容を吸い出して確認してみたところ、見事に0xFFFFになっていました・・・。ここをMPLAB IPEで0x600Dに変更して書き戻してみたところ、ブートローダプログラムでは「Detached」になり、さらにGtkTermで /dev/ttyACM0 にアクセスしてCDC Basic Demo(入力した文字が1文字ずれてエコーバックされる)の機能を確認することができました。
つまり、SIGNATURE VALUEが書かれていないことが原因であることがわかりました。
ソースコードの「BootPIC18NonJ.c」の359行目からのProcessIO()を見ると、532行目付近に
case SIGN_FLASH: SignFlash(); BootState = IDLE; break;
というコードがあって、これはホスト(PC)からのコマンド解析部分のようです。ホストから「SIGN_FLASH」というコマンド(ちなみに値は0x09)が来ると、591行目付近のSignFlash()という関数が呼び出されて、該当領域を読み込んだ後、バッファ内のSIGNATUREの部分を書き換えて、書き込む処理を行うように見えます。
つまり、そもそもこの「SIGN_FLASH」というコマンドが来ていないのではないか、ということになります。
こんどはホスト側のソースコードが格納されている「microchip_solutions_v2013-06-15/USB/Device – Bootloaders/HID/software_cross_platform/Bootloader」の下を見てみました。
「SIGN_FLASH」を送出するっぽいコードは、Comm.cppの640行目付近にある、「Comm::SignFlash(void)」という関数のようです。送られていれば、それなりのデバッグメッセージを出すコードのようなのですが、残念ながらビルドできる環境がありません。(方法もわかりません)
この部分を呼び出すのはMainWindow.cppの555行目付近のようです。
if(failureDetected == false) { //Successfully verified all regions without error. //If this is a v1.01 or later device, we now need to issue the SIGN_FLASH //command, and then re-verify the first erase page worth of flash memory //(but with the exclusion of the signature WORD address from the verify, //since the bootloader firmware will have changed it to the new/magic //value (probably 0x600D, or "good" in leet speak). if(deviceFirmwareIsAtLeast101 == true) { comm->SignFlash(); //Now re-verify the first erase page of flash memory. if(device->family == Device::PIC18) {
書き込み後にVerifyをする関数の最後の方で、Verifyに問題がなく、なおかつdeviceFirmwareIsAtLeast101がセットされていれば、呼び出されるようです。で、この値はブートローダファームウェアのバージョンが1.01以上であればセットされるようです。
実際にセットしている箇所は、MainWindow.cppの1150行目付近、GetQuery()という関数・・・GetQueryコマンドに対する応答パケットを解析する部分の中で、
for(int i = 0; i < MAX_DATA_REGIONS; i++) { if(bootInfo.memoryRegions[i].type == END_OF_TYPES_LIST) { //Before we quit, check the special versionFlag byte, //to see if the bootloader firmware is at least version 1.01. //If it is, then it will support the extended query command. //If the device is based on v1.00 bootloader firmware, it will have //loaded the versionFlag location with 0x00, which was a pad byte. if(bootInfo.versionFlag == BOOTLOADER_V1_01_OR_NEWER_FLAG) { deviceFirmwareIsAtLeast101 = true; qDebug("Device bootloader firmware is v1.01 or newer and supports Extended Query."); //Now fetch the extended query information packet from the USB firmware. comm->ReadExtendedQueryInfo(&extendedBootInfo); qDebug("Device bootloader firmware version is: " + extendedBootInfo.PIC18.bootloaderVersion); } else { deviceFirmwareIsAtLeast101 = false; } break; }
bootInfo構造体のversionFlagがBOOTLOADER_V1_01_OR_NEWER_FLAG(これはDeviceData.hの中で0xA5として定義されています)となっているかで判別しているようです。そしてこの構造体は、Comm.hというヘッダファイルで、
#pragma pack(1) struct MemoryRegion { unsigned char type; unsigned long int address; unsigned long int size; }; struct BootInfo { unsigned char command; unsigned char bytesPerPacket; unsigned char deviceFamily; MemoryRegion memoryRegions[MAX_DATA_REGIONS]; unsigned char versionFlag; unsigned char pad[7]; };
として宣言されています。BootInfoは受信したパケットそのもののようなので、WiresharkでUSBのパケットキャプチャをしてみました。
0040Hからが受信したデータの部分で、構造体の構造にそって読んでいくと、赤丸の部分がversionFlagで0xa5になっていることがわかります。つまり、ブートローダのファームウェア側からは正しく情報が上がっているようです。
書き込みの処理の部分全体をUSBパケットキャプチャしてみたところ、最後のパケットは、
となっていて、最後のコマンドは0x07で「GET_DATA」でした。遡っていってもずーっと0x07で、その前が0x06(PROGRAM_COMPLETE)が2つ、その前が0x05(PROGRAM_DEVICE)がずっと続きます。先頭から書いていくと、
という感じで、0x09(SIGN_FLASH)は含まれていません。・・・・そりゃあ、ダウンロードしたプログラムが動くわけがありません。
よくみると、ブートローダアプリケーションに表示される文字列は、ブートローダアプリのソースコードではコメントアウトされているものになっています。つまり、ソースコードはついているけど、それをコンパイルしたアプリケーションがついているわけではないようです。・・・・おいおい。
ちなみに、タイムスタンプをみると、ソースコードのタイムスタンプは2013年6月18日2時37分18秒で、添付されているアプリケーションのタイムスタンプは2013年6月18日2時36分36秒です。・・・・この微妙な逆転、一体何なんでしょうね・・・・。
Qt5をコンパイルする環境はないので、 処置するとしたらブートローダのファームウェア側で処置するしかありません。
簡単に対策するなら、SIGNATUREのチェックを外す、ということになります。
main.c の317行め付近に、
//Never comment this out. If you do, you won't be able to recover if the user //unplugs the USB cable (or power is lost) during an erase/program sequence, //unless you rely on the I/O pin check entry method. The only reason ever to //comment this out, is if you are trying to use this bootloader firmware with //an old bootloader PC application that does not have knowledge of the v1.01 //and newer command set (with QUERY_EXTENDED_INFO and SIGN_FLASH commands). //However, a better solution in such a case, is to upgrade to use a newer PC //application to do the bootloading. #define ENABLE_FLASH_SIGNATURE_VERIFICATION
というコードがあって、コメントには、「ここはコメントするな!」と書いてありますが、そんなことを言っても仕方ありませんので、ここをコメントアウトしてSIGNATUREチェックをやめさせます。コメントの内容によると、書き換え中に電源を切ったりUSBケーブルを抜いたりすると、復活できなくなる・・・・ということみたいです。
ここをコメントしてビルドしたブートローダを書き込んで、PORT B4をGNDに落としてブートローダを起動、ブートローダアプリケーションでCDC Basic Demoを書き込んでみました。その後、PORT B4をHにしてブートローダアプリの「Reset Device」をクリックしたところ、無事にCDC Basic Demoが動作しました。
疲れてしまったので、PIC18F14K50でのトライはまた今度ということで。
HIDブートローダを使う場合の各PICでのリンカスクリプトは「microchip_solutions_v2013-06-15/USB/Device – Bootloaders/HID/Firmware – PIC18 Non-J/Linker files for applications」にあるようです。
連続稼働に向けてラックに吊り下げたRaspberryPiですが、こいつに緊急地震速報を報告させてみました。
Pythonのスクリプトとして作ったのですが、処理としては以下のような感じです。表示および音声合成は別スクリプト&別スレッドにすることで、表示処理と音声読上げを同時に起動し、遅れをなるべく少なくするようにしています。
Twitterのアカウントでeewbotというbotアカウントを運営している方がいて、実験的に高度利用者向けの緊急地震速報をcsvで配信されています。
こちらのtweetをストリーミングAPIで受けとります。
デコードと言っても、都合のいいように表示用と読上げ用のテキストを生成するだけです。
生成したテキストをUSB接続のLCDに表示させます。
RaspberryPiに接続したスピーカに合成音声で出力します。音声合成にはAquesTalk Piを使っています。いわゆる「ゆっくり声」で読み上げます。(笑)
(注:AquestTalk Piは商用利用では有償なので、真似する人はご注意を)
いまのところ、RaspberryPiのスピーカ端子にダイソーの100円スピーカ(ケーブルなし)を挿しているので、音量が小さくて静かな状況でないと聞き取れません。なるべくシンプルかつ小さくアンプを仕込む方法を考えています。
遠めの地震だと地震速報の読上げ&表示後に揺れがきます。比較的近くだと揺れが先か同時くらいですが。音声だけだと読み取れなくても同じ内容がLCDをみれば表示されているので、読上げ完了を待たなくても内容がわかりますし、聞き取れなくてもやはり内容はわかりますので、結構便利です。
ちなみに、日本気象協会の地震情報(tenkijp_jishin)もフォローしていて、その内容もそのまま表示&読上げをしています。こちらは緊急地震速報が落ち着いてしばらくしてからTweetされます。
以降は自分用のメモです。
動かすの使った追加パッケージは以下のとおり。
$ sudo apt-get install python-gd
$ sudo apt-get install ttf-kochi-gothic-naga10 ttf-kochi-mincho-naga10
$ sudo pip install pyserial
「TweetToVoiceLCD.py」を走らせれば動作開始。
先に作った気圧計ですが、連続稼働していたら電池がなくなってました。稼働し始めて約9~10日というところですが、もともと何かの電池として使っていたものを持ってきたので、寿命としては怪しいです。
なので、改めて新しい電池(といっても、ダイソーアルカリ乾電池ですが・・・)を入れてみました。
さて、どのくらいの寿命となるでしょうか。
これまでネットワーク上のRaspberryPiにSSHでリモートログインする場合には、事前にIPアドレスを固定で割り振っておいて、
$ ssh pi@192.168.1.41
とかしていました。しかし、これは結構面倒くさいし、スマートじゃない。RasPiの場合はSDカード側にホスト情報が保持されているので、RasPiが複数台あるとIPアドレスがぶつかりそうになります。
で、簡単に済ます方法が avahi です。調べると、BonjourのOSS版っぽいことが書いてあったり、Zeroconfだぁとか書いてあったりしますが、要は固定IPじゃなくても極めて簡単に名前ベースでsshとかrdpとかが使えるようになります。
インストールは、
$ sudo apt-get install avahi-daemon
です。
RaspberryPi側のホスト名(/etc/hostname)が「raspberrypi」だとすると、インストール後には「raspberrypi.local」で名前解決できるようになります。そうすると、
$ ssh pi@raspberrypi.local
でSSHログインできたり、
$ sshfs pi@raspberrypi.local:/ /mountpoint
でsshfsでマウントできたり、
$ rdesktop -u pi raspberrypi.local
でリモートデスクトップ接続ができます。また、
$ avahi-brouse -ar
でローカルネットワーク上の対応しているサービスの一覧を表示させることができます。
SDカードにホスト名を書いておけば、DHCP環境ではIPアドレスがぶつかる心配をすることなく複数のRaspberryPiを動作させることができます。
※別にRasPiでなくてもBeagleBoneでも同じ手順で対応できます。
調子に乗ってPIC18F14K50の他のサンプルを秋月の800円ボードで動かしてみました。
動作環境はLinuxMint13 + PicKit3です。「MPLAB-X」「MPLAB C18ツールチェーン」「Microchip Library for Applications」はすでにインストールされているものとします。(といっても、ダウンロードして、スーパーユーザー権限でスクリプトを走らせるだけでインストールできますが)
まず、サンプルプロジェクトを開いて初期設定を行います。ここではUSBキーボードのサンプルプロジェクトを動かしてみます。
次にビルド&書き込みを行います。
本来は評価ボードにはスイッチがついているので、そのスイッチを押すと文字が入力されるようなのですが、800円ボードにはそんなものはありませんので、勝手にキー入力されていってしまいます。
何のキーが入力されるかは、keyboard.c の907行目で HID REPORT にキーのコードに相当するusageを設定しています。usageはUSBの仕様書のうち、HID Usage Tableの53頁からの一覧表に乗っていて、4が「A」のキーに相当します。その後の926行目付近ではキーを話した場合のREPORTを送っています。
この辺を読んでソースを弄っていけば、変なキーボード(CTRL + ALT + DEL専用キーボードとか)も作れるはずです。(^^;