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を使いました。