NanoPi NEOとGPSモジュールでNTPサーバ 簡易PPS編

NanoPi NEOピン配置
©FriendlyARM.

前回はGPSのMNEAだけでNTPサーバという内容だった。
今回はなるべく簡単にPPS信号を使って精度を高めようという記事。

先ず、GPSモジュールからのTime Pulse(1PPS)信号をNanoPiに受け取らせたい。
前回と同じNanoPi NEOのピン配置図を見るとGPIOという割にすべてのピンに何らかの役割がアサインされている。
取り敢えず今回はGPIO18番ピンを使いたいと思う。18番ピンは上の図の右端の下から4番めのPG9と書かれたところ。これは本来はUART1のフロー制御用のピン。そのままではPPS入力用としては使えないのでGPIOのアサイン制御を行う。
その際、Linux側からは物理的なピン番号は使わずLinux gpio番号を使う。
各GPIOピン番号に対応するLinux gpio番号は上のピン配置図の画像をクリックして表示されるWikiページに書いてある。linux gpio番号の求め方(計算方法)というものもあるようだが憶える意味がないのでWikiのページを見るだけで。

GPSモジュールと接続
GPSモジュールのPPS出力からNanoPi NEOの18番ピンを接続した。黄色の線。

# echo "201" > /sys/class/gpio/export
# echo "in" > /sys/class/gpio/gpio201/direction
# cat /sys/class/gpio/gpio201/direction  ←確認
in  ←inが表示されたらOK

/sys/class/gpio/export に対して制御したいLinux gpio番号の"201"を送信する。
Linux gpio201番を受信用(in)にする。今回は受信用だけど、信号を出力する用途ならinの代わりにoutを指定する。

書くまでもないことだが、デジタルなので信号の値は1 (Hi)、0 (Low)の2種類だけ。
現在のLinux gpio201番への入力の値を確認する。

$ cat /sys/class/gpio/gpio201/value
0      ←現在の値は0 (Low)

PPS信号を入力していると基本は0の筈だが連続して繰り返すと値が1になる瞬間に当たる筈。

今回は入力専用でしか使わないが、出力用にセットした場合は値をセット(信号を出力)できる。

$ echo 1 > /sys/class/gpio/gpio201/value #Hiを出力
$ echo 0 > /sys/class/gpio/gpio201/value #Lowを出力

PPS信号入力(出力)用としての利用をやめる時は下のように開放する。

# echo "201" > /sys/class/gpio/unexport

rpi_gpio_ntpdのインストール

rpi_gpio_ntpdはGPIOからの入力をNTPに渡してくれるツール。1秒ごとにピコンピコンと来るのをセンテンスにしてNTPに渡すのかな。rpi_gpio_ntpdは名前にrpiと付いているので元々はラズパイ用のツール。NanoPiシリーズでも使用できるとは書いてないけどOrangePiで使えるんだからNanoPiでもイケる。(GPIOを使える状態にしてから実行しないとダメみたいだけど)

rpi_gpio_ntpdのビルドには特に追加しなければならないパッケージは無いので以下3行の実行で終わる。(所要時間1分以内)

# git clone https://github.com/flok99/rpi_gpio_ntpd.git
# cd rpi_gpio_ntpd
# make install

rpi_gpio_ntpの使用方法

# /usr/local/bin/rpi_gpio_ntp -h
rpi_gpio_ntp v1.5, (C) 2013-2015 by folkert@vanheusden.com

-N x    x must be 0...3, it is the NTP shared memory unit number
-g x    gpio pin to listen on
-G x    explicit path to the gpio-pin-path, for special cases like the cubieboard1 (/sys.../gpio1_pg9 instead of /sys.../gpio1). Note: you need to "export" and configure the pin in this use-case by hand.
-d      debug mode
-F x    fudge factor (in microseconds)
-p x    when enabled, toggle GPIO pin x so that you can measure delays using a scope
-f      do not fork
-b      handle both on rising/falling but ignore falling
-P      use polling - for when the device does not support interrupts on gpio state changes
-i x    polling: how long shall we sleep (part of a second) and not poll for interrupts. e.g. 0.95

