Raspberry Pi Pico WのSPI通信とMicroPythonで温度センサ(ADT7310)を使ってみよう。

2024年8月30日

【 当サイトには広告リンクが含まれています。 】


ラズパイPicoW(Raspberry Pi Pico W)に温度センサ(ADT7310)と液晶ディスプレイ(LCD)を接続し、SPI通信を使ってLCDに温度センサで測定した気温を表示させます。



実験準備

実験に必要な環境や部品を準備します。

機器とプログラム

「I2C通信で液晶ディスプレイ(LCD)に文字表示。」の回路に温度センサ(ADT7310)を追加し、プログラムを改造して実験します。


使う部品

温度センサ(ADT7310)及び液晶ディスプレイ(LCD)、I2CシリアルI/Fモジュールなど実験に使う部品を準備します。(「I2C通信で液晶ディスプレイ(LCD)に文字表示。」と一部、重複しています。)

部 品 名規 格数 量取扱い店(参考)
ADT7310 温度センサモジュール
(ピンヘッダ 要半田付け)
 AE-ADT73101秋月電子通商
液晶ディスプレイ(LCD)LCD 1602A1Amazon (セット)
I2CシリアルI/FモジュールLCM1602
ブレッドボードBB-1021秋月電子通商
ジャンパーワイヤオス-メス
(約20cm)
1電子工作ステーション
ジャンパーワイヤオス-オス
(約20cm)
1電子工作ステーション



配線

準備した機器と部品をつなぎ、MicroPythonでプログラミングを行って、温度センサで測定した気温を、液晶ディスプレイ(LCD)に表示させます。

配線リスト

温度センサ(ADT7310)は、SPI通信のチャンネルにジャンパーワイヤで接続し、液晶ディスプレイ(LCD)は、I2C通信のチャンネルにジャンパーワイヤで接続します。

液晶ディスプレイ(LCD)は5Vで動作しますので、VBUS (5V)に接続し、温度センサ(ADT7310)も、仕様上、5Vで動作しますが、動作が不安定となるため、3.3Vの3V3に接続します。


▶️温度センサ(ADT7310)の配線

