前にちょっと書いたけど、水晶発振子のモデル化のための測定についてもうちょっと詳しく書いておく

LCRメータでCpを測る

Cp=1.95pF @100kHz

VNA で測ってもいいけどキャリブレーションが面倒なのでLCRメータを使うのが楽 (値が小さいのでLCRメータでもキャリブレーションはすること)

VNAでfs/fp/Rsを測る

fs/fp


VNA のポート1ポート2に直列で水晶発振子を繋いで、共振周波数付近をさがす。最もゲインが大きいところがfsで、小さいところがfpになる。

fs=9.9985Mhz
fp=10.0126Mhz

Rs

VNAのポート1に水晶発振子を繋いで、直列共振周波数 (fs) 付近で測る。fs 付近だと(共振しているので)水晶が純抵抗に近くなり精度が高くなる。

Rs=12.8Ω

Cs/L を求める

以下のように Cs L を求める

f_s = 9.9985
f_p = 10.0126
R = 12.8
C_p = 1.95e-12

C_s = C_p * ( (f_p**2) / (f_s**2) - 1)
L = 1 / (4 * (Math.PI ** 2) * (f_s ** 2) * C_s)
console.log({C_s, L}); //=> { C_s: 5.503702932046284e-15, L: 0.04603790760936647 }

LTSpice でモデル化してみる

求められた R/Cp/Cs/L を使って等価回路を作り、周波数特性を見てみる。

LTSpice の結果は信号源の出力に対する比なので、負荷の電圧を見る場合にVNAとスケールをあわせるには2倍 (電圧 +6dB) する。

  • LTSpice: -1.04dB
  • VNA実測: -1.31dB

ダイナミックレンジの関係で並列共振周波数の値は参考にしかならない。

ref

  1. トップ
  2. tech
  3. 水晶発振子のモデル化

ケーブルのLCを直接測る

伝送路の損失を無視した場合、特性インピーダンス

であるため、直接ケーブルのLとCを計測してやる。


例:50Ωの基準点に75Ωケーブルを単純な変換コネクタを使って接続している。ケーブルインピーダンスは10MHzでの値が規定されていることが多い?ようなので 10MHz で SPAN 0 にし、スミスチャートを表示させている。

  • オープンの場合 139pF
    • オープンはケーブルの芯線とGND間の静電容量を測っている
  • ショートの場合 736nH
    • ショートは芯線とGNDによるインダクタンスを測っている
Math.sqrt(736e-9 / 139e-12) //=> 72.7665034804956

集中定数とみなせる範囲でできるだけ長いケーブルでやるべきとのこと。10MHz の波長は 30m なので、その1/10での 3m 程度で計測するのが正しいらしい。長すぎると集中定数とみなせず、短かすぎると誤差が多くなる。

欠点

  • 基準点に直結できる必要がある
  • 精度が良くないらしい (50Ωから離れたところを計測するので)

λ/8 法

やりかたと例


  1. オープンケーブルを繋ぐ
  2. リアクタンスが0になる最初の周波数をさがす → 28.24MHz
    • スミスチャート的にはX軸と交わるところを探す
  3. 得られた周波数のさらに半分の周波数を計測する → 14.1MHz
  4. リアクタンスを読んで絶対値をみる → -71.6Ω → 71.6Ω (ショートさせたほうが正確らしい?)

欠点

  • 基準点に直結できる必要がある

利点

  • 比較的正確?

原理

伝送路の損失を無視した場合、伝送路の特性インピーダンス と入力インピーダンス には を伝送路の電気長、 を負荷インピーダンスとすると以下のような関係がある

は波数

特に伝送路端がオープン ( ) の場合

ショート( ) の場合

オープンの場合で電気長 の場合、

ということでこのようなケースの場合は入力インピーダンスの虚部が特性インピーダンスと一致する。

伝送速度 のとき周波数 なので、測定したいケーブルの長さを固定とすると周波数をうまく選んであげれば良いことになる。

ところで、電気長が の伝送路(オープン)は共振を起こす、つまりリアクタンスがゼロになる。共振点を探すのは周波数領域では簡単なので、まずこの共振点を探し、さらに周波数を半分にすることで電気長 の条件を達成できる。

