はじめに
本記事では、Raspberry PiでSPI(Serial Peripheral Interface)を使用して、周辺デバイスと接続する方法について記載いたします。SPI対応のK型熱電対温度センサモジュール(MAX6675)を接続先デバイスとして使用します。
SPIは、パラレルに比べて接続端子数が少ないシリアルバスの一種で、I2Cよりも高速なデータ転送を行うことができます。
インストール直後の初期状態ではSPIが無効になっていますので、使用する場合は有効化する必要があります。SPIを有効化する方法には、「raspi-config」コマンドを使用して有効化する方法と「/boot/config.txt」を直接編集して有効化する方法があります。
はじめに、SPIの概要について説明した後、「/boot/config.txt」ファイルを編集し、SPIを有効化します。その後、実際にSPIインターフェイスに対応したK型熱電対温度センサモジュール(MAX6675)を使用して、動作確認を行います。
環境
- ボート
Raspberry Pi 4 Model B - OS
Raspberry Pi OS (32-bit) Lite
Minimal image based on Debian Buster
Version: August 2020
Release date: 2020-08-20
Kernel version: 5.4
SPI について
SPI の概要
SPI (Serial Peripheral Interface)は、マイクロコントローラとその周辺デバイスとの接続に使用されるインターフェイスの一種です。各種センサー、A/Dコンバータ、D/Aコンバータ、SRAMなどとマイクロコントローラを接続するのに幅広く採用されています。
SPIは、同期式/全二重のマスター・スレーブ型のインターフェイスです。マスターまたはスレーブからのデータは、クロックの立ち上がりエッジまたは立ち下がりエッジによって、同期がとられます。また、マスターとスレーブは、同時にデータを送信することも可能です。
SPIには、3線式のものと4線式のもがあります。ここでは、一般的な 4 線式のSPIについて記載します。4線式のSPIデバイスには、次の4つの信号があります。
- クロック(SPI CLK, SCLK)
- チップ・セレクト(CS)
- マスター出力/スレーブ入力(MOSI:Master Output Slave Input)
- マスター入力/スレーブ出力(MISO:Master Input Slave Output)
2種類のデバイスのうち、クロック信号を生成する方が、マスターと呼ばれます。マスターとスレーブ間で送信されるデータは、マスターによって生成されるクロック信号に同期して転送されます。SPIデバイスは、I2Cインターフェイスよりもはるかに高いクロック周波数に対応していますので、高速なデータ転送を行うことができます。
SPIに対応するデバイスを使用する場合は、データシートを参照しSPIのクロック周波数に関する仕様を確認してください。
SPIでは、1つのマスターに対して、複数のスレーブを接続することができます。マスターとスレーブ間の接続は、標準モードまたはデイジーチェーンモードで接続します。図1に標準モードでのマスターとスレーブ間の接続を示します。
マスターからのCS信号は、スレーブの選択に使用されます。通常、この信号はActive Lowで、SPIバスからスレーブを切り離したいときに、Highにします。複数のスレーブを接続して使用する場合は、マスターから各スレーブに対して、個別にCS信号を送信する必要があります。マスターがCS信号をイネーブルにする(CS信号をLowに引き下げる)と選択されたスレーブに対して、クロックとMOSI/MISO上のデータが有効になります。 複数のCS信号がイネーブルになると、どのスレーブからデータが送信されているか識別できないため、MISO上のデータが壊れてしまいます。
MOSIとMISOは、データを送受信するために使用する信号線です。MOSI(Master Output Slave Input)は、マスターからスレーブに対してデータを送信するために使用されます。MISO(Master Input Slave Output)は、スレーブからマスターに対してデータを送信するために使用されます。
データ転送
SPI による通信を開始するためには、マスターからクロック信号を送信するともに CS 信号をイネーブルにすることより対象のスレーブを選択する必要があります。SPIは全二重インターフェイスのため、マスターとスレーブはそれぞれ MOSIとMISOを使用して、同時にデータを送信することができます。つまり、MOSI/SDOバスに対してシリアルにデータをシフトさせて出力する送信動作とMISO/SDIバスのデータをサンプリングする受信動作を同時に行うことが可能です。シリアル・クロックのエッジによって、データのシフト処理/サンプリング処理の同期がとられます。SPIでは、クロックの立ち上がりエッジと立ち下がりエッジのうち、どちらでデータをシフト/サンプリングするかをユーザーが選択することができます。
SPI モード(クロックの極性と位相)
SPIマスターは、クロックの極性と位相を選択することができます。CPOLビットは、アイドル状態におけるクロック信号の極性を設定するためのものです。アイドル状態とは、送信を開始するためにCS信号がHighからLowに遷移するまでの間と、送信を終了するためにCS信号がLowからHighに遷移するまでの間のことです。CPHAビットは、クロックの位相を選択するためのものです。CPHAビットにより、データのサンプリング/シフトにクロックの立ち上がりエッジと立ち下がりエッジのうち、どちらを使用するかを設定します。マスターでは、スレーブの要件に適合するように、クロックの極性と位相を選択する必要があります。表1 に示すように、CPOLビットとCPHAビットの組み合わせによって、4つの SPIモードが存在することになります。
SPI モード | CPOL | CPHA | アイドル状態の クロックの極性 | データのサンプリング/シフト に使用されるクロックの位相 |
---|---|---|---|---|
0 | 0 | 0 | Logic Low | データを立ち上がりエッジでサンプリング、 立ち下がりエッジでシフト |
1 | 0 | 1 | Logic Low | データを立ち下がりエッジでサンプリング、 立ち上がりエッジでシフト |
2 | 1 | 1 | Logic High | データを立ち下がりエッジでサンプリング、 立ち上がりエッジでシフト |
3 | 1 | 0 | Logic High | データを立ち上がりエッジでサンプリング、 立ち下がりエッジシフト |
図2 から図5 に、4つのSPIモードにおけるタイミング図の例を示します。各例において、データの値は MOSIとMISOの部分に示しています。送信の開始と終了は緑色の点線、サンプリング用のエッジは橙色の点線、シフト用のエッジは青色の点線で示しています。
図2は、SPI モード0のタイミング図です。このモードでは、クロックの極性を0に設定しているので、アイドル状態におけるクロック信号はLowになります。クロックの位相については、0に設定しているので、データはクロック信号の立ち上がりエッジ(橙色の点線)でサンプリングされ、立ち下がりエッジ(青色の点線)でシフトされます。
図3は、SPIモード1のタイミング図です。このモードでは、クロックの極性を0に設定しているので、アイドル状態におけるクロック信号はLowになります。クロックの位相については、1に設定しているので、データはクロック信号の立ち下がりエッジ(橙色の点線)でサンプリングされ、立ち上がりエッジ(青色の点線)でシフトされます。
図4は、SPIモード2のタイミング図です。このモードでは、クロックの極性を1に設定しているので、アイドル状態におけるクロック信号はHighになります。クロックの位相については、1に設定しているので、データはクロック信号の立ち下がりエッジ(橙色の点線)でサンプリングされ、立ち上がりエッジ(青色の点線)でシフトされます。
図5は、SPIモード3のタイミング図です。このモードでは、クロックの極性を1に設定しているので、アイドル状態におけるクロック信号はHighになります。クロックの位相については、0に設定しているので、データはクロック信号の立ち上がりエッジ(橙色の点線)でサンプリングされ、立ち下がりエッジ(青色の点線)でシフトされます。
Raspberry Pi における SPI の有効化
インストール直後の初期状態では、SPIは無効になっています。SPIを有効化するため、「/boot/config.txt」ファイルを以下のように編集します。
「#dtparam=spi=on」の行のコメントアウトを削除します。
pi@raspberrypi:~ $ sudo vi /boot/config.txt
(略)
# Uncomment some or all of these to enable the optional hardware interfaces
#dtparam=i2c_arm=on
#dtparam=i2s=on
dtparam=spi=on <-- コメントアウトを削除する。
(略)
pi@raspberrypi:~ $
「/boot/config.txt」ファイルを編集後、SPI を有効化するため、再起動を行います。
pi@raspberrypi:~ $ sudo reboot
SPI の有効化確認
SPIが有効化されていることを確認します。
以下のコマンドを実行し、「spi_bcm2835」モジュールがロードされていることを確認します。
pi@raspberrypi:~ $ lsmod | grep spi
spidev 20480 0
spi_bcm2835 24576 0
pi@raspberrypi:~ $
以下のコマンドを実行し、/devディレクトリに SPI0 のデバイスファイル「spidev0.X」が作成されていることを確認します。
pi@raspberrypi:~ $ ls -l /dev | grep spi
crw-rw---- 1 root spi 153, 0 Nov 3 20:17 spidev0.0
crw-rw---- 1 root spi 153, 1 Nov 3 20:17 spidev0.1
pi@raspberrypi:~ $
SPI の動作確認
ここでは、SPI対応のMAX6675を搭載したK型熱電対温度センサモジュールを使用して、SPI接続を行い温度を取得して動作確認を行います。MAX6675は MAXIM integrated 社の販売しているSPI対応のK型熱電対温度センサのコントロール IC です。
動作確認には、Python spidevライブラリを使用してSPI通信を行い、温度センサーモジュールから温度を取得します。Pythonは、Python 3.7.3を使用します。
温度センサーモジュールとの接続
以下のとおり、GPIOを使用してRaspberry PiとMAX6675温度センサーモジュールを接続します。
Raspberry Pi ヘッダーピン | 温度センサー モジュール |
---|---|
21番ピン:GPIO 9 (MISO) | SO ピン |
23番ピン:GPIO 11 (SCLK) | SCK ピン |
24番ピン:GPIO 8 (CE0) ピン | CS ピン |
17番ピン:3V3 power ピン | VCC ピン |
25番ピン:Ground ピン | GND ピン |
参考までにRaspberry Pi 4 Model B の GPIOピン配置を記載します。
参考URL:GPIO ピン配置
動作確認環境の準備
Python(Python 3.7.3)の spidevライブラリを使用して、温度計測を行う動作環境の準備を行います。
Python pip パッケージのインストール
Python spidevライブラリをインストールするため、pip(Python パッケージ管理ツール)をインストールします。以下のコマンドを実行し、pipパッケージをインストールします。
pi@raspberrypi:~ $ sudo apt-get install python3.7-dev
pi@raspberrypi:~ $
pi@raspberrypi:~ $ sudo apt-get install python3-pip
pi@raspberrypi:~ $
以下のコマンドを実行し、インストールされたpipバージョンを確認します。
pi@raspberrypi:~ $ pip3 --version
pip 18.1 from /usr/lib/python3/dist-packages/pip (python 3.7)
pi@raspberrypi:~ $
Python spidev ライブラリのインストール
pipをインストールした後、以下のコマンドを実行し、pipを使用してPython spidevライブラリをインストールします。
pi@raspberrypi:~ $ sudo pip3 install spidev
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting spidev
Downloading https://www.piwheels.org/simple/spidev/spidev-3.5-cp37-cp37m-linux_armv7l.whl (40kB)
100% |????????????????????????????????| 40kB 143kB/s
Installing collected packages: spidev
Successfully installed spidev-3.5
pi@raspberrypi:~ $
MAX6675を使用した温度計測モジュールについて
MAX6675は、冷接点補償を実行しK型熱電対からの信号をデジタル化するICです。データは12ビットの分解能(分解能:0.25°C)で、SPIで接続します。SPIは、読み取り専用形式の 3 線(CS、SCK、SO)を使用して、データを読み取ります。
データは、16ビット(D15 ~ D0)で構成されていて、読み出しに16クロックサイクルが必要です。
読み出しは、クロックの立ち下がりエッジで行います。
最初のD15は、ダミーの符号ビットで常にゼロです。
D14からD3ビットの12ビットに、MSBからLSBの順序で計測された温度データがセットされます。
D2ビットは、通常、Lowで熱電対入力がオープン(接続されていない状態)になっている時に1:Highにセットされます。
D1ビットは、MAX6675のデバイスIDを提供するために0:Lowで、D0ビットは3ステートになっています。
このため、D14からD3ビットの12ビットを使用して計測温度を取得します。図7にSOの出力について記載します。
動作確認
Python spidevライブラリが利用できるようになれば、動作確認環境の準備は完了です。
以下の温度取得テストプログラムを作成し動作確認を行いました。
温度取得テストプログラム「measure_temp.py」
import spidev
import time
import datetime
dummyData = [0x00,0x00]
spi = spidev.SpiDev()
spi.open(0, 0) # bus 0,cs 0
# Settings
spi.max_speed_hz = 1000000 # 1MHz
spi.mode = 3 # SPI mode : 3
while 1:
dt_now = datetime.datetime.now()
readByteArray = spi.xfer2(dummyData)
temperatureData = ((readByteArray[0] & 0b01111111) << 5) | ((readByteArray[1] & 0b11111000) >> 3)
temperature = temperatureData * 0.25
print(dt_now.strftime('%Y/%m/%d %H:%M:%S') + ',' + str(temperature))
time.sleep(0.5)
spi.close
温度取得テストプログラムを実行し温度計測を開始します。
以下のように、正常にSPI通信で温度センサモジュールから1秒間隔で温度を取得できました。
pi@raspberrypi:~ $ python measure_temp.py
2020/10/30 06:44:34,Temperature: 22.75
2020/10/30 06:44:35,Temperature: 22.25
2020/10/30 06:44:36,Temperature: 22.75
2020/10/30 06:44:37,Temperature: 22.75
2020/10/30 06:44:38,Temperature: 22.5
(略)
pi@raspberrypi:~ $
以上です。