2024年9月20日金曜日

STM32 CMSISのdecimatorを動かす

おはよーございます。

セカイの信号処理LOVERSの皆さんこんにちは。今回は中目黒スタバからお送りしています。

今日の目標:
32kサンプルデータを8kサンプルに下げる
8kサンプルでDACへ出力する

サンプリング周波数下げをdecimatorといいます(サンプリング周波数上げはinterporator)
どちらもCMSIS関数にあるのですが、1024個入れたら256個出てくるんだろうなぁ やってみなくちゃわからない

簡単に言えばdataを4つおきに採用して、間の3つを捨てればいいのですが、ただそうしたのではエイリアシングノイズが発生してSN悪化しちゃいます
そこで間引く前に4kHzのLPFを通しておけばOK、というのが1/4 decimatorの原理です
実際の実装はLPFと3つ捨てを同時にやるとか簡素化テクがあるんですがねー

ここまで書いて珈琲が無くなったので帰宅します
エイリアン・ロムルス観にいこうかなという煩悩がアタマをもたげつつある......

ーーーー
帰宅してエイリアンロムルスの劇場を検索する...
評判良いけど、劇場はあまり熱を入れてないですね.
 ・映写室が教室みたいな小さい部屋ばかり(とくにTOHOが不熱心)
 ・IMAXとかいう+700円の部屋なら大きいけど¥2700払うのは嫌
 ・上映回数が多くない
この映画を教室で観る気ないんで、急速にやる気が減退しました.

ーーーー
やっと動きました
↓8kサンプルと32kサンプルの比較(DAC出力)
↓拡大しないとわからない

ーーーー
ブロック図
これを実現するにはADC~DACのclock設定で一苦労する必要があります.

ブロック図の解説:
・下段は、32KサンプルのままストレートでDACへ出します
・上段は、1/4に間引いて、8kサンプルでDACから出します
・DACのbufferサイズが異なります.下段1024、上段256になります
・演算量が1/4になって嬉しいな
・DACを駆動する8kHzは32kHzと同期してなくちゃいけません
・そのためTIM4→TIM5のslave modeでタイマを動かします

ーーーー
TIM4→TIM5のslave modeについて

これに一番苦労したんだよなぁ...

STM32H723 reference manual(pdfサイズ大きいので注意)のtable 355にこういう表があります.これを探すのに苦労しました.
timerのmaster-slave運用とは何かというと、まぁ読者がイメージする通りですよ.
TIM4の出力でTIM5を動かすということ.
ここでは、TIM4が出力する32kHzをTIM5に与え、TIM5が4カウントして8kHzを作る.
32kと8kは同期する.

なのですが、、、TIM4→TIM5が連結しているってどうやったら判るんですか?
それが上のtable355の赤の注記です.日本語に翻訳するとこうゆうこと.
TIM4のトリガ出力(32k)が、TIM5のITR3に接続されているので、TIM5をslave modeにしてITR3をTIM5のclockにすれば8kを発生できる.

table355によれば、TIM4→TIM5だけでなく、いろいろなペアが採用できる.なのにどうしてTIM4とTIM5を使ったのか?

それは、ADCとDACがTIM4とTIM5を変換トリガとして選択できるからです.
まずADCはこんな選択肢がある.TIM4 32kHzを選択可.めでたし.
DACはch1とch2にそれぞれTIM5 8kとTIM4 32kを選択.めでたし.
他のペアも使えると思います.

そしたら、TIM4とTIM5の設定をします.
TIM4はsystem clk 275MHzを8593分周する普通の動きです.masterともいう.
TIM5はslaveで設定は少し複雑.

TIM4の設定
①275MHz/8593=32kHz
②32kHzを出力する意味 →ADC/DACへ供給

TIM5の設定
①外部clock mode
②外部clock=ITR3=TIM4のトリガが接続される=32kHz
③4分周
⑤8kHzを出力する意味 →DACへ供給

以上でサンプリングclockをSTM32内部に生成できました

ーーーー
ブロック図に描いたring-bufferの取り扱いについては、こちらに書きましたので割愛します.

ーーーー
ADCのDMA設定

ーーーー
DACのDMA設定
CH1とCH2のDMAを追加します

ーーーー
main()の初期化
↓ring-bufferを確保
uint32_t ADCfifo[1024];
uint32_t DAC1CH1fifo[256]; // 1/4 decimated
uint32_t DAC1CH2fifo[1024]; // No decimation

↓構造体初期化
decimation4_init();
fir_init();

↓ADC,DACをDMAでstartさせる、あとは自走する
HAL_ADC_Start_DMA(hadc1, ADCfifo, 1024);
HAL_Delay(1); // DAC runs 1mSec after ADC
HAL_DAC_Start_DMA(hdac1, DAC_CHANNEL_1, DAC1CH1fifo, 256,
     DAC_ALIGN_12B_R);  8kHzサンプル
HAL_DAC_Start_DMA(hdac1, DAC_CHANNEL_2, DAC1CH2fifo, 1024,
     DAC_ALIGN_12B_R);  32kHzサンプル

↓decimation4_init()界隈
extern float32_t fir_decimate_taps[]; ←別途記述、タップ係数
float32_t fir_decimate_variables[TAP_NUM_DECIMATE+BLOCKSIZE];
arm_fir_decimate_instance_f32  fir_decimate_params;

void decimation4_init(void){
    arm_fir_decimate_init_f32(
        &fir_decimate_params,
        TAP_NUM_DECIMATE, // 79
        DECIMATION,  // 4
        fir_decimate_taps,
        fir_decimate_variables,
        BLOCKSIZE   //512
    );
}

↓fir_init()界隈
arm_fir_instance_f32 fir_params;
arm_fir_instance_f32 fir_params2;
float32_t fir_variables[TAP_NUM_DECIMATE+BLOCKSIZE];
float32_t fir_variables2[TAP_NUM_DECIMATE+BLOCKSIZE];

void fir_init(void){
    arm_fir_init_f32(
        &fir_params,
        TAP_NUM_DECIMATE,
        fir_decimate_taps, ←decimateと同じタップ係数
        fir_variables,
        BLOCKSIZE
    );
    arm_fir_init_f32(
        &fir_params2,
        TAP_NUM_DECIMATE,
        fir_decimate_taps,
        fir_variables2,
        BLOCKSIZE
    );
}

ーーーー
ring-buffer半分割り込み
ring-buffer全部割り込み
ADCがring-bufferに半分までdataを積んだら割り込みがかかります.
ADCがring-bufferの全部までdataを積んだら割り込みがかかります.
ring-bufferが良く判らない人はこちらを読んでください.

q31_t ADCcopy[512];  ←512はring-bufferの半分のサイズ
float32_t ADCcopyf[512];
float32_t firout[512];
float32_t decimated[512/4];  ←1/4に間引かれたもの

ring-buffer半分割り込み weak宣言されているので上書きします
void HAL_ADC_ConvHalfCpltCallback(){
    arm_copy_q31( (q31_t*)&ADCfifo[0], ADCcopy, 512);
    arm_q31_to_float( ADCcopy, ADCcopyf, 512);
    arm_fir_f32( &fir_params, ADCcopyf, firout, 512);
    arm_fir_decimate_f32( &fir_decimate_params, ADCcopyf,
         decimated, 512);
    arm_float_to_q31( decimated, (q31_t*)&DAC1CH1fifo[512/4], 512/4);
    arm_float_to_q31( firout, (q31_t*)&DAC1CH2fifo[512], 512);
}

ring-buffer全部割り込み weak宣言されているので上書きします
void HAL_ADC_ConvCpltCallback(){
    arm_copy_q31( (q31_t*)&ADCfifo[512], ADCcopy, 512);
    arm_q31_to_float( ADCcopy, ADCcopyf, 512);
    arm_fir_f32( &fir_params, ADCcopyf, firout, 512);
    arm_fir_decimate_f32( &fir_decimate_params, ADCcopyf,
         decimated, 512);
    arm_float_to_q31( decimated, (q31_t*)&DAC1CH1fifo[0], 512/4);
    arm_float_to_q31( firout, (q31_t*)&DAC1CH2fifo[0], 512);
}


追記:FLASH容量をずいぶん食ってるので調べたら、、、twiddleってのが入ってます.twiddleっていうのはsin/cos tableみたいな係数なのでFFTに関連したものなのは明らかです.でも、2048点FFTや4096点FFTは使ってないので余計なものを入れないで欲しいんだけどなぁ.
 twiddleCoef_2048      16 KB
 twiddleCoef_rfft_4096   16 KB
 twiddleCoef_1024      8 KB
 twiddleCoef_rfft_2048   8 KB
 armBitRevIndexTable2048  7.44 KB
 twiddleCoef_512       4 KB
 twiddleCoef_rfft_1024   4 KB


以上です

かしこ

0 件のコメント:

コメントを投稿