TDR

直読できる

欠点

  • 基準点から計測点までのケーブル損失によって誤差がでる
  • 多重反射の影響をうける

利点

  • グラフと現実が一致するのでわかりやすい
  • 余計な操作がいらない

参考文献

  1. トップ
  2. tech
  3. VNA によるケーブル特性インピーダンス測定

  • DC外挿してないけどなんで?
  • どのぐらい誤差がでる?

DC外挿とは?

VNA で負の周波数まで拡張して時間領域変換する場合、DC値も計算対象に含むが、VNA はDC値は測定できないため、推定値をいれてあげる (外挿する) 必要がある。

上の図はローパスモードの処理対象の概念図で、DC〜開始周波数までのデータポイントが抜けていることを説明している。

外挿の方法

  • 周波数ステップの延長上に0Hzがくるように調整
  • DC付近の数点から0Hzを推定

推定は簡単にいえば最低周波数が計測ステップ数と同じでなくてはならないということになる。

50kHz〜1.5GHz で計測するなら、1.5e9 / 50e3 = 30000点を計測ステップにしないといけない。がこれはさすがに計測点が多すぎて時間がかかるので、最低周波数を上げる必要がある。しかし最低周波数を上げてしまうとDC推定が困難になるという問題がある。

反射係数の応答は周波数ドメインでは振動している。周波数ステップが広すぎると、直近 n 個が増加傾向だからといって、次のステップでも同じように増加するとはあまりいえず、推定をはずすことが多くなる。なので、最低周波数は上げたくない。

1パスでの処理を諦めて、最低周波数付近だけを先にサンプリングして処理する方法も考えられる。VNA の下限周波数が 50kHz なら、50kHz、100kHz、150kHz を計測してDC推定し、3つの実測値は捨てる。そして測定開始周波数を最大周波数 / 計測点数にセットして全体をスキャンする。こうすればDC値と連続した計測点(n+1)がつくれる。

外挿の方法は自体は単純に、直近 3 個の増減の平均をとり、最初の点から線を延長する形でやるのがよさそうだ。

            nn = np.mean(np.diff(x[0:3]))
            dc = x[0] - nn
            data[0] = dc

NanoVNA では DC外挿してない?

NanoVNA は組み込みだと 101 点計測。これだけの値でDC値を推定しようとすると、周波数ステップが広すぎてうまくいかない。前述のように2パスで処理するとか工夫がいりそうで、これをやるには若干めんどうな設計変更がいる気がする。

外挿しないというのは最低周波数の値 (50kHzとか) をDC値としてそのまま採用しているということで、IFFT 時の周波数ステップには実質的に誤差が生じていることになる。低い周波数ほど誤差が大きく、全域の平均誤差は25kHz。

どのぐらい誤差がでる?

周波数誤差が時間領域でどのぐらい影響するのかの理論値はどう計算すればいいかよくわからないので、実験的に比較してみる。

  • 最大周波数 1.5GHz
  • 窓関数なし
  • 測定点 500 点
  • 測定対象は50cmぐらいの50Ω同軸ケーブルのS11

DC外挿する場合は IFFT の有効なデータ点数は 1001 点、しない場合は 999点 (測定点の最初をDCとして折り返すので)

DC外挿する

python を使って2パスで処理させてみる。前述のように 50kHz、100kHz、150kHz を計測してDC推定し、3MHz〜1.5GHz を 500 ポイント測定した結果に挿入する。実装は python/nanovna.py を変更する形で行なった。変更点

以下の3つのグラフは上から「DC付近の拡大 (周波数ドメイン)」「DC〜200MHzまで切り取り (周波数ドメイン)」「IFFT して積分したステップ応答(時間ドメイン)」

一番上のグラフにはDC外挿のために参照する3つの測定点(+マーカー)と、外挿されたDCの値(☆マーカー)を表示している。

二番目のグラフはもう少し広い範囲を表示している。このグラフは外挿されたDC値を含む

(ちなみに101点でも別にそれほど変化はない)

