電子回路わからん日記

にゃーんと言いながら電子回路いじってます

リアルタイムDSDコンバータの出力波形確認

だいぶ前にリアルタイムDSDコンバータをFPGA上に実装したお話を書きましたが

audio-diy.hatenablog.com

今回は実際にこのリアルタイムDSDコンバータから出力された信号をローパスフィルタに通して出力波形を見てみたいと思います。


DSDの利点。それは・・・・

原理上はアナログのローパスフィルタを通すだけで再生したいアナログ波形を取り出せるという点です。(実際はそうはいきませんが・・・・)

ということで、CRローパスフィルタを通してアナログ波形をオシロスコープで観測してみます。


配線

PC→Amanero Combo384→Terasic DE10-Lite (ここでPCM-DSD変換)→CRローパスフィルタ→オシロスコープで配線します。

最初ちょっと積分器の設計をやらかしていたんですが

修正すると・・・・

ゼロクロス周辺の変な歪は消えましたが正弦波ではなく三角波っぽい波形になっています。

 

三角波ということは奇数次高調波が乗っているということですね。

ちょっくらFFTで周波数特性を見てみます。


ローパスフィルタを通したあとの出力波形をFFTで見てみる

f:id:AUDIY:20220211180404p:plain

ビンゴです。

なぜか5次高調波はいませんが、1kHz、3kHz、7kHz、9kHz・・・・と奇数次高調波がガッツリ乗っています。

 

しかも今回のローパスフィルタのカットオフ周波数は20kHzですし、PCM-DSD変換でオーバーサンプリングなどの信号処理も一切行っていません。

それを1bitの矩形波の密度の変化で送っているわけですから奇数次高調波が乗って当然です。これを少なくしようとすると「マルチビット出力」や「変調の次数を増やす」ということになるのでしょう。


ということで、1次ΔΣのリアルタイムDSDコンバータの出力波形を見てみました。

次回は同じく1bit出力のまま、ΔΣ変調の次数を増やして波形の変化を確認してみたいと思います。

PT8211Sを購入しました

・・・・・寒いです。(突然)

 

今回は面白いものを秋月電子で購入したのでちょい遊んでみようかと思いまして、ちょっとしたイントロダクションです。


出会いは突然

やってきました。

今どき珍しいモノリシックのオーディオ用R-2R DAコンバータICです。

どうやらPrinceton Technologyという会社がPhilips TDA1311Aのピンコンパチ品として現在も生産を続けているもののようです。

www.princeton.com.tw

 

I2Cなどによる設定も必要なく、電源と16bit右詰めフォーマットさえ入力すればアナログ信号が電圧出力されます。I2Sから右詰めへの変換さえできればすぐに使うことができそうです。

 

THDがTypical値0.1%程度と特性がなかなかに不利ですが、このあたりは後段のアナログ回路で改善できる可能性は大いにあります。

 

欲を言えば電圧出力でなく電流出力であればなおのこと特性の追い込み甲斐がありますが、そんな事を言っても仕方がないのでまずは1石で音を出すところから始めてみたいと思います。


今後の計画

さて、PT8211Sを使用したDAコンバータですが、以下の計画で遊んでいきたいと思います。

  1. データシート記載のリファレンス回路で動作確認
  2. デュアルモノラル化、低歪化検討
  3. FPGA/CPLDと組み合わせた基板設計
  4. 電源部まで含めた設計(ここまで行くかな・・・・?)

さっそくリファレンス回路の基板を起こしています。

Combo384を組み合わせればUSB-DACもできますし、現時点で妄想が膨らんでいます。

アイソレータと組み合わせて「USBのノイズをシャットアウト」というのもよいかもしれません。


ざっくりイントロダクションでした。

 

ゆくゆくは最近複数のメーカーがされているような「FPGA/CPLDと抵抗を組み合わせたディスクリートR-2R DAC」もやってみたいです。

 

まずはSPDIF光-I2Sコンバータの基板が届くのでそちらを先に完成させます・・・・

 

リアルタイムDSDコンバータのHDL記述

新年明けましておめでとうございます。
2022年もド素人電子工作アカウントのAUDIYをよろしくお願いいたします。

今回から前回のシミュレーション結果をもとに、いよいよFPGAにリアルタイムPCM-DSDコンバータを実装していきたいと思います。


ΔΣ変調のブロック図

さて、前回シミュレーションしたPCM-DSD変換ですが、

audio-diy.hatenablog.com

 

これをリアルタイム実装するにはΔΣ変調をFPGA上に実装する必要があります。

ΔΣ変調のブロック図としては、個人的に下記ブログが最も理解しやすいと感じました。

xx3stksm.hatenablog.com

 

積分器のブロック図含めて描かれています。

今回はこれをFPGAに実装するためにVerilogコードにしましたので解説していきます。


I2S-パラレルPCM変換

これは今回のリアルタイムDSDコンバータを作るモチベーションとなったものですが、Verilogコードそのものの公開は初めてと思います。

github.com

 

HDLを書いたことの無い方のために言葉で説明しますと

  • BCLKの立ち上がりエッジに合わせてデータを64bit分(Lch: 32bit、Rch: 32bit)ストック&LRCKを1BCLK分遅延させる
  • 遅延させたLRCKの立ち下がりエッジに合わせて64bitを上位32bit(Lch)、下位32bit(Rch)に分離して出力
  • RST_I信号がLowとなった際には出力と内部の保存データを全て0にする

という機能を実現しています。

これによりLRCKをワードクロックとしています。


微分

ΔΣ変調の「Δ」に相当する部分です。

github.com

デジタルにおける微分は「入力値から1サンプル前の微分値を引き算する」ことによって実現していますが、今回は上記のブロック図に則り引き算のみを担当します。

 

1サンプル前の数値は、後ほど紹介するDSM_MAXIMIZER.vが担当します。


積分

ΔΣ変調の「Σ」に相当する部分です。

github.com

 

微分器の反対なので「入力値と1サンプル前の積分値を足し算する」ものです。

ただ足し算だけ繰り返すといずれオーバーフローしますので、足し合わせる計算結果は1ビット→シフトします。


量子化

積分した結果を1ビット信号として出力します。

積分した結果が正であれば1、負であれば0を出力すれば良いのですが、

github.com

上記のコードの通り最上位ビットを反転すれば良いです。

 

なぜかというと、符号付き整数においては最上位ビットが符号ビットであり、

0: 正の整数

1: 負の整数

であるためです。

 

出力したい結果は

正の整数なら1

負の整数なら0

なので、0→1、1→0つまり最上位ビットのNOT演算で実現できます。


遅延+振幅の最大化

量子化された結果を微分器へフィードバックするためのモジュールです。

ここで微分のために1BCLK分の遅延を加えています。

 

量子化された結果は1bitだと1か0となり、そのままでは入力と計算しても正しく微分できないので、これをPCM信号の最大振幅とします。(ブロック図上でよく「DAC」と表記されている部分です)

github.com


配置配線

筆者はQuartusで開発していますので、ブロック図を描いて機能を実現します。

github.com

 

これを論理合成すると下のような回路図になりました。

github.com

 

ひと通り見たところ間違いはなさそうですが、どうでしょうか?

ブロック図は下記リンクのVerilogコードに変換済ですので、VivadoやDiamondをお使いの方でも合成・配置配線できるかと思います。(Intel固有IPも使っていません。)

github.com

 

使って遊んでくれると嬉しいです。


次回は実際にFPGA上で動作させて出力波形をオシロスコープで覗いていきたいと思います。

リアルタイムDSDコンバータのシミュレーション

前回の続きです。

audio-diy.hatenablog.com

 

今回は前回作成した関数を用いてPCMからDSDへの変換のシミュレーションを行います。


シミュレーションの流れ

前回でもお伝えしたかと思いますが、ここで改めてシミュレーションの流れを伝えます。

  1. 浮動小数点数で正弦波を作る
  2. 正弦波を浮動小数点数から符号付き整数(リニアPCM)へ変換
  3. リニアPCMを0次ホールドで64倍に補間(I2SのBCLKを模擬するため)
  4. 補間したデータを1次ΔΣ変調
  5. 変調したデータをローパスフィルタに通しリニアPCMの正弦波と比較

ざっとこんな感じです。


シミュレーションプログラム

シミュレーションプログラムは下記GitHubにアップロードしています。

github.com

 

実行して得られた画像は下の通りです。振幅は[-1, 1]の区間に正規化しています。

最初DSDを通すローパスフィルタは実回路を模すためにIIRで試したんですが、発散してしまいグチャグチャになったので今回は簡単のため10タップの移動平均フィルタです。このあたりもしっかり勉強しなくちゃですね・・・・

f:id:AUDIY:20211227001422p:plain

10タップと次数が小さいのでアレですが、平均化されたデータとPCMデータの振幅値はほぼ一致していますね。

 

1次ΔΣ変調はちゃんと機能していそうです。


ということで、次回からこの「リアルタイムDSDコンバータ」FPGA内部の回路として落とし込んでいきます。

 

実は低次のΔΣ変調であればシミュレーションプログラムをコーディングするよりもFPGAに落とし込む方が簡単だったりします。

 

もちろん、FPGAに落とし込むために作成したVerilogコードもGitHub上で共有できればと思います。

 

I2Sを受けることができるようになったので・・・

電子回路主体のブログのはずなんですが、だんだんと組み込み一辺倒になってしまっています。良くない良くない・・・・

 

FPGAの開発の模様も記事にしていけたらと思います。

 

実はAUDIY、FPGAもやっていまして、

ちゃんと評価ボードも持っています。ちなみに愛用している評価ボードはDE10-Liteです。いずれはXilinxの開発知識も身につけたいですね。

www.marutsu.co.jp


何を開発しようか

もともと自作オーディオに興味を持ち、そのために回路の勉強の報告をしていくのが当ブログのモチベーションですから、やはりここはオーディオ関係でいきたいと思います。

 

ここは最も単純なものとして、低次(~2次)のΔΣ変調を利用したPCM-DSD変換から始めてみたいと思います。

 

DSD変換を選んだ理由として

  • 基本的に加算、減算、遅延(フリップフロップ)、ビットシフト、ビット選択の要素だけで作ることができる(シンプル)。
  • PCMサンプリング周波数の64倍のDSDサンプリング周波数であればI2Sのビットクロックに同期してリアルタイムに変換可能(I2SのBCLK周波数がLRCK周波数の64倍であるため)。
  • 音質や特性はさておき、DSD変換後はカップリングコンデンサとアナログフィルタを通せば音声出力可能(DA変換自体は容易)。

 

たとえばこれがDSD→PCM変換だと

  • DSDの高周波ノイズを抑え込んでPCMに変換するには高性能なデジタルフィルタが必要
  • デジタルフィルタの性能を高めるために周波数の高いマスタークロックが必要
  • FPGAのロジックを圧迫しないためにRAM IPなどを上手く組み合わせる必要がある

と、長期化と入念な検討が必要になるのでこれはまたの機会とします。


まずはシミュレーション

そうと決まれば、FPGAに実装する信号処理をまずはソフトウェアに落とし込んでシミュレーションします。

 

こういうときに結果をすぐにプロットできるPythonは便利です。

余談ですが、AUDIYはPythonエンジニア認定基礎試験合格者の資格を持っています。持ってて損はないと思いますので興味のあるかたはぜひ挑戦してみてください。

www.pythonic-exam.com


シミュレーションの流れ

大まかに以下の流れでPCM→DSD変換をシミュレーションします。

  1. 区間[-1, 1]の浮動小数点数で正弦波を作成
  2. 任意の量子化ビット数を持つ符号付き整数(PCM)に変換
  3. PCMに変換した信号をΔΣ変調(パルス密度変調)の計算で使用できるように0次ホールドでアップサンプリング
  4. 0次ホールドした正弦波をΔΣ変調する
  5. 変調して得られたPDM波形にローパスフィルタをかけて正弦波が得られるか確認する

