USB(デバイスモード) HIDクラス
PIC-PC間 IO制御
(LEDのOnOff、SW・VR電圧読込)
(→プロジェクトファイル(Harmony Ver.2.04版 ) ダウンロード)
・ USB (デバイスモード)のHIDクラスを用いて PICとPC間でI/Oを制御する例を紹介します。 I/O制御は要約すると以下の3つです。 ① PC側のボタンスイッチをクリックして PIC側のLEDをON/OFFする。 ② PIC側スイッチのON/OFFの状態をリアルタイムでPC側のウィンドウに表示する。 ③ PIC側の可変抵抗器による可変電圧値をリアルタイムでPC側の制御ウィンドウに表示する。 尚、PC側のアプリケーションソフトはVC++(Visual Studio 2012) 及びVC#(Visual Studio 2017)で作成しています。 ・ 本サンプルプログラムはマイクロチップの以下2つのプログラムを参考にして作成しています。 ① Harmonyのhid_basic "C:\microchip\harmony\v2_04\apps\usb\device\hid_basic" ② MLAのDevice - HID - Custom Demos "C:\microchip_solutions_v2013-06-15\USB\Device - HID - Custom Demos\Simple Demo - Windows Software\Microsoft Visual C++ 2005 Express" ・ USB(デバイスモード)HIDクラスを使ったプログラムを実装したPICでWindows PCにアクセスする場合、USBドライバーは特に作成する必要はありません。 Windowsがキーボードやマウス用に使用しているUSB(HID)ドライバーがそのまま使えます。 ・ PC側プログラムは を参照してください。
|
||||||||||||||||||||
<仕様> ・PCとPIC間のUSB通信はHIDクラスとする。 ・PICはPIC32MZ2048EFH100を使用のこと。 ・PC側のプログラムは、マイクロソフトのVC++で作成のこと。 ・PC側はダイアログウィンドウを作成して、以下を実施できること。
① PIC側のLEDのON/OFF制御 ② PIC側のスイッチの状態のモニタ ③ PIC側の可変抵抗器の電圧もモニタ ・ダイアログウィンドウのボタンスイッチ(コントロール)のクリックにより、PIC側のLEDがON/OFFできること。 ・ボタンスイッチの背景色はLEDがONの場合は赤色、LEDがOFFの場合は灰色とする。 ・ボタンコントロールはLEDがONの場合は赤色に、OFFの場合は灰色とする。 ・LEDがONの場合は画像 ![]() ![]() ・PIC側のスイッチのON/OFF状態が分かるモニターとして 円形のオーバルシェープコントロールを使用のこと。 尚、色はONの場合はオレンジ色、OFFの場合は灰色とする。 ・スイッチがONの場合は画像 ![]() ![]() ・PIC側の可変抵抗器の電圧値に表示のこと。値は四捨五入して小数点以下2桁を表示のこと。 ・入力電圧値がビジュアルにわかるようにプログレスバーでも表示のこと。 ・途中切断も含め、USB通信がOFFの場合はLED,、スイッチ、可変抵抗器などに係る表示は行わないようにすること。 ・PCのアプリケーションは100msec毎にPIC側の状態をサンプリングのこと。 ・ PC側のダイアログウィンドウ例を以下に示す。
・PIC側のモニタ用液晶として、I2C制御のキャラクタ液晶を用いること。 ・PICが起動する時、キャラクタ液晶には以下を表示のこと。 1行目: USB device HID 2行目: start !! ・USB通信が始まったら液晶に可変抵抗器の入力電圧をモニタする。 1行目: AdValue= ◯◯◯◯ 2行目: Volt= ◯◯◯◯[V] AdValueの〇〇〇〇は内臓ADコンバータの読み込み整数値、Voltは電圧換算値で小数点以下3桁を表示のこと。 ・可変抵抗器の電圧のサンプリングタイムは50msecとする。 ・USBケーブルが送受信中に抜かれたりして通信が切断された場合は LED、スイッチ、可変抵抗器のPictureBoxはDisableとなり ダイアログウィンドウ画面は 上記の <USB通信OFFの場合>の画面に戻ること。 <回路図> (→ PDFファイル)
<外観> PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。 汎用モジュール評価ボード(段積みボード)には本テーマと関係ない部品が多々実装されています。 |
<動作結果> (→ 動画:1080pのHD動画を見ることができます。)
<解説> 以下に、記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います
(以下は、Harmony v2.04 をもとに作成しています。最新のバージョンとは異なることがあるかもしれませんので注意してください。)
■ MHC設定
■ Options
■Pin Settings
項目 | ポート設定(その1) | ポート設定(その2) | ポート設定(その3) |
MHC | ![]() |
![]() |
![]() |
備考 | RG15: 出力モード設定 RB5: アナログ入力モード |
RB0: 入力モード、プルアップ設定 | I2Cポート設定 RA2: SCL2 RA3: SCLA |
■ I2Cインターフェースのキャラクタ液晶表示ライブラリの追加要領
キャラクタ液晶表示のライブラリ lcd_ACM1602_lib_i2c.h と lcd_ACM1602_lib_i2c.cを main.cと同じフォルダにコピーして、
プロジェクトに追加します。
(使い方 → I2Cインターフェースキャラクタ液晶表示制御 参照)
プロジェクトへの追加は以下のようにします。
■app.h を以下の要領で変更します。
①列挙型変数 APP_STATES の定義を以下のように変更し、所要の変数を定義ます。
typedef enum
{
/* Application is initializing */
APP_STATE_INIT,
/* Application is waiting for configuration */
APP_STATE_WAIT_FOR_CONFIGURATION,
/* Application is running the main tasks */
APP_STATE_MAIN_TASK,
/* Application is in an error state */
APP_STATE_ERROR
} APP_STATES;
②構造体 APP_DATAのメンバーを以下のように変更し、宣言します。
typedef struct
{
/* The application's current state */
APP_STATES state;
/* Device layer handle returned by device layer open function */
USB_DEVICE_HANDLE usbDevHandle;
/* Recieve data buffer */
uint8_t * receiveDataBuffer;
/* Transmit data buffer */
uint8_t * transmitDataBuffer;
/* Device configured */
bool deviceConfigured;
/* Send report transfer handle*/
USB_DEVICE_HID_TRANSFER_HANDLE txTransferHandle;
/* Receive report transfer handle */
USB_DEVICE_HID_TRANSFER_HANDLE rxTransferHandle;
/* Configuration value selected by the host*/
uint8_t configurationValue;
/* HID data received flag*/
bool hidDataReceived;
/* HID data transmitted flag */
bool hidDataTransmitted;
/* USB HID current Idle */
uint8_t idleRate;
} APP_DATA;
以下、app.h
■ app.cに、青字部の修正、追加をします。
①インクルードファイルを追加します。stdio.hがないと、sprintf( )がコンパイラのバージョンによってコンパイルでエラーが
でることがあります。
#include "stdio.h"
②USB送受信バッファーや液晶表示のバッファー、また制御用の各種変数などを定義します。 USBの送受信バッファーメモリの
配置は16バイト単位となっています。
APP_DATA appData;
char Buf[32];
#define APP_MAKE_BUFFER_DMA_READY __attribute__ ((coherent, aligned(16)))
//16バイト単位で配置
uint8_t receiveDataBuffer[64] APP_MAKE_BUFFER_DMA_READY; //受信バッファー
uint8_t transmitDataBuffer[64] APP_MAKE_BUFFER_DMA_READY; //送信バッファー
……
……
③NOPによる遅延関数delay_us(), delay_ms()を定義します。
void delay_us(volatile unsigned int usec) //1μsec遅延
{
volatile int count;
count = (int)(delay_Clock/20000000)*usec;
……
……
④DRV_TMR_AlarmRegister ( )から50msec毎に呼び出され、入力電圧のAD変換を行うコールバック関数を定義します。
void tmrISR(uintptr_t context, uint32_t alarmCount ) //タイマ1コールバック関数
{
//AD変換開始 Trigger a conversion
PLIB_ADCHS_GlobalSoftwareTriggerEnable(ADCHS_ID_0);//変換開始(グローバルソフトウェアエッジトリガの場合)
……
……
⑤USBデータ送受信バッファーの補助関数です。
データ送信済みフラグを偽として、ホストへの送信データをバッファーにセットしてホストへ送信要求を出します。
また、データ受信済みフラグを偽として、受信バッファを空にしてホストへ受信要求をだします。
void BufferRW(void)
{
appData.hidDataTransmitted = false;
/* Prepare the USB module to send the data packet to the host */
USB_DEVICE_HID_ReportSend (USB_DEVICE_HID_INDEX_0,
&appData.txTransferHandle, appData.transmitDataBuffer, 64 );
appData.hidDataReceived = false;
/* Place a new read request. */
USB_DEVICE_HID_ReportReceive (USB_DEVICE_HID_INDEX_0,
&appData.rxTransferHandle, appData.receiveDataBuffer, 64 );
}
⑥USB接続チェック用の関数です。 USBの送信バッファーに"Ready"の5バイトを書き込んで送信要求を
出します。PC側で100msec毎に "Ready"受信できるかチェックします。"Ready"が受信できない場合は
USB接続がされていないと判断します。
void ConnectCheck(void) //接続確認応答
{
appData.transmitDataBuffer[2] = 'R';
appData.transmitDataBuffer[3] = 'e';
appData.transmitDataBuffer[4] = 'a';
appData.transmitDataBuffer[5] = 'd';
appData.transmitDataBuffer[6] = 'y';
BufferRW();
}
⑦RG15に接続されているLED の点灯/消灯を制御している関数です。USB受信信号の中にあるLED点灯/消灯制御信号
(appData.receiveDataBuffer[1])を判別して制御しています。 点灯/消灯制御の結果を送信バッファーに
セットして送信要求を出しています。
void LedOnOff(void) //LED点灯/消灯制御
{
switch(appData.receiveDataBuffer[1])
{
case 0x30: //RG15 //LED1
if(appData.receiveDataBuffer[2] == 0x30)
{
LATGbits.LATG15 = 0; //RG15 消灯
appData.transmitDataBuffer[2] = 0x30;
}
else
{
LATGbits.LATG15 = 1; //RG15 点灯
appData.transmitDataBuffer[2] = 0x31;
}
BufferRW();
break;
default :
break;
}
}
⑧スイッチRB0の状態を検出している関数です。 スイッチの状態を検出後に結果を送信バッファーにセットして
送信要求を出しています。
void SwDetect(void) //スイッチの状態検出
{
if(appData.receiveDataBuffer[1] == 0x31) //RB0
{
if(PORTBbits.RB0 == 0)appData.transmitDataBuffer[2] = 0x30;
else appData.transmitDataBuffer[2] = 0x31;
}
BufferRW();
}
⑨AD変換された可変抵抗器の電圧を送信バッファーにセットして送信要求を出す関数です。
また、AD変換値AdcValue 及びAD変換値の電圧換算値を I2Cインターフェースのキャラクタ液晶に
表示も行っています。
void VR_Detect(void) //可変抵抗器VRの電圧検出
{
float Volt;
appData.transmitDataBuffer[2] = AdcValue; //下位8ビット at 12ビット
appData.transmitDataBuffer[3] = ((AdcValue >> 8) & 0b00001111);
//上位4ビット摘出 at 12ビット
BufferRW();
Volt = (float)AdcValue/4096*3.3;
cd_ACM1602_cmd_i2c(0x80); //1行目の先頭へ
sprintf(Buf,"AdcValue=%d ",AdcValue);
lcd_ACM1602_str_i2c(Buf);
lcd_ACM1602_cmd_i2c(0xC0); //2行目の先頭へ
sprintf(Buf,"Volt=%.3f ",Volt);
lcd_ACM1602_str_i2c(Buf);
}
⑩USB初期化に係る関数です。 APP_USBDeviceEventHandler( )関数の中で呼び出されています。
USB_DEVICE_HID_EVENT_RESPONSE APP_USBDeviceHIDEventHandler
(
USB_DEVICE_HID_INDEX iHID,
USB_DEVICE_HID_EVENT event,
void * eventData,
uintptr_t userData
)
{
……
……
return USB_DEVICE_HID_EVENT_RESPONSE_NONE;
}
⑪USB初期化に係る関数です。APP_Tasks ( )のcase APP_STATE_INIT:の中で呼び出されています。
void APP_USBDeviceEventHandler(USB_DEVICE_EVENT event, void * eventData,
uintptr_t context)
{
switch(event)
{
……
……
default:
break;
}
}
⑫ 変数の初期設定をします。
appData.state = APP_STATE_INIT;
appData.usbDevHandle = USB_DEVICE_HANDLE_INVALID;
appData.deviceConfigured = false;
……
……
⑬ 専用(Dedicated) ADCのADC0入力ポートをデフォルトのAN0からAN45に変更して、ADCをスタートさせます。
ADCTRGMODEbits.SH0ALT = 1; //入力ポート変更: AN0 --> AN45
DRV_ADC0_Open();
DRV_ADC_Start();
⑭ I2Cインターフェースのキャラクタ液晶を初期化して立ち上がり時に表示する文字を設定します。
lcd_ACM1602_init_i2c(); //I2Cインターフェース式液晶初期化
lcd_ACM1602_cmd_i2c(0x0C); //カーソル:0FF、ブリンク:0FF
lcd_ACM1602_cmd_i2c(0x80); //1行目の先頭へ
……
……
⑮ タイマ1の割り込み諸元やコールバック関数をセットし、タイマをスタートさせます。
uint32_t divider = 19532; //周期レジスタ初期値 //最初の割り込みまでの時間をセット
//10 nsec x 19532 x 256 = 50.00192msec = 50mec //PBCLK3のデフォルトは100MHz
DRV_TMR_AlarmRegister ( myHandle, divider, true, 0, tmrISR ); //繰り返し呼び出し
……
……
⑯ USBを初期化します。
case APP_STATE_INIT:
……
……
break;
⑰ USBの初期化完了を待ちます
case APP_STATE_WAIT_FOR_CONFIGURATION:
……
……
break;
⑱ 通常のUSBによる送受信を繰り返し行います。appData.receiveDataBuffer[0]の値を判別して
接続確認応答、LED点灯/消灯制御、スイッチの状態検出、VR電圧検出などの処理を行います。
case APP_STATE_MAIN_TASK:
……
……
switch(appData.receiveDataBuffer[0]) //コマンドの種類判定
{
case 0x30: //接続確認応答
ConnectCheck();
break;
case 0x80: //LED点灯/消灯制御
LedOnOff();
break;
case 0x81: //スイッチの状態検出
SwDetect();
break;
case 0x82: //VR電圧検出
VR_Detect();
break;
……
……
以下、app.c