2025年2月23日日曜日

raspberry pi picoでSSD1302に日本語を表示させてみた。

raspberry pi picoでSSD1302に漢字を表示させたい。
というわけで以下の順でやってみた。

1.フォントを探す
2.イメージのバイナリデータをファイル化
3.漢字コードからバイナリファイルの場所を算出するインデックスを作成
4.文字コード変換1(ShiftJIS→JIS変換)
5.文字コード変換2(UTF16→ShiftJIS変換)
6.文字画像確認その1(コンソール出力)
7.文字画像確認その2(SSD1302への出力)
8.文字列を表示する。

1.フォントを探す
https://www.mgo-tec.com/blog-entry-ssd1306-shinonome.html を見てみると東雲フォント
というものがあるみたいなので、今回はこれを(shnmk16.bdf)を使ってみる。

2.イメージのバイナリデータをファイル化
どうせメモリ上にフォントイメージを載せられないので、一字毎イメージファイルを読むことにする。
バイナリデータは1文字32バイトで保存。
そのためのshnmk16.bdfからバイナリファイルを抽出するスクリプトを作って流してみる。

def readandwritefontsfile():
    isImageArea=False
    code=0
    image=b''
    with open('shnmk16.bdf') as f:
        with open('fontImage.bin','wb') as wf:
            for str in f:
                if str[:9]=="STARTCHAR" :
                    code=int(str[10:14],16)
                elif str[:6]=="BITMAP" :
                    image=b''
                    isImageArea=True
                elif str[:7]=="ENDCHAR" :
                    wf.write(image)
                    isImageArea=False
                elif isImageArea:
                    image+=bytes.fromhex(str[:4])
    
readandwritefontsfile()

で作成されたfontImage.binのファイルサイズが、220,128バイト。
CircuitPyドライブには載せられるが、メモリ上にこのイメージを載せるのは容量的に少し不安がある。
なので、表示の都度このファイルから1文字ずつ読み出すことにする。

3.漢字コードからバイナリファイルの場所を算出するインデックスを作成
shnmk16.bdf内で定義しているコードはJISのよう。
最初の文字コード(10進数)8481からで、使われていない文字はそのコードを飛ばしていて、
フォントコードが飛び番になっている。

確認方法として、以下を実施
・「shnmk16.bdf」をキーワードENCODINGでgrepし、
・出てきた行をExcelに貼り付け、
・区切り位置ウィザードで半角スペースを区切り
・E2セルに=D2-D1-1の計算式を入れ、E列末までコピーし、値貼り付けにより計算式を数値にして
・オートフィルタでE列として、マイナス値が1件を確認したのち、値0のもののみ表示し、表示された行を行削除し、
・オートフィルタを解除して、F1セルに=E1、F2セルに=F1+E2の計算式を入れ、F2セルをF列末までコピーして
・D列(コード)、E列(飛び番)、F列(累積)を抽出して確認
確認したところ、以下のコード値で直前のコードから+1より大きい数字に飛んでいる。コード値と飛び番数は以下。
コード	飛び番	累積
8737	162	162
8762	11	173
8778	8	181
8796	11	192
8818	7	199
8830	4	203
9008	177	380
9025	7	387
9057	6	393
9249	166	559
9505	173	732
9761	170	902
9793	8	910
10017	200	1110
10065	15	1125
10273	175	1300
12321	2016	3316
12577	162	3478
12833	162	3640
13089	162	3802
13345	162	3964
13601	162	4126
13857	162	4288
14113	162	4450
14369	162	4612
14625	162	4774
14881	162	4936
15137	162	5098
15393	162	5260
15649	162	5422
15905	162	5584
16161	162	5746
16417	162	5908
16673	162	6070
16929	162	6232
17185	162	6394
17441	162	6556
17697	162	6718
17953	162	6880
18209	162	7042
18465	162	7204
18721	162	7366
18977	162	7528
19233	162	7690
19489	162	7852
19745	162	8014
20001	162	8176
20257	162	8338
20513	205	8543
20769	162	8705
21025	162	8867
21281	162	9029
21537	162	9191
21793	162	9353
22049	162	9515
22305	162	9677
22561	162	9839
22817	162	10001
23073	162	10163
23329	162	10325
23585	162	10487
23841	162	10649
24097	162	10811
24353	162	10973
24609	162	11135
24865	162	11297
25121	162	11459
25377	162	11621
25633	162	11783
25889	162	11945
26145	162	12107
26401	162	12269
26657	162	12431
26913	162	12593
27169	162	12755
27425	162	12917
27681	162	13079
27937	162	13241
28193	162	13403
28449	162	13565
28705	162	13727
28961	162	13889
29217	162	14051
29473	162	14213
29729	162	14375

これをもとに以下の関数で入力code(SJISコード)を、その値により補正値を変え、
最終的にイメージファイルの読み込みアドレスを算出する
具体的には、
・G2セルに="elif code <"&D2&" :(Alt+Enter)    code-="&E1 計算式を入れG列末までコピーして、
・G列をコピーし、テキストエディタに貼り付け、正規表現置換で\r?\nを\r\nに置換。
あとは前後を整備して、以下の関数を作る。
def binfileindex(code):
    if code < 8737 :
        pass
    elif code <8762 :
        code-=162
    elif code <8778 :
        code-=173
    elif code <8796 :
        code-=181
    elif code <8818 :
        code-=192
    elif code <8830 :
        code-=199
    elif code <9008 :
        code-=203
    elif code <9025 :
        code-=380
    elif code <9057 :
        code-=387
    elif code <9249 :
        code-=393
    elif code <9505 :
        code-=559
    elif code <9761 :
        code-=732
    elif code <9793 :
        code-=902
    elif code <10017 :
        code-=910
    elif code <10065 :
        code-=1110
    elif code <10273 :
        code-=1125
    elif code <12321 :
        code-=1300
    elif code <12577 :
        code-=3316
    elif code <12833 :
        code-=3478
    elif code <13089 :
        code-=3640
    elif code <13345 :
        code-=3802
    elif code <13601 :
        code-=3964
    elif code <13857 :
        code-=4126
    elif code <14113 :
        code-=4288
    elif code <14369 :
        code-=4450
    elif code <14625 :
        code-=4612
    elif code <14881 :
        code-=4774
    elif code <15137 :
        code-=4936
    elif code <15393 :
        code-=5098
    elif code <15649 :
        code-=5260
    elif code <15905 :
        code-=5422
    elif code <16161 :
        code-=5584
    elif code <16417 :
        code-=5746
    elif code <16673 :
        code-=5908
    elif code <16929 :
        code-=6070
    elif code <17185 :
        code-=6232
    elif code <17441 :
        code-=6394
    elif code <17697 :
        code-=6556
    elif code <17953 :
        code-=6718
    elif code <18209 :
        code-=6880
    elif code <18465 :
        code-=7042
    elif code <18721 :
        code-=7204
    elif code <18977 :
        code-=7366
    elif code <19233 :
        code-=7528
    elif code <19489 :
        code-=7690
    elif code <19745 :
        code-=7852
    elif code <20001 :
        code-=8014
    elif code <20257 :
        code-=8176
    elif code <20513 :
        code-=8338
    elif code <20769 :
        code-=8543
    elif code <21025 :
        code-=8705
    elif code <21281 :
        code-=8867
    elif code <21537 :
        code-=9029
    elif code <21793 :
        code-=9191
    elif code <22049 :
        code-=9353
    elif code <22305 :
        code-=9515
    elif code <22561 :
        code-=9677
    elif code <22817 :
        code-=9839
    elif code <23073 :
        code-=10001
    elif code <23329 :
        code-=10163
    elif code <23585 :
        code-=10325
    elif code <23841 :
        code-=10487
    elif code <24097 :
        code-=10649
    elif code <24353 :
        code-=10811
    elif code <24609 :
        code-=10973
    elif code <24865 :
        code-=11135
    elif code <25121 :
        code-=11297
    elif code <25377 :
        code-=11459
    elif code <25633 :
        code-=11621
    elif code <25889 :
        code-=11783
    elif code <26145 :
        code-=11945
    elif code <26401 :
        code-=12107
    elif code <26657 :
        code-=12269
    elif code <26913 :
        code-=12431
    elif code <27169 :
        code-=12593
    elif code <27425 :
        code-=12755
    elif code <27681 :
        code-=12917
    elif code <27937 :
        code-=13079
    elif code <28193 :
        code-=13241
    elif code <28449 :
        code-=13403
    elif code <28705 :
        code-=13565
    elif code <28961 :
        code-=13727
    elif code <29217 :
        code-=13889
    elif code <29473 :
        code-=14051
    elif code <29729 :
        code-=14213
    else :
        code-=14375
    return (code-8481)*32

d=binfileindex(29400)


4.文字コード変換1(ShiftJIS→JIS変換)
pythonの標準でのJISコード変換は見つからなかったので、SJIS→JISの変換関数を作る。
def sjis2jis(indata):
    h_data=int.from_bytes(indata)>>8
    l_data=int.from_bytes(indata) & 0xff
    if h_data<=0x9f :
        if l_data < 0x9f :
            h_data=(h_data<<1) - 0xe1
        else :
            h_data=(h_data<<1) - 0xe0
    else :
        if l_data < 0x9f :
            h_data =(h_data << 1 ) -0x161
        else:
            h_data =(h_data << 1 ) -0x160
    if l_data < 0x7f :
        l_data -= 0x1f
    elif l_data < 0x9f:
        l_data -=0x20
    else:
        l_data -=0x7e

    return h_data << 8 | l_data

5.文字コード変換2(UTF16→ShiftJIS変換)
circuitpythonでは、「'あ'.encode('shift_jis')」が使えずUTF-8で表示されてしまう。
なので自前で変換テーブルを作成する。元ネタとして、
https://ameblo.jp/fc2miha/entry-12876272336.html
から、
https://drive.google.com/file/d/1dMNG8bcM58w7uHRdBbqsP2R1HGAU9K2z/view?usp=sharing
で、UTF-8→SJISの変換テーブル(list.dat)をダウンロードして、
次のプログラムでutf8tosjis.binを作り、

import json
def readandwriteconvfile():
    flg_firstline=True
    dic=dict()
    with open('utf8tosjis.bin','wb') as wf:
        with open(r'C:\Users\whisk\Downloads\list.dat','r',encoding='utf-8') as f:
                for str in f:
                    if flg_firstline :
                        flg_firstline=False
                        continue
                    str_utf8code,str_sjiscode,_=str.split(',')
                    wf.write(int(str_utf8code,16).to_bytes(3,byteorder='big'))
                    wf.write(int(str_sjiscode,16).to_bytes(2,byteorder='big'))
readandwriteconvfile()

出来上がったbinファイルをcircuitpyドライブ直下におく。
で、変換表を使ったutf8文字列→sjis文字列への変換関数を作る。
dic=dict()
allbyte=b''
with open('utf8tosjis.bin','rb') as f:
    allbyte=f.read()
def utf8str2sjisstr(utf8str):
    retval=b''
    for utf8char in utf8str:
        utf8=utf8char.encode()
        i=0
        while i>8
    l_data=indata & 0xff
    if h_data<=0x9f :
        if l_data < 0x9f :
            h_data=(h_data<<1) - 0xe1
        else :
            h_data=(h_data<<1) - 0xe0
    else :
        if l_data < 0x9f :
            h_data =(h_data << 1 ) -0x161
        else:
            h_data =(h_data << 1 ) -0x160
    if l_data < 0x7f :
        l_data -= 0x1f
    elif l_data < 0x9f:
        l_data -=0x20
    else:
        l_data -=0x7e

    return h_data << 8 | l_data


str_mainte='メンテナンス中'
putString(utf8str2sjisstr(str_mainte),28,8)


表示はこんな感じ。
  
ま、泥臭いし半角対応はできていないので、今回作ってみたものの使わないかな? 8x8の美咲フォントを使う方は、いろいろ整備されているみたいだし。

0 件のコメント: