TOPに戻る

ラズパイ5 pythonの仮想環境 ⑧ 12ビットA-DコンバータADS1015<その1>

 ラズパイにはアナログ入力はありません。I2Cバスに接続して利用できるAdafruitの12ビットA-DコンバータADS1015を利用します。

 Adafruitのボード解説ページ

 Stemma QT/Qwiic(JST SH 4ピン)コネクタは2か所に装着されていて、どちらにつないでもかまいません。このコネクタを使ってI2Cで制御する場合、特に、ジャンパ線をつなぐなどは不要です。

 コネクタは、表と裏のどちらも差し込めそうですが、ピンが内部の上部に並んでいるので、差し込める方向は一意です。ロック機構はないですが、すぐに抜けるということはありません。

ADS1015の主なスペック

  •  解像度 12ビット
  •  基準電圧 内蔵
  •  電源電圧 2.0~5.5V
  •  入力 差動(A0-A1、A0-A3、A1-A3、A2-A3)、シングル(A0、A1、A2、A3とGND)
  •  増幅器 プログラマブル前置アンプ内蔵(PGA)フルスケール;±6.144V、±4.096V、±2.048V、±1.024V、±0.512V、±0.256V
  •  変換方式 ΔΣ(デルタ・シグマ)方式
  •  変換速度 128、250 、490 、920、1600 、2400 、3300、3300SPS
  •  インターフェース I2C(0kHz~3.4MHz)
  •  I2Cのスレーブ・アドレス デフォルト0x48、A0ピンをショートすると0x49(0x4a、0x4bも設定可能)

環境

  • ハードウェア Raspberry Pi 5(4GBモデル)
  • OS Raspberry Pi OS (64ビット)、リリース日December 5th 2023
  • Windows10 22H2にて、ssh(OpenSSH_9.2p1 Debian-2+deb12u2, OpenSSL 3.0.11 19 Sep 2023)および、VNC Viewerを動作させている

接続

仮想環境に入る

 このA-Dコンバータを動かすための専用の仮想環境encadcを作ります。

yoshi@ras05:~ $ python -m venv envadc
yoshi@ras05:~ $ source envadc/bin/activate


(envadc) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
pip        23.0.1
setuptools 66.1.1
(envadc) yoshi@ras05:~ $

pythonのADS1015ライブラリをインストール

(envadc) yoshi@ras05:~ $ pip install ads1015
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting ads1015
  Downloading https://www.piwheels.org/simple/ads1015/ads1015-1.0.0-py3-none-any.whl (9.0 kB)
Collecting i2cdevice>=1.0.0
  Downloading https://www.piwheels.org/simple/i2cdevice/i2cdevice-1.0.0-py3-none-any.whl (10 kB)
Collecting smbus2
  Using cached https://www.piwheels.org/simple/smbus2/smbus2-0.4.3-py2.py3-none-any.whl (11 kB)
Installing collected packages: smbus2, i2cdevice, ads1015
Successfully installed ads1015-1.0.0 i2cdevice-1.0.0 smbus2-0.4.3


(envadc) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
ads1015    1.0.0
i2cdevice  1.0.0
pip        23.0.1
setuptools 66.1.1
smbus2     0.4.3

 ADS1015のライブラリがありました。

  ADS1015 4 channel differential/single-ended ADC

 デバイス自体は古いのですが、4か月前にGitHubで更新されているので、放置されてはいないようです。

サンプル・プログラムの実行

 GitHub ads1015-python/examplesにあるread-all.py をads1015.pyの名称で保存し実行しました。

(envadc) yoshi@ras05:~ $ python ads1015.py
Traceback (most recent call last):
  File "/home/yoshi/ads1015.py", line 3, in <module>
    from ads1015 import ADS1015
  File "/home/yoshi/ads1015.py", line 3, in <module>
    from ads1015 import ADS1015
ImportError: cannot import name 'ADS1015' from partially initialized module 'ads1015' (most likely due to a circular import) (/home/yoshi/ads1015.py)

 自分自身を import して循環してしまったというエラーのようです。ファイル名をads1015a.pyに変更して実行しました。


(envadc) yoshi@ras05:~ $ python ads1015a.py
read-all.py - read all three inputs of the ADC

Press Ctrl+C to exit!

Found: ADS1015
Reference voltage:  0.295v 

in0/ref:  0.295v
in1/ref:  0.295v
in2/ref:  0.295v

 A0-GNDをショートしました。

in0/ref: -5.981v
in1/ref: -0.382v
in2/ref:  0.313v

in0/ref: -6.065v
in1/ref:  0.439v
in2/ref:  0.292v

 0Vであるはずですが、正しく読み出せていないようです。

pythonのsmbus2ライブラリを使う

 先ほどのADS1015ライブラリを削除します。

