データシートにまさにこれという値は載っていないので計算する必要がある。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Ω ぐらいか。
OCXO と GPS 1PPS その3 | tech - 氾濫原 というのを3年前に書いてからやる気をうしなって放置してましたが、STM32F103 bluepill と組合せて、10MHz GPSDO をつくってます。今度は完成させたい。前よりは進捗していて、現状では 1mHz 未満ぐらい (0.1ppb) に誤差を抑えることができています。
このようにMOSFETのレベルシフタと分圧抵抗でオフセットさせる。
回路シミュレータ使って試行錯誤するのが一番簡単だが、一応自分で計算して、直接抵抗比を求められるようにしておく。
入力電圧 を図のような回路に入力したときの を求める。
テブナンの定理を使って計算していく。 と で分圧されている部分を内部抵抗のある電圧源に置き換える。このため、 を外した状態での の値 と、その内部抵抗 を求める。 は と で分圧された値なので、。内部抵抗を求める場合、既存の電圧源はショートして考えれば良いので、 と の並列合成抵抗 。
ここで は と によって分圧された出力なので、。
以下の通り
入出力関数 を以下のようにする
入力電圧の最小・最大がそれぞれ のとき、出力電圧範囲 と中心電圧 はそれぞれ以下のようになる。
連立方程式を適当に計算すると、 を決めたときのほかの抵抗の値は以下のようになる。
入力電圧の最小値 が0の場合は
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});
} clean しても rebuild しても解決しない場合 inner class を利用していてかつ、inner class を import している可能性が考えられる。
この場合、inner class の import 文を削除し、完全修飾するようにすれば通るようになる。
エラーコードからは lombok が原因かはわからないし、それが innner class 絡みだということもわからない。lombok や inner class が原因か?ということに気付いたところでこの問題の9割は解決している。なのでこのエントリは実際には空虚で無意味な、躓いたということを知らせるだけのエントリである。
MCP4922 などの使用例に書いてあるやつを考えてみる。http://akizukidenshi.com/download/MCP4922.pdf この方法で考えられる問題は
低速かつ相対的に細かい値を扱うには使えるかもしれない。絶対値が重要な場合はかなり使うのが難しいと思う。
以下のとき1:1000
B がおおざっぱな調整、A が精密調整用の DAC を担うことになっている。A で B の 1000分の1 を追加で補正する。 理論上は 4096分の1にすれば最大の解像度を得られるが DNL (微分非直線性) があるためそうはいかない。
DNL が±0.75LSBであるため、1.5LSB分は余裕をもって二重に出力できないと、どうやっても出力できない電圧が発生する。
1:1000 のときの伝達関数をグラフ化すると以下のようになる。Y軸が出力電圧で、X軸は24bitの出力コード。実際は DNL があり、それぞれの直線の傾きがランダムに変化する
見ての通りで同じ出力電圧にする場合でも複数の値のとりかたがある。どれをどう選ぶかは制御のアルゴリズム次第で、結構やっかい。
必然的に相対値の調整が重要なはずなので、前回からの差分をうけとって、精密調整しつつ、範囲をはずれたら粗調整を動かすというのをヒステリシスにやる必要があると思う。
内部に温度センサを内蔵しており、追加の部品なしにある程度の温度は知ることができる。あまり正確とはいえないが相対的に温度の動きを見るには十分
ADC1 の 16ch に繋がっている。データシート通りだが以下の手順を踏む
V_{25} と Avg_Slope は個別のデータシートに書いてある。STM32F103 の場合
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;
}
$ vim [1] 37147 killed vim
みたいになってエラーも出ず、一切起動しない。ビルドした src/vim は起動するが、make install 後のバイナリでこの現象が起こる。ファイルサイズも異なるなるのでインストールプロセスを疑ったところ strip を OS 標準のものにしたら正常に起動するようになった。
STM32F103C8 のやっすいボード (bluepill などと呼ばれているらしい) については数年前に書いた STM32F103 C8 T6 の安いボードでLチカ (platformio + mbed) が、これを題材にして ChibiOS の環境を用意する。
適当に git repo を作って ChibiOS を submodule に追加する。もちろんただダウンロードしてきてコピーしても良いがとにかくレポジトリ内に全コードを入れておく
mkdir foo cd foo git init git submodule add git@github.com:ChibiOS/ChibiOS.git
できれば同じ MCU のボードだと変更点がすくなくてよい。Bluepill は Maplemini というののクローンの一種らしいので、そこからコピーしてくる。
cp -r ChibiOS/demos/STM32/RT-STM32F103-MAPLEMINI/* .
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 は通るはず
入力として使う alternate function は、ピンに特別な設定は必要なく、PAL_MODE_INPUT などにして、ペリフェラル側の設定を有効にするだけで良い。
出力として使う alternate function は、ピンにもPAL_MODE_STM32_ALTERNATE_PUSHPULL を指定する必要がある。
(STM32F401 などとは設定方法が違う)
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 採用すればいいので気にしないことにする。
以下をいれるととりあえずおさまる。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'],
\ }) 以下を根拠に測定する
アマチュアの 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波になる。
正直めんどい。同じことを3回やるだけなので、今回は省略して中央のみで行う。
占有周波数帯幅の許容値が40kHz なので 帯域外領域は 2.5倍して ±100kHz。
無変調で測定する。
「30MHzを超え54MHz以下」「1Wを超え50W以下」の条件では「1mW以下であり、かつ、基本周波数の平均電力より60dB低い値」
「50μW以下又は基本周波数の搬送波電力より70dB低い値」-13dBm または搬送波電力-70dB以下ならよい
https://www.hakodate-ct.ac.jp/~moriya/class/5SCE_Exp/text05-1.pdf
http://ja5fp.org/bessel.pdf
最初に知ること
「振幅の最大値のときの最大周波数偏移」と「ある入力に対する最大周波数偏移」で若干ややこしいので注意。単に「最大周波数偏移」といったときどっちのことを言っているか気をつける。
1kHz の正弦波で変調した場合、キャリアを中心にして、1kHz ごとにピークが立つ。各ピークのレベルについては後述
入力信号周波数を変化させるとこのように変化する。ピークの幅と、ピークの振幅それぞれが変化する。
ある周波数の入力信号 を変調して、最大周波数偏移が だった場合、変調指数 は以下のようになる。
変調指数には以下のような特徴がある
出力のキャリア周波数の振幅及び、側波帯の各ピークの振幅は、変調指数をとる第1種ベッセル関数にしたがう。
縦(y)軸が振幅で、横(x)軸が変調指数。J(n,m) は変調指数 m のときの、側波帯ピークn番目(ただし0はキャリア)の振幅をあらわす。負の値が出るがスペアナで観測できるのは絶対値。
変調指数が一定の値、例えば 2.4 とかになるとキャリアの振幅は0になる。
どれも最初のキャリアゼロにあわせているので変調指数は2.4。結果、最大周波数偏位・入力ゲインが変わる。変調指数が同じなので、各ピークの大きさはどれも一致するはずだが、実際は出力バンドパスフィルタとかの影響で帯域が広くなると誤差が増える。
最大周波数偏移は入力信号振幅によるため、同じ振幅を持ち、キャリア周波数振幅が0となるように入力信号周波数を加減する。キャリア周波数振幅が最初に0となったとき m=2.4 なので、入力信号周波数 * m で最大周波数偏移を求めることができる。
ただしこの方法は、入力信号周波数を変化させても、無線機内部で振幅が変化しないことを前提としているので、イコライザやコンプレッサーなどがあり、周波数応答がフラットではない場合は成立しない。
RBWを十分に大きくして、FM波全体を見るようにする。この状態で Max Hold / Min Hold して偏位の最大周波数と最小周波数をさがす。少し測定に時間がかかるのと、スペアナのスイープタイミングと入力信号周波数が互いに素になっていないと最悪いつまでたっても結果がでないのが欠点
マーカーを2つ使い、Max Hold / Min Hold それぞれのトレースで同ゲインにくるように調整する。そのときの周波数の値を読んで、差を計算する。これが最小と最大の周波数偏位になる。(51008700 - 51004100) = 4600 なので、最大周波数偏位はこの半分の ±2300Hz になる。この画像の場合は信号周波数が 1000Hz なので、変調指数は 2.3。
例えば 1000Hz で変調指数 3.5、つまり最大周波数偏位を3500Hzにしたい場合。これは変調指数から振幅を求めて、近くなるようにゲインを調整するのが一番簡単。
ベッセル関数を使って各ピークを求めて、それにあうようにする。
これでやって調整して、上記の方法で、最大周波数偏位を測定しなおすとより良い。
(51009800 - 51003050) / 2 = 3375 (最大周波数偏位)。3375 / 1000 = 3.375 (変調指数)。なかなかぴったりにはいかない…