この流れをPythonプログラムに落とし込んでいきます。


作成した関数の説明

今回は一次ΔΣ変調のシミュレーションを紹介したいと思います。

今回のシミュレーションに伴い、以下3つの関数を作成しました。

  1. 浮動小数点数リニアPCMに変換する関数
  2. リニアPCMを0次ホールドで整数倍アップサンプリングする関数
  3. アップサンプリングされた信号を一次ΔΣ変調する関数

この3つの関数について以下説明です。

1. リニアPCM変換関数LPCM(x, nbits)

浮動小数点の信号配列xを任意の量子化ビット数nbitsを持つリニアPCMに変換します。

変換の数式は、現在の離散時間における入力をx[i]、出力をy[i]、所望の量子化ビット数をnとすると

 

f:id:AUDIY:20211216145032p:plain



となります。ここで、はxの床関数を表します。

 

これをPythonの関数にするのは比較的容易です。numpyを利用して配列全体に計算を適用できるので高速です。

import numpy as np
 
# float to Linear PCM Conversion
def LPCM(x, nbits):
    xmax = np.max(x) # Maximum float Amplitude
    xmin = np.min(x) # Minimum float Amplitude
   
    # Quantization
    x_LPCM = np.floor(((x - xmin)/(xmax - xmin)) * (2 ** nbits - 1) + 0.5) \
        - (2 ** (nbits-1))
   
    return x_LPCM.astype(int)

2. 0次ホールド関数ZeroOrderHold(xPCM, OSR)

「0次ホールド」は、内挿するサンプルの数値をもとからあったサンプルに維持する内挿方法です。

これはScipyのsignal内にあるupfirdn関数を使えば高速に実行可能です。

docs.scipy.org

import numpy as np
import scipy.signal as signal
 
# Zero-order Hold Linear PCM Upsampling
def ZeroOrderHold(xPCM, OSR):
    y = signal.upfirdn(np.ones(OSR), xPCM, OSR)
   
    return y

3. ΔΣ変調関数PDM(x, nbits)

こればかりは自作になります。

しかも都度誤差を更新しないといけないので、ループを回すしか方法はないと思われます。

関数はこちらを参考にします。

ja.wikipedia.org

技術系のブログにWikipediaというのも気が引けますが、まずは手を動かすための情報としてこちらを参考にしました。

基本的にはWikipedia内の擬似コードそのままですが、フィードバックの量子化誤差計算で1bitのDSD信号をPCM信号xの振幅nbitsまで最大化して計算するように変更しています。

import numpy as np
 
# 1st-order Pulse Density Modulation
def PDM(x, nbits):
    qe = 0 # Quantization Error
    y = np.zeros(np.shape(x)[0]) # Return Array
   
    # Modulation
    for i in range(np.shape(x)[0]):
        if x[i] >= qe:
            y[i] = 1
        else:
            y[i] = 0
         
        # Maximize the Modulated Signal to PCM max Amplitude
        yi_Analog = np.floor((((y[i]*2 - 1) + 1)/2) * (2**nbits - 1) + 0.5) \
            - (2 ** (nbits-1))
       
        # Calcurate the Quantization Error
        qe = qe + (yi_Analog - x[i])
       
    return y.astype(int), qe

 


と、関数を紹介していたら記事が長くなってしまいました。

シミュレーションそのものは次回としたいと思います。

 

 

AUDIY、STM32マイコンを始めるってよ

お久しぶりです。

 

突然ではありますが、STM32マイコンの評価ボードの一種であるNUCLEO-F411REを秋月電子から購入しました。

akizukidenshi.com

 

もともとマイコンにも興味があったんですが、最近は特にマイコンプログラミングの必要性を感じずにはいられませんでした。

 

