CubeIDEにおけるSTM32マイコンのUARTハードフロー制御について

# UARTのハードフロー制御について
UARTではハードフロー制御を取り入れることでデータを確実に伝達することができます。
詳しくは以下の記事にまとめてあるのでそちらを参考にしてください。

https://qiita.com/me26003t/items/0e0722d093a6e9221b72

本記事ではCubeIDEを用いたSTM32マイコンでのハードフロー制御の設定と、その振る舞いについて記述します。

::: note info
**本記事の環境**
- IDE：STM32CubeIDE Version: 1.18.1 @ Windows11
- MCU：STM32F401RDT6
    - 使用ペリフェラル：USART１
:::

# STM32マイコンでのハードウェアフロー制御
Coretex系MCUの代表格であるSTMマイコンですが、多くのSTM32シリーズにてハードフロー制御付きのUSART,UARTペリフェラルをサポートしています。参考資料として、STM32WBシリーズの資料となりますが、UARTペリフェラルについてSTMicroelectronics社が公開している資料がありますのでリンクを貼っておきます。

https://www.stmcu.jp/wp/wp-content/uploads/files/presentation-ja/STM32WB/33_STM32WB-Peripheral-USART-interface-(USART)_J.pdf

さて、STM32マイコンにおけるハードウェアフロー制御ですが、フロー制御がサポートされているUARTペリフェラルにおいて適切に設定を行えば、特別なフローをソフトウェアに実装することなく制御を実行することができます。次章ではCubeIDEでの設定方法について説明します。

# CubeIDEでのハードウェアフロー制御の設定 
以下にとあるプロジェクトにて使用したiocの画像と各モードの対応表を添付します。
ハードフロー制御の設定は実に簡単で、対象のUSART設定画面にて"Hardware Flow Control(RS232)"の項目で使用したいモードを選択するだけです。
| モード     | 機能                |
| ------- | --------------------- |
| Disable | ハードフロー制御を使用しない |
| CTS Only | 送信可能かの確認用入力のみ使用 |
| RTS Only | 受信可能の確認用出力のみ使用 |
| CTS/RTS | 送信可能かの確認用入力、受信可能の確認用出力のどちらも使用 |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1092604/97df6bef-5292-4f89-8126-b5525b8e10a7.png)

上記のモードを設定し、HALコードを生成することでmain.c内に以下のUSART初期化関数が生成されます。huart1.Init.HwFlowCtlに渡されるマクロが設定したモードと対応していれば無事生成されています。

 ``` c:main.c
/**
  * @brief USART1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART1_UART_Init(void)
{

  /* USER CODE BEGIN USART1_Init 0 */

  /* USER CODE END USART1_Init 0 */

  /* USER CODE BEGIN USART1_Init 1 */

  /* USER CODE END USART1_Init 1 */
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 57600;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_RTS_CTS;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART1_Init 2 */

  /* USER CODE END USART1_Init 2 */

}
```

あとは、UART送受信を行いたいタイミングで`HAL_UART_Transmit`や`HAL_UART_Recive`を行えば勝手にハードフロー制御付きのUART通信を行ってくれます。
なお、**UART初期化前（`MX_USART1_UART_Init`実行前）ではRTSピンはHigh(入力不可）が出力されるため、初期化後の通信開始タイミングには気をつけてください。**

# ハードフロー制御時のHAL関数の振る舞い。
ハードウェアフロー制御有効化のUART通信においては、ソフトウェア実行、割り込み実行、DMA実行での振る舞いが異なります。以下に各方式での違いを表にしておきます。

| 通信方式 | 使用関数 | RTSの動作 | CTSの動作 | 備考 |
|-----------|-----------|-------------|-------------|--------|
| ポーリング方式（ソフトウェア制御） | `HAL_UART_Transmit` / `HAL_UART_Receive` | 使用されない（手動制御不可） | 有効：CTSがHighなら送信ブロック | 長時間ブロックする可能性あり。非推奨。 |
| 割り込み方式（非同期） | `HAL_UART_Transmit_IT` / `HAL_UART_Receive_IT` | 有効：受信バッファが埋まるとHighになり相手の送信を制御 | 有効：送信中にCTSがHighになるとUARTが自動停止 | アプリ側でバッファ処理が遅いとRTSが頻繁にトグルする可能性あり |
| DMA方式（非同期・高速） | `HAL_UART_Transmit_DMA` / `HAL_UART_Receive_DMA` | 有効：DMA受信バッファが満杯になるとHigh、空きでLow | 有効：送信中にCTSがHighになると自動停止、Lowで再開 | 最も安定した方式。高速通信や大量データ向き |

# おわりに
STLINKでの簡易デバッグや、同基盤上でのMCU間簡易通信にはここまで格式張った通信制御は必要がないかと思いますが（むしろRTS/CTSのタイミングを上手く設定できなかった場合、オシロやロジアナを引っ張り出して沼る可能性がある）、無線モジュールとの通信やPCとの大容量高速通信などの不安定かつ通信に一定の品質を担保しなければならない場合に事故を回避する手段としてハードウェアフロー制御は非常に有効です。
必要なシチュエーションでは是非ともハードウェアフロー制御の採用をご検討ください。