Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
パラレル接続有機LED(OLED)ディスプレイ

概要

SO2002AWWB-UA-WB-Uというパラレル接続有機LEDキャラクタディスプレイを使った実験です。I2C接続の有機ELキャラクタディスプレイの実験はこちらから。有機ELグラフィックディスプレイの実験はこちらから。

データシートを読んでいくと、LiquidCrystalライブラリがほぼそのまま利用できることがわかりました。ただ、このOLEDのすべての機能を実装しているわけではなく、ディスプレイ上のRAMのアドレスが異なるようです。LiquidCrystalライブラリに実装されているけれども実際は使えない機能もあるようなので、このOLEDに特化してみました。

液晶と違い、とてもきれいです。このページの最後に写真を載せてあります。

目的

SO2002AWWB-UA-WB-Uという有機LEDディスプレイをArduinoから制御できるようにします。標準的なLCDディスプレイの日立のHD44780用のLiquidCrystalライブラリは利用できないとのことなので、Arduinoから利用できるようにしてみます。

このページでは、HD44780との違いを中心にSO2002AWWB-UA-WB-Uの特徴などを記述していきます。

データシート

SO2002AWWB-UA-WB-Uのデータシートを参照して、どのようなものかを調べました。

概要

OSTA71A1D-Aの仕様の概要は以下の通りです。

項目 値など
入力電圧(絶対定格) 5.5V
入力電圧 3.6V
コントローラ US2066もしくは同等品

通常は3.6Vまでしかかけないほうがよさそうです。ということで、今回は、3.3Vで動作するArduino Dueで試してみました。Arduino Unoなど、5Vで動作するもので利用する場合は自己責任でお願いします。

また、コントローラには、US2006というチップが使われているということなので、あわせて、US2006のデータシートも少し眺めてみました。

ピンは14本あります。意味と今回の実験のときに使ったArduinoのピン番号は以下の通りです。

OLEDディスプレイのピン 意味 Arduinoのピン
1 VSS GND
2 VDD 3.3V
3 コントラスト調整 - (コントラスト調整用の20キロΩの抵抗を接続)
4 レジスタ選択 33
5 Read/Write 31
6 Enable Signal 30
7 データビット0 29
8 データビット1 28
9 データビット2 27
10 データビット3 26
11 データビット4 25
12 データビット5 24
13 データビット6 23
14 データビット7 22

US2066のデータシートによると、US2066は8ビットのパラレルインターフェイス以外にもI2Cのインターフェイスやシリアルインターフェイスを持っています。パラレルインターフェイスは、8ビットだけではなく4ビットのデータ転送にも対応していて、かつ、6800と8080の双方に対応しているようです。ただし、この設定は、US2066のピンを使った設定であり、SO2002AWWB-UA-WB-Uを使った場合には、設定はできず、8ビットパラレル(6800)で制御することになります。Arduinoのピンを一番消費するモードです。

なお、「IC-736 LCD代替手順」というページでは、4ビットパラレルで利用できるように改造した事例が紹介されています。

LiquidCrystalライブラリで提供されているAPIは実現可能でした。以下に今回実装したAPIを示します。

項番 API 意味 HD44780との比較
1 コンストラクタ オブジェクトを作成する。 意味は同じだが、SO2002Aでは、データ線は必ず8本必要。
2 begin() ライブラリの初期化 同じ
3 clear() OLEDの表示を消し、カーソルを(0,0)に移動させる。 同じ
4 home() カーソルを(0,0)に移動させる。 同じ
5 setCursor() カーソル位置を指定する。 意味は同じだが、DDRAM(Display Data RAM)のアドレス体系が異なるようです。下記参照。
6 cursor()/noCursor() カーソルを表示する。 同じ
7 blink()/noBlink() カーソルを点滅させる。 同じ
8 display()/noDisplay() 表示を行う。 同じ
9 scrollDisplayLeft()/scrollDisplayRight() 表示されている文字を左右に1文字ずらす。 同じ
10 autoscroll()/noAutoscroll() 自動スクロールを行う。 同じ
11 leftToRight()/rightToLeft() 文字の表示方向を設定する。 同じ
12 createChar() ユーザ定義の文字を作成する。 同じ
13 singleHeightFont()/doubleHeightFont() フォントの高さを設定する。 追加。
14 selectCharacterRom() フォントセットを選択する。 追加。下記参照。
15 setScrollQuantity() スクロール量を指定する。 追加。
16 setContrastControl() コントラストを調整する。 追加。
17 setFadeOutAndBlinking() フェードアウトとブリンク。 追加。

