Arduinoで実験 (グラフィック液晶(2))

2013/05/04

 

グラフィック表示に挑戦してみます。 完全にソフトウェアでの処理なのでアルゴリズムやC言語の学習になります。

グラフィック表示を行うためには、基本関数として、コマンド書込み/読込み、データの書込み/読込み、初期化の関数が必要となります。文字表示で使用した各々の関数を一度掲載しておきます。

このページでう画面の座標とは、ドット単位の位置で X(横) 0 ~ 127、Y(縦) 0 ~ 63 となります。 座標値のエラー処理は特に行いません。

//
// 10-01 グラフィック液晶(基本関数)
//

// ピンの設定をする。
byte pins_DB[] = {0, 1, 2, 3, 4, 5, 6, 7};  // DB0~DB7
#define pins_RS 10
#define pins_RW 9
#define pins_E 8
#define pins_CS1 11
#define pins_CS2 12
#define pins_RST 13

void setup() {
  initGlcd();  // GLCD の初期化
  glcdCLS();   // CLS
}

void loop() {
}

// チップの選択
void chipSelect(boolean cs) {
  if (cs == 0) {
    digitalWrite(pins_CS1, HIGH); // cs = 0 chip 1
    digitalWrite(pins_CS2, LOW);
  } else {
    digitalWrite(pins_CS1, LOW);  // cs = 1 chip 2
    digitalWrite(pins_CS2, HIGH);
  }
}

// ポートのセットと送信
void writeBUS(boolean rs, boolean rw, byte dat) {
  digitalWrite(pins_RS, rs);
  for (int i=0; i<8; i++) {
    digitalWrite(pins_DB[i], (dat >> i) & 0x01);
  }
  digitalWrite(pins_E, HIGH);
  digitalWrite(pins_E, LOW);
}

// コマンドの送信
void writeCommand(byte dat) {
  writeBUS(0, 0, dat);  // RS=0, RW=0
}

// データの送信
void writeData(byte dat) {
  writeBUS(1, 0, dat);  // RS=1, RW=0
}

// アドレスのセット
void setAddress(byte col, byte row) {
  writeBUS(0, 0, 0x40 | (col & 0x3F));
  writeBUS(0, 0, 0xB8 | (row & 0x07));
}

// グラフィック液晶の初期化
void initGlcd(void) {
  pinMode(pins_RS, OUTPUT);
  pinMode(pins_RW, OUTPUT);
  pinMode(pins_E, OUTPUT);
  pinMode(pins_CS1, OUTPUT);
  pinMode(pins_CS2, OUTPUT);
  pinMode(pins_RST, OUTPUT);
  for(int i=0; i<8; i++) pinMode(pins_DB[i], OUTPUT);

  digitalWrite(pins_RS, LOW);
  digitalWrite(pins_RW, LOW);
  digitalWrite(pins_E, LOW);
  digitalWrite(pins_CS1, LOW);
  digitalWrite(pins_CS2, LOW);
  digitalWrite(pins_RST, HIGH);
  delay(30);

  chipSelect(0);
  writeCommand(0xC0);
  writeCommand(0x3F);
  chipSelect(1);
  writeCommand(0xC0);
  writeCommand(0x3F);
}

// Clear Display & Return Home
void glcdCLS(void) {
  byte col, row, i;

  for(i=0; i<2; i++) {
    chipSelect(i);
    for(row=0; row<8; row++) {
      setAddress(0,row);
      for(col=0; col<64; col++) {
        writeData(0);
      }
    }
  }
  setAddress(0, 0);
}

// 既に表示されているデータを読み取る
byte readData(void) {
  byte ret = 0;
  for (int i=0; i<8; i++) pinMode(pins_DB[i], INPUT);
  digitalWrite(pins_RS, HIGH);
  digitalWrite(pins_RW, HIGH);
  digitalWrite(pins_E,  HIGH);
  digitalWrite(pins_E,  LOW);
  digitalWrite(pins_E,  HIGH);

  for (int i=0; i<8; i++) ret += (digitalRead(pins_DB[i]) << i);
  digitalWrite(pins_E,  LOW);
  digitalWrite(pins_RW, LOW);
  for (int i=0; i<8; i++) pinMode(pins_DB[i], OUTPUT);

  return (ret);
}

// ページをドット単位で指定し、アドレスでチップを選択する関数
void glcdLocate(byte x, byte y) {
  if (x < 64) {
    chipSelect(0);
  } else {
    chipSelect(1);
    x -= 64;
  }
  setAddress(x, y / 8);
}

◆ ドットのオン/オフ/反転

座標値を指定し 1 ドットの点灯(オン)/消灯(オフ)/反転、を行います。 引数が 0 でオフ、 1 でオン、引数を省略で反転とします。

