STM32 DMAを使ったUART通信ではまる


はじめに

UARTでDMAを使いたかった。ところが...

送信時,どうも連続して送ろうとすると失敗する。

受信時,配列に最初の1このデータしか入力されない。

Mem側をIncrement指定しているのに,デバッガで止めてみると無視されている。
MINC=1になってほしいのに0になっている。

レジスタを触って解決するのは何か気持ちが悪いので,解決方法を探ってみた。

https://community.st.com/s/question/0D50X0000Bmob3u/dma-not-working-in-cubemx-generated-code-order-of-initialization

DMAをめぐるトラブルは結構あるみたい。

https://community.st.com/s/question/0D50X0000BVnBhASQV/bug-report-dma-and-adc-initialization-order-changed-in-stm32f4-hal-v1241-causing-incorrect-adc-operation

このあたりの情報で解決した。Device Configuration Tool - Project Manager - Advanced Setting で MX_DMA_Init を上の方にもっていく。

見つけた解決法

以下,手順を覚書。

RX/TXでDMAを追加します。

さらにこの画面で,

こんな感じにします。

さらに USART2 global interrupt を有効にします。これをしないと,送信終了,受信終了がわかりません。

main関数の前です。送信完了がわかるようにします。また,受信完了したら再度受信スタンバイします。ここではついでにLEDをToggleしてます。

main.c
/* USER CODE BEGIN 0 */

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
    huart2.gState = HAL_UART_STATE_READY;
}

uint8_t UART2_RX_Buffer[8];

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
    if (huart->Instance == USART2) {
        HAL_UART_Receive_DMA(&huart2, UART2_RX_Buffer, 8);
    }
}
/* USER CODE END 0 */

そしてmain関数の序盤です。

  • 送信完了を検知してから次のデータを送らないと,無視されてしまいます。
  • 受信待機開始します。
main.c
    /* USER CODE BEGIN 2 */

    HAL_UART_Transmit_DMA(&huart2, (uint8_t*) "Boot NUCLEO\r\n", 13);
    while (huart2.gState != HAL_UART_STATE_READY) {
    } // 送信完了を確認
    HAL_UART_Transmit_DMA(&huart2, (uint8_t*) "Type any key.\r\n", 14);
    while (huart2.gState != HAL_UART_STATE_READY) {
    } // 送信完了を確認
    HAL_UART_Transmit_DMA(&huart2,
            (uint8_t*) "Then toggle LED each 8 letters.\r\n", 33);

    HAL_UART_Receive_DMA(&huart2, UART2_RX_Buffer, 8);

    /* USER CODE END 2 */

    /* Infinite loop */
    /* USER CODE BEGIN WHILE */
    while (1) {
        /* USER CODE END WHILE */

        /* USER CODE BEGIN 3 */
    }
    /* USER CODE END 3 */

補足

色々な事情で,NUCLEO-F303K8 (CPU:STM32F303K8) だけでなく,STM32G0 の CPU 2種類も,同様であることを確認しました。

また暫定ですが,ADCでDMAを使いたい場合も,DMA初期化をADC初期化の前に持ってくる必要がありそうです。

参考URL

みなさんに感謝です!