STM32でDDCを作ろう!(DCCじゃないんだ)
暑い夏は家に閉じこもってエアコンと扇風機直射の作業机で回路製作するのが一番です.
突然ですが、feedbackが動きました.今までfeedbackが動かないと悩んでいた理由は、windows HOSTのUAC2.0デバドラのfeedbackの応答速度が想像してたよりもすごく遅いからだった、と思われます.あまりにも低速なloopに対して「動け!動け!」とfeedback量を過大にしたためloopが発振していました.
てかこの表現って、loopを扱った経験の無い人には理解困難かと思います.しかし以下ではもっと理解困難な説明になってしまう事をあらかじめお詫びしときまぁす.
【環境・条件】
HOST: windows10 USB Audio Class2.0 driver
DEVICE: STM32F205 UAC2.0 class 48kHz32bit(現状ここまで)
feedback interval: 4mSecまたは1mSec isochronous転送
deviceは自作プリント基板
UAC規格書に書かれているfeedback仕様は、
・1マイクロフレーム中のサンプル数
・位取りは、整数部16bit+小数部16bitの4bytes (little endian)
・feedback end point経由でHOSTへ送る
1マイクロフレームは125uSecなので、48kHzはその中に6ヶ入ります.ゆえに、48kHz再生時のfeedback値は0x00060000がtypical値となります.typical値を中心にしてclock jitter等でふらつきます.
UAC規格書には、feedback intervalについて推奨する記述はありません.
UAC規格書にはdevice側でどのような平均化処理をするべきかについての記述はありません.同様にHOSTがどのような平均化処理をするべきかについても記述がありません.ゆえにfeedback loopの応答速度を推察させるような情報はUAC1.0 UAC2.0規格書のどこにもないんです.
【HOSTの応答速度が遅い件】
あとで動画を見てもらいますので事前説明が必要です.
動画には赤と青の波形が映っています.これらはaudio stream dataを格納するリングバッファの挙動を観測しています.
赤: リングバッファへHOSTレートで書く.read pointerよりも半周先行していて欲しいが、諸事情でブレている様子 (=write pointer)
青: リングバッファを48kHzの定レートで読む一周分 (=read pointer)
つまり、赤い波形のエッジが中央で安定していればOKというのが着眼点です.feedbackをかけているけれど安定度はあまり良くないです.
ここで、読者の疑問に答えます.
疑問:マイクロフレームレートは125uSecで一定なのにHOSTがレート調整するもなにもないだろう
回答:上で1マイクロフレームは125uSecなので48kHzはその中に6ヶ入ると書きました.これをbytesに直すと、1サンプル32bit=4bytes、さらにステレオなのでその倍、すなわち6x4x2=48bytesが1マイクロフレームに内挿されてaudio dataがUSBを飛んできます.ただしこれは標準状態でのハナシ.
feedbackによってレート上げ下げが要請されるとHOSTはこの48bytesを増やしたり減らしたりします.
わたしが観測したところでは、47bytesとか49bytesのような半端な制御はしないみたいです.40bytesとか56bytesにもしないっぽい.0か48bytesかのswitch制御をしているみたいです.
訂正:40bytes,56bytesに限り盛大に発生します.32や64は出ません.
前置きが長くなりました、これがfeedback interval 4mSecのときの動画です.赤線のエッジが画面中央で安定してればGOODという目で見てください.おっとっとlockが外れそうだという場面を選んだとはいえ、揺れて危なっかしいです.リングバッファの深さは10kbytesにしてあります.リングバッファを倍増させるなどの対策をしないとダメだなこりゃ.
この動画から読み取りたい重要情報は、応答速度の遅さです.大雑把に言って行き過ぎが是正されるまでに4秒ぐらいかかっています.4mSec毎のfeedbackにして応答が4秒ですから、HOSTは鬼の様な平均化処理をしていると推察されます.約1000倍かかってるので、数式的にはこんなオーダーのIIR filterのようなものをHOSTデバドラは実装していると推察されます.
rate(N+1) = 0.999 rate(N) + 0.001 feedback(N)
遅いなぁ..... 諸事情は色々と思いつきますが、それにしても応答が遅くて使いづらいぞこのloopは.
ここに至り、応答速度を速めたいと思います.そのためにfeedback intervalを1mSecに変更してみました.変更する場所はdevice descriptorのfeedback end pointの項目です.こちらの味付けだと安定感が増していますので1mSecを採用です.
UAC2.0 device descriptorのfeedback EP書式に1mSec未満を記述できるのかどうか自信がありません.不可能なんだろうと思っています.
応答速度の遅さについては以上.
【feedbackの定義に疑問あり】
UAC規格書記載のfeedback値の定義は上で述べた通り、「1マイクロフレーム中のサンプル数」なのですが、字句通りにこれを実装しても動かないんじゃないかなぁと初期から思っていたわたしです.実際やってみて、やっぱり動きませんでした.
その事情を説明します.
feedback loopは一種のPLLです.つうかレート調整回路はPLLそのものです.
PLLの挙動は2stepから成ります.
step1: 周波数ロック
step2: 位相ロック
つまり、refとvco outをオシロで観測すると最初は周波数すら一致していないので高速で流れています.周波数が近づくにつれて流れが緩やかになる、ここまでがstep1.その後両者の位相がカチッとロックする、それがstep2です.
わたしの懸念は「1マイクロフレーム中のサンプル数」を測定してfeedbackしたって、周波数ロックは達成出来ても、位相ロックなんか出来ないと思うんだよなぁ.しかもHOSTがクソ遅いんじゃloopの安定性にかな~りに気を使いますぞと.この懸念は当たりでした.
位相ロックが必要な理由を述べます.
リングバッファの図を再掲.read pointerは48kHzレートで定速運転します.write pointerは別時刻のHOSTレートです.HOSTレートの速い遅いでwrite pointerがread pointerに衝突してしまうとaudio dataを踏みつぶしてしまうので死にます.それを防ぐアクセル/ブレーキがfeedbackだと言えます.
だとすると、feedbackが制御対象とすべきなのはwrite pointerが半周先行位置をkeep(位相サーボ)することなはずです.ところがUAC規格書に書かれているのは1マイクロフレーム中のサンプル数(速度サーボ)です.だけどそれを制御対象にしてもアバウト過ぎるとわたしは思うんです.
喩え話1:目的は位置制御なんだけど、諸事情により速度サーボとせざるを得ず、速度を積分してみたら位置精度がゲスになってしまってめでたく死亡
喩え話2:VTRのキャプスタンサーボは、速度サーボ+位相サーボをmixしている
以上の考察により、上の動画を撮影したfeedback loopはリングバッファの位相サーボ方式で実装してあります.サンプル数方式も実装して試したけどマトモには動きませんでした.
仔細なハナシですが、元来deviceとHOSTのレート差は在っても100ppmオーダーなわけです.XTALの周波数誤差がレート差の由来ですから.なのでfeedbackに速度サーボは不要で位相サーボのみで良いと思われます.
【feedback実装仕様】
まず、リングバッファ上で、write pointerの半周先行位置をゼロとします.そこからのoffset bytesを正負のintで数値化します.変数名をint wr_advとします.
feedback intervalを1mSecとしますので、1mSec毎のTIM割り込みで、以下の計算をします.
uint32 feedback(N+1) = 0x00060000 - wr_adv(N) / 2
ここで0x00060000は48kHz再生におけるfeedback値のtypical値です.要するに、半周先行位置からの偏差をfeedbackに反映させるわけですね.割る2はゲイン調整.
式は他にもいろいろなのを試してみました.
・センター付近に不感帯を設ける
・step応答にする
・リミッタを設ける
・センター付近のみ高ゲインにする
しかし最も素直な上式が良さげでした.
仔細なハナシですが、リングバッファのサイズを10kbytesとすると、それは1250サンプル分に相当し、48kHz再生では26mSecに相当します.リングバッファからDACへDMA転送するので、read pointerはDMAの都合で半周に一度、すなわち13mSec毎にしか採取できません.したがって、1mSec毎のfeedbackですが13回は同じ値を返すことになります.少し残念です.
それと、1ms毎のisochronous要求を無視したからといって直ちにUSB busがhungupするような事は無いみたいです.
長らく進捗が滞っていたfeedbackでしたが光明が見えてきました.
かしこ
追記:hard faultが出て困っていたのですが、sprintfを使うのをやめてsnprintfに変えたら治った.困ったときはsnprintf.
その昔「ファクシミリ」という装置がありまして(今でもあるか・・・)
返信削除その開発の初期の頃に、「画像の位置をどうやって同期させるか?」という
議論がありました。大まかに言って、それは「2派」に分かれていて、一つは、
・基準クロックの周波数精度を極限まで上げれば、最初に一回だけ合わせればよい
というもので、もう一派は、
・基準クロックの周波数精度はそこそこでよくて、毎回画像を送る度に、位置合わせのための信号を送るべき
というものでした。
両者は、当時の技術の粋を集めて試作をしていたのですが、「周波数派」のほうは、
「どんなに、周波数を合わせ込んでも」時間が経つにつれて「画像が乱れる」
問題に悩まされて、結局、うまくいったのは、「一画像ごとに、同期をとる」
方式のほうでした・・・
※まぁ、当時の技術だと「周波数精度」といっても「恒温槽入り水晶発振器」
位のモノだったんでしょうけど。
という話を、思い出しました。
※「周波数精度」よりも「同期信号」のほうが重要である、ということを、
規格制定者(あるいは、実装者)は、知らなかったんでしょうかね・・・
(まぁ、ありがちですが。)
※勿論、実際は「両方重要」ですが
おはようございます.
削除ファクシミリ、いちおうウチにもありんす.滅多に使わないですけど.
>基準クロックの周波数精度を極限まで上げれば
削除あははー、いいなぁこの一派.プラトン主義者ですね.
アリストテレスなわたしとしては「そんなの何やったってズレていくんじゃね?」って笑ってたと思われます.
>「周波数精度」よりも「同期信号」のほうが重要である、ということを、規格制定者(あるいは、実装者)は、知らなかったんでしょうかね・・・
削除同期問題は規格にはあまり盛り込まれないことが多いみたいですね.設計で勝手にやれというつもりの様で.また研究者はプラトンなので同期を無視する人が多いです.
たぶんですけど、「基準クロックの周波数精度を極限まで上げれば」をガチでやったのがGPS衛星でしょうか? 原子時計積んで上がっているものかと思います.相対論的時間のセカイへw
削除>同期問題は規格にはあまり盛り込まれないことが多い
返信削除多分「食事する哲学者の問題」と同じで、
「下手に規格に盛り込むと、技術の進歩などの影響で後々禍根を残す」
ことを恐れているのかもしれませんね。
※「食事する哲学者の問題」なんて、未だに新しい方式が提唱されているという
「古くて新しい問題」ですからね・・・
ちなみに、高精度な基準クロックと言えば、
削除「ルビジウム原子発振器(原子時計)」が、ネットで探せば
安いもので数万円で買えます。
※もともとは、携帯の基地局に使われていたもの等が出回っているらしい
※一時、「ハイレゾ音源」のマスタークロックとして使える!という祭りがあった
(最近は、落ち着いてるみたいですが、一時価格が高騰w)
※頑張れば、10e-10くらいの精度は出せるらしい・・・
※ちなみに余談ですが、安いルビジウム原子発振器よりも、「高度に温度制御した
水晶発振器」(一部屋くらい占拠するのか?)のほうが、実は精度が高いそうです
「食事する哲学者の問題」って知らなかったです.中華屋の回転台よりも複雑だと思いました.
削除いまお昼ご飯でパスタをフォーク1本で食ったとこです.
初めまして、私も似たようなものが作りたくてRaspberry PiでやってたんですがWindowsで認識しないわコマンドラインが変なエラー吐くわで困ってました。STMマイコンなら何とかならんかなーと思っていた時に見つけたのでありがたい限りです。
返信削除こんにちはです.
削除STM32cubeMXが吐いてくれるのはUAC1.0なのでわたしはUAC2.0に変更してやっています.
安価なDDCを欲しい人は世の中にはたくさんいるんじゃないかと思います.完成したら設計情報を公開しますのでよろしくお願いいたします.ではー