4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【電子工作】A/Dコンバーター(MCP3002)でアナログ温度センサーを扱う

Last updated at Posted at 2023-08-13

■ 作るもの

アナログ温度センサーとA/Dコンバータを利用した室温測定器を作ってみる。

ソースコードはこちら

※ この記事の続きとして、測った温度を7セグ表示器に表示してみました。

■ 回路図

こんな感じの回路を作ってみた。
温度センサーのアナログ出力をA/Dコンバーターで0~1023のデジタル値に変換して、その値を使ってラズパイで温度を計算するしくみ。

fig_00.png

■ 利用する部品

MCP3002

10Bit A/Dコンバーター

データシート

基本情報

  • 分解能: 10bit
  • 電源電圧: 2.7V ~ 5V
  • SPI: モード0とモード3が利用可能
  • 最大サンプリングレート 200ksps(5V), 75ksps(2.7V)

ピン

VDD/VREF CLK DOUT DIN
 --------------------
| )  MCP3002        |
 --------------------
CS/SHDN  CH0 CH1  VSS
  1. CS/SHDN :ラズパイのCE0(CE1)端子に接続
    • 通信時にMCP3002からラズパイのCE0(CE1)端子をLOWに設定する
  2. CH0: アナログデバイスの出力端子に接続
    • アナログデバイスの出力電圧を受け取る
  3. CH1: アナログデバイスの出力端子に接続
    • アナログデバイスの出力電圧を受け取る
  4. VSS: ラズパイのGND端子に接続
  5. DIN: ラズパイのMISO(Master In Slave Out)端子に接続
    • ラズパイからMCP3002へのデータ入力
  6. DOUT: ラズパイのMOSI(Master Out Slave In)端子に接続
    • MCP3002からラズパイへのデータ入力
  7. CLK: ラズパイのSCKL(シリアルクロック)端子に接続
    • 通信するデバイス同士のタイミング合わせで利用
  8. VDD/VREF: ラズパイの3.3V端子に接続
    • 電源/基準電圧入力 (2.7V~5.5V)

データの送受信

送信するデータは2バイトで、受信するデータも2バイト。受信データはMSBファーストフォーマットで受け取ることができる。(オプションでLSBファーストフォーマットでの受け取りも可能らしい。)
今回はMSBファーストフォーマットでのみデータを受信する方式を利用する。

データシートを確認すると、データの送受信の図があったので見方をざっくり調べてみた。

  • CS: ラズパイのCE0(CE1)端子(MCP3002のCS/SHDN端子に接続されている)の電位。LOWに設定するのが通信開始の合図。
  • CLK: クロック。この図だとアイドルクロックはLOWになっている。(後述のSPIモードを調べるときに重要になってくる)
  • DIN: ラズパイ -> A/Dコンバータに送信するデータ (bit単位)
  • DOUT: A/Dコンバータ -> ラズパイに送信するデータ (bit単位)

fig_01.png

送信データ

送信データは2バイトで、上位5bitにオプションを設定する。

fig_02.png

  1. 固定で0
  2. スタートビット: 1 (固定)
  3. SGL/DIFF: 動作モードを選択
    • 0: 擬似差動モード (よくわからん)
    • 1: シングルエンドモード (とりあえず今回はこっち)
  4. ODD/SIGN: シングルエンドモードで使用されるチャンネルを選択。
    • 0: CH0 (CH0端子にアナログデバイスを接続している場合はこちら)
    • 1: CH1 (CH1端子にアナログデバイスを接続している場合はこちら)
  5. MSBF: 受信データのフォーマットを選択。
    • 0: MCP3002はMSBファーストフォーマット(最上位ビットから)で送信
    • 1: MCP3002はMSBファーストフォーマットで送信した後、LSBファーストフォーマット(最下位ビットから)に変換された値を送信
  6. 以降は何でも良い

受信データ

受信データは2バイトで、下位10bitが値(アナログデバイスの出力電圧 / VREF * 1023)となる。

fig_03.png

LM61CIZ

温度センサー

データシート

基本情報

  • 動作温度範囲: -30℃ ~ 100℃
  • 電源電圧: 2.7V ~ 10V
  • 消費電力: 125μA (最大値)
  • 出力電圧: 600mVを0℃として、1℃あたり10mVの増減
    • -30℃: 300mV
    • 0℃: 600mV
    • 100℃: 1600mV

ピン

*** 下から見た図 ***

 -----------------
|                 |
|  +VS  VOUT GND  |
|  ---  ---  ---  |
 \               /
  \             /
   \           /
    \_________/
  1. +VS: ラズパイの3.3V端子に接続
    • 電源入力 (2.7V~10V)
  2. VOUT: A/Dコンバータのアナログインプット端子に接続
    • 温度センサーのアナログアウトプット(300mV(-30℃)~1600mV(100℃))
  3. GND: ラズパイのGND端子に接続

■ SPIモードがよく分からなかったので調べてみた

記事としてはこのあたりがわかりやすい

SPIメイン(ラズパイ)はクロックの極性と位相を選択できるとのことだが、、、
早い話がアイドル時のクロックがHIGHかLOWかを選択できて、クロックがHIGHになるときにデータを読み込むか、LOWになるときにデータを読み込むかを選択できるということ。

この、アイドル時のクロック(HIGH, LOW)、データをいつ読み込むか(HIGH, LOW) の組み合わせで4つのモード(SPIモード)を選択できる。

