ライン

PIC32のDMA機能を使ったLEDディスプレイ表示実験

ライン

2018年4月30日
最終更新日 2019年12月8日 フレーム重み付けでメモリ削減

 街中でLEDを多数並べて作られた表示板をよく見かけるようになりました。駅の行き先表示板からビル壁面に据え付けられた大型ディスプレイまで、単色のものからフルカラー表示されているものなど、街を歩いていると様々なLEDディスプレイを見ることができます。
 その昔学生だった頃、8×8=64個の赤色LEDをユニバーサル基板に格子状にハンダ付けして簡易表示板を作った身としては、気になって仕方がありません。調べてみると64×32のRGBカラーLEDの表示板が通販で3000円ちょっとで買えることがわかったので、思わず買ってしまいました。説明書などは一切付いていませんでしたが、ネット上の情報と実験により制御方法がわかりました。
 (2020.10.10) 今では64×64のものが3000円で買えるようになりました。

実験画像


構造

 私が購入したLEDディスプレイはこちらの商品です。
 P3 RGBピクセルパネルHDビデオディスプレイ64×32ドットマトリックスSMD LEDディスプレイモジュール192×96mm
 この64列×32行のRGB LEDディスプレイの内部構造は、下図のようになっていることが実験から推測できました。あくまで私の推測のため、詳細な異なるかもしれませんが、模式図として認識してください。また、今後仕様変更の可能性もあるのでご注意ください。
 全体で大きく上下に半分に分かれており、64列×16行の同じ構造のものが2つつながっています。それぞれに64ビットのシフトレジスタがR、G、B用に3本あり、同一のクロック信号でシフトします。これで列方向のオン、オフを制御します。シフトレジスタにはラッチが接続されており、オンとなっているビットのLEDをドライブする定電流源がつながっています。
 一方、行方向は4ビットのデコーダがあり、16行の内のどの行を選択するか指定します。このようにRGBそれぞれ64×16のマトリクスを形成しており、一度に点灯できるLEDは1行分(上下合わせると2行分)だけとなるため、全体を点灯しているように見せるためには、高速で行を順次変更し続けるダイナミック点灯の制御を行う必要があります。

LEDディスプレイの内部構成図


点灯方法

 このディスプレイの点灯には、以下の手順で信号処理を行います。

(1)シフトレジスタにデータを送信

  R1/G1/B1、R2/G2/B2それぞれに信号を与え、CLKにパルスを送ることで1番目の色を決定します。
  R1/G1/B1が上半分、R2/G2/B2は下半分の表示用信号です。CLKは立ち上がり時にシフトするようです。
  これを64回繰り返し、全てのシフトレジスタに任意の値を書き込みます。

(2)OEをHにしてLEDを消灯

  瞬間的な表示の乱れを防ぐため、一瞬ですが全てのLEDを消灯します。

(3)A/B/C/Dの4ビット信号で表示行を決定

  Aが最下位、Dが最上位の4ビット信号を出力し、表示する行(セクション)を決定します。
  セクションは上半分と下半分の同じ行が同時に指定されます。

(4)シフトレジスタからラッチに転送

  LATにパルスを与えることで上下それぞれのラッチにシフトレジスタからデータが転送されます。
  LATは立ち下がり時に転送されるようです。

(5)OEをLにしてLEDを点灯

  これで上下各1行分のLED表示が行われます。

(6)一定の時間を待つ

  人間の目に残像として残すため、LEDを点灯させてから少し時間をおきます。

 以上の(1)から(6)を繰り返し、シフトレジスタへの送信信号と表示行を高速で変更していくことで、画面全体に対し任意の表示をさせることができます。


濃淡表現方法

 上記の方法で、任意のLEDについてRGB各色の点灯、消灯をさせることができましたが、これだけでは黒を含めた8色しか表現することができません。しかし、1つ1つのLEDに対して点灯させる時間を制御し、目に映る明るさを変えることで擬似的に濃淡のある中間色を表現することができます。
 例えば右図の場合、同じ場所のLEDを4回、高速で点灯または消灯を繰り返すことで、5段階の明るさを表現可能となります。実際には今回の実験では同じ行のLEDに対して15回繰り返し行い、RGB各色16段階、すなわち16×16×16=4096色(12ビット色)相当を表現することにしました。
 では実際にどの程度の周期で点灯、消灯を行うのがよいかですが、全画面分の出力を60分の1秒で行えばチラつきは感じないと経験的に知っているので、それを目指します。60分の1秒の間に16行×15回の繰り返しとなるため、1回あたり69.4マイクロ秒、周波数でいうと14.4KHzで1行分のLED点灯パターンを更新することになります。

濃淡表現方法
濃淡表現方法


制御回路

 本LEDの制御には13本の信号線が必要です。コネクタのピン配置は右図のようになっており、一般的に「HUB75」と呼ばれる規格のようです。
 制御用マイコンにはPIC32MX250F128Bを採用しました。28ピンのDIPタイプで、動作クロック最高50MHz、RAMは32Kバイト搭載しています。単純なLEDのオンオフだけの制御であれば8ビットマイコンでもできますが、濃淡表現するのであればこの程度のスペックがあったほうがよさそうです。実際の動作は内蔵FRCによる48MHzとしました。
 ピン接続は通常の汎用I/Oでも制御できますが、今回は6本のシフトレジスタ用信号とCLK信号の出力に、PICマイコンのPMP(パラレルマスターポート)機能を使用しましたので、この回路図のような配線となっています。なお、メモリ容量が倍のPIC32MX270F256Bでも同じ回路で使用できますが、USB非搭載のPIC32MX150F128BやPIC32MX170F256BではPMP関連のピン配置が全く異なるため、このままでは使用できません。

 電源はLEDディスプレイ用には5V、2A以上のものが必要です。また制御マイコンには3.3Vの電源が必要です。私はそれぞれにACアダプタから電源をとりました。なお、LEDディスプレイは5Vで動作していますが、マイコンとのインターフェイスは3.3Vでも問題なく認識されます。

HUB75コネクタピン配置
HUB75コネクタピン配置

回路図
回路図

DMAとPMPについて

 このLEDディスプレイを表示させるためには、先に述べた手順で信号を出力し続ける必要があります。マイコンの汎用I/Oポートに信号を出力し続けるプログラムでも実現できますが、上記のタイミングで出力し続けるのは、32ビットマイコンにとっても重い処理となり、静止画を表示するだけならともかく、アニメーションしたり、その他の処理をする余裕がなくなってしまいます。そこでまず考えるのは、DMA(Direct Memory Access)機能を使えないか、ということです。DMAはCPUを介さずにメモリからメモリに転送する機能ですが、PIC32の場合周辺機能用レジスタ(FSR)がメモリに割り当てられているため、周辺機能もDMAで扱うことができます。
 DMAで置き換えたいのはシフトレジスタに64個分のデータを送信する部分です。R1/G1/B1/R2/G2/B2の6ビットデータを同時に出力した後、CLKにパルスを与えます。この動作をさせるのにちょうどよいのがPMP(パラレルマスターポート)機能です。
 PMPはメモリや液晶のようなパラレル接続する周辺装置とのインターフェイスを想定した機能で、右図のように8ビットのパラレル信号をPMDINレジスタに書き込みした後、特定のタイミングで自動的に書き込みパルスを発生させることができます。アドレス信号やチップセレクト、読み込みパルスにも対応していますが、今回は使いませんので無効にしておきます。
 無効に設定したピンは本来なら汎用I/Oとして使えるはずですが、アドレス用ポートはマイコンチップのバグにより、汎用I/Oとして使用することができません。

PMP概説図
PMP概説図

 次にPMPへの出力をDMAで連続動作させることを考えます。このとき、メモリ上には転送したいデータが必要数分連続して並んでいるものとします(DMA転送用バッファ)。
 PMP出力は1回の処理完了に指定した時間が掛かります。PMP出力をDMAで行う際、PMDINレジスタに対して送りたいデータ数分を単純に転送させると、PMPの処理完了を待たずに次のデータを転送することになるため、エラーが発生します。そこで、右図のような工夫が必要です。
 DMAチャネルの設定で、1回の連続する転送サイズ(セルサイズ)を1バイトとします。そして転送開始イベントとしてPMP終了割り込みを指定します。PMP終了割り込みは、PMPの処理が完了して次回データが受け付け可能となった時点で発生する割り込みです。割り込みといっても、DMAの転送イベントとして使う場合、割り込み処理ルーチンは不要です。割り込み許可フラグもオフのままとします。
 これにより、1バイトのPMP出力処理が完了すると、DMAチャネルに対して次の転送開始イベントが発生するため、連続的に出力することができます。
 DMAチャネルに対する最初の転送開始指示はCPUで行います。実際には1行分(64個)の処理間隔をタイマーに設定し、タイマー割り込み処理ルーチンにてDMAチャネルの強制転送開始フラグを立てます。
 転送終了は転送元サイズ、転送先サイズのどちらか大きいほうまで転送されるとDMAチャネルが無効化されるので、自動的に行われます(ブロック転送の終了)。このとき、割り込みを発生させることができ、割り込み処理ルーチンにてLEDのシフトレジスタからラッチへの取り込みや次回転送の準備を行います。そして、次回のタイマー割り込みにより次のブロック転送が開始されます。

PMPをDMAで連続動作させる方法
PMPをDMAで連続動作させる方法

