MAX7219を使った8×8ドットマトリックスのArduinoライブラリを作った話

8×8ドットマトリックスが安く売ってた

中華ECサイトでは頻繁にセールが開催されてますが,特に年末には大きいセールがありますね.11.11の独身の日セールに始まりブラック・フライデーなど.そんな中いつものようにGearbestを彷徨っていたらこんなものを見つけました.

https://www.gearbest.com/other-accessories/pp_1257191.html?wid=1433363

これどっかで見たことあるなぁと思ったら同じものが秋月に売っていました.

http://akizukidenshi.com/catalog/g/gM-09984/

微妙に基板の色が違うけど,古いタイプなんでしょうかね?でも乗っているICは同じものの模様.秋月電子では2100円に対しGearBestでの価格は654円(レートによる変動など有り).なんと約1/3倍.面白いのでポチりました.

MAX7219とは

さてこれらのドットマトリックスLEDの制御に使われているMAX7219について調べました.データシートは↓になります.

https://datasheets.maximintegrated.com/en/ds/MAX7219-MAX7221.pdf

タイトルにもあるように,”Serially Interfaced, 8-Digit LED Display Drivers”,つまりもともとは7セグメントLED(右下のドットを含めて8セグ)のダイナミック点灯制御をやってくれるICみたいですね.1つのMAX7219で最大8個の7セグメントLEDを制御できる模様.更にMAX7219をシリアルに接続して制御できる7セグLEDを増やすことができるスグレモノ.なるほど,8×8ドットマトリックスなら1個に付きMAX7219 IC 1つですべてのLEDを制御できますね.

わりと便利そうなものだし,秋月電子にも利用した製品が売っているのですでにライブラリを誰か作成してそうなのでググってみました.が,出回っているのはドットマトリックスに文字を出力して流れるように制御するもの(電車内の電光掲示板みたいな感じ,最近の電車ではLCDが多くあまり見かけないが)がほとんどでした.

なんかこう,図形描写とかに特化したライブラリあってもいいんじゃないの,さらにProcessingみたいな感じで簡単に扱えるようにしようということで作りました.

ライブラリを使ってみる

作成したライブラリはGithubで公開しています.

https://github.com/pic-man749/Arduino_MAX7219_Library

利用するには↑サイトから”clone or download”,”Download ZIP”を選択してzipファイルを落としてください.あとは解凍してArduinoのライブラリ置き場(ユーザのドキュメント\Arduino\libraries)とかに置いてください.

簡単な使い方

LEDを1つずつ点灯していきドットマトリックスを埋めるようなプログラムをexampleに入れておきました.これを利用してみましょう.まず回路をつなぎます.SPIを利用しているので非常に単純です.配線は次のようになっています.

回路をつないだところ
Arduino      MAX7219
  5V  -------- VCC
  GND -------- GND
  11  -------- DIN
  10  -------- CS
  13  -------- CLK

そしてコードは↓のようになります(exampleにあるBasicと同じです).

#include<MAX7219_DotMatrix.h>

#define MATRIX_ROW 1
#define MATRIX_COL 4
#define MATRIX_DOT 8

// create instance, args:(row, column)
MAX7219_DotMatrix dm = MAX7219_DotMatrix(MATRIX_ROW, MATRIX_COL);

void setup() {

  // initialize method, you must call.
  dm.begin();

}

void loop() {

  for(int i=0; i<MATRIX_DOT*MATRIX_ROW; i+=1){
    for(int j=0; j<MATRIX_DOT*MATRIX_COL; j+=1 ){
      dm.point(j, i); // draw a dot at(j, i)
      dm.draw();      // draw. No change is made to the LED until this method is called.
      delay(100);
    }
  }

  // All LEDs turn off
  dm.allOff();
}

はじめに#includeでライブラリを読み込みます.そしてインスタンスを生成します.引数はドットマトリックスの個数を指定します.(x軸方向個数, y軸方向個数 )の順でセットします.そしてsetup()内で.begin()メソッドを呼び出して初期化します(必ず呼んでください).

その後はProcessingのようにpoint()を使って1ドットずつLEDのを点灯していってます.ちなみにドットマトリックスの左上の点が原点(0, 0)で右方向がx軸正方向,下方向がy軸正方向になります.

座標.これは2×4のドットマトリックスを設定したとき.

Processingと異なる点としては,.draw()メソッドを呼ぶまで描画されません.LEDの点灯データをいちいち呼んでたらMAX7219へ転送するのに時間がかかってフレームレートが落ちそうだと思ったのでこうしました.

使えるメソッド一覧

