ラズパイ5 2023年10月更新 bookworm ⑤ 4桁7segment LED

　ラズパイ4を用いた＜[ラズパイ 2023年10月更新 bookworm　連載記事](https://www.denshi.club/parts/2024/01/202310-bookworm-5-ble.html)＞をラズパイ5で再検証した記事です。
 
　AdafruitのStemma QT/Qwiicボードの中に、I2Cバス接続の4桁7セグメントLED表示器があります。コントローラはHT16K33です。[白色をスイッチサイエンスから入手](https://www.switch-science.com/products/8586?variant=42464298172614)しました。Adafruitには、白色以外の色も用意されています。Stemma QT/Qwiicコネクタがついていないモデルは、アマゾンでも入手できます。

　　　[4桁の7セグメントLED表示器 i2c HT16K33](https://www.amazon.co.jp/s?k=4%E6%A1%81%E3%81%AE7%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88LED%E8%A1%A8%E7%A4%BA%E5%99%A8+i2c+HT16K33&__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=3JWAE7003EJJQ&sprefix=4%E6%A1%81%E3%81%AE7%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88led%E8%A1%A8%E7%A4%BA%E5%99%A8+i2c+ht16k33%2Caps%2C156&ref=nb_sb_noss)

![ht16k33-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/56990e87-3da7-f20c-cc42-964ce6cd242a.png)


　裏面の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のデータシートはこれ](https://cdn-shop.adafruit.com/datasheets/ht16K33v110.pdf)ですが、プログラムを記述するには足りないように思えます。検索して見つかった様々なプログラムを参考にします。

`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を消灯します。この記述ではなく、もっと別の方法で消灯できるかもしれません。

```python
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'が表示されています。

 ```python
 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)です。

 ```python
 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を表示します。表示できる実数の範囲は制限を設けていないので、あくまでも、半導体センサが測定する温度ぐらいの表示用です。

 ```python
 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回](https://qiita.com/jamjam/items/24bb944f4db85313a4ae)を参照して、I2Cバスに気圧センサBME280をつなぎ、測定値の中の温度を4桁7セグメントLED表示器に表示します。

　上記<step2>で作ったdisp7seg(data)関数は室温付近だと桁がオーバフローすることはありません。

　第4回を参照してデバイス・ドライバを組み込んでリブートします。

　組み込まれたことを確認します。0x77がUUになってます。

 ![ras101-e4e3ed25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/a98b4f72-f01a-8a1e-5215-33f65c1c9211.png)

　プログラムです。

```python
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)
```

　実行中の様子です。

 ![IMGP2094.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/1b1b9dcc-c29f-b169-8076-436b578a0171.png)