JavaScript が無効です。有効にしてください。
ななよんのホームページ
トップ > 電子工作 > 鉄道部品制御のすすめ > 【スクロールあり】ESP32 で 8000 系セレクトカラー表示器を光らせる

【スクロールあり】ESP32 で 8000 系セレクトカラー表示器を光らせる

最終更新日さいしゅうこうしんび:2023-08-10

 東京メトロ 8000 系(メトハチ)の側面セレクトカラー行先表示器を ESP32 で光らせる方法になります。日亜製 LED モジュールの解説は、こちらをご覧ください。

配線

 高校卒業に伴い Illustrator が使えなくなったため配線図はありません。以下の点に注意してください。

  • ESP32 は 3.3 V、LED は 5 V です。3.3 V レベルの信号を 5 V レベルに変換する必要があります。LVCH16T245(秋月電子通商の通販コード:K-15696)を使用してください。
  • アドレスの指定にはカウンタ IC 4520(TC4520BP 等)が必要になります。秋月電子通商の通販コートは I-10918 です。
  • ESP32 には入力専用ピンや起動に関係するピン、内部フラッシュメモリのピン等使用できないピンがありますので、それらを避けてピンを割り当ててください。
注意
 LVCH16T245 モジュールの使い方のページは、やはり Illustrator が使えないため、ここに簡単な使い方のメモを残しておきます。このモジュールは、16 ビットなので 16 本の信号線のレベル変換が可能です。8 ビットが 2 つに分かれていて、それぞれアウトプットイネーブルやディレクション(方向)の設定が可能です。出力を有効にするには、/nOE ピン(n は 1 または 2 で、前のスラッシュは負論理(上バー)を表しています)を GND に落とすことで有効になります。有効にしないと、出力が出ません。また、nDIR ピンが H のとき(基板上にプルアップ抵抗があるため H がデフォルト設定)A から B に、GND レベルのとき B から A といった方向になります。なお、nDIR ピンの H レベルは A 側電源(VA)に接続します。
 私の場合、B 側を 3.3 V IN、A 側を 5 V OUT として使用しました。この場合、nDIR は L になり、VB に 3.3 V、VA に 5 V を印加してあげます。
 4520 も電圧変換が必要です。4520 は 3.3 V でも 5 V でも動作するので、「4520 を 5 V で動かしクロックとクリアをレベル変換回路にかける」か「4520 を 3.3 V で動かし出力をレベル変換回路にかける」かは自由です。
 ちなみに、4520 は同じ回路が 2 組入っていて、どちらも同様に動作するので、配線しやすいほうを使用してください。余った回路は、入力ピンを必ず H か L に固定してあげてください。浮かせた場合、CMOS ですので消費電力の増大や故障の原因になります。この場合、出力ピンはオープンにします。
 配線についてよくわからない場合は、お問い合わせフォームや Twitter の DM で気軽に質問してください。

 メトハチフルカラー表示器は、縦 32 ドットですが、16 ドットのモジュールが上下に並んでいるので、それぞれの段に信号を入れる必要があります。今回は、3 本ずつあるデータ線を独立させ、その他のピン(WE、アドレス線等)は共通化しました。

固定表示

 制御の考え方は Arduino UNO を使うときと同様です。ただし、冒頭にも書いた通り電圧変換が必要なので注意してください。使用するピンは #define でマクロ定義します。使用できないピンに注意して、各自 自分の基板に合わせて #define を変更してください。また、表示データはこちらで作成してください。使いたい色はスケッチ内で決めるため、表示データ作成時は原色のみを使用してください。色は赤・緑・青各色 3 ビットになります。

表示の様子

 上の写真は、2023 年の元旦に合わせて制作した画面です。色の定義(セレクトカラー)を変更することで、このようなイラストの表示も可能です。

 サンプルスケッチは以下になります。ESP32 は I/O が早いため、デジタル出力の後に 1 us のディレイを入れています。