(envadc) yoshi@ras05:~ $ pip uninstall ads1015
Found existing installation: ads1015 1.0.0
Uninstalling ads1015-1.0.0:
  Would remove:
    /home/yoshi/envadc/lib/python3.11/site-packages/CHANGELOG.md
    /home/yoshi/envadc/lib/python3.11/site-packages/LICENSE
    /home/yoshi/envadc/lib/python3.11/site-packages/README.md
    /home/yoshi/envadc/lib/python3.11/site-packages/ads1015-1.0.0.dist-info/*
    /home/yoshi/envadc/lib/python3.11/site-packages/ads1015/*
Proceed (Y/n)? Y
  Successfully uninstalled ads1015-1.0.0


(envadc) yoshi@ras05:~ $ pip list
Package    Version
---------- -------
i2cdevice  1.0.0
pip        23.0.1
setuptools 66.1.1
smbus2     0.4.3

 smbus2は残されたようです。

 プログラムです。デバッグのためにprint文がたくさん入っています。オシロスコープで波形を常時出したいので、初期設定もループの中に入れてあります。

 i2c_rdwrは、combined write/read transactions with repeated startです。ADS1015は、リピーテッド・スタート・コンディションを要求しているデバイスです。

  ループの際ののブロックでは、Config Register(0x01)に設定値を書き込んでいます。二番目のブロックは、そのConfig Registerを読み出しています。

 3番目のブロックは、Conversion Register(0)を読み出し、16ビットのデータを2の補数の処理sign16(x)をし、12ビットのデータを取り出し(raw_s >> 4)、今回は、フルスケールを±6.144 Vに設定したので乗じて、11ビット(2^11=2048)で割ることで、電圧を計算しています。

各種設定をするコンフィグ・レジスタ

 プログラムの最初で、コンフィグ・レジスタ(Config Register)を設定します。アドレス・ポインタ・レジスタは0x01です。

  • bit1 OS。'1'で変換開始。デフォルト1
  • bit14、bi13、bit12 チャネル・セレクト。
    000 : AINP = AIN0 and AINN = AIN1 (デフォルト)
    001 : AINP = AIN0 and AINN = AIN3
    010 : AINP = AIN1 and AINN = AIN3
    011 : AINP = AIN2 and AINN = AIN3
    100 : AINP = AIN0 and AINN = GND
    101 : AINP = AIN1 and AINN = GND
    110 : AINP = AIN2 and AINN = GND
    111 : AINP = AIN3 and AINN = GND
  • bit11、bit10、bit9 PGA。
    000 : FSR = ±6.144 V
    001 : FSR = ±4.096 V
    010 : FSR = ±2.048 V (デフォルト)
    011 : FSR = ±1.024 V
    100 : FSR = ±0.512 V
    101 : FSR = ±0.256 V
    110 : FSR = ±0.256 V
    111 : FSR = ±0.256 V
  • bit8 MODE。0;連続、1;ワンショット(デフォルト)
  • bit7、bit6、bit5 サンプルレート。
    000 : 128 SPS
    001 : 250 SPS
    010 : 490 SPS
    011 : 920 SPS
    100 : 1600 SPS (デフォルト)
    101 : 2400 SPS
    110 : 3300 SPS
    111 : 3300 SPS
  • bit4 COMP_MODE;コンパレータ。 0
  • bit3 COMP_POL ;コンパレータ。0
  • bit2 COMP_LAT ;コンパレータ。0
  • bit1、bit0 COMP_QUE[1:0]  ;コンパレータ。11

from smbus2 import SMBus, i2c_msg
import time

addr = 0x48

def sign16(x):
    return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))

print('start test')

while 1:

    with SMBus(1) as bus:
        msg = i2c_msg.write(addr, [1,0x80,0x83])
        bus.i2c_rdwr(msg)

    write = i2c_msg.write(addr, [1])
    read = i2c_msg.read(addr, 2)
    with SMBus(1) as bus:
        bus.i2c_rdwr(write, read)
        print(list(read))

    write = i2c_msg.write(addr, [0])
    read = i2c_msg.read(addr, 2)
    with SMBus(1) as bus:
        bus.i2c_rdwr(write, read)
        rawdata = list(read)[0] * 256 + (list(read)[1] & 0xf0)
        print(hex(rawdata))
        raw_s = sign16(int(hex(rawdata), 16))
        print(hex(raw_s))
        voltage = (raw_s >> 4) * 6.144 / 2048
        print(voltage)
    time.sleep(1)

入力に電圧源をつなぐ

 基準電圧源TL431を電池で駆動した電源をA0-A1につなぎます。テスタで測ると2.49Vでした。

 

 実行中のターミナルの様子です。2.49xVが電源のA0-A1間の電圧です。

関数化した

from smbus2 import SMBus, i2c_msg
import time

addr = 0x48
FC = 6.144

i2c = SMBus(1)
i2c.write_i2c_block_data(addr, 1,[0x80,0x83])

def sign16(x):
    return (-(x & 0b1000000000000000) | (x & 0b0111111111111111))

def readVoltage():
    write = i2c_msg.write(addr, [0])
    read  = i2c_msg.read(addr, 2)
    with SMBus(1) as bus:
        bus.i2c_rdwr(write, read)
        rawdata = list(read)[0] * 256 + (list(read)[1] & 0xf0)
        raw_s = sign16(rawdata)
        return (raw_s >> 4) * FC / 2048

# main
print('start test')

while 1:
    voltage = readVoltage()
    print(round(voltage,3))
    time.sleep(1)

◆入力を、A0端子とGND間のシングルエンドに変更;100 : AINP = AIN0 and AINN = GND 

 コンフィグ・レジスタ(Config Register)を設定内容[0x80,0x83]を[0xc0,0x83])に変更し、A0端子をGNDへショートしました。0Vです。

 A0端子をVin端子につなぎました。3.3Vです。

-