技術

Arduino互換のダ・ヴィンチ32Uからグラフィック有機ELモジュール100×16に絵を表示する

グラフィック有機ELモジュール(きれい!)

グラフィック有機ELモジュール

ダ・ヴィンチ32U with Arduino Bootloader(ちっさい!)

ダ・ヴィンチ32U with Arduino Bootloader

この前、HPのタワー型サーバであるHP ProLiant ML110 G7をNTT-X Storeで1万円ほどで入手しました。
このML110 G7、サーバとしては非常にコストパフォーマンスが高い(その代わりデスクトップPCとしては細かな仕様的に使いづらい点が多々ある)のですが、室温が20°C台後半になると冷却ファンが超高速回転をしはじめて最終的にダイソンの掃除機並の音になるという、ラックマウントサーバなどでは当たり前な騒音ですが自宅サーバとしてはかなりキツイ状態になってしまいます。

色々検索したところ、標準ファンの特性をオシロで調べられた方の記事(HP ProLiant G7 ML110 静音化:独楽日記:So-netブログ)を見つけまして、これならマイコン(特にArduino)で簡単に制御出来そうということが分かりました。
ちなみに、実際にArduinoでファンを制御された方がスケッチを公開されてるページが1ヶ月ほど前まであった気がするんですが、今は見つけられませんでした。
ファンの青線にPWMを流しこむだけでいいハズなのに、説明やスケッチが長すぎる気がしたので内容はちゃんと見てませんでした。
保存しておけばよかった。

とにかく、オシロの記事を元に手元にあったArduino Duemilanoveで試したところ、PWM対応ピンにファンの青線をつないでanalogWriteしてあげるだけで制御出来ました。
せっかくなんでファン制御用のArduinoを新たに購入する事にして調べてみるとArduino Leonardoという新しいArduinoが発売されるということが分かりました(5月末当時)。
当然新しいのがいいよねと思ったわけですが、このArduino Leonardo、部品が集約されてチップも小型化したというのに基板サイズがそのままで、なんだかとても不恰好(今見るとそうでもないですね…)。
ピン配置やシールドとかの互換性を考えると基板サイズを変えるわけにはいかないんでしょう(Arduino MiniとかNanoの流れはどうなったんでしょう?)。

他に良いのはないか?とさらに調べると、ダ・ヴィンチ32U with Arduino Bootloaderという、Arduino IDEを使った開発が行え、Leonardoと同じATMEGA32U4を搭載した超小型のマイコンボードがStrawberry Linuxで販売されてるのを発見。
また、グラフィック有機ELモジュール100×16なんて面白そうなものも同じところで販売されているのを発見し、これと組み合わせてML110 G7の温度やファン出力(%)を表示できたらカッコイイよねということでその方向でやってみることに。

このグラフィック有機ELモジュールは、ArduinoのLiquidCrystalライブラリを使えば一応はキャラクタ液晶として文字を表示する事は可能なんですが、キャラクタモードの場合文字がくっついて表示されそれを回避する方法がありません。
もう一つグラフィックモードというものがあり、そちらだと100×16ドットの明暗(2値)を自由に制御できます。
今回はグラフィックモードを使います。

未だ最終的なものは出来てないのですが、今回の目標を実現するにあたって一番の山場がグラフィック有機ELモジュールに何かを表示する事であり、とりあえずそこをクリア出来たのでスケッチを公開します。

にこにこ顔が左から右に流れるというのを繰り返します。
ピンの対応はマクロ定義に書いてます。
ダ・ヴィンチとArduinoのピンの対応はTETRASTYLE-dev-BLOG: Leonardo っぽいのを参照。
例えば、PIN_D4のデジタル14番ピンはPB3ピンに対応。
液晶の制御は4bitモードで行います。
全画素分のメモリを確保し、毎フレーム全画面を書き換えるコードになってるので、効率はあまり良くないと思います。
(グラフィック有機ELモジュールにも全画素分のメモリがあるので、メモリ効率優先なら本当はArduino側で確保する必要はないはずです。)
電源のオン・オフ、リセットをしたときに液晶の表示を安定させるために、デジタル出力ピンから液晶の電源をとってます。
(これはマズイ気が。)
分かりやすさ優先のためdigitalWriteを使ってますが、効率を考えるとポートレジスタを使うべきかなと思います。
この規模のスケッチを書いたのははじめてです。
というかArduino自体たいしてやってないのでまずい点があったら教えていただけると嬉しいです。

#define PIN_PW 21
#define PIN_RS 19
#define PIN_RW 18
#define PIN_EN 15
#define PIN_D7 10
#define PIN_D6 9
#define PIN_D5 8
#define PIN_D4 14

#define SX 100
#define SY 2
byte displayData[SX][SY];

#define GB(b, i) (((b & (1 << i)) != 0)?HIGH:LOW)

void pulseEnable() {
  digitalWrite(PIN_EN, LOW);
  delayMicroseconds(1);    
  digitalWrite(PIN_EN, HIGH);
  delayMicroseconds(1);
  digitalWrite(PIN_EN, LOW);
}

void pinModeAll(boolean mode) {
  pinMode(PIN_RS, mode);
  pinMode(PIN_RW, mode);
  pinMode(PIN_EN, mode);
  pinMode(PIN_D4, mode);
  pinMode(PIN_D5, mode);
  pinMode(PIN_D6, mode);
  pinMode(PIN_D7, mode);
}

void _send(boolean RS, boolean RW, boolean D7, boolean D6, boolean D5, boolean D4) {
  digitalWrite(PIN_RS, RS);
  digitalWrite(PIN_RW, RW);
  digitalWrite(PIN_D7, D7);
  digitalWrite(PIN_D6, D6);
  digitalWrite(PIN_D5, D5);
  digitalWrite(PIN_D4, D4);
  pulseEnable();
}

void wait() {
  pinMode(PIN_D7, INPUT);
  
  int busy = HIGH;
  while (busy) {
    digitalWrite(PIN_RS, LOW);
    digitalWrite(PIN_RW, HIGH);
    pulseEnable();
    busy = digitalRead(PIN_D7);
    digitalWrite(PIN_RS, LOW);
    digitalWrite(PIN_RW, HIGH);
    pulseEnable();
  }
  
  pinMode(PIN_D7, OUTPUT);
}

void sync() {
  wait();
  for(int i = 0; i < 5; i++) {
    _send(LOW, LOW, LOW, LOW, LOW, LOW);
  }
}
void functionSet() {
  wait();
  _send(LOW, LOW, LOW, LOW, HIGH, LOW);
  _send(LOW, LOW, LOW, LOW, HIGH, LOW);
  _send(LOW, LOW, HIGH, LOW, LOW, LOW);
}

void displayOn() {
  wait();
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  _send(LOW, LOW, HIGH, HIGH, LOW, LOW);
}

void displayClear() {
  wait();
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  _send(LOW, LOW, LOW, LOW, LOW, HIGH);
}

void returnHome() {
  wait();
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  _send(LOW, LOW, LOW, LOW, HIGH, LOW);
}

void entryModeSet() {
  wait();
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  _send(LOW, LOW, LOW, HIGH, HIGH, LOW);
}

void graphicModeSet() {
  wait();
  _send(LOW, LOW, LOW, LOW, LOW, HIGH);
  _send(LOW, LOW, HIGH, HIGH, HIGH, HIGH);
}

void clearDisplayData() {
  for (int x = 0; x < SX; x++) {
    for (int y = 0; y < SY; y++) {
      displayData[x][y] = 0;
    }
  }
}

void point(int x, int y) {
  int ax = x % SX;
  int ay = ((y % 16) >= 8)?1:0;
  int by = (y % 16) % 8;
  
  displayData[ax][ay] |= (1 << by);
}

