見出し画像

ORANGE pico とOLEDモジュールで「大石泉すき」~大石泉生誕祭2023~

大石泉さん、お誕生日おめでとうございます。


大石泉

大石泉は、ゲーム『アイドルマスター シンデレラガールズ スターライトステージ』に登場するアイドルの一人で、趣味がプログラミングです。

友達が居ます。

大石泉すき

今回は、プログラミングで「大石泉すき」してみます。

役者

  • ORANGE pico

    • ワンボードで高速なBASIC言語が動作します。今回はコンパクトな type S を用いました。

  • FTDI USBシリアル変換アダプター Rev.2 — スイッチサイエンス

    • 5Vの電源を供給しながら3.3Vで通信できる優れものです。ORANGE pico に直結できるピン配置になっており、これ1台でパソコンからの電源の供給とプログラムの流し込みができます。

  • 0.96インチ 128×64ドット有機ELディスプレイ(OLED) 青 (秋月電子通商 P-15870)

    • I2C通信で制御できる小型のモノクロディスプレイです。大石泉のイメージに合った青色の表示です。

I2C通信の準備

ORANGE pico type S では、I2Cの信号がEEPROMのソケットからしか出ていません。
そこで、秋月電子通商で以下の部品を用意し、EEPROMのソケットからI2Cの信号を取り出せるようにしました。
(これを用いるときは、EEPROMを取り外し、かわりに制作物を差し込みます)

  • 0.3mm厚 十字配線ガラスユニバーサル基板 Cタイプ(72×47mm) (P-09746)

  • ピンソケット(メス) 1×4(4P) (C-10099)

  • 連結ソケット(両端オスピン) 8P 2227P-08G-03-L2 (P-16274)

回路図でGND、VCC、SDA、SCLの位置を確認し、十字配線を活かしてピンソケットに電源と信号を引き出す方法を考えました。
その結果、以下のような配線になりました。

I2C通信用アダプタの配線

基板から4穴×4穴の部分をハサミやカッターで切り出し、余計なところを切らないように、かつ切るべきところが確実に切れるように、十字配線をカッターで切断してこの配線を実現しました。
今回は配線された面を裏にするため、配線面から見たとき上記画像の右側のような形に導通するようにします。
不都合を防ぐため、使わない端子同士も接続されないよう、忘れずに配線を切りましょう。

切り終わると、以下のようになりました。

基板の切り出しと配線の切断が完了した状態

これに向きに注意してピンソケットと連結ソケットをはんだ付けすれば、I2C接続用のアダプターができます。
ピンソケットを先にはんだ付けし、そのままだと長い足が連結ソケットと干渉するのでピンソケットの足を切ってから連結ソケットをはんだ付けします。

プログラミング

今回は、秋月電子通商に掲載されているデータシートなどを参考に、以下のプログラムを書きました。

