秋月電子で購入できる「STM32F303K8T6」を使ってLチカしてみました。

「STM32F303K8T6」は秋月電子で350円で購入できるSTM32マイコンシリーズの一つです。CPUには、ARM Cortex-M4コアを搭載しており、72MHzで動作させることが可能です(内部発振器利用の場合は64MHz)。DSP命令が実装されていることと、単精度のFPUも搭載されているため信号処理演算も高速に処理できそうで良いですね。
STM32F303K8T6テスト用ボードを製作して簡単に演算特性を評価してみました。
STM32F303K8T6テスト用ボード
まず、STM32F303K8T6を用いてLチカするためのハードウェアを用意します。とりあえずデバッカとの接続とLEDが1つあればソフトが動作しているかテストができるので最低限の構成としました。

回路図

電源とST-Link接続用のコネクタ、動作確認用のLEDを設けただけの非常に簡単な回路構成としました。動作確認用のLEDを接続したIOポートは演算速度測定用のHi-Lo信号出力としても用います。
ST-LINK
ST-LINKとはSTM32/STM8用のデバッカです。これがないとSMT32マイコンのデバックおよびROM書き込みができません。最近だとSTM32の評価ボードNucleoに付属しているもの(基板一体型)を使うことが多いようです。今回は、8年くらい前に購入して積み基板と化したSTM32F4DISCOVERYが手元にあるので、このボードのST-LINK部分を有効に活用していきます。

外部マイコンデバック用の6pinコネクタが用意されているので、このコネクタ部分から必要な配線を引き出して「STM32F303K8T6」へ接続してデバックと書き込みを行いました。

このボード現在も秋月電子で購入できます。当時1600円くらいだっと思いますが、今見てみると倍くらいの値段になっていますね。ボードのバージョンが上がって機能アップしているのだとか…ST-LINKを安く入手したいのであれば、Nucleoを購入したほうが良いと思いますね。
製作
STM32F303K8T6は36pinのQFPのため2.54mmピッチのユニバーサル基板で回路を製作する場合、変換基板が必要です。手元に秋月電子の変換基板の在庫があると思っていましたが、ありませんでした…仕方ないので、基板加工機で似たような変換基板を削り出しました。

基板加工機で削りだした基板は、そのままだと銅箔面が錆びるのでハヤコートを吹き付けて表面をコーティングします。この時、部品の上からハヤコートを吹き付けても良いのですが、マーキング読みにくくなるのが嫌なのでマスキングしています。

DIP化してユニバーサル基板上に組み込みました。


STM32マイコンの開発環境の構築
STM32の開発環境は、STM32Cube IDEとST-LINK serverというソフトをインストールすることで構築できます。
STM32Cube IDEは、STM32マイコンのソフトウェア開発用の統合開発環境です。Eclipseベースの開発環境でコンパイラ、エディタ、コード生成ツールなどが全て統合されています。以下のページよりダウンロードすることができます。
ST-LINK serverは、ST-LINKを用いてデバックを行う場合に必要になります。ST-LINKは、このST-LINK serverを介して管理され、ターゲットデバイスとの通信を行うようです。このソフトをインストールしておかないと、STM32Cube IDEでデバックを開始しようとした時、「ST-LINK serverが見つからない」というエラーが表示されてデバックを開始することができません。
Lチカソフトを作る
STM32Cube IDEは最新の1.5.0、ST-LINK serverも最新の2.02をインストールしました。

新規プロジェクトの作成

インストール後起動するとインフォメーション画面が表示されます。
→Start new STM32 projectを選択

デバイスの選択画面になります。
今回用いるマイコンである「STM32F303K8」を入力して次へ

プロジェクトに適当な名前を付けて終了

プロジェクト生成時にライブラリが自動的にダウンロードされインストールされます。

ただ、私の環境ではなぜかこのライブラリファイルの解凍が正常にできませんでした。仕方がないのでライブラリファイルが保存されているフォルダから手動で解凍してインストールします。
「C:\Users\ユーザー名\STM32Cube\Repository」に保存されています。

2つのzipファイルが保存されていました。どうやらサイズの大きなほうがベースのライブラリファイルでサイズの小さいほうがアップデート分という形のようです。とりあえず「stm32cube_fw_f3_v1110」のファイルを「stm32cube_fw_f3_v1112」のファイルで上書きして元の場所に保存しておきました。

これだけだとライブラリのインストールが完了していないので
HelpからEmbedded Software Package Managerを起動してインストールを完了させます。

RefreshでOK

新規プロジェクトが作成されるとこんな感じになります。ここからコード生成ツールを利用して基本コードを生成、目的の動作をするコードを作り込んでいきます。
コードの作成

まずGUI上でポートの設定を行います。今回はLEDが接続されているPB7をOUTPUT設定にして終了です。

次にクロック周りの設定を行います。Clock configurationから設定します。
デフォルトだとPLLを使わない設定になっています。PLLを使わない場合、内部発振器から供給される8MHzのクロックがSYSCLKとなります。PLLで内部発振器からの8MHzを逓倍することで最大64MHzで動作させることができます。今回は切りよく60MHzで動作させることにしました。

設定できない周波数の場合、エラー部分が赤くなって表示されます。そういった場合は、設定可能な周波数範囲に調整する必要があります。わかりやすくて良いですね!

設定が完了したらコード生成ボタンを押してコードを生成します。

