以下をいれるととりあえずおさまる。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が削除される

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 を使ってみる

$ 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 すると起動しない

  • 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 で意図した出力がでないときのチェックリスト

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 で内部温度センサを読み出す

  • 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 のシンボルが見つからないと言われ続ける場合

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

データシートにまさにこれという値は載っていないので計算する必要がある。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 の出力インピーダンス

秋月で売っている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)が定期的に受信できなくなる

stdio.h や math.h などが見つからないと言われる場合

open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg 

で SDK header をインストールしなければならない。

  1. トップ
  2. tech
  3. Mojave で clang が stdio.h などを見つけられない場合