// ピンの定義。環境に合わせて変更してください
#define CTCLR 32  // カウンタ 4520 のクリア端子
#define CTCLK 33  // カウンタ 4520 のクロック端子
#define WE    25  // 6
#define MSEL  26  // 8
#define D2A   27  // 11_UPPER
#define D1A   14  // 12_UPPER
#define D0A   12  // 13_UPPER
#define D2B   13  // 11_LOWER
#define D1B   15  // 12_LOWER
#define D0B   16   // 13_LOWER
#define CLK   4   // 14

const unsigned char data[32][112] PROGMEM = {
  // 表示データを入れる。
};

// 色定義。{B, G, R} の順で指定します。
uint8_t color[8][3] = {
  {0,0,0}, // 黒
  {0,0,7}, // 赤
  {0,7,0}, // 緑
  {0,7,7}, // 黄
  {7,0,0}, // 青
  {7,0,7}, // マゼンタ
  {7,7,0}, // シアン
  {7,7,7}, // 白
};

void setup() {
  pinMode(CTCLR, OUTPUT);
  pinMode(CTCLK, OUTPUT);
  pinMode(WE, OUTPUT);
  pinMode(MSEL, OUTPUT);
  pinMode(D2A, OUTPUT);
  pinMode(D1A, OUTPUT);
  pinMode(D0A, OUTPUT);
  pinMode(D2B, OUTPUT);
  pinMode(D1B, OUTPUT);
  pinMode(D0B, OUTPUT);
  pinMode(CLK, OUTPUT);
  
  // カウンタリセット
  digitalWrite(CTCLR, HIGH);
  delayMicroseconds(1);
  digitalWrite(CTCLR, LOW);

  // データ送出
  writeColor(color);
  for(unsigned char i=0; i<16; i++) {
    for(unsigned char j=0; j<112; j++) {
      sendData1(pgm_read_byte(&data[i][j]));
      sendData2(pgm_read_byte(&data[i+16][j]));
      digitalWrite(CLK, HIGH);
      delayMicroseconds(1);
      digitalWrite(CLK, LOW);
      delayMicroseconds(1);
    }
    digitalWrite(WE, HIGH);
    delayMicroseconds(1);
    digitalWrite(WE, LOW);
    delayMicroseconds(1);
    digitalWrite(CTCLK, HIGH);
    delayMicroseconds(1);
    digitalWrite(CTCLK, LOW);
    delayMicroseconds(1);
  }
}

void loop() {
  // empty
}

// 上段に送るデータを指定する関数
void sendData1(uint8_t d) {
  switch(d) {
    case 0:
      digitalWrite(D0A, LOW);
      digitalWrite(D1A, LOW);
      digitalWrite(D2A, LOW);
      break;
    case 1:
      digitalWrite(D0A, HIGH);
      digitalWrite(D1A, LOW);
      digitalWrite(D2A, LOW);
      break;
    case 2:
      digitalWrite(D0A, LOW);
      digitalWrite(D1A, HIGH);
      digitalWrite(D2A, LOW);
      break;
    case 3:
      digitalWrite(D0A, HIGH);
      digitalWrite(D1A, HIGH);
      digitalWrite(D2A, LOW);
      break;
    case 4:
      digitalWrite(D0A, LOW);
      digitalWrite(D1A, LOW);
      digitalWrite(D2A, HIGH);
      break;
    case 5:
      digitalWrite(D0A, HIGH);
      digitalWrite(D1A, LOW);
      digitalWrite(D2A, HIGH);
      break;
    case 6:
      digitalWrite(D0A, LOW);
      digitalWrite(D1A, HIGH);
      digitalWrite(D2A, HIGH);
      break;
    case 7:
      digitalWrite(D0A, HIGH);
      digitalWrite(D1A, HIGH);
      digitalWrite(D2A, HIGH);
      break;
  }
  delayMicroseconds(1);
}