というのも、

  • オーディオ用DAコンバータの殆どは設定をI2CまたはSPIで行う
  • FPGAでアップサンプリングなどの信号処理を実装し、動作確認としてDAコンバータと接続したい
  • 確認用のDAコンバータの設定にI2CやSPIでの制御が必要になる

 

というのが理由です。


探すのに一苦労

で、探し始めるわけですがこれがなかなか難しかったです。

求める条件としては

  1. I2C/SPIが出せるもの
  2. デバッガが一体となっていること
  3. PCと接続してプログラムするもの

といったところです。

 

常々マイコンを使っているという方であればPICやAVRマイコンのICを単体で購入して・・・という手順を踏むのだと思いますが、何もしたことがないAUDIYからしたらデバッガやライタなどを別途揃えるというのも不安でした。

 

また、I2Cが出せるのかというのも、明確な記載がされているものが少なく(「そこが組み込みプログラマの腕の見せどころ」ということでしょうか?)、目星をつけても決定打に欠けるという印象です。

 

また、条件3の時点でラズベリーパイは無し。

 

・・・とまぁそんなこんなで調べて行き着いた候補が

の2つとなりました。


STM32を選んだ理由

最終的にArduinoかSTM32-NUCLEOかに決めるわけですが、

  1. ペリフェラルの数が最も一般的なArduino Unoより豊富
  2. Arduinoより安価
  3. 開発環境含めArduino互換
  4. Arduinoより実務より(?)

という理由からSTM32に決定しました。

最後の理由はAUDIYの偏見が入っていますが、Arduinoが実際の家電に入っているという例は聞いたことがありません(まぁマイコンIC自体はAVRですから・・・)。

 

「ひょっとしたら実務でも扱われているような開発環境で勉強しておけば今後役立つときが来るかもしれない」という淡い期待です。


注文

で、このSTM32-NUCLEOボードなんですが、

どのマイコンICが載っていても大きな価格差がありません。

 

akizukidenshi.com

 

秋月だと1500円~2000円で購入できます。

ということで、本来であれば秋月の取り扱いの中で最も高性能なNUCLEO-F446REが欲しかったですが、在庫切れとのことでその次に高性能なF411REを購入しました。

www.st.com


開封

シンプルです。本体と簡単な説明書きのみです。

f:id:AUDIY:20211212161757j:plain

 

USBケーブルすら入っていないというシンプルさ。

 

そして本体も想像より小さかったです。

f:id:AUDIY:20211212162102j:plain

Raspberry Piより長さが少し短くなり、若干幅広になった感じです。

 

ボード上にスリットがありますが、それより上の部分がデバッガ/ライタとなっています。

 

USBケーブルはUSB Mini-Bとなっているので要注意です。

AUDIYもまんまとやられました・・・


早速試してみる

STM32純正の開発環境(STM32CubeIDE)でさっそくプッシュスイッチに連動するLチカの実践動画があったので試してみました。

もともと学生までずっとプログラミングしていた人間だったのですが、組み込みソフトウェアというのは本当に初めてなもので、ソースコードの内容を未だ全ては理解できていませんが、なかなか新鮮で刺激がありました。

 

開発環境のSTM32CubeIDEですが、ECLIPSEベースのIDEらしく、「ECLIPSEベース」から想像していたのとは裏腹に軽快に動作していました。

 

また、ピンアサインをGUIで決定できるというのはとても直感的でわかりやすいと感じました。


Arduinoスケッチでも動作させてみる

Arduino IDEでもSTM32用の設定をすれば開発可能とのことで、正弦波をPWMで出力してLEDを点滅させてみました。

 

Arduino IDEの設定はこちらを参考にしました。

ameblo.jp

 

const int pwmpin = 10;
const double pi = 3.14159265;

void setup() {
  // put your setup code here, to run once:
 
}

void loop() {
  // put your main code here, to run repeatedly:
  int led_value = 0;
  for (int i = 0; i < 1000; i += 1 ) {
    led_value = (int)(255 * (sin(2*pi*(double)i/1000)/2 + 0.5));
    analogWrite(pwmpin, led_value);
    delay(1);
  }
}

 

