STM32でCMSIS-DSP使うための設定

STM32でCMSIS-DSP使うの簡単かと思ったけど結構手間取ったので記録、FFT処理とかはCMSIS-DSP使うのが一番お手軽と思ったけどね、ラズピコでも使ったけどラズピコの方が構築は簡単、依存関係がないから

<環境>

・STM32CubeIDE 1.19

・STM32F401re(Nucleoボード)

・M4 MacBook Tahoe

 

<手順> — 以下の構成でDSP/Sourceは通常の使い方なら入れてはいけない、最後に理由追加、CMSIS-DSPのカスタマイズするなら別だけど

やる事を突き詰めれば必要なCore/DSPのincludeディレクトリを持ってきてパスに追加するということ

1. 新規プロジェクト作成

File → New → STM32 Project

MCU/Board Selector でターゲット MCU を選択

例:STM32F401RE

プロジェクト名を入力

Finish

2. 必要な CMSIS-Core / DSP を準備
2.1 CubeF4 パッケージの場所確認
~/STM32Cube/Repository/STM32Cube_FW_F4_Vx.xx.x/

存在を確認するディレクトリ:

Drivers/CMSIS/Core/Include
Drivers/CMSIS/Device/ST/STM32F4xx/Include
Drivers/CMSIS/DSP/Include

2.2 個別のプロジェクトにコピーする
元ディレクトリとコピー先
Drivers/CMSIS/Core/Include <Project>/Drivers/CMSIS/Core/Include
Drivers/CMSIS/Device/ST/STM32F4xx/Include <Project>/Drivers/CMSIS/Device/ST/STM32F4xx/Include
Drivers/CMSIS/DSP/Include <Project>/Drivers/CMSIS/DSP/Include

STM32CubeIDE では
CMSIS-DSP の主要関数は静的ライブラリ(.a)として提供されている

👉 arm_math.h(宣言)+ .a(実装)で利用が成立している、arm_math.cという巨大ファイルが以前のCubeIDEのバージョンではあったらしいけど今はない、標準以外が必要になるならもってきて追加する

・元ディレクトリ構造

3. 不要なファイルの除外
3.1 除外対象— 存在しない機能を使ったコードなので、とりあえずbuild対象から除外しないと全体buildできない
Drivers/CMSIS/DSP/Examples/
Drivers/CMSIS/Core/Template/
Drivers/CMSIS/DSP/ComputeLibrary/

SupportFunctions 内の 不要な .c

3.2 除外方法

フォルダ or 複数ファイル選択

右クリック → Resource Config → Exclude from Build

Debug / Release 両方 にチェック

4. インクルードパス設定
<Project>/Drivers/CMSIS/Core/Include
<Project>/Drivers/CMSIS/Device/ST/STM32F4xx/Include
<Project>/Drivers/CMSIS/DSP/Include

Properties → C/C++ General → Paths and Symbols

Language = GNU C

Debug / Release 共通

5. ビルド

Clean → Build

#include “arm_math.h” が解決される

arm_mult_f32() / arm_rfft_fast_f32() などの基本機能は 追加なしで使用可能

👉 実体は libarm_cortexM4lf_math.a に存在するから

6. 最終ディレクトリ構成
<Project>
Drivers/
CMSIS/
Core/Include/
Device/ST/STM32F4xx/Include/
DSP/Include/
DSP/Source/SupportFunctions/(いずれ必要な場合のみ)
Src/
Inc/

・CubeIDEのディレクトリ
斜線入りはそのディレクトリを右クリックでResouce ConfigulationsでExclude from Buildでbuild対象外にしているディレクトリ

P.S. 2026/1/17

実はCMSIS/DSP/Sourceは標準的な処理ならば不要、というか入れとくとSourceファイルがエラーの嵐になる、libarm_contexM4lf_math.a(Middlewares/ST/CMSIS/DSP/Lib/GCC配下)だけでほぼ事足りるから

 

admin

freeRTOSの周辺デバイスの初期化

昨日の記事で、

freeRTOSのユーザーコードを隔離する

debuggerもしくはrunでプログラム実行させると、LCD(st7789)画面が真っ黒な画面になることがままある、デバッガーで見る限りst7789のライブラリに制御は飛んで実行はされてるけど画面出ないというのは、初期化に失敗することがあるということだろう

