2019年5月21日火曜日

Arduino+74HC595+8×8nマトリクスLED(1)

なんとなくではあるものの、マトリクス1つならばスクロールもできるようになったので、マトリクスと74HC595(秋月のSN74HC595N)をもう一つ足して8×16で遊んでみることに。
【参考文献】
全ての発端。マトリクスとシフトレジスター。これがいいんです。
https://hadatti.files.wordpress.com/2012/02/8x8matrix_logic_circuit.jpg

ドットマトリクスのピン配置(たまたま同じ型番使ってる方発見。データシートだけではどこが一番ピンかわかんねーんですわ)
http://akagi13213.hatenablog.com/entry/2017/10/22/224406

8*8ドットマトリクス用美咲フォント(これがなければウンザリするほどフォントを手打ちしなきゃいけません。素晴らしいものを公開してくださってありがとう。感謝感激)
http://nuneno.cocolog-nifty.com/blog/2016/03/arduino-e818.html

トランジスタアレイについては、どこのサイトで知ったか忘れました。Arduino直でマトリクス制御しているサイトで、8個分の電流をArduinoの入出力ピンで受け取るとやばいよ!回避するにはトランジスタアレイをつかうんやで、という表記をみたもので…


なぜか各ページのプログラムは参考にせず、自力で書きました(笑)
ただしフォント周りはライブラリになっていたのでさくっと使わせていただきましたよ。




【重要】回路図のCADの使い方がよくわからないので、文章で説明しますw

今回使ったマトリクスは秋月の MOA20UP018GJ [I-0886]というもので、行側カソードコモンというタイプになります。小さめのブレッドボードに2つマトリクスを配置して、かつカソードを二つ分まとめた形に結線して表示部としました。
【余談】
 LEDマトリクスは単色のものに限っていえば、構造上アノードコモンともいえるしカソードコモンともいえる(というか事実上区別しても意味がない)デバイスでありながら、ネット上の作例では大半がアノードコモン/カソードコモンとしか書かない、という変な状況です。自分の使ったマトリクスであれば、行を基準にすればカソードコモンですし、列を基準にすればアノードコモンです。自分の場合には行側カソードコモンと書かないと正確な表現になりません。そのあたりが無頓着なのはすげー不思議なんですけど。
制御部は別のブレッドボードに作成しました。制御部と表示部の間は適宜ジャンパー飛ばしといてください。(文章で書くと簡単www)

制御部は以下のイメージで。
ArduinoさんからはSER・SRCLK・RCLK・SRCLRへ向けた計4本の制御線をブレッドボード上の74HC595に接続します。(デジタルピンであればどこにつないでもかまいません。自分は2,3,4,5と割り振りました)
SRCLRについては繋いでない作例が多いようなのですが、setup()の中で明示的にシフトレジスタをクリアしたかったことと、任意のタイミングでクリアできたら便利やろ、という感覚的なものが働きました。(=好みの問題)

今回は縦・横・横の合計24ビットですので、74HC595は計3つ使います。
接続順はArduino→74HC595(縦)→74HC595(右)→74HC595(左)です。
74HC595間は接続順の下位側QH'ピン→上位側SERピンをつなぎます。SRCLK・RCLK・SRCLRは同じタイミングでHIGH/LOWを切り替えるっぽいので、そのまま同じピン同士をつなぎます。OEは忘れずにGNDと接続して出力有効にしておかないといけません。その他のピンはデータシートとにらめっこして適宜GNDとか5Vとか繋いどいてください。
行側がコモンですので、74HC595(縦)はスキャン用、74HC595(右)・74HC595(左)は点灯パターン指定用です。
※行側コモンだと8ビットデータを配列で持ったときにフォントのイメージそのまんまなんですが、列側コモンだと縦横変換をしたデータを配列に格納するんでしょうか?Arduinoというより電子工作まだひと月たってないのでよくわかりません。

74HC595(縦)は直接マトリクスにつながずにシンクドライブなトランジスタアレイ(TBD62083APG)を挟んであります。一行につき8個のLEDがぶら下がっていてその分の電流を受け流す役割があります。もう一つはそのままだと行側がカソードであるため74HC595(縦)にはビット反転したデータを与えねばなりません(意図通りに動かすには該当ピンをLOWにする必要がある)が、TBD62083APGを挟むことによってビット反転する必要がなくなります(該当ピンをHIGHにしたときにカソードからGNDに電流が流れる)。これにより縦横でビット0とビット1の意味が揃うので頭の弱い自分は大助かり(笑)
※TBD62083APGの使い方はリンク先が詳しいです。COMMONが何のためにあるのかわかりました。ちなみにTBD62083APGはTD62083APの後継にあたります。ピン互換ですので同じように使えます。
トランジスタアレイ「TD62083AP」のホントの使い方


右と左はぶっちゃけどっちを先につないでもいいと思います。物理的配置にあわせて右→左として、データの送り順を把握しやすくすることを優先しました。将来的にマトリクスを3つ4つ繋ぐ予定があるのなら、つなぎ方も考えておくと接続順が決めやすいのではないかと思います。自分は左側に順次追加していくとうまくいくはずです。