rpi_gpio_ntpの動作確認

先にgpioを念の為開放して入力モードにする。

# echo "201" > /sys/class/gpio/unexport         # ←開放
# echo "201" > /sys/class/gpio/export           # ←使用開始
# echo "in" > /sys/class/gpio/gpio201/direction # ←入力モードセット
# /usr/local/bin/rpi_gpio_ntp -g 201 -d
rpi_gpio_ntp v1.5, (C) 2013-2015 by folkert@vanheusden.com

NTP unit : 0
GPIO pin : 201
GPIO pout: -1
Fudge    : 0.000000000
"Fork into the background" disabled because of debug mode.
1489463315.000045988] interrupt #1, 0 wraps, offset 0.000046s nan/0.000046/nan
1489463316.000037482] interrupt #2, 0 wraps, offset 0.000037s 0.000037/0.000042/nan
1489463317.000006725] interrupt #3, 0 wraps, offset 0.000007s 0.000022/0.000030/nan
1489463318.000021552] interrupt #4, 0 wraps, offset 0.000022s 0.000022/0.000028/nan
1489463319.000007224] interrupt #5, 0 wraps, offset 0.000007s 0.000018/0.000024/nan
1489463320.000021080] interrupt #6, 0 wraps, offset 0.000021s 0.000019/0.000023/nan
1489463321.000023681] interrupt #7, 0 wraps, offset 0.000024s 0.000019/0.000023/0.000024
1489463322.000010403] interrupt #8, 0 wraps, offset 0.000010s 0.000017/0.000022/0.000024
1489463323.000028576] interrupt #9, 0 wraps, offset 0.000029s 0.000017/0.000023/0.000026
1489463324.000001205] interrupt #10, 0 wraps, offset 0.000001s 0.000015/0.000020/0.000026
1489463325.000010702] interrupt #11, 0 wraps, offset 0.000011s 0.000015/0.000020/0.000026
1489463326.000010529] interrupt #12, 0 wraps, offset 0.000011s 0.000014/0.000019/0.000026
1489463327.000020516] interrupt #13, 0 wraps, offset 0.000021s 0.000014/0.000019/0.000024
1489463328.000007457] interrupt #14, 0 wraps, offset 0.000007s 0.000013/0.000018/0.000024
1489463329.000026186] interrupt #15, 0 wraps, offset 0.000026s 0.000013/0.000019/0.000025
1489463330.000019952] interrupt #16, 0 wraps, offset 0.000020s 0.000013/0.000019/0.000024
1489463331.000023795] interrupt #17, 0 wraps, offset 0.000024s 0.000013/0.000019/0.000024
1489463332.000034301] interrupt #18, 0 wraps, offset 0.000034s 0.000013/0.000020/0.000025
1489463333.000014637] interrupt #19, 0 wraps, offset 0.000015s 0.000014/0.000020/0.000025
1489463334.000019262] interrupt #20, 0 wraps, offset 0.000019s 0.000014/0.000020/0.000025
1489463335.000019454] interrupt #21, 0 wraps, offset 0.000019s 0.000014/0.000020/0.000025
1489463336.000029841] interrupt #22, 0 wraps, offset 0.000030s 0.000014/0.000020/0.000026
1489463337.000002309] interrupt #23, 0 wraps, offset 0.000002s 0.000014/0.000019/0.000026
1489463338.000030650] interrupt #24, 0 wraps, offset 0.000031s 0.000014/0.000020/0.000026

停止させるのはCtrl+C
上にも書いたが、物理的なピン番号は18だが制御するのはLinux gpio番号の201。
使用した後はgpio201を開放する。

# echo "201" > /sys/class/gpio/unexport

ntpdのビルド (不要)

カーネルPPSを使用する場合はarmbianのパッケージでインストールしたntpdはPPSに非対応なので自分でビルドすることになるが、今回はrpi_gpio_ntpなのでビルドの必要は無い。でも、ビルドしたい場合は下

# apt-get install libcap-dev pps-tools
# /etc/init.d/ntp stop
# apt-get remove ntp
$ wget http://archive.ntp.org/ntp4/ntp-4.2.8p8.tar.gz
$ tar zxvf ntp-4.2.8p8.tar.gz
$ cd ntp-4.2.8p8
$ ./configure --enable-linuxcaps
$ make -j4
# make install

# ln -s /usr/local/sbin/ntpd /usr/sbin/ntpd
# ln -s /usr/local/bin/ntpdc /usr/bin/ntpdc      #必要なら
# ln -s /usr/local/bin/ntpq /usr/bin/ntpq          #必要なら
# ln -s /usr/local/bin/ntpsweep /usr/bin/ntpsweep  #必要なら
# ln -s /usr/local/bin/ntptrace /usr/bin/ntptrace  #必要なら

/etc/ntp.confにPPS用の設定を追記する。
このとき、カーネルPPSであれば127.127.22.*を指定するが、rpi_gpio_ntpを使用する場合は、NMEAと同じく127.127.28.*を使用する。前回(今回も引き続き)MNEA用として127.127.28.0を使用しているのでPPS用は1足して127.127.28.1を指定。

1
2
server 127.127.28.1 minpoll 4 maxpoll 4 prefer
fudge 127.127.28.1 refid PPS

準備完了

# echo "201" > /sys/class/gpio/unexport         # ←GPIO201開放(念の為)
# echo "201" > /sys/class/gpio/export           # ←同使用開始
# echo "in" > /sys/class/gpio/gpio201/direction # ←入力モードセット

# /usr/local/bin/rpi_gpio_ntp -N 1 -g 201   # ←rpi_gpio_ntp開始
# /etc/init.d/ntp start                     # ←ntp開始

暫く待ってから確認。

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+SHM(0)          .NMEA.           0 l   10   16  377    0.000    1.471   0.153
*SHM(1)          .PPS.            0 l    9   16  377    0.000    0.001   0.006
+ntp1.jst.mfeed. 133.243.236.17   2 u   14   64  377   14.508    3.409   0.184
+ntp2.jst.mfeed. 133.243.236.17   2 u   15   64  377   13.886    2.908   0.229
+ntp3.jst.mfeed. 133.243.236.17   2 u   19   64  377   14.500    1.873   0.161

上手く行ったらシステム起動時に自動実行できるように/etc/rc.localに追記。

1
2
3
echo "201" > /sys/class/gpio/export
echo "in" > /sys/class/gpio/gpio201/direction
/usr/local/bin/rpi_gpio_ntp -N 1 -g 201

最後のexit 0より前に3行挿入する。
ただ、これだとちょっと雑過ぎて最初に大きな時間のズレが出るようなので要改良。ntpdateとか挟む方が良いかも。

カーネルPPSではないが精度はまぁまぁな感じ。でも、offsetの値に落ち着きがない印象。
やっぱりカーネルをビルドしようかしら?

2017年3月15日追記:
armbianで配布しているパッケージでカーネルを4.10.0に更新するとUART1が無効になる。そこで以下1行追記。

# vi /boot/armbianEnv.txt
overlays=sun8i-h3-uart1

ついでにPPS-GPIOモジュールを有効にしたい。こちらも1行追記

# vi /etc/modules
pps-gpio

システムを再起動する。

# lsmod
Module                  Size  Used by
sunxi_cir               3825  0 
cfg80211              192770  0 
rfkill                 10928  1 cfg80211
evdev                   9979  0 
sun8i_codec_analog     13766  0 
snd_soc_core          115473  1 sun8i_codec_analog
snd_pcm_dmaengine       4221  1 snd_soc_core
snd_pcm                70145  2 snd_pcm_dmaengine,snd_soc_core
sun8i_ths               3134  0 
gpio_keys               8517  0 
uio_pdrv_genirq         3354  0 
cpufreq_dt              3522  0 
uio                     8012  1 uio_pdrv_genirq
thermal_sys            43232  2 cpufreq_dt,sun8i_ths
pps_gpio                2833  0 
g_serial                3737  0 
libcomposite           34692  1 g_serial
fuse                   70718  1 

上の例では下から4行目にpps_gpioが表示されている。
ここまでは上手くいったが/dev/pps*が生えてこなくて詰んだ。
追記ここまで。

関連記事: