STM32 LLでUARTしてみる 送信(割り込み編)

STM32 LLでUARTしてみる 送信(割り込み編)

皆さま こんにちは。

今回は LL UART通信の送信を割り込みを使って行ってみます。

LLってな~に?という方は HALとLL の記事をご覧ください。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.5.0
Configurator: STM32CubeMX Version6.1.0
Board: STM32Nucleo-F401RE

プロジェクトを作成する

IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。

Project 名に F401UartLLTxInt と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。

UARTをLLで使う準備をする

Project Managerタブを選択し Advanced Settings の USART を HAL (初期値)から LL に変更します。(コンボボックスで選択する)

HALの部分をクリックするとコンボボックスのリストが表示されるので、LLを選択します。

Project – Build All を選択します。

Do you want Generate Code ? と聞いてくるのでRemenber my decision のチェックボックスにチェックを入れて OKを押します。

This action can be associated with C/C++ perspective.Do you want to open this perspective now?

と聞いてくることがあれば Remenber my decision のチェックボックスにチェックを入れて Yesを押します。

UARTのパラメーターを変更する

Project Explorer から F401UartLL.ioc をダブルクリックして GUIの画面を開きます。

Pinout & Configuration – Connectivity – USART2 を選択します。

Modeのところは Asynchronus が選択されていて使える状態になっています。(使わない場合は Disable)

今回もパリティを使ってみます。
Configuration – Parameter Settings のところで以下のように設定します。
(デフォルトからの変更はParityを None -> Even にするだけです)

Baud Rate : 115200
Word Length : 8bits
Parity : Even
Stop Bits : 1

パリティとデータ長とSTM32

STM32の場合、一般的な通信仕様と少し異なる部分があるので確認しておいてください。

この図はマイコンのTX, RXラインを流れるシリアルデータのフォーマットです。
データは左から右に向けて流れていきます。

S : スタートビット 論理0
D0-D7 : データビット
P : パリティビット
ST : ストップビット 論理0

UARTによる非同期通信の場合は LSBファーストと言い、D0ビットから送り始めます。

上の図の場合、データ長は7ですが STM32の場合、8を指定します。
下の図の場合、データ長は8ですが STM32の場合、9を指定します。
(STM32の場合パリティはデータ長に含まれると考えれば良い)

マイコンUSARTパラメータの設定でパリティを ODD または EVEN に設定すると、

・送信時には

マイコンがパリティを計算してデータビットの次に付加してくれるのでユーザーはコーディング不要です。

・受信時には

マイコンが送られてきたシリアルデータからパリティを計算し、パリティビットと比較してエラーを検出してくれます。
結果はステータスレジスタの PEビットで確認することができます。
ユーザーはその判定と処理だけを書けば良いことになります。

偶数パリティとは?

偶数:データビット(7または8)の和とパリティビット(1ビット)の和が 0 になるようにパリティをつくります。
奇数:データビット(7または8)の和とパリティビット(1ビット)の和が 1 になるようにパリティをつくります。

データ8ビットにおける偶数パリティの例.

データが 0b00000000 の時パリティは 0
データが 0b00000001 の時パリティは 1
データが 0b00000010 の時パリティは 1
データが 0b00000110 の時パリティは 0
データが 0b11111111 の時パリティは 0

データ8ビットにおける奇数パリティの例.

データが 0b00000000 の時パリティは 1
データが 0b00001000 の時パリティは 0
データが 0b11111111 の時パリティは 1

割り込みの設定をおこなう

Configuration – NVIC Settings の USART2 global interrupt の Enabled にチェックを入れます。

ここで一度ビルドしてコードを確認してみましょう。
Project – Build All を選択します。

Coretex-Mと割り込み

Coretex-Mマイコンは割り込みのベクタテーブルにハンドラのアドレスが格納されています。

ベクタテーブルは Project Explorer で F401UartLL – Core – Startup にある startup_stm32f401retx.s というファイルに記述されています。

アセンブラのソースファイルです。

181行目に次の記述がありました。

.word USART2_IRQHandler /* USART2 */

USART2の割り込みが有効で、USART2に関する割り込み要因を検出すると、この関数にジャンプしてくれるようになっています。

USART2_IRQHandler()は Core – Src – stm32f4xx_it.c に書かれているので、割り込みの処理をこの部分に記述します。