ラズパイPicoW温度センサ(ADT7310備 考
GP16SDO
GP17CS(上部に-が付いています。)
GP18SCL
GP19SDI
3V3 (3.3V)VDD
GNDGND


▶️液晶ディスプレイ(LCD)の配線

ラズパイPicoW液晶ディスプレイ(LCD)備 考
GP0SDA
GP1SCL
VBUS (5V)VCC
GNDGND


配線図



液晶ディスプレイ(LCD)とI2CシリアルI/Fモジュールは、ブレッドボード内部の配線で接続します。


気温測定プログラム

温度センサ(ADT7310)は、測定した温度を、内臓のA/Dコンバータで、13ビットまたは16ビットのディジタルデータに変換し、SPI通信を使ってラズパイPicoWなどのコンピュータに提供します。

ラズパイPicoWは読み込んだディジタルデータを処理し、温度を摂氏として液晶ディスプレイ(LCD)に表示します。

Raspi4BのThonnyを起動し、次のコードを「エディタ」に入力するか、リストをコピーしてペーストします。


#モジュールの読み込み(インポート)
from machine import Pin, I2C, SPI
from pico_i2c_lcd import I2cLcd
from time import sleep


#I2Cパラメータ設定
I2C_SDA = Pin(0)
I2C_SCL = Pin(1)


#I2Cを利用するため、オブジェクト(i2c_obj)を作成
i2c_obj = I2C(0,sda=I2C_SDA, scl=I2C_SCL, freq=400000)


#SPIのパラメータを設定
SPI_MISO = Pin(16)
SPI_SCK  = Pin(18)
SPI_MOSI = Pin(19)


#SPIのチップセレクト(スレーブセレクト)用の出力ピンを設定
spi_cs = Pin(17, mode=Pin.OUT)


#SPIのチップセレクトをOFFに設定
spi_cs.value(1)


#SPI通信でADT7310を利用するため、オブジェクト(spi_obj)を作成
spi_obj = SPI(0, baudrate=125000, polarity=1, phase=1, bits=8, \
              firstbit=SPI.MSB, mosi=SPI_MOSI, miso=SPI_MISO, sck=SPI_SCK)


#液晶ディスプレイ(LCD)のパラメータを設定
LCD_ADR = 0x27
LCD_ROW = 2
LCD_COL = 16


#LCDを利用するため、オブジェクト(i2c_lcd)を作成
i2c_lcd = I2cLcd(i2c_obj, LCD_ADR, LCD_ROW, LCD_COL)


#メイン処理

#例外処理(「Ctrl + c」キーにより中断)を設定
try:
  
        
    while True:
    
        #********** ADT7310 温度測定 **********
        
        
        #シリアル・インターフェース リセット
        spi_cs.value(1)
        buff=bytearray([0xff,0xff,0xff,0xff])
        spi_cs.value(0)
        spi_obj.write(buff)
        spi_cs.value(1)
        sleep(0.1)
        
        
        #コンフィギュレーション・レジスタのアドレスと
        #内容(一回のみの測定/変換、分解能を16ビット)を設定
        buff=bytearray([0x08,0xa0])
        
        
        #SPIのチップセレクトをONに設定
        spi_cs.value(0)
        

        #コマンド・バイトとコンフィギュレーション・レジスタに設定値を書き込む
        spi_obj.write(buff)
        
        
        #SPIのチップセレクトをOFFに設定
        spi_cs.value(1)
    

        while True:
                
            # ステータスレジスタの読み込み
            sleep(0.01)
            buff=bytearray([0x40])
            spi_cs.value(0)
            spi_obj.write(buff)
            status_reg = bytearray(spi_obj.read(1,0x00))
            spi_cs.value(1)
            if ((status_reg[0] >> 7) & 1) == 0:
                break       
        
        
        #温度値レジスタのアドレスを設定
        buff=bytearray([0x50])
         
         
        #SPIのチップセレクトをONに設定
        spi_cs.value(0)
        
        
        #コマンド・バイトに設定値を書き込む
        spi_obj.write(buff)        
   
    
        #温度データ(2バイト)を読込
        raw = bytearray(spi_obj.read(2,0x02))
        
        
        #SPIのチップセレクトをOFFに設定
        spi_cs.value(1)
        
        
        
        #温度データの上位バイト
        msb = raw[0]       
        #温度データの下位バイト
        lsb = raw[1]
        
        
        #バイト連結
        temp = (msb << 8) | lsb
    
    
        #32768以上は65536を引いて負の値に変換
        if(temp >= 32768):
            temp = temp - 65536
    
    
        #温度に変換    
        temp = temp/128
      

        #********** 液晶ディスプレイ(LCD)に温度を表示 ********** 

        #表示位置を初期化
        i2c_lcd.move_to(0, 0)
    
    
        #LCDに小数点第一位までの温度を表示
        i2c_lcd.putstr("SPI-TEMP:{:.1f}".format(temp))
    
    
        #℃表示
        ch=[
        #°
        bytearray([0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00]),
        #C
        bytearray([0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E, 0x00])
        ]
    
        for i in range(len(ch)):
            i2c_lcd.custom_char(i, ch[i])
            i2c_lcd.putchar(chr(i))   
         
    
        #1秒待つ
        sleep(1)
      

#「Ctrl + c」キーにより中断。
except KeyboardInterrupt:
    # Turn off the display
    print("「Ctrl + c」キーが押されました。")
    
    #LCD消灯
    i2c_lcd.backlight_off()
    i2c_lcd.display_off()


Thonnyの「F5」キーを押して、文字表示プログラムを実行します。

気温測定プログラムは、温度センサ(ADT7310)の測定した温度を、小数点第一位までの数値として、液晶ディスプレイ(LCD)に表示します。




確認後、Thonnyの「Ctrl + c」キーを押すと、液晶ディスプレイ(LCD)が消灯し、処理が中断しますので、中断後、「Ctrl + F2」キーを押して、プログラムを終了させます。


任意のファイル名でラズパイPicoWに保存します。(ここでは「3_8spitmprtr.py」で保存しました。)


プログラムの仕組み

温度センサ(ADT7310)や液晶ディスプレイ(LCD)と、どのようにやり取りをするのか、プログラムの動きを簡単に見ていきます。

▶️I2C、SPIモジュール等の読み込み

液晶ディスプレイ(LCD)とI2C通信するモジュールと、温度センサ(ADT7310)とSPI通信するためのモジュール等を読み込みます。

液晶ディスプレイ(LCD)と通信をするために、GitHubユーザー(T-622)が開発したライブラリ、「 lcd_api.py」と「 pico_i2c_lcd.py」の2種類のモジュールを組み合わせて使います。

ライブラリが保存されていない場合は、こちらの手順で保存してください。

#モジュールの読み込み(インポート)
from machine import Pin, I2C, SPI
from pico_i2c_lcd import I2cLcd
from time import sleep


▶️I2Cを利用するためのオブジェクトを作成

液晶ディスプレイ(LCD)とI2C接続するGP0とGP1の変数を設定後、I2C接続するオブジェクトを作成します。

#I2Cパラメータ設定
I2C_SDA = Pin(0)
I2C_SCL = Pin(1)


#I2Cを利用するため、オブジェクト(i2c_obj)を作成
i2c_obj = I2C(0,sda=I2C_SDA, scl=I2C_SCL, freq=400000)



【i2c_objのパラメータ】

項 目説 明
0I2Cのチャンネル番号(ラズパイPicoWの場合は0または1)
sda=I2C_SDASDA(シリアルデータ)として使うGPピン番号
scl=I2C_SCLSCL(シリアルクロック)として使うGPピン番号
freq=400000通信速度(400000bps=400kbps)


▶️SPI通信で温度センサ(ADT7310)を利用

温度センサ(ADT7310)とSPI接続するGP16~GP19の変数を設定します、

GP17は温度センサ(ADT7310)の選択を制御する出力ピンとして設定し、初期設定は出力をOFF(1)にして非選択状態にします。

SPI接続するオブジェクト(spi_obj)を作成します。

#SPIのパラメータを設定
SPI_MISO = Pin(16)
SPI_SCK  = Pin(18)
SPI_MOSI = Pin(19)


#SPIのチップセレクト(スレーブセレクト)用の出力ピンを設定
spi_cs = Pin(17, mode=Pin.OUT)


#SPIのチップセレクトをOFFに設定
spi_cs.value(1)


#SPI通信でADT7310を利用するため、オブジェクト(spi_obj)を作成
spi_obj = SPI(0, baudrate=125000, polarity=1, phase=1, bits=8, \
              firstbit=SPI.MSB, mosi=SPI_MOSI, miso=SPI_MISO, sck=SPI_SCK)


【spi_objのパラメータ】

項 目説 明
0SPIのチャンネル番号(ラズパイPicoWの場合は0または1)
baudrate=125000通信速度(125000bps=125kbps)
polarity=1クロック(SCLK)の極性を表わすパラメータ(0または1)で、1の場合、通信を行っていない間はSCLK信号はHに固定し、通信時には、Lのクロックパルスを生成します。
phase=1クロックパルスがデータをサンプルするタイミング(0または1)で、1の場合、クロックパルスの最後の部分で、MOSIやMISOの信号がサンプリングされます。
bits=8転送のビットは8ビット固定です。
firstbit=SPI.MSBデータの転送の時、マスタから送出される最初のビット(MSBまたはLSB)を設定します。
mosi=SPI_MOSIマスタからスレーブにデータを転送する、信号線のGPピンを指定します。
miso=SPI_MISOスレーブからマスタにデータを転送する、信号線のGPピンを指定します。
sck=SPI_SCKシリアルクロック。MISOやMOSIのデータは、動作モードによりSCKの立ち上がりまたは立ち下がりに同期して転送されます。


▶️液晶ディスプレイ(LCD)の選択と表示情報を設定

液晶ディスプレイ(LCD)のI2Cアドレス(0x27)と表示範囲(2行16列)を設定します。

#液晶ディスプレイ(LCD)のパラメータを設定
LCD_ADR = 0x27
LCD_ROW = 2
LCD_COL = 16


▶️I2C通信で液晶ディスプレイ(LCD)を利用

液晶ディスプレイ(LCD)に文字を表示させるため、I2cLcd() 関数を使ってオブジェクト(i2c_lcd)を作成し、i2c オブジェクト、アドレス(ADR)、行数(ROW)と列数(COL)を引数として渡します。

#LCDを利用するため、オブジェクト(i2c_lcd)を作成
i2c_lcd = I2cLcd(i2c_obj, LCD_ADR, LCD_ROW, LCD_COL)


▶️「Ctrl + c」キーにより中断させる。

メイン処理は「while」文を使った無限ループの中で、1秒ごとに温度センサ(ADT7310)からデータを読み込み、計算した温度を摂氏として液晶ディスプレイ(LCD)に表示します。

「Ctrl + c」キーを押して、無限ループを抜けるために、KeyboardInterrupt例外「try … except KeyboardInterrupt: …」を利用します。

#例外処理(「Ctrl + c」キーにより中断)を設定
try:
    
    while True:
    
        #********** ADT7310 温度測定 **********   
             ・・・
             ・・・
        #********** 液晶ディスプレイ(LCD)に温度を表示 ********** 
             ・・・
             ・・・

#「Ctrl + c」キーにより中断。
except KeyboardInterrupt:

             ・・・


▶️温度センサ(ADT7310)を初期化

ラズパイPicoWから温度センサ(ADT7310)に 「0xff,0xff,0xff,0xff」(32クロックの間High)を送り、温度センサ(ADT7310)をリセットします。

ラズパイPicoWと温度センサ(ADT7310)の間のデータ転送は一般的に次の手順で行います。

  1. 温度センサ(ADT7310)を非選択状態にします。

  2. 転送するデータを設定します。

  3. 温度センサ(ADT7310)を選択状態にします。

  4. データを転送します。

  5. 温度センサ(ADT7310)を非選択状態にします。


#シリアル・インターフェース リセット
spi_cs.value(1)
buff=bytearray([0xff,0xff,0xff,0xff])
spi_cs.value(0)
spi_obj.write(buff)
spi_cs.value(1)
sleep(0.1)


▶️測定方法と測定値のデータの提供方法(13ビット、16ビット)の設定

測定した温度を即座にディジタル・データに変換、停止する測定方法に設定し、データを16ビットで提供するように設定します。

設定は次の手順で行います。

  1. 「コマンド・レジスタ」に「コンフィギュレーション・レジスタ」のアドレス(0x01)と、W(0)(書き込み)を設定します。(0x08)

  2. 「コンフィギュレーション・レジスタ」に、測定方法とデータの変換方法を(0xa0)設定し、上記の(0x08)と一緒に、spi_objオブジェクトのwriteメソッドで書き込みます。


【コマンド・レジスタ】

MSB654321LSB
0R(1)
/W(0)
レジスタのアドレス連続読み出し00


【コンフィギュレーション・レジスタ】


#コンフィギュレーション・レジスタのアドレスと
#内容(一回のみの測定/変換、分解能を16ビット)を設定
buff=bytearray([0x08,0xa0])
        
        
#SPIのチップセレクトをONに設定
spi_cs.value(0)
     

#コマンド・バイトとコンフィギュレーション・レジスタに設定値を書き込む
spi_obj.write(buff)
        
        
#SPIのチップセレクトをOFFに設定
spi_cs.value(1)   


▶️16ビットのデータとして提供できるか確認

測定された温度をディジタル・データに変換するには時間を要するため、「ステータス・レジスタ(アドレス:0x00)」のビット7の状態から、ディジタル・データの保存状況を確認します。

「コマンド・レジスタ」に「ステータス・レジスタ」のアドレス(0x00)と、R(1)(読み込み)を設定(0x40)し、spi_objオブジェクトのwriteメソッドで書き込みます。

書き込み後、spi_objオブジェクトのreadメソッドで1バイトを読み込み、ビット7の状態を判定します、


【ステータス・レジスタ】


while True:
                
    # ステータスレジスタの読み込み
    sleep(0.01)
    buff=bytearray([0x40])
    spi_cs.value(0)
    spi_obj.write(buff)
    status_reg = bytearray(spi_obj.read(1,0x00))
    spi_cs.value(1)
    if ((status_reg[0] >> 7) & 1) == 0:
        break     


▶️ADT7310の温度データ(2バイト)を読み込み

温度センサ(ADT7310)のデータは2バイト(16ビット)で構成されているため、spi_objオブジェクトのreadメソッドを使って、指定のレジスタから2バイト分を読み込みます

「コマンド・レジスタ」に「温度の値を保存しているレジスタ」のアドレス(0x02)と、、R(1)(読み込み)を設定(0x50)し、spi_objオブジェクトのwriteメソッドで書き込みます。

「温度の値を保存しているレジスタ」(0x02)から2バイト分を読み込み、上位バイトと下位バイトをそれぞれの変数に保存します。

上位バイトと下位バイトを連結した2バイトデータから、温度を算出します。

#温度値レジスタのアドレスを設定
buff=bytearray([0x50])
         
         
#SPIのチップセレクトをONに設定
spi_cs.value(0)
        
        
#コマンド・バイトに設定値を書き込む
spi_obj.write(buff)        
   
    
#温度データ(2バイト)を読込
raw = bytearray(spi_obj.read(2,0x02))
        
        
#SPIのチップセレクトをOFFに設定
spi_cs.value(1)
        
        
        
#温度データの上位バイト
msb = raw[0]       
#温度データの下位バイト
lsb = raw[1]
        
        
#バイト連結
temp = (msb << 8) | lsb
    


▶️温度の算出

有効データ16ビットの内、最上位ビットは符号ビットであるため、温度の値は15ビット(0~32,768)で表現されます。

15ビットのデータで、−55°C~+150°Cの範囲の温度を、0.0078125°C刻みで表わすことができます。

下位7ビットは小数点を表わすビットになっているため、128で除算し、最終的に16ビットのデータを、小数点第七位を含めた温度に変換します。

#32768以上は65536を引いて負の値に変換
        if(temp >= 32768):
            temp = temp - 65536
    
    
        #温度に変換    
        temp = temp/128


次の図は、温度センサ(ADT7310)の「温度の値を保存しているレジスタ」(0x02)から、msb = 0x0b、lsb = 0xa0のデータを読み込んだ時の温度の算出過程です。



次の表は、温度とデータとの関連です。

温 度ディジタルデータ(2進数)ディジタルデータ(16進数)
-55℃1110 0100 1000 00000xE480
−50°C1110 0111 0000 00000xE700
−25°C1111 0011 1000 00000xF380
−0.0078125°C1111 1111 1111 11110xFFFF
0°C0000 0000 0000 00000x0000
+0.0078125°C0000 0000 0000 10000x0008
+25°C0000 1100 1000 00000x0C80
+50°C0001 1001 0000 00000x1900
+125°C0011 1110 1000 00000x3E80
+150°C0100 1011 0000 00000x4B00



以下は温度を液晶ディスプレイ(LCD)に表示させるのプログラムです。詳しい表示方法については、「I2C通信で液晶ディスプレイ(LCD)に文字表示。」を参照してください。


▶️液晶ディスプレイ(LCD)に温度を表示

表示位置を初期化した後、小数点第一位までの温度を℃記号を含めて、1秒ごとに表示します。

#表示位置を初期化
i2c_lcd.move_to(0, 0)
    
    
#LCDに小数点第一位までの温度を表示
i2c_lcd.putstr("SPI-TEMP:{:.1f}".format(temp))
    
    
#℃表示
ch=[
#°
bytearray([0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00]),
#C
bytearray([0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E, 0x00])
]
    
for i in range(len(ch)):
    i2c_lcd.custom_char(i, ch[i])
    i2c_lcd.putchar(chr(i))   
         
    
#1秒待つ
sleep(1)


▶️プログラムの中断。

「Ctrl + c」キーを押すと、「KeyboardInterrupt」例外が発生し、液晶ディスプレイ(LCD)消灯後、処理が中断します。

#「Ctrl + c」キーにより中断。
except KeyboardInterrupt:
    # Turn off the display
    print("「Ctrl + c」キーが押されました。")
    
    #LCD消灯
    i2c_lcd.backlight_off()
    i2c_lcd.display_off()


まとめ

ラズパイPicoW(Raspberry Pi Pico W)に温度センサ(ADT7310)と液晶ディスプレイ(LCD)を接続し、SPI通信を使って、温度センサで測定した気温を、液晶ディスプレイ(LCD)に表示させました。

SPI通信に接続された、デバイスの制御方法を学びました。

温度センサ(ADT7310)を使った、温度測定の計算原理を学びました。