ちなみに、アイドル時のクロックを設定するオプションを CPOLビット 、データをいつ読み込むかを設定するオプションを CPHAビット と呼び、まとめると↓のような感じ。

SPIモード CPOLビット CPHAビット 説明
0 0 0 アイドル時のクロックは0。
クロックの立上がりでデータをサンプリング、立下がりでシフト
1 0 1 アイドル時のクロックは0。
クロックの立下がりでデータをサンプリング、立上がりでシフト
2 1 0 アイドル時のクロックは1。
クロックの立下がりでデータをサンプリング、立上がりでシフト
3 1 1 アイドル時のクロックは1。
クロックの立上がりでデータをサンプリング、立下がりでシフト

MCP3002のデータシートのシリアルタイミングを確認すると

fig_04.png

  • アイドル状態のクロックの極性は0( CPOL=0 )。
  • ラズパイ(マスター)側はビットの立ち上がりでデータのサンプリングを行い、立ち下がりでシフトを行えば良い ( CPHA=0 )

なので、SPIモードは 0

■ 実装

少し調べたら、WiringPi は開発中止?になっていたので、pigpioを使ってみる。
pigpioは pigpiod というデーモンを立ち上げて、このデーモン経由でGPIOを操作するらしい。

インストール

# pigpioのインストール
sudo apt update
sudo apt install pigpio

# pigpioデーモン起動
sudo systemctl enable pigpiod
sudo systemctl start pigpiod

# pythonでpigpioを扱うライブラリをインストール
pip install pigpio

# SPIを利用するためのラズパイのオプションも有効化しておく
sudo raspi-config nonint do_spi 0  # SPI通信有効化

実装

公式ドキュメントを参考に実装してみた

temp_pigpio.py
import pigpio
import time
from typing import Union

def int_to_binary(n: int, bits: int = 8):
    return ''.join([str(n >> i & 1 ) for i in reversed(range(0, bits))])

def bytes_to_binary(data: Union[bytearray,bytes]):
    return ','.join([int_to_binary(byte) for byte in data])

def main(debug: bool, chip_select: int, channel: int):
    pi = pigpio.pi()
    if not pi.connected:
        raise Exception("pigpio connection faild...")

    VREF = 3.3  # A/Dコンバータの基準電圧

    # オプション (http://abyz.me.uk/rpi/pigpio/python.html#spi_open)
    # オプションは下記のように22ビットで構成されている
    #
    # 21 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    # b  b  b  b  b  b  R  T  n  n  n  n  W  A u2 u1 u0 p2 p1 p0  m  m
    #
    # m m: SPIモード (0 ~ 3)
    # A  : メインSPI(0), AuxSPI(1) どちらを利用するか選択
    # W  : 3線のSPIを利用するなら(1)、4線なら(0) (メインSPIでしか利用できない)
    # あとは使いどころがよくわからん、、、
    SPI_MODE = 0b00  # SPIモード0を設定。アイドル時のクロックはLOW(CPOL=0)、クロックがHIGHになるときにデータをサンプリング(CPHA=0)
    OPTION = 0b0  
    OPTION = OPTION | SPI_MODE
    CLOCK_SPEED = 50000  # 50KHz
    h = pi.spi_open(chip_select, CLOCK_SPEED, OPTION)
    try:
        while True:
            # 1bit: 0固定
            # 2bit: スタートビット (1固定)
            # 3bit: SGL/DIFF: 動作モード。疑似差動モード(0)、シングルエンドモード(1)
            # 4bit: ODD/SIGN: MCP3002で利用するチャンネル。 CH0(0), CH1(1)
            # 5bit: MSBF: 受信データの形式。MSBF + LSBF(0), MSBFのみ(1)、
            write_data = 0b0110100000000000
            write_data = write_data | (0b1 * channel) << 12  # ODD/SIGN: 入力されたチャンネルで設定
            write_data = write_data.to_bytes(2, "big")
            cnt, read_data = pi.spi_xfer(h, write_data)
            if cnt != 2:
                print("[error] skip.")
                continue
            value = int.from_bytes(read_data, "big") & 0b1111111111  # 10ビットを値として取り出す
            volt = (value / 1023.0) * VREF  # 温度センサーから入力された電圧
            temp = (volt - 0.6) / 0.01  # 電圧を温度に変換。(0℃で600mV , 1℃につき10mV増減)

            if (debug):
                print(f"w: {bytes_to_binary(write_data)}")
                print(f"r: {bytes_to_binary(read_data)}")
                print(f"value: {value}, volt: {volt}, temp: {temp}")
            else:
                print(f"Temp: {temp}")
            time.sleep(1)
    finally:
        pi.spi_close(h)
        pi.stop()
        print("[info] spi closed.")

if __name__ == "__main__":
    CHIP_SELECT = 0  # ラズパイの CE0端子, CE1端子どちらに接続するか
    CHANNEL = 0  # MCP3002のCH0端子,CH1端子どちらを利用するか
    main(True, CHIP_SELECT, CHANNEL)

とりあえず動いた!

(.venv) pi@raspberrypi:~/iot-work $ python src/temp_sensor/temp_pigpio.py 
w: 01101000,00000000
r: 00000001,00000110
value: 262, volt: 0.8451612903225806, temp: 24.51612903225806
w: 01101000,00000000
r: 00000001,00000110
value: 262, volt: 0.8451612903225806, temp: 24.51612903225806
w: 01101000,00000000
r: 00000001,00000110
value: 262, volt: 0.8451612903225806, temp: 24.51612903225806

■ 参考

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?