2021年8月31日火曜日

FV-1テスト用基板の製作 その2

FV-1のアナログ入力ピン、PIN1、PIN2は1.65Vのバイアスがかけられているようです。したがって前回のように単に100kΩの抵抗と置き換えた場合とは入力保護用のダイオードによる歪み方が異なります。むしろ歪はほとんどなくなりました。

回路図

アナログ入力ピンへの入力信号


電源は3.3Vです。

入力信号なし(入力はGNDにショート)

C1:入力 C2:PIN1

無信号時、PIN1の電位は1.66Vです。

入力信号 1kHz/1Vppサイン波 GND~+1V

C1:入力 C2:PIN1

GNDから正側の1Vppの入力です。PIN1は1.66V付近のバイアスがかかっています。

入力信号 1kHz/1Vppサイン波 -0.5V~+0.5V

C1:入力 C2:PIN1

-0.5V~+0.5Vの1Vpp入力です。C6でACカップリングしているため入力信号のDC成分は取り除かれ、同じく1.66Vのバイアスがかかっています。

入力信号 1kHz/2Vppサイン波 -1V~+1V

C1:入力 C2:PIN1

2Vppの入力でも波形に歪みは見られません。

FV-1のデータシートに、Maximum input signal levelは2.6V/Min、3.0V/Maxとなっているのでこれぐらいの振幅で使うのがよさそうです。絶対定格はAVDD+0.5V(3.3V駆動だと3.8V)ですが振幅が大きすぎると内部演算で波形がクリップします。秋月のモジュールはClip LEDが実装されているのでLEDの点灯で判別がつきます。

出力波形


いくつかのプログラムで出力の波形を観測しました。入力は1Vpp/1kHzサイン波、パラメータ設定用のPOTは中点付近の設定です。

Prg#:0 Chorus-reverb


C1:LOUT C2:ROUT

Prg#:3 Pitch shift


C1:IN C2:LOUT

POT0を左いっぱいに回した状態。


C1:IN C2:LOUT

POT0を右いっぱいに回した状態。

Prg#:6 Reverb1


C1:LOUT C2:ROUT

Prg#:7 Reverb2


C1:LOUT C2:ROUT

Reverb1とReverb2はL/R chで出力が異なります。モノラル信号をステレオ化できますね。

2021年8月22日日曜日

FV-1テスト用基板の製作

FV-1のテスト用の基板を製作しました。FV-1は秋月のDIP化モジュールを使用しています。


回路図

FV-1 DIP化モジュールに掲載されている回路図をもとに、ピンヘッダをジャンパでショートすることで内蔵プログラムを選択できるようにし、パラメータ設定用のPOTも使えるようにしています(基板上では3pinのピンヘッダを出している)。

FV-1は3.3V駆動で、アナログ入力は -0.5/Min、AVDD+0.5V/Maxとなっているためショットキーバリアダイオードで過電圧保護しています。

入力保護のシミュレーション


入力部の特性をLTSpiceでシミュレーションしました。データシートにアナログ入力インピーダンスは、80kΩ/Min、120kΩ/Maxとあるので100kΩの抵抗で置き換えています。

シミュレーション回路図

実験ではSBDは1S4を使いましたが、Deviceが見当たらないのでシミュレーションでは1N5819を指定しています。

+1Vpp入力の過渡解析

正側の1Vpp/1kHzのサイン波を入力した場合の過渡解析です。下側が少し歪んでいます。

±5Vpp入力の過渡解析

±5Vppのサイン波を入力した場合の過渡解析です。-0.2V~+3.5Vで振幅が制限されています。

AC解析

秋月の作例から定数を決めたのですが帯域が少し狭いですね。3kHzで-3dBとなっています。

出力フィルタのシミュレーション


シミュレーション回路図

RC一次のLPFとHPFの組み合わせです。

AC解析

高域のカットオフ周波数は70kHz付近です。FV-1のサンプリング周波数Fs=48kHzなので少し広すぎですね。24kHzあたりで切りたいところです。

実験



+1Vpp入力

正側の1Vpp/1kHzのサイン波を入力した場合、やはり下側が少し歪むようです。

±5Vpp入力

±5Vppのサイン波を入力した場合、-0.21V~+3.54Vで振幅が制限されています。FV-1の絶対定格ではVDD=3.3Vの場合、-0.5V~+3.8Vなので振幅制限は十分でしょう。

入力部の周波数特性

実験でもカットオフ周波数は3kHz付近となりました。

出力部の周波数特性

高域のカットオフ周波数は70~80kHz付近です。

入力部の周波数特性の帯域を広げる


C7を0.047uFをFV-1のデータシートのアプリケーション・ノートの定数、1nF(1000pF)程度にすると改善します。

シミュレーション回路図

AC解析

このコンデンサ(C7)はピンソケットを使わず直にはんだ付けしてしまっているので交換しづらいです。どの定数がいいかは音出ししてみないとわかりません。入力バッファとDRY/WETを調節する出力ミキサ部を製作する予定です。

2021年8月17日火曜日

4入力ミキサー Eurorack仕様 MIX04の製作

Eurorack仕様の4入力ミキサーMIX04を製作しました。



プリント基板はfusionPCBに依頼しました。


以前、スタンドアロンの4入力ミキサーMIX0401を製作しましたが、9Vの単電源で動作し仮想GNDを使っているので入出力をACカップリングしています。動作電圧が低いので信号の振幅が稼げず、ACカップリングしているため制御用のDC信号は扱えません。アナログシンセのルーティング中にオーディオ信号やCV信号をMIXするには不向きです。

また、OPアンプを使った増幅器の利得を可変するためフィードバック抵抗の値をPOTで可変するようにしていますが、POTのワイパーに電流が流れるためガリの原因となっています。

一般的には増幅器の利得は一定とし前段のPOTで信号を減衰させて、利得可変としていることが多いようです。これもPOTのワイパーには電流が流れません。(OPアンプの入力インピーダンスは非常に高いため)

前回のMIX0401ではPOTをレオスタットとして利用しています。これはワイパーに帰還電流が流れます。
今回のMIX04では下図ようにワイパーに電流が流れないようにしています。ただしPOTによる分圧比がR8との合成抵抗になるため、A特性ではなくB特性のPOTを使用しています。

ガリが出るか出ないかしばらく使って様子をみてみます。

参考「新・低周波/高周波回路設計マニュアル」鈴木雅臣著 p232

MIX04の製作


回路図

C1、C2、C3はかんたんな位相補償用です。

増幅率を測定


増幅率を決める抵抗値は、出力用のPOT(RV1)を中点付近、または入力用のPOT(RV2など)を中点付近にしたときに0dBになるようにしています。POTの位置を調節して増幅率を測定しました。

RV2(INPUT1)最大、RV1(OUTPUT)最大


C1:IN1 C2:OUT

最大で、増幅率 Av = 10.386 / 2.0315 ≒ 5.1倍です。

RV2(INPUT1)最大、RV1(OUTPUT)中点付近

C1:IN1 C2:OUT

出力レベルを中点あたりに設定すると増幅率はほぼ1となっています。

RV2(INPUT1)中点付近、RV1(OUTPUT)最大

入力レベルを中点あたりに設定しても増幅率はほぼ1となります。

RV2(INPUT1)中点付近、RV1(OUTPUT)中点付近

入力、出力ともに中点付近にすると

増幅率 Av = 0.35092 / 2.0080 ≒ 0.17倍

POTの位置合わせはおおよそなので、真ん中、真ん中にすると増幅率は約1/5です。

ミックスの様子


IN1に1kHz、IN2に3kHzの正弦波を入力してMIXの様子を観測しました。測定に使用したAnalog Disocovery 2の波形生成は2chなので2chのみの測定です。

RV2(INPUT1)最大、RV3(INPUT2)最大、RV1(OUTPUT)成り行き

 C1:IN1 C2:OUT

位相反転出力


今回はパネルに端子を出していませんが、位相を反転した信号も出力できます。


C1:OUT C2:INV_OUT

周波数特性


基板には位相補償用のパターンを入れてありますが、まずは位相補償なしの状態で測定しました。入力レベルは最大、利得が0dBになるように出力レベルを設定して測定しています。

OPアンプ:NJM4580DD 位相補償なし

300~400kHzあたりにピークが発生しています。これは危険です。C1とC3に10pFのセラコンを入れてみます。

OPアンプ:NJM4580DD C1=10pF C3=10pF

ピークが抑えられました。位相が180度回った地点の利得が-12dB程度、利得が-3dBの地点が100kHz以上なので、これはこれでありでしょう。

OPアンプをNJM072Dに変更してみました。

OPアンプ:NJM072D 位相補償なし

位相補償なしでもカットオフ周波数付近で多少利得が上がっていますが、NJM4580DDより良好です。C3にのみ10pFのセラコンを入れてみました。

OPアンプ:NJM072D C3=10pF

これが良さそうです。エフェクターや楽器では072系が使われることが多いですがJFET入力で入力インピーダンスが高いこと以外にもメリットがありそうです。過渡特性がまったりしているなど、特性が素直なんでしょうか。


2021年8月12日木曜日

STM32CubeIDE Nucleo-G431KBの実行速度を測定 I2S

I2Sのサンプリング周波数が192kHzの場合、周期 T = 1 / f ≒ 5.21us、L/Rの2chだとその半分の2.60usがリアルタイムな計算に使える時間となります。

前回、FPUを使って算術関数の実行速度を測定しました。G431KBの場合、expf()1000回で1msだったので、1回あたり1usとなります。リアルタイムに計算する場合せいぜいexpf()2回が限度となります。

さらに、I2SのDMA転送が完了するまでメモリの内容はいじれませんので、転送が完了するまでの待ち時間が発生します。転送完了の割り込みが発生してから次の転送開始までどれぐらいの猶予があるか測定しました。

ソースコード(main.cに追加)

/* USER CODE BEGIN PV */
int32_t data[2] = { 0 };
int32_t tx_buffer[2] = { 0 };
/* USER CODE END PV */

<中略>

