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

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

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

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

ラズパイで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接続)

マイクロチップの10ビットADコンバータです。 ■主な仕様 ・8チャンネル ・16ピンDIP ・2.7V~5.5V動作(VREF=VDD固定) ・SPIインターフェース

Amazon
Raspberry Pi Zero W - ヘッダー ハンダ付け済み - ラズベリー・パイ ゼロ W ワイヤレス
Raspberry Pi Zero W - ヘッダー ハンダ付け済み - ラズベリー・パイ ゼロ W ワイヤレス

Amazon
正規代理店商品 Raspberry Pi 4 Model B (8GB) made in UK element14製 技適マーク入
正規代理店商品 Raspberry Pi 4 Model B (8GB) made in UK element14製 技適マーク入

Amazon

Raspberry Piの参考書

Raspberry Pi クックブック 第2版 (Make:PROJECTS)
Raspberry Pi クックブック 第2版 (Make:PROJECTS)

本書は、全世界で多くのユーザーの支持を集めているマイコンボード「Raspberry Pi」を使いこなすためのレシピ集です。ハードウェアの基本、オペレーティングシステムの使い方、ネットワーク接続、Pythonプログラミングの基本を紹介した上デ、実際の作品製作に必要になる、高度なPythonプログラミング、GPIO(汎用入出力)、モーター、センサー、ディスプレイなどの使い方へと解説を進めていきます。

Amazon
写真や図解でよくわかる ラズパイZeroを使い倒す本 Raspberry Pi Zero/Zero W対応
写真や図解でよくわかる ラズパイZeroを使い倒す本 Raspberry Pi Zero/Zero W対応

本書ではRaspberry Pi Zero / Zero Wの概要から必要な周辺機器の説明、OSの導入やセットアップなどといった準備、そしてLinuxに初めて触れる人に向けてLinuxの基礎やシェルの操作などを解説しています。また、準備が整ったら実際に電子部品をRaspberry Pi Zero / Zero Wで制御する方法も解説しました。

KindleAmazon
Raspberry Pi ZeroによるIoT入門- Zero W 対応
Raspberry Pi ZeroによるIoT入門- Zero W 対応

本書は、大人から子供まで、初心者の方でも、ラズパイZeroとラズパイZero WをIoTのデバイスとして使いこなせるようになることを目的とした入門書です。また、ラズパイZeroとラズパイZero WをUSBケーブル1本でパソコンに接続できる便利な「Zero over USB」について、日本で初めて詳しく解説しました。

Amazon

Amazonでお得に購入するなら、Amazonギフト券がオススメ!

コンビニ・ATM・ネットバンキングで¥5,000以上チャージすると、プライム会員は最大2.5%ポイント、通常会員は最大2%ポイントがもらえます!
Amazonギフト券

あなたにおすすめ