DDRAMアドレス

SO2002AWWB-UA-WB-UのDDRAM(Display Data RAM)のアドレスは、SO2002AWWB-UA-WB-Uのデータシートには明記されていませんでしたが、試してみたところ以下のようになっているようです。アドレスは16進数です。

列→ 1 2 3 4 5 18 19 20
1行目 00 01 02 03 04 11 12 13
2行目 20 21 22 23 24 31 32 33

US2066のデータシートによると、5ドットフォントの2行表示の場合の2行目は0x40から始まるようですが、SO2002AWWB-UA-WB-Uでは、5ドットフォントの3行もしくは4行表示のアドレス体系となっているようです。

左に1文字ずらした後のアドレスは以下の通りです。

列→ 1 2 3 4 5 18 19 20
1行目 01 02 03 04 05 12 13 00
2行目 21 22 23 24 25 32 33 00

右に1文字ずらした後のアドレスは以下の通りです。

列→ 1 2 3 4 5 18 19 20
1行目 13 00 01 02 03 10 11 12
2行目 23 20 21 22 23 20 21 22

フォントセット

SO2002AWWB-UA-WB-Uは、フォントセットを3種類持っています。括弧内は、selectCharacterRom()の引数に渡すマクロです。

種類 概要
ROM A(SO2002A_CHARACTER_ROM_A) 何の文字かわからず
ROM B(SO2002A_CHARACTER_ROM_B) 何の文字かわからず
ROM C(SO2002A_CHARACTER_ROM_C) カタカナ入り

ソース

SO2002Aというクラスを作成しました。Arduino Due以外でコンパイルしたときはエラーが出るようにしてあります。Arduino Due以外で使う場合は、SO2002A.hの最初のほうを自己責任でいじってください。

Githubのリポジトリはこちら

SO2002A.h

#ifndef SO2002A_H
#define SO2002A_H

#ifndef __SAM3X8E__ 
#error "Not Arduino Due"
#error "Remove this with care"
#endif

#include "Print.h"

/* 
 * Macros for users
 */
#define SO2002A_CHARACTER_ROM_A 0x00
#define SO2002A_CHARACTER_ROM_B 0x04
#define SO2002A_CHARACTER_ROM_C 0x08

#define SO2002A_DISABLE_FADE_OUT_AND_BLINKING 0b00000000
#define SO2002A_ENABLE_FADE_OUT_MODE          0b00100000
#define SO2002A_ENABLE_BLINKING_MODE          0b00110000


/*
 * Macros for internal Use
 */
#define SO2002A_COMMAND_CLEAR_DISPLAY              0b00000001
#define SO2002A_COMMAND_RETURN_HOME                0b00000010
#define SO2002A_COMMAND_ENTRY_MODE_SET             0b00000100
#define SO2002A_COMMAND_DISPLAY_ON_OFF             0b00001000
#define SO2002A_COMMAND_CURSOR_OR_DISPLAY_SHIFT    0b00010000
#define SO2002A_COMMAND_FUNCTION_SET               0b00100000
#define SO2002A_COMMAND_SET_CGRAM_ADDRESS          0b01000000
#define SO2002A_COMMAND_SET_DDRAM_ADDRESS          0b10000000
#define SO2002A_COMMAND_SET_SCROLL_QUANTITY        0b10000000
#define SO2002A_COMMAND_SET_CONTRAST_CONTROL       0b10000001
#define SO2002A_COMMAND_SET_FADE_OUT_AND_BLINKING  0b00100011