ちょっと長いが一応グラフ出す手順を書いておく

        dcExtrapolation = True
        port = 0

        if dcExtrapolation:
            print("start:%d stop:%d points:%d" % (self.frequencies[0], self.frequencies[-1], self.points))
            nv.set_frequencies(int(self.frequencies[-1] / opt.points), opt.stop, opt.points)
            print("start:%d stop:%d points:%d" % (self.frequencies[0], self.frequencies[-1], self.points))

        data = nv.scan()
        x = data[port]
        # window = np.kaiser(len(x) * 2, 6.0)
        # x *= window[len(x):]
        nh = len(x) * 2
        NFFT = 2**(len(str(bin(nh)[2:])))
        print("num: %d, NFFT: %d" % (nh, NFFT))
        data = np.zeros(NFFT, dtype='complex128')

        fig = pl.figure(figsize=(8,10), dpi=100)

        ax1 = fig.add_subplot(3, 1, 1)
        ax1.set_ylim(-1.2, +1.2)
        ax1.set_xlim(0, 200e3)
        ax1.xaxis.set_major_formatter(EngFormatter(unit='Hz'))

        ax2 = fig.add_subplot(3, 1, 2)
        ax2.set_ylim(-1.2, +1.2)
        ax2.set_xlim(0, 200e6)
        ax2.xaxis.set_major_formatter(EngFormatter(unit='Hz'))

        ax3 = fig.add_subplot(3, 1, 3)
        ax3.set_ylim(-1.2, +1.2)
        ax3.set_xlim(0, 20e-9)
        ax3.axhline(y=0, color='grey')
        ax3.xaxis.set_major_formatter(EngFormatter(unit='s'))

        if dcExtrapolation:
            # DC extrapolation

            ## get vna lowest freq data 50kHz 100kHz 150kHz
            _start = self.frequencies[0]
            _end   = self.frequencies[-1]
            _points = self.points
            nv.set_frequencies(50e3, 50e3 * 3, 3)
            print(self.frequencies)
            expdata = np.array(nv.scan()[port])
            ax1.plot(self.frequencies, expdata.real[0:3], marker="+", label="measured real", markersize=10)
            ax1.plot(self.frequencies, expdata.imag[0:3], marker="+", label="measured imag", markersize=10)
            # restore
            nv.set_frequencies(_start, _end, _points)

            ## extrapolate from lowest 3 data points
            di = np.diff(expdata[0:3])
            print(di)
            nn = np.mean(di)
            dc = expdata[0] - nn
            ax1.plot([0], dc.real, marker="*", label="extrapolated real", markersize=10)
            ax1.plot([0], dc.imag, marker="*", label="extrapolated imag", markersize=10)

            # negative freq
            data[-len(x):] = np.conjugate(x)[::-1]
            # positive freq
            data[1:len(x)+1] = x
            ## data[0] is dc term
            data[0] = dc
        else:
            # dc + positive freq
            data[0:len(x)] = x
            # negative freq
            data[-len(x)+1:] = np.conjugate(x)[1:][::-1]

        step = self.frequencies[1] - self.frequencies[0]
        xaxis = np.concatenate([
            np.linspace(0, step * NFFT / 2, int(NFFT / 2)),
            np.linspace(-step * NFFT / 2, 0, int(NFFT / 2), endpoint=False)
            ])

        ax1.plot(xaxis, data.real, marker=".", color="lightgrey", label="real")
        ax1.plot(xaxis, data.imag, marker=".", color="lightgrey", label="imag")
        ax2.plot(xaxis, data.real, marker=".", label="real")
        ax2.plot(xaxis, data.imag, marker=".", label="imag")

        td = np.real(np.fft.ifft(data, NFFT))
        step = td.cumsum()
        print(self.frequencies[1] - self.frequencies[0])
        time = 1 / (self.frequencies[1] - self.frequencies[0])
        t_axis = np.linspace(0, time, NFFT)

        ax3.plot(t_axis, step, label="step response")
        # ax3.plot(t_axis, td,label="impulse response")

        ax1.legend( loc = 'lower right')
        ax2.legend( loc = 'lower right')
        ax3.legend( loc = 'lower right')

DC外挿しない

