LoginSignup
1
1

ラズパイ5 2023年10月更新 bookworm ② pythonでI2Cバスをアクセス

Posted at

<ラズパイ 2023年10月更新 bookworm 連載記事>をラズパイ5で再検証した記事です。

 前回、下記の環境で、I2Cのアクセスまで実験しました。それまでのOSと変わらない使い勝手でした。

環境

  • ハードウェア 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)を動作させている

Pythonで気圧センサLPS22HBをアクセスする

 ここでは、Pythonを使って、I2Cバスにつながっているセンサのデータ(気圧と温度)を読み取ります。

AdafruitのStemma QT/Qwiicボード

  LPS22ボード解説のページ

LPS22a.png

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

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

気圧センサLPS22のおもなスペック

 LPS22HBのデータシートはこのWebページから

  • 電源電圧  1.7~3.6V
  • 気圧測定範囲 260~1260hPa、確度;±1hPa、分解能;±0.1hPa
  • 温度測定範囲 -40~+85℃、確度;±1.5℃
  • インターフェース I2C(0~400kHz)、SPI(10MHz)
  • スレーブ・アドレス 0x5d、裏面のAddrをショートすると0x5c

接続

lps22hb-rsp-100.png

プログラムではI2Cのアクセスにsmbusをimportするーその1

 I2Cバスで利用するライブラリ(モジュール)は、smbusです。

 Python3にインストールされているライブラリを表示するpip listからはsmbus2が表示されますが、smbusは出てきません。

 LinuxでネイティブにサポートされているI2Cアクセス用デバイス・ドライバにi2c-devがあります。

 apt-getコマンドは、パッケージの操作・管理を行うコマンドです。$ apt-cache search smbus を実行します。

python3-crccheck - implementation of 170+ CRC algorithms for Python 3
python3-smbus - Python 3 bindings for Linux SMBus access through i2c-dev
libdevice-i2c-perl - module to control and read hardware devices with i2c(SMBus)
python3-smbus2 - another pure Python implementation of the python-smbus package
python3-smbus2-doc - doc for another pure Python implementation of the python-smbus

 2行目python3-smbusは、「i2c-dev を介した Linux SMBus アクセス用の Python 3 バインディング」と書かれています。Python3バインディングとは、C/C++で書かれたコードをpythonで使えるようにすることを言っているようです。Python3では、smbusライブラリを利用してI2Cデバイスをアクセスすればいいようです。

 python3-smbus2の説明は、別の実装と書かれていますが、pureとわざわざ書かれているのが気になります。smbusとsmbus2の使い分けは不明です。

 smbusライブラリを使ったプログラムです。

import smbus
import time

i2c =  smbus.SMBus(1)
addr = 0x5d

i2c.write_byte_data(addr, 0x10, 0x10)

while 1:
    i2c.write_byte_data(addr, 0x11, 0x11)
    data = i2c.read_i2c_block_data(addr, 0x28, 3)
    press = (data[2]<<16 | data[1] << 8 | data[0] ) / 4096.0
    print("\nPress = " + str(int(press)) + "hPa")
    data = i2c.read_i2c_block_data(addr, 0x2b, 2)
    temp = (data[1] << 8 | data[0] ) / 100.0
    print("Temp  = " + str(round(temp,1)) + "`C")
    time.sleep(2)

 python3-smbusのソースは、こちらにありますが、ドキュメントにアクセスできません。

 smbus2にライブラリを変更しました。

import smbus2
import time

i2c =  smbus2.SMBus(1)

以下同じ

 問題なく動きます。smbus2のドキュメントはこちらにあります。

 以下、smbus2を利用し、そのドキュメントを参照しながらプログラムを説明します。

プログラムで使われているsmbus2ライブラリの関数

i2c.write_byte_data(addr, 0x10, 0x10)

 書き込み関数の引数は左から、スレーブ・アドレスaddrに対し、オフセット0x10に0x10のデータを1バイト書き込みます。

 オフセット0x10は、データシートにあるCTRL_REG1 (10h) =Control register 1です。1バイト長あり、bit6、bit5、bit4はODR[2:0]で、デフォルトが0になっており、スリープ・モードです。bit4に'1'(0x10)を書き込むことで、ODR(Output data rate)を1Hzを設定し、スリープから抜け出ます。

 ループ(while 1:)の中に入ります。

i2c.write_byte_data(addr, 0x11, 0x11)

 書き込み関数の引数は左から、スレーブ・アドレスaddrに対し、オフセット0x11に0x11のデータを1バイト書き込みます。

 オフセット0x11は、データシートにあるCTRL_REG2 (11h) =Control register 2です。1バイト長あり、bit4は、IF_ADD_INCでデフォルトが'1'になっており、Register address automatically incremented during a multiple byte access with a serial interfaceです。そのままでいいはずですが、過去のプログラムではbit4に'1'を書き込んでいたので、同じようにしました。bit0はONE_SHOTで、デフォルトは'0'なので、'1'を書き込んで有効にします。結果、データは0x11です。