void applyDisplay() {
  wait();
  _send(LOW, LOW, HIGH, LOW, LOW, LOW);
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  
  _send(LOW, LOW, LOW, HIGH, LOW, LOW);
  _send(LOW, LOW, LOW, LOW, LOW, LOW);
  
  for (int y = 0; y < SY; y++) {
    for (int x = 0; x < SX; x++) {
      byte b = displayData[x][y];
      wait();
      _send(HIGH, LOW, GB(b, 7), GB(b, 6), GB(b, 5), GB(b, 4));
      _send(HIGH, LOW, GB(b, 3), GB(b, 2), GB(b, 1), GB(b, 0));
    }
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(PIN_PW, OUTPUT);
  digitalWrite(PIN_PW, LOW);
  delay(500);
  digitalWrite(PIN_PW, HIGH);
  delay(500);
  pinModeAll(OUTPUT);

  sync();
  functionSet();
  displayOn();
  displayClear();
  returnHome();
  entryModeSet();
  graphicModeSet();
}

// グラフィックデータ(輝点をp1, p2, ...としてx1, y1, x2, y2, ...の順で定義する)
int data[] = {
  8, 2,
  7, 2,
  6, 3,
  5, 3,
  4, 4,
  3, 5,
  3, 6,
  2, 7,
  2, 8,
  2, 9,
  2, 10,
  3, 11,
  3, 12,
  4, 13,
  5, 14,
  6, 14,
  7, 15,
  8, 15,
  
  9, 2,
  10, 2,
  11, 3,
  12, 3,
  13, 4,
  14, 5,
  14, 6,
  15, 7,
  15, 8,
  15, 9,
  15, 10,
  14, 11,
  14, 12,
  13, 13,
  12, 14,
  11, 14,
  10, 15,
  9, 15,
  
//  6, 7,
//  7, 7,
  6, 8,
  7, 8,
  6, 9,
  7, 9,
  
//  10, 7,
//  11, 7,
  10, 8,
  11, 8,
  10, 9,
  11, 9,
  
  6, 11,
  7, 12,
  8, 12,
  9, 12,
  10, 12,
  11, 11,
  0, 0
};

int dx = 0;
int frame = 0;

void loop() {
  // 動作確認のため本体のLEDを点滅させる
  pinMode(13, OUTPUT);
  frame = (frame + 1) % 10;
  if (frame > 5) {
    digitalWrite(13, HIGH);
  } else {
    digitalWrite(13, LOW);
  }
  
  // グラフィックの表示
  clearDisplayData();
  int i = 0;
  while (true) {
    int x = data[i++];
    int y = data[i++];
    if (x > 0) {
      point(x - 1 + dx, y - 1);
    } else {
      break;
    }
  }
  applyDisplay();
  dx++;
}

コメント

はじめまして、ねこパパと申します。
10日前よりAruduino工作を始め、衝動的に有機ELを買い求め、何とかモノにならないかと検索した結果、貴殿のサイトにたどり着きました。
おかげさまで大変参考になり、100×16に歌姫を降臨させることに成功しました。どうも有難うございました。

どうも!

動画みました。
縦にするとキャラの立ち絵とかも表示出来るんだ!とちょっと感動してしまいました。
今まで横でしか考えてなかったのでw

このコード、液晶モジュールのチップの仕様書を読みながら書いたんですが、なかなか辛かった記憶があります。
なので、役に立ててほんとよかったなと思います。

 はじめまして、spinelifyと申します。
 Arduino標準のLCDライブラリでSEL10016Bをキャラクターモードで使用すると時々文字化けしてしまい、改善しようとライブラリの編集に挑戦したのですがイニシャライズが難しくうまくいきませんでした。そこで、調べていたところ、こちらのサイトが大変参考になりました。 おかげさまで文字化けしないキャラクターモード用のスケッチを作ることができました。ありがとうございました。 

初期化は私も苦労した覚えが。
それでコントローラーIC(WS0010)のデータシートとにらめっこしてコードを書いたんですよね〜(ちょっとなつかしい)
ともかくお役に立ててなによりです。

コメントを残す

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



※画像をクリックして別の画像を表示

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください