GPSのPPS入力はやっていたが、カーネルでGPIOにPPSが出力できるだなんて、知らなかった。
ただし、内蔵クロックの精度で。多分。
pps-gen-gpio
pps-gen-gpioというオープンソースがあるのを教えてもらった。知らなかった。
元は、パラレルポートに出力していたものを、Device Treeを参照するように変えたらしい。
Device Tree
BeagleBoneでのDTの書き方
README.meには、BeagleBoneの例が書いてある。
GPIO1_19という端子があり、&gpio1 19 GPIO_ACTIVE_HIGH
と書くらしい。
我らがRaspberry Piではどうか。
Raspberry PiでのDTの書き方
まずはRaspberry Pi本家のDT情報。いっぱい書いてあるが、pps-genに使えそうな書き方については、えーと…。Linux本家の情報でも勉強しつつ…。
Raspberry Piのdtsファイル
わからないときは、真似をするのも有効。というわけで、Raspberry Piで今使っているDTSを見る。
bcm2708-rpi-b.dtb, bcm2708-rpi-b-plus.dtb, bcm2708-rpi-cm.dtb, and bcm2709-rpi-2-b.dtb
の4つがあるとのことで、確かに
~/git/raspberrypi/linux$ ls -1 arch/arm/boot/dts/bcm270*.dts
arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
arch/arm/boot/dts/bcm2708-rpi-b.dts
arch/arm/boot/dts/bcm2708-rpi-cm.dts
arch/arm/boot/dts/bcm2709-rpi-2-b.dts
あるのだが、いきなり、無い。自分の手元にあるModel A+用のものが。
よく読むと、
Note that Model As and A+s will use the "b" and "b-plus" variants, respectively.
とあるので、b-plusを使えということか。ややこしい。
bcm2708-rpi-b-plus.dtsの内容
gpioで検索すると、
&spi0 {
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
という記載がある。こちらを見ると、SPI0はGPIO7と8で共用っぽいので、第1引数がGPIOの番号だろう。
それでは、第2引数にある1
とは、何だろうか。
他の例を見ると、
&hdmi {
hpd-gpios = <&gpio 46 GPIO_ACTIVE_LOW>;
};
とあるので、GPIO_ACTIVE_LOW
というマクロで書くのがわかりやすそうだ。
他にどんなマクロがあるか探してみると、
/* Bit 0 express polarity */
# define GPIO_ACTIVE_HIGH 0
# define GPIO_ACTIVE_LOW 1
とあるので、1
はGPIO_ACTIVE_LOW
か。想像と逆だった。
GPIOの初期状態
こちらの記事によると、GPIOの番号によって初期状態が異なる。
初期状態が0(Low)のGPIOに対して、Active Highに設定すれば、問題ないのではないか(勘)。
dtsへの追記
pps-gen-gpio.txtを見ながら、記載。
/ {
compatible = "brcm,bcm2708";
model = "Raspberry Pi Model B+";
pps-gen {
compatible = "pps-generator-gpios";
pps-gen-gpios = <&gpio 18 GPIO_ACTIVE_HIGH>;
/* assert-falling-edge; */
};
};
AssertはRising Edgeにしてみたかったので、コメントに。
dtsのbuild
Raspberry Pi本家のKernel building情報をもとに、
~/git/raspberrypi/linux$ make -j8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
とすると、色々出た後、
DTC arch/arm/boot/dts/bcm2708-rpi-b-plus.dtb
と出て、dtbが作成される。
dtbのインストール
先ほどのbuild手順の一部をそのまま
~/git/raspberrypi/linux$ sudo mount /dev/sdb1 mnt/fat32
~/git/raspberrypi/linux$ sudo cp arch/arm/boot/dts/*.dtb mnt/fat32/
~/git/raspberrypi/linux$ sudo umount mnt/fat32
起動してDTの確認
またも本家DT情報に、procでの確認方法が書いてある。なんて教育的なんだ(当たり前)。
pi@raspberrypi:~ $ dtc -I fs /proc/device-tree
:
pps-gen {
compatible = "pps-generator-gpios";
pps-gen-gpios = <0xa 0x12 0x0>;
};
反映されている模様。
再度pps-gen-gpio
ようやくpps-gen-gpioに戻ってきた。これをbuildする。
…が、build通らず。さらに、通してinsmod
しても、反応無しだった…。
そこで、2点修正。
devm_gpiod_get()への第3引数の追加
kernel 4.4.30では、引数を3つ取るらしい。
@@ -198,7 +198,7 @@ static int pps_gen_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
/* pps-gen is the function associated with gpio list pps-gen-gpios */
- devdata->pps_gpio = devm_gpiod_get(dev, "pps-gen");
+ devdata->pps_gpio = devm_gpiod_get(dev, "pps-gen", GPIOD_ASIS); /* from enum gpiod_flags */
if (IS_ERR(devdata->pps_gpio)) {
dev_err(dev, "cannot get PPS GPIO %ld\n",
PTR_ERR(devdata->pps_gpio));
GPIOD_ASIS
なら、デフォルトの端子状態から変えないんだろうな(また勘)。
これ以外には、こんなのがあるらしい。
enum gpiod_flags {
GPIOD_ASIS = 0,
GPIOD_IN = GPIOD_FLAGS_BIT_DIR_SET,
GPIOD_OUT_LOW = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT,
GPIOD_OUT_HIGH = GPIOD_FLAGS_BIT_DIR_SET | GPIOD_FLAGS_BIT_DIR_OUT |
GPIOD_FLAGS_BIT_DIR_VAL,
};
compatibleがドキュメントと異なる
README.meやpps-gen-gpioによると、
pps-generator-gpios value of ".compatible" property in pps-gen node
とあるが、ドライバのソース上は{ .compatible = "pps-gen-gpios", },
となっているように見える。
DTとドライバでcompatibleが異なると、probe
が呼ばれず、dmesg
しても
[ 369.273294] pps_gen_gpio: GPIO PPS signal generator
より先が表示されない気がしたので、ソースをドキュメントに合わせて修正。
@@ -233,7 +233,7 @@ static int pps_gen_gpio_remove(struct platform_device *pdev)
* when a match is found, the corresponding DT node name is passed
* backed in pdev->name */
static const struct of_device_id pps_gen_gpio_dt_ids[] = {
- { .compatible = "pps-gen-gpios", },
+ { .compatible = "pps-generator-gpios", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pps_gen_gpio_dt_ids);
pps-gen-gpioのbuild例
カーネルソースの場所は環境に合わせて下さい。
$ make -C ~/git/raspberrypi/linux ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- M=`pwd` CONFIG_PPS_GENERATOR_GPIO=m
実行
pi@raspberrypi:~ $ sudo insmod pps_gen_gpio.ko
dmesg
でこんなログが出てきていれば、きっと動いている。
[ 671.795926] pps_gen_gpio: GPIO PPS signal generator
[ 671.796269] pps_gen_gpio: found 1 GPIOS defined in DT
[ 671.796391] pps_gen_gpio: port write takes 500ns
測定

パルス幅は約28usくらいらしい。
おまけ::Active Low

おお、逆になっている。
あ、DTにassert-falling-edge
付けるの忘れたが、edgeはどっちになるんだろ。
自由研究
- ドリフトとジッタの調査
-
dmesg
に出るエラー(?)の内容の調査 - NTP補正との関係の調査
修正ソース
こちら。