#概要
7ch 12bits ADコンバータICであるMCP3208を、RaspberryPiとPython、ボリューム抵抗を用いて利用する方法は、各種書籍に例がありますがデータシートと突き合わせてみて分からない事が多かったので、データシートを読み込み、分かった事を記事にしてみました。
- 雑ですみません、ワイヤに隠れてしまっていますが左下のDIP ICがMCP3208で、Raspberry Piと接続しています。SPICLK⇔CLK、SPIMOSI⇔DIN、SPIMISO⇔DOUT、SPICE0⇔CS
- MCP3208のCH0には3.3Vを接続したボリューム抵抗を接続しています。
- 12bitなので4095階調、値が0なら0.0V、2048なら1.65V、4095なら3.3Vのように読み替えます。
#MCP3208制御の手順
-
基本的にSPIインタフェースで通信をするので、下図のように信号をやりとりすればAD変換ができます。初期ステートはCS=High、CLK=LOWです。CS=Lowにすると処理を開始できます。(CLKはHIGHでアイドル状態でも問題ありません)
-
MCP3208へのコマンド入力をすると、MCP3208はDout端子から12bitのデータを吐き出します。MCP3208は__コマンド入力はクロックの立ち上がりエッジで認識__、__データ出力はクロックの立下りエッジで出力される__ことを理解してデータシートを読むとわかりやすいです。(間違ってたら指摘お願いします)
-
まずは下図のMCU Transmitted Dataとある一番左の8bitデータを見ると、MSBから0が5つ並んでいます。次にスタートビット1、Single-endedモードの1、チャンネルを指定する3ビットの3ビット目(チャンネル0なら000なので0)が記載されており、クロックの立ち上がりで1ビットずつMCP3208へ入力がされていきます。※実は0が5つ並んでいるのは入力する必要がありません。データシートにはSPIは8ビット単位で通信する事が多いので0パディングするといいよとのことだけなので、各種書籍にはこの0をスルーしていますが、今回はこの図を忠実に再現してみます。
-
次に一つ右の8bitデータを見ると、チャンネル指定ビットの2ビット目と1ビット目をD1、D0に入れます。(チャンネル0なら000なので00)残りの6ビットは使いません。
-
0 5つを含めた10bitをMCP3208へ入力が完了タイミングは図のクロック10の数字文字の左の立ち上がりエッジになります。Doutから出力がはじまるのは、次の11クロック目の立下りエッジです。それから出力データ1+12ビットが出力されますが、最初の1ビットはnullビットなので対象外処理をする必要があります。
-
12ビットを受信し終わったらCSをHIGHにしてAD変換の1処理を完了します。
#ソース
上記制御手順と照らし合わせてみてください。説明の便宜上、まわりくどい記載になっているので、実用的なソースは各種書籍記載のソースを参考にしてください。
import RPi.GPIO as GPIO
from time import sleep
def initialize(): #制御手順1
GPIO.output(SPICE0,GPIO.HIGH) #このHigh、Lowで1クロックを再現
GPIO.output(SPICLK,GPIO.LOW)
def sendToMCP3208(): #制御手順3
GPIO.output(SPICE0,GPIO.LOW)
Transmitted_Data1 = 0b00000110 #10ビット中この8ビットを最初に入力する
for i in range(8):
if Transmitted_Data1 & 0b10000000: #MSBが1ならTrue
GPIO.output(SPIMOSI,GPIO.HIGH) #1をセット
else:
GPIO.output(SPIMOSI,GPIO.LOW)#0をセット
GPIO.output(SPICLK,GPIO.HIGH)#クロック出力して1bit入力
GPIO.output(SPICLK,GPIO.LOW)
Transmitted_Data1 = Transmitted_Data1 << 1 #1bit左シフトする事で、if判定時に次のビットを操作対象にできる
#制御手順4
Transmitted_Data2 = 0b00000000 #10bit中の2ビット、上位2ビットだけ使用
for i in range(2):
if Transmitted_Data2 & 0b10000000:
GPIO.output(SPIMOSI,GPIO.HIGH)
else:
GPIO.output(SPIMOSI,GPIO.LOW)
GPIO.output(SPICLK,GPIO.HIGH)
GPIO.output(SPICLK,GPIO.LOW)
Transmitted_Data2 = Transmitted_Data2 << 1
def receiveFromMCP3208(): #制御手順5
Received_Data = 0b000000000000 #戻り値の12bitデータ変数
for i in range(13): #iは0→12まで1ずつインクリメントされる
GPIO.output(SPICLK,GPIO.HIGH)
GPIO.output(SPICLK,GPIO.LOW)
if i == 0: #nullビットなので対象外処理
pass
elif i >=1 and i <= 11: #通常処理
Received_Data = Received_Data | GPIO.input(SPIMISO)
Received_Data = Received_Data << 1
elif i== 12: #最終ビットの場合左ビットシフトをしない
Received_Data = Received_Data | GPIO.input(SPIMISO)
return Received_Data
def close(): #制御手順6
GPIO.output(SPICE0,GPIO.HIGH)
SPICLK = 11
SPIMOSI = 10
SPIMISO = 9
SPICE0 = 8
GPIO.setmode(GPIO.BCM)
GPIO.setup(SPICLK,GPIO.OUT)
GPIO.setup(SPIMOSI,GPIO.OUT)
GPIO.setup(SPIMISO,GPIO.IN)
GPIO.setup(SPICE0,GPIO.OUT)
while True:
initialize()
sendToMCP3208()
value = receiveFromMCP3208()
print value
close()
sleep(0.2)
#ロジアナ計測
本ソース実行時の計測結果です。コンソール出力値は約2450。入力時はクロックの立ち上がりで00000110 00が入力されている事が分かりますね、8bitと2bitの間に時間がかかってます。出力時はクロックの立下りで100110010100、これは10進数で2452なのでコンソール出力値と当然ですがあっています。ボリューム抵抗の実測値は1.973Vだったので、4095*1.973V/3.3V≒2448でAD変換が問題なくできている事がわかりました。