外挿しないので一番上のグラフには何も描かれていない。

単純に開始周波数を 50kHz として、その値をそのままDC値として利用する。イメージとしては中央のグラフが本来あるべきものより左に少しずつずれている。終了周波数は一緒

なお開始周波数を 500kHz 5MHz 10MHz と段階的に上げていくと以下のように崩れていく。

比較

別々に見ても違いがわからないので、外挿したケースと外挿せず50kHzをDCとみなすものの重ねあわせている。

確かに 50kHz と DC外挿をちゃんとやる場合とで若干差がみえる。が、-1 〜 +1 の範囲で見ている限りはかなり分かりにくい。

雑感

現行の実機の実装に正確なDC外挿を入れるのはけっこうやっかいなのと、実質的な使われかたは絶対的な数値というよりも相対的な変化地点の発見なので、がんばってやる価値があるかはちょっと微妙だなあという印象。何かもっと影響を受けやすいDUTがあれば違うのかも。

参考資料

日本語での文献がほとんどなく、英語でも実際にどうやってDC外挿を行なうべきかの資料はあまりないので結構こまった。VNAのマニュアルやアプリケーションノートのおかげである程度理解できた。

  1. トップ
  2. tech
  3. NanoVNA の時間領域測定 (TDR/TDT) ローパスモードのDC外挿

アンテナの調整をする場合、給電点 (測定点) と調整点は離れていることが多いなというコンテキストで、NanoVNA の Bluetooth シリアル化を試すというのを前にやりましたがイマイチで、実用に及びませんでした。なので別の方法を検討します。

「古いスマフォでUSBシリアルを中継したほうが」と前述のエントリで書いていましたが、ESP32 で Wi-Fi AP にする方法をやってみたかったので試してみました。

構成

  • USB Host (CDC デバイスをUARTにする)
  • 無線インターフェイス (Wi-Fi)

NanoVNA とは USB 接続して、スマフォから Wi-Fi で繋ぐと計測結果が見れるようなイメージのものです。なので本体への改造はありません。

アーキテクチャ

既存の資産 (NanoVNA-WebApp) をそのまま生かしたいため、USBシリアル通信層を置き換えて Wi-Fi 経由にするイメージにします。双方向の小さなパケットのやりとりなので、自然と WebSocket を想定し、これにそのままシリアル通信を乗せます。こうするとクライアントの多重接続はできませんがとりあえず考えないことにします。

作成

USB ホスト CDC と UART の中継

まずは USB CDC のホストになれるデバイスを作ります。ESP32 は直接的に USB Host になるような機能・実装はないため、別の MCU を使います。

STM32F4 系は USB Host になれるようになっており、WeAct MiniF4 という小さいボードが安価 (400円ほど) で Type-C コネクタ付きで販売されているので、これを利用します。

特に難しいことはせず、USB CDC と UART を中継する役目だけをさせます。面倒だったので CubeMX の生成コードを活用して実装しました。

この段階のデバッグはまた別途 USB Serial デバイスを使ってやりました。UART を中継するという役目だけなので、独立したコンポーネントになっておりデバッグは楽です。これが SPI 中継だとさらにややこしくなるので、UART で中継というのはスピードが出ない以外は良い選択だと思います。

UART のボーレートは 4M baud です。4M baud / (8 bit +1 stopbit) = 444444 bytes。444kbytes/sec です。これは CPU の HCLK が 64MHz で、PCLK1 (APB1 clock) が 32MHz であることからくる最大の制限です。PCLK1 はこのデバイスの UART2 のベースクロックで、UART2 を8倍のオーバーサンプリングにしているため、4M baud が最大クロックとなります。HCLK は実はもう少し早くできるのですが、デバッガの関係で 64MHz にしています。実用上は 4M baud で十分に感じます。

なお RTS/CTS によるハードウェアフローコントロールを有効にしています。これがないとデータ抜けが発生したりしてややこしいです。

Wi-Fi インターフェイス

UART と WebSocket の中継

あとは ESP32 上でいろいろと実装を作ります。ESP32 はかなり高級なMCUなので、ゴリゴリ処理を書いてリソースを書きこんでいけます。