data = i2c.read_i2c_block_data(addr, 0x28, 3)

 read_i2c_block_data()関数を用いて24ビットある気圧データを読み込みます。read_byte_data()関数で、1バイトずつ、三つのデータを読むこともできますが、read_i2c_block_data()は、指定した複数バイトを一度に読み込めます。

 関数の引数は左から、スレーブ・アドレスaddrに対し、オフセット0x28から3バイト読み込み、変数dataに入れます。データシートによれば、0x28はPRESS_OUT_XLレジスタで、気圧データの最下位バイトです。ちなみに、0x29はPRESS_OUT_Lの真ん中のバイト、0x2aはPRESS_OUT_Hの最上位バイトです。

 data[2]にPRESS_OUT_Hが、data[1]にPRESS_OUT_Lが、data[0]にPRESS_OUT_XLが収納されます。

press = (data[2]<<16 | data[1] << 8 | data[0] ) / 4096.0

 最下位バイトPRESS_OUT_Hを左に16ビット・シフトし、真ん中のバイトPRESS_OUT_Lを左に8ビット・シフトし、および最下位バイトPRESS_OUT_XLの三つのデータをor(論理和)すると、2の補数形式の24ビット・データが得られます。
 データシートに書かれた計算式により4096で割れば、hPaの値が得られます。

data = i2c.read_i2c_block_data(addr, 0x2b, 2)

 同様に、温度データをオフセット0x2bから読み出します。2バイトです。

temp = (data[1] << 8 | data[0] ) / 100.0

 データシートに書かれた計算式により16ビット・データを100で割れば、摂氏の値が得られます。

得られる温度は、このセンサ自体の温度です。周辺温度より3度ほど高めになります。

:

 

1バイトごとにデータを読むプログラムーその2

 <その1>で用いたread_i2c_block_data()関数は、指定したバイト数分データを読み込みます。読み込んだデータはリストになっているので、処理しやすいです。

 1バイトごとに読むには、read_byte_data(addr, オフセット)を使います。気圧、温度ともに書き換えました。

import smbus2
import time

i2c =  smbus2.SMBus(1)
addr = 0x5d

i2c.write_byte_data(addr, 0x10, 0x10)

while 1:
    i2c.write_byte_data(addr, 0x11, 0x11)
    pdata_XL = i2c.read_byte_data(addr, 0x28)
    pdata_L  = i2c.read_byte_data(addr, 0x29)
    pdata_H  = i2c.read_byte_data(addr, 0x2a)
    press = (pdata_H<<16 | pdata_L << 8 | pdata_XL ) / 4096.0
    print("\nPress = " + str(int(press)) + "hPa")
    tdata_L = i2c.read_byte_data(addr, 0x2b)
    tdata_H = i2c.read_byte_data(addr, 0x2c)
    temp = (tdata_H << 8 | tdata_L ) / 100.0
    print("Temp  = " + str(round(temp,1)) + "`C")
    time.sleep(2)

 実行結果は変わりません。

2バイトごとにデータを読むプログラムーその3

 温度データは2バイトです。read_word_data()を使うと一度で2バイトのデータを読み込めます。気圧は、<その2>のままです。

import smbus2
import time

i2c =  smbus2.SMBus(1)
addr = 0x5d

i2c.write_byte_data(addr, 0x10, 0x10)

while 1:
   i2c.write_byte_data(addr, 0x11, 0x11)
   pdata_XL = i2c.read_byte_data(addr, 0x28)
   pdata_L  = i2c.read_byte_data(addr, 0x29)
   pdata_H  = i2c.read_byte_data(addr, 0x2a)
   press = (pdata_H<<16 | pdata_L << 8 | pdata_XL ) / 4096.0
   print("\nPress = " + str(int(press)) + "hPa")
   tdata = i2c.read_word_data(addr, 0x2b)
   temp = tdata / 100.0
   print("Temp  = " + str(round(temp,1)) + "`C")
   time.sleep(2)

それぞれの波形を見る

その1>read_i2c_block_data() 気圧は3バイト、温度は2バイトと連続してデータが読み出せています。

lps22hb-rsp-103a.png

その2>read_byte_data() 1バイトごとデータが読み出されています。

lps22hb-rsp-102a.png

その3>温度データをread_word_data() 連続してデータが読み出せています。

lps22hb-rsp-101a.png

1
1
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
1
1