先にも述べたようにProcessingのように図形を描写することができます.Processingと同様に使うことのできるメソッドは↓になります.(便宜上描画メソッドとよんでいます)

  • point(x, y)
  • line(x1, y1, x2, y2)
  • triangle(x1, y1, x2, y2, x3, y3)
  • rect(x, y, w, h)
  • quad(x1, y1, x2, y2, x3, y3, x4, y4)
  • ellipse(x, y, w, h)
  • fill()
  • noFill()

Processingのように使えるとか言っておきながら(今のところ)これしか組み込んでません。゚(゚∩’▽’∩゚)゚。 この他,オリジナルのメソッドは次のようになります.

  • allOn()
  • allOff()
  • begin()
  • draw()
  • getPoint(x, y)
  • setBrightness({0x00-0x0f})
  • setDrawMode(bool)
  • setDirection({DM_DIRECTION_0, DM_DIRECTION_90, DM_DIRECTION_180, DM_DIRECTION_270})
  • toggle(x, y)

大体呼んで字のごとく動きますが,詳しい動作についてはwikiの方に書いてあるので参照してください.説明が必要そうなメソッドについて解説します.

setBrightness({0x00-0x0f})

このメソッドはドットマトリックスLEDの輝度を設定できます.LED全体の輝度設定になりますので,LED1個ずつの輝度を変えることはできません.引数は0x00から0x0fの範囲で与えてください.ハードウェア的な問題なのか,輝度が低すぎる(0x00~0x04くらい)と表示が安定しませんでした.

setDrawMode(bool)

このメソッドの引数にtrueをセットして呼ぶと,それ以降の描画メソッドで描画した部分のLEDが点灯します.また,引数にfalseをセットして描画メソッドを呼ぶと,描画した部分のLEDが消灯します.

setDirection()

このメソッドはドットマトリックスLEDの向きを設定します.引数は

DM_DIRECTION_0
DM_DIRECTION_90
DM_DIRECTION_180
DM_DIRECTION_270

で与えてください.今の所0度か180度しか対応してません.

苦労した点・改善点

覚書程度に苦労したところをメモしておく.

fill(), noFill()の実装

このライブラリでは円や四角形を描画することができるが,それを描画するときに枠だけLEDのを点灯させるか,中身を枠とともに埋めるか選択できる.それがfill(), noFill()メソッドである.枠を描画するのは比較的簡単な作業(線を描画するline()も作ったのでそれを頂点同士でむすぶだけである)が,その図形の中身を埋めるとなるとどう実装するのか知識がなかった.

とりあえず”塗りつぶし アルゴリズム”とかでググって出てきたサイトを参考にした.結果シード・フィルアルゴリズムというものを用いた.以下サイトを参考にさせていただいた.

https://www.hiramine.com/programming/graphics/2d_seedfill.html

簡単にアルゴリズムの説明をすると,

  1. 領域内の1点を起点とし,スタックに格納
  2. スタック内に要素があれば取り出し,そのポイントを埋める
  3. 取り出した要素の上下左右方向が枠線でないor埋まってなければスタックに格納
  4. 2へループ

という簡単なものである.領域内の一点をどう抽出するか迷ったが,とりあえず各図形の重心を取ることにした.このアルゴリズムはシンプルであるが,そのかわりスタック領域としてArduinoには大きなメモリを確保する必要があったり,ループが長くなってしまっていると思う.特にUnoなんて2kBのSRAMしかない.上で紹介した4連ドットマトリックスをつないだ場合,今の実装では計算上1024B必要になる.え,マジで?(今はじめて計算した)要改良.なぜこんなに食っているかというと,すべてのドット×2[byte]×2値(x,y座標)分も確保しているからである.すべてのドット分の座標を保存できる領域を確保する必要は十中八九ないのでなんとかする.

文字を表示したい

冒頭で他のライブラリはほとんど文字を表示するだけだとか言ってたけど結局文字を表示することもできたほうが便利だと思う.しかしこれまたキャラクタパターン分のメモリを確保する必要があるのでメモリの問題を解消する必要がありそうだ.たしかフラッシュメモリから呼び出すようなこともできた気がするのでそれを実装したい.表示する文字は0-9, a-Z, ア-ンと一部記号を予定している.できればキャラクタ液晶のHD44780互換にしたい.

Arduino Uno以外のサポート

現状Unoでしか動作確認を行っていない.Arduinoライブラリを名乗っておいてUnoだけとはなんとも格好が悪いので他でも動作確認はしたい.とりあえず手元にArduino M0があるので対応させておきたいと思う(動作電圧が3.3Vなのでトランジスタか何かでスイッチングする必要がありそう).

あとはMbedあたりでも使えるように移植したいですね.このライブラリを作成するにあたってArduino特有のコードの書き方はしないよう意識していたので多分すんなりできそう(大抵できない).

おわりに

まだまだ改良点はあるので今後も開発を続けていきたい.モチベーションが保てるといいな.

