Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
フリップドットディスプレイの実験

概要

ポーランドのALFAZETAという会社が製造・販売しているXY5というフリップドットディスプレイをArduinoから制御するための実験です。

以前、Arduino Project Hubで紹介されていたFlip-Dot-Clockで利用されているデバイスが面白そうだったので調べていたところ、作者の方がこちらのページに入手方法等を書かれていたので、参考にして入手しました。

フリップドットディスプレイを使ったデジタル時計はこちら

データシート

概要

フリップドットディスプレイは、裏と表で色の違う小さいディスクを電磁石の力を利用して回転させ、情報を表示させます。いろいろなサイズのものがありますが、今回私が入手したディスプレイは、縦7ドット、横28ドットのディスプレイです。それでも、横幅は30cm弱あります。ディスクが回転する際、カシャカシャと音を立てます。また、ディスクそのものは光りません。

デモモードも用意されていて、その場合は、電源を接続するだけで、ディスプレイにいろいろ表示されます。

電圧と電流、制御方法は以下の通りです。

項目 内容
電源電圧 24V
最大消費電流(ただしサイズは14x28のもの) 680mA
制御方法 RS-485

RS-485を利用して、制御します。Arduinoでは、RS-485を扱うことはできないので、LTC485CN8というRS-485トランシーバを利用しました。

インタフェース

コマンド

以下の順序で、RS-485を通じて、コマンドを送信すると、ディスクが回転し、情報が表示されます。ディスプレイからは何も送信されません

名前 説明
0x80 ヘッダ 制御開始
0x83 コマンド 表示コマンド
アドレス アドレス ディスプレイのアドレス。アドレスはDIPスイッチを使って設定します。
データ データ 28バイト分のデータ。各データは、0x00から0x7f
0x8f エンドバイト 制御終了。

実験

XY5とLTC485CN8、Arduinoとの接続

XY5とLTC485CN8、Arduinoは以下のように接続しました。

XY5 LTC485CN8のピン番号 Arduinoのピン番号
VCC(24V) - -
GND - -
- 8(VCC) 5V
- 5(GND) GND
+485 6(A) -
-485 7(B) -
- 4(DI) 1(TX)
- 2(RE) 5V
- 3(DE) 5V

XY5からデータを受信する必要がないので、Arduinoの0番とLTC485CN8の1番は使用せず、LTC485CNの2番(RE)と3番(DE)は5V固定にしています。

Arduinoプログラミング

大きめのデータ用のバッファを作成し、ディスプレイのサイズのウインドウを切り出して表示するようにしてみました。また、任意のドットの表示・非表示、文字の表示をできるようにしました。フォントは、7x4のサイズで作成しました。とても小さいフォントなので、うまく読めない文字もあります。英小文字はあきらめました。

全体としては、LiquidCrystal ライブラリっぽくしてみました。

実行の様子

スクロールする前の様子です。HELLO WORLDと表示していますが、WORLDの部分はまだ見えていません。

私は白色のものを入手しましたが、いくつか色は用意されています。黄色が、紫外線耐性が一番高いとのことです。

スケッチ

「HELLO WORLD」と表示し、左にスクロールするプログラムです。いつものようにエラー処理はほとんど入っていません。

githubにもコードを置いてあります。

hello.ino

#include "XY5Class.h"

XY5Class xy5(&Serial);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(57600);
  xy5.begin(28, 56, 0x01);
  xy5.clear();
  xy5.home();
  xy5.print("HELLO WORLD");
  xy5.flush();
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(2000);
  xy5.scrollDisplayLeft();
}

XY5Class.h

#ifndef XY5_CLASS_H
#define XY5_CLASS_H

#include "Print.h"

#define XY5_HEADER          (0x80)
#define XY5_WRITE_COMMAND   (0x83)
#define XY5_ENDBYTE         (0x8f)

#define XY5_FONT_WIDTH      (4)

class Stream;

class XY5Class : public Print {
  public:
    XY5Class(Stream *port);

    void begin(int displayWidth, int bufferWidth, int address);
    void clear();
    void home();
    void scrollDisplayLeft();
    void scrollDisplayRight();
    virtual size_t write(uint8_t c);