#define SO2002A_COMMAND_FUNCTION_SELECTION_B    0b01110010
#define SO2002A_COMMAND_OLED_ENABLE_SD          0b01111001
#define SO2002A_COMMAND_OLED_DISABLE_SD         0b01111000

#define SO2002A_ENTRY_MODE_SHIFT_ON               (~SO2002A_ENTRY_MODE_SHIFT_OFF)
#define SO2002A_ENTRY_MODE_SHIFT_OFF                0b00000001
#define SO2002A_ENTRY_MODE_DIRECTION_LEFT_TO_RIGHT 0b00000010
#define SO2002A_ENTRY_MODE_DIRECTION_RIGHT_TO_LEFT (~SO2002A_ENTRY_MODE_DIRECTION_LEFT_TO_RIGHT)

#define SO2002A_DISPLAY_MODE_BLINK_ON    0b00000001
#define SO2002A_DISPLAY_MODE_BLINK_OFF   (~SO2002A_DISPLAY_MODE_BLINK_ON)
#define SO2002A_DISPLAY_CURSOR_ON        0b00000010
#define SO2002A_DISPLAY_CURSOR_OFF       (~SO2002A_DISPLAY_CURSOR_ON)
#define SO2002A_DISPLAY_MODE_DISPLAY_ON  0b00000100
#define SO2002A_DISPLAY_MODE_DISPLAY_OFF (~SO2002A_DISPLAY_MODE_DISPLAY_ON)

#define SO2002A_FUNCTION_EXT_IS_ON         0b00000001
#define SO2002A_FUNCTION_EXT_IS_OFF        (~SO2002A_FUNCTION_EXT_IS_ON)
#define SO2002A_FUNCTION_EXT_RE_ON         0b00000010
#define SO2002A_FUNCTION_EXT_RE_OFF        (~SO2002A_FUNCTION_EXT_RE_ON)
#define SO2002A_FUNCTION_DOUBLE_HEIGHT_ON  0b00000100
#define SO2002A_FUNCTION_DOUBLE_HEIGHT_OFF (~SO2002A_FUNCTION_DOUBLE_HEIGHT_ON)
#define SO2002A_FUNCTION_2LINE_MODE_ON     0b00001000
#define SO2002A_FUNCTION_2LINE_MODE_OFF    (~SO2002A_FUNCTION_2LINE_MODE_ON)

#define SO2002A_SCROLLQUANTITY_MASK     0b00111111
#define SO2002A_FADE_OUT_INTERVAL_MASK  0b00001111

#define SO2002A_SHIFT_CONTROL_ON 0b00001000
#define SO2002A_SHIFT_RIGHT      0b00000100
#define SO2002A_SHIFT_LEFT       0b00000000


class SO2002A : public Print {
public:
  SO2002A(uint8_t rs, uint8_t rw, uint8_t e, 
       uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, 
       uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7);
      
  void begin(uint8_t columns, uint8_t rows);
  void clear();
  void home();
  void setCursor(uint8_t x, uint8_t y);
  void cursor();
  void noCursor();
  void blink();
  void noBlink();
  void display();
  void noDisplay();
  void scrollDisplayLeft();
  void scrollDisplayRight();
  void autoscroll();
  void noAutoscroll();
  void leftToRight();
  void rightToLeft();
  void createChar(uint8_t location, uint8_t charmap[]);

  void singleHeightFont();
  void doubleHeightFont();
  void setScrollQuantity(uint8_t dot);
  void selectCharacterRom(uint8_t font);
  void setContrastControl(uint8_t contrast);
  void setFadeOutAndBlinking(uint8_t mode, uint8_t interval);
  
  virtual size_t write(uint8_t);
  