まずは WebSocket に UART を中継する実装を行いました。開発PC側ではさらにターミナルと WebSocket を中継するような実装を node.js で書き、まずこれで WebSocket 経由で NanoVNA のシェルを不自由なく使えるレベルにしました。

ちなみに、この段階では家庭内の Wi-Fi ルーターに接続するコードにしておき、いちいち AP を繋ぎなおすようなことはしていません。

シェルぐらいなら WebSocket・Wi-Fi 経由で問題なく動くぐらい安定して動くことが確認できたので次の段階を作りはじめました。

ウェブインターフェイスの実装

前述の通り、NanoVNA-WebApp を生かすように WebSocket でシリアル通信を行うような実装を追加しました。まだここでは、いちいち書きこんでデバッグするようなことはせず、コンテンツ配信のウェブサーバは手元で起動し、WebSocket のみを ESP32 で処理するようにしてデバッグしました。ウェブアプリのデバッグとプロトコルの中継をわけて実行できるので割と開発しやすいです。

手動で操作するシェルと違い、自動的にゴリゴリシリアル通信をしはじめるとタイミングの競合などが出てくるので、それらを潰していきました。

Capative Portal 化

Wi-Fi の AP としてアクセスできるのはいいとして、AP 接続後にいちいちブラウザを開いて特定のアドレスを入力するというプロセスがだるいです。

Wi-Fi 接続時に OS のログインダイアログを出させる方法 (Captive portal) というのを書きましたが、この方法だと Wi-Fi 接続と同時にウェブアプリを起動させることができて便利になります。実際の動作状況は以下の通り

esp32 には mDNS サーバの実装はあるものの、DNS サーバーの実装がないため、独自に簡単なものを実装しました

今後の課題

  • ちゃんとケースに入れて実用できるようにしたい
  • 電源
    • USB Host は NanoVNA 側にも電源供給源になる必要があり、要求電流量が多い
    • 電圧降下すると NanoVNA が起動しなかったりするので工夫がいりそう
  1. トップ
  2. tech
  3. NanoVNA のリモート化 (Wi-Fi 中継)

TDR (Time Domain Reflectometry) はともかく、TDT (Time Domain Transmissometry) の使い道がよくわからなかったので調べていた。

TDRの横軸と縦軸

  • 横軸は時間
  • 縦軸はその時間における反射係数。または反射係数から導きだせるもの

特定の箇所のインピーダンスだとかSWRがわかる。

TDTの横軸と縦軸

  • 横軸は時間
  • 縦軸はその時間における透過係数。または透過係数から導きだせるもの

例えば透過係数の大きさをとれば、どのタイミングで信号が伝わってきたかがわかる。もしマルチパスがあるような伝送経路だとそれぞれが可視化される。

TDR と TDT

このスクリーンショットは、青色が TDT、黄色が TDR を表示している。単に VNA の入出力に 2m の同軸ケーブルを繋いでいる (ダイナミックレンジが高くないとわかりにくいでのS-A-A-2 でのスクショ)

  1. TDT は片道なのでまず最初に(0sに近いほうで)現われる。そしてVNAの入力ポートで一部は反射されて戻っていく
  2. TDR 側は戻ってきた反射を観測する。そしてVNAの出力ポートで一部は反射されてまた戻っていく
  3. TDT はまた出力ポートから反射されてきた信号を観測する

という挙動がわかる。キーサイトの時間領域透過率測定の項目 だとトリプルトラベルパスと書いてある。

光速で伝達する波がしっかり往復しているのをぱっと観察できて面白い。

ref

  1. トップ
  2. tech
  3. VNA の TDR と TDT

Aliexpress を徘徊していたらそういうものが出ていたので買ってみた (400円ほど)。これ元ネタはおそらく SDRKit のVNWA Testboard kit というものだと思う。

自分が手に入れたものは以下のセットであった

  • 基板 (PWB)
  • 49.9Ωチップ抵2抗 * 2 0805
  • 丸ピンヘッダ
  • SMA メスエッジコネクタ * 6
  • ナット・ビス 4セット

