ラズピコでRust

ラズベリー財団がVScodeのpcio拡張機能でRust(実はZephyrも)サポートするようになったので動かしてみた

<構成と環境>

・ラズピコ2(無線なし)

・M4 MacBook Pro 32GB

・純正デバッグプローブ

 

<手順>

すでにインストール済みのpico拡張機ののメニューにはRustプロジェクト作成のメニューが出ています

ここでNew Rust Projectを選択するとLチカのサンプル、実は初代ラズピコやラズピコのRISCコア対応にもなってます、が開かれます

ラズピコ2にデバッガー接続、ピン立っていないので追加で半田付けして接続

接続確認は、

% probe-rs list
The following debug probes were found:
[0]: Debug Probe _CMSIS_DAP_ -- 2e8a:000c:E663AC91D3826F39 (CMSIS-DAP)

のように見えればつながっています

 

この状態でほぼすぐにデバッグができました、Runはファイルモードでしか機能しませんがデバッグは上位互換なので問題なし、デバッグだと.uf2のファイルも必要なくて、バイナリそのままがラズピコに書き込まれます

これ、デバッガーでブレークポイント設定して止めたとこ

 

めちゃくちゃ簡単に環境構築できるから、まだ無線関連のcrateは不備だけど、もうRustでできることはRustz以外の選択は、ラズピコではないと思う

 

admin

STM32開発環境はVScodeにしない方が正解

STM32のVScode拡張機能でサポート出来るらしいのでやってみたけど、既存のraspberry picoのc開発環境を破壊してしまう

具体的にはVScodeの全体設定ファイルであるsetting.json、

~/Library/”Application Support”/code/User/settings.json

をSTM32の拡張機能がある状態ではstm32の環境に汚染(上書き)させてしまう、profileを分離というのもあるんだろうけど、そんな面倒なことやるなら、STM32の環境ではbuildはCubeIDE(クラシックだけど確実)で実行してソースコードのupdateはたとえばcursorとか使い分けるのが正解だよね

実はraspberry pico tool(VScode拡張機能)もc言語のbuild環境構築を楽にするためのツールなんだけどね

もはやラズピコもRustを公式でサポート(Wi-Fi, BLEはcrateが未完)始めてんだから、今後はc言語使わなければいけないケースは徐々に減っていくよ間違いなく

それと、これから参入の若手はRustとcどっち選ぶと言われたら、間違ってもcは選ばないはずだしね

ということでc言語は他に選択できない時だけ使うようにしましょう

 

admin

 

スペアナの見栄えを改善

ベースレベルがかなりバタつくので、平均化処理を入れてみた

やっていることは、過去の平均値の重み付け0.9、最新の計算値の重み付け0.1で加算していくだけ、ベースレベルが暴れなくなるので視覚上の効果は結構大きい改善、スペアナ的になるという意味でね

	/* 5. 平均化と実効値計算(振幅スペクトル : db) */
	for (uint32_t i = 0; i < FFT_LEN / 2; i++) {
		float32_t real = fft_out[2 * i];
		float32_t imag = fft_out[2 * i + 1];
		cur[i] = (real * real + imag * imag) * HANN_GAIN_CORRECTION + 1e-12f;
		// averaging
		if (first) {
			fft_ave[i] = cur[i];
		} else {
			fft_ave[i] = fft_ave[i] * 0.9f + cur[i] * 0.1f;
		}
		fft_rms[i] = (int16_t) (10.0f * log10f(fft_ave[i]));
	}
	first = false;	// to set first flag to false

<写真>

<条件>

以前の記事と変わらずですが、

・入力は5KHzのduty50%のPWM

・ADCサンプリング周波数 200KHzで一次IIRフィルタ(cut off 20KHz)処理後に1/5 decimation

・Hann windows処理

・FFT実行後に平均化処理後に対数スケール変換

前の記事の補足でも書いたけど、一次IIRフィルタにすることで15KHzの第三高調波はほぼ理論通りのおよそ10db(正確には9db)に収まってる、ただし20KHzに近づくとノイズレベルが落ちているのはフィルターの影響です

 

admin

 

spi LCDにFFT結果を表示させる

前回からの続きですが、spi LCD (320*240 : st7789)に結果を表示させます

一点ハマりどころは、ドライバーの標準サポートが240*240までのこと

https://github.com/AlexKaut/ST7789-STM32-DMA

実は単純に数字の書き換えだけではうまくいかなくて、結局数箇所変更

物理インターフェースはspi2使うように.hファイル変更(これは以前に実施済み)

/* choose a Hardware SPI port to use. */
#define ST7789_SPI_PORT hspi2		// to accommodate with actual usage
extern SPI_HandleTypeDef ST7789_SPI_PORT;

画面サイズの定義は、これも.hファイル変更

#ifdef USING_240X320
    #if ST7789_ROTATION == 0 || ST7789_ROTATION == 2
        #define ST7789_WIDTH  240
        #define ST7789_HEIGHT 320
    #else
        #define ST7789_WIDTH  320
        #define ST7789_HEIGHT 240
    #endif

    #define X_SHIFT 0
    #define Y_SHIFT 0
#endif

に変更


初期化の処理に最後に、これは.cファイル変更

void ST7789_Init(void)

 	ST7789_SetRotation(ST7789_ROTATION);	// to change for using 320*240 Panel
 	
 を追加、

この変更で、320*240でかつローテーション表示(横方表示)ができるようになりました、結果表示はこんな感じですが、

<動画はこちら>

マイムービー – 大- 540p

 

<気付き>

