電子工作初心者です。
Raspberry PiでADコンバータを利用したくて、Raspberry PiにA/DコンバータMCP3002をつなぐの記事を見ながらやってみたものの、応答が全部ゼロになってしまいます。どうやらspidevが正常に動作しなくなった模様。(~~~原因不明~~~ → 原因判明しました。再起動が必要だったようです。spidevを使った例はこちら)
そこで、いつも参考にしているカラー図解 最新 Raspberry Piで学ぶ電子工作を見てみると、spidevを使わずにGPIOの制御だけで値を取得していたので、これを参考にコードを書いてみることにしました。
ただし、本書ではMCP3208を使用しているので、一部MCP3002用に書き換える必要があります。
##使用機材
- Raspberry Pi 3 model B+
- MCP3002(ADコンバータ)
- 可変抵抗10kΩ
- ジャンパー線など
##MCP3208とMCP3002の違い
MCP3002 | MCP3208 | |
---|---|---|
チャンネル数 | 2 | 8 |
分解能 | 10bit | 12bit |
チャンネル数も分解能も違うので、Pythonコードでは入力も出力も変える必要があることが想像できます。そこでデータシートで詳しく見ていきます。
###入力
MCP3002もMCP3208と似ていますが、チャンネル数が違うので多少差があります。
####MCP3208
- Start
- SGL/DIFF : SINGLE ENDED MODEならば1、DIFFERENTIAL MODERNITYならば0。
- D2
- D1
- D0
の5ビットで入力します。D2、D1、D0でチャンネル番号を指定します。
SINGLEモードでCH0からデータを取るなら0d11000を送信すれば良さそうです。
####MCP3002
- Start
- SGL/DIFF
- ODD/SIGN : アナログ値を取りたいチャネルを指定。
- MSBF
の4ビットで入力します。チャンネル数が2つになるのでその分少なくなりました。
最後のMSBFは出力するフォーマットの設定。1ならばMSB、0ならばLSB。今回は単純そうなMSBを選んでおくことにします。
SINGLEモードでCH0からデータを取るなら0d1101を送信すれば良さそうです。
###出力
分解能が異なるので、出力されるビット数が異なります。
####MCP3208
分解能が12ビットで、最初にNullがつくので、合計13ビット読む必要があります。
取得された値は4096段階のデータになります。
####MCP3002
分解能が10ビットで、最初にNullがつくので、合計11ビット読む必要があります。
取得された値は1024段階のデータになります。
以上のことをふまえて、Pythonで書いてみました。
##Pythonコード
import RPi.GPIO as GPIO
import time
#MCP3002からSPI通信で10ビットのデジタル値を取得。0から1の2チャンネル利用可。
def readadc(adcnum, clockpin, mosipin, misopin, cspin):
if adcnum > 1 or adcnum < 0:
return -1
GPIO.output(cspin, GPIO.HIGH) #CSをONにする
GPIO.output(clockpin, GPIO.LOW) #SCSKをONにする
GPIO.output(cspin, GPIO.LOW) #CSをOFFにする。これで通信がスタートする。
#commandout = 0b01101000
commandout = adcnum #データを取得するCH番号
commandout |= 0x0d #SGL/DEF=1 および MSBフォーマット
commandout <<= 3 #LSBから8ビット目を送信するようにする
for i in range(4):
# LSBから数えて8ビット目から3ビット目までを送信
if commandout & 0x80:
GPIO.output(mosipin, GPIO.HIGH)
else:
GPIO.output(mosipin, GPIO.LOW)
commandout <<= 1
GPIO.output(clockpin, GPIO.HIGH)
GPIO.output(clockpin, GPIO.LOW)
adcout = 0
#11ビット読む(ヌルビット+10ビットデータ)
for i in range(11):
GPIO.output(clockpin, GPIO.HIGH)
GPIO.output(clockpin, GPIO.LOW)
adcout <<= 1
if i>0 and GPIO.input(misopin)==GPIO.HIGH:
adcout |= 0x1
GPIO.output(cspin, GPIO.HIGH)
return adcout
GPIO.setmode(GPIO.BCM)
# ピンの名前を変数として定義
SPICLK = 11
SPIMOSI = 10
SPIMISO = 9
SPICS = 8
# SPI通信用の入出力を定義
GPIO.setup(SPICLK, GPIO.OUT)
GPIO.setup(SPIMOSI, GPIO.OUT)
GPIO.setup(SPIMISO, GPIO.IN)
GPIO.setup(SPICS, GPIO.OUT)
vol_old = -1
try:
while True:
inputVal0 = readadc(0, SPICLK, SPIMOSI, SPIMISO, SPICS)
vol = "{0}%".format(int(inputVal0*100/1023)) #分解能が10ビットなので1023で割る
print(vol)
time.sleep(0.2)
except KeyboardInterrupt:
pass
GPIO.cleanup()
###実行結果
実行して抵抗値を変えると以下のように出力されます。
21%
21%
21%
29%
34%
34%
64%
71%
72%
71%
かなり手間はかかりましたが、仕組みがよくわかったので勉強になりました。
##参考
カラー図解 最新 Raspberry Piで学ぶ電子工作
5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (9) A-Dコンバータの利用6 MCP3002/MCP3008