USB - CDC とは、USBをVirtual COM Port(VCP)仮装崇信ポートとし、PICと通信するためのアプリケーションです。
PICとのデータ交換の状況は、Maicrochip社が配布している専用ソフト, Dynamic CDC Demo で確認をします。 VCPは本来、古くからあるRS232Cシリアルポートの通信をUSBに置き換える目的で設定されたUSBの機能ですから、「TeraTerm」や「CoolTerm」などの通信ソフトで送受信を確認することもできます。PCからASCII文字を送信すると、その文字の次の文字が、エコーバックされます。つまり、PCが「ABC」の3文字を送信すると、PICは「BCD」と返送します。
ここでは、USB - CDC を MPLAB X のプラグインの MCC で作成し、その手順を詳しく説明します。
<目次>
このページでは、MPLAB X MCC に登録されている USB Framework Lite v1.27.0 を使用します。MPLAB X バージョンはv4.0.2で作成しています。
図2:USB通信回路図
左の回路図は、必要な最小限の部品だけで構成しました。アクティブクロックチューニング(ACT)を備えた内部発振器を使用するため、高精度の外部クリスタルとその回路は必要ありません。
PICはホストPCから電力を得ることができます。USB仕様2.0によれば、低電力デバイスあたり100mAまたは高電力デバイスでも500mAを超えることはできません。USBから電力を引き出すデバイスの消費電力に留意してください。
デカップリングコンデンサはVDDとグランド(PICのできるだけ近く)に接続する必要があります。0.1uFの値で十分です。
PICは、内蔵3.3V USB電圧レギュレータを利用して、内部トランシーバに電力を供給し、内部プルアップ抵抗のソースを提供します。内部コンポーネントを使用することで、外部コンポーネントの数を減らすことができます。ファームウェアでUSBモジュールを無効にすることにより、USB接続を電気的に切り離すこともできます。ファームウェアでUSBモジュールを無効にする(UCONレジスタのUSBENビットを「0」に設定する)と、オンチップUSB電圧レギュレータも無効になります。これは、USBケーブルの物理的な取り外しをシミュレートします。
内部電圧レギュレータのPIC VUSBピンに0.47uFのような外部コンデンサを接続する必要があります(詳細はデータシートをご覧ください)。外部レギュレータがVUSBに接続されている場合、この内部レギュレータを無効にできます。
アクティブクロックチューニング(ACT)は、PIC16F1455、PIC16F1459などの新しい8ビットPICマイクロコントローラーの機能です。
アクティブクロックチューニング(ACT)モジュールは、USBホストからの外部リファレンスを使用して、16 MHz内部オシレーターを継続的に調整し、USB規格で要求される±0.20%の精度を達成します。これにより、高精度の外部クリスタルなしで、USBフルスピード動作を実現できます。
新しいMPLAB Xプロジェクトを開始し、PIC16F1459を選択します。MCCを起動して、周辺機器を設定します。
プロジェクトリソースのシステムモジュールをクリックします。PIC16F1459をUSB接続に使用する場合は、48MHzをUSBモジュールに提供する必要があります。これを実現するには、PLL(フェーズロックループ)で内部16 MHzクロックを使用して 3x することで、USBクロック周波数を48MHzに上げることができます。
画面を見ながら設定します。赤丸で示した項目以外はデフォルトでOKでした。Current System clock ga、48MHz になっていることを確認します。
図6:MCCを使用した48MHz USBフルスピードのPIC16F1459クロック設定
HFINTOSCは、フルスピードUSBイベントを基準としてATC機能により、16 MHz内部発振器を ±0.2%の精度に調整されます。ACTは、画面上部の[Registers]タブから、ACTCON レジスタの ACTENビットをセットすると有効になり、有効にすると、ACTが OSCTUNEレジスタを制御します。
図7:PIC16F1459 ACTONレジスタ
デバイスリソースの USB下で、MLA USB Device Lite をダブルクリックして、プロパティリソースに追加します。
表示される画面を確認します。MCCでのセットアップは非常に簡単です、ほとんどデフォルト設定は変更せずにそのままでOKです。必要ならマウスで各項目をクリックして設定します。
*重要*
シリアル番号はデフォルトで空欄になっています。空欄ののまま動作させると、完成したPICユニットをWindows PCの違うUSBポートに接続するたびに新しいCOMポート番号が作成されてしまい、COMポート番号の数がすぐに2桁になってしまいます。
シリアル番号を付与すれば、どこのポートに接続しても同一の番号が与えられます。ただし、同一シリアル番号を異なるPICユニット書き込まないよう十分な注意が必要です。
図8:MCCでのMLA USB Device Lite構成
デフォルトの抽象制御管理機能の設定はそのままにします。
図9:USB CDC設定
MCCプロジェクトリソースで、[generate]をクリックして、すべてのUSB関連ファイルとMain.cファイルを生成します。
典型的なUSB CDCのコードは次のようになります。
void main(void)
{
USBDeviceInit();
while(1)
{
USBDeviceTasks(); //USBイベントを処理
if((USBGetDeviceState() < CONFIGURED_STATE) ||
(USBIsDeviceSuspended() == true))
{
//デバイスが構成されていないか、または中断されているため、
// アプリケーションコードは実行しない
continue; //whileループの先頭に戻る
}
else
{
//送信待ちデータがあればPCに送信し続ける
CDCTxService();
//アプリケーションコードを実行
UserApplication();
}
}
}
これらの関数の説明は、Microchip Libraries for Applicationsのインストールフォルダーに作成される MLA USB Libraryヘルプドキュメントからか、MCC USBヘッダーファイルの関数プロトタイプの説明( usb_device.h 、 usb_device_cdc.hなど )から得ることができます。
この関数は、デフォルトの状態でデバイススタックを初期化します。USBモジュールは、すべての内部変数、レジスタ、および割り込みフラグを含め、完全にリセットされます。この関数は、 USBDeviceTasks()を含む他のUSBデバイス関数を呼び出す前に呼び出す必要があります。
MCCを使用している場合、 USBDeviceInit() は SYSTEM_Initialize() 関数から呼び出され、割り込み、発振器、ピンマネージャも初期化します。したがって、MCCがデフォルトで生成する最初のコードは次のとおりです。
// デバイスを初期化 SYSTEM_Initialize();
MCCを使用している場合、この関数は SYSTEM_Initialize() 関数から呼び出され、何もする必要はありません。この機能は、USBデバイスがバスに接続されていることをUSBホストに示します。デバイスがバス上で列挙を開始するには、この関数を呼び出す必要があります。
この関数は、 USB_INTERRUPT が定義されている場合にのみ呼び出す必要があることに注意してください。また、main()ループコンテキストからのみ呼び出す必要があります。MCCでは、以下のコードがMCCによって自動的に生成されます。
void SYSTEM_Initialize(void)
{
INTERRUPT_Initialize();
PIN_MANAGER_Initialize();
OSCILLATOR_Initialize();
USBDeviceInit();
USBDeviceAttach();
}
USBDeviceAttach()
この関数は、USBデバイス側スタックのメインステートマシン/トランザクションハンドラです。USBスタックが「 USB_POLLING 」モードで動作している場合(usb_config.hユーザーオプション) 、スタックを介してパケットを送受信するために、 USBDeviceTasks()関数を定期的に呼び出す必要があります。この関数は、USB列挙プロセスに関連する制御転送の処理、およびさまざまなUSBイベント(サスペンドなど)の検出も行います。 この関数は、USB列挙プロセス中に少なくとも1.8msごとに1回呼び出す必要があります。列挙プロセスが完了すると(USBGetDeviceState()がCONFIGURED_STATEを返すときに判断できます)、
USBスタックが「 USB_POLLING 」モードで動作している場合、メインループからUSBDeviceTasks()を呼び出す必要があります。USBDeviceTasks()ハンドラーは、9.8msごとに1回、またはハードウェアUSTAT FIFOがフルにならないことを確認するために必要な頻度で、より高速に呼び出すことが必要です。大まかな目安としては、USBTransferOnePacket()が呼び出される頻度、または1回/1.8ミリ秒のいずれか短い方の頻度でUSBDeviceTasks()を呼び出すことです。
USBDeviceTasks()を呼び出すときの最小タイミング要件の詳細については、usb_device.cの上部にあるインラインコードコメントを参照してください。
USBスタックが「 USB_INTERRUPT 」モードで動作している場合、メインループコンテキストからUSBDeviceTasks()を呼び出す必要はありません。USB_INTERRUPTモードでは、USBDeviceTasks()ハンドラーは、USB割り込みが発生したときにのみ実行する必要があるため、割り込みコンテキストからのみ呼び出す必要があります。
この関数は、USB上のデバイスの現在の状態を返します。この関数は、デバイスがバス上で通信する準備ができたことを判断するために使用されます。この関数が CONFIGURED_STATEを返すまで、アプリケーションはデータの送受信を試みるべきではありません 。
さまざまなデバイスの状態の詳細については、www.usb.orgから入手できるUSB仕様セクション9.1を参照してください。下の表1は、USB表示デバイスの状態を示しています。
| Attached | Powered | Default | Address | Configured | Suspended | 状態 |
|---|---|---|---|---|---|---|
| No | − | − | − | − | − | デバイスは USBに接続されていません。その他の属性は重要ではありません。 |
| Yes | No | − | − | − | − | デバイスは USBに接続されていますが、電力が供給されていません。他の属性は重要ではありません。 |
| Yes | Yes | No | − | − | − | デバイスはUSBに接続され、電源が入っていますが、リセットされていません。 |
| Yes | Yes | Yes | No | − | − | デバイスはUSBに接続され、電源が入っており、リセットされましたが、一意のアドレスが割り当てられていません。デバイスはデフォルトのアドレスで応答します。 |
| Yes | Yes | Yes | Yes | No | − | デバイスがUSBに接続され、電源が供給され、リセットされ、一意のデバイスアドレスが割り当てられました。デバイスが構成されていません。 |
| Yes | Yes | Yes | Yes | Yes | No | デバイスはUSBに接続され、電源が供給され、リセットされ、一意のアドレスを持ち、構成されており、一時停止されていません。これで、ホストはデバイスが提供する機能を使用できます。 |
| Yes | Yes | Yes | Yes | Yes | Yes | デバイスは少なくともUSBに接続されており、電力が供給されているが、3 ms間バスアクティビティが発生していません。また、一意のアドレスが割り当てられ、使用できるように構成されている可能性があります。ただし、デバイスが一時停止されているため、ホストはデバイスの機能を使用できません。 |
表1:USB表示デバイスの状態
USBGetDeviceState()によって返されるUSBデバイスの状態:
typedef enum
{
/* Detachedは、デバイスがバスに接続されていない状態です。
* 切り離された状態では、デバイスのプルアップがD +またはD-ラインに
* 接続されてはいけません。*/
DETACHED_STATE
/*DOM-IGNORE-BEGIN*/ = 0x00 /*DOM-IGNORE-END*/,
/* Attachedは、デバイスがバスに接続されているが、
* 接続されているハブ/ポートがまだ設定されていない状態です。*/
ATTACHED_STATE
/*DOM-IGNORE-BEGIN*/ = 0x01 /*DOM-IGNORE-END*/,
/* デバイスがバスに接続され、接続されているハブ/ポート
* が接続されている状態構成されています。*/
POWERED_STATE
/*DOM-IGNORE-BEGIN*/ = 0x02 /*DOM-IGNORE-END*/,
/* Default の状態は、デバイスがホストからRESETコマンドを
* 受け取った後の状態です。*/
DEFAULT_STATE
/*DOM-IGNORE-BEGIN*/ = 0x04 /*DOM-IGNORE-END*/,
/* アドレス保留状態は、USB定義状態の正式な状態ではありません。
* この状態は、 デバイスがSET_ADDRESSコマンドを受信したが、
* 転送のSTATUSステージをまだ受信していないことを示すために
* 内部的に使用されます。デバイスは、STATUSステージが完了する
* までアドレスを切り替えないでください。*/
ADR_PENDING_STATE
/*DOM-IGNORE-BEGIN*/ = 0x08 /*DOM-IGNORE-END*/,
/* アドレスは、デバイスがバス上に独自の特定のアドレスを持っている状態です。
*/
ADDRESS_STATE
/*DOM-IGNORE-BEGIN*/ = 0x10 /*DOM-IGNORE-END*/,
/* 構成済みは、デバイスが完全に列挙され、バス上で動作している状態です。
* これで、デバイスはアプリケーションのタスクを実行できます。また、現在の
* 構成の構成記述子で指定された値まで現在の消費量を増やすこともできます。*/
CONFIGURED_STATE
/*DOM-IGNORE-BEGIN*/ = 0x20 /*DOM-IGNORE-END*/
} USB_DEVICE_STATE;
このマクロは、CDCクラスハンドラーファームウェアがCDCバルクINエンドポイントを介してホストにデータを送信する準備ができているかどうかを確認するために使用されます。
trueの戻り値は、CDCハンドラーファームウェアが新しいデータを受信する準備ができていることを示します。したがって、 putrsUSBUSART() や putsUSBUSART()などの
他のAPI を呼び出しても安全 です。falseの戻り値は、ファームウェアがまだ最後のデータの送信でビジーであるか、そうでない場合は現時点で新しいデータを処理する準備ができていないことを意味します。
この関数の戻り値は、デバイスが構成済みの状態にある場合にのみ有効です(つまり、USBDeviceGetState()はCONFIGURED_STATEを返します)。
備考:アプリケーションが CDCTxService() ハンドラーを定期的に呼び出すことを確認してください。そうしないと、保留中のUSB IN転送が続行および完了できなくなります。
構文 :
#define USBUSARTIsTxTrfReady (cdc_trf_state == CDC_TX_READY)
例:
if(USBUSARTIsTxTrfReady())
{
putrsUSBUSART("Hello World");
}
これは、デバイスからホストへのトランザクションを処理します。この関数は、デバイスが構成された状態に達した後、メインプログラムループごとに1回呼び出す必要があります。この機能は、CDCシリアルデータに関連付けられたIN USBデータの複数のトランザクションをホストに送信する処理を行う内部ソフトウェアステートマシンを進めるために必要です。CDCTxService()を定期的に呼び出さないと、CDCシリアルデータインターフェイスを介してUSBホストにデータが送信されません。
これは、USB CDC Bulk OUTエンドポイントを介して受信したBYTEの文字列をユーザーの指定した場所にコピーします。利用可能なデータが無い場合は、データを受信するまで待つことなく、「0」を返し、使用可能なデータがないことを呼び出し元に通知します。
uint8_t numBytes;
uint8_t buffer[64]
numBytes = getsUSBUSART(buffer,sizeof(buffer)); // バッファが解放されるまで
if(numBytes > 0)
{
// numBytesバイトのデータを受信し、それらは「buffer配列」にコピーされます。
// ここのデータをここで処理することができます。
}
構文:
uint8_t getsUSBUSART ( char *buffer, uint8_t len )
uint8_t * buffer: 受信したBYTEが格納される場所へのポインター。入力引数 'buffer'は、 'len'で指定されたサイズ以上のバッファー領域を指す必要があります。
uint8_t len: 予期されるBYTEの数。入力引数 'len'の値は、CDCクラスのUSBホストからバルクデータを受信する最大エンドポイントサイズよりも小さい必要があります
返数: buffer配列にコピーされたByte数が返されます。データを受信してないときは「0」が返されます。
これにより、ヌル文字を含む一連のデータがUSBに書き込まれます。このバージョンの 'putrs'を使用して、データリテラルとプログラムメモリにあるデータを転送します。
if(USBUSARTIsTxTrfReady())
{
putrsUSBUSART("Hello World");
}
これにより、ヌル文字を含む一連のデータがUSBに書き込まれます。このバージョンの「puts」を使用して、RAMバッファーからデータを転送します。
if(USBUSARTIsTxTrfReady())
{
char data[] = "Hello World";
putsUSBUSART(data);
}
これにより、データの配列がUSBに書き込まれます。このバージョンを使用すると、0x00を転送できます(通常、文字列転送関数のNULL文字です)。
if(USBUSARTIsTxTrfReady())
{
char data[] = {0x00, 0x01, 0x02, 0x03, 0x04};
putUSBUSART(data,5);
}
このプロジェクトの例(図2の回路)では、PIC16F1459のクロック構成(図6)を使用してフルスピードモードで実行しています。MCCのUSB Device Lite構成は図8にあり、USBタスク割り込みモードが選択されています。
グローバル割り込みとペリフェラル割り込みを必ず有効にしてください。
受け取った文字はすべて1にエコーバックされます。「 a 」を受け取った場合、「 b 」をエコーし、「 1 」は「 2 」をエコーします。これにより、ユーザーは端末自体のエコー機能ではないことがわかります。
/** USB CDC Basic
Device : PIC16F1459
XC8 Compiler : Version 2.10
MCC : Version 4.0.2
MPLAB X IDE : Version 5.45
*/
#include "mcc_generated_files/mcc.h"
static uint8_t readBuffer[64];
static uint8_t writeBuffer[64];
void MCC_USB_CDC_DemoTasks(void);
/*
Main application
*/
void main(void)
{
SYSTEM_Initialize();
// USB割り込み モードをMCCでの設定時に選択しているため
// 割り込みを有効にします
INTERRUPT_GlobalInterruptEnable();
INTERRUPT_PeripheralInterruptEnable();
while (1)
{
MCC_USB_CDC_DemoTasks();
}
}
void MCC_USB_CDC_DemoTasks(void)
{
// USBデバイスがまだ構成されていない場合、
// 通信するホストがないため、何もせずにwhileループに戻ります。
if( USBGetDeviceState() < CONFIGURED_STATE ) return;
// 現在中断(suspended)の場合は、リモートウェイクアップを発行する必要があるか
// 確認する必要があります。また、現在ホストと通信していないため、どのような
// キーボードコマンドも処理できません。whileループに戻ります。
if( USBIsDeviceSuspended()== true ) return;
// CDCドライバーの送信の準備ができていることを確認
if( USBUSARTIsTxTrfReady() == true)
{
uint8_t i;
uint8_t numBytesRead;
numBytesRead = getsUSBUSART(readBuffer, sizeof(readBuffer));
// すべての読み取りバイトに対し以下を繰り返す
for(i=0; i<numBytesRead; i++){
switch(readBuffer[i]) {
// 復帰または改行コマンドを受け取った場合は、コードをそのまま返送
case 0x0A:
case 0x0D:
writeBuffer[i] = readBuffer[i];
break;
// 他のコードを受け取った場合は、その値を +1 し返送
// 例えば、 'a'を受け取った場合、 'b'を返送する
default:
writeBuffer[i] = readBuffer[i] + 1;
break;
}
}
// 受信したすべてのデータを処理した後、「エコー」データを送信する
if(numBytesRead > 0)
{
putUSBUSART(writeBuffer,numBytesRead);
}
}
// デバイスからホストへのトランザクションを処理します。
// CDCTxService()を定期的に呼び出さないと、
// データがUSBホストに送信されません
CDCTxService();
}
/**
End of File
*/
https://www.studentcompanion.co.za/usb-communication-with-pic-microcontroller-cdc-xc8/