    int set(int x, int y);
    int unset(int x, int y);
    int setCursor(int x);
    int setWindow(int x);
    void flush();

    using Print::write;

  private:
    Stream *port;
    int displayWidth;
    int bufferWidth;
    int address;
    int cursor;
    int window;
    uint8_t *buffer;
    static const uint8_t fontData[];
};

#endif /* XY5_CLASS_H */

XY5Class.cpp

#include <avr/pgmspace.h>
#include "Arduino.h"
#include "XY5Class.h"

XY5Class::XY5Class(Stream *port) : port(port) {
}

void XY5Class::begin(int displayWidth, int bufferWidth, int address) {
  this->displayWidth = displayWidth;
  this->bufferWidth = bufferWidth;
  this->address = address;
  buffer = (uint8_t *)calloc(bufferWidth, sizeof(uint8_t));
  cursor = 0;
  window = 0;
}

void XY5Class::clear() {
  for (int i = 0; i < bufferWidth; i++) {
    buffer[i] = 0;
  }
}

void XY5Class::home() {
  cursor = 0;
}

void XY5Class::scrollDisplayLeft() {
  window = (window + 1) % bufferWidth;
  flush();
}

void XY5Class::scrollDisplayRight() {
  window = (window - 1 + bufferWidth) % bufferWidth;
  flush();
}

size_t XY5Class::write(uint8_t c) {
  int offset;

  if ((cursor + XY5_FONT_WIDTH) >= bufferWidth) {  // no space available.
    return 0;
  }

  if ((0x20 <= c) && (c <= 0x60)) {        // ' ' to `
    offset = 0x20;
  } else if ((0x7b <= c) && (c <= 0x7e)) { // { to ~
    offset = 0x3a;
  } else {
    c = 0x7f;
    offset = 0x3a;
  }

  for (int i = 0; i < XY5_FONT_WIDTH; i++) {
    buffer[cursor++] = pgm_read_byte_near(fontData + XY5_FONT_WIDTH * (c - offset) + i);
  }

  cursor++;
  return 1;
}

int XY5Class::set(int x, int y) {
  buffer[x] |= 1 << y;
}

int XY5Class::unset(int x, int y) {
  buffer[x] &= ~(1 << y);
}

int XY5Class::setCursor(int x) {
  if ((0 <= x) && (x <= bufferWidth)) {
    cursor = x;
    return 1;
  } else {
    return 0;
  }
}

int XY5Class::setWindow(int x) {
  if ((0 <= x) && (x <= bufferWidth)) {
    window = x;
    return 1;
  } else {
    return 0;
  }
}

void XY5Class::flush() {
  port->write(XY5_HEADER);
  port->write(XY5_WRITE_COMMAND);
  port->write(address);

  for (int i = 0; i < 28; i++) {
    port->write(buffer[(i + window) % bufferWidth]);
  }

  port->write(XY5_ENDBYTE);
}