10 dch=2:daddr=&H3C
20 zeros$="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
30 x=i2cw(dch,daddr,"\x00\xAE\x81\x7F\xA4\xA6\x2E\x40\xA0\xA8\x3F\xC0\xD3\x00\xDA\x12\xD5\x80\xD9\x22\xDB\x20\x8D\x14",1)
40 x=i2cw(dch,daddr,"\x00\x20\x00\x21\x00\x7F\x22\x00\x07",1)
50 for i=1 to 32
60 x=i2cw(dch,daddr,"\x40"+zeros$,1)
70 next
80 x=i2cw(dch,daddr,"\x00\xA1\xC8",1)
90 dim sx(4)=[16,48,80,32,64]
100 dim sy(4)=[0,0,0,4,4]
110 x=i2cw(dch,daddr,"\x00\xAF\x20\x80",1)
120 for m=1 to 2
130 for i=0 to 4
140 senddata$=format$("\x00\x21%c%c\x22%c%c",&H80+sx(i),&H80+sx(i)+31,&H80+sy(i),&H80+sy(i)+3)
150 x=i2cw(dch,daddr,senddata$,1)
160 for j=0 to 3
170 if m=1 then gosub 1000+10*(i*4+j) else tosend$=zeros$
180 x=i2cw(dch,daddr,"\x40"+tosend$,1)
190 next
200 pause 1000
210 next
220 pause 1000
230 next
240 goto 120
1000 tosend$="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\xf8\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1010 tosend$="\x00\x00\x00\x02\x07\x07\x07\x07\x07\x07\x07\x07\xf7\xff\xff\xff\xf7\xe7\x07\x07\x07\x07\x07\x07\x07\x07\x06\x00\x00\x00\x00\x00":return
1020 tosend$="\x00\x00\x00\x00\x00\x00\x00\x00\x80\xc0\xf0\xff\x7f\x1f\x03\x01\x0f\x1f\x7f\xf8\xf0\xe0\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1030 tosend$="\x00\x00\x00\x00\x00\x00\x18\x3e\x3f\x0f\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x03\x07\x0f\x0f\x0c\x00\x00\x00\x00\x00\x00\x00":return
1040 tosend$="\x00\x00\x00\x20\x70\x70\x70\x70\x70\x70\x70\x70\x70\x70\x70\xf0\xf0\xf0\xf0\x70\x70\x70\x70\x70\x70\x60\x00\x00\x00\x00\x00\x00":return
1050 tosend$="\x00\x00\x00\x00\x00\x00\x00\x00\x80\xc0\xe0\xf0\xf8\x7c\x1e\x1f\x0f\x03\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1060 tosend$="\x00\x00\x00\x18\x1c\x1e\x0f\x0f\x07\x07\xff\xff\xff\x07\x07\x07\x07\x07\x07\x07\x07\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00":return
1070 tosend$="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x1f\x1f\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1c\x1f\x1f\x1f\x00\x00\x00\x00\x00\x00\x00\x00":return
1080 tosend$="\x00\x00\x00\xf0\xf0\xf0\x70\x70\x70\x70\x70\x70\x78\x7c\x7e\x7e\x7e\x76\x70\x70\x70\x70\x70\x70\xf0\xf0\xf0\x00\x00\x00\x00\x00":return
1090 tosend$="\x00\x00\x00\xff\xff\xff\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\x9e\xff\xff\xff\x00\x00\x00\x00\x00":return
1100 tosend$="\x00\x00\x00\x23\x73\x73\x73\x73\xf3\xf3\xf3\xf3\xf3\xff\xff\xff\xf3\xf3\x73\x73\x73\x7b\x3b\x33\x03\x03\x03\x00\x00\x00\x00\x00":return
1110 tosend$="\x00\x00\x00\x00\x08\x1c\x1e\x1f\x0f\x07\x03\x11\x38\x7f\x7f\x7f\x03\x07\x0f\x1e\x1c\x3c\x38\x38\x38\x30\x00\x00\x00\x00\x00\x00":return
1120 tosend$="\x00\x00\x00\x80\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xfc\xfe\xfc\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x80\x00\x00\x00\x00\x00\x00":return
1130 tosend$="\x00\x00\x00\x00\x01\x01\xe1\xf1\xf9\x79\x39\x39\x39\x79\xf1\xff\xff\xff\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00":return
1140 tosend$="\x00\x00\x00\x00\x00\x00\x03\x0f\x1f\x3e\x78\x78\x78\xfc\xff\xff\x7f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1150 tosend$="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x30\x7c\x7f\x3f\x0f\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1160 tosend$="\x00\x00\x00\x00\x00\x80\xc0\xc0\xc0\xc0\xc0\xdc\xfe\xfc\xf0\xe0\xe0\xe0\xe0\xe0\xf0\x70\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1170 tosend$="\x00\x00\x00\x00\x00\x80\x81\x81\x81\x81\x81\x81\xc1\xc7\xff\xff\xfc\xe0\xc0\xc0\xc0\xc0\xe0\xe0\xe0\xc0\x00\x00\x00\x00\x00\x00":return
1180 tosend$="\x00\x00\x00\xc0\xe1\xc3\x03\x03\x03\x03\x03\x03\x03\x01\x41\xe1\xff\xff\xff\xf1\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00":return
1190 tosend$="\x00\x00\x00\x01\x03\x07\x0f\x1e\x1c\x1c\x3c\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x38\x30\x00\x00\x00\x00\x00":return

行30では、設定をデータシートでリセット時の設定とされているものに設定した後、チャージポンプを有効化 (0x8D 0x14) します。
このチャージポンプを有効化するコマンドは、無いと表示がされない (リセット時の設定から変更する) 上に、データシート内のコマンド一覧に載っていない (最後の方のアプリケーションノートに隔離されて載っている) という不親切設計となっています。
自分は、ボクにもわかる IchigoJam用 I2C 有機ELディスプレイ OLED の接続方法のプログラムA1が行う通信をロジックアナライザでキャプチャして発見しました。

行40~70で、画面をゼロクリアします。
ORANGE pico では、文字列にゼロ (値 0x00 のバイト) を含められないようで、以下の仕様になっているようです。

  • 文字列リテラルに \x01 などを書くとその値のバイトに変換されるが、\x00 はそのまま \x00 の4文字になる

  • i2cw 関数では、文字列 \x00 があると、かわりに1バイトのゼロを送信する

  • 扱える文字列の長さは255文字以下のみ

このため、固定の文字列で送信内容を表すときは \x00 とその他のバイトを同じように表記できますが、\x00 は4バイトで表されるため文字列の長さの制限に引っかかりやすくなります。
また、送信する内容を動的に生成する際はゼロをそのまま文字として含めようとしないよう注意が必要になります。

行80で、画面を上下反転および左右反転 (すなわち、180度回転) させる設定をします。

行90および100で、「大石泉すき」の各文字を表示する位置の情報を用意します。
OLEDモジュールの仕様に合わせ、横位置 (sx) は1ピクセル単位、縦位置 (sy) は8ピクセル単位です。

行110で、画面の点灯を有効化し、データの設定方法を設定します。
といっても、画面をゼロクリアしているので、この時点ではまだ点灯しません。

行120~240で、「大石泉すき」を出したり消したりします。

行140および150で、画面のデータを設定する範囲を設定します。
コマンド内のドントケアの部分(の一部)に1を入れることで、値 0x00 のバイトの仕様を回避しています。
i2cw 内に format$ を直接入れると Formula too cmplex が出てしまったので、分割しました。
範囲の開始点は用意した配列に基づき、大きさは32ピクセル×32ピクセル固定です。

行170で、状況に応じて送信するデータを選択します。
行180で、選択したデータを送信します。

行1000~1190で画像データを用意しました。
32ピクセル×32ピクセルのデータが、4行ずつに分割されています。
文字列の配列を作る方法はわからなかったので、行番号でデータを選択できるようにしました。

このプログラムと解説は、CC BY 4.0 でライセンスします。

実行結果

大石泉すき。


この記事が気に入ったらサポートをしてみませんか?