ebay で注文した OCXO が届いたのでテストしてみました。このぐらいの小さな OCXO です。

MORION MV102 10MHz +12V OCXO

出力は HCMOS 矩形波 (10kΩ 39pF 負荷) となっています。正弦波出力が欲しかったのですが間違えて買ってしまいました。バッファしてフィルタすればいい気はします。とりあえずデジタル回路に直結でき、軽くテストするには便利なのでこれはこれで良いことにします。

GND          Us

RF   Uref   Uin

というピンアサインになっています。Us は +12V入力、RF は出力、Uref はリファレンス電圧(5V)、Uin はVCOの入力のようです。

Uin には 0〜5V で入力し、これにより±4e-7 (±4Hz) 調整できるというスペックです。

とりあえず 12V 加えるだけで 10MHz の出力が得られます。約 200mA ぐらい (2.5W程度) の消費電力があります。2時間ぐらい通電してみましたが特に下がりませんでした。クソ寒い部屋でやっているせいかもしれませんが…

OCXO なので結構熱くなります。しばらく触っていられるぐらいなので45〜55℃ぐらいでしょうか…

GPS の 1PPS で周波数カウントしてみる

GPSモジュールはこの前試した NEO-6Mです。3pin から線を別途引き出して使っています。窓際でやっているので、あまり安定した受信とはいえませんが、3D Fix したタイミングで計ってみました。

MPU として LPC1114FN28 を使って、以下のようなコードを書きました。ハードウェアカウンタは mbed の API からは使えるようになっていないので、データシートを読む必要があります。

本当は、ハードウェアでカウントしつつ、1PPS シグナルのキャプチャ信号でカウンタをキャプチャレジスタに自動的に入れて、同時に割込みを発生させてその値を読み出すみたいにしたかったのですが、少なくとも LPC1114 においてはそういうことはできないようでした (2つ以上のキャプチャピンを持っていない・外部入力カウンタとして使う場合、キャプチャレジスタを使用することはできない)

なので、実際のコードでは 32bit のカウンタを外部入力(10MHz)でフリーランカウントさせ、GPS の1PPSのタイミングでカウンタの値を読みこんで、前回との差分をとって周波数を求めています。

1PPS ごとの表示なので、簡単なコードですが、GPS同期の正確なゲート時間1秒周波数カウンタとして動いています。

#include "mbed.h"
#include <stdint.h>

constexpr uint32_t CLOCK = 10000000;
constexpr uint16_t HISTORY = 1000;

int16_t errors[HISTORY];
uint16_t error_index = 0;
uint16_t error_count = 0;

volatile bool updated = 0;
Serial serial(USBTX, USBRX);

InterruptIn gps1pps(dp25);

int main() {
	// enable 32bit counter
	LPC_SYSCON->SYSAHBCLKCTRL |= (1<<9/*CT32B0 32bit counter clock*/);

	// Capture pin dp14
	LPC_IOCON->PIO1_5         |= (0b010<<0/*FUNC=CT32B0_CAP0*/);

	// Match output (not used)
	//	LPC_IOCON->PIO1_6         |= (0b010<<0/*FUNC=CT32B0_MAT0*/);
	//	LPC_IOCON->PIO1_7         |= (0b010<<0/*FUNC=CT32B0_MAT1*/);
	//	LPC_IOCON->PIO0_1         |= (0b010<<0/*FUNC=CT32B0_MAT2*/);
	//	LPC_IOCON->R_PIO0_11      |= (0b011<<0/*FUNC=CT32B0_MAT3*/);

	LPC_TMR32B0->PR  = 0;
	LPC_TMR32B0->CCR = 0;
	LPC_TMR32B0->CTCR =
		(0b01<<0/*Counter/Timer Mode=Counter, Rising Edge*/) |
		(0b00<<2/*Count Input Select*/);
	LPC_TMR32B0->TCR = (1<<0/*Counter Enable*/);

	gps1pps.rise([]{
		static uint32_t prev = 0;

		uint32_t count = LPC_TMR32B0->TC;
		uint32_t pps_counter;
		if (prev < count) {
			pps_counter = count - prev;
		} else {
			// overflowed
			pps_counter = (0xffffff - prev) + count + 1;
		}
		prev = count;
		serial.printf("pps_counter: %lu\n", pps_counter);

		int16_t error = static_cast<int32_t>(CLOCK) - static_cast<int32_t>(pps_counter);
		error_index = (error_index + 1) % HISTORY;
		errors[error_index] = error;
		if (error_count < HISTORY) {
			error_count++;
		}
		updated = 1;
	});

	// NVIC_SetPriority();

	serial.baud(115200);
	for (;;) {
		if (updated) {
			updated = 0;
//			serial.printf("last: %d\n", errors[error_index]);
//
//			uint32_t sum = 0;
//			for (int i = 0; i < error_count; i++) {
//				sum += errors[ (error_index + HISTORY - i) % HISTORY ];
//			}
//			uint32_t error = sum * 1000 / error_count;
//			serial.printf("sum(%ds): %d\n", error_count, error);
		}
	}
}

結果

VFC 端子を 0V〜5V まで可変させて周波数を読んでみました。仕様上は の調整範囲 (±4Hz) があると書いてありました。実際やってみると以下のように ±5Hz ぐらいの可変ができるようです。

0〜5V の範囲で10Hz可変なので、2 Hz/V、2mHz/mV です。DAC で 1mHz 単位ぐらいで電圧を設定するなら、最低でも14bit程度の精度が必要そうです。

  1. トップ
  2. tech
  3. OCXO の VFC ピンでの可変範囲を GPS の 1PPS を使って調べた

答: デフォルトではしない

ARMのNVIC (Nested Vector Interrupt Controller) は名前の通りネスト可能ですが、実行中の処理よりも優先順位が高いものだけがネストします(ネストとは割り込み処理中にさらに割り込みが発生すること)。

mbed の場合、優先度を設定するAPIはないため、全割り込みは同一優先順位となり、他の割り込みがネストすることはありません (ライブラリ側でやってる場合を除く)。

優先度をつけるには

もし優先順位をつけてネスト可能にする場合、CMSIS(ARM規定のAPI群)のAPI (NVIC_SetPriority()) を使えば可能です。ただ、このAPIを使うためには、mbedのどのAPIでどんな割り込みを設定しているかを把握している必要があります (mbed 側のコードを全部読む必要がある)。

Cortex-M0 の NVIC の割り込みの優先の仕組み

優先順位は0から3までの4段階あり、0が最も優先です。

デフォルトでは全ての割り込みが0に設定されているので、相互に割り込みがネストすることはなく、通常処理が動いている間だけ割り込みが発生します。

また、通常処理についても、__disable_irq() を呼ぶと一時的に優先度0のコードになり、割り込みが発生しなくなります。__enable_irq() で元の優先度に戻ります。

外部ピン変化割り込みで情報が失われるケース

割り込み発生から、次の割り込みまでの間に2回ピン変化が起こると、2つめの変化による割り込み情報は失われます。

ある割り込みを優先した場合、その割り込みと同じ優先度の割り込み(その割り込み自身を含む)の処理時間だけ考えればいいことになります。

  1. トップ
  2. tech
  3. mbed InterruptIn はネストするか