PIC24FでUARTのDMA転送

やりたいこと

PIC24FでUARTの送受信をDMA転送をします。開発環境はMPLABで、MCC (MPLAB Code Configurator) によるコード生成を用います。

MCCでUARTの設定

まず、MCCでUARTの設定します。MCCはUIがゴチャゴチャして分かりにくいツールなので、大きいモニタで作業したほうが良いです。ノートPCだとつらいです。直感的でないUIなので、使い方は「MPLAB Code Configurator v3.xx ユーザガイド」を読んで確認しましょう。

ここではUART3を用いるものとします。Easy Setupで設定します。

  • Enable UART にチェック。
  • Baud Rate, Parity, Data Bits, Stop Bits, Flow Controlを適宜設定します。
  • Enable UART Interrupts はチェックしません。 ←ここポイント
  • Redirect Printf to UART はチェックしません。

あと、ピン割り当てもMCCで別途設定します。ここでは説明を省略します。

MCCでDMAの設定

続いてDMAもMCCで設定します。ここではChannel 0を送信に、Channel 1を受信に割り当てます。こちらもEasy Setupで設定します。

  • Enable DMA にチェック。
  • Low Address Limit, High Address Limitは0x800, 0xFFFFのままでもOK。
Channel 0 (送信用)の設定
  • Enable Channel 0 をチェック。
  • Enable Reloadはチェックしない。
  • Transfer Mode は One Shot を選択。 ←ここポイント
  • Trigger Source は UART3 TX を選択。
  • Source Address Mode は Incremented を選択。
  • Destination Address Mode は Unchanged を選択。
  • Source Address, Destination Address, Transfer Count はここでは0のままにしておく。
  • Data Size は 8bit を選択。 ←ここポイント
Channel 1 (受信用)の設定
  • Enable Channel 1 をチェック。
  • Enable Reloadはチェックしない。
  • Transfer Mode は One Shot を選択。 ←ここポイント
  • Trigger Source は UART3 RX を選択。
  • Source Address Mode は Unchanged を選択。
  • Destination Address Mode は Incremented を選択。
  • Source Address, Destination Address, Transfer Count はここでは0のままにしておく。
  • Data Size は 8bit を選択。 ←ここポイント

設定が終われば「Generate」ボタンを押してコードを生成します。

初期化処理

UAR3とDMAの初期化は、UART3_Initialize() と DMA_Initialize() ですが、これらは SYSTEM_Initialize() の中で呼ばれます。

送信処理

ここでは特にDMA割り込みは使用せず、送信完了、送信バイト数はポーリングすることにします。

static uint16_t s_sendSize; // 送信サイズをおぼえておく

// 送信開始
// data: 送信データ
// size: 送信サイズ
void Uart_send(uint8_t *data, uint16_t size)
{
    s_sendSize = size;
    
    // UART送信いったん無効
    U3STAbits.UTXEN = 0; 
    // DMAチャンネルをいったん無効
    DMA_ChannelDisable(DMA_CHANNEL_0);
    // 転送サイズ、転送元 (メモリ)、転送先 (UART)の設定
    DMA_TransferCountSet(DMA_CHANNEL_0, size);
    DMA_SourceAddressSet(DMA_CHANNEL_0, (uint16_t)data);
    DMA_DestinationAddressSet(DMA_CHANNEL_0, (uint16_t)&U3TXREG);
    // DMAチャンネルを有効
    DMA_ChannelEnable(DMA_CHANNEL_0);
    // UART送信を有効(送信開始)
    U3STAbits.UTXEN = 1; 
}

// 送信完了したか?
bool Uart_sendComplete(void)
{
    return DMA_IsOperationDone(DMA_CHANNEL_0);
}

// 送信したデータサイズ
uint16_t Uart_sendSize(void)
{
    // 送信したサイズ = 送信すべきサイズ - 残りサイズ
    uint16_t size = s_sendSize - DMA_TransferCountGet(DMA_CHANNEL_0);
    return size;
}

受信処理

ここでは特にDMA割り込みは使用せず、受信バイト数はポーリングすることにします。

static uint16_t s_recvSize; // 受信サイズをおぼえておく

// 受信開始
// data: 受信データの格納先
// size: 受信最大サイズ
void Uart_recv(uint8_t *data, uint16_t size)
{
    s_recvSize = size;
    
    // DMAチャンネルをいったん無効
    DMA_ChannelDisable(DMA_CHANNEL_1);
    // UART受信バッファオーバーランクリア
    U3STAbits.OERR = 0;
    // UART受信バッファ読み捨て
    volatile uint16_t rx = U3RXREG; rx = U3RXREG; rx = U3RXREG; rx = U3RXREG;
    // 転送サイズ、転送元 (UART)、転送先 (メモリ)の設定
    DMA_TransferCountSet(DMA_CHANNEL_1, size);
    DMA_SourceAddressSet(DMA_CHANNEL_1, (uint16_t)&U3RXREG);
    DMA_DestinationAddressSet(DMA_CHANNEL_1, (uint16_t)data);
    // DMAチャンネルを有効
    DMA_ChannelEnable(DMA_CHANNEL_1);
}

// 受信したデータサイズ
uint16_t Uart_recvSize(void)
{
    // 受信したサイズ = 受信最大サイズ - 残りサイズ
    uint16_t size = s_recvSize - DMA_TransferCountGet(DMA_CHANNEL_1);
    return size;
}