TinyJoypad名刺化奮闘記 No.1


2023年11月18日

2023年11月19日
事の経緯
ニキシー管作品制作記の連載(?)途中ですが、突如始まりましたTinyJoypad名刺化奮闘記。
現在更新中のニキシー管作品制作記ですが、この中で「プリント基板を発注する」ということがネックになっていると何度か書きました。
本当はこの「名刺基板」と一緒に発注したかったのですが、このままではニキシー管時計が完成せずに12月になってしまいます。
前回のニキシー管作品制作記 No.12で意を決して基板の発注をしましたが、これはこれで制作をしたいのです。なので基板が届くまでの閑話としてやってしまおう!ということです。
それでは「ニキシー管作品制作記」の壮大な閑話、はじまります。
本編に入る前に
色々と今後もこのサイトの更新を続けていくことに当たって、一つここで方針を決定します。
「この記事以降の全ての文章」に対して、「ですます調での記述を行う」事とします。
というのも、今後このサイトがマネタイズされる可能性が出てきたためです。
人に見せる文章である以上、ここは明確なラインを引いた方が良いという判断になりました。今後とも本サイトをよろしくお願いいたします。
では、気を取り直して本編へどうぞ。
名刺基板Ver.1の話
名刺基板ですが、私は以前に名刺基板を制作したことがあります。この世にたった20枚しか存在せず、私の手元に残ったものはたった1枚です。

それはTiny Joypadというミニゲーム機を名刺基板に組み込んだものでした。
回路図はTiny Joypadそのまんまです。
基板を製造した後、自分の配線そのものにミスがあったことに気づくも、それ以外にも懸念事項が多すぎて見た目だけの代物になってしまいました。
つまり、私は不良品を配ったわけです。正直これは相当まずいことです。せめて修正版は作らないと、技術者としてもクリエイターとしても終わりです。
では、Ver.1の何がいけなかったのか?
単純な回路のミスもそうですが、それ以外の問題点を挙げていきましょう。
Ver.1の問題点 -情報を入れるスペース・金属突起-
情報とは即ち名刺が名刺たる役割を果たすための視覚情報の事です。
自分の名前、所属、連絡先…
基板に張り巡らされた穴を掻い潜ってデザインする必要があり、これにはかなり手こずらされました。
また、貫通式の部品を使うと必ず裏には金属突起が発生します。
ゲーム機なのに持つ部分が痛いという致命的な問題が発生しているのです。
これらの問題の解決案は、表面実装部品を利用するということ。
特に問題だったのはタクトスイッチと抵抗達なので、これを差し替える若しくは除去すればいいワケです。
名刺基板Ver.2の設計
これらの反省点を踏まえて、名刺基板Ver.2を設計していきましょう。
そもそもOLED(今後SSD1306と呼称)以外の選択肢はあるのか?という点。
私がポートフォリオにも書いているGLCD Systemですが、これには元になった作品があります。
こちらのサイトにあるグラフィックLCDゲーム機です。
ここで使っているグラフィックLCDはAQM1248Aというものですが、必要な電流値はいくらだろう?
なんとたったの1mA。10mAのSSD1306と比較するとその低さは一目瞭然です。
しかし、TinyJoypadを実装している様々な方のサイトを見ているとコイン電池で十分動いてそうなのです。
つまり、電源に問題はないのです。実装のやりやすさや金属突起などの要素を天秤にかけると、SSD1306の方に軍配が上がります。
それではメインプロセッサはどうでしょう。
TinyJoypadが使用しているのは主にATtiny85というマイコンです。これは、入手性と値段の観点からVer.2にはあまり採用したくありません。
ATtiny85は秋月電子でも常に在庫切れな上、Amazon等で頼もうにも輸送に数週間掛かる代物なのです。
というわけで、利用するプロセッサはArduino UNOにも使われているATmega328Pにします。
ATmega328PのLチカ
今持っているものをかき集め、実際にATmega328Pを使ってみます。
こちらの記事を参考にスケッチを書き込み、CR2032で駆動させたところ、5mA程度の電流が回路全体に流れて、LEDが薄く点灯したことを確認できました。
残念ながら画像は残っていませんがLチカは上手くいきました。
ATmega328Pの書き込みエラー(未解決)
何度か書き込んでいる内に大問題が発生しました。
生のATmega328Pにスケッチを書き込めなくなったのです。
エラー内容はこう。
avrdude: Yikes! Invalid device signature.
Double check connections and try again, or use -F to override
this check.
これはどういう事でしょうか。
まずUNO側に問題が無いか確認します。適当なLチカプログラムを書き込み、動くことを確認。
問題なさそうなので、再びArduino ISPをUNOに書き込みます。
問題は被書き込み側のATmega328Pのようです。ブートローダの書き込みもできません。
可能性として考えられるのは、内部振動子による駆動が出来なくなった可能性です。
まず、書き込み時の情報を全部見れるようにします。
環境設定 → より詳細な情報を表示する にチェック。
書き込み時にずらずらとメッセージが流れるようになります。
こちらのサイト によると、どうにも配線が駄目になっている可能性があるようなので、ブレッドボードを変えてみます。
上手くいきません。公式のドキュメントによると、16MHzで駆動させたい場合には18~22pFのセラミックコンデンサが要るとのこと。
部屋の中からかき集めたセラミックコンデンサはこうなりました。

