5ドル!ラズパイ・ゼロ(Raspberry pi Zero)でIoT (37) A-Dコンバータの利用12 SPI ADS1120
■安定な基準電圧源を内蔵
ADS1120は、基準電源を内蔵した16ビットのA-Dコンバータです。50/60Hzのノッチ・フィルタが内蔵されているので、商用電源による誘導性ノイズに強くなっています。センサなどの接続が切れていることを感知したり、センサへの電流供給量の制御もできます。
●ADS1120の主なスペック
- ビット数;16
- チャネル数;ディファレンシャル2チャネルもしくはシングルエンド4チャネル
- PGA;低雑音、増幅度×1~×128
- 基準電圧;内蔵。2.048V ドリフト5ppm/℃
- 電源電圧; 2.3~5.5V
- 消費電流;動作時120uA
- 変換レート;20SPS(ターボ・モード2000SPS)
- インターフェース;SPI(最大6.6MHz)
- 温度計を内蔵 確度0.5℃
●DIP化
TSSOPパッケージのADS1120をDigi-Keyから購入しました。リード線のピッチは0.65mmの12ピンです。秋月電子通商のDIP変換ボードにはんだ付けしてから利用しました。はんだ付けは、こちらを参照ください。
マニュアルに従って、AVdd、DVdd端子からは0.1uFの積層セラミック・コンデンサをGND端子に接続しました。
●接続
ラズパイとは次のように接続しました。SPIバス0の信号端子は使っていません。
ラズパイのGPIO 接続先 |
ADS1120 | ラズパイのGPIO 接続先 |
|||
---|---|---|---|---|---|
GPIO20 | SCLK | 1番ピン | 16 | Din | GPIO12(MOSI) |
GPIO21 | /CS | 2 | 15 | Dout | GPIO16(MISO) |
6番 GND | CLK | 3 | 14 | /DRDY | GPIO26 |
6番 GND | DGND | 4 | 13 | DVdd | 1番 3.3V |
6番 GND | AVss | 5 | 12 | AVdd | 1番 3.3V |
- | Ain3 | 6 | 11 | Ain0 | - |
- | Ain2 | 7 | 10 | Ain1 | - |
- | REFm0 | 8 | 9 | REFp0 | - |
●読み書きのタイミング
SPIバスでは、チップ・セレクト信号をLowにした後、マスタがデータを送るときにクロックがでます。そのクロックに従って、スレーブ・デバイスがデータを送り出します。マスタがスレーブに送るデータやコマンドがなくても、何かしら送ってクロックを発生させないと、データを受け取れません。
ADS1120では、それらの信号以外にDRDYというデータ・レディが追加されているので、ラズパイのsmbusライブラリでは対応できません。SPIインターフェースでは、クロックの幅やディーティ、一連のやり取りで同じクロック幅であることが規定されていません。したがって、GPIOを直接ソフトウェアでHIGH/LOWすることでクロックを作り対応します。
既存のSPIの19番から24番付近のインターフェース端子ではうまく動作しなかったので、GPIOの40ピン付近にある何も使われていない端子を割り当てました。
PGA=x1のときの読み出しデータです。標準的な2の補数形式で、最上位ビットが符号です。
-2.048V | -2.048/2^15 | 0V | 2.048/2^15 | 2.048V |
---|---|---|---|---|
8000h | fffh | 0h | 0001h | 7fffh |
●レジスタ
コマンド | 内容 | コマンド・バイト |
---|---|---|
RESET | リセット | 0000 011x |
START/SYNC | ワンショットの変換スタート | 0000 100x |
Powerdown | パワーダウン・モード | 0000 001x |
RDATA | データ読み出し |
0001 xxxx |
RREG | レジスタの読み出し | 0010 rrnn |
WREG | レジスタの書き込み | 0100 rrnn |
xx;何でもよい。rr;スタート・レジスタ、nn;数量-1 |
◆設定関係
コンフィギュレーション・レジスタ0(オフセット=0x00、デフォルト0x00)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
MUX[3:0] | GAIN[2:0] | PGA_BYPASS | |||||
R/W-0h | R/W-0h | R/W-0h |
※ R/W = Read/Write; -n = パワー・オン・リセット時の値 |
詳細
ビット | 項目 | R/W | 初期値 | 詳細 |
---|---|---|---|---|
7:4 | MUX[3:0] | R/W | 0h | 入力選択 0000 : AINP = AIN0, AINN = AIN1 (default) 0001 : AINP = AIN0, AINN = AIN2 0010 : AINP = AIN0, AINN = AIN3 0011 : AINP = AIN1, AINN = AIN2 0100 : AINP = AIN1, AINN = AIN3 0101 : AINP = AIN2, AINN = AIN3 0110 : AINP = AIN1, AINN = AIN0 0111 : AINP = AIN3, AINN = AIN2 1000 : AINP = AIN0, AINN = AVSS 1001 : AINP = AIN1, AINN = AVSS 1010 : AINP = AIN2, AINN = AVSS 1011 : AINP = AIN3, AINN = AVSS 1100 : (V(REFPx) – V(REFNx)) / 4 monitor (PGA bypassed) 1101 : (AVDD – AVSS) / 4 monitor (PGA bypassed) 1110 : AINP and AINN shorted to (AVDD + AVSS) / 2 1111 : Reserved |
3:1 | GAIN[2:0] | R/W | 0h | PGAゲイン 000 : Gain = 1 (default) 001 : Gain = 2 010 : Gain = 4 011 : Gain = 8 100 : Gain = 16 101 : Gain = 32 110 : Gain = 64 111 : Gain = 128 |
0 | PGA_BYPASS | R/W | 0h | PGAをバイパスするか 0 : PGA enabled (default) 1 : PGA disabled and bypassed |
コンフィギュレーション・レジスタ1(オフセット=0x01、デフォルト0x00)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
DR[2:0] | MODE[1:0] | CM | TS | BCS | |||
R/W-0h | R/W-0h | R/W-0h | R/W-0h | R/W-0h |
※ R/W = Read/Write; -n = パワー・オン・リセット時の値 |
詳細
ビット | 項目 | R/W | 初期値 | 詳細 |
---|---|---|---|---|
7:5 | DR[2:0] | R/W | 0h | データ・レート 次のDR表を参照 |
4:3 | MODE[1:0] | R/W | 0h | オペレーティング・モード 00 : Normal mode (256-kHz modulator clock, default) 01 : Duty-cycle mode (internal duty cycle of 1:4) 10 : Turbo mode (512-kHz modulator clock) 11 : Reserved |
2 | CM | R/W | 0h | 変換モード 0 : Single-shot mode (default) 1 : Continuous conversion mode |
1 | TS | R/W | 0h | 温度センサ 0 : Disables temperature sensor (default) 1 : Enables temperature sensor |
0 | BCS | R/W | 0h | センサのバーンアウト電流検出ソース 0 : Current sources off (default) 1 : Current sources on |
DR(データ・レート)ビット
通常モード | デューティ・サイクル・モード | ターボ・モード |
---|---|---|
000 = 20 SPS | 000 = 5 SPS | 000 = 40 SPS |
001 = 45 SPS | 001 = 11.25 SPS | 001 = 90 SPS |
010 = 90 SPS | 010 = 22.5 SPS | 010 = 180 SPS |
011 = 175 SPS | 011 = 44 SPS | 011 = 350 SPS |
100 = 330 SPS | 100 = 82.5 SPS | 100 = 660 SPS |
101 = 600 SPS | 101 = 150 SPS | 101 = 1200 SPS |
110 = 1000 SPS | 110 = 250 SPS | 110 = 2000 SPS |
111 = Reserved | 111 = Reserved | 111 = Reserved |
提供されるデータレートは、内部オシレータまたは外部の4.096MHzクロックを使用して計算される。 4.096MHz以外の外部クロックを使用する場合、外部クロック周波数に比例してデータレートは変化する。 |
コンフィギュレーション・レジスタ2(オフセット=0x02、デフォルト0x00)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
VREF[1:0] | 50/60[1:0] | PSW | IDAC[2:0] | ||||
R/W-0h | R/W-0h | R/W-0h | R/W-0h |
※ R/W = Read/Write; -n = パワー・オン・リセット時の値 |
詳細
ビット | 項目 | R/W | 初期値 | 詳細 |
---|---|---|---|---|
7:6 | VREF[1:0] | R/W | 0h | 基準電圧源の選択 00 : Internal 2.048-V reference selected (default) 01 : External reference selected using dedicated REFP0 and REFN0 inputs 10 : External reference selected using AIN0/REFP1 and AIN3/REFN1 inputs 11 : Analog supply (AVDD – AVSS) used as reference |
5:4 | 50/60[1:0] | R/W | 0h | 50/60Hzフィルタ 20-SPSと5-SPS のデューティ・サイクル・モード以外では 00 に。 00 : No 50-Hz or 60-Hz rejection (default) 01 : Simultaneous 50-Hz and 60-Hz rejection 10 : 50-Hz rejection only 11 : 60-Hz rejection only |
3 | PSW | R/W | 0h | 低雑音パワー・スイッチ AIN3/REFN1 とAVSSのロー・サイド・スイッチ。 0 : Switch is always open (default) 1 : Switch automatically closes when the START/SYNC command is sent and opens when the POWERDOWN command is issued |
2:0 | IDAC[2:0] | R/W | 0h | IDAC電流 センサに供給する定電流源 IDAC1 と IDAC2 の電流設定。 000 : Off (default) 001 : Reserved 010 : 50 µA 011 : 100 µA 100 : 250 µA 101 : 500 µA 110 : 1000 µA 111 : 1500 µA |
コンフィギュレーション・レジスタ3(オフセット=0x03、デフォルト0x00)
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
I1MUX[2:0] | I2MUX[2:0] | DRDYM | 0 | ||||
R/W-0h | R/W-0h | R/W-0h | R/W-0h |
※ R/W = Read/Write; -n = パワー・オン・リセット時の値 |
詳細
ビット | 項目 | R/W | 初期値 | 詳細 |
---|---|---|---|---|
7:5 | I1MUX[2:0] | R/W | 0h | IDAC1接続先 000 : IDAC1 disabled (default) 001 : IDAC1 connected to AIN0/REFP1 010 : IDAC1 connected to AIN1 011 : IDAC1 connected to AIN2 100 : IDAC1 connected to AIN3/REFN1 101 : IDAC1 connected to REFP0 110 : IDAC1 connected to REFN0 111 : Reserved |
4:2 | I2MUX[2:0] | R/W | 0h | IDAC2接続先 000 : IDAC2 disabled (default) 001 : IDAC2 connected to AIN0/REFP1 010 : IDAC2 connected to AIN1 011 : IDAC2 connected to AIN2 100 : IDAC2 connected to AIN3/REFN1 101 : IDAC2 connected to REFP0 110 : IDAC2 connected to REFN0 111 : Reserved |
1 | DRDYM | R/W | 0h | DRDYモード 新しく変換データが用意できたかを示す。 0 : Only the dedicated DRDY pin is used to indicate when data are ready (default) 1 : Data ready is indicated simultaneously on DOUT/DRDY and DRDY |
0 | 予約 | R/W | 0h | 予約 常に 0 |
◆出力レジスタ(推測)
b15 | b14 | b13 | b12 | b11 | b10 | b9 | b8 |
D15 | D14 | D13 | D12 | D11 | D10 | D9 | D8 |
b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
●プログラム
デフォルト設定のままデータを読み出します。Startデータを書き込んだ後、データが用意できるとDRDYをLowからHighにし、データが読み取れる状態になります。アナログ信号はAin0とAin1のディファレンシャル入力です。ここに単4電池をつなぎました。
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
Vref = 2.048
SPI_CLK = 20
SPI_MISO = 16
SPI_MOSI = 12
SPI_CS = 21
DRDY = 26
def init():
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO
GPIO.setup(SPI_MOSI, GPIO.OUT)
GPIO.setup(SPI_MISO, GPIO.IN)
GPIO.setup(SPI_CLK, GPIO.OUT)
GPIO.setup(SPI_CS, GPIO.OUT, initial = GPIO.HIGH)
GPIO.setup(DRDY,GPIO.IN)
print "Done"
def readADC():
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
while GPIO.input(DRDY) == 1 :
continue
data = 0
for i in range(16): # Read 16 bits
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse UP
data <<= 1 # Shift left
if GPIO.input(SPI_MISO): # If high
data |= 0x1
GPIO.output(SPI_CLK, GPIO.LOW) # Clock pulse DOWN
return data
def startADC():
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
data = '00001000'
for i in list(data) :
if i=='0' :
clk_low()
else:
clk_high()
print ("Start ")
def clk_high() :
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse
GPIO.output(SPI_MOSI,GPIO.HIGH) # Dout High
GPIO.output(SPI_CLK, GPIO.LOW)
GPIO.output(SPI_MOSI,GPIO.LOW)
def clk_low() :
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse
GPIO.output(SPI_MOSI,GPIO.LOW) # Dout Low
GPIO.output(SPI_CLK, GPIO.LOW)
def resetADC():
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
GPIO.output(SPI_CS, GPIO.LOW) # Enable chip
data = '00000110'
for i in list(data) :
if i=='0' :
clk_low()
else:
clk_high()
print ("reset ")
GPIO.output(SPI_CS, GPIO.HIGH) # Disable chip
def sign16(x):
return ( -(x & 0b1000000000000000) |
(x & 0b0111111111111111) )
#main
init()
resetADC()
try :
while 1:
GPIO.output(SPI_CS, GPIO.LOW) # Enable chip
startADC()
data = readADC()
GPIO.output(SPI_CS, GPIO.HIGH) # Disable chip
print round((Vref * (sign16(int(hex(data),16))) / 32767.0),5),"V "
time.sleep(1)
except :
GPIO.cleanup()
GPIO.cleanup()
タイミングを確認しながらプログラムを作りました。
●実行結果
Ain0に電池のマイナス電極をつないだときの測定値です。
●移動平均
シングルエンド入力に変更し、9回もしくは10回の移動平均で表示データを安定にしました。
#!/usr/bin/env python
import RPi.GPIO as GPIO
import time
Vref = 2.048
SPI_CLK = 20
SPI_MISO = 16
SPI_MOSI = 12
SPI_CS = 21
DRDY = 26
def init():
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM) # GPIO
GPIO.setup(SPI_MOSI, GPIO.OUT)
GPIO.setup(SPI_MISO, GPIO.IN)
GPIO.setup(SPI_CLK, GPIO.OUT)
GPIO.setup(SPI_CS, GPIO.OUT, initial = GPIO.HIGH)
GPIO.setup(DRDY,GPIO.IN)
print "Done"
def readADC():
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
while GPIO.input(DRDY) == 1 :
continue
data = 0
for i in range(16): # Read 16 bits
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse UP
data <<= 1 # Shift left
if GPIO.input(SPI_MISO): # If high
data |= 0x1
GPIO.output(SPI_CLK, GPIO.LOW) # Clock pulse DOWN
return data
def startADC():
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
data = '00001000'
for i in list(data) :
if i=='0' :
clk_low()
else:
clk_high()
print ("Start ")
def clk_high() :
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse
GPIO.output(SPI_MOSI,GPIO.HIGH) # Dout High
GPIO.output(SPI_CLK, GPIO.LOW)
GPIO.output(SPI_MOSI,GPIO.LOW)
def clk_low() :
GPIO.output(SPI_CLK, GPIO.HIGH) # Clock pulse
GPIO.output(SPI_MOSI,GPIO.LOW) # Dout Low
GPIO.output(SPI_CLK, GPIO.LOW)
def writeADC(data):
GPIO.output(SPI_CLK, GPIO.LOW) # Start with clock low
GPIO.output(SPI_CS, GPIO.LOW) # Enable chip
for i in list(data) :
if i=='0' :
clk_low()
else:
clk_high()
GPIO.output(SPI_CS, GPIO.HIGH) # Disable chip
def sign16(x):
return ( -(x & 0b1000000000000000) |
(x & 0b0111111111111111) )
#main
init()
writeADC('00000110') #reset
#writeADC('0100000000000010') #PGA x2
writeADC('0100000010000000') #Ain0 singleEnd input
try :
datas=[]
while 1:
GPIO.output(SPI_CS, GPIO.LOW) # Enable chip select
startADC()
if len(datas)<10 :
datas.append(readADC())
else:
del datas[0]
idou = sum(datas)/len(datas)
GPIO.output(SPI_CS, GPIO.HIGH) # Disable chip select
print round((Vref * (sign16(int(hex(idou),16))) / 32767.0),5),"V "
time.sleep(1)
except :
print ("error")
GPIO.cleanup()
GPIO.cleanup()
実行例です。
※ソフトウェアSPI 参考URL http://www.python-exemplary.com/index_en.php?inhalt_links=navigation_en.inc.php&inhalt_mitte=raspi/en/adc.inc.php
※執筆時点;2017-11-29版をダウンロードし、sudo apt-get update と sudo apt-get upgrade -y および sudo rpi-update で更新し、カーネルは、uname -a で確認。4.9.67でした。
※プログラムを仮にADS1120.pyと/home/piに保存すると、sudo chmod 755 ADS1120.py で実行権を付け、ターミナルから、python ADS1120.pyで実行します。I2CやSPIのグループにpiユーザが属しているので、sudoは不要です。
プログラム・リストは、表示の関係でTabキーが無視されるので、スペースに代えてあります。また、リスト中を2回クリックすると全選択になるので、CTRL-Cでコピーし、テキスト・エディタにCTRL-Vで貼り付けて利用してください。ラズパイに持っていくと、リターン・コードなどが化けていることがあるので、一度消して、ラズパイのテキスト・エディタで改行してください。
※SPIは無効化しています。この説明を参照ください。
※電圧発生器はアドバンテストTR6142、DMMはケースレー2000、4チャネル・オシロスコープはレクロイLT224、2チャネル・オシロスコープはPicoScope 5242Bを使いました。