秋月で売っているGYSFFMANC(MT3333) GNSSモジュールがおかしい。

中央下のグラフを見るのがわかりやすいが、定期的に (衛星が見えているにも関わらず) 一切受信できなくなり、しばらくすると回復するという挙動をする。なので全く安定して受信できない。

  • 少しずつ SV Used が減少しはじめ、3D Fix を維持できなくなる。SV View はほぼ変化しない
  • リセットから3〜4時間経過すると一切受信できなくなる
  • そこからしばらくすると、再び受信できるようになる
  • 上記の挙動を繰替えす
  • 一切受信できなくなったタイミングで手動でリセット・ホットスタートすると即座に 3D Fix される。これよりアンテナの問題ではないと考えられる
  • ほぼ同じ位置に別のアンテナをつけ、別の受信機(ublox NEO M8N) をつかって測位しているが、そちらではこのような現象は起きない。衛星の見える環境の問題ではないと考えられる

その他の情報

  • バージョンは AXN_5.1.2_3333_17113000
  • GPSとGLONASS で測位。SBAS の有効・無効は関係なく再現する。
  • GLONASS は表示にはでるが測位に使われている形跡がないように思う。

考察

ゴミなのでは? とりあえず使用に耐えないので買って損してイライラするが、挙動について考えてみる。

  • 衛星のエフェメリス情報の有効期限が4時間なので、周期と似ていて怪しい
    • 3D Fix した状態だと捕捉している別の衛星の情報を一切処理しないとか?
  • 特定の環境でだけ再現する可能性がある?
    • 受信環境が悪い、かつ既に 3D Fix 状態だと他の衛星を無視するとか?
  • ファームのバグ (メモリリークみたいな?)
    • メモリリークとかでスタックするならリセット後にメッセージが出て、それが記録されると思うが、それは観測できない

よくわからんけど、とりあえずこのモジュールはおすすめできない。中国の怪しい GNSS モジュールのほうがはるかに安定している……

  1. トップ
  2. tech
  3. GYSFFMANC(MT3333)が定期的に受信できなくなる

データシートにまさにこれという値は載っていないので計算する必要がある。VCC などによって出力インピーダンスが変わってくる。

たとえば Toshiba の TC74HCU04 の場合、「出力電圧」は以下のようになっている。

または

で出力インピーダンス(最小)がわかる。H レベルのときは 4.5V のときだと (4.5 - 4.18) / 4e-3 = 80,、6.0V のときだと (6.0 - 5.68) / 5.2e-3 = 61。5V で中間ぐらいとすると 71Ω ぐらいか。

  1. トップ
  2. tech
  3. 74HC04 の出力インピーダンス

OCXO と GPS 1PPS その3 | tech - 氾濫原 というのを3年前に書いてからやる気をうしなって放置してましたが、STM32F103 bluepill と組合せて、10MHz GPSDO をつくってます。今度は完成させたい。前よりは進捗していて、現状では 1mHz 未満ぐらい (0.1ppb) に誤差を抑えることができています。

  • PWM 信号は普通 0-5V などだが、1.5V-3.5V とかに変換したい
  • その際、電源ソースを変更したい。
    • PWMソースとは別の安定した電源から 1.5V-3.5V をとりだしたい

このようにMOSFETのレベルシフタと分圧抵抗でオフセットさせる。

出力を計算する

回路シミュレータ使って試行錯誤するのが一番簡単だが、一応自分で計算して、直接抵抗比を求められるようにしておく。

入力電圧 を図のような回路に入力したときの を求める。


テブナンの定理を使って計算していく。 で分圧されている部分を内部抵抗のある電圧源に置き換える。このため、 を外した状態での の値 と、その内部抵抗 を求める。 で分圧された値なので、 。内部抵抗を求める場合、既存の電圧源はショートして考えれば良いので、 の並列合成抵抗

これで下の図のような等価回路とみなせるようになる。

ここで によって分圧された出力なので、

以下の通り

出力電圧範囲と中心電圧から抵抗値を求める。

入出力関数 を以下のようにする

入力電圧の最小・最大がそれぞれ のとき、出力電圧範囲 と中心電圧 はそれぞれ以下のようになる。

連立方程式を適当に計算すると、 を決めたときのほかの抵抗の値は以下のようになる。

