PIC32MX+Harmony3:シリアル通信

PIC32MXとHarmony3でシリアル通信のプログラムをつくるメモです。数ヶ月前につくって動いていたプログラムを、久しぶりに使おうとしたら動かなくなっていました。何が原因か探っていたら、Harmony3のシリアル通信ドライバの使い方が少し変わったのかな(?)という感じではっきり原因がわかりませんでした。昔のシリアル通信のドライバに戻す方法がよくわからなかったので、最新のドライバに対応すべくプログラムにかじりつきました。あまり情報がないのでメモとして残しておきます。

まずはPIC32MX120F032Bで、Harmony3のプロジェクトをつくります。そのあとのMicrochip Harmony3 Configurator(MHC)の設定からメモしていきます。

1. MHCの設定

  • クロック:40MHz
  • UART:9600bps,8ビット,パリティなし,ストップビット1
  • ピン割り当て:RB2・U1RX,RB3・U1TX
  • シリアル受信だけしたいのだけど・・・?

memo

「USART Drivers」を設定すると、「FreeRTOS」と「Core」を追加するようにダイアログが出てきますが、この記事の例ではそのあと「FreeRTOS」を削除して使っています。ここではFreeRTOSは使わないプログラムをつくっていきます。


2. コードの生成と参考になるソース

コードを生成すると「app.c」「app.h」が自動生成さでます。これらに自分でいちから書いていくよりも、GithubにあるMicrochip公式のサンプルを参考にしながら、書き換えていくほうがよいでしょう。ここでメモですが、MHCのドライバを使ったサンプルと、使っていないサンプルが分かれていて、探すのに時間がかかってしまいました。

PIC32MXのドライバを使ったサンプル
https://github.com/Microchip-MPLAB-Harmony/core_apps_pic32mx

PIC32MXのドライバを使っていないサンプル
https://github.com/Microchip-MPLAB-Harmony/csp_apps_pic32mx

これらサンプルにあるものは、自分の使いたいPICと違う種類だったり、シリアル通信の受信だけ使いたいのに、送受信するサンプルしかなかったり、少しずつ違います。種類の違うPICの場合はPIC32MXかMZかなど、できるだけ近い型番のサンプルを使えば、MHCの設定だけで使うことができます。MHCを使うメリットでもありますね。そして実際の細かい動作は、ソースを理解していきながら、適宜変更していきます。そうすることで自分の欲しい機能に対して、最低限のソースにできてスッキリします。

ではMHCで自動生成した「app.c」「app.h」に、サンプルソースの「app.c」「app.h」をコピーします。LED制御のプログラムがエラーになるのでコメントアウトしておきました。

app.c

/*******************************************************************************
  MPLAB Harmony Application Source File
  Company:
    Microchip Technology Inc.
  File Name:
    app.c
  Summary:
    This file contains the source code for the MPLAB Harmony application.
  Description:
    This file contains the source code for the MPLAB Harmony application.  It
    implements the logic of the application's state machine and it may call
    API routines of other MPLAB Harmony modules in the system, such as drivers,
    system services, and middleware.  However, it does not call any of the
    system interfaces (such as the "Initialize" and "Tasks" functions) of any of
    the modules in the system or make any assumptions about when those functions
    are called.  That is the responsibility of the configuration-specific system
    files.
 *******************************************************************************/

// DOM-IGNORE-BEGIN
/*******************************************************************************
* Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries.
*
* Subject to your compliance with these terms, you may use Microchip software
* and any derivatives exclusively with Microchip products. It is your
* responsibility to comply with third party license terms applicable to your
* use of third party software (including open source software) that may
* accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
* EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
* INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
* WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
* BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
* FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
* ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*******************************************************************************/
// DOM-IGNORE-END

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************

#include 
#include "app.h"
#include "user.h"
// *****************************************************************************
// *****************************************************************************
// Section: Global Data Definitions
// *****************************************************************************
// *****************************************************************************

// *****************************************************************************
/* Application Data
  Summary:
    Holds application data
  Description:
    This structure holds the application's data.
  Remarks:
    This structure should be initialized by the APP_Initialize function.
    Application strings and buffers are be defined outside this structure.
*/

APP_DATA appData;

static const char messageBuffer[] =
"*** USART Driver Echo Demo Application ***\r\n"
"*** Type a character and observe it echo back ***\r\n"
"*** LED toggles on each time the character is echoed ***\r\n";

// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Functions
// *****************************************************************************
// *****************************************************************************