プログラム上では8ビットを3回に分けて出力することになります。出力順は接続末尾のものを先に送る必要がありますので、74HC595(左)→74HC595(右)→74HC595(縦)という順番になります。つまりフォントデータを2文字分先頭から送った後、該当行のみHIGHにしたデータを送って1行分完了、これを8行分繰り返して1回の表示が完了します。

スクロールなどの具体的な処理は説明を省きます(ぉぃ)

2019/06/21  追記
digitalWriteをくるむ関数を作ってそいつの引数をuint16_tにしてもちゃんと出力されないよー、という問題ですが、digitalWriteのvalという引数がuint8_tで宣言されていますので、データが欠落してそうなっちゃうよね、ということで自己解決しました(汗)
ただ、初心段階での早とちりということで、このまま残しておきます。参考にはならないのでどこからどこまで削除、という形で特記しておきます。あしからず。
以降 削除 =====>>>>>
躓いたところはそこではなくて、文字パターン部分の出力です。
下記のような関数を作ってそいつで出力する感じでコーディングしました。別にdigitalWriteは遅いよ、とかdigitalWriteは遅いよ、ということを言いたいのではありません。
ちなみにcolShiftOutがあるってことはrowShiftOutってのも作ったってことです。当初は行側にトランジスタアレイを挟んでいなかったので、rowShiftOutは引数でもらったデータをビット反転して送信していました。が、トランジスタアレイを挟んだことによって分けてある意味がなくなりました…

void colShiftOut (uint8_t data) {
for ( uint16_t i = 0; i < 8; i++ ) {
digitalWrite(SHIFT_CLK, LOW);
digitalWrite(SERIAL_DATA, data & BIT_MASK >> i);
digitalWrite(SHIFT_CLK, HIGH);
}
}

BIT_MASKってのは定数でB10000000です。ループのたびにシフトしてデータと論理積を取っていますので、目的のビットの内容がSERIAL_DATAピンに反映されるってだけです。
さて。
見ての通り、引数がuint8_tですので8ビット幅ですが、これを安直にuint16_tとし、forループの i<8 を i<16 に変えて実行してみると、なぜか上位8ビットが送信されていないような振る舞いになりました。つまり右側は正常にデータが表示されるのですが、左側はデータが表示されていませんでした。理由がいろいろ考えてもよくわからなかったので、8ビットに戻し、呼び出し側で上位ビット、下位ビットを引数として2回呼ぶ形でコーディングしたらあっさりとうまくいきました。つまりこんな感じでコールします。
colShiftOut(bitPattern>>8); 
colShiftOut(bitPattern);
これ、マトリクスを追加するたびに>>16とか>>24としたものを追加する必要があるのか(うんざり)という印象だったので、こんな感じで定数いじくって対処できるようなコードにしてみたところ、劇的に「遅くなり」表示がちらつくようになりましたorz

for ( int sendCount = sendLimit; sendCount >= 0; sendCount-- ) {
shiftColumns = sendCount * 8;
colShiftOut(bitPattern >> (shiftColumns));
}
非常に腑に落ちませんが、まぁそういうもんなのでしょう。
colShiftOutをわざわざ作ったのはshiftOutが8ビット送信限定っぽかったので、16ビット一気に送れたらいいなぁという目的があったからなのに、目的を果たせませんでしたw
意図通りに送信できてないっぽい問題は、シフトレジスタを2つ繋いで各ピンにLEDを一つだけつないだ実験回路を組んで動きを検証したほうがよいかも知れません。
まだまだ経験不足ですが、2つつなげた場合も意図通りの表示ができるようになりましたので満足満足。
<<<<<===== 削除ここまで


※行側の74HC595は行スキャン用なので、どのタイミングでもいずれか1ビットしか立ってません。折角256パターンの表現ができるのにもったいない感じがします。パルスを送るたびに8つある出力ピンのうちいずれか一つを順番に有効にしてくれるようなカウンタICってないのかな?多数マトリクスを接続していくとすると、スキャン専用信号線はパルス送るだけ、パターン用データはマトリクス分のパターン送るだけ、っていう構成にしたほうがプログラムもシンプルになるような気がします。

【おまけ】
接続順が同じ場合でしか使えませんが、動作チェック用に左→右→改行みたいな順序で1つずつ点灯していくチェック用ロジックです。

//起動時LEDマトリクスチェック。LEDを順番に一つずつ全て点灯する。
int colWidth = 16;
int sendLimit = colWidth / 8 - 1;

for ( int row = 0; row < 8; row++ ) {
for ( int col = 0; col < 16; col++ ) {
buffer = 0x8000 >> col;
for ( int sendCount = sendLimit; sendCount >= 0; sendCount-- ) {
colShiftOut(buffer >> (sendCount * 8));
}
rowShiftOut(BIT_MASK >> row);
updateStorage();
delay(20);
}
}

へたくそなコードだな、とか苦情は受け付けません。


0 件のコメント:

コメントを投稿

ESP32 Devkit C での疑問点

 前回の投稿から放置状態にあった当ブログですが、再び何かしら作ろうということで、スマートコンセントもどきに取り組んでいます。 回路なども一応動作するものができたのですが、ブレッドボードから移行するために基板に用意しておいたピンソケットにESP32を取り付けたところ、なぜか動作しま...