Raspberry Pi 3 の SPI を用いてA/Dコンバーターからデータを読む python 関数 readadc() の各行の動作

教育用の覚え書き。ラズパイ3で標準のSPIバス(+RPi.GPIOライブラリ)を用いてA/DコンバーターICからセンサ値を読み出す解説記事に頻出する python 関数 readadc() の各行の動作を確認したのでメモを残す。output()でbit1個分のピンへのON/OFF信号の送出, input()でbit1個分のデータ読み出しを実行しているので、呼び出し時の関数の動作時間は多分、一定していない。コメントはA/DコンバーターとしてMCP3208を想定して書いている。

def readadc(adcnum, clockpin, mosipin, misopin, cspin):
# 宣言しておく import 文
# import RPi.GPIO as GPIO
# 入力(すべてint型)
# adcnum: データを読み出すMCP3208のチャネル番号 0, ..., 7
# clockpin: クロック(CLK)信号を出力するラズパイのピン番号
# misopin: Master In Slave Out のラズパイのピン番号
# mosipin: Master Out Slave In のラズパイのピン番号
# cspin: Channel Selector のラズパイのピン番号
# 戻り値
# int 型, 0, 1, 2, ..., 4095: adcnum チャネルのA/D変換値
# -1: adcnum が 0, ..., 7 の範囲にない

# 読み出しチャンネル番号が 0, ..., 7 に無ければ処理しない
if adcnum > 7 or adcnum < 0:
return -1

# CLK への出力を0に初期化しておく
# この関数では CLK のパルス生成を GPIO.output(*,1),
# GPIO.output(*,0) の連続呼出で実行している
# CLKパルスの立ち下がりのタイミングで1bitの入出力が起こる
#
GPIO.output(cspin, GPIO.HIGH) # I/Oが起きないようにCSを1にする
GPIO.output(clockpin, GPIO.LOW) # CLK を0に設定しておく
GPIO.output(cspin, GPIO.LOW) # I/Oを始めるためにCSを0にする

# この3行は commandout に MSB IN のデータ(8bitで上位5bitがデータ)を
# 作成する部分(秋月の MCP3204/3208 のPDFの p.20, Fig. 5-1.)
#
# タイミングチャートの3行目の D_IN に書いてある
#
# [Start][SGL/DIFF][D2][D1][D0]
# | | | | |
# | | 読み出しチャンネルの指定
# | 0:差動入力
# | 1:シングル・エンド
# 1:スタートビット
#
# を作成する
commandout = adcnum # [ 0][ 0][ 0][ 0][ 0][D2][D1][D0] adcnum
commandout |= 0x18 # [ 0][ 0][ 0][ 1][ 1][D2][D1][D0] 0x18と論理和
commandout <<= 3 # [ 1][ 1][D2][D1][D0][ 0][ 0][ 0] 3bitシフト

# このループは作成した commandout の上位5bitを1bitずつ送り込むループ
for i in range(5):

# commandout の最上位1bitが1ならば、MOSIに1を送り込み、
# さもなければ、MOSIに0を送り込む
if commandout & 0x80:
GPIO.output(mosipin, GPIO.HIGH)
else:
GPIO.output(mosipin, GPIO.LOW)

# MOSIに送り込んだら commandout の内容を1bitずらす
commandout <<= 1

# CLKに1パルス送り込む
GPIO.output(clockpin, GPIO.HIGH)
GPIO.output(clockpin, GPIO.LOW)

# 出力データ adcout を1bitずつ読み込むループ
adcout = 0 # データ保存変数 adcout の初期化
for i in range(13): # 全部で 13 bit 読み込む

# A/Dコンバーターの CLK に1パルス送り込む
GPIO.output(clockpin, GPIO.HIGH)
GPIO.output(clockpin, GPIO.LOW)

# adcoutを1bit左にずらして、最下位bitに空きを作る
adcout <<= 1

# もし MISO に1が入っていたら、adcout の最下位bitに1を書き込む
if i>0 and GPIO.input(misopin)==GPIO.HIGH:
adcout |= 0x1

# データ通信が終わったら CS をHIGHにして通信を止める
GPIO.output(cspin, GPIO.HIGH)

# 読み出しデータを呼び出し側に戻す
return adcout

複数のGPIOピンからのpwm信号出力が欲しいがゆえにpigpioライブラリをインストールしたが、ライブラリ中のSPIの関数pigpio.spi_*()たちの引数の与え方が分からずに、このコードをpigpio.read()/write()を使って書き換えた関数を使ってセンサ値を読み出しているのは内緒。

[2018.10.29追記] pigpio の spi_open(), spi_xfer() の用法が分かった:pigpio ライブラリの spi_xfer() 関数を用いて Raspberry Pi 3B から SPI 接続のA/Dコンバータ MCP3208 を利用する - あらきけいすけのメモ帳