static void APP_USARTBufferEventHandler(
    DRV_USART_BUFFER_EVENT bufferEvent,
    DRV_USART_BUFFER_HANDLE bufferHandle,
    uintptr_t context
)
{
    switch(bufferEvent)
    {
        case DRV_USART_BUFFER_EVENT_COMPLETE:
            appData.transferStatus = true;
            break;

        case DRV_USART_BUFFER_EVENT_ERROR:
            appData.state = APP_STATE_ERROR;
            break;

        default:
            break;
    }
}

// *****************************************************************************
// *****************************************************************************
// Section: Application Local Functions
// *****************************************************************************
// *****************************************************************************


/* TODO:  Add any necessary local functions.
*/


// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

/*******************************************************************************
  Function:
    void APP_Initialize ( void )
  Remarks:
    See prototype in app.h.
 */

void APP_Initialize ( void )
{
    /* Place the App state machine in its initial state. */
    appData.state           = APP_STATE_INIT;
    appData.transferStatus  = false;
    appData.usartHandle     = DRV_HANDLE_INVALID;
    appData.bufferHandle    = DRV_USART_BUFFER_HANDLE_INVALID;

    //LED_OFF();
}


/******************************************************************************
  Function:
    void APP_Tasks ( void )
  Remarks:
    See prototype in app.h.
 */

void APP_Tasks ( void )
{
    /* Check the application's current state. */
    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:

            appData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, DRV_IO_INTENT_READWRITE);
            if (appData.usartHandle != DRV_HANDLE_INVALID)
            {
                DRV_USART_BufferEventHandlerSet(appData.usartHandle, APP_USARTBufferEventHandler, 0);
                appData.state = APP_STATE_TRANSMIT_MESSAGE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_TRANSMIT_MESSAGE:

            DRV_USART_WriteBufferAdd(appData.usartHandle, (void*)messageBuffer, strlen(messageBuffer), &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE:
            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                appData.state = APP_STATE_RECEIVE_DATA;
            }
            break;

        case APP_STATE_RECEIVE_DATA:

            DRV_USART_ReadBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_RECEIVE_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_RECEIVE_COMPLETE:

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                appData.state = APP_STATE_TRANSMIT_DATA;
            }
            break;

        case APP_STATE_TRANSMIT_DATA:

            /* Echo the received data back on the terminal */
            DRV_USART_WriteBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_TRANSMIT_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_TRANSMIT_COMPLETE:

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;

                //LED_TOGGLE();

                appData.state = APP_STATE_RECEIVE_DATA;
            }
            break;

        case APP_STATE_ERROR:

            //LED_OFF();
            appData.state = APP_STATE_IDLE;
            break;

        case APP_STATE_IDLE:
        default:
            break;
    }
}


/*******************************************************************************
 End of File
 */

app.h

/*******************************************************************************
  MPLAB Harmony Application Header File
  Company:
    Microchip Technology Inc.
  File Name:
    app.h
  Summary:
    This header file provides prototypes and definitions for the application.
  Description:
    This header file provides function prototypes and data type definitions for
    the application.  Some of these are required by the system (such as the
    "APP_Initialize" and "APP_Tasks" prototypes) and some of them are only used
    internally by the application (such as the "APP_STATES" definition).  Both
    are defined here for convenience.
*******************************************************************************/

// DOM-IGNORE-BEGIN
/*******************************************************************************
* Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries.
*
* Subject to your compliance with these terms, you may use Microchip software
* and any derivatives exclusively with Microchip products. It is your
* responsibility to comply with third party license terms applicable to your
* use of third party software (including open source software) that may
* accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
* EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
* INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
* WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
* BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
* FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
* ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*******************************************************************************/
// DOM-IGNORE-END

#ifndef _APP_H
#define _APP_H

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************
#include 
#include 
#include 
#include 
#include 
#include "configuration.h"
#include "driver/usart/drv_usart.h"


// DOM-IGNORE-BEGIN
#ifdef __cplusplus  // Provide C++ Compatibility

extern "C" {

#endif
// DOM-IGNORE-END

// *****************************************************************************
// *****************************************************************************
// Section: Type Definitions
// *****************************************************************************
// *****************************************************************************
#define APP_DATA_SIZE   1
// *****************************************************************************
/* Application states
  Summary:
    Application states enumeration
  Description:
    This enumeration defines the valid application states.  These states
    determine the behavior of the application at various times.
*/

typedef enum
{
    APP_STATE_INIT,
    APP_STATE_TRANSMIT_MESSAGE,
    APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE,
    APP_STATE_RECEIVE_DATA,
    APP_STATE_WAIT_RECEIVE_COMPLETE,
    APP_STATE_TRANSMIT_DATA,
    APP_STATE_WAIT_TRANSMIT_COMPLETE,
    APP_STATE_ERROR,
    APP_STATE_IDLE,

} APP_STATES;


// *****************************************************************************
/* Application Data
  Summary:
    Holds application data
  Description:
    This structure holds the application's data.
  Remarks:
    Application strings and buffers are be defined outside this structure.
 */

typedef struct
{
    APP_STATES              state;
    DRV_HANDLE              usartHandle;
    DRV_USART_BUFFER_HANDLE bufferHandle;
    char                    readBuffer[APP_DATA_SIZE];
    volatile bool           transferStatus;
} APP_DATA;


// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Routines
// *****************************************************************************
// *****************************************************************************
/* These routines are called by drivers when certain events occur.
*/

// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

/*******************************************************************************
  Function:
    void APP_Initialize ( void )
  Summary:
     MPLAB Harmony application initialization routine.
  Description:
    This function initializes the Harmony application.  It places the
    application in its initial state and prepares it to run so that its
    APP_Tasks function can be called.
  Precondition:
    All other system initialization routines should be called before calling
    this routine (in "SYS_Initialize").
  Parameters:
    None.
  Returns:
    None.
  Example:
    
    APP_Initialize();
    
  Remarks:
    This routine must be called from the SYS_Initialize function.
*/

void APP_Initialize ( void );


/*******************************************************************************
  Function:
    void APP_Tasks ( void )
  Summary:
    MPLAB Harmony Demo application tasks function
  Description:
    This routine is the Harmony Demo application's tasks function.  It
    defines the application's state machine and core logic.
  Precondition:
    The system and application initialization ("SYS_Initialize") should be
    called before calling this.
  Parameters:
    None.
  Returns:
    None.
  Example:
    
    APP_Tasks();
    
  Remarks:
    This routine must be called from SYS_Tasks() routine.
 */

void APP_Tasks( void );



#endif /* _APP_H */

//DOM-IGNORE-BEGIN
#ifdef __cplusplus
}
#endif
//DOM-IGNORE-END

/*******************************************************************************
 End of File
 */

3. サンプルプログラムの流れ

サンプルプログラムで何をしているのか流れを追っておきます。「app.c」の中を見ていくと「app.h」で定義されたマクロや変数型が出てきます。先にapp.cの中を自分の仕様に整理し、あとでapp.hから不要なものを消していくことにします。

「app.c」の中で重要なのは、「APP_Tasks」関数の部分です。ここにステートマシンが組み込まれていて、初期化・送信・送信完了・受信・受信完了・エラー処理などの多くのステートが分けられています。「app.h」の中にはそのステートが定義されています。

公式サンプルは、起動したら最初にメッセージ送信するようになっていて、送受信のほかに「メッセージ送信」「メッセージ送信完了」のステートもあります。自分としては、シリアル受信のみ行いたいので送信部分は不要ですが、送受信したい場合でも、メッセージ送信あたりは不要かなと思います。このあたりのカスタマイズが今回のポイントになるので、よく見ておきます。

以下、app.hから抜粋

typedef enum
{
    APP_STATE_INIT,
    APP_STATE_TRANSMIT_MESSAGE,
    APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE,
    APP_STATE_RECEIVE_DATA,
    APP_STATE_WAIT_RECEIVE_COMPLETE,
    APP_STATE_TRANSMIT_DATA,
    APP_STATE_WAIT_TRANSMIT_COMPLETE,
    APP_STATE_ERROR,
    APP_STATE_IDLE,

} APP_STATES;

以下、app.cから抜粋

void APP_Tasks ( void )
{
    /* Check the application's current state. */
    switch ( appData.state )
    {
        /* Application's initial state. */
        case APP_STATE_INIT:

            appData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, DRV_IO_INTENT_READWRITE);
            if (appData.usartHandle != DRV_HANDLE_INVALID)
            {
                DRV_USART_BufferEventHandlerSet(appData.usartHandle, APP_USARTBufferEventHandler, 0);
                appData.state = APP_STATE_TRANSMIT_MESSAGE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_TRANSMIT_MESSAGE:

            DRV_USART_WriteBufferAdd(appData.usartHandle, (void*)messageBuffer, strlen(messageBuffer), &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE:
            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                appData.state = APP_STATE_RECEIVE_DATA;
            }
            break;

        case APP_STATE_RECEIVE_DATA:

            DRV_USART_ReadBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_RECEIVE_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_RECEIVE_COMPLETE:

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                appData.state = APP_STATE_TRANSMIT_DATA;
            }
            break;

        case APP_STATE_TRANSMIT_DATA:

            /* Echo the received data back on the terminal */
            DRV_USART_WriteBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_TRANSMIT_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }
            break;

        case APP_STATE_WAIT_TRANSMIT_COMPLETE:

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;

                //LED_TOGGLE();

                appData.state = APP_STATE_RECEIVE_DATA;
            }
            break;

        case APP_STATE_ERROR:

            //LED_OFF();
            appData.state = APP_STATE_IDLE;
            break;

        case APP_STATE_IDLE:
        default:
            break;
    }
}

プログラムの流れは、app.hのAPP_STATESの定義を見るとわかります。

APP_STATE_INIT,初期化
APP_STATE_TRANSMIT_MESSAGE,メッセージ送信
APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE,メッセージ送信完了
APP_STATE_RECEIVE_DATA,受信待機
APP_STATE_WAIT_RECEIVE_COMPLETE,受信完了
APP_STATE_TRANSMIT_DATA,送信待機
APP_STATE_WAIT_TRANSMIT_COMPLETE,送信完了
APP_STATE_ERROR,エラー処理
APP_STATE_IDLE,アイドル時

このステートに注目しながら、「app.c」の「APP_Tasks」関数をみていきましょう。


APP_STATE_INIT

            appData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, DRV_IO_INTENT_READWRITE);
            if (appData.usartHandle != DRV_HANDLE_INVALID)
            {
                DRV_USART_BufferEventHandlerSet(appData.usartHandle, APP_USARTBufferEventHandler, 0);
                appData.state = APP_STATE_TRANSMIT_MESSAGE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }

MHCのUARTドライバを開始しています。このとき「DRV_IO_INTENT_READWRITE」で送受信モードになっています。このときDRV_HANDLE_INVALID以外の値が返ってくるので(INVALIDは無効という意味)、無事に開始できたらバッファハンドラをセットして、「メッセージ送信」ステートへ。開始できなかったら「エラー」ステートへ。自分の欲しいシリアル受信のみに整理するためには、「送受信モード」を「受信モード」にしたり、「メッセージ送信」ステートを「受信待機」ステートにしたりするとよさそうです。実際の変更は、全体をみたあとに行います。

APP_STATE_TRANSMIT_MESSAGE

            DRV_USART_WriteBufferAdd(appData.usartHandle, (void*)messageBuffer, strlen(messageBuffer), &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }

ここではメッセージを送信しています。送信すると結果が返ってくるので「DRV_USART_BUFFER_HANDLE_INVALID」以外が返ってくれば送信成功とし送信完了ステートへ。それ以外はエラーステートへいきます。なおメッセージはapp.cの上のほうで以下のように定義されています。

static const char messageBuffer[] =
"*** USART Driver Echo Demo Application ***\r\n"
"*** Type a character and observe it echo back ***\r\n"
"*** LED toggles on each time the character is echoed ***\r\n";

APP_STATE_WAIT_MESSAGE_TRANSFER_COMPLETE

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                appData.state = APP_STATE_RECEIVE_DATA;
            }

transferStatusで送信完了かどうか調べ、完了していれば、transferStatusをクリアして受信待機モードへ。このtransferStatusは、APP_STATE_INITで設定したコールバックハンドラの「APP_USARTBufferEventHandler」の中で更新されています。

static void APP_USARTBufferEventHandler(
    DRV_USART_BUFFER_EVENT bufferEvent,
    DRV_USART_BUFFER_HANDLE bufferHandle,
    uintptr_t context
)
{
    switch(bufferEvent)
    {
        case DRV_USART_BUFFER_EVENT_COMPLETE:
            appData.transferStatus = true;
            break;

        case DRV_USART_BUFFER_EVENT_ERROR:
            appData.state = APP_STATE_ERROR;
            break;

        default:
            break;
    }
}

APP_STATE_RECEIVE_DATA

            DRV_USART_ReadBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
            if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
            {
                appData.state = APP_STATE_WAIT_RECEIVE_COMPLETE;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }

受信待機ステートでは、読み込み用バッファを追加して、「DRV_USART_BUFFER_HANDLE_INVALID」以外が返ってくれば、追加成功として、受信完了ステートへ。それ以外はエラーステートへ。

APP_STATE_WAIT_RECEIVE_COMPLETE

        if(appData.transferStatus == true)
        {
            appData.transferStatus = false;
            appData.state = APP_STATE_TRANSMIT_DATA;
        }

受信完了ステートでは、バッファへ転送が完了したかどうか調べて、完了していれば、送信ステートへ。

APP_STATE_TRANSMIT_DATA

        /* Echo the received data back on the terminal */
        DRV_USART_WriteBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
        if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
        {
            appData.state = APP_STATE_WAIT_TRANSMIT_COMPLETE;
        }
        else
        {
            appData.state = APP_STATE_ERROR;
        }

