UMEHOSHI ITA TOP PAGE COMPUTER SHIEN LAB
「テスト・ウメ・フラッシュ」はUSBからのコマンドなどをを受信して制御するスタイルになっています。
これを担うのが、『app.c』から呼び出される_my_app_tasks();のマクロで、
デフォルトの起動時の呼び出しがここで示すMy_APP_Tasks()関数です。
(スルーモードの起動時は、My_APP_Tasks_Through())関数呼び出しに変わります。)
このMy_APP_Tasks()では、受信したら速やかにコマンド解析して、
なんらかの処理をした後に必要に応じてUSBへ出力するスタイルです。
速やかに行わせるために「テスト・ウメ・フラッシュ」で、USB受信のアプリケーションレベルにおける
バッファリングをしていません。
受信したら速やかに、「my_usb_com.cのset_recive_data関数」へデータを渡して対応処理をしています。
(なお、MPLAB HarmonyによるUSBのシステムコードに64byteの受信バッファが存在します。)
対してUSBの出力のアプリケーションレベルで「my_usb_com.cのoutBuffers[OB_MAX][OB_SIZE]」を使ってバッファリングしています。
My_APP_Tasks()では、このバッファ「outBuffers」に出力情報があれば、USBに出力する仕組みを作っています。
上記を実現しているMy_APP_Tasks()の基本的な動作の流れを説明します。
基本的には、変数appData.stateに応じた処理で,次の手順です。
#ifndef _MY_APP_H /* Guard against multiple inclusion */ #define _MY_APP_H #include "app.h" #define APP_READ_BUFFER_SIZE 64 /* Macro defines USB internal DMA Buffer criteria */ #define APP_MAKE_BUFFER_DMA_READY /* USB read buffer */ extern uint8_t APP_MAKE_BUFFER_DMA_READY readBuffer[APP_READ_BUFFER_SIZE] __attribute__((coherent, aligned(16))); extern APP_DATA appData; extern int usb_receiver_disable_flag;//受信を無効にするフラグ. // USBから長いブロックデータを送信する場合、これを一時的にセットして制御. // デバイスのイベントハンドラ void APP_USBDeviceEventHandler ( USB_DEVICE_EVENT event, void * eventData, uintptr_t context ); bool APP_State_Reset(void); // My_APP_Tasks内のリセット時処理 void Init_Usb( void ); // USBの初期設定 void My_APP_Tasks ( void ); // USBの状態遷移に対する分岐処理関数 void My_APP_Tasks_Through ( void ); //USBとUART間で伝達するだけのUSB分岐処理関数 #define MY_APP_ERR_IS_READ_COMPLETE 0x0001 // USBへの受信完了状態エラー #define MY_APP_ERR_IS_WRITE_COMPLETE 0x0002 // USBへの送信完了状態エラー #define MY_APP_ERR_STATE 0x0004 // USBへの状態エラー #define MY_APP_ERR_DEFAULT 0x0008 // USB defaultエラー #endif /* _MY_APP_H*/
#include "my_app.h" #include "my_usb_cmd.h" #include <xc.h> #include "my_sys.h" #include "my_uart.h" #include "common.h" #define countermeasure_wait_parameter 200000 //矛盾処理対策待機パラメータ(実験値) /* USB read buffer */ uint8_t APP_MAKE_BUFFER_DMA_READY readBuffer[APP_READ_BUFFER_SIZE] __attribute__((coherent, aligned(16))); // coherent の指定は、バッファにキャッシュ(変数の配置場所を指定)) bool flag_APP_STATE_WAIT_FOR_READ_COMPLETE = false; bool flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = false; int usb_receiver_disable_flag = 0;//受信を無効にするフラグ // USBから長いブロックデータを送信する場合、これを一時的にセットして制御 // これが1の場合は、エコーもしない状態になる。!★ uint16_t my_app_err=0;// my_app.c でのエラー情報 int my_app_set_err( uint16_t d){// 上記エラー情報への設定関数 my_app_err |= d; } /******************************************************************************* * USB CDC Device Events - Application Event Handler * ( callback functions) ******************************************************************************/ USB_DEVICE_CDC_EVENT_RESPONSE APP_USBDeviceCDCEventHandler ( USB_DEVICE_CDC_INDEX index , USB_DEVICE_CDC_EVENT event , void * pData, uintptr_t userData ) { APP_DATA * appDataObject; appDataObject = (APP_DATA *)userData; USB_CDC_CONTROL_LINE_STATE * controlLineStateData; USB_DEVICE_CDC_EVENT_DATA_READ_COMPLETE * eventDataRead; switch ( event ) { case USB_DEVICE_CDC_EVENT_GET_LINE_CODING: /* This means the host wants to know the current line * coding. This is a control transfer request. Use the * USB_DEVICE_ControlSend() function to send the data to * host. */ USB_DEVICE_ControlSend(appDataObject->deviceHandle, &appDataObject->getLineCodingData, sizeof(USB_CDC_LINE_CODING)); break; case USB_DEVICE_CDC_EVENT_SET_LINE_CODING: /* This means the host wants to set the line coding. * This is a control transfer request. Use the * USB_DEVICE_ControlReceive() function to receive the * data from the host */ USB_DEVICE_ControlReceive(appDataObject->deviceHandle, &appDataObject->setLineCodingData, sizeof(USB_CDC_LINE_CODING)); break; case USB_DEVICE_CDC_EVENT_SET_CONTROL_LINE_STATE: /* This means the host is setting the control line state. * Read the control line state. We will accept this request * for now. */ controlLineStateData = (USB_CDC_CONTROL_LINE_STATE *)pData; appDataObject->controlLineStateData.dtr = controlLineStateData->dtr; appDataObject->controlLineStateData.carrier = controlLineStateData->carrier; USB_DEVICE_ControlStatus(appDataObject->deviceHandle, USB_DEVICE_CONTROL_STATUS_OK); break; case USB_DEVICE_CDC_EVENT_SEND_BREAK: /* This means that the host is requesting that a break of the * specified duration be sent. Read the break duration */ appDataObject->breakData = ((USB_DEVICE_CDC_EVENT_DATA_SEND_BREAK *)pData)->breakDuration; /* Complete the control transfer by sending a ZLP */ USB_DEVICE_ControlStatus(appDataObject->deviceHandle, USB_DEVICE_CONTROL_STATUS_OK); break; case USB_DEVICE_CDC_EVENT_READ_COMPLETE: /* This means that the host has sent some data */ eventDataRead = (USB_DEVICE_CDC_EVENT_DATA_READ_COMPLETE *)pData; appDataObject->isReadComplete = true; appDataObject->numBytesRead = eventDataRead->length; break; case USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_RECEIVED: /* The data stage of the last control transfer is * complete. For now we accept all the data */ USB_DEVICE_ControlStatus(appDataObject->deviceHandle, USB_DEVICE_CONTROL_STATUS_OK); break; case USB_DEVICE_CDC_EVENT_CONTROL_TRANSFER_DATA_SENT: /* This means the GET LINE CODING function data is valid. We dont * do much with this data in this demo. */ break; case USB_DEVICE_CDC_EVENT_WRITE_COMPLETE: /* This means that the data write got completed. We can schedule * the next read. */ appDataObject->isWriteComplete = true; break; default: break; } return USB_DEVICE_CDC_EVENT_RESPONSE_NONE; } /****************************************************************************** * Application USB Device Layer Event Handler. * This is the callback functions used by APP_Tasks of app.c ******************************************************************************/ void APP_USBDeviceEventHandler ( USB_DEVICE_EVENT event, void * eventData, uintptr_t context ) { USB_DEVICE_EVENT_DATA_CONFIGURED *configuredEventData; switch ( event ) { case USB_DEVICE_EVENT_SOF: /* This event is used for switch debounce. This flag is reset * by the switch process routine. */ appData.sofEventHasOccurred = true; break; case USB_DEVICE_EVENT_RESET: appData.isConfigured = false; break; case USB_DEVICE_EVENT_CONFIGURED: /* Check the configuratio. We only support configuration 1 */ configuredEventData = (USB_DEVICE_EVENT_DATA_CONFIGURED*)eventData; if ( configuredEventData->configurationValue == 1) { /* Register the CDC Device application event handler here. * Note how the appData object pointer is passed as the * user data */ USB_DEVICE_CDC_EventHandlerSet(USB_DEVICE_CDC_INDEX_0, APP_USBDeviceCDCEventHandler, (uintptr_t)&appData); /* Mark that the device is now configured */ appData.isConfigured = true; } break; case USB_DEVICE_EVENT_POWER_DETECTED: /* VBUS was detected. We can attach the device */ USB_DEVICE_Attach(appData.deviceHandle); break; case USB_DEVICE_EVENT_POWER_REMOVED: /* VBUS is not available any more. Detach the device. */ USB_DEVICE_Detach(appData.deviceHandle); break; case USB_DEVICE_EVENT_SUSPENDED: break; case USB_DEVICE_EVENT_RESUMED: break; case USB_DEVICE_EVENT_ERROR: break; default: break; } } //------------------------------------------------------------------------------ /******************************************************************************* * Initilaize USB functions ******************************************************************************/ void Init_Usb( void ) { /* Device Layer Handle */ appData.deviceHandle = USB_DEVICE_HANDLE_INVALID ; /* Device configured status */ appData.isConfigured = false; /* Initial get line coding state */ //appData.getLineCodingData.dwDTERate = 115200; appData.getLineCodingData.dwDTERate = _UME_USB_CDC_DATE; /* 標準のボー レートは、110、300、600、1200、2400、4800、9600、14400、 * 19200、38400、57600、115200、128000、256000 ビット/秒 */ appData.getLineCodingData.bParityType = 0; appData.getLineCodingData.bDataBits = 8; /* Read Transfer Handle */ appData.readTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; /* Write Transfer Handle */ appData.writeTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; /* Intialize the read complete flag */ appData.isReadComplete = true; /*Initialize the write complete flag*/ appData.isWriteComplete = true; /* Initialize Ignore switch flag */ appData.ignoreSwitchPress = false; /* Reset the switch debounce counter */ appData.switchDebounceTimer = 0; /* Reset other flags */ appData.sofEventHasOccurred = false; appData.isSwitchPressed = false; /* Set up the read buffer */ appData.readBuffer = &readBuffer[0]; } /****************************************************************************** * This function returns true if the device was reset * This function is called in every step of the application state machine. * called by My_APP_Tasks (Local Functions) *****************************************************************************/ bool APP_State_Reset(void) { bool retVal; if(appData.isConfigured == false) { appData.state = APP_STATE_WAIT_FOR_CONFIGURATION; appData.readTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.writeTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.isReadComplete = true; appData.isWriteComplete = true; flag_APP_STATE_WAIT_FOR_READ_COMPLETE = false; flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = false; retVal = true; init_outBuffers();// ★ USB出力用リングバッファ用変数の初期化 } else { retVal = false; } return(retVal); } /****************************************************************************** Extension function of APP_Tasks ******************************************************************************/ void My_APP_Tasks( void ){ static USB_DEVICE_CDC_RESULT result; check_uart_error();// UARTエラーチェック error_notice_beep();//エラーがあれば通知する // 関数実行要求のが合って、送信文字列が無ければ実行 extern void (*go_func)(void); if(go_func != NULL){ if(outBuffCount == 0){ (*go_func)();// 関数実行要求の実行 go_func = NULL; } } // CDC は非同期通信なので、HOSTが受け取るべきタイミングで受け取ることを期待して // 制御している。しかし連続して高速にHOSTに送る処理があると、HOSTが情報を // 取り損なうケースが生じる。 // この時不具合時、appData.isWriteCompleteは、falseのままで、 // flag_APP_STATE_WAIT_FOR_WRITE_COMPLETEは、trueのままで、 // 送信が終わるの待つシーケンスから抜けることができなくなる。 // そこで、そうなった時に知らせる次のモードを作る。 static bool wait_WriteComplete_Error = false; // これがTrueになると、D1のLED を点滅を行う。 static int wait_WriteComplete_Error_count = 0; if(wait_WriteComplete_Error){ if(++wait_WriteComplete_Error_count > countermeasure_wait_parameter){ wait_WriteComplete_Error_count = 0; _RB15 = ! _RB15;// エラーLED点滅 } } // 上記wait_WriteComplete_Errorモードは、次のカウンターの値で判断する。 static int check_wait_WriteComplete_count = 0; // この変数は、appData.isWriteCompleteがfalseでクリアされる。 _def_polls_uart();//UARTのデフォルトポーリング送受信処理 // このマクロのデフォルト呼び出しは「recive_and_send_uart_with_polling();//」 // USBへの送信データがoutBuffersのバッファにあるならUSB送信を促す requset_send_usb_if_available();// USBへの送信データがあるならUSB送信を促す static int no_read_task_count=0; no_read_task_count++; // USB送信バッファにデータがあればUSB送信を優先にスケジュールする. if( (appData.state == APP_STATE_SCHEDULE_READ || appData.state == APP_STATE_WAIT_FOR_READ_COMPLETE) && outBuffCount > 0 && ! flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE //&& no_read_task_count < 100 ){ appData.state = APP_STATE_SCHEDULE_WRITE; } /* Check the application's current state. */ switch ( appData.state ) { case APP_STATE_WAIT_FOR_CONFIGURATION: /* Check if the device was configured */ if(appData.isConfigured) {// デバイスが設定された? /* If the device is configured then lets start reading */ appData.state = APP_STATE_SCHEDULE_READ; } break; case APP_STATE_SCHEDULE_READ: if(APP_State_Reset()) { break; } no_read_task_count = 0; /* If a read is complete, then schedule a read * else wait for the current read to complete */ if( ! appData.isReadComplete ){// 受信状態エラー my_app_set_err(MY_APP_ERR_IS_READ_COMPLETE); } else { appData.isReadComplete = false; appData.readTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.state = APP_STATE_WAIT_FOR_READ_COMPLETE; flag_APP_STATE_WAIT_FOR_READ_COMPLETE = true; result = USB_DEVICE_CDC_Read (USB_DEVICE_CDC_INDEX_0, &appData.readTransferHandle, appData.readBuffer, APP_READ_BUFFER_SIZE); if( result != USB_DEVICE_CDC_RESULT_OK || appData.readTransferHandle == USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID) { appData.state = APP_STATE_ERROR; } } break; case APP_STATE_SCHEDULE_WRITE: if(APP_State_Reset()){ break; } if( request_acc_outbuff(_ID_ACCESS_USBTASK) == false) break; uint8_t *pSendBuff = getSendBuff();//送信バッファ取得 // (上記でグローバル変数numbOutputs送信文字数にセット //accessing_outbuffer &= ~_ID_ACCESS_USBTASK;//出力バッフ利用終了 release_acc_outbuff(_ID_ACCESS_USBTASK); if( ! appData.isWriteComplete || numbOutputs == 0){// USBへの送信完了状態エラー my_app_set_err(MY_APP_ERR_IS_WRITE_COMPLETE); break; } appData.writeTransferHandle= USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.isWriteComplete = false; appData.state = APP_STATE_WAIT_FOR_WRITE_COMPLETE; flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = true; result = USB_DEVICE_CDC_Write(USB_DEVICE_CDC_INDEX_0, &appData.writeTransferHandle, //appData.readBuffer, appData.numBytesRead,//出力位置とサイズ pSendBuff,numbOutputs, USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE ); if( result != USB_DEVICE_CDC_RESULT_OK || appData.writeTransferHandle==USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID){ appData.state = APP_STATE_ERROR; } break; case APP_STATE_WAIT_FOR_READ_COMPLETE: case APP_STATE_WAIT_FOR_WRITE_COMPLETE: if(APP_State_Reset()) { break; } /* The isReadComplete flag gets updated in the CDC event handler. */ if( flag_APP_STATE_WAIT_FOR_READ_COMPLETE && ! usb_receiver_disable_flag && appData.isReadComplete == true){// USB受信完了 int wait=countermeasure_wait_parameter; while( request_acc_outbuff(_ID_ACCESS_USBTASK) == false) { //出力バッフ利用許可を待つ if(wait-- <= 0)return; } int i; for(i=0; i < appData.numBytesRead; i++){ if(set_recive_data( appData.readBuffer[i], i+1 == appData.numBytesRead // 繰り返し最後ならtrue ) == 'W'){ } } flag_APP_STATE_WAIT_FOR_READ_COMPLETE = false; if( flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE ){ appData.state = APP_STATE_WAIT_FOR_WRITE_COMPLETE; } } /* Check if a character was sent. * The isWriteComplete flag gets updated in the CDC event handler */ if(flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE){ if(appData.isWriteComplete == true){// USB受信完了 int wait=countermeasure_wait_parameter; while( request_acc_outbuff(_ID_ACCESS_USBTASK) == false) { //出力バッフ利用許可を待つ if(wait-- <= 0)return; } // 送信処理が終わった通知]に対する処理 noticeSendCcomplete();//(numbOutputs送信文字数が0に戻している) flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = false; check_wait_WriteComplete_count = 0; if( flag_APP_STATE_WAIT_FOR_READ_COMPLETE ){ appData.state = APP_STATE_WAIT_FOR_READ_COMPLETE; } } else if(++check_wait_WriteComplete_count > countermeasure_wait_parameter){ wait_WriteComplete_Error = true;// HOSTが遅いエラー! } } //accessing_outbuffer &= ~_ID_ACCESS_USBTASK;//出力バッフ利用終了 release_acc_outbuff(_ID_ACCESS_USBTASK); if(flag_APP_STATE_WAIT_FOR_READ_COMPLETE == false && flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE == false ){ appData.state = APP_STATE_SCHEDULE_READ; } break; case APP_STATE_ERROR: my_app_set_err(MY_APP_ERR_STATE); default: my_app_set_err(MY_APP_ERR_DEFAULT); ; } } //USBとUART間で伝達するだけのUSB分岐処理関数 void My_APP_Tasks_Through ( void ) { // UARからの受信 そして、USBへの送信バッファ static uint8_t * recUartBuff = (uint8_t *) outBuffers; static int recSize=0; static USB_DEVICE_CDC_RESULT result; check_uart_error();// UARTエラーチェック error_notice_beep();//エラーがあれば通知する // 起動時の受信を無視して、"\r\n"のメッセージを1回だけ送出する. if( reply_boot_message() ) { for(;;){// UARTからの受信をポーリングで、受信データがあればバッファに記憶 if(U1STAbits.URXDA == 0) { break;// UART信バッファ内にデータが存在しない? } uint8_t c = U1RXREG; // UART受信データ取得 ringbuf_set_data( & uartRecBuf, c); } } if( ringbuf_is_read( & uartRecBuf ) > 0 && appData.isConfigured // デバイスが設定済み && ! flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE ){ appData.state = APP_STATE_SCHEDULE_WRITE;// USB出力 } /* Check the application's current state. */ switch ( appData.state ) { case APP_STATE_WAIT_FOR_CONFIGURATION: /* Check if the device was configured */ if(appData.isConfigured) {// デバイスが設定された? /* If the device is configured then lets start reading */ appData.state = APP_STATE_SCHEDULE_READ; } break; case APP_STATE_SCHEDULE_READ: if(APP_State_Reset()) { break; } /* If a read is complete, then schedule a read * else wait for the current read to complete */ if( ! appData.isReadComplete ){// 受信状態エラー my_app_set_err(MY_APP_ERR_IS_READ_COMPLETE); } else { appData.isReadComplete = false; appData.readTransferHandle = USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.state = APP_STATE_WAIT_FOR_READ_COMPLETE; result = USB_DEVICE_CDC_Read (USB_DEVICE_CDC_INDEX_0, &appData.readTransferHandle, appData.readBuffer, APP_READ_BUFFER_SIZE); if( result == USB_DEVICE_CDC_RESULT_OK ) { flag_APP_STATE_WAIT_FOR_READ_COMPLETE = true; } else { appData.state = APP_STATE_ERROR; } } break; case APP_STATE_WAIT_FOR_READ_COMPLETE: if(APP_State_Reset()) { break; } /* The isReadComplete flag gets updated in the CDC event handler. */ if( appData.isReadComplete == true){// USB受信完了 int i; for(i=0; i < appData.numBytesRead; i++){ for(;;){ // UARTへ送信 if(U1STAbits.UTXBF == 0 // 送信バッファはフルではない //&& U1STAbits.TRMT == 1////送信バッファが空? ){ uint8_t data = appData.readBuffer[i]; U1TXREG = data;//UART送信バッファに入れる。 break; } } } appData.state = APP_STATE_SCHEDULE_READ; flag_APP_STATE_WAIT_FOR_READ_COMPLETE = false; } if(flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE){ appData.state = APP_STATE_WAIT_FOR_WRITE_COMPLETE; } break; case APP_STATE_SCHEDULE_WRITE: if(APP_State_Reset()){ break; } appData.writeTransferHandle= USB_DEVICE_CDC_TRANSFER_HANDLE_INVALID; appData.isWriteComplete = false; appData.state = APP_STATE_WAIT_FOR_WRITE_COMPLETE; recSize =ringbuf_copy_to( & uartRecBuf, recUartBuff); result = USB_DEVICE_CDC_Write(USB_DEVICE_CDC_INDEX_0, &appData.writeTransferHandle, recUartBuff,//送信バッファ recSize,//送信バイト数 USB_DEVICE_CDC_TRANSFER_FLAGS_DATA_COMPLETE ); if( result == USB_DEVICE_CDC_RESULT_OK ){ flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = true; } else { appData.state = APP_STATE_ERROR; } break; case APP_STATE_WAIT_FOR_WRITE_COMPLETE: if(APP_State_Reset()) { break; } /* Check if a character was sent. * The isWriteComplete flag gets updated in the CDC event handler */ if(appData.isWriteComplete == true){// USB送信完了 // 送信処理が終わった通知]に対する処理 flag_APP_STATE_WAIT_FOR_WRITE_COMPLETE = false; recSize = 0; appData.state = APP_STATE_SCHEDULE_READ; } if(flag_APP_STATE_WAIT_FOR_READ_COMPLETE){ appData.state = APP_STATE_WAIT_FOR_READ_COMPLETE; } break; case APP_STATE_ERROR: my_app_set_err(MY_APP_ERR_STATE); default: my_app_set_err(MY_APP_ERR_DEFAULT); } }