// 下段に送るデータを指定する関数
void sendData2(uint8_t d) {
  switch(d) {
    case 0:
      digitalWrite(D0B, LOW);
      digitalWrite(D1B, LOW);
      digitalWrite(D2B, LOW);
      break;
    case 1:
      digitalWrite(D0B, HIGH);
      digitalWrite(D1B, LOW);
      digitalWrite(D2B, LOW);
      break;
    case 2:
      digitalWrite(D0B, LOW);
      digitalWrite(D1B, HIGH);
      digitalWrite(D2B, LOW);
      break;
    case 3:
      digitalWrite(D0B, HIGH);
      digitalWrite(D1B, HIGH);
      digitalWrite(D2B, LOW);
      break;
    case 4:
      digitalWrite(D0B, LOW);
      digitalWrite(D1B, LOW);
      digitalWrite(D2B, HIGH);
      break;
    case 5:
      digitalWrite(D0B, HIGH);
      digitalWrite(D1B, LOW);
      digitalWrite(D2B, HIGH);
      break;
    case 6:
      digitalWrite(D0B, LOW);
      digitalWrite(D1B, HIGH);
      digitalWrite(D2B, HIGH);
      break;
    case 7:
      digitalWrite(D0B, HIGH);
      digitalWrite(D1B, HIGH);
      digitalWrite(D2B, HIGH);
      break;
  }
  delayMicroseconds(1);
}

// 色データを書き込む関数
void writeColor(uint8_t c[8][3]) {
  digitalWrite(MSEL, HIGH);
  for(uint8_t y=0; y<8; y++) {
    for(uint8_t x=0; x<3; x++) {
      sendData1(c[y][0]);
      sendData2(c[y][0]);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(1);
      digitalWrite(CLK, LOW);
      delayMicroseconds(1);
      sendData1(c[y][1]);
      sendData2(c[y][1]);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(1);
      digitalWrite(CLK, LOW);
      delayMicroseconds(1);
      sendData1(c[y][2]);
      sendData2(c[y][2]);
      digitalWrite(CLK, HIGH);
      delayMicroseconds(1);
      digitalWrite(CLK, LOW);
      delayMicroseconds(1);
    }
    digitalWrite(WE, HIGH);
    delayMicroseconds(1);
    digitalWrite(WE, LOW);
    delayMicroseconds(1);
    digitalWrite(CTCLK, HIGH);
    delayMicroseconds(1);
    digitalWrite(CTCLK, LOW);
    delayMicroseconds(1);
  }
  digitalWrite(MSEL, LOW);

  // カウンタリセット
  digitalWrite(CTCLR, HIGH);
  delayMicroseconds(1);
  digitalWrite(CTCLR, LOW);
  delayMicroseconds(1);
}

広告

和英交互表示+部分スクロール

 複雑な制御に挑戦してみましょう。種別は固定表示、行先は日本語と英語を交互に表示させ、右下の領域に停車駅をスクロールさせるサンプルプログラムです。行先の和英切り替えにはタイマ割込みを使用しています。うまくいけば、下の写真のように動作するはずです。架空の「野々山メトロ 新都市通線」の急行の表示です。

表示の様子 表示の様子

表示データの作成

 表示データは、以下の 4 種を別で作成します。

  • 上段(日本語、種別部分含む、16 x 112)
  • 上段(英語、種別部分含む、16 x 112)
  • 下段の種別部分(16 x 32)
  • スクロール領域(16 ドット文字で 6 文字以上のデータであること)

 上段は和英を切り替えますが、この例では種別部分は共通にします。

 先ほどの新都市通線の表示を作りたいので、表示データは下のように作りました。

上段日本語 上段英語 下段種別部分 スクロール部分

サンプルスケッチ

 この例のサンプルスケッチは、ページが重くなる原因なのでダウンロード形式にしました。ファイルは分割してあります。フォントのライセンス上再配布が可能なため、この例のみ、表示データを同梱しています。オリジナルの表示データを作る前に、新都市通線のサンプルで動作を確認してみてください。スクロール部の横幅は 6 文字以上でないと動作しないため、注意してください。逆に、6 文字以上であれば、(変数がオーバフローしない限り & プログラムメモリの許す限り)どれだけ大きな横幅でも動作します。

 サンプルをダウンロードする。※確認画面等なしにダウンロードが始まりますのでご注意ください


広告
戻る

右メニュー

【表示がおかしい・崩れる場合】PC では Shift と F5 を同時押し。スマホではキャッシュを削除してみてください。



とうサイトでは Cookieくっきー使用しようします。くわしくは上記じょうきの「プライバシーポリシー・免責事項等めんせきじこうとう」もご確認かくにんください。



上へ戻る