サンプルではエコーなので受信したものをそのまま送信します。メッセージ送信と同じく、送信バッファに先程受信したreadBufferを追加し「DRV_USART_BUFFER_HANDLE_INVALID」以外が返ってくれば送信成功とし送信完了ステートへ。それ以外はエラーステートへいきます。

APP_STATE_WAIT_TRANSMIT_COMPLETE

        if(appData.transferStatus == true)
        {
            appData.transferStatus = false;

            //LED_TOGGLE();

            appData.state = APP_STATE_RECEIVE_DATA;
        }

送信完了ステートでは、バッファへ転送が完了したかどうか調べて、完了していれば、受信ステートへ。

APP_STATE_ERROR

        //LED_OFF();
        appData.state = APP_STATE_IDLE;

エラーステートでは、何もせずアイドルステートへ。ここで注意するのが、サンプルのままでは次のアイドルステートとも関係してきますが、エラーが起こっても何もしないので、ずっとステートが更新されません。つまり一度でもエラーが起きると、うんともすんとも言わなくなります。実はこれにハマっていました。

APP_STATE_IDLE

アイドルステートでは、何もないので何も行いません。


4. シリアル受信のみに書き換え

シリアル受信に必要なステートだけに整理します。そのあと、細かく書き換えていきます。

APP_STATE_INIT,初期化
APP_STATE_RECEIVE_DATA,受信待機
APP_STATE_WAIT_RECEIVE_COMPLETE,受信完了
APP_STATE_ERROR,エラー処理
APP_STATE_IDLE,アイドル時

APP_STATE_INIT

シリアル受信のみモードでドライバを開始し、開始後すぐ受信待機するようにします。

UARTドライバを開始するときの引数、DRV_IO_INTENT_READWRITEを、DRV_IO_INTENT_READに。ドライバ開始成功後のステートを、APP_STATE_TRANSMIT_MESSAGEからAPP_STATE_RECEIVE_DATAに。

//            appData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, DRV_IO_INTENT_READWRITE);
            appData.usartHandle = DRV_USART_Open(DRV_USART_INDEX_0, DRV_IO_INTENT_READ);
            if (appData.usartHandle != DRV_HANDLE_INVALID)
            {
                DRV_USART_BufferEventHandlerSet(appData.usartHandle, APP_USARTBufferEventHandler, 0);
//                appData.state = APP_STATE_TRANSMIT_MESSAGE;
                appData.state = APP_STATE_RECEIVE_DATA;
            }
            else
            {
                appData.state = APP_STATE_ERROR;
            }

APP_STATE_RECEIVE_DATA

ここは変更なく、そのままです。

        DRV_USART_ReadBufferAdd(appData.usartHandle, appData.readBuffer, APP_DATA_SIZE, &appData.bufferHandle);
        if (appData.bufferHandle != DRV_USART_BUFFER_HANDLE_INVALID)
        {
            appData.state = APP_STATE_WAIT_RECEIVE_COMPLETE;
        }
        else
        {
            appData.state = APP_STATE_ERROR;
        }

APP_STATE_WAIT_RECEIVE_COMPLETE

シリアル受信成功したら、たとえば自分の変数に入れたり、実際の受信処理をここに書きます。そして次のステートが送信待機になっていたので、受信待機に変更します。これで、受信のみを扱うことができます。

            if(appData.transferStatus == true)
            {
                appData.transferStatus = false;
                
                //実際の受信処理
                //etc.自分の変数に格納するなど
                myData[0] = appData.readBuffer[0];
                      
//                appData.state = APP_STATE_TRANSMIT_DATA;
                appData.state = APP_STATE_RECEIVE_DATA;
            }

APP_STATE_ERROR

エラー発生にどうするかを書くので、エラーは無視して受信待機ステートに戻るようにしようと思います。ここに記述してもよいですが、このあとのアイドルステートで受信待機に戻るようにしておき、ここはそのままにしておきます。

        //LED_OFF();
        appData.state = APP_STATE_IDLE;

APP_STATE_IDLE

        appData.state = APP_STATE_RECEIVE_DATA;

5. ダウンロード

Harmony3は便利ですが、まだまだ使い慣れるまでは難しいなぁと思っています。でも公式のサンプルプログラムは、とても勉強になりますね。本来はここからシリアル受信のみの最小限のソースに整理していきますが、汎用的にするため、いったんここまででアップしておきます。

Githubにまとめました。

https://github.com/mathrax-s/pic32mx_mhc_uart_rcv