コードが生成されました。
Lチカ
自動生成されたmain.cファイルのwhileループへIOポートを制御するコードを追加します。これだけでLチカができてしまいます。
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); //LEDを点灯
HAL_Delay(1000); //1s待つ
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); //LEDを消灯
HAL_Delay(1000); //1s待つ
}
/* USER CODE END 3 */
}
実行速度の測定
ということで無事Lチカに成功したので、実行速度がどんなものなのか調べていきたいと思います。
SysTickタイマーを用いた実行速度の測定
ARM Cortex-M シリーズにはSysTickタイマーという簡易的なタイマーがコアの一部として搭載されています。この簡易的なタイマーを用いて処理実行にかかるサイクル数の測定を行います。SysTickタイマーはデータシートによるとRTOS用のタイマー用途が想定されているようです。RTOSを用いない場合は標準のダウンカウンターとして用いることができます。以下に特徴を記載します。
・24bit ダウンカウンター
・自動リロード機能
・カウンタが0になったときにマスク可能な割込み生成ができる。
・プログラム可能なクロックソース(内部、外部、分周の設定が可能)
SysTickタイマーは4つのレジスタを持っています。LOAD、CTRL、VALの3つのレジスタを使うことで実行速度の測定ができます。

CTRLレジスタを設定します。ENABLE=1に設定することでカウンタが有効になります。また、CLKSOURCE=1に設定することでクロックソースをプロセッサクロックに設定することができます。この設定で、カウンタはCPUのクロックに同期して動作することになります。測定したい処理、前後のカウンタ値を読み込むことで処理の実行スピードを測定することができるようになります。

//SysTickタイマーの設定
SysTick->LOAD = 0xFFFFFF; //カウント値の設定 24bit最大値を設定
SysTick->CTRL = 5; //SysTickタイマー制御レジスタの設定 ENABLE=1,CLKSOURCE=1
SysTick->VAL = 0; //カウンタ値リセット
//実行スピードを測定したい処理
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_SET); //LEDを点灯
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, GPIO_PIN_RESET); //LEDを消灯
//実行スピードを測定したい処理
SysTickCnt = 0xFFFFFF - SysTick->VAL; //実行サイクル数の測定
SysTickCntの値はST-LINKを用いてデバック状態で読み込みます。

Window→Show View→Live Expressions
ソフトを動かしたまま変数の値を確認することができます。
実行速度の測定
Type-3補償器(3次IIRフィルタ)を実装した時の演算速度を調べてみました。
比較対象としてC2000マイコンでも演算速度を調査して比較してみます。
実行速度を測定する処理
実装するType-3補償器です。入出力は特にどこにも接続しません。
補償器に用いる変数は最適化で消えてしまわないようにvolatileをつけて最適化を抑制しています。

//Type-3補償器係数
//定数は全部float型にしておかないとFPUが使われない
#define a1 1.257873708494f
#define a2 -0.264633152863f
#define a3 0.006759444370f
#define b0 1.062196736738f
#define b1 -0.783617871698f
#define b2 -1.045727879254f
#define b3 0.800086729181f
//IIRフィルタ変数
volatile float xn; //入力
volatile float yn; //出力
volatile float u1n; //入力加算点(u[n])
volatile float u2n; //入力前回値(u[n-1])
volatile float u3n; //入力前回値(u[n-2])
volatile float u4n; //入力前回値(u[n-3])
//**************************************
//Type-3補償器
//**************************************
u1n = xn + a1 * u2n + a2 * u3n + a3 * u4n;
yn = b0 * u1n + b1 * u2n + b2 * u3n + b3 * u4n;
u4n = u3n;
u3n = u2n;
u2n = u1n;
詳細は以下の記事にまとめてあります。
測定結果
Type-3補償器(3次IIRフィルタ)を実装した時のSTM32とC2000の演算速度の測定結果です。STM32は最適化レベルで処理速度が大きく変化する傾向がみられました。

STM32というかCortex-M4は最適化レベルで処理速度が大きく変化してくるので、最適化レベルを大きく設定していても、CPUレジスタの使用状況によって演算速度が変化する可能性があるかもしれません。試していないので何とも言えないのですが…
とはいっても最適化OFFでも216サイクルだし、60MHz動作なら3.6usで演算終わるから大抵は問題にならないです。それに秋月電子で350円で購入できるというのも大変良いです。これから小ピン用途のマイコンはSTM32をメインに使っていきたいと思います。
※秋月電子価格(2021年夏ごろ?)が改定されていました。350円→500円
最適化レベルでコードが変化する様子
さすがにType-3補償器(3次IIRフィルタ)のアセンブラを読むのは辛すぎるので、簡単なコードで確認してみました。確認したコードはIOポートをHi→Loに切り替える単純なものです。
最適化OFF

最適化(-Ofast)

IOのデータレジスタを制御するコードですが、生成されるアセンブラに変化があります。
最適化OFFの場合、3命令でIOデータレジスタへのアクセスが行われていますが、最適化(-Ofast)の場合は、1命令でIOデータレジスタへのアクセスが行われています。
アセンブラをよく見るとR0レジスタへあらかじめIOアドレスを格納しておいて、ループ処理内に含まれないように制御していることがわかります。簡単な処理でR0レジスタが更新されないため、このような配置になっていると考えられます。
おそらく、Type-3補償器の処理でもCPUレジスタの使い方に変化があるのだと思いますが、アセンブラを読み解くのは非常に時間がかかるため、気が向いたらどう変化しているかを見てみようと思います。
参考にしたページ

コメント