コーディングする

注意点は /* USER CODE BEGIN xxx */ と /* USER CODE END xxx */ の間にコーディングすることでしたね。

※そうしなかった場合、コンフィグレーターを使った場合にせっかく書いたコードが消されてしまいます。

まず main.c

冒頭の付近で

/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE BEGIN Includes */
void TransmitData(void);

#define _CR 0x0d
#define _LF 0x0a

uint8_t buffer[] =
{
  '#', 'a', 'b', 'c', 'd', 'e', _CR, _LF
};

bufInfo bi;

/* USER CODE END Includes */

元々書かれているSystemClock_Configのプロトタイプの前あたりに、TransmitData()の実装を書きます。

/* USER CODE BEGIN PV */
void TransmitData(void)
{
  bi.ptr = &buffer[1];	// 割り込みでは2バイト目から
  bi.size = sizeof(buffer);
  bi.counter = 0;
  LL_USART_TransmitData9(USART2, (uint16_t)buffer[0]);
  LL_USART_EnableIT_TXE(USART2);
}
/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);

while()ループ内

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
  /* USER CODE END WHILE */
  TransmitData();
  HAL_Delay(3000);
  /* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

main.h

main.h は Core – Inc 内にあります。
bufInfo型の構造体をつくったので、定義しておきます。

/* USER CODE BEGIN Includes */
typedef struct
{
	uint8_t *ptr;
	uint16_t size;
	uint16_t counter;
} bufInfo;
/* USER CODE END Includes */

stm32f4xx_it.c

冒頭付近で変数を参照する記述を行います。

/* USER CODE BEGIN Includes */
extern bufInfo bi;
/* USER CODE END Includes */

207行目(付近)に割り込みハンドラ関数が存在するので中に以下のコードを書きます。

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
  uint8_t d;
  if (LL_USART_IsActiveFlag_TXE(USART2))
  {
    bi.counter++;
    if (bi.counter >= bi.size)
    {
      LL_USART_DisableIT_TXE(USART2);
      return;
    }
    d = *(bi.ptr++);
    LL_USART_TransmitData9(USART2, (uint16_t)d);
  }
  /* USER CODE END USART2_IRQn 0 */
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

ビルドする

ビルドしてエラーがないことを確認しておきます。
PC のアプリ Tera Term を使って通信するのでUSBケーブルでPCとボードを接続しておきます。

Tera Termの設定

何度も出てきているので画像は省略します。
以下の要領で設定します。

COMポート : 新しい接続でシリアルを選択し、STLink Virtual COMのポートを選択する。

設定 – シリアルポートより

スピード : 115200
データ : 7bit
パリティ : even
ストップビット : 1bit
フロー制御 : none

端末の設定でローカルエコーにチェックを入れます。

プログラムの概要

今回は送信するデータに CR LF を付けて、次々にデータが送られてくることをわかりやすくしました。

TransmitData()では、
LL_USART_TransmitData9(USART2, (uint16_t)buffer[0]) で1バイト目(#)を送信します。
LL_USART_EnableIT_TXE(USART2) で送信データエンプティ割り込みを有効にします。

これで1バイト目のデータを送ったら割り込みが入るので、2バイト目以降の送信を割り込み処理内に書いています。
LL_USART_TransmitData9()の第2引数は uint16_t 型なのでキャストしました。
パリティ付きの場合、この関数を使うのでしたね。
HAL_Delay()して3秒毎にデータを送っています。

割り込みハンドラのUSART2_IRQHandler()

LL_USART_IsActiveFlag_TXE()で送信データエンプティか確認し、エンプティなら次のデータを送ります。

8バイト全て送り終えたなら LL_USART_DisableIT_TXE() で割り込みを禁止します。

プログラムを動かしてみる

IDEで Run – Debug から Run – Resume してプログラムを動作させます。

Teram Term上で3秒毎に #abcde が表示されれば成功です。

いかがでしたか、うまく動きましたか?

次回は、よりCPUの使用効率がよいDMAを使った送信についてみていきます。

お疲れさまでした。

関連記事

STM32 LLでUARTしてみる 送信(ポーリング編)
STM32 LLでUARTしてみる 送信(DMA編)

LLカテゴリの最新記事