アプリケーションの作成の前に
最初に、通常のアプリケーションのプロジェクトを作成します。 その後下記の設定を加えることで、HID ブートローダに対応したアプリケーションを作成できます。
1. リンカにて、Codeoffsetを変更する。
MPLAB C IDEの[Project Properties]を開いて、[XC8 linker]-[Addition options] を変更します。
Codeoffsetは、プログラムの開始位置を変更します。 通常はアドレスの"0x000"から始まります。 Codeoffsetを「0x800」にするとアドレスの"0x080"から始まるようになります。
Microchip純正のHIDブートローダを使用する場合は、Codeoffsetを「0x1000」とします。
2. リンカにて、ROM rangesを変更する。
次に、[XC8 linker]-[Memory model] を変更します。
ROM rangesは、使う領域を(メモリ)を指定します。 ROM rangesを「default,-0-7FF,-806-807,-816-817」に指定します。
- default : 全領域
- -0-7FF : 0x000~0x07FF を除く(ブートローダー領域)
- -806-807 : 0x0806~0x0807 を除く(GOODサイン領域)
- -816-817 : 0x0816~0x0817 を除く(アプリケーションバージョン領域)
という意味になります。
Microchip純正のHIDブートローダを使用する場合は、ROM rangesを 「default,-0-8FF,-1006-1007,-1016-1017」とします。
3. HID ブートローダをロードする (オプション)
アプリケーションに、HID ブートローダーを組み込む作業です。 Pickit3等のライタで、ブートローダ付きアプリケーションを書き込みたいときに使います。 HID ブートローダーのみで書き換える場合は不要です。
[Projects]タブ-[Loadables] を右クリックすると[Add Loadable Project...]または[Add Loadable File...]があります。
[Add Loadable File...]は、HID ブートローダのHexファイルを指定します。
通常は、こちらを使います。
[Add Loadable Project...]は、左のダイアログがてて、プロジェクト(BOOTLOADERr.X)とアプリケーションに対応する コンフィグレーションを選択します。
HID ブートローダの変更(configuration bitの変更等)があるときは、こちらを使ったほうが便利です。 ただし、アセンプルの時間が増えます。
サンプル アプリケーション
USB HID カスタム通信のサンプル アプリケーションです。
HID sample (HID Bootloader (pic16Non-J対応) Restart Address:0x0800)
ヘッダファイル
- usbconfig.h
- アプリケーション固有のUSB設定を行います。
- usbdesc_Custom.h
- USBのディスクリプタ(HIDのカスタム)を設定を行います。
- usbhiddev.h
- usbhiddev.cのヘッダファイルです。
この3種ヘッダファイルは、usbconfig.h -> usbdesc_Custom.h -> usbhiddev.h の流れで1つにまとまっています。
usbconfig.h と usbdesc_Custom.h は、アプリケーションによって変更しなければなりません。 usbhiddev.hは、USB通信の本体のヘッダなので変更はしません。
ソースファイル
- main_pic18f14k50.c
- メインプログラムです。(ファイル名を変更しました。2018/04/03)
- usbhiddev.c
- USB HID通信のプログラム本体です。
ブートローダーHexファイル
- pic18f14k50_bootloader.X.production.hex
- アプリケーションにブートローダー組み込む為のファイルです。
メインプログラムの解説を少し。
このサンプルは、HIDで通信処理を行います。64byte/msで通信できます。 512kbps相当かな?
これができれば、PCとやり取りができます。 PC側のプログラムも必要ですけど、HIDなのでドライバが標準のものが使えます。 (ドライバを自前で用意する必要がない。)
通信は、SIO同様に必要な時にデータを送ります。 有効なデータ量が可変なので、この方法を取っています
ちなみにHID キーボードやマウスは、データの変更があった時に加え一定間隔にデータを出しています。 データ抜けを防ぐには有効です。
コードの一部の解説を
1. configuration bitブートローダと同じにする。
// Configuration /////////////////////////////////////////////////////////////// // HIDブートローダーと同一コンフィグレーションにしてください。 // pic18f14k50_xc8_bootloader.hexに対応 #pragma config CPUDIV = NOCLKDIV // CPU システムクロック選択ビット #pragma config USBDIV = OFF // USB クロック選択ビット #pragma config FOSC = HS // オシレータ選択ビット #pragma config PLLEN = ON // 4 X PLL イネーブルビット : : #pragma config EBTR0 = OFF // テーブル読み出し保護ビット #pragma config EBTR1 = OFF // テーブル読み出し保護ビット #pragma config EBTRB = OFF // ブートブロック テーブル読み出し保護ビット
PICには、ブートローダーが書き込まれており"configuration bit"は、変更できないためです。 ブートローダーと同じ"configuration bit"を記入してください。(ErrorとかWarring等になります。)
アプリケーションに"configuration bit"を書かないするwebページも見受けられます。 必ずブートローダーを使う場内には有効です。 ただし最終的にアプリケーション単体で書き込む事を考えていなるから記述してください。
2. USB割り込み処理
//////////////////////////////////////////////////////////////////////////////// // USB割り込み処理部処理部 // 引 数 : 無し // 戻り値 : 無し // USBのプラグ&プレイ関連の処理を行っています。必須です。 //////////////////////////////////////////////////////////////////////////////// void interrupt low_priority IsrLow(void) { // USB割り込み処理 USBInterruptTasks(); }
USBの通信処理(主にプラグ&プレイ関連)を割り込みで行います。 通常の通信も割り込みますがフラグ処理するのみです。
時間的制約が少ないので、低レベル割り込みにします。 基本的に1mS毎の割り込みがあります。
USBの通常データ通信は、ProcessIO関数内で送受信します。
2. アプリケーションからHID ブートローダへの行き方
//////////////////////////////////////////////////////////////////////////////// // ブートローダー処理部 // 引 数 : 受信データバッファポインタ // 戻り値 : 無し // sw2または受信コマンドによりHIDブートローダーへ移行します。 // PIC 新型2KB版 HID Bootloaderに対応しています。 //////////////////////////////////////////////////////////////////////////////// void bootloader(uint8_t* pbuf) { if (!sw2 || (pbuf != 0 && *(pbuf+0) == 'B' && *(pbuf+1) == 'o' && *(pbuf+2) == 'o' && *(pbuf+3) == 't')) { INTCONbits.GIEL = 0; // 低割り込み禁止 INTCONbits.GIEH = 0; // 高割り込み禁止 LATA = 0; // ポートAクリア LATB = 0; // ポートBクリア LATC = 0; // ポートcクリア TRISA = 0; // ポートA初期化 TRISB = 0; // ポートB初期化 TRISC = 0; // ポートC初期化 uint16_t cnt = 0xFFFF; while (cnt--) { ClrWdt(); UCON = 0; // ウェイト付きUSB停止 } asm("goto 0x001C"); // bootloader Entryへ } }
サンプルでは、sw2または、通信文の[Boot]にてHID ブートローダに移行します。
ROMアドレスの0x1CにHID ブートローダのエントリ(入口)が設定してありますので必ずインラインアセンブラでジャンプします。 ジャンプ先で、スタックポインタやバンク等の処理をしますので、気にせず飛びます。
このエントリはMicrochip純正のHIDブートローダにもありますので、同じように使えます。
気にして欲しいのは、各機能を停止することです。A/DやSIOを使ってるなら必ず初期状態にすることです。 割り込みも禁止して下さい。内部は停止していても外部割込みとかあると困ります。 ポートも初期化していますが、これはアプリケーション次第で安全に停止できる状態ならOKです。
「// ウェイト付きUSB停止」がありますが…PC本体の切断/接続待ちの状態を確保するためです。 長い分には問題ないです。
3. USBステータス処理 (サスペンド処理等)
//////////////////////////////////////////////////////////////////////////////// // USBステータスによる各処理部 // 引 数 : 無し // 戻り値 : 無し //* サスペンド処理 // サスペンド時にスリープします。 //////////////////////////////////////////////////////////////////////////////// void MainUsbStatus(void) { // USB ステータス表示処理 /////////////////////////////////////////////////// : : // サスペンド処理 /////////////////////////////////////////////////////////// if (USBGetDeviceState() == CONFIGURED_STATE && USBIsSuspended() ) { // メインのサスペンド移行処理部 : : // サスペンド中何もせずスリープする while (!USBIsActive() && !RCONbits.TO) { Sleep(); } // メインのウェイクアップ処理部 : : } }
USBのステータスを見て処理する部分です。 基本敵にサスペンド時のメイン処理を行います。
スリープ状態を解除するのは、ウォッチドックタイマです。 スリープ状態をUSB割り込みで解除するには、電力管理ををIDLE モードにする必要があります。 このサンプルでは、USB割り込みでの解除は、使っていません。
使い方 (複数プロジェクト)
現在(2018/04/6時点)ブートローダーの変更に伴い確認をしています。
その為HID Bootloaderの旧版(Ver3.12)をそのまま載せています。 少々お待ちください。
サンプルプログラム 2016/03/12
上記サンプル同様で修正してあります。
Complexed HID sample (HID Bootloader 2KB版 Restart Address:0x0800)
複数のプロジェクトを扱う方法です。 簡単に言うとHIDBootloader とHID sampleを同時にアセンブルとコンパイルが行えるようにしたものです。 ついでにAE-PIC18F14K50も扱えるようにしています。
include directories
ヘッダファイル(.h)が複数のフォルダに分かれている時、ここにそのフォルダ名を記載するとうまくいきます。 直接 #include にフォルダ名付きで書いてもOKですが、こちらの方が使い勝手が良いです。
PC側 プログラム
HID コンソール プログラム (Visual Studio Community 2015 / Visual C++にて作成)
コンソールプログラムとソース ファイルを同封しています。 Visual Studio Community 2015 に開発環境を移しました。 ソースファイルは、変更有りません。
setupapi.h - 2015/11/29 誤記修正 -
WindowsでHIDにてデータを受け渡すには、<setupapi.h>をインクルードして、setupapi.libを登録します。
ここら辺までは、"Device - HID - Custom Demos"を見れば判るのですが…。 WriteFlie/ReadFileのハンドル取得までが大変。 一応複数同一ベンダーID/プロダクトIDが有ってもハンドル取得可能なようですが試してません。 (Ver1.5で複数同一ベンダーID/プロダクトIDを取得可能にしてみましたがテストしてません。)
もう少しHIDに深く入り込みたいならhid.libを登録して"hidsdi.h"をインクルードしてって思いますが、 SDKを探して来ないと行けないし今回は殆ど使うことないから止めました。
バッファサイズは65byte
HIDは、固定バイトのデータの受け渡しをします。 その為の約束事をデバイスが"HID Report Descriptor"を送って決めています。 ブートローダーでは、一回のレポートを送るのに64byteと決めています。 5byteでも200byteでも構わないのですが、エンドポイントと同じサイズの64byteが都合がよいのでしょう。
"HID Report Descriptor"の情報からWriteFlie/ReadFileのバッファサイズが決まるようです。 64byteでは無くて1byte付加されて65byteです。 1byte目に0x00を入れて残りがデータバッファです。
"hidpi.h"にあるHidP_GetCaps関数を使うとバッファサイズ(65byte)とが取れるらしいのですが試していません。 製造者名とか製品名も取り出したくなると思うので、hid.libを登録して試してみるかもしれません。 (Ver1.5でHidP_GetCaps関数を取得するようにしました。8Byteでも32Byteでも多分対応できてると思います。)
1mS間隔で読み出し?
データはインターラプト転送なので、1mS間隔毎に受信/送信されています。 受信はReadFileで行ないますが、同期処理なら有効なデータが来るまで待ってしまいます。 その間アプリケーションは何もできず止まった状態になります。
今回は、別スレッドにしてアプリケーション自体は止まらない状態にしています。 ただ別スレッドでも止まった状態なので、終了時には空のデータをWriteFlieにて送ってエコーバックを貰って終了します。 (Ver1.5でReadFileの非同期処理に完全対応しました。のでエコーバック処理は不要になりました。)
他の方法として、ReadFileの非同期処理があります。 有効なデータが無ければすぐ戻ってくるので1mS毎に確認しに行くと言う手も有るかもしれません。 (Ver1.5でReadFileの非同期処理に完全対応しました。)
Ver1.5で、受信を同期処理/非同期の両方を扱えるようにしました。 完全な1送信対1受信処理を行なうときだけ同期処理に、それ以外は非同期処理に出来ます。
免 責
情報は出来るだけ正確に書くつもりです。ただこの記事を見て作ると思ったときは、 個人の責任において作業を行なってください。 データの喪失や機器の損傷が有っても、一切の責任は取れません。