LoginSignup
7
9

More than 3 years have passed since last update.

[STM32メモ]NUCLEO基板搭載のST-LINKからシリアル通信する

Last updated at Posted at 2019-05-07

NUCLEO基板には、ライタ・デバッガとして使えるST-LINKが搭載されており、ST-LINKを介してVirtual COMポートでシリアル通信ができるようになっている。ここでは、Keil MDKに同梱されるフリーのRTOS(RTX5)とCMSIS-Driverを使って1秒ごとにメッセージをPCに送るためのサンプルプログラムを作る手順をまとめる。

何かぱっと作るときに必要な設定は一通りまとまっているはず。

プロジェクト作成

STM32F0ではKeil MDKが無制限で使えるので、ここではuVision(統合可発環境)で開発を行う。まずは、必要なソフトウェアパッケージ(Keil RTX5とUSART Async)を選択・依存関係を解決後、STM32CubeMXでHAL(Hardware Abstraction Layer)の設定を行う。
image.png

HAL設定

ST-LINKにつながっているUSARTのピンと割り込み・DMAの設定を行う。

回路図を調べる

NUCLEO基板の回路図から、使用している基板の回路図を調べる。以下はNUCLEO-F072RB基板の回路図の抜粋。ST-LINKにつながっている信号は、USART_TXUSART_RXと書かれている。
image.png

USART_TXUSART_RXはそれぞれPA2PA3というピンであることが分かった。これを、STM32CubeMXで設定する。

ピン設定

Pinout viewから、PA2PA3に割り当てられているUSART2の信号USART2_TXUSART2_RXを選択する。この段階では、USART2の設定をしていないので、ピンはオレンジで表示される。
image.png

次にConnectivityからUSART2の機能を選択し、モードをDisabled(無効)からAsynchronous(非同期…いわゆる普通のシリアル通信)に変更する。これにより、USART2が利用できるようになる。同時に、PA2PA3の表示がオレンジから緑になる。(有効な機能が割り当てられたということ)

CMSIS-Driverのための設定

USARTはSTM32CubeMXが生成するHALを直接叩くことで通信できるが、もう少し抽象化されたCMSIS-Driverというドライバを使えば、簡単にDMAを用いた通信ができるようになっている。CMSIS-DriverはテンプレートはARMのリポジトリから入手できて、色々なマイコンに移植できるらしい。Keil MDKにはCAN, I2C, SPI, USART, USBなどが同梱されているj。抽象化している分、効率は若干劣るらしいものの、ぱっと使えるので便利。

CMSIS-DriverはSendReceiveといった、バッファの送受信を行えるようになっているので、割り込みの設定が必要となる。(しないとTX_RX_BUSYという状態で止まってしまう)

DMAを使わない場合

USART2NVIC(Nested Vector Interrupt Controller)で、USART2の割り込みを有効化する。この場合、CMSIS-Driverは1文字送信/受信完了するたびに、割り込みハンドラでバッファからデータを読み出す/書きこむ。
image.png

DMAを使う場合

DMAの設定をしてから、USART2の割り込みを有効化する。この場合、CMSIS-Driverはバッファ全部をDMAで送信/受信完了してくれる。(割り込みハンドラで後処理する)
image.png

RTOSの設定

動作確認用の周期生成に、贅沢にもRTOSを使う。

RTOSにはRTX v5という、GitHubで公開されていてApacheライセンスのもとフリーで利用できるものを使う。Keil MDKにも同梱されているので、STMF0シリーズ向けならuVisionからパッケージを追加するだけで使えるので利用しやすい。

割り込み優先度の設定

RTX5のマニュアル(Create an RTX5 Project)を参考に、割り込み優先度を設定する。
image.png

STM32F0では、優先度が高い順に0~3の4つが選べるので、以下のように設定する。(SVCは最低+1だが、これは優先度が+1であって優先度の数字が+1ではないので注意)
image.png

なお、それぞれの役割はTheory of Operationの項に書かれている。ざっくり以下のような使われ方をする。

  • SysTick: 周期的にタスクを起床するために使われる割り込み。基本的に他の割り込みより低くしたいので最低優先度にする。
  • PendSV: ハンドラモード(多分ISR:割り込みサービスルーチン)から要求のあった処理を行うための割り込み。(タスクのスケジュールとかリソースのロックが)SysTickと干渉しないよう、SysTickと同じ優先度にする。(下図のSVに該当…ISRから要求があったRTOSへの要求が、SysTickの処理が終わった後で実行されるようにするために使われる)
  • SVC: タスク上でOSの特殊な機能(タスクのロック解除とか)を使うためのソフト的な割り込み。他のタスクに割り込まれないようにしたいため、前記2つより優先度が高い。 image.png

コード生成の設定

RTOSがSysTick、PendSV、SVCの割り込みハンドラを定義するので、STM32CubeMXではこれらの割り込みハンドラの生成を行わないようにする必要がある。(生成してしまうと、同名の関数が二重に定義されているとリンカに怒られる)

image.png

お試しコード

実はこれはCMSIS-DriverのUSARTのサンプルをほぼそのままコピペしたもの。

main.c抜粋
1. インクルード
/* USER CODE BEGIN Includes */
#include "RTE_Components.h"
#include "stm32f0xx.h"
#include "cmsis_os2.h"
#include "Driver_USART.h"
/* USER CODE END Includes */
...

2. USARTのアクセス構造体のExtern宣言(実体はstm32_hal_uart.c)
/* USER CODE BEGIN PV */
extern ARM_DRIVER_USART Driver_USART2;
/* USER CODE END PV */
...

3. 1秒ごとに(送信完了しているかにかかわらず)"hello"と送信する
/* USER CODE BEGIN 0 */
__NO_RETURN void thread_a( void * arg ) {
    while( 1 ) {
        osDelay( 1000 );
        Driver_USART2.Send( "hello\n\r", 7 );
    }
}
/* USER CODE END 0 */


int main(void)
{
...
  /* USER CODE BEGIN 2 */
4. USART2のドライバ設定(ボーレートやフロー制御など)
  Driver_USART2.Initialize( NULL ); /* CMSIS-Driverからのコールバックは無視 */
  Driver_USART2.PowerControl( ARM_POWER_FULL );

  /*Configure the USART to 38400Bits/sec */
  Driver_USART2.Control(ARM_USART_MODE_ASYNCHRONOUS |
                    ARM_USART_DATA_BITS_8 |
                    ARM_USART_PARITY_NONE |
                    ARM_USART_STOP_BITS_1 |
                    ARM_USART_FLOW_CONTROL_NONE, 38400);

  /* Enable Receiver and Transmitter lines */
  Driver_USART2.Control (ARM_USART_CONTROL_TX, 1);
  Driver_USART2.Control (ARM_USART_CONTROL_RX, 1);

5. RTOS起動(3.のタスクを登録)
  osKernelInitialize();                            // initialize RTX
  osThreadNew(thread_a, NULL, NULL);               // create some threads
  osKernelStart ();                                // start RTX kernel
  /* USER CODE END 2 */
…略…
}

これだけで1秒ごとに"hello"と遅れてしまうのだから、良い時代になってしまった。ちなみに、ST-LINKは自動でボーレートを調整してくれるみたいなので、コードと、ターミナルソフトのボーレートさえ合わせれば良いようになっている。
image.png

この後の発展

上記のサンプルは送信完了も確認せず、1秒ごとに送信するだけだが、受信したり、送信完了をチェックしたいという場合があると思う。

その時は、アクセス構造体の初期化時に登録するコールバック関数(上記ではNULL)内で、抽象化されたイベント(受信完了、送信完了、エラー発生など)を判定し、その場で応答するか、OSのサービスコールでタスクを起床するなどして、イベントに応じた処理を行うと良いらしい。

この辺のことはCMSIS-DriverのTheory of Operationを読むと載っているのでそちらを参照のこと。
image.png

各ツールの連携について

  • CMSIS-Driver: HALが決まった命名で
  • STM32CubeMX: HAL

参考

7
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
9