なにか困ったことがあったらコメントに書いておいてください.GitHubのissuesでもいいです.

余談

なんとなくAliexpressでも検索してみたらこちらにもあった

381円!?

頭にきたのでAliでも発注した.送料無料だし…

余談2

家の部品箱漁ってたらドットマトリックス単体が出てきた.

どんだけドットマトリックスうちにあんねん.

PIC MAN

ソフトとハードの両方の目線を持てるようになりたいです.

おすすめ

7件のフィードバック

  1. Akihabara より:

    はじめまして
    とても興味深い記事で、ワクワクさせて頂いております。

    私は、ドットマトリクスも文字だけじゃつまらないので、図形でも書いてみようかと思い
    ネットを辿っていたところ、こちらに到着致しました。
    まさにこんな感じの事をしたかったので、僭越ながら使わせていただこうと思っております。
    よろしくお願いいたします。

    SeeduinoV4.2でサンプルの動作確認いたしました。
    (サンプルTest2だけ不明でしたが個別に動きました。)
    LEDマトリクスは、やはりAliExpressで購入した格安品を使用しております。

    所で、setBrightness(); ですが、 設定0x0にして使用しました。
    スケッチで、1行だけ
         dm.setBrightness(0x00);
    を記入すると、最初の8x8のモジュールだけ暗くなり、
    2つ記入すると、最初の2つが暗くなりました。
    もしやと思い、4行繰り返しで記入したところ、8x8x4の全体が暗く設定されました。
    チップの数だけ設定したら成功したっていう感じです。

    いま使用しているマトリクスは、赤タイプクリアLEDのもので、非常に明るいです。
    ちょうど元年11月11日にセールに乗じて発注したところで、
    追って青タイプが到着する予定です。
    以上 失礼いたしました。

    • PIC MAN より:

      >Akihabaraさん

      ライブラリを使っていただいてありがとうございます!
      誰かのお役に立てられていること嬉しく思います。

      setBrightness()関数のバグ報告ありがとうございます。
      私の想定では一度呼ぶだけで全体の明るさを設定できると思っておりました。
      後ほど検証して修正したいと思います。

    • PIC MAN より:

      >Akihabaraさん
      ライブラリを修正しました。マトリックスの個数分輝度設定を繰り返すようにしたので一度setBrightness()メソッドを呼ぶだけですべてのマトリックスの輝度が設定できるようになっています。お手数ですがGithubからもう一度ダウンロードしていただけると幸いです。

      ちなみにですが、Test1, test2のサンプルコードは私がテスト用に書いたコードをそのままgithubにのっけてしまっただけなので特に意味はありません、ごめんなさい。

      そして残念なことに、このライブラリではメモリを多く消費するため8マトリックスを1つのATmega328Pでは制御できません…
      手元にそのようなマイコンがないので試してはいませんが、より多くのSRAMを搭載したArduino(互換機)であれば動くと思います。

  2. pachiras より:

    こんにちは、

    Arduino 初心者です。LED matrix を購入したものの、MAX7219 の使い方が分からず、こちらのライブラリを使わせていただきました。example をやってみましたが、これは楽しい!次はPC側からコマンドを送って、任意の場所を光らせることにチャレンジしたいです。また楽しい記事を期待しています!

    • PIC MAN より:

      >pachirasさん
      コメントありがとうございます!
      このライブラリを活用していただける方がいらっしゃるのはとても嬉しいです。
      実はこのブログ記事を書いた後に文字出力も可能になっています。アルファベット、記号、半角カナに対応していますが半角カナの出力の実装がうまくできていないのでほったらかしになっています。。
      もしご興味がありましたら、GitHubの方をご参照ください。

  3. gray より:

    コメント失礼します。Arduino 初心者です。こちらのライブラリを使用させて頂いております。exampleのbasic4を参考にMAX7219 8×8ドットマトリックスを8個、x方向に接続しアルファベットのスクロール表示をしたいのです。ですが5個までは表示され、右の3つが空白になってしまいます。解決法がわかりましたら教えて頂きたいです。よろしくお願いいたします。

  4. PIC MAN より:

    >grayさん
    ライブラリを使っていただきありがとうございます!
    推測ですが、Arduino Unoのメモリ容量が2KBと少ないため、ドットマトリックスの点灯状態を保存しているバッファが確保できていないことが原因と思われます。つまり、Unoではハード的な問題で8個のドットマトリックスを制御できません。すみません。。。
    未検証ですが、ESP-WROOM-32等を利用すれば8個のドットマトリックスでも動作可能ではないかと思います。ただし、ESP-WROOM-32ではピンの出力電圧が3.3[V]なので、5[V]の出力に変換する回路やレベルシフタIC等が必要になります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です