実験結果

 PIC32MX250F128Bを用いて、これまでに述べた方法で実際にLEDディスプレイを表示させた結果が右の写真です。RGBの原色だけではなく、中間色も表現されていることがわかります。1秒間に60回の画面更新を行っており、肉眼でチラつきは全く見られません。写真で見るより実際にははるかに明るく、眩しいので普段は紙をかぶせて実験しています。

 上で述べたDMA転送用のバッファは、1回分(64バイト)だけではなく全画面表示分の領域を確保しました。必要な容量は1回64バイト×15回×16行=15Kバイト 1行64バイト×4フレーム×16行=4Kバイトです。これにより静止画であれば、一度バッファにデータを作成しておくと更新が必要ないため、CPUの処理はほとんど不要となります。実際にはDMA転送開始のためのタイマー割り込み処理とブロック転送終了割り込み時の処理だけです。オシロスコープで観測してみると全時間の約2%しかCPUは動作していないことがわかりました。
 また、LEDディスプレイへの転送にも空き時間があり、1秒間の表示回数を倍の120回にすることも可能なことがわかります。1LEDあたりの繰り返し数を31回にして、表現可能な色数を32×32×32=32768色(15ビット色)相当にすることも考えられますが、必要なバッファ容量が31Kバイトとなるため、よりRAM容量の大きいマイコンが必要となりそうです。必要なバッファ容量は1フレーム増えるので5Kバイトです。
(2019.12.8) 15階調の濃淡表現は4フレームで表示時間の重み付けをすればよいとのアイディアを掲示板経由で連絡いただき、改良しました。これにより、メモリ効率が大幅に改善しました。

フォント表示
フォント表示。実際のデモプログラムでは上にスクロールします

グラデーション表示
グラデーション表示

オシロスコープで観測
静止画出力時のCPUとCLK信号をオシロスコープで観測。
CPUは割り込み処理中に特定ポートをHにすることで観測。

ダウンロード

 今回の実験で作成したシステム、サンプルプログラムのソースファイルとHEXファイルを公開します。サンプルプログラムではフォントスクロールとグラデーションを10秒ずつ繰り返します。
 実験に使用した環境はMPLAB X IDE v4.00とXC32 Compiler v1.42です。ソースプログラムをコンパイルする場合、文字コードはUTF-8を、最適化オプションはO1を指定してください。

プログラム一式のダウンロード

(2019.12.8) フレームごとの表示時間の重み付けにより使用メモリ削減しました。

 PICマイコンへの書き込みについては、上記の回路図では記載省略しています。PICkit3等を用いて書き込んでください。書き込みの際、LEDディスプレイがつながっていると、激しく光る場合があります。それも楽しいですが、できれば別の書き込み用ボード上で書き込んだほうがよさそうです。参考にPICkit3で書き込む際の最低限の回路を右図に掲げておきます。

PICkit3接続図
PICkit3で書き込むだけの回路


最後に

 以前から自作したいと思っていたRGBカラーのマトリクスLED電光表示板が、自作で想定していたものより解像度が高く、しかもはるかに安く入手できてしまったため、ちょっと拍子抜けしてしまった感はありますが、これほど低価格で手に入るのはやはりいい時代です。
 街にあふれるLEDディスプレイを見かけた時には、本ページを参考に、内部構造や制御方法などに気をめぐらせてみてはいかがでしょうか。

 何かご不明点やご意見などありましたら、お気軽にこちらの掲示板に書き込みをお願いします。





64×64ドット版 (2019.6.30追加)

 縦のドット数が2倍の64×64ドット版を入手でき、ほぼ同じ回路での制御に成功しました。使用したのはこちらの商品です。なんと3000円で購入できました。
 64×32ドット版との違いを調べてみたところ、インターフェイスがHUB75Eと呼ばれ、信号が1本追加されているようです。入手したものの型番等が不明なため、同様の製品で同じ結果となるかはわかりませんが、おそらく64×64のものは大抵同じ構造だと思います。
 縦32ドット版では上16行と下16行に2分割されていましたが、64ドット版では上下32行ずつに分かれています。そのためデコーダは5ビット必要となり、信号「A」〜「D」の他に「E」が追加されています。その他の違いはなさそうです。行数が倍になる分、1画面全体を表示するために信号を送る時間は倍となります。

 使用した回路は64×32ドット版とほぼ同じですが、信号「E」をマイコンのRB15に接続しました。また、画像領域が増えるため、マイコンはRAM容量の大きいPIC32MX270F256Bとしました。
 制御プログラムもほぼ同じですが、行数が倍になっても1画面60分の1秒での表示はキープしないとチラつきが発生するため、横64個のデータを送ってから次の64個のデータを送るまでの待ち時間を縮めました。1行あたりの周波数は28.8KHzとなります。縦32ドット版ではまだ余裕がありましたが、これでほとんど余裕がなくなりました。
 実験に使用したデモプログラムは、64×32ドットと同様のグラデーションとフォント表示のほか、HSV色空間の表示も試してみました。

プログラム一式のダウンロード

(2019.12.8) フレームごとの表示時間の重み付けにより使用メモリ削減しました。

グラデーション表示
グラデーション表示

フォント表示
フォント表示

64×64ドットマトリクスRGB LED
64×64ドットマトリクスRGB LED

HUB75Eコネクタピン配置
HUB75Eコネクタピン配置

回路図
回路図

HSV色空間
HSV色空間

Copyright (C) KenKen All Rights Reserved.