理由は明確ではないけど、以下のmain.c中のuser code begin2の中から呼び出すとうまくいかないことがあって、気になるのはそのあとでfreeRTOS起動しているから関係あるとすればそれかな(ADC関連もいずれここから移動する、rtos化してない時の残渣コード)

元々がfreeRTOS化した時の処理分担のクリアさから言えば、main.cにはできるだけペリフェラル向けのロジックは記述しない(Nucleoの初期化だけは実装)で、個別ペリフェラル向けの処理はそれぞれのrtos処理モジュールに持っていくのが自然だと思うよね

で以下のようにLCD表示処理のタスクのfor loopの前でST7789_Init()を呼び出すと問題は出ていない、基本思想通り個別のペリフェラルの初期化処理及びその後の実行はすべてrtos側で受け持たせるのが自然だしバグの入る余地も少ないと思う

 

admin

freeRTOSのユーザーコードを隔離する

STM32 シリーズですが、freeRTOSのユーザコードをできる限り自動生成されるコードから分離したい、というか移植性とか考えたらそれが自然

<実行環境>

・STM32F401re

・M4 MacBook Pro Tahoe

・CubeIDE 1.19.0

Core/IncとCore/Srcの下にprocというユーザコード用のディレクトリ作成、Inc/Srcの中ならばbuildはソースコードを見つけて実行してくれる

<やっていること>

defaultTaskはLEDの定期点滅、lcd_taskはst7789 LCDの表示処理を呼び出しているだけ、freeRTOSはpreemptiveなのでdafultTaskの実行が必要になればlcd_taskの実行を中断してLCD表示のtoggle処理をおこなく、処理時間は人間の時間感覚からは全くの瞬時だからLCD表示的には全く認識できないレベル

<task定義とそれを扱う仕組み>

① CubeMXで定義

lcd表示タスクは優先度を下げておく、ADC DMA結果を分析するタスクの方が優先順位高いよね普通は

Task Nameは②のTask_attributeで使われ、Entry Functionはfreertos.c中の関数名になります

② freertos.cでタスクの属性とタスクそのものの定義

③ freertos.cのタスク初期化(osThreadNew())で②の定義が使われ、Task(StartDefualtTask)等に紐付けされる

<ユーザコードの分離>

freertos.c中のコード、defaultTaskではボード上のLEDを点滅させてるだけ、StartTask03は実態はなくて、ユーザコードの呼び出しだけ

void StartDefaultTask(void *argument) {
  /* 生存確認(Lチカ)だけして大半は眠る */
  for(;;) {
    HAL_GPIO_TogglePin(GPIOA, LED_PIN);
    osDelay(500);
  }
}

/* USER CODE BEGIN Header_StartTask03 */
/**
* @brief Function implementing the lcd_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartTask03 */
void StartTask03(void *argument)
{
  /* USER CODE BEGIN StartTask03 */
  /* Infinite loop */
  for(;;)
  {
	lcd_process();  // LCD display process
    osDelay(1);
  }
  /* USER CODE END StartTask03 */
}

ユーザ処理(lcd_task.hとlcd_task.c)

cmsis_os.h、これは今は使わないけどいずれかならず使う

/*
 * lcd_task.h
 *
 *  Created on: Jan 13, 2026
 *      Author: usamiryuuichi
 */

#ifndef INC_PROC_LCD_TASK_H_
#define INC_PROC_LCD_TASK_H_