const uint8_t XY5Class::fontData[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, // 0x20, space
  0x00, 0x00, 0x5f, 0x00, // 0x21, !
  0x02, 0x01, 0x02, 0x01, // 0x22, "
  0x74, 0x1f, 0x74, 0x1f, // 0x23, #
  0x26, 0x79, 0x4f, 0x32, // 0x24, $
  0x73, 0x0b, 0x64, 0x63, // 0x25, %
  0x76, 0x49, 0x59, 0x26, // 0x26, &
  0x00, 0x04, 0x03, 0x00, // 0x27, '
  0x00, 0x3e, 0x41, 0x41, // 0x28, (
  0x41, 0x41, 0x3e, 0x00, // 0x29, )
  0x00, 0x14, 0x7f, 0x14, // 0x2a, *
  0x00, 0x08, 0x7f, 0x08, // 0x2b, +
  0x40, 0x20, 0x00, 0x00, // 0x2c, ,
  0x08, 0x08, 0x08, 0x08, // 0x2d, -
  0x60, 0x60, 0x00, 0x00, // 0x2e, .
  0x40, 0x38, 0x0e, 0x01, // 0x2f, /
  0x3e, 0x41, 0x41, 0x3e, // 0x30, 0
  0x44, 0x42, 0x7f, 0x40, // 0x31, 1
  0x62, 0x51, 0x49, 0x46, // 0x32, 2
  0x22, 0x49, 0x49, 0x36, // 0x33, 3
  0x1c, 0x12, 0x7f, 0x10, // 0x34, 4
  0x47, 0x49, 0x49, 0x31, // 0x35, 5
  0x3e, 0x49, 0x49, 0x32, // 0x36, 6
  0x01, 0x71, 0x09, 0x07, // 0x37, 7
  0x36, 0x49, 0x49, 0x36, // 0x38, 8
  0x26, 0x49, 0x49, 0x3e, // 0x39, 9
  0x00, 0x00, 0x36, 0x00, // 0x3a, :
  0x00, 0x20, 0x16, 0x00, // 0x3b, ;
  0x08, 0x14, 0x22, 0x41, // 0x3c, <
  0x14, 0x14, 0x14, 0x14, // 0x3d, =
  0x41, 0x22, 0x14, 0x18, // 0x3e, >
  0x02, 0x01, 0x59, 0x06, // 0x3f, ?
  0x3e, 0x55, 0x55, 0x5e, // 0x40, @
  0x7e, 0x09, 0x09, 0x7e, // 0x41, A
  0x7f, 0x49, 0x49, 0x36, // 0x42, B
  0x3e, 0x41, 0x41, 0x22, // 0x43, C
  0x7f, 0x41, 0x41, 0x3e, // 0x44, D
  0x7f, 0x49, 0x49, 0x41, // 0x45, E
  0x7f, 0x09, 0x09, 0x01, // 0x46, F
  0x3e, 0x41, 0x49, 0x3a, // 0x47, G
  0x7f, 0x08, 0x08, 0x7f, // 0x48, H
  0x00, 0x41, 0x7f, 0x41, // 0x49, I
  0x21, 0x41, 0x3f, 0x01, // 0x4a, J
  0x7f, 0x0c, 0x12, 0x61, // 0x4b, K
  0x7f, 0x40, 0x40, 0x40, // 0x4c, L
  0x7f, 0x06, 0x06, 0x7f, // 0x4d, M
  0x7f, 0x06, 0x18, 0x7f, // 0x4e, N
  0x3e, 0x41, 0x41, 0x3e, // 0x4f, O
  0x7f, 0x09, 0x09, 0x06, // 0x50, P
  0x3e, 0x41, 0x61, 0x5e, // 0x51, Q
  0x7f, 0x09, 0x19, 0x66, // 0x52, R
  0x26, 0x49, 0x49, 0x32, // 0x53, S
  0x01, 0x01, 0x7f, 0x01, // 0x54, T
  0x3f, 0x40, 0x40, 0x3f, // 0x55, U
  0x1f, 0x60, 0x60, 0x1f, // 0x56, V
  0x3f, 0x78, 0x78, 0x3f, // 0x57, W
  0x43, 0x3c, 0x3c, 0x43, // 0x58 ,X
  0x00, 0x07, 0x78, 0x07, // 0x59, Y
  0x71, 0x49, 0x45, 0x43, // 0x5a, Z
  0x00, 0x7f, 0x41, 0x41, // 0x5b, [
  0x00, 0x17, 0x78, 0x17, // 0x5c, "\"
  0x41, 0x41, 0x7f, 0x00, // 0x5d, ]
  0x02, 0x01, 0x01, 0x02, // 0x5e, ^
  0x40, 0x40, 0x40, 0x40, // 0x5f, _
  0x01, 0x02, 0x04, 0x00, // 0x60, `
  0x00, 0x08, 0x36, 0x41, // 0x7b, {
  0x00, 0x00, 0x7f, 0x00, // 0x7c, |
  0x41, 0x36, 0x08, 0x00, // 0x7d, }
  0x02, 0x01, 0x02, 0x01, // 0x7e, ~
  0x7f, 0x7f, 0x7f, 0x7f, // out of range
};

バージョン

Arduino 1.8.1/Arduino Uno



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2017 garretlab all rights reserved.
inserted by FC2 system