フィルタ用のパターンがあるが今のところ使う予定がないのでコネクタも実装してない。

冒頭の写真はわざと誘導性の非連続を作って観察してるところで、教科書通り?の波形が見えて結構おもしろい。

  1. トップ
  2. tech
  3. NanoVNA Test board kit というやつ

無料 Wi-Fi なんかでよくある、接続時にログインさせられるやつ。あれの名前は Captive portal というやつらしい。各OSともにこれを検出したときはログインダイアログを表示するような実装になっている。

Captive portal が出る条件は (OSによって違うだろうけど)

  • OS 側が用意するドメインへの http アクセスをリダイレクトすること

のようだ。OS側が用意するドメインとは例えば

  • captive.apple.com (macOS)
  • connectivitycheck.gstatic.com (Android)

みたいなやつ

やることとしては

  1. DNS サーバを立てる。全ての A レコードクエリに対し captive portal の IP アドレスを返すようにする
  2. DHCP の DNS サーバに自分の IP アドレスを指定
  3. captive portal は HTTP サーバを立て、自分以外のホストへのリクエストをすべて自分のホストへリダイレクトする

これで macOS と Android でダイアログが出ることは確認した。

メモ

自力で DNS Server を実装するとOSの違いに気付くことがある

  • macOS は DNS response に QUERY が含まれていなくても (ANSWER だけで) 認識する
  • Android は DNS response にQUERY が含まれていないと認識しない
  1. トップ
  2. tech
  3. Wi-Fi 接続時に OS のログインダイアログを出させる方法 (Captive portal)

トラブルシューティング

DMA ではない HAL_UART_Transmit は動くか試す。動かないなら UART の設定やピンを確認してみたほうが良い。

DMA すると何も動かんとき

generated code がうごかないじゃん…… となったら HAL_DMA_Init を最初にもってくると動くことがある。

とりあえず変更して試すなら適当に編集して試せば良い。が再生成するとき困るので、CubeMX 上でこの順番は変えておくのが良い。Project Manager → Advanced Settings にいくと順番を変更するUIがある。

最初の一回だけ成功するとき

UARTn global interrupt をオンにする。HAL_UART_IRQHandler あたりで送信の後処理とかをしているようで、この割込みが有効でないと最初の DMA 以降は HAL_BUSY となり成功しない。

  1. トップ
  2. tech
  3. STM32 CubeMX で生成した HAL_UART_Transmit_DMA がうまく動かないとき

Aliexpress で LNA 買ってみた。

Specification:
Model: 50K-4G
Optional Type: Bare Board, CNC Shell
Power Supply Voltage: DC9-15V, typical +12V (Typical current value 45mA)
Gain: Typical value 25dB 0.8G
Input Output Impedance: 50Ω
Maximum output power: P1db + 16dBm 0.8G
Input signal: <0dBm (>0dBm input signal has been distortion)
Bandwidth: 50K-4GHZ (different gain in different frequency)
Noise Coefficient: 1.9dB 1.9GH
Bare Board Size: Approx. 6*2.5cm/2.4*1inch

CNC Shell Size: Approx. 7*3.3*1.3cm/2.8*1.3*0.5inch

Weight: Approx. 5g~40g / 0.2oz~1.4oz

3G までの特性。測定方法は以下の通り

  • スペアナの入力に 30dB のアッテネータをつける
  • -20dBm のトラッキングジェネレータ出力を入力とする
  • LNAの代わりにスルーコネクタをつけてノーマライズ
  • LNAに変えて電源を入れて測定

  1. トップ
  2. tech
  3. 50K-4G LNA (25dB@0.8G) を買ってみた

SWD (Serial Wire Debug) やっててさらにトレース(printfみたいなこと)も行いたいことは多い。別途 USART を繋げて printf() デバッグしても良いが、正直めんどうくさい。

ITM とは?

Instrument Trace Macrocell の略で Cortex-M の Arm CoreSight (MCU 側のデバッグ機能) に含まれるトレース機能。トレース機能は雑にいうと printf() みたいなもので、実際 printf() を中継するのに使うこともできる。

SWO (Serial Wire Output) というトレース用のピンを使う。

