平成26年12月16日
MPLAB Harmonyの使い方(9) USBライブラリの導入(2) cdc_com_port_dualをループバックに変更
取りあえず、今のサンプルプログラムの動作(2chあるシリアルポートの送受信データが互いに交差して接続されている状態)では動作検証が面倒なので、通常のループバックに直します。
(2014/12/17追記)
従来のシリアルの接続状態の説明が分かり難いので簡単な図を書いてみました。
PCからCOM1へ送信されたデータはCOM2の出力を介してPCに返信されます。COM2も同様です。
(追記、ここまで)
(2014/12/17追記)
記述内容が間違っていましたので、削除しました。正しい内容は
ここをご覧ください。
(追記、ここまで)
プログラムの修正は厳密な動作を追跡するとそれなりに面倒ですが、プログラム自体が階層化されているので、それらしい所を重点的に調べて変更することで取りあえず動作することを確認します。
ますは、受信データをアプリケーションレベルで受け取る部分を見つけます。それが見つかれば受信データを自身の送信バッファにコピーして渡すことでループバック動作になります。
該当しそうなプログラム箇所はapp.c内にあるAPP_Tasks()関数です。ステートマシンとして構成されており、初期化から始まり動作の進捗に合わせて個々の動作が記述されています。受信動作以降の該当箇所を以下に示します。
case APP_STATE_CHECK_FOR_READ_COMPLETE:
if(appData.appCOMPortObjects[COM1].isReadComplete == true)
{
/* This means we got data on COM1. Write this data to COM2.*/
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM2].isWriteComplete = false;
USB_DEVICE_CDC_Write(COM2,
&appData.appCOMPortObjects[COM2].writeTransferHandle,
com1ReadBuffer, 1,
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
}
if(appData.appCOMPortObjects[COM2].isReadComplete == true)
{
/* This means we got data on COM2. Write this data to COM1 */
appData.appCOMPortObjects[COM2].isReadComplete = false;
appData.appCOMPortObjects[COM1].isWriteComplete = false;
USB_DEVICE_CDC_Write(COM1,
&appData.appCOMPortObjects[COM1].writeTransferHandle,
com2ReadBuffer, 1,
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
}
appData.state = APP_STATE_CHECK_FOR_WRITE_COMPLETE;
break;
case APP_STATE_CHECK_FOR_WRITE_COMPLETE:
/* Check if the write is complete */
if(appData.appCOMPortObjects[COM2].isWriteComplete)
{
USB_DEVICE_CDC_Read(COM1,
&appData.appCOMPortObjects[COM1].readTransferHandle,
com1ReadBuffer, APP_READ_BUFFER_SIZE);
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM2].isWriteComplete = false;
}
if(appData.appCOMPortObjects[COM1].isWriteComplete)
{
USB_DEVICE_CDC_Read(COM2,
&appData.appCOMPortObjects[COM2].readTransferHandle,
com2ReadBuffer, APP_READ_BUFFER_SIZE);
appData.appCOMPortObjects[COM2].isReadComplete = false;
appData.appCOMPortObjects[COM1].isWriteComplete = false;
}
appData.state = APP_STATE_CHECK_IF_CONFIGURED;
break;
状態 APP_STATE_CHECK_FOR_READ_COMPLETEがPCからPICへデータが送られた時に実施される処理です。
プログラムは2ポート分の記述があるのですが今はCOM1側だけに注力します。
まず、受信が完了するとフラグisReadCompleteがtrueになり、関数USB_DEVICE_CDC_Write()が呼ばれます。
この関数によってCOM1で受信したデータがCOM2の出力データとして出力バッファに記憶されます。
ステートマシンの状態は関数が呼び出される度毎に順次変化しています。
その後、状態 APP_STATE_CHECK_FOR_WRITE_COMPLETE:では出力データの送信終了をフラグisWriteCompleteがtrueになることで確認すると、再び受信データの受信動作を要求します。
つまり、このプログラムでは送信と受信は同時には行わない半二重通信でデータのやり取りをしています。
これらの記述ではCOM1とCOM2が混ざった記述になっていますので、ループバック動作になるようにCOM1側だけを書き直したのが下記です。取りあえずCOM2に関する記述部分はマクロでコメント化してあります。
case APP_STATE_CHECK_FOR_READ_COMPLETE:
if(appData.appCOMPortObjects[COM1].isReadComplete == true)
{
/* This means we got data on COM1. Write this data to COM2.*/
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM1].isWriteComplete = false;
USB_DEVICE_CDC_Write(COM1,
&appData.appCOMPortObjects[COM1].writeTransferHandle,
com1ReadBuffer, 1,
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
}
#if 0
if(appData.appCOMPortObjects[COM2].isReadComplete == true)
{
/* This means we got data on COM2. Write this data to COM1 */
appData.appCOMPortObjects[COM2].isReadComplete = false;
appData.appCOMPortObjects[COM1].isWriteComplete = false;
USB_DEVICE_CDC_Write(COM1,
&appData.appCOMPortObjects[COM1].writeTransferHandle,
com2ReadBuffer, 1,
USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE);
}
#endif
appData.state = APP_STATE_CHECK_FOR_WRITE_COMPLETE;
break;
case APP_STATE_CHECK_FOR_WRITE_COMPLETE:
/* Check if the write is complete */
#if 0
if(appData.appCOMPortObjects[COM2].isWriteComplete)
{
USB_DEVICE_CDC_Read(COM1,
&appData.appCOMPortObjects[COM1].readTransferHandle,
com1ReadBuffer, APP_READ_BUFFER_SIZE);
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM2].isWriteComplete = false;
}
#endif
if(appData.appCOMPortObjects[COM1].isWriteComplete)
{
USB_DEVICE_CDC_Read(COM1,
&appData.appCOMPortObjects[COM1].readTransferHandle,
com1ReadBuffer, APP_READ_BUFFER_SIZE);
appData.appCOMPortObjects[COM1].isReadComplete = false;
appData.appCOMPortObjects[COM1].isWriteComplete = false;
}
appData.state = APP_STATE_CHECK_IF_CONFIGURED;
break;
この変更でループバック動作は確認できました。
次は転送速度を測ってみたいのですが、どうもこのサンプルプログラムは転送ブロックのサイズが1バイト単位になっているようです。もう少し内部を確認して、必要ならブロックサイズを変更するようにします。