ラズパイ4を用いた<ラズパイ 2023年10月更新 bookworm 連載記事>をラズパイ5で再検証した記事です。
AdafruitのStemma QT/Qwiicボードの中に、I2Cバス接続の4桁7セグメントLED表示器があります。コントローラはHT16K33です。白色をスイッチサイエンスから入手しました。Adafruitには、白色以外の色も用意されています。Stemma QT/Qwiicコネクタがついていないモデルは、アマゾンでも入手できます。
裏面のA0をショートして、スレーブ・アドレスを0x77に変更して使っています。変更前のデフォルトは0x76です。
pythonのプログラム step1
I2Cバスをアクセスするsmbus2ライブラリを利用します。
1
2
3
4
5
6
7
8
9
0
-
.
を7セグメントLEDに繰り返して表示します。
System Setup Registerに'0'を書き込んで、クロックを有効にします。
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_SYSTEM_ON, 0)
この記述と、
i2c.write_byte(i2c_address,HT16K33_GENERIC_SYSTEM_ON)
は、同じ結果になるようです。
HT16K33のデータシートはこれですが、プログラムを記述するには足りないように思えます。検索して見つかった様々なプログラムを参考にします。
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_DISPLAY_ON, 0)
は、
i2c.write_byte(i2c_address,HT16K33_GENERIC_DISPLAY_ON)
でも、同様に動作します。
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_CMD_BRIGHTNESS, 8)
は、輝度の設定です。0~15が指定でき、15が最大の明るさになります。
i2c.write_i2c_block_data(i2c_address, 0, [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
は、4桁のLEDを消灯します。この記述ではなく、もっと別の方法で消灯できるかもしれません。
import time
import smbus2
i2c_address = 0x71
i2c = smbus2.SMBus(1)
HT16K33_GENERIC_SYSTEM_ON = 0x21
HT16K33_GENERIC_DISPLAY_ON = 0x81
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_SYSTEM_ON, 0)
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_DISPLAY_ON, 0)
HT16K33_GENERIC_CMD_BRIGHTNESS = 0xe0
i2c.write_byte_data(i2c_address, HT16K33_GENERIC_CMD_BRIGHTNESS, 8)
while 1:
i2c.write_i2c_block_data(i2c_address, 0, [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 0, 0x06) #1
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 2, 0x5b) #2
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 6, 0x4f) #3
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 8, 0x66) #4
time.sleep(2)
i2c.write_i2c_block_data(i2c_address, 0, [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 0, 0x6d) #5
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 2, 0x7d) #6
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 6, 0x07) #7
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 8, 0x7f) #8
time.sleep(2)
i2c.write_i2c_block_data(i2c_address, 0, [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 0, 0x67) #9
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 2, 0x3f) #0
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 6, 0x40) #-
time.sleep(0.1)
i2c.write_byte_data(i2c_address, 8, 0x80) #dot
time.sleep(1)
pythonのプログラム step2
pythonの辞書機能を使って、
- 左からseg1、seg2、seg3、seg4と表示LEDの位置を指定でき
- 0~9はnum[x]で表示
できるプログラムです。
今、7セグメントLEDには、'5678'が表示されています。
import time
import smbus2
i2c_address=0x71
i2c=smbus2.SMBus(1)
HT16K33_GENERIC_SYSTEM_ON=0x21
HT16K33_GENERIC_DISPLAY_ON=0x81
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_SYSTEM_ON,0)
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_DISPLAY_ON,0)
HT16K33_GENERIC_CMD_BRIGHTNESS=0xe0
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_CMD_BRIGHTNESS,8)
i2c.write_i2c_block_data(i2c_address,0,[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
time.sleep(0.1)
num={0:0x3f,1:0x06,2:0x5b,3:0x4f,4:0x66,5:0x6d,6:0x7d,7:0x07,8:0x7f,9:0x67} # -;0x40,dot;0x80
seg1=0
seg2=2
seg3=6
seg4=8
i2c.write_byte_data(i2c_address, seg1, num[5])
i2c.write_byte_data(i2c_address, seg2, num[6])
i2c.write_byte_data(i2c_address, seg3, num[7])
i2c.write_byte_data(i2c_address, seg4, num[8])
関数化を図ります。
作ったのは、LEDを消灯するLEDclear()、7セグメントLEDの表示桁位置と、数字を引数とするdispLED(segment,number)です。
import time
import smbus2
i2c_address=0x71
i2c=smbus2.SMBus(1)
HT16K33_GENERIC_SYSTEM_ON=0x21
HT16K33_GENERIC_DISPLAY_ON=0x81
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_SYSTEM_ON,0)
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_DISPLAY_ON,0)
HT16K33_GENERIC_CMD_BRIGHTNESS=0xe0
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_CMD_BRIGHTNESS,8)
def LEDclear():
i2c.write_i2c_block_data(i2c_address,0,[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
num={0:0x3f,1:0x06,2:0x5b,3:0x4f,4:0x66,5:0x6d,6:0x7d,7:0x07,8:0x7f,9:0x67} # -;0x40,dot;0x80
seg={1:0,2:2,3:6,4:8}
def dispLED(segment,number):
i2c.write_byte_data(i2c_address, seg[segment], num[number])
#main
LEDclear()
dispLED(1,0)
dispLED(2,9)
dispLED(3,8)
dispLED(4,7)
実数dataを受けて表示をできるようにします。
実数を引数とするdisp7seg(data)関数です。最初に、負の数かを判断しています。負の数であれば、neg=Trueとします。
次に、小数点があるかどうかを見て、その位置dotNumPlaceを知ります。
segmentPlace=1
if neg==True:
i2c.write_byte_data(i2c_address, seg[1], 0x40)
segmentPlace=2
表示は、左詰めにしています。segmentPlace=1は一番左の桁の位置用の変数です。負の数であれば、その位置にマイナス0x40を表示します。segmentPlace=2と表示位置を進めます。
正の数であれば、segmentPlace=1のままです。
lenString= len(dataString)
実数の長さlenStringを知ります。負の数の時は、マイナスを省きます。
while lenString>0:
dispNum=dataString[mojiichi-1:mojiichi]
...
lenString=lenString-1
長さlenStringをカウンタとして利用したループです。lenStringは一つの桁を表示するとだんだん減少していきますが、最初の桁位置位置を知るためのカウンタmojiichiを用意しています。最初は'1'でループに入り、表示すると増加します。
実数を文字列とした文字列dataStringから1文字取り出して変数dispNumを得ます。
もし小数点であれば、小数点の前の文字にドットを追加して表示dispLED(segmentPlace,int(dispNum),True)します。小数点と関係ないなら、そのまま表示dispLED(segmentPlace,int(dispNum),False)します。
dispNum=dataString[mojiichi-1:mojiichi]
if dispNum==".":
mojiichi=mojiichi-1
segmentPlace=segmentPlace-1
dispNum=dataString[mojiichi-1:mojiichi]
dispLED(segmentPlace,int(dispNum),True)
mojiichi=mojiichi+1
else:
dispLED(segmentPlace,int(dispNum),False)
表示関数dispLED(segment,number,dot)の引数は、表示LEDの桁位置segment、表示する数字number、ドットの有無を示すbool変数dotです。dotがTrueであれば、表示する数字の最上位ビットを'1'にして、ドットが点灯します。
def dispLED(segment,number,dot):
if dot:
i2c.write_byte_data(i2c_address, seg[segment], num[number] +128) # |= 0x80
else:
i2c.write_byte_data(i2c_address, seg[segment], num[number])
23.4、-23.0、0、105.6を表示します。表示できる実数の範囲は制限を設けていないので、あくまでも、半導体センサが測定する温度ぐらいの表示用です。
import time
import smbus2
i2c_address=0x71
i2c=smbus2.SMBus(1)
HT16K33_GENERIC_SYSTEM_ON=0x21
HT16K33_GENERIC_DISPLAY_ON=0x81
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_SYSTEM_ON,0)
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_DISPLAY_ON,0)
HT16K33_GENERIC_CMD_BRIGHTNESS=0xe0
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_CMD_BRIGHTNESS,8)
def clearLED():
i2c.write_i2c_block_data(i2c_address,0,[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
num={0:0x3f,1:0x06,2:0x5b,3:0x4f,4:0x66,5:0x6d,6:0x7d,7:0x07,8:0x7f,9:0x67} # -;0x40,dot;0x80
seg={1:0,2:2,3:6,4:8}
def dispLED(segment,number,dot):
if dot:
i2c.write_byte_data(i2c_address, seg[segment], num[number] +128) # |= 0x80
else:
i2c.write_byte_data(i2c_address, seg[segment], num[number])
def disp7seg(data):
if data<0 :
neg=True
else:
neg=False
dataString=str(data)
dotNumPlace=dataString.find('.')+1
dataString=str(abs(data))
dotNumPlace=dataString.find('.')+1
segmentPlace=1
if neg==True:
i2c.write_byte_data(i2c_address, seg[1], 0x40)
segmentPlace=2
lenString= len(dataString)
mojiichi=1
while lenString>0:
dispNum=dataString[mojiichi-1:mojiichi]
if dispNum==".":
mojiichi=mojiichi-1
segmentPlace=segmentPlace-1
dispNum=dataString[mojiichi-1:mojiichi]
dispLED(segmentPlace,int(dispNum),True)
mojiichi=mojiichi+1
else:
dispLED(segmentPlace,int(dispNum),False)
segmentPlace=segmentPlace+1
lenString=lenString-1
mojiichi=mojiichi+1
print("test start")
clearLED()
data=23.4
print(data)
disp7seg(data)
time.sleep(3)
clearLED()
data=-23.0
print(data)
disp7seg(data)
time.sleep(3)
clearLED()
data=0
print(data)
disp7seg(data)
time.sleep(3)
clearLED()
data=105.6
print(data)
disp7seg(data)
pythonのプログラム step3
第4回を参照して、I2Cバスに気圧センサBME280をつなぎ、測定値の中の温度を4桁7セグメントLED表示器に表示します。
上記で作ったdisp7seg(data)関数は室温付近だと桁がオーバフローすることはありません。
第4回を参照してデバイス・ドライバを組み込んでリブートします。
組み込まれたことを確認します。0x77がUUになってます。
プログラムです。
import time
import smbus2
i2c_address=0x71
i2c=smbus2.SMBus(1)
HT16K33_GENERIC_SYSTEM_ON=0x21
HT16K33_GENERIC_DISPLAY_ON=0x81
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_SYSTEM_ON,0)
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_DISPLAY_ON,0)
HT16K33_GENERIC_CMD_BRIGHTNESS=0xe0
i2c.write_byte_data(i2c_address,HT16K33_GENERIC_CMD_BRIGHTNESS,8)
def clearLED():
i2c.write_i2c_block_data(i2c_address,0,[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
num={0:0x3f,1:0x06,2:0x5b,3:0x4f,4:0x66,5:0x6d,6:0x7d,7:0x07,8:0x7f,9:0x67} # -;0x40,dot;0x80
seg={1:0,2:2,3:6,4:8}
def dispLED(segment,number,dot):
if dot:
i2c.write_byte_data(i2c_address, seg[segment], num[number] +128) # |= 0x80
else:
i2c.write_byte_data(i2c_address, seg[segment], num[number])
def disp7seg(data):
if data<0 :
neg=True
else:
neg=False
dataString=str(data)
dotNumPlace=dataString.find('.')+1
dataString=str(abs(data))
dotNumPlace=dataString.find('.')+1
segmentPlace=1
if neg==True:
i2c.write_byte_data(i2c_address, seg[1], 0x40)
segmentPlace=2
lenString= len(dataString)
mojiichi=1
while lenString>0:
dispNum=dataString[mojiichi-1:mojiichi]
if dispNum==".":
mojiichi=mojiichi-1
segmentPlace=segmentPlace-1
dispNum=dataString[mojiichi-1:mojiichi]
dispLED(segmentPlace,int(dispNum),True)
mojiichi=mojiichi+1
else:
dispLED(segmentPlace,int(dispNum),False)
segmentPlace=segmentPlace+1
lenString=lenString-1
mojiichi=mojiichi+1
# main
print("test start")
while 1:
clearLED()
f = open('/sys/bus/i2c/devices/1-0077/iio:device0/in_temp_input')
Temp = round(int(f.read()) / 1000.0,1)
f.close
data= Temp
print(data)
disp7seg(data)
time.sleep(3)
実行中の様子です。