#ifdef __cplusplus
extern "C" {
#endif

void lcd_process(void);

#endif /* INC_PROC_LCD_TASK_H_ */


/*
 * lcd_task.c
 *
 *  Created on: Jan 13, 2026
 *      Author: usamiryuuichi
 */

#include "proc/lcd_task.h"
#include "st7789.h"
#include "cmsis_os.h"

void lcd_process(){
	ST7789_TestColors();
}

実際の動作は以下の動画、

全体の骨組みできてきたから、実際の処理に取り掛かれるかな

 

admin

STM32のADCの精度向上について

STM32F401REの実行精度は12bitsのADCでも10bits程度らしいのですが、条件によって精度が変わってくるのでちょっと比較をしてみた

<実行環境>

・STM32f401RE

・CubeIDE 1.19.0

・macOS Tahoe

 

<AGNDとGND>

普通に考えると、AGNDの方が品質良さそうに思うけれども、実はそれは思い違いで内部的には単にGNDと繋がっているらしく、それどころか通常のGNDよりもノイジーらしい

測定してみた結果

・測定条件

サンプリング周波数:200KHz

DMAバッファーサイズ:2,560 (512*5)

ADCのSampling Time : 56 Cycles

※ Sampling Timeの変更方法

① CubeMXのADC設定プルダウンメニューで選択

② buildでADC初期化処理に反映(赤で囲ったとこ)

ADCの入力をAGNDとGNDに切り替えて実測

計算方法

DMA転送された領域のlow側平均値を求めてるだけ、100以下判定はPWMじゃないときは不要

ということでAGNDの方がよりノイジーでした

 

<Sampling Time違いによる精度の違い>

ADCのサンプリング安定度:ADCの設定でSampling Timeが短すぎると結果が安定しない、なぜならADCサンプリンのCapの電荷蓄積ができない、あるいは外部ノイズの影響受けるから

従って、Sampling Time 最少3 Cyclesは安定性を考えると、可能な限り長く取った方が良い、200KHzサンプリング(5μSインターバル)ADC clock 21MHzだと56Cyclesの選択か

ノイズレベルの目安は1~1.5LSBとするとmax 2ぐらいが許容レベルかと思う、SPI動作は明らかにノイズ源になっているけれども、Sampling Time設定である程度は改善できていそう、実行の繰り返しで値は変わっているので数値は傾向を表しているだけと思った方が良い

Nucleoのボード(F401RE)上にC25の捺印はあるけど、部品は実装されていないけど、おそらくAGND関連じゃないかと思うのでそこにセラミックキャパシタ実装すれば振る舞いも変わってくるだろう、必ずしも改善じゃないかもしれないけども、STM32の12bits ADCの実行精度は10bitsと言われているのでそれほど高いレベルは望めない

 

admin

Debuggerでデフォルトはmain.cの最初の行で止まるを変更する¥

CubeIDEのデバッガーは、デフォルト設定ではmain.cの最初の行で止まるような設定になってるけど、いちいち止まんなくて良いよね、どう変更するの?

Run -> Debug ConfigulationsでSet break point at: のチェックを外せば良い、mainを変えればおそらく他のコードの先頭で止まるようになるんだろうね

単純だけど有用な設定だと思う

 

admin

 

STM32でST7789 LCD(240*320)をDMAで表示させる

LCD表示もDMA転送で表示させるようなライブラリがあるので、それ使ってデモプログラムを動かしてみた

https://github.com/AlexKaut/ST7789-STM32-DMA/tree/master/ST7789_STM32F103C8_Demo

<GPIOのピンの使い方>

実はCSは常時Lowにしてるから使ってないけど、

<CubeMXでのピン名称指定>

st7789.hでは以下のように定義されてるけど、最後の部分は落としてCubeMXで名称設定するのが正しいようです

<表示させてみた>

main.cにst7789関連のコードをデモプログラム参考に追加

はいこの状態では240*240設定なので、残りエリアはノイズが表示されます

<240*320対応の変更>

st7789.hファイルを変更します

<変更後の表示>

全面表示されるようになりました、LCDの種類によってはオフセット調整が必要な場合もあるらしい

<配線>

SPIのclock線(今SPI clockは21MHzに設定してます)はできるだけ短く、かつGND線とツイストしてインピーダンス下げた

実はDMA使うケースはそんなに多くないから、動作確認の比重が高いと思う、実際にはほぼプログラムモードでの制御になるから、データ準備でMCU介在するから

 

admin

STM32の周辺デバイスはCPU停止に関係なく動作継続

前回の記事、

STM32でのPWMの設定方法(コード/初期設定)

この記事に関連して、PWMの周波数が1/2**nだと綺麗にADCからの値取れるけど、任意の周波数入れると値がミックスしてグタグタ状態、なんでかなと色々調べると結局ADC変換のDMAがデバッガーで止めても動き続けるからどんどん同じ領域に上書きされるせい、たまたま1/2**nだと少なくとも位相はずれないから問題ないように見えるだけという結論

この検証のために、

① 正弦波入力してみたけどデータが綺麗に昇順降順にはならなかったから気づいた

10KΩ抵抗でGND – VCCを分割してcap(1.5μF)でカップリング、つまり電源電圧の中点をオフセット電圧にして、周波数5KHz、電圧は2V p-p(USBオシロの信号発生器を使う)、ADCのサンプリング周波数は100KHz

データレディのタイミングでDMA停止させて、ブレークポイントの先で再起動するように変更

概ね、20サンプルで1サイクルの値(単調増加と単調減少)が確認できた、数字的にも1.65 V ± 1Vぐらいの雰囲気

② PWMで5KHzを入力

こちらも概ね20サンプルで周回が確認できた、複数回停止しているので前回と値が変わったところは黄色になってる、上書きされると4095付近と0付近が混合されたメチャクチャな値になってた

<学び>

STM32のペリフェラルはCPUの状態関係なく動作するから、デバッグ時には考慮が必要

 

P.S. あとCubeMXの設定変更後に一度buildしてもdebuggerモード起動すると再度buildが必要になるのはMacのCubeIDEによくある問題らしい、二回buildすればいいだけではあるけども、debugger起動時にbuild結果の整合性チェックが通らないらしい

 

admin

STM32でのPWMの設定方法(コード/初期設定)

PWMでdutyを設定するときに、

__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 250);

のようなやり方と、

CubeMXの設定で設定は等価であると、

<環境>

・M4 MacBook ProでCubeIDE ver1.19.0

・STM32F401RE

・クロック系:ST-linkの水晶発振器を使ってFCLKは84MHz

 

<設定方法>

設定の場合にはPWM Generation *****のPulseでdutyを設定、この場合にはduty 50%になります

設定した結果の出力(84Mhz/(84*200) = 5KHz)、クロックソースが水晶なのでRC発信器と違って正確

この波形を、サンプリング周波数10KHzのADC(DMA転送)の入力に突っ込んでやると、

のように、High/Lowがサンプリングできています、同じクロックで同期しているので周期が狂うことはない

 

admin

 

CubeIDEでペリフェラルごとにソースコードファイルを分離

STM32で初期状態のままにペリフェラルを追加していくと、main.cの中にペリフェラル関連の初期化などの処理が全て組み込まれて、はっきり言って読みづらくなる

で、ペリフェラルごとにソースコード分離するのは実は簡単で、設定だけで対応できてプロジェクト開発の途中で変えても問題ない

 

.iocの設定、Project ManagerでCode Generator選択して、コード分離指定にチェックするだけ、この後プロジェクトを再度ビルドすると、

こんな感じにヘッダファイルとソースファイルがそれぞれ対応ディレクトリ以下に配置される、この方がはるかに普通だと思うよ

 

admin

ラズパイ5のデスクをUSB SSD(SATA)にしてみた

以前の記事でNVMeのSSDにした記事掲載しましたが、どうも安定して動作しないので結局SDカードに戻してましたが、今回USBのSATA SSDのケース買ってそこにイメージ写してシステムデスク(128GB SATA SSD)を変更してみた

<SDカードをUSB SSD化する>

SD Card Copierで丸ごとコピー(New Partition UUIDsにチェック忘れずに)

再起動で、

・boot orderの確認
pi@rasp5:~ $ sudo rpi-eeprom-config
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf146	#NVMe / USB / SDカードのオーダー
PCIE_PROBE=1

Sdカードよりも優先順位は高くなっているけど最優先にする

$ sudo rpi-eeprom-config --edit

で変更してrebootで、

$ sudo rpi-eeprom-config
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf164
PCIE_PROBE=1

SDカードは抜いてないから見えてるけど、USB SSDに切り替わった、loopxはsnapで仮想的にインストールされたモジュールが割り当てされるドライブネームなのでmountpointsは/snapから始まっている

ついでに性能測定、

     Category                  Test                      Result     
HDParm                    Disk Read                 361.17 MB/sec            
HDParm                    Cached Disk Read          327.60 MB/sec            
DD                        Disk Write                292 MB/s                 
FIO                       4k random read            29510 IOPS (118040 KB/s) 
FIO                       4k random write           24323 IOPS (97292 KB/s)  
IOZone                    4k read                   44614 KB/s               
IOZone                    4k write                  42302 KB/s               
IOZone                    4k random read            22563 KB/s               
IOZone                    4k random write           44361 KB/s               

                          Score: 12810         

sdカードに比較して書き込みは一桁上、MVNEe SSDのPCIe接続に比較すると1/3~1/4ぐらい、そんなものかな(以下のリンクは過去記事)

ラズパイ5のドライブをSSDにする

実装はこんな感じ、chatbotのエンビパイプの中に収めてる、

ケースは、

https://www.amazon.co.jp/dp/B0C2D1ZZGW?ref=ppx_yo2ov_dt_b_fed_asin_title

 

admin