左上から、12pF, 1000pF, 0.01μF, 0.1μF, 1μF, 4.7μFです。
積層セラミックコンデンサは活躍場面が少ないので抵抗ほど持っていません。
ここから18μF~22μFを作るのは余りに不便なのでセラミックコンデンサを買い物リストに入れておきましょう。
ただ、コンデンサ捜索の際に思わぬ報酬がありました。

私が恐らく6~8年前に作成したatmega88系統用書き込み基板とATmega328Pです。
のっかっていたATmega328Pを使って再度書き込みに挑戦します。
なんと書き込みに成功してしまいました。
これで、問題がATmega328P側にあるという事がはっきりしました。
ここまでくると、ATmega328Pをいくつか破壊した可能性に思い当たります。
本体自体も古いので、ここで使えなかったATmega328Pは破棄します。

現在の価値で1200円分ですが、私が購入した当時はおそらく200~300円で購入できたのだと思います。
気をとりなおして
SSD1306を駆動させるには
次なる大問題のSSD1306の駆動をやってみましょう。
TinyJoypadを動かすので、プログラムはTinyJoypad用のテストプログラムを動かす方が良いでしょう。
こういうサイトがありました。
こちらはTinyJoypadをArduino nano、即ちATmega328Pで駆動させるという私のやりたいことをやっているわけです。
つまり同じことをすればよいのです。
ダウンロードしたファイルの中の.ino拡張子のついたファイルを確認すると、TinyDriver.hをインクルードしています。
このヘッダファイルの中にSSD1306の駆動するためのプログラムが書かれています。
ここを開くと、関数群が書かれていますが悲しいことに関数の中身は空で自分で実装してくださいと書いてあります。
では、いろんなサイトで使われているssd1306xledというライブラリを入れていく訳ですがここでトラップがありました。
ssd1306xledというライブラリはこの世にいくつか存在するということです。
先程リンクを貼り付けたサイトで使っているのはこちらのgithubページから配布されているものです。
ここまできたら、ライブラリをATmega328P仕様に書き換えます。
ssd1306xled・TinyJoypad・ATmega328Pを繋げ合わせる
やっているのは直前の項で貼ったリンクと同じです。まず、ssd1306xled.hの以下の二行を次の通りに書き換えます。
変更前
#define SSD1306_SCL PB2
#define SSD1306_SDA PB8
変更後
#define SSD1306_SCL PC5
#define SSD1306_SDA PC4
もし、Slave addressが利用しているSSD1306と違うならそれもしっかり書き換えておきましょう。
次にssd1306xled.cppを書き換えます。
変更前(#define部分)
#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)
変更後(#define部分)
#define DIGITAL_WRITE_HIGH(PORT) PORTC |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTC &= ~(1 << PORT)
変更前(初期化関数部分)
DDRB |= (1 << SSD1306_SDA); // Set port as output
DDRB |= (1 << SSD1306_SCL); // Set port as output
変更後(初期化関数部分)
DDRC |= (1 << SSD1306_SDA); // Set port as output
DDRC |= (1 << SSD1306_SCL); // Set port as output
これらは何をやっているのか?というと、プロセッサの「出力ポート」を弄っています。
ATiny85の出力ポートはBだったわけですが、ATmega328PではCに変更します。
マイコン自体の仕様上、同じ役割(ここではI2C通信)を行うピンが異なることに由来します。
引き続きピンの初期設定を弄ります。
TESTMODE.inoのsetup関数を見ると、別関数で初期化が行われていることがわかります。
TinyOLED_init()関数はTinyDriver.hに書かれています。
ここで、TinyDriver.hを整備していきましょう。
TESTMODE.inoの内容を確認しつつ、TinyDriver.hが何をしたいのかすり合わせをしていきます。
と、ここでこのプログラムに対する疑念が生まれます。
インデント・改行などが成されておらず、「非常に読みにくい」のです。
メモリの削減のためなのかもしれませんが、ATmega328Pは余裕があるので、私の信条に則り読みやすいようにします。
#define F_CPU 8000000ULを追加し、その結果がこちらです。
#include "TinyDriver.h"
#include "ELECTROLIB.h"
#include "TESTMODE_PIC.h"
/* 追加文。8MHz駆動用 */
#define F_CPU 8000000UL
//public var
uint8_t X_JOY,Y_JOY;
void setup() {
TinyOLED_init();
TINYJOYPAD_INIT();
}
/////// main ///////
/////////////////////
void loop() {
for (uint8_t t=2;t<250;t++){
Sound(t,1);
}
X_JOY=29;
Y_JOY=30;
Tiny_Flip_JOYTESTER();
while(1){
if (TINYJOYPAD_RIGHT) {
X_JOY=(X_JOY<47)?X_JOY+6:47;
}
else if (TINYJOYPAD_LEFT) {
X_JOY=(X_JOY>11)?X_JOY-6:11;
}
else{
if (X_JOY<29) {X_JOY+=6;}
if (X_JOY>29) {X_JOY-=6;}
}
if (TINYJOYPAD_DOWN) {Y_JOY=(Y_JOY<48)?Y_JOY+6:48;}
else if (TINYJOYPAD_UP) {Y_JOY=(Y_JOY>12)?Y_JOY-6:12;}
else{
if (Y_JOY<30) {Y_JOY+=6;}
if (Y_JOY>30) {Y_JOY-=6;}
}
Tiny_Flip_JOYTESTER();
}
}
//////// main end ////////
//////////////////////////
uint8_t JOYTEST_JOYTESTER(uint8_t xPASS,uint8_t yPASS){
return pgm_read_byte(&JOYTEST[xPASS+(yPASS*128)]);
}
uint8_t boutton_JOYTESTER(uint8_t xPASS,uint8_t yPASS){
if (BUTTON_DOWN) {
return (0xff-blitzSprite(97,20,xPASS,yPASS,0,boutton));
}
else{
return 0xff;
}
}
uint8_t Joy_JOYTESTER(uint8_t xPASS,uint8_t yPASS){
return (blitzSprite(X_JOY,Y_JOY,xPASS,yPASS,0,Joy));
}
void Tiny_Flip_JOYTESTER(void){
uint8_t y,x;
for (y = 0; y < 8; y++){
TinyOLED_Data_Start(y);
for (x = 0; x < 128; x++){
TinyOLED_Send(
Joy_JOYTESTER(x,y)
|(
JOYTEST_JOYTESTER(x,y)
&boutton_JOYTESTER(x,y)
)
);
}
}
TinyOLED_End();
}
こちらを解析すると、
- 初期化
- 音を鳴らす
- Tiny_Flip_JOYTESTER()関数
- ボタン検知/表示座標操作
- Tiny_Flip_JOYTESTER()関数
- 4へ戻る
このTiny_Flip_JOYTESTER()がネックとなっているので、これが何をしているのか把握する必要がありそうです。
void Tiny_Flip_JOYTESTER(void){
uint8_t y,x;
for (y = 0; y < 8; y++){
TinyOLED_Data_Start(y);
for (x = 0; x < 128; x++){
TinyOLED_Send(
Joy_JOYTESTER(x,y)
|(
JOYTEST_JOYTESTER(x,y)
&boutton_JOYTESTER(x,y)
)
);
}
}
TinyOLED_End();
}
SSD1306に大きくかかわるのは、TinyOLED_Data_Start()関数とTinyOLED_Send()関数です。
というわけでこれを参考に、TinyDriver.hの関数群をこのように書き換えました。
void TinyOLED_init(void){
SSD1306.ssd1306_init();
}
void TinyOLED_Begin(void){
SSD1306.ssd1306_xfer_start();
}
void TinyOLED_End(void){
SSD1306.ssd1306_xfer_stop();
}
void TinyOLED_Send(uint8_t byte_){
SSD1306.ssd1306_send_data_start();
SSD1306.ssd1306_send_byte(byte_);
}
void TinyOLED_send_command(uint8_t command_){
SSD1306.ssd1306_send_command(command_);
}
void TinyOLED_Data_Start(uint8_t Y_){
SSD1306.ssd1306_send_data_start();
SSD1306.ssd1306_setpos(0, Y_);
}
そしてSSD1306を色々弄っているとこのような状態になりました。

異様な画面のちらつきです(カメラの性能等で綺麗に撮れません)。
SSD1306を変えてみても微妙にちらつきます。現状、プログラム側に問題があるのかSSD1306側に問題があるのかこれではわかりません。
どうにもSSD1306側に問題がある可能性が濃厚になってきました。(秋月で買ったものは比較的ちゃんと動くため)
ここで思い当たったのは、I2Cにノイズが乗っている可能性です。
手持ちの積層セラミックコンデンサをいくつか挿入すると、表示の乱れが少なくなりました。
ということは、この問題はI2C線に乗っているノイズでほぼ間違いないでしょう。
なにを思ったか、ここで駆動電圧を5Vから3.3Vの切り替えてみました。するとどうでしょう。

現れていたノイズがほぼ無くなったのです!
本格的にノイズ対策ということを考えるフェーズに到達したようです。
それでは、目的環境とI2Cのノイズについて考えてみよう!というところで今回は終わります!