2013-08-28

Raspberry PiでSPI通信機能を利用する
(NTP時計を無線LAN化する)

SPIはクロック(SCK)と出力信号(MOSI : Master Out Slave In)と入力信号(MISO : Master In Slave Out)の3本で双方向通信を行う機能です。UART / RS-232C(調歩同期式シリアル通信)と並んで、シンプルな通信方式で、マイコン間の通信でよく用いられます。UARTはGNDを除くと、TxDとRxDの2本の信号線があれば通信できますが、あらかじめ通信速度を決めておいて、双方の速度を一致させていなければなりません。SPIではGND以外では、SCK、MOSI、MISOの3本で通信するクロック同期式シリアル通信です。通信速度はマスターとなる側がクロックを送出し、スレーブ側はそのクロックに同期して送受信を行います。

UARTもSPIもRaspberry Piは標準でサポートされていますが、事前の設定が必要です。Raspberry PiでUARTを使った通信はこちらで紹介しています。この項ではSPIを使用します。題材は2年前に作ったNTP時計です。Raspberry PiからNTP時計へは表示情報を送信するだけで、受信は行いません。Raspberry Piがマスターになるので、SCKとMOSIだけ使用し、MISOは配線していません。

SPIを有効にする

piでログインしたら、rootになります。

pi@raspberrypi:~$ sudo su

modulesの最後にspidevを追加します。

/etc/modules

spidev

raspi-blacklist.confのblacklist spi-bcm2708の行をコメントアウト(先頭に#を記述)します。

/etc/modprobe.d/raspi-blacklist.conf

#blacklist spi-bcm2708 

再起動します。

root@raspberrypi:/home/pi# reboot

piでログインしたら、spidevが有効になっているか確認します。

pi@raspberrypi:~/develop/spi$ ls -la /dev/spidev*
crw------- 1 root root 153, 0 Jan 1 1970 /dev/spidev0.0
crw------- 1 root root 153, 1 Jan 1 1970 /dev/spidev0.1

root以外のユーザーでもSPIが使用できるように、読み書き権限を付与します。

pi@raspberrypi:~/develop/spi$ sudo chmod 666 /dev/spidev0.0
pi@raspberrypi:~/develop/spi$ ls -la /dev/spidev*
crw-rw-rw- 1 root root 153, 0 Jan 1 1970 /dev/spidev0.0
crw------- 1 root root 153, 1 Jan 1 1970 /dev/spidev0.1

NTP時計を制御する

NTP時計にRaspberry Piを接続します。

セルフパワーのUSBハブから、Raspberry Piと時計本体の電源を取ります。

SCKとMOSIを結線します。入力はしないのでMISOは接続しません。

テストプログラム

信号を送信してみます。

通信プロトコルは、拙作NTP時計のページでも解説しています。表示する数字の桁位置と文字を1バイトのデータで送信します。

spi

上の図でCKがSCK、DOがMOSIに対応します。

表示する桁位置と値を1バイトのデータとして送信します。

送信値

ビット 7 6 5 4 3 2 1 0
内容 桁位置 数字

桁位置

0 秒の1の位
1 秒の10の位
2 分の1の位
3 分の10の位
4 時の1の位
5 時の10の位
6 日の1の位
7 日の10の位
8 月の1の位
9 月の10の位
10 年の1の位
11 年の10の位
12 年の100の位
13 年の1000の位

テストプログラム

spi.cpp

動作確認ができましたので、rc.localにchmod 666 /dev/spidev0.0を記述しておきます。

/etc/rc.local

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

chmod 666 /dev/spidev0.0

exit 0

時刻を表示する

現在時刻を表示するようにプログラムを変更しました。

spiclock.cpp

実行する前に、環境変数TZを設定しておく必要があります。

$ export TZ=JST-9
$ ./spiclock

無線LAN対応にする

JJYを利用する一般の電波時計は、周囲の建物で電波が遮られたり、屋内では電波が弱くなったりして、うまく捕らえられないことがあります。さらに、液晶表示の時計なら良いのですが、LED表示では、ダイナミック点灯により発生するノイズのために、JJYの電波を受信することができません。そこで、無線LANを使用し、NTPサーバから時刻を取得するWiFi電波時計にします。

USB接続のWiFiアダプタを使用します。既に無線LANのアクセスポイントが動作していることを前提とします。

USB WiFiアダプタの設定方法は、検索するといろいろなサイトでwpa_supplicant.confを編集する方法が紹介されていたりしますが、私はうまく設定できなかったヘタレなので、X Windowを起動して、GUIのWiFi Configで設定しました。

コンソールからX Windowを起動するには、startxコマンドを実行します。WiFi Configをダブルクリックして、scanボタンを押し、アクセスポイントに接続します。接続を開始してから完了するまでに、10~30秒くらいかかります。