① ノイズレベルがかなり変動、それはFFT処理の中身からしてそう言うものらしいので見せるための処理、例えば平均化とかが必要、USBオシロのFFT機能で見るとフラットに見える、ただしS/Nが基本波で60db程度取れているのはほぼ同等

② 基本波と第三高調波の差が見かけ20db程度ある、入力は5KHzのDuty 50%のPWMなので理屈ではおよそ9dbぐらいの際になるはず、ノイズレベルのカーブからするとADCの入力にこんな周波数特性があるのかもしれない

–> 今の二次のIIRフィルタは15KHzで10db程度減衰することが問題なのでカットオフ20KHzの一次IIRフィルタに変更でまともそうになった、ノイズレベルも均一化してるしね

LCDへの表示処理は見かけ1秒近くかかっているので、FFT処理は比較すると瞬時

実測でFFT計算関連書処理時間はおよそ3.2ms、512samples*40Kspsだとおよそ13msなので十分にFFT処理は高速、実際のFFT計算は0.5msぐらいで完了するだろうから前後の処理が大半を占めています

<rtosの処理>

・ADCサンプル/FFT実行タスク

表示タスクにはFETした結果が準備できたら通知(xTaskNotifyGive)、LCD表示タスク側の処理が終わったら送られる通知を待つ(ulTaskNotifyTake)

void StartTask02(void *argument) {
	/* USER CODE BEGIN StartTask02 */
	// ADC DMA start & Hann window coefficient calculation
	Init_HannWindow();	// to prepare Hann window coefficient
	Init_Iir_FFT_Instance();	// to initialize IIR filter and FFT instance

	HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc_dma_buff, DMA_COUNTS);
	HAL_TIM_Base_Start(&htim3);

	/* Infinite loop */
	for (;;) {
		Adc_Process();
		xTaskNotifyGive(lcd_TaskHandle);	// start LCD display task
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);// wait for LCD display completion
		osDelay(1);
	}
	/* USER CODE END StartTask02 */
}

・LCD表示タスク

void StartTask03(void *argument) {
	/* USER CODE BEGIN StartTask03 */
	ST7789_Init();
	ST7789_SetRotation(1);   // 90度回転
	ST7789_Fill_Color(BLACK);
	draw_fft_frame();
	/* Infinite loop */
	for (;;) {
		ulTaskNotifyTake(pdTRUE, portMAX_DELAY);	// wait for FFT data ready
		Lcd_Process();
		xTaskNotifyGive(adc_TaskHandle);	// restart FFT task
		osDelay(1);
	}
	/* USER CODE END StartTask03 */
}

もう少し中身を吟味して次のアクション決めよう

 

admin

freeRTOSのスタックサイズ(@STM32)

freeRTOSではデフォルトのスタックサイズ128wordsはFFTなどやらせると少なすぎます

どうなるかというとスタック食い潰して暴走するのが普通

<環境>

・STM32F401re

・CubeIDE 1.19.0(@M4 MacBook Pro Tahoe)

・タスク構成:default task/ADC結果をFFTするタスク(fft_task)/LCD表示制御用のタスク(lcd_task)

 

<スタックサイズ設定>

128wordsでは全く不足と思ったので1024wordsにしたらちゃんと動くようになった、以下はfreertosタスクの設定値、CubeMXで設定すると反映もされますがコードで入力もできる

<スタックサイズの取得>

おそらく一番スタックを消費するであろうfft_taskの最後部分に以下のコードを埋め込み

デバッガー起動してosDelay(1)をbreakpointにして取得した値は以下のとおり

単位はbytesだからword換算だとおよそ230wordsということなで、8割近くは消費しています、この変数はコード実行中の最小値を記録しているらしい

 

<FFTの実行結果>

まだLCDには表示できないので、デバッガーで取得した値をグラフ化してみた図

FFT実行部分のコード(ADC DMAのISRから起動されて、その後に計算実行

uint32_t flags = osThreadFlagsWait(0x01 | 0x02, osFlagsWaitAny,	// 0x01:half, 0x02:full
			osWaitForever);

	/* 1. int16 → float */
	uint32_t base = (flags & 0x01) ? 0 : ADC_RAW_LEN;// buffer start point set

	for (uint32_t i = 0; i < ADC_RAW_LEN; i++) {
		adc_q15_buf[i] = ((int16_t) adc_dma_buff[base + i] - 2048) << 4;
	}

	arm_q15_to_float(adc_q15_buf, adc_float_buf, ADC_RAW_LEN);

	/* 2. IIR + 1/5 デシメーション */
	uint32_t idx = 0;
	for (uint32_t i = 0; i < ADC_RAW_LEN; i += DECIMATE_FACTOR)
	{
		float32_t tmp;
		arm_biquad_cascade_df2T_f32(&iir_inst, &adc_float_buf[i], &tmp, 1);
		decimated_buf[idx++] = tmp;
	}

	/* 3. 窓関数適用*/
	arm_mult_f32(decimated_buf, hann_window, windowed_input, FFT_LEN);

	/* 4. FFT実行 */
	arm_rfft_fast_f32(&fft_inst, windowed_input, fft_out, 0);

	/* 5. 実効値計算(振幅スペクトル : db) */
	for (uint32_t i = 0; i < FFT_LEN / 2; i++) {
		float32_t real = fft_out[2 * i];
		float32_t imag = fft_out[2 * i + 1];
		fft_rms[i] = 10.0f
				* log10f(
						(real * real + imag * imag) * HANN_GAIN_CORRECTION
								+ 1e-12f);

 }

 

admin

 

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