2021年3月24日水曜日

STM32CubeIDE: I2S DACのPCM5102Aを32bitで使う

STM32CubeIDE: Version 1.5.1
Target board: Nucleo-F446RE

32bit/48kHz動作


まず、DMAを利用して32bit/48kHzでサイン波を出力させてみます。16bit/48kHzのプログラムを32bitに変更します。

参考

配線


16bitの場合と同様です。


MXの設定


System Core
  GPIO
    PC5: 
      GPIO mode: Output Push Pull
      User Label: CK_PERIOD
    PC6:
      GPIO mode: Output Push Pull
      User Label: CK_CPLT
    PC8:
      GPIO mode: Output Push Pull
      User Label: CK_HALF_CPLT
Multimedia
  I2S2
    Mode
      Mode: Half-Duplex Master
  Configuration
    Parameter Settings
      Generic Parameters
        Data and Frame Format: 32Bits Data 32Bits Frame
        Selected Audio Frequency: 48KHz
    DMA Settings
      SPI2_TX
        DMA Request Settings
          Mode: Circular
          Peripheral: Data Width: Half Word
          Memory   : Data Width: Half Word

 Data and Frame Formatを32Bits Data 32Bits Frameにしています。

main.cにコードを追加


/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <math.h>
/* USER CODE END Includes */

<中略>

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define PI_F	(3.1415926f)
/* USER CODE END PD */

<中略>

/* USER CODE BEGIN PV */
float sampling_rate = 48000.0f;
float frequency = 1000.0f;
float phi = 0.0f;
float delta;
int32_t tx_buffer[2] = { 0, 0 };
/* USER CODE END PV */

<中略>

int main(void)
{
  /* USER CODE BEGIN 1 */
  delta = (2.0f * PI_F * frequency) / sampling_rate;
  /* USER CODE END 1 */

<中略>

  /* USER CODE BEGIN 2 */
  HAL_I2S_Transmit_DMA(&hi2s2, (uint16_t *)tx_buffer, 2);
  /* USER CODE END 2 */

<中略>

/* USER CODE BEGIN 4 */
static int32_t swap16(int32_t x)
{
	return ((uint32_t)x << 16) | ((uint32_t)x >> 16);
}

void HAL_I2S_TxHalfCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sine wave
	float fv = sinf(phi);
	int32_t v = fv * 0x7fffffff;
	tx_buffer[0] = swap16(v);

	HAL_GPIO_WritePin(GPIOC, CK_HALF_CPLT_Pin, GPIO_PIN_RESET);
}

void HAL_I2S_TxCpltCallback(I2S_HandleTypeDef *hi2s)
{
	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_SET);

	// Generate Sawtooth wave
	float fv = phi / PI_F;
	int32_t v = fv * 0x7fffffff;
	tx_buffer[1] = swap16(v);

	// Advance in phase
	phi += delta;
	if (phi > PI_F) {
		phi -= 2.0f * PI_F;
		HAL_GPIO_TogglePin(GPIOC, CK_PERIOD_Pin);
	}

	HAL_GPIO_WritePin(GPIOC, CK_CPLT_Pin, GPIO_PIN_RESET);
}
/* USER CODE END 4 */

出力波形を観測



16bitの場合とノコギリ波で比較するとほとんど変わらないようです。

スペクトラム


Lch(サイン波) 500kHzレンジ

Lch(サイン波) 100kHzレンジ

Rch(ノコギリ波) 50kHzレンジ

Rch(ノコギリ波) 500kHzレンジ

スペクトラムも大差ありません。縦の精度はオーバーサンプリングとデジタルフィルタで十分補間されているということでしょうか。

32bit/192kHz動作


サンプリング周波数を上げてみます。STM32F446Eでは32bit/192kHzが最高のレートです。I2Sのクロック周波数Fclkは
Fclk = 2 * 32bit * 192kHz = 12.288MHz
となります。

MXの設定 - Pinout & Configuration


System Core
  GPIO
    PC5: 
      GPIO mode: Output Push Pull
      User Label: CK_PERIOD
    PC6:
      GPIO mode: Output Push Pull
      User Label: CK_CPLT
    PC8:
      GPIO mode: Output Push Pull
      User Label: CK_HALF_CPLT
Multimedia
  I2S2
    Mode
      Mode: Half-Duplex Master
  Configuration
    Parameter Settings
      Generic Parameters
       Data and Frame Format: 32Bits Data 32Bits Frame
       Selected Audio Frequency: 192KHz
    DMA Settings
      SPI2_TX
        DMA Request Settings
          Mode: Circular
          Peripheral: Data Width: Half Word
          Memory   : Data Width: Half Word

Selected Audio Frequencyを192KHzに変更しています。

MXの設定 - Clock Configuration


STM32F446REのデフォルトのクロック周波数(84MHz)で、I2Sのサンプリング周波数を192kHzで動作させると処理が間に合わないので、STM32F446REを180MHz動作させます。

PLL Source Mux: HSE
HCLK(MHz): 180


コンパイラで速度優先最適化を指定


最適化しないと処理が間に合わないので、速度優先で最適化を指定します。階層が深いので迷いますw


Project > Properties > C/C++ Build > Settings
Tool Settings > MCU GCC Compiler > Optimization
Optimization level: Optimization for speed (-Ofast)

main.cにコードを追加


32bit/48kHzのコードのサンプリング周波数の定義を変更します。

float sampling_rate = 48000.0f;
                    ↓
float sampling_rate = 195312.0f;

MXでSelected Audio Frequencyに192kHzを指定すると、Real Audio Frequencyが195.312kHzとなるので、これを使って出力波形の周波数を補正します。

出力波形を観測



32bit/48kHzの場合と比べるとノコギリ波のエッジの部分の振動が細かくなっています。過渡特性はかなり向上します。

スペクトラム


Lch(サイン波) 500kHzレンジ

Lch(サイン波) 100kHzレンジ

サイン波のスペクトラムを32bit/48kHzの場合と比較するとノイズフロアが15dBぐらい下がりますが、ところどころピークが現れています。いやですねw

Rch(ノコギリ波) 50kHzレンジ

Rch(ノコギリ波) 500kHzレンジ

※ノコギリ波のスペクトラムは変動が激しいのでLinear RMS Averageにして平均値を表示しています。

ノコギリ波のスペクトラムを32bit/48kHzの場合と比較するとナイキスト周波数の96kHz付近までノコギリ波らしい高調波が見られます。平均値をとっているので単純には比較できませんが、高調波の間のピークが残っています。ノコギリ波の綺麗さではやはりアナログ発振器に分がありそうです。

こういう歪んだ(?)ノコギリ波のほうが味がある場合もあるので、音源としてはいろいろなビット数/サンプリング周波数で出力できるようにすると面白いかもしれません。

PCM5102Aのハードウェア設定


32bit/192kHzの場合もFLTとDEMPの設定による出力の変化を見てみました。

FLT: H

上側の振動がなくなり下側の振動が増えてますね。

DEMP: H

32bit/192kHzではDEMPによる変化はないようです。PCM5102Aのデータシートには以下の記述があります。

De-emphasis control for 44.1-kHz sampling rate(1): Off (Low) / On (High)

(1) Failsafe LVCMOS Schmitt trigger input

 

0 件のコメント:

コメントを投稿