オン/オフと反転は同じ名前だが引数の個数の違いでの別個の関数として扱われる。

反転
  座標値のデータを読み込み変数へ格納。
  座標値からページ内でのずれを算出し、反転位置のビットフラグを立てマスクパターンを作る。
  読取値をマスクパターンで反転させ書込値を作る。
  書込値を元のアドレスへ書き込む。

オン/オフ
  座標値のデータを読み込み変数へ格納。
  座標値からページ内でのずれを算出する。
  点灯/消灯位置のビットフラグを立てマスクパターンを作る。
 (オンの場合)
    読取値とマスクパターンの論理和(|)を算出し書込値とする。
 (オフの場合)
    反転した読取値とマスクパターンの論理和(|)を算出し仮の書込値とする。
    仮の書込み値を反転させ書込値とする。
  書込値を元のアドレスへ書き込む。
//
// 10-02 グラフィック液晶(ドットの点灯/消灯/反転)
//

// ドットの反転
void pset( byte x, byte y ) { // 反転
  byte rDat, mDat, wDat;

  byte gap = y % 8;   // ずれを算出
  glcdLocate(x, y);
  rDat = readData();  // データを読み込む
  mDat = (1 << gap);  // マスクパターンを作る

  wDat = rDat ^ mDat; // 排他的論理和(XOR)

  glcdLocate(x, y);
  writeData(wDat);    // データの書込み
}

// ドットの点灯/消灯
void pset( byte x, byte y ,boolean sw) {
  byte rDat, mDat, wDat;

  byte gap = y % 8;      // ずれを算出
  glcdLocate(x, y);
  rDat = readData();     // データを読み込む
  mDat = (1 << gap);     // マスクパターンを作る

  if (sw) {
    wDat = rDat | mDat;  // 論理和(OR)
  } else {
    wDat = ~rDat | mDat; // 読取値を反転して論理和(OR)
    wDat = ~wDat;        // 書込値を反転
  }
  glcdLocate(x, y);
  writeData(wDat);       // データの書込み
}

◆ 直線/四角

 2点の座標値を指定して直線を表示する関数を作ります。 四角は直線を組み合わせて表示します。

直線
  同じ値で 0 除算エラーが起きるがチェックしない。
  座標値が範囲外でもチェックしない。

  横の差と縦の差の大きい方を比較して各々の処理を選択する。
  (Xの差が大きい場合)
    X1 が X0 より小さい場合、起点と終点の座標を入れ替える
    Y の傾斜率を計算する。
  (X0 から X1 までのループ)
      Y に増加率を加算した値を算出する。
      座標値の位置を点灯する。

  (Yの差が大きい場合)
    Y1 が Y0 より小さい場合、起点と終点の座標を入れ替える
    X の傾斜率を計算する。
  (Y0 から Y1 までのループ)
      X に増加率を加算した値を算出する。
      座標値の位置を点灯する。

四角
  座標値の左上と右上を指定して直線を表示。
  座標値の左上と左下を指定して直線を表示。
  座標値の右上と右下を指定して直線を表示。
  座標値の左下と右下を指定して直線を表示。
//
// 10-03 グラフィック液晶(直線/四角)
//

// 直線描画
void line( byte x0, byte y0, byte x1, byte y1 ) {
  byte x, y, tmp;
  double slope = 0;

  if (abs(x1 - x0) > abs(y1 - y0)) {
    if (x1 < x0) {                         // 座標値の入れ替え
      tmp = x0;
      x0 = x1;
      x1 = tmp;
      tmp = y0;
      y0 = y1;
      y1 = tmp;
    }
    slope = (double)(y1 - y0) / (x1 - x0); // 傾斜率の算出
    for (int i=0; i<=(x1-x0); i++) {
      x = x0 + i;
      y = y0 + slope * i;
      pset(x, y, 1);
    }
  } else {
    if (y1 < y0) {                         // 座標値の入れ替え
      tmp = x0;
      x0 = x1;
      x1 = tmp;
      tmp = y0;
      y0 = y1;
      y1 = tmp;
    }
    slope = (double)(x1 - x0) / (y1 - y0); // 傾斜率の算出
    for (int i=0; i<=(y1-y0); i++) {
      x = x0 + slope * i;
      y = y0 + i;
      pset(x, y, 1);
    }
  }
}

// 四角描画
void box( byte x0, byte y0, byte x1, byte y1 ) {
  line(x0, y0, x1, y0);
  line(x0, y1, x1, y1);
  line(x0, y0, x0, y1);
  line(x1, y0, x1, y1);
}

他には円の描画や塗りつぶし、直線の消去等の関数を作成する必要があるのだが、気が向いたら追加する。