semihosting との違い

セミホスティングとはが詳しいが、セミホスティングはホスト側でシステムコールの一部をホストするという仕組みで、bkpt (ブレークポイント) などで発生する例外を ARM プロセッサのデバッガインターフェイスが拾ってホスト側に中継する。

仕組み的にビルド・トレース環境とかを作るのがめんどい。

ITM の Pros. Cons.

  • ↑ 特殊なビルドが不要
  • ↓ 余計な配線が1つ必要 (SWO)

semihosting の Pros. Cons.

  • ↑ できることが多い
  • ↓ ビルドが複雑

中華 ST-Link V2 の改造

よく売ってる安い ST-Link V2 には SWO 用のピンが出ていない。機能自体はあるので配線してやる。5V と 3.3V はピンが2本ずつ重複しているので、片方を潰せば特にデメリットなく拡張できる。

↑ こういう感じでケース(カバー?)は外せる。USB コネクタ側にスライドする

↑ 31ピンが SWO 用のピン。一応親切?なのか若干配線が伸びてるので、カッターでレジストを剥してはんだづけするのが楽

↑ 今回は 5V のピンを1つ配線をカットして使うことにした。22Ωを介して外に出す。UEW 線でやってる。ちゃんとテスターで導通チェックしましょう

配線


MCU 側は SYS_JTDO_SWO の機能があるピンを設定する。STM32F401 の場合は PB3。Alternate function を設定しなければデフォルトで SWO のはず?モノによるかも

コード側

ここでは printf() を中継する前提で _write() を適当な位置に定義する

int _write(int file, char *ptr, int len) {
	for(int i = 0; i < len; i++) {
		ITM_SendChar(*ptr++);
	}
	return len;
}

あとは printf() を使うところで以下のように stdio.h を include すれば良い

#include <stdio.h> 

VSCode + Cortex-Debug の設定

Cortex-Debug は ITM のデコードをサポートしていて以下のような設定をできる。

launch.json

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "OpenOCD-Debug",
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "openocd",
            "executable": "build/stm32f401-usbserial-host.elf",
            "configFiles": [
                "interface/stlink.cfg",
                "target/stm32f4x.cfg"
            ],
            "postLaunchCommands": [
                "monitor reset init",
                "monitor itm port 0 on"
            ],
            "cwd": "${workspaceRoot}",
            "svdFile": "STM32F4x1.svd",
            "device": "stm32f4x",
            "preLaunchTask": "build",
            "swoConfig": {
                "cpuFrequency": 64000000,
                "source": "probe",
                "swoFrequency": 1000000,
                "enabled": true,
                "decoders": [
                    {
                        "port": 0,
                        "label": "ITM",
                        "type": "console"
                    }
                ]
            }
        }
    ]
}

これで、VSCode の「出力」タブのドロップダウン「SWO: ITM [port: 0, type: console]」に ITM 経由のログが出るようになる。type: console の場合 "\n" 区切りであることを前提にタイムスタンプを追加してくれる。

postLaunchCommandsmonitor reset init はないほうがいいかも。cpuFrequency は HCLK にあわせるのが良い。

ref

  1. トップ
  2. tech
  3. Cortex-M の SWD/ITM を使った UART を使わない printf() デバッグ

現行の最新は V2 Plus (V2.3) または V2 Plus4 だけど、 これらが出る直前に V2.2 を買ってしまったのでちょっと残念な感じ。とはいえ V2.2 と V2.3 はハードウェア的には非常に差分が少ないようなので、改造してみた。Upgrade your NanoVNA v2 to v2 Plus を参考にしてやれば良い。

変更を加える箇所

  • R311 10 -> 68
  • C318 220nF -> 10nF
  • C321 10μ -> 100nF
  • L302 22μH (変更なし参考)




ファームウェアのバックアップ

bootloader も含めてファームウェアのすべてをバックアップしておく。

接続

よく売ってるやすい ST-Link V2 というやつを使う 参考

メインボード上にスルーホールがありシルクも印刷されているので普通に繋ぐだけ。

スルホール用テストワイヤを使うと楽

backup and restore 手順

まず一応書きこまれている内容をバックアップしておく

openocd を使う場合

読みこみ (ダウンロード)

openocd -f interface/stlink.cfg -f target/stm32f3x.cfg -c init -c "reset init"  -c "flash read_bank 0 backup.bin 0x0000000 0x40000" -c exit

書きこみ (アップロード)

openocd -f interface/stlink.cfg -f target/stm32f3x.cfg  -c "program backup.bin 0x08000000 verify reset exit"
st-flash を使う場合 (書けない)
$ st-flash read backup.bin 0x08000000 0x8040000
st-flash 1.4.0
2020-10-27T01:00:56 INFO common.c: Loading device parameters....
2020-10-27T01:00:56 INFO common.c: Device connected is: F1 High-density device, id 0x17010414
2020-10-27T01:00:56 INFO common.c: SRAM size: 0x10000 bytes (64 KiB), Flash: 0x40000 bytes (256 KiB) in pages of 2048 bytes

ただ、これを st-flash で書こうとすると失敗してしまう……

$ st-flash write backup.bin 0x08000000
st-flash 1.4.0
2020-10-27T01:02:39 INFO common.c: Loading device parameters....
2020-10-27T01:02:39 INFO common.c: Device connected is: F1 High-density device, id 0x17010414
2020-10-27T01:02:39 INFO common.c: SRAM size: 0x10000 bytes (64 KiB), Flash: 0x40000 bytes (256 KiB) in pages of 2048 bytes
2020-10-27T01:02:39 INFO common.c: Ignoring 864 bytes of 0xff at end of file
2020-10-27T01:02:39 INFO common.c: Attempting to write 261280 (0x3fca0) bytes to stm32 address: 134217728 (0x8000000)
Flash page at addr: 0x0803f800 erased
2020-10-27T01:02:41 INFO common.c: Finished erasing 128 pages of 2048 (0x800) bytes
2020-10-27T01:02:41 INFO common.c: Starting Flash write for VL/F0/F3/F1_XL core id
2020-10-27T01:02:41 ERROR flash_loader.c: unknown coreid, not sure what flash loader to use, aborting! coreid: 2ba01477, chipid: 414
2020-10-27T01:02:41 WARN flash_loader.c: Failed to write flash loader to sram!
2020-10-27T01:02:41 ERROR common.c: stlink_flash_loader_init() == -1
stlink_fwrite_flash() == -1
exit 255

bootloader

bootloader は Serial Number によって何かが異なるらしく、実際間違った bootloader を書きこむと BBGAIN の値がおかしくなったり、起動しなくなったりする。とにかく現状をバックアップしておけば元には戻せるので、バックアップは大事。

レポジトリにある bootloader は V2_2 用で、最新版は事情があって今のところ公開されていないらしい。現時点では Discord で開発者にコンタクトをとってビルドしてもらう必要がある。が、そのうち SN を指定してビルド・ダウンロードできるページを作るらしいので急ぎじゃないのなら待ってればいいかも。

どういう変更点なのか

該当箇所の回路図

  • 前段の AD8342 はミキサ
  • GS8722C は2回路入りオペアンプ (11MHz)
  • MXD8641 は SP4T スイッチ。オペアンプの増幅率を決めている

なのでここの一連の回路はミキサ出力を ADC 前にフィルタ・増幅する回路になる。


R311, C318, L302 はローパスフィルタで、カットオフは変更前は約72kHz、変更後は34kHz。noise improvement はここのカットオフ変更によるものだと思う。

C321 は交流増幅のための直流カットキャパシタだと思うが、小さくすることで反応性を上げてスキャン速度を上げられるようにしたのだと思う。違うかも

メモ: ADCのサンプルレート

S-A-A-2 の場合 ADC は MCU のものを直接使っている。

  • V2.2 では 300kHz board_v2_1/board.cpp (v2_1 と v2_2 は同じ)
    • ADC のクロックは 6MHz
  • V2.3 では 1500kHz board_v2_plus/board.cpp
    • ADC のクロックは 30MHz
  1. トップ
  2. tech
  3. S-A-A-2 (NanoVNA V2) V2.2 から V2.3 への変更