MAX7219の7セグLEDモジュールを使う2020-05-21
Teensy Audioのテストは一旦置いておいて、今回は表示装置の話です。
ルーパーやサンプラー的な機材の操作系として、最初はLED付きのスイッチを並べようと思っていました。しかし先の記事の通り、Teensy4.0に専用のAudio boardを載せると使える入出力ピンが残り僅かなので、それだとスイッチ4つ(8pin)くらいしか使えません。
OLEDなら電源(VccとGND)含めたった4つの端子で、様々な表示が出来ますが安いのものはあまりに小くて、大きくなると値段が急に上がってしまいます。
MAX7219の7セグLEDモジュール
ネットショップを彷徨って目に留まったのが、写真のような7セグメントLEDのモジュールでした。
これは8桁(4桁*2)の7セグLEDが基盤に配線された状態で売られており、電源を除けば3つのピンだけで全て制御できるようです。値段はe-bayやAliexpressあたりだと何と数百円なので1個買ってみました(ただし最近は送料が高いので注意)。
因みに基盤の裏に付いてるチップはMAX7219と言って、シリアル接続のディスプレードライバらしく、7セグメントLED(ドットを含めると8LED)を最大8個=64個のLEDを制御できるとの事。従って8*8のドットマトリックスLEDの制御に使わたりもします。
MAX7219ライブラリ
Arduino IDEのツール>ライブラリの管理で”MAX7219”を検索すると関連のライブラリが4つほど出て来るので一応全部インストールしてみました。
今回はオーディオのように重い処理ではないので、以前買っておいたArduino UNO互換ボードで実験する事にしました。ところがいざサンプルスケッチを走らせると、どのライブラリも妙な文字が表示されてすぐ消えたりしてちゃんと動きません。何度も配線を確認し、別のUNO互換ボードでも試しましたが同じ状態です。
そこで本命Teensy4.0で試したところ、まともに動くではないですか!ただよく見ると若干おかしな挙動が見られます。これはMAX7219ボードの方が壊れてる可能性もあるのでebayのセラーに問い合わせたところ、案の定技術的な事は全く分からないようで「使えなければ返金する」としか言いません。
仕方ないので別の店でもう一個買ってみるか等と考えながら放置していたある日、ふと思い立ってVCCの端子をArduinoの3.3V供給ピンに繋いでいたのを5Vピンに変更してみたら何と正確に動くではないですか!
実は商品説明では「compatible with 5V and 3.3V」とあったし、3.3VでもLEDが眩しいくらいで5Vは過剰だと思いそのまま使っていたのです。ただTeensy4.0も実は3.3Vピンで使っていたのに、こっちはもっとまともに動くのはやはり謎です。
DeditLedDisplay
取敢えずUNO互換機で5V出力を使えば上手くいくことは判ったので、この方法で先に進みます。
4つほどライブラリがあると書きましたが、その中で比較的文法が優しそうだし何より任意のパタンを表示出来るDeditLedDisplayを選んでみました。ルーパーでは録音レベルやループ長のインジケータとして、7セグの上下の小さな丸を表示させたかったからです。
とは言えこのライブラリの公式Webサイトは無く、上にリンクしたGitHubに超簡単な説明があるだけです。ただ、Digit7SegmentDemo.inoというサンプルスケッチを見ると大体文法が判ってきました。
スケッチに書かれた順で関数を解説していきます。
DigitLedDisplay ld = DigitLedDisplay(7, 6, 5);
ArduinoのどのピンとLEDボードのどのデータピンを繋ぐかを宣言します。ここでは下記のようにアサインしています。
- Pin 7 to DIN
- Pin 6 to CS (load)
- Pin 5 to CLK
ただしここで注意が必要なのは、Arduino IDEのツールからこのライブラリをインストールした際にできる同名のスケッチでは、何故かこのピンの順番が7,5,6になっています。なので何方でも動くはずですが、スケッチの記述と配線を間違えずに合わせる必要があります。
ld.setBright(10);
LEDの輝度を設定します。最小が1で最大が15です。因みにデフォルトの10では眩しいほど明るいので私は3~5で使っています。
ld.setDigitLimit(8);
7セグLEDを何個使うかを設定します。ここでは最多の8個なので8になっています。
ld.printDigit(12345678);
ここからはvoid loop()内に記述する実際の表示命令です。示したい8個の数字をそのまま書くだけです。ただしアルファベットは表示できないようです。
ld.clear()
これは表示した数字を一旦クリアする命令です。ここでは500msec毎に別の数字を表示しているので、クリアしなくても上書きされるだけだと思いますが…
ld.printDigit(i, 4);
これは表示したい場所を指定する方法です。最初の引数は表示したい数字ですが、このスケッチではiという変数にしてfor文で1から99まで表示させています。
2番目の引数は表示させる場所(桁)で、右から0,1,2,3…で左端が7です。もしここに何も書かなければ0(右端)を指定したことになり、最初の8文字の命令と同じです。このスケッチではポジション0と4でそれぞれ1~99まで表示させています。
ld.off(); ld.on();
これは直前に表示させた数字を点滅させているだけです。
ld.write(5, B01100011);
これがこのライブラリの肝ともいうべき関数です。数字やアルファベットに限らず、7セグメント+1ドット中任意のLEDを表示させられます。先ず、左の引数は表示する場所を示しています。さっきの関数とは逆の位置なので混乱しますね。
そして右の引数が表示させたいパタンです。最初のBは多分バイナリの意味で必ず付くようです。次の桁はドットの点滅で、当然0=Off、1=Onです。更に次の桁は7セグメント(数字の8)の一番上の横線のOn/Off。以下右回りに一周して、最後は8の真ん中の横線となります。
このスケッチでは”B01100011”というパタンですが、右図に従って見ていくと、最初のドットは無しで次のAはOn、BもOn、C,D,EはOffでF,GはOnですから、A,B,G,Fで構成される小さな丸というか「ロ」の字が表示されます。
オリジナルスケッチの作成
上述のスケッチをベースに、ちょっと改造してみました。最初の方はほぼ同じですが、一番違うのは上のロの字を使って左から右、右から左に順次点灯させているところです。下にコードを記載します。
/* Include DigitLedDisplay Library */
#include "DigitLedDisplay.h"
/* Arduino Pin to Display Pin
7 MOSI to DIN,
6 i/o to CS(load),
5 SCK to CLK */
DigitLedDisplay ld = DigitLedDisplay(7, 6, 5);
void setup() {
/* Set the brightness min:1, max:15 */
ld.setBright(3);
/* Set the digit count */
ld.setDigitLimit(8);
}
void loop() {
/* Prints data to the display */
ld.printDigit(12345678);
delay(500);
ld.clear();
ld.printDigit(22222222);
delay(500);
ld.clear();
ld.printDigit(44444444);
delay(500);
ld.clear();
/*Display from 0 to 99 on digit 0 and digit 4*/
for (int i = 0; i < 100; i++) {
ld.printDigit(i);
/* Start From Digit 4 */
ld.printDigit(i, 4);
delay(50);
}
for (int i = 0; i <= 10; i++) {
/* Display off */
ld.off();
delay(150);
/* Display on */
ld.on();
delay(150);
}
/* Clear all display value */
ld.clear();
delay(500);
/*Display from 100 to 199 */
for (long i = 100; i < 200; i++) {
ld.printDigit(i);
delay(25);
}
int dtime =100;
/*upper & lower small circle move left to right & right to left */
for (int i = 0; i <= 5; i++) {
ld.write(8, B01100011);
delay(dtime);
ld.write(7, B01100011);
delay(dtime);
ld.write(6, B01100011);
delay(dtime);
ld.write(5, B01100011);
delay(dtime);
ld.write(4, B01100011);
delay(dtime);
ld.write(3, B01100011);
delay(dtime);
ld.write(2, B01100011);
delay(dtime);
ld.write(1, B01100011);
delay(dtime);
/* start lower circle*/
ld.write(1, B00011101);
delay(dtime);
ld.write(2, B00011101);
delay(dtime);
ld.write(3, B00011101);
delay(dtime);
ld.write(4, B00011101);
delay(dtime);
ld.write(5, B00011101);
delay(dtime);
ld.write(6, B00011101);
delay(dtime);
ld.write(7, B00011101);
delay(dtime);
ld.write(8, B00011101);
delay(dtime);
}
/* Clear all display value */
ld.clear();
delay(500);
}
そして下の動画がこのスケッチを走らせた時の表示です。ここではLEDが白っぽく光っていますが、肉眼で見るともっと暗いオレンジ色です。
次はスイッチの操作に対して何か表示するようなものを作りました。以下がスケッチです。
/* Include DigitLedDisplay Library */
#include "DigitLedDisplay.h"
/* Arduino Pin to Display Pin
7 (MOSI) to DIN,
6 (i/o) to CS(load),
5 (SCK) to CLK */
DigitLedDisplay ld = DigitLedDisplay(7, 6, 5);
void setup() {
/* Set the brightness min:1, max:15 */
ld.setBright(3);
/* Set the digit count */
ld.setDigitLimit(8);
/* Arduino pin to 4 switch
pin1 to switch1
pin2 to switch2
pin3 to switch3
pin4 to switch4
all other terminals to the GND */
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
}
/* initialize intervals for loop below */
int one = 1;
int two = 0;
int three = 1;
int four = 0;
void loop() {
/* set delay time for scripts below */
int dtime =250;
/* display from "1" to "10" by each push switch1 */
if (digitalRead(1)==LOW){
ld.printDigit(one, 6);
one = one+1;
if (one == 11){one = 1;}
delay(dtime);
}
/* display from "0" on digt 0 to "7" on digit 7 by switch 2 */
if (digitalRead(2)==LOW){
ld.printDigit(two, two);
two = two+1;
delay(dtime);
if (two == 8){
two = 0;
}
}
/* turning circle by switch3 */
if (digitalRead(3)==LOW){
ld.write(3, B01100011);
delay(dtime);
ld.write(3, B00100011);
delay(dtime);
ld.write(3, B01000011);
delay(dtime);
ld.write(3, B01100010);
delay(dtime);
ld.write(3, B01100001);
delay(dtime);
}
/* moving circle from left to right by each pushing switch 4*/
if (digitalRead(4)==LOW){
if(four==0){
ld.write(4, B01100011);
delay(dtime);
four=1;
}else if(four==1){
ld.write(3, B01100011);
delay(dtime);
four=2;
}else if(four==2){
ld.write(2, B01100011);
delay(dtime);
four=3;
}else if(four==3){
ld.write(1, B01100011);
delay(dtime);
four=0;
}
}
/* Clear all display value */
ld.clear();
}
Arduino UNOのピン1~4を入力モード(プルアップ抵抗付き)に設定して、各スイッチに繋いでいます。もっとスマートな書き方がありそうですが、ここでは気にしないでください。
そして下がその結果です。やってる事は見ての通り、スイッチの順番(左から)で;
- 7桁目に1-10の数字を表示し繰り返す。1回押す毎に数字が1増え、押し続けるとそれを自動的に繰り返す。
- 右端から桁を左に移動しながら、1から7まで表示して元に戻る。スイッチ1と同じく押し続けると自動再生、
- 3桁目で上のロの字がグルグル回る(消えてるLEDが順次移動する)。これはコマ送り不可。
- 4桁目から1桁目に上のロの字が順次移動して元に戻る。連続再生/コマ送り両方OK。
Teensy4.0のその後
さて次は一旦棚上げしていたTeensy4.0です。3V供給のまま上の2つのサンプルを走らせると、最初のは書き込み直後は表示しないループがあり、一度リセットボタンを押した後はちゃんと動きます。
そして2番目のスケッチは書き込み直後はちゃんと動きますが、何十秒か放置した後スイッチを操作すると、LEDが格段に暗くなってしまいます。そして更に何十秒か放置した後、再びスイッチを押すと遂に何も表示されなくなります。
因みに最初から休まずに操作し続けると、LEDは明るいまま正常に表示されます。これは最初のスケッチと同じで、LEDを点滅させ続ければ正しく表示するが、休ませると電源供給が減衰していくという事でしょうか?
何れにせよ、Teensy4.0でも3V供給では正常に動かない事が判ったので5Vに切り替える事にしました。ただ、Teensy4.0にはUNOのような5Vピンは無く、代わりに3V~5Vまで対応するVinというピンがあるだけです。
このVinはその名の通り外部電源を繋ぐ為のピンで、電力供給用のピンではないと思っていましたが結論から言うと私の勘違いでした。全く別の事例でTeensyシリーズのVinピンを電力供給に使っているのを見つけたので、MAX7219の電源にも使ってみたら全て上手く動作するようになりました。
結論から言うと、MAX7219の電源は5V一択という事になりますが、それなら3.3Vと互換性が有るみたいな表記はやめて欲しいですね。それにTeensyシリーズのVinピンに関する説明も公式サイトには見当たらず、ちょっと不親切ですね。