  using Print::write;
private:
  uint8_t columns;      // width
  uint8_t rows;         // height
  uint8_t rsPin;        // Register Select
  uint8_t rwPin;        // Read/Write
  uint8_t ePin;         // Enable Signal
  uint8_t dbPin[8];     // Data Bit 0-8
  uint8_t entryMode;
  uint8_t displayMode;
  uint8_t functionMode;

  void setEntryMode();
  void setDisplayMode();
  void setFunctionMode();
  void setFunctionMode(uint8_t mode);
  void oledEnableSd();
  void oledDisableSd();
  void setCgramAddress(uint8_t address);
  void setDdramAddress(uint8_t address);
  void writeCommand(int rs, int data);
  void pulseEnable();
};

#endif  

SO2002A.cpp

#include "SO2002A.h"
#include "Arduino.h"

SO2002A::SO2002A(uint8_t rs, uint8_t rw, uint8_t e, 
            uint8_t db0, uint8_t db1, uint8_t db2, uint8_t db3, 
            uint8_t db4, uint8_t db5, uint8_t db6, uint8_t db7) {
  rsPin = rs;
  rwPin = rw;
  ePin  = e;
  dbPin[0] = db0;
  dbPin[1] = db1;
  dbPin[2] = db2;
  dbPin[3] = db3;
  dbPin[4] = db4;
  dbPin[5] = db5;
  dbPin[6] = db6;
  dbPin[7] = db7;

  pinMode(rsPin, OUTPUT);
  pinMode(rwPin, OUTPUT);
  pinMode(ePin, OUTPUT);
  
  for(int i = 0; i < 8; i++) {
    pinMode(dbPin[i], OUTPUT);
  }
}

void SO2002A::begin(uint8_t columns, uint8_t rows) {
  this->columns = columns;
  this->rows = rows;
  
  entryMode =    0b00000010;
  displayMode =  0b00000000;
  functionMode = 0b00000000;

  noDisplay();
  clear();
  display();
  
  setDisplayMode();
  setFunctionMode();
}

void SO2002A::clear() {
  writeCommand(0, SO2002A_COMMAND_CLEAR_DISPLAY);
  delayMicroseconds(1520);
}

void SO2002A::home() {
  writeCommand(0, SO2002A_COMMAND_RETURN_HOME);
  delayMicroseconds(1520);
}

void SO2002A::setCursor(uint8_t col, uint8_t row) {
  int row_begin[] = {0, 32};
  setDdramAddress(col + row_begin[row] );
}

void SO2002A::cursor() {
  displayMode |= SO2002A_DISPLAY_CURSOR_ON;
  setDisplayMode();
}

void SO2002A::noCursor() {
  displayMode &= SO2002A_DISPLAY_CURSOR_OFF;
  setDisplayMode();
}

void SO2002A::blink() {
  displayMode |= SO2002A_DISPLAY_MODE_BLINK_ON;
  setDisplayMode();
}

void SO2002A::noBlink() {
  displayMode &= SO2002A_DISPLAY_MODE_BLINK_OFF;
  setDisplayMode();
}

void SO2002A::display() {
  displayMode |= SO2002A_DISPLAY_MODE_DISPLAY_ON;
  setDisplayMode();
}

void SO2002A::noDisplay() {
  displayMode &= SO2002A_DISPLAY_MODE_DISPLAY_OFF;
  setDisplayMode();
}

void SO2002A::scrollDisplayLeft() {
  writeCommand(0, SO2002A_COMMAND_CURSOR_OR_DISPLAY_SHIFT | SO2002A_SHIFT_CONTROL_ON | SO2002A_SHIFT_LEFT);
  delayMicroseconds(37);
}

void SO2002A::scrollDisplayRight() {
  writeCommand(0, SO2002A_COMMAND_CURSOR_OR_DISPLAY_SHIFT | SO2002A_SHIFT_CONTROL_ON | SO2002A_SHIFT_RIGHT);
  delayMicroseconds(37);
}