int main(void)
{
  /* 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(CK_HALF_CPLT_GPIO_Port, CK_HALF_CPLT_Pin, GPIO_PIN_SET);

	tx_buffer[0] = swap16(data[0]++);

	HAL_GPIO_WritePin(CK_HALF_CPLT_GPIO_Port, CK_HALF_CPLT_Pin, GPIO_PIN_RESET);
}

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

	tx_buffer[1] = swap16(data[1]--);

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

32bit値をインクリメント、デクリメントして出力しています。STM32のI2Sは32bitの場合上下16bitを入れ替える必要があり、swap16()で行っています。参考「STM32CubeIDE: I2Sを32bit長で使う

F303K8はSTM32CubeIDEではI2Sの設定ができませんので、F446RE、F767ZI、G431KBで測定しました。

F446REの設定


MXの設定

Pinout & Configuration
  System Core
    GPIO
      PC10
        Maximum output speed: Very High
        User Label: CK_HALF_CPLT
      PC12
        Maximum output speed: Very High
        User Label: CK_CPLT
  Multimedia
    I2S2
   Mode: Half-Duplex Master
      Parameter Settings
        Data and Frame Format: 32 Bits Data on 32 Bits Frame
        Selected Audio Frequency: 192KHz
        Real Audio Frequency: 195.312Khz
      DMA Settings
        SPI2_TX
          Mode: Circular
          Data Width: Half Word / Half Word

Clock Configuration
     HSE->PLL HCLK: 180MHz

プロジェクトの設定

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

F767ZIの設定


Nucleo-F767ZIはデフォルトでETHが有効化されていて実行時にエラーが発生するので、MXで無効化します。

MXの設定

Pinout & Configuration
  System Core
    GPIO
      PB10
        Maximum output speed: Very High
        User Label: CK_CPLT
      PB11
        Maximum output speed: Very High
        User Label: CK_HALF_CPLT
  Connectivity
    ETH
      Mode: Disable
  Multimedia
    I2S2
   Mode: Half-Duplex Master
      Parameter Settings
        Data and Frame Format: 32 Bits Data on 32 Bits Frame
        Selected Audio Frequency: 192KHz
        Real Audio Frequency: 187.5Khz
      DMA Settings
        SPI2_TX
          Mode: Circular
          Data Width: Half Word / Half Word

Clock Configuration
     HSE->PLL HCLK: 216MHz

プロジェクトの設定

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

G431GBの設定


Nucleo-G431KBはPF0、PF1を有効にするためにボード上のはんだジャンパSB8、SB11をブリッジしました。



MXの設定

Pinout & Configuration
  System Core
    GPIO
      PA12
        Maximum output speed: Very High
        User Label: CK_HALF_CPLT
      PB0
        Maximum output speed: Very High
        User Label: CK_CPLT
  Multimedia
    I2S2
   Mode: Half-Duplex Master
      Parameter Settings
        Data and Frame Format: 32 Bits Data on 32 Bits Frame
        Selected Audio Frequency: 192KHz
        Real Audio Frequency: 189.732dKhz
      DMA Settings
        SPI2_TX
          Mode: Circular
          Data Width: Half Word / Half Word

Clock Configuration
     HSI->PLL HCLK: 170MHz

プロジェクトの設定

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

各Nucleoボードのデフォルトの設定で、I2Sを初期化すると割り当てられるピンは以下の通りです。

Board I2S2_CK I2S2_WS I2S2_SD
F446RE PB10 PB12 PC1
F767ZI PD3 PB12 PC3
G431KB PF1 PF0 PA11

実行結果


LchのDMA転送が完了したときに発生する割り込みHAL_I2S_TxHalfCpltCallback()から、RchのI2S通信が開始するWSがHighになる時点までの時間を計測しました。

WSがHighになってRchの通信が開始してからも、しばらくはLchのバッファをいじれるかもしれませんが、よくわかっていないので考慮にいれていません。少なくともRchのCPLTが呼び出されて、MPU本体に制御が戻る前にLchの仕事を完了させないといけません。

F446RE

F767ZI

G431KB


Board 猶予時間(us)
F446RE 1.59
F767ZI 1.1
G431KB 1.89


このテストもF767ZIの成績が芳しくありません。

おおよその目安として、192kHz/2chでI2S出力する場合、リアルタイムに算術関数を1回実行するが精一杯というところでしょうか。

2021年8月2日月曜日

STM32CubeIDE Nucleo-G431KBの実行速度を測定 FPU

Cortex-M4は単精度FPUを備えています。STM32F767ZIはCortex-M7で倍精度も使えます。G431KBとF303K8、F446RE、F767ZIについてsinf()、cosf()、expf()の実行速度を測定しました。

STM32CubeIDEのバージョンは1.7.0です。

ソースコード(Core/Src/main.cに追加)


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

<中略>

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

<中略>

/* USER CODE BEGIN PV */
float v[DIV_N] = {0};
/* USER CODE END PV */

<中略>

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
	HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 0xFFFF);
	return ch;
}
/* USER CODE END 0 */

<中略>

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  volatile float phi = 0.0f;
	  HAL_GPIO_WritePin(PIN_CHK1_GPIO_Port, PIN_CHK1_Pin, GPIO_PIN_SET);
	  for (int i = 0; i < DIV_N; i++) {
		  // Uncomment out the functions to be used.
		  v[i] = sinf(phi);
		  //v[i] = cosf(phi);
		  //v[i] = expf(phi);

		  phi += DELTA;
	  }
	  HAL_GPIO_WritePin(PIN_CHK1_GPIO_Port, PIN_CHK1_Pin, GPIO_PIN_RESET);
	  HAL_Delay(1);

	  //for (int i = 0; i < DIV_N; i++)  printf("%f\r\n", v[i]);
  }
  /* USER CODE END 3 */
}

メインループ内で算術関数をDIV_N=1000回繰り返し、その前後でGPIOをHigh/Low出力しオシロで間隔を測定しました。算術関数の切り替えは原始的にコメントアウトをつけたり外したりしてBuildし直して行っています。

算術関数がちゃんと使えているかどうか、結果が入ったv[]をUART出力(printf())しています。UART出力はかなり時間がかかるので、測定時にはprintf()コメントアウトしました。

STM32CubeIDEでのprintf()については「STM32CubeIDE: UARTでprintfを使う(浮動小数点数型あり)」で書きました。

各Nucleoボードの設定


Clock:
G431KB HSI→PLL 170MHz
F303K8 HSI→PLL  64MHz
F446RE HSE→PLL 180MHz
F767ZI HSE→PLL 216MHz

Pinout & Configuration:
System Core
  GPIO
    <Arduino D2に割り当てられるPin>
        GPIO mode: Output Push Pull
        Maximum ouput speed: Very High (F303K8はHigh)
        User Label: PIN_CHK1

Arduino D2に割り当てられるPinは、F303K8とG431KBはPA12、F446REはPA10、F767ZIはPF15です。

Build Configurations: Release 

Build Configurationsについてですが、DebugとReleaseを切り替えて実行するには、メニューの[Run] - [Run As...] - [1 STM32 Cortex-M C/C++ Application]からDebugかReleaseを選んで実行する必要があります。

最適化: -Ofast

Release Configurationのデフォルトは-Os (サイズ優先)ですが-Ofast (スピード優先)に変更しました。

G431KBの実行結果


sinf()

cosf()

expf()

UART出力


UART出力したデータをRlogin(ターミナルソフト)で受信しグラフ化しました。

sinf()

expf()

実行結果比較


関数名 機種名 周期(ms) パルス幅+(ms) パルス-(ms)
sinf() G431KB 3.005 1.04 1.96
sinf() F303K8 6.01 4.62 1.4
sinf() F446RE 2 1 1
sinf() F767ZI 3 1.6 1.4
cosf() G431KB 3 1.04 1.96
cosf() F303K8 6.01 4.54 1.48
cosf() F446RE 3 1.12 1.88
cosf() F767ZI 3 1.5 1.5
expf() G431KB 2 1 1
expf() F303K8 6.01 4.24 1.76
expf() F446RE 2 0.96 1.04
expf() F767ZI 3 1.28 1.72

周期がほぼ1ms区切りになっていますが、HAL_Delay()を使っているためだと思います。1ms単位のタイマーなので、1ms区切りでHAL_Delay()から抜ける動作をするようです。したがって周期ではなく、出力がHighになっている時間で比較します。


G431GBとF446REが同程度で最も速く、ついでF767ZI、F303K8が最も遅い結果になりました。GPIOの場合ほどではないですが、最も高スペックのはずのF767ZIの成績があまり芳しくないですね。

他の開発環境

以前、mbed OS5とSW4STM32で浮動小数点演算の実行時間を測定しました。


同じF446REの1000回ループで

関数 CubeIDE(us) mbed-cli(us) SW4STM32(us)
sinf() 1000 569 525
cosf() 1120 621 591
expf() 960 958 998

と、sinf()、cosf()の結果がかなり悪くなっています。Build時の設定が何かおかしいのかもしれませんね。