STM32のDMAとNucleoにおけるUART通信

DMA on STM32 and UART on Nucleo
 
DMA(Dicrect Memory Access)はCPUを使用せずにメモリーを転送する技術で、オーディオなどの継続的に送受信する必要のある信号のやりとりでは必須のテクニックです。STM32CubeMXでは各種モジュールとを紐付けることで、複雑なプログラムを組むことなくDMAによるデータの送受信が行えます。

試しにUSART2にDMAを割り当てて動かしてみましょう。STM32CubeMXの「USART2→DMA Settings」を開き、USART2_RX(受信)とUSART2_TX(送信)にそれぞれDMAのストリーム(STM32F4では2つのDMAコントローラーそれぞれに8つのストリームを設定できる)を割り当てます。
DMAによるシリアルデータの転送は「HAL_UART_Transmit_DMA」で、受信は「HAL_UART_Receive_DMA」関数を使用します。それぞれの転送が完了すると割り込みが発生しますが、割り込みはUSARTの割り込みではなく、DMA側の割り込み関数(DMA1_Stream6_IRQHandlerなど)で処理します。
stm32f4xx_it.c
void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */

  /* USER CODE END DMA1_Stream5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
  /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
  OnUart2RxDmaComplete();  // Uart受信バッファの転送が完了
  /* USER CODE END DMA1_Stream5_IRQn 1 */
}

void DMA1_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */
  /* USER CODE END DMA1_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_tx);
  /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */
  OnUart2TxDmaComplete();  // Uart送信バッファの転送が完了
  /* USER CODE END DMA1_Stream6_IRQn 1 */
}
app.c
#include "main.h"
#include "app.h"
#include <memory.h>

const char msg1[] = "Button pressed.\n";
const char msg2[] = "Received.\n";
char result[10];

extern UART_HandleTypeDef huart2;

void InitApp()
{
	// データの受信を開始する(main.cでメインループの前で呼び出す)
	HAL_UART_Receive_DMA(&huart2, (uint8_t*)result, 10);
}

void OnUart2RxDmaComplete()
{
	// 転送可能状態なら受信した旨を返す
	if(huart2.gState == HAL_UART_STATE_READY){
		HAL_UART_Transmit_DMA(&huart2, (uint8_t*)msg2, strlen(msg2));
	}

	// 受け入れを再開する
	HAL_UART_Receive_DMA(&huart2, (uint8_t*)result, 10);
}

void OnUart2TxDmaComplete()
{
	// 転送ステートをリセット
	huart2.gState = HAL_UART_STATE_READY;
}

void OnInterruptEXTI15_10()
{
	// スイッチを押したときに転送可能状態ならメッセージを送信
	if(huart2.gState == HAL_UART_STATE_READY){
		HAL_UART_Transmit_DMA(&huart2, (uint8_t*)msg1, strlen(msg1));
	}
}
ではターミナルエミュレータ「Tera Term」のシリアルモニター機能で、送受信のテストをしてみましょう。Tera Termを起動したら「新規接続」より「シリアル→STMicroelectronics STLink...」を選択します。
Nucleoでは、USART2ピン(PA2/PA3)はSTLinkへと繋がっており、ボード上のピンは断線されていて機能していない状態です。写真のように市販のUSBシリアル変換器を接続しても、開発ボードを半田付けするなりして結線しない限り、何も起きないので注意しましょう。
Tera Termでの「端末設定」で「改行コード」を「LF」にし、「ローカルエコー」にチェックを入れます。続いて、「シリアルポート設定」でスピードをSTM32におけるシリアル通信設定を反映させます。
プログラムが正常に機能すれば、青スイッチを押すたびにメッセージが表示されます。なお、DMA割り込みが発生するタイミングは、DMA送信で指定したメモリーが規定量に達したときなので、Tera Termで文字を入力すると、改行を待たずしてメッセージが返されています。
2019/03/06