void SO2002A::autoscroll() {
  entryMode |= SO2002A_ENTRY_MODE_SHIFT_OFF;
  setEntryMode();
}

void SO2002A::noAutoscroll() {
  entryMode &= SO2002A_ENTRY_MODE_SHIFT_ON;
  setEntryMode();
}

void SO2002A::leftToRight() {
  entryMode |= SO2002A_ENTRY_MODE_DIRECTION_LEFT_TO_RIGHT;
  setEntryMode();
}

void SO2002A::rightToLeft() {
  entryMode &= SO2002A_ENTRY_MODE_DIRECTION_RIGHT_TO_LEFT;
  setEntryMode();
}

void SO2002A::createChar(uint8_t location, uint8_t charmap[]) {
  location &= 0x7;
  writeCommand(0, SO2002A_COMMAND_SET_CGRAM_ADDRESS | (location << 3));
  delayMicroseconds(37);
  for (int i = 0; i < 8; i++) {
    write(charmap[i]);
    delayMicroseconds(37);
  }
}

void SO2002A::singleHeightFont() {
  functionMode &= SO2002A_FUNCTION_DOUBLE_HEIGHT_OFF;
  setFunctionMode();
}

void SO2002A::doubleHeightFont() {
  functionMode |= SO2002A_FUNCTION_DOUBLE_HEIGHT_ON;
  setFunctionMode();
}

void SO2002A::setScrollQuantity(uint8_t dot) {
  setFunctionMode(functionMode | SO2002A_FUNCTION_EXT_RE_ON);
  writeCommand(0, SO2002A_COMMAND_SET_SCROLL_QUANTITY | (dot & SO2002A_SCROLLQUANTITY_MASK));
  //writeCommand(0, 0b10000011);
  delayMicroseconds(37);
  setFunctionMode(functionMode & SO2002A_FUNCTION_EXT_RE_OFF);
}

void SO2002A::selectCharacterRom(uint8_t font) {
  setFunctionMode(functionMode | SO2002A_FUNCTION_EXT_RE_ON);
  writeCommand(0, SO2002A_COMMAND_FUNCTION_SELECTION_B);
  writeCommand(1, font);
  delayMicroseconds(37);
  setFunctionMode(functionMode & SO2002A_FUNCTION_EXT_RE_OFF);
  clear();
}

void SO2002A::setContrastControl(uint8_t contrast) {
  setFunctionMode(functionMode | SO2002A_FUNCTION_EXT_RE_ON);
  oledEnableSd();
  writeCommand(0, SO2002A_COMMAND_SET_CONTRAST_CONTROL);
  writeCommand(0, contrast);
  delayMicroseconds(37);
  oledDisableSd();
  setFunctionMode(functionMode & SO2002A_FUNCTION_EXT_RE_OFF);
}

void SO2002A::setFadeOutAndBlinking(uint8_t mode, uint8_t interval) {
  setFunctionMode(functionMode | SO2002A_FUNCTION_EXT_RE_ON);
  oledEnableSd();
  writeCommand(0, SO2002A_COMMAND_SET_FADE_OUT_AND_BLINKING);
  writeCommand(0, mode | (interval & SO2002A_FADE_OUT_INTERVAL_MASK));
  delayMicroseconds(37);
  oledDisableSd();
  setFunctionMode(functionMode & SO2002A_FUNCTION_EXT_RE_OFF);
}

size_t SO2002A::write(uint8_t value) {
  writeCommand(1, value);
  delayMicroseconds(37);
  return 1;
}

void SO2002A::setEntryMode() {
  writeCommand(0, SO2002A_COMMAND_ENTRY_MODE_SET | entryMode);
  delayMicroseconds(37);
}

void SO2002A::setDisplayMode() {
  writeCommand(0, SO2002A_COMMAND_DISPLAY_ON_OFF | displayMode);
  delayMicroseconds(37);
}

