spi.xfer2関数の使い方を徹底解説


画像の拡大

この記事では、Raspberry PiでSPI通信する時に使うspi.xfer2関数の使い方を詳しく解説する。なお、本記事で紹介するPythonプログラムは、ラズパイとADコンバータMCP3008でSPI通信するプログラムとなっている。

ADコンバータMCP3008の使い方はこちらを参考

ラズパイのSPI初期設定が済んでない方はこちらを参考

MCP3008 8チャネル 10ビット A/Dコンバータ(SPI接続)
MCP3008 8チャネル 10ビット A/Dコンバータ(SPI接続)
Amazon

ラズパイでMCP3008とSPI通信するPythonプログラム

ラズパイでMCP3008とSPI通信するPythonプログラムがこちら。

# -*- coding:utf-8 -*-
import time
import spidev

Vref = 3.334  # 電圧をテスターで実測する

spi = spidev.SpiDev()

spi.open(0, 0)  # bus0,cs0
spi.max_speed_hz = 100000  # 100kHz 必ず指定する


def readAdc(channel):
    adc = spi.xfer2([1, (8 + channel) << 4, 200])
    data = ((adc[1] & 3) << 8) + adc[2]
    return data


def convertVolts(data, vref):
    volts = (data * vref) / float(1023)
    return volts


def convertTemp(volts):
    temp = (volts - 0.75) / 0.01 + 25.0
    return temp


if __name__ == '__main__':
    try:
        while True:
            data = readAdc(channel=0)
            volts = convertVolts(data, Vref)
            temp = convertTemp(volts)
            print("CH0 volts: {:.2f}".format(volts))
            print("temp : {:.2f}".format(temp))

            time.sleep(2)

    except KeyboardInterrupt:
        spi.close()

spi.xfer2([1, (8 + channel) << 4, 0])の解説

それでは先ほどのPythonプログラムの中身を詳しくみていこう。

次の一行は、多くの人が謎に思われたのではないだろうか。

adc = spi.xfer2([1, (8 + channel) << 4, 0])
  • spi.xfer2の引数のリストはなぜ3つなのか?
  • そして返り値adcとはいったい?

色々と調べた結果、自分なりに理解できたので説明していく。

はじめに重要なことをお伝えする

  • spi.xfer2の引数に渡すリストは8ビットデータとして解釈される。

これを頭におきながら、先を読み進めてもらいたい。

MCP3008のデータシートから、送受信データのフォーマットを見てみよう。データシートを読むと、3バイト(8ビットx3)のデータで送受信する仕様となっている。


画像の拡大

MCP3008のデータシートはこちら

まずは送信データだけに絞って考えてみよう

送信データの1バイト目を見てみると、start bit として 1 を送信している。


画像の拡大

spi.xfer2([1, (8 + channel) << 4, 0]) のリストの最初が1だったのはそのためだ。次に送信データの2バイト目を見てみよう。


画像の拡大

わかりやすくするために、上位4ビット SGL/DIFF D2 D1 D0 だけ考えてみる。コンフィギュレーションビットで説明した通り、今回はSGL/DIFFを1の値で固定する。残りの3ビットはチャネル番号になるので、1 0 0 0 の形にするにはチャネル番号に二進数で表される 1 0 0 0 すなわち8を足し合わせれば良い。

>>> bin(8)
'0b1000'

そして下位4ビットの X X X X の値は気にしなくて良いので、<< 4 で4ビット分を左にシフトして0で埋め合わせている。だからリストの2番目は (8 + channel) << 4 で計算された値を代入しているのだ。ちなみに、<< 左ビットシフトは、その数だけ左にずらしてゼロで埋める演算だ。

>>> bin(8 << 4)
'0b10000000'

最後に送信データの3バイト目を見ていこう。


画像の拡大

これは8bit分データを送れば値は気にすることはない。だから spi.xfer2([1, (8 + channel) << 4, 0]) の第3引数にはゼロを入れてあるが、べつにゼロでなくても構わないのだ。

  • 第三引数まで入れる理由は、第二引数までだとシリアルクロック(CLK)が3バイト分働いてくれず、Doutからデータを取り出せないからである。

つまりは、spi.xfer2関数の引数のリストは、ひとつに付き8回のシリアルクロックを送信する仕組みとなっているようだ。 spi.xfer2([1, (8 + channel) << 4, 0]) がなぜ3つの引数をとるのか、これでお分かりいただけたと思う。

data = ((adc[1] & 3) << 8) + adc[2]の解説

今度は受信データをみていこう。

はじめにひとつ、重要なポイント

  • 関数spi.xfer2は、たとえばDinに3バイトデータ送信したら、同時にDoutから3バイトデータを取り出せる仕組みになっている。

adc = spi.xfer2([1, (8 + channel) << 4, 0]) の一行は3バイトのデータをDinへ書き込んでいるとともに、 Doutの値を3バイト分読み込んで、返り値 adc に格納しているのだ。

  • spi.xfer2の返り値のリストは、引数(送信データ)と同じ数だけある。
  • その値は8ビットをlong型にした値である。

これが分かれば、受信データのフォーマットの理解も簡単であろう。データシートの受信データのフォーマットを一気に見ていく。

1バイト目


画像の拡大

2バイト目


画像の拡大

3バイト目


画像の拡大

図から、次の3つがわかる。

  1. 1バイト目は無視してよい。
  2. 2バイト目も上位6ビット分は無視して、下位2ビットを取り出せばよい。
  3. 3バイト目には8ビットのデータがつまっている。

つまり、2バイト目の下位2ビットと3バイト目の8ビットをつなぎ合わせた形が、10bitで表現される電圧値となる。だからチャネルから取り出す値は、data = ((adc[1] & 3) << 8) + adc[2] で計算されることがお分かりだろう。

論理演算がややこしいので、少し補足しておく。adc[1] & 3& とはAND演算(論理積)の計算である。たとえば、3を二進数で表すと、

>>> bin(3)
'0b11'

となり、11を二進数で表すと、

>>> bin(11)
'0b1011'

となる。この11と3のAND演算をやってみよう。

>>> bin(11 & 3)
'0b11'

これは、11の下位2ビットを取り出したのと同じである。つまり、11を3でマスクしたことになる。

話を戻して、先ほどの ((adc[1] & 3) << 8) をもう一度みてみよう。この式でやっていることは、2バイト目の受信データの下位2ビットを取り出して、左に8ビットシフトし、ゼロで埋め合わせている。。だからこれにadc[2] を足すことで、10bitで表現されたアナログ電圧値を読み取れるわけだ。

以上でspi.xfer2の使い方の説明を終わる。データシートをよく見て送受信のやりとりをしっかりイメージできれば、SPI通信も難しくないことが分かったと思う。

MCP3008 8チャネル 10ビット A/Dコンバータ(SPI接続)
MCP3008 8チャネル 10ビット A/Dコンバータ(SPI接続)
Amazon

人気のラズパイ

Raspberry Pi 4 Model B 8GB 技適マーク入 正規品!ラズベリーパイ4 モデルB
Raspberry Pi 4 Model B 8GB 技適マーク入 正規品!ラズベリーパイ4 モデルB
Amazon
TRASKIT Raspberry Pi 4 Model B Starter Kit
TRASKIT Raspberry Pi 4 Model B Starter Kit
Amazon
Raspberry Pi Zero W - ヘッダー ハンダ付け済み
Raspberry Pi Zero W - ヘッダー ハンダ付け済み
Amazon

人気のラズパイ周辺機器

10 インチRaspberry Pi用タッチモニター EleDuino HDMI モバイルディスプレイ
10 インチRaspberry Pi用タッチモニター EleDuino HDMI モバイルディスプレイ
Amazon
Raspberry Pi4 Model B /アルミニウム金属ケース/ファンレス/放熱シート付き
Raspberry Pi4 Model B /アルミニウム金属ケース/ファンレス/放熱シート付き
Amazon
Freenove Raspberry Pi 4 B 3 B+ 400用の究極のスターターキット
Freenove Raspberry Pi 4 B 3 B+ 400用の究極のスターターキット
Amazon
KEYESTUDIO DC 5V 4チャンネル リレーシールドモジュール 拡張ボード for Raspberry Pi
KEYESTUDIO DC 5V 4チャンネル リレーシールドモジュール 拡張ボード for Raspberry Pi
Amazon

Raspberry Piのオススメ入門書

Raspberry Piクックブック 第3版 (Make:PROJECTS)
Raspberry Piクックブック 第3版 (Make:PROJECTS)
Amazon
これ1冊でできる! ラズベリー・パイ 超入門 改訂第6版 Raspberry Pi 4/Zero W対応
これ1冊でできる! ラズベリー・パイ 超入門 改訂第6版 Raspberry Pi 4/Zero W対応
KindleAmazon
写真や図解でよくわかる ラズパイZeroを使い倒す本 Raspberry Pi Zero W対応
写真や図解でよくわかる ラズパイZeroを使い倒す本 Raspberry Pi Zero W対応
KindleAmazon
記事に関するご質問などがあれば、ぜひTwitterへお返事ください。