BBD コーラス (6) -- STM32VL Discovery プログラム (3)

プログラムとしては、下の図に示すように、外部から BBD クロックをトリガとして ADC / DAC それぞれに加え、ADC からは DMA を介してディレイ・メモリに書き込み、ディレイ・メモリからは DMA を介して読み出して DAC に出力しているだけです。
これは周辺モジュールの初期化のあとは CPU の介在なしに行われ、初期化後の main 関数の本体としては、何もしない無限ループ、

for (;;) {} 

となっています。
読み書きされるデータには、一切、手を加えていないので、ADC と DACデータ形式は一致している必要があり、両者ともに同じ 0 〜 4095 までの 12 ビット・ストレート・バイナリ形式の内蔵 ADC と 内蔵 DAC を使用しています。
出力側に外付け 16 ビット・ディジタル・オーディオ用シリアル DAC を使うと、16 ビット 2 の補数形式のデータが必要なので、内蔵 ADC を使う場合には何らかの形でデータ形式の変換が必要になります。
「変換」と言っても、b15 を反転する程度のものですが、CPU を介在させず、DMA だけを使用する場合、外付け DAC と内蔵 DAC の両立は難しいので内蔵 DAC オンリーとなっています。
内蔵 ADC の「インジェクテッド・グループ」では、AD 変換値から一定数を減算する機能があるので、これを利用すれば CPU の介在なしに変換は可能となりますが、インジェクテッド・グループでは AD 変換完了で DMA リクエストを出すことはできないので、別の方法で DMA を起動する必要が出てきます。

この構成で、1024 BBD クロック分のディレイが生じる原理は、次のようになります。

  • ADC 側の DMA メモリ書き込みポインタと、DAC 側の DMA メモリ読み込みポインタは、ディレイ・メモリ中の同じ配列要素を指しています。
  • ADC 側の変換開始トリガとして、GPIO からの信号を使う EXTI_11 が選択されており、同様に DAC 側は EXTI_9 が選択されていて、両者の対応する GPIO ポート入力ビットには同じ BBD クロック信号が入力されています。
  • ADC 側の変換完了信号で DMA リクエストを発生させる設定になっており、接続先の DMA チャネルは、ハードウェア上の制約で CH 1 に固定になっています。 同様に、DAC (DAC1) 側の DMA リクエストが接続される DMA チャネルは CH 3 に固定です。
  • BBD クロックの立ち上がりで、ADC および DAC が同時に変換を開始しますが、DAC 側は外部トリガ入力と同時に DMA リクエストが発生します。
  • 一方、ADC 側は変換完了には最短でも AD クロック 14 サイクル分を要するので、14 / 12 [MHz] = 1.17 [us] となり、必ず DAC の DMA リクエストの後に ADC の DMA リクエストが発生することになります。
  • 先に発生する DAC の DMA リクエストにより、ディレイ・メモリの配列要素からデータが読み出され、DAC のデータ・バッファ・レジスタに値が書き込まれますが、その値は、1024 BBD クロック前に ADC から書き込まれた値となっています。
    DMA 完了後には DMA のメモリ・ポインタは次の配列要素を指すようにインクリメントされます。
  • 次に、ADC 側の DMA リクエストが発生し、AD 変換された値がディレイ・メモリの配列要素に上書きされます。 この値は、1024 BBD クロック経過後に DAC 側に読まれることになります。

ディレイ・メモリにより 1024 BBD クロック分の遅延が実現されることになりますが、DAC のデータホールディング・レジスタ (DAC_DHRx) により 1 BBD クロック分の遅延が付加されるので、厳密にはトータルでは 1024 + 1 = 1025 BBD クロック分の遅延となります。
ADC 側の外部トリガとして EXTI_11、DAC 側の外部トリガとして EXTI_9 を使用していますが、これらは、「外部割込み / イベントコントローラ」 (EXTI) モジュールにより、どの GPIO 信号でどのイベントを発生させるかを設定することができます。
ただし、ポート内のビット位置は変更できないので、たとえば、EXTI_9 であれば、
PA9、PB9、PC9、PD9、PE9、PF9、PG9
の中から選択することになります。
STM32VL Discovery では、64 ピンのデバイスを使用しており、使用可能なのは、
PA9、PB9、PC9
および
PA11、PB11、PC11
のみです。
この割り当ては、プログラム冒頭の

#define ADC_TRIG_PORT     GPIOA
#define ADC_TRIG_PORT_SRC GPIO_PortSourceGPIOA
#define DAC_TRIG_PORT     GPIOB
#define DAC_TRIG_PORT_SRC GPIO_PortSourceGPIOB

の定義を変更すればポートの選択が可能です。
また、DAC1_OUT のポートは PA4 (コネクタ P1 19 番ピン) に固定ですが、

#define SIG_IN_PORT   GPIOC
#define SIG_IN_BIT    GPIO_Pin_5
#define SIG_IN_CH     ADC_Channel_15

の定義を変えれば、ADC の入力ポートは変更可能です。
スペック上は、ADC の外部トリガ可能な周波数は 800 kHz 程度ですが、ここでは、MN3207 のスペックに相当する 200 kHz クロックを上限と想定しているので、ADC の入力サンプリング時間を多め (28.5 AD クロック) に設定しています。
定義、

#define SIG_SAMP_TIME ADC_SampleTime_28Cycles5

を変えれば、サンプリング時間を変更できます。
ただし、BBD クロックは、まず 24 MHz の APB2 クロック系列に属する GPIO でクロック同期が取られ、次に ADC モジュール内で 12 MHz に設定した AD クロックで同期が取られるので、時間方向には 12 MHz クロックでリサンプリングされて離散化されることになります。
つまり、リサンプリングされた後の BBD クロックには、最大、 1 / 12 [MHz] = 83.3 [ns p-p] のジッタが乗ることになり、BBD クロック周波数を上げれば上げるほど影響が大きくなります。
ここが、実際の BBD チップに対して劣る点です。
ここで使用している STM32VL Discovery の STM32F100 チップでは CPU および周辺クロックは 24 MHz と低いのですが、STM32F103 や STM32F105 などの 72 MHz デバイスでも、AD クロックの上限は 14 MHz なので、高クロックの高性能デバイスを使っても、あまり改善されません。
ディレイ量は

#define DELAY_MEM_SIZE 1024

の定義を変更することで変えられます。
STM32VL Discovery に使用されているチップは RAM 8 K バイトですから、最大 4096 サンプル・ディレイまで実現可能ですが、実際には、Atollic TrueSTUDIO/STM32 のデフォルト設定では、C スタートアップ・ルーチンなどで 28 バイト、スタックに 128 バイト割り当てられているので、その分目減りします。
プログラム本体は、何もしない無限ループなので、スタック領域などは必要ではなく、RAM 領域を全てディレイ・メモリに使うことも可能ですが、コンパイル時のセクション指定や、リンカ・スクリプトに細工が必要になります。