void SO2002A::setFunctionMode() {
  writeCommand(0, SO2002A_COMMAND_FUNCTION_SET | functionMode);
  delayMicroseconds(37);
}

void SO2002A::setFunctionMode(uint8_t mode) {
  writeCommand(0, SO2002A_COMMAND_FUNCTION_SET | mode);
  delayMicroseconds(37);
}

void SO2002A::oledEnableSd() {
  writeCommand(0, SO2002A_COMMAND_OLED_ENABLE_SD);
}

void SO2002A::oledDisableSd() {
  writeCommand(0, SO2002A_COMMAND_OLED_DISABLE_SD);
}

void SO2002A::setCgramAddress(uint8_t address) {
  writeCommand(0, SO2002A_COMMAND_SET_CGRAM_ADDRESS | address);
  delayMicroseconds(37);
}

void SO2002A::setDdramAddress(uint8_t address) {
  writeCommand(0, SO2002A_COMMAND_SET_DDRAM_ADDRESS | address);
  delayMicroseconds(37);
}


/*
 * Low Level Control
 */
void SO2002A::writeCommand(int rs, int data) {
  digitalWrite(rsPin, rs);
  digitalWrite(rwPin, LOW);
  
  for (int i = 0; i < 8; i++, data >>= 1) {
    digitalWrite(dbPin[i], 1 & data);
  }

  pulseEnable();
  digitalWrite(rwPin, HIGH);
}

void SO2002A::pulseEnable() {
  digitalWrite(ePin, LOW);
  delayMicroseconds(1);
  digitalWrite(ePin, HIGH);
  delayMicroseconds(1);
  digitalWrite(ePin, LOW);
}

OLED.ino

#include "SO2002A.h"

SO2002A oled(33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22);

/*
 * SO2002A    Arduino
 * VSS  1
 * VDD  2
 * Vo   3
 * RS   4 33 
 * R/W  5 31
 * E    6 30
 * DB0  7 29
 * DB1  8 28
 * DB2  9 27
 * DB3 10 26
 * DB4 11 25
 * DB5 12 24
 * DB6 13 23
 * DB7 14 22
 */

char *string = "Hello World!!";

void setup() {
  oled.begin(20, 2);
  oled.selectCharacterRom(SO2002A_CHARACTER_ROM_A);
  oled.setCursor(0, 0);
  oled.print(string);
}

void loop() {
  for (int i = 0; i < (20 - strlen(string)); i++) {
    oled.scrollDisplayRight();
    delay(200);
  }
  for (int i = 0; i < (20 - strlen(string)); i++) {
    oled.scrollDisplayLeft();
    delay(200);
  }
  
  for (int i = 0; i < 255; i++) {
    oled.setContrastControl(i);
    delay(10);
  }
}

HelloWorld.ino

#include "SO2002A.h"

SO2002A oled(33, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22);

/*
 * SO2002A    Arduino
 * VSS  1
 * VDD  2
 * Vo   3
 * RS   4 33 
 * R/W  5 31
 * E    6 30
 * DB0  7 29
 * DB1  8 28
 * DB2  9 27
 * DB3 10 26
 * DB4 11 25
 * DB5 12 24
 * DB6 13 23
 * DB7 14 22
 */

void setup() {
  oled.begin(20, 2);
  oled.selectCharacterRom(SO2002A_CHARACTER_ROM_A);
  oled.setCursor(0, 0);
  oled.print("Hello World!!");
  oled.setCursor(0, 1);
  oled.print("Hello Arduino!!");
}

void loop() {

}

HelloWorld.ino を実行した様子。液晶よりもきれいです。

その他

今回は20列×2行のOLEDを利用しましたが、16列×2行のOLEDも販売されています。おそらく同一のソースで動くのではと考えているのですが、試していただける方がいらっしゃるとありがたいです。

そのうち、ライブラリ化して公開したいと考えています。

バージョン

Arduino 1.5.2/Arduino Due



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

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