Arduinoスケッチ、記法からして明らかにC言語派生ですが基本的なプログラムならライブラリのincludeが不要など、比較的優しい仕様になっていると感じます。

ただ、Arduino IDEからの書き込みは数分待つ必要があるので、早いところSTM32CubeIDEに慣れてしまったほうが良いかもしれません。


以上、STM32マイコンの購入報告と、「組み込みプログラミング界のHello World (?)」であるLチカを試してみたという報告でした。

 

STM32マイコンそのものはARMなので、知識さえ身につければかなり高度なプログラミングが楽しめるかと思います。

 

電子工作のかたわらマイコンプログラミングの技術も少しずつ身に付けて行きたいです。

 

 

自作リニアレギュレータの正電圧側の謎をLTspiceで解析してみる

前回は正電圧側のドロップ電圧が大きすぎることがわかりました.

audio-diy.hatenablog.com

 

この原因を定電圧源にテスターを当てて観察する前に,LTspiceで原因が推測できないか見てみました.

 

全く同じではないですが,最低限必要な部品で構成したシミュレーション回路で見てみます.

f:id:AUDIY:20211121135226p:plain

 

まず気になってたのは,正電圧側に発生した2V周辺での急激な出力上昇.

f:id:AUDIY:20211005014827p:plain

 

これはLTSpiceでも観測されました.

f:id:AUDIY:20211121135329p:plain

0.4~0.5Vあたりに急激な出力電圧の上昇が見えます.

 

まずはダイオード単体の電圧を見ます.

出力と同様の曲線を描いています.(青色がダイオード,緑色が出力)

f:id:AUDIY:20211121135537p:plain

次に,ツェナーダイオードに電流を供給するトランジスタ(2SA1312)のベース・コレクタ間電圧(シアン色の曲線)と,ベース電流を決定する10kの抵抗の電圧(赤色の曲線)を見ます.

 

急激にベース・コレクタ間電圧が下がると同時に出力電圧が上昇している様子がわかります.

f:id:AUDIY:20211121140029p:plain

また,出力が5Vに安定してからはこのベースとコレクタの間の電圧降下が上昇するのもわかりますね.

 

次に,同じくツェナーダイオードに電流を供給する2SA1312のベース・エミッタ間電圧.(マゼンタ)

f:id:AUDIY:20211121140401p:plain

なるほど.トランジスタの動作が始まる(ベース・エミッタ間電圧がおよそ0.6V)ポイントでこれが起きているようです.

 

最後に,同じトランジスタのコレクタ・エミッタ間電圧(マゼンタ)とベース・エミッタ間電圧(緑)を見るとなんとなく理由がわかってきます.

f:id:AUDIY:20211121141425p:plain

ツェナーが出力を開始するまでトランジスタのコレクタ・エミッタ間電位差が上昇しています.

 

おそらくはこういうことかと思います.

では負電圧側はなぜこの盛り上がりが発生しないのでしょう?


負電圧出力も見てみる

同様に2SC3324側のコレクタ・エミッタ間電位差の推移も見てみました.

f:id:AUDIY:20211121143215p:plain

なるほど.2SA1312側は電位差が増えていくのに対し2SC3324は能動領域に近づくにつれ電位差が減少していくようです.

 

なので正電圧側の急激な出力電圧の変動は負電圧側では起きないということでしょう.


今回はコンプリメンタリでも遮断領域→能動領域での遷移でベース・エミッタ間電圧とコレクタ・エミッタ間電圧の関係が異なるという事例をシミュレーションベースではありますが掴むことができました.

 

正電源側も不電源側も同じトランジスタで定電流源を作ることができれば解決しそうですが,電源電圧を遷移させて動かす回路でもないのでその必要はなさそうです.

 

ちなみに,正電源側のドロップ電圧が大きい理由はシミュレーションではつかめませんでした.いよいよテスターで当たり直す必要がありそうです.

 

今回も最後までお読みいただきありがとうございます!