入力電圧の最小値 が0の場合は

JS

JS のコードにするとこういう感じ

const V_center = 2.5;
const V_range = 1;
const V_SS = -5;
const V_DD = 5;
const V_min = 0;
const V_max = 5;
const R_1 = 11e3;
const R_2 = ((2*R_1*V_SS-2*R_1*V_DD)*V_range)/((V_min+V_max-2*V_SS)*V_range+(2*V_center-2*V_SS)*V_min+(2*V_SS-2*V_center)*V_max);
const R_3 = -((2*R_1*V_SS-2*R_1*V_DD)*V_range)/((V_min+V_max-2*V_DD)*V_range+(2*V_center-2*V_DD)*V_min+(2*V_DD-2*V_center)*V_max);
// re-calculate R_23 V_23 for reference
const R_23 = (R_2 * R_3) / (R_2 + R_3);
const V_23 = (V_DD - V_SS) * (R_3 / (R_2 + R_3)) + V_SS;
console.log({R_1, R_2, R_3, R_23, V_23});
for (let V_in of [0, 2.5, 5]) {
	const V_out=(R_2*R_3*V_in+R_1*R_2*V_SS+R_1*R_3*V_DD)/(R_2*R_3+R_1*R_3+R_1*R_2);
	console.log({V_in, V_out});
}
  1. トップ
  2. tech
  3. PWM や DAC の出力範囲を変える

clean しても rebuild しても解決しない場合 inner class を利用していてかつ、inner class を import している可能性が考えられる。

この場合、inner class の import 文を削除し、完全修飾するようにすれば通るようになる。

エラーコードからは lombok が原因かはわからないし、それが innner class 絡みだということもわからない。lombok や inner class が原因か?ということに気付いたところでこの問題の9割は解決している。なのでこのエントリは実際には空虚で無意味な、躓いたということを知らせるだけのエントリである。

  1. トップ
  2. tech
  3. lombok のシンボルが見つからないと言われ続ける場合

MCP4922 などの使用例に書いてあるやつを考えてみる。http://akizukidenshi.com/download/MCP4922.pdf この方法で考えられる問題は

  • 誤差の扱いかたが複雑になる
    • 2つのDACの温度係数の違い
    • DNL/INL の扱い
  • 外部抵抗の正確さ
  • 短期的なグリッチ
    • 同時に値をセットできるわけではないため
    • 粗いほうのDACを調整したときに生じる DNL による歪み
  • 制御の仕方が面倒

低速かつ相対的に細かい値を扱うには使えるかもしれない。絶対値が重要な場合はかなり使うのが難しいと思う。

以下のとき1:1000


B がおおざっぱな調整、A が精密調整用の DAC を担うことになっている。A で B の 1000分の1 を追加で補正する。 理論上は 4096分の1にすれば最大の解像度を得られるが DNL (微分非直線性) があるためそうはいかない。

DNL が±0.75LSBであるため、1.5LSB分は余裕をもって二重に出力できないと、どうやっても出力できない電圧が発生する。

1:1000 のときの伝達関数をグラフ化すると以下のようになる。Y軸が出力電圧で、X軸は24bitの出力コード。実際は DNL があり、それぞれの直線の傾きがランダムに変化する

見ての通りで同じ出力電圧にする場合でも複数の値のとりかたがある。どれをどう選ぶかは制御のアルゴリズム次第で、結構やっかい。

必然的に相対値の調整が重要なはずなので、前回からの差分をうけとって、精密調整しつつ、範囲をはずれたら粗調整を動かすというのをヒステリシスにやる必要があると思う。

  1. トップ
  2. tech
  3. 12bit DAC 2つで 24bit DAC?

内部に温度センサを内蔵しており、追加の部品なしにある程度の温度は知ることができる。あまり正確とはいえないが相対的に温度の動きを見るには十分

ADC1 の 16ch に繋がっている。データシート通りだが以下の手順を踏む

  • ADC1_IN16 を選択する
  • 17.1μs のサンプル時間にする
  • ADC_CR2 の TSVREFE ビットをセットする
    • 温度センサおよびV_{REFINT}がパワーオンする
  • ADON で変換する
  • V_{SENSE} を読みだす
  • 温度(℃) = (V_{25} - V_{SENSE}) / Avg_Slope + 25 を計算する

V_{25} と Avg_Slope は個別のデータシートに書いてある。STM32F103 の場合

  • V_{25} : 1.34〜1.52 (typ=1.43) V
  • Avg_Slope: 4.0〜4.6 (typ=4.3) mV/℃

17.1μs は、ADCクロックが12MHz なら 17.1e-6 / (1/12e6) = 205.2サイクル以上 、6MHz なら 102.6サイクル以上。どっちにしろサンプル時間レジスタは 0b111 (239.5サイクル) しかない。

static void setupADC(void) {
	rccEnableADC1(true);
	ADC1->CR1 = 0;
	ADC1->CR2 = ADC_CR2_ADON;

	ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_RSTCAL;
	while ((ADC1->CR2 & ADC_CR2_RSTCAL) != 0) ;

	ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_CAL;
	while ((ADC1->CR2 & ADC_CR2_CAL) != 0) ;

	ADC1->SMPR1 |= (
		(0b111<<ADC_SMPR1_SMP17_Pos) |
		(0b111<<ADC_SMPR1_SMP16_Pos)
	);
}
static uint16_t startADC(uint8_t channel) {
	ADC1->CR2 |= ADC_CR2_TSVREFE;

	const uint8_t count = 1;
	ADC1->SQR1 = (count-1) << ADC_SQR1_L_Pos;
	ADC1->SQR2 = 0;
	ADC1->SQR3 = (channel & 0b11111) << ADC_SQR3_SQ1_Pos;

	ADC1->CR2 |= ADC_CR2_SWSTART;
	// write same bit to start conversion
	ADC1->CR2 |= ADC_CR2_ADON;

	while ((ADC1->SR & ADC_SR_EOC) == 0) ;

	return ADC1->DR & 0xffff;
}
static float getTemp(void) {
	float adc = 0;
	const uint8_t len = 10;
	for (int i = 0; i < len; i++) {
		adc += (float)startADC(16);
	}
	adc /= len;

	const float V_sense = adc / (float)(1<<12) * 3.3;
	const float V_25 = 1.43;
	const float Avg_Slope = 4.3e-3;
	const float temp = (V_25 - V_sense) / Avg_Slope + 25;
	return temp;
}
  1. トップ
  2. tech
  3. STM32F103 で内部温度センサを読み出す

  • Alternate function の再配置(remap)の確認 AFIO_MAPR
    • STM32CubeMX でペリフェラルのピン設定を確認してたりすると、知らぬまに再配置ピンを使うつもりでいることがある
  • GPIO の設定 (GPIOx_CRL GPIOx_CRH)
    • Alternate Function を出力にする場合、ピンの設定が Alternate push-pull などになっているかを確認する
  • 他の Alternate function が有効になっていないか
    • 特にデバッグ関係のピンはリセット直後にデフォルトで有効 (すなわち GPIO が無効) なので、かぶっている場合は自分でオフにする必要がある
      • SWG の場合 JTMS/SWDIO と JTCK/SWCLK しか使わないので、SWJ_CFG ビットを適切に設定して JTAG 用のピンを GPIO 用に使えるように再配置するのを習慣にしておくとハマりにくい
  • ペリフェラルの設定
    • 正しくプリスケーラーが設定されているかなど
  1. トップ
  2. tech
  3. STM32F103 で意図した出力がでないときのチェックリスト

$ vim
[1]    37147 killed     vim

みたいになってエラーも出ず、一切起動しない。ビルドした src/vim は起動するが、make install 後のバイナリでこの現象が起こる。ファイルサイズも異なるなるのでインストールプロセスを疑ったところ strip を OS 標準のものにしたら正常に起動するようになった。

  1. トップ
  2. tech
  3. GNU strip (GNU Binutils) 2.33.1 が入った環境で vim を make install すると起動しない

STM32F103C8 のやっすいボード (bluepill などと呼ばれているらしい) については数年前に書いた STM32F103 C8 T6 の安いボードでLチカ (platformio + mbed) が、これを題材にして ChibiOS の環境を用意する。

レポジトリとChibiOSの依存の追加

適当に git repo を作って ChibiOS を submodule に追加する。もちろんただダウンロードしてきてコピーしても良いがとにかくレポジトリ内に全コードを入れておく

mkdir foo
cd foo
git init
git submodule add git@github.com:ChibiOS/ChibiOS.git

demo から一番近い設定を持ってくる

できれば同じ MCU のボードだと変更点がすくなくてよい。Bluepill は Maplemini というののクローンの一種らしいので、そこからコピーしてくる。

cp -r ChibiOS/demos/STM32/RT-STM32F103-MAPLEMINI/* .

Makefileを書きかえる

CHIBIOS  := ./ChibiOS 

ボード定義を自分で作りなおす

手元にあるボードと、既にあるボード定義はだいたい一致していないので、修正する必要がある。

Makefile に以下のような行がある。

include $(CHIBIOS)/os/hal/boards/MAPLEMINI_STM32_F103/board.mk  

このディレクトリをまるっと自分のディレクトリにコピーする

cp -r ChibiOS/os/hal/boards/MAPLEMINI_STM32_F103 STM32F103_BLUEPILL

board.mk を編集する。まずはパスを変更したディレクトリにあわせるだけ。

# List of all the board related files.
BOARDSRC = STM32F103_BLUEPILL/board.c

# Required include directories
BOARDINC = STM32F103_BLUEPILL

# Shared variables
ALLCSRC += $(BOARDSRC)
ALLINC  += $(BOARDINC)

board.h を確認して、必要であれば編集する。

まずクロック設定があっているか確認する。実際の基板のものとあってないと何もかもがおかしくなる。後述する mcuconf.h と組合せてよくチェックする。

#define STM32_LSECLK            32768
#define STM32_HSECLK            8000000

VAL_GPIOACRL など、GPIO の初期設定を必要に応じて変更する。とはいえアプリケーションコード (main.c) で、palSetPadMode などで、あとから変えることもできるので必須ではない。

ここまででベースはおわり。make は通るはず

ほかのチェックポイント

  • cfg/mcuconf.h で各クロックのプリスケーラーなどが正しいか確認する
  • cfg/mcuconf.h で STM32_ST_USE_TIMER で指定したタイマーは OS によって予約されるので適切にする
  • cfg/mcuconf.h で利用する HAL ドライバーを有効にする。
  • cfg/halconf.h で必要なサブシステム設定を有効にする。

備考: STM32F103 での Alternate function の設定方法は?

入力として使う alternate function は、ピンに特別な設定は必要なく、PAL_MODE_INPUT などにして、ペリフェラル側の設定を有効にするだけで良い。

出力として使う alternate function は、ピンにもPAL_MODE_STM32_ALTERNATE_PUSHPULL を指定する必要がある。

(STM32F401 などとは設定方法が違う)

備考: 「これをChibiOSでやる方法がわからん」

ChibiOS の HAL ドライバのインターフェイスは MCU ごとに柔軟に設定できるようになっている。裏をかえすと、基本的に MCU ごとに設定方法を変えなければならない。

一番簡単なのは定義を読むことで、ChibiOS/os のディレクトリで git grep したりして、必要なソースコードを読むのがてっとりばやい。ChibiOS は幸い、あまり変なコードはなく素直に読みくだせるようになっている。

特殊なことをしないなら HAL ドライバを使ったほうが圧倒的に楽だが、もちろん利用せずに自力でレジスタを設定しても良い。データシートを読みながらペリフェラルを設定する場合は結局こうすることになる。その場合 mcuconf.h で利用するペリフェラルのドライバは無効にすること。

感想

できたものは https://github.com/cho45/STM32F103C8-ChibiOS

STM32 の開発環境で悩んでいて、ちょうどよく気にいるものがなかなくていろいろ試している。STM32 デバイスでの例が多く抽象化やビルドシステムもちょうどよく手に収まる感じで、今のところいい感じ。

ChibiOS は RTOS のライセンスがGPLであるのが若干やっかいだが、OSS ならまぁ GPL 採用すればいいので気にしないことにする。

  1. トップ
  2. tech
  3. STM32F103C8 で ChibiOS を使ってみる

以下をいれるととりあえずおさまる。ccls と相性が悪い??

let g:lsp_insert_text_enabled = 0
let g:lsp_text_edit_enabled = 0  

あとラベルが勝手に補完に含まれるのはinitialization_optionsになんか指定するとよいらしい?

	   au User lsp_setup call lsp#register_server({
		  \ 'name': 'ccls',
		  \ 'cmd': {server_info->['ccls']},
		  \ 'root_uri': {server_info->lsp#utils#path_to_uri(lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), 'compile_commands.json'))},
		  \ 'initialization_options': #{
		  \    cache: #{directory : '/tmp/ccls_cache'},
		  \    completion: #{detailedLabel: v:false}
		  \ },
		  \ 'whitelist': ['c', 'cpp', 'objc', 'objcpp', 'cc'],
		  \ })
https://github.com/prabirshrestha/vim-lsp/issues/328#issuecomment-555304328
  1. トップ
  2. tech
  3. ccls + vim-lsp で補完時に後続のwhitespaceが削除される

測定方法について

以下を根拠に測定する

測定前の知識

アマチュアの F3E の占有周波数帯幅の許容値は 40kHz (ただし 430-440MHz では 30kHz)。ただし事実上の帯域幅は 16kHz 程度になっていることが多い。

周波数変調は振幅が周波数偏移になるため、最大振幅でどれぐらい周波数偏移をするかを決めなければならない。アマチュアで通常使われてる狭帯域FMで、最大振幅時の最大周波数偏移は ±5kHz。

測定準備

標準変調度とは、通常、規定の最大周波数偏移許容値を100%としたものであり、最大周波数偏移の許容値が規定されていない場合は工事設計書の設計値(工事設計書に記載される値)の最大周波数偏移を100%としたものである。基準周波数偏移とは、試験機器の最大周波数偏移が規則で規定されていない場合、測定のための基準点の周波数偏移である。

で F3E では、「標準変調度又は基準周波数偏移(位)」は「正弦波1kHzで最大周波数偏移の70%」になる。測定時には「基準周波数偏移の入力から10dB増加(擬似音声)」

アマチュアのF3Eの場合「標準変調度とは (略) 工事設計書の設計値(工事設計書に記載される値)の最大周波数偏移を100%としたものである」が適用される。工事設計書の設計値の最大周波数偏移は通常±5kHzなので、つまり 1kHz 入力したときに、5 * 0.7 = 3.5kHz 周波数偏移するように入力ゲインを変更し、疑似音声はこのときのレベルに +10dB することになる。

前に書いた「特定の変調指数になるようにゲインを調整したい場合」にそって 1kHz で変調指数 3.5 になるようにして、疑似音声をそれの +10dB で出力できるように信号源のゲインを調整する。

今回は温度制限がかかるので、出力は 5W (37dBm) でやる。(本来は最大でやらなければならない)

1kHz で最大周波数偏位 3500Hz にあわせる。


(52013416 - 52006833) / 2 = 3291Hz

測定周波数

FM なので 50MHz 帯で測定することにする。別表第三十五 証明規則第2条第1項第12号に掲げる無線設備の試験方法によれば、50MHzは50〜54MHzと、2MHz以上の帯域があり、この場合の試験周波数は3波になる。

  • 下限周波数に 60kHz 加えた周波数
    • 50060000Hz
  • 中央の周波数
    • 52000000Hz
  • 上限周波数から60kHz減じた周波数
    • 53940000Hz

正直めんどい。同じことを3回やるだけなので、今回は省略して中央のみで行う。

帯域外領域

占有周波数帯幅の許容値が40kHz なので 帯域外領域は 2.5倍して ±100kHz。
無変調で測定する。

「30MHzを超え54MHz以下」「1Wを超え50W以下」の条件では「1mW以下であり、かつ、基本周波数の平均電力より60dB低い値」

スプリアス領域

「50μW以下又は基本周波数の搬送波電力より70dB低い値」-13dBm または搬送波電力-70dB以下ならよい

RBW=1kHz 9kHz〜150kHz

RBW=10kHz 150kHz〜30MHz

RBW=100kHz 30MHz〜520MHz

ref

https://www.hakodate-ct.ac.jp/~moriya/class/5SCE_Exp/text05-1.pdf
http://ja5fp.org/bessel.pdf

  1. トップ
  2. tech
  3. スペアナでスプリアス測定してみる3 KX3 FMの測定