2015年12月13日日曜日

メモリ液晶ブレークアウト(サンプルプログラム)


メモリ液晶ブレークアウト基板 サンプルプログラム(Arduino用)

最初にヘッダ部分です。

#include <avr/pgmspace.h>
#include <SPI.h>

// GND  GND
// SCK  13
// MOSI 11
// MISO 12
#define CSR 10
#define CSL  9
// 5V   5V

#define SCREEN0 0
#define SCREEN1 1
#define LINE_LENGTH 50
#define RAMLINE_LENGTH 52
// mode
#define BYTE_MODE 0x00
#define SEQUENTIAL_MODE 0x40
// command
#define READ 0x03
#define WRITE 0x02
#define READ_STATUS 0x05  // called RDSR in datasheet
#define WRITE_STATUS 0x01 // called WRSR in datasheet
// BASE_ADDR
#define SCREEN0_BASE 0x0000
#define SCREEN1_BASE 0x4000
int _comflag;
int _modeflag;
int _clearflag;
byte lcd_line[] = {
 0x00,
 0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0,0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0,0x08,
 0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8,0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8,0x04,
 0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4,0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4,0x0C,
 0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC,0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC,0x02,
 0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2,0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2,0x0A,
 0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA,0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA,0x06,
 0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6,0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6,0x0E,
 0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE,0x01,
 0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1,0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1,0x09,
 0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9,0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9,0x05,
 0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5,0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5,0x0D,
 0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED,0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD,0x03,
 0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3,0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3,0x0B,
 0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB,0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB,0x07,
 0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7,0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7,0x0F,
};


「#include <avr/pgmspace.h>」と「#include <SPI.h>」は、もともとArduinoに準備されているライブラリです。
 #これらを使用しているので「#include」して下さい。
「// GND  GND」と「// SCK  13」と「// MOSI 11」と「// MISO 12」と「// 5V   5V」は、Arduinoに接続する場所が固定です。
 #指定の場所に繋いでください
「#define CSR 10」と「#define CSL  9」は任意の場所に変更可能です。
 #変更したピンと接続してください
それ以外の部分は「オマジナイ」だと思って下さい。
#今回は説明を割愛させていただきます

そして初期化部分です。
void setup() {
  AkiSpiLcd();
  delay(1);
  cls();
  cls_ram(SCREEN0);
}
「AkiSpiLcd」これはメモリ液晶ブレークアウト基板を使う為の初期化命令です。
「cls」これはメモリ液晶の全画面消去命令です。
「cls_ram(SCREEN0)」これはSRAM上の画面バッファ(0ページ目)の消去命令です。


次にメイン部分です。
void loop() {
  drawDot(random(0,399),random(0,239));
  ram2lcd(SCREEN0);
}
「drawDot」これは任意の横縦座標にドットを書くプログラムです。
 #今回、横座標は乱数で0~399の間に、縦座標は乱数で0~239の間にドットを書きます。
「ram2lcd」これはSRAM(0ページ目)内に作った画面イメージをメモリ液晶に転送する命令です。


「drawDot」命令です。
void drawDot(int posX, int posY) {
  int address = (posX / 8) + (posY * RAMLINE_LENGTH) + 2;
  byte data = ram_read(address);
  data |= 0x80 >> (posX % 8);
  ram_write(address, data);
}
「int address」このaddressにドットを書きたいSRAMの座標が入ります。
「ram_read」これはSRAMの任意の場所のデータを読み込む命令です。
 #これで「data」に現状のドット情報を読み込みます
「data |= 0x80 >> (posX % 8);」これは「現状のドット状況」に「新しく書くドット」を重ね合わせています。
「ram_write」これはSRAMへの書き込み命令です。
 #完成した「data」を書き込みます。

最後は「みんなのラボで準備したプログラム」で、メモリ液晶ブレークアウト基板用のプログラムです。

// LCD ----------------------------------------
void AkiSpiLcd() {
  pinMode(CSR,OUTPUT);
  pinMode(CSL,OUTPUT);
  _ram_deselect();
  _lcd_deselect();
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setClockDivider(SPI_CLOCK_DIV2);
  SPI.setDataMode(SPI_MODE0);
  _comflag = _modeflag = _clearflag = 0;
}
void cls() {
  _modeflag = 0;
  _clearflag = 1;
  _lcd_select();
  SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
  SPI.transfer(0x00);
  _lcd_deselect();
  cominvert();
}
void cls_ram(int screen) {
  switch(screen) {
    case 0:
      _cls_ram(SCREEN0_BASE);
      break;
    case 1:
      _cls_ram(SCREEN1_BASE);
      break;
  }
}
void directUpdateSingle(int line, byte *data) {
  _modeflag = 1;
  _clearflag = 0;
  _lcd_select();
  SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
  SPI.transfer(lcd_line[line]);
  for(int x = 0;x < LINE_LENGTH;x++) {
    SPI.transfer(*(data + x));
  }
  SPI.transfer(0);
  SPI.transfer(0);
  _lcd_deselect();
  cominvert();
}
void directUpdateMulti(int line, int length, byte *data) {
  _modeflag = 1;
  _clearflag = 0;
  _lcd_select();
  for (int y = 1;y < length;y++) {
    SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
    SPI.transfer(lcd_line[line]);
    for(int x = 0;x < LINE_LENGTH;x++) {
      SPI.transfer(*(data + y * LINE_LENGTH + x));
    }
    line++;
  }
  SPI.transfer(0);
  SPI.transfer(0);
  _lcd_deselect();
  cominvert();
}
void cominvert() {
  _modeflag = 0;
  _clearflag = 0;
  _lcd_select();
  SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
  SPI.transfer(0x00);
  _lcd_deselect();
  if (_comflag == 0) {
    _comflag = 1;
  } else {
    _comflag = 0;
  }
}
void ramReadSingleLine(int line, byte *buffer, int screen) {
  switch(screen) {
    case 0:
      screen = SCREEN0_BASE;
      break;
    case 1:
      screen = SCREEN1_BASE;
      break;
  }
  line = line * RAMLINE_LENGTH;
  int address = screen + line;
  ram_read2(address, buffer, RAMLINE_LENGTH);
}  
void ramReadMultiLine(int line, int length, byte *buffer, int screen) {
  switch(screen) {
    case 0:
      screen = SCREEN0_BASE;
      break;
    case 1:
      screen = SCREEN1_BASE;
      break;
  }
  line = line * RAMLINE_LENGTH;
  int address = screen + line;
  ram_read2(address, buffer, RAMLINE_LENGTH * length);
}
void ramWriteSingleLine(int line, byte *data, int screen) {
  switch(screen) {
    case 0:
      screen = SCREEN0_BASE;
      break;
    case 1:
      screen = SCREEN1_BASE;
      break;
  }
  line--;
  line = line * RAMLINE_LENGTH;
  int address = screen + line;
  ram_write2(address, data, LINE_LENGTH);  
}
void ramWriteMultiLine(int line, int length, byte *data, int screen) {
  _modeflag=1;
  _clearflag=0;
  switch(screen) {
    case 0:
      screen = SCREEN0_BASE;
      break;
    case 1:
      screen = SCREEN1_BASE;
      break;
  }
  int address = screen + line;
  _ram_writeStatus(SEQUENTIAL_MODE);
  _ram_prepareCommand(WRITE, address);
  for (int y = 0;y < length;y++) {
    SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
    SPI.transfer(lcd_line[line] );
    for (int x = 0;x < LINE_LENGTH;x++) {
      SPI.transfer(*data);
      data++;
    }
    line++;
  }
  _ram_deselect();
  _ram_writeStatus(BYTE_MODE);
}
void _lcd_select() {
  digitalWrite(CSL,HIGH);
  delayMicroseconds(5);
}
void _lcd_deselect() {
  delayMicroseconds(5);
  digitalWrite(CSL,LOW);
}
// SRAM ----------------------------------------
void ram2lcd(int screen) {
  byte linebuffer[RAMLINE_LENGTH];
  for (int y = 0;y < 240;y++) {
    ram_read2(y * RAMLINE_LENGTH + 2,linebuffer,RAMLINE_LENGTH);
    directUpdateSingle(y + 1,linebuffer);
  }
}
byte ram_read(int address) {
  _ram_prepareCommand(READ, address);
  byte result = SPI.transfer(0);
  _ram_deselect();
  return result;
}
void ram_read2(int address, byte *buffer,int count) {
  _ram_writeStatus(SEQUENTIAL_MODE);
  _ram_prepareCommand(READ, address);
  for (int i = 0;i< count;i++) {
    buffer[i] = SPI.transfer(0);
  }
  _ram_deselect();
  _ram_writeStatus(BYTE_MODE);
}
void ram_write(int address, byte data) {
  _ram_prepareCommand(WRITE, address);
  SPI.transfer(data);
  _ram_deselect();
}
void ram_write2(int address, byte *buffer, int count) {
  _ram_writeStatus(SEQUENTIAL_MODE);
  _ram_prepareCommand(WRITE, address);
  for (int i = 0;i < count;i++) {
    SPI.transfer(buffer[i]);
  }
  _ram_deselect();
  _ram_writeStatus(BYTE_MODE);
}
byte _ram_readStatus() {
  _ram_select();
  SPI.transfer(READ_STATUS);
  byte result = SPI.transfer(0);
  _ram_deselect();
  return result;
}
void _ram_writeStatus(byte status) {
  _ram_select();
  SPI.transfer(WRITE_STATUS);
  SPI.transfer(status);
  _ram_deselect();
}
void _ram_prepareCommand(byte command, int address) {
  _ram_select();
  SPI.transfer(command);
  SPI.transfer((address >> 8) & 0xff);
  SPI.transfer(address & 0xff);
}
void _ram_select() {
  digitalWrite(CSR,LOW);
}
void _ram_deselect() {
  digitalWrite(CSR,HIGH);
}
void _cls_ram(int address) {
  _modeflag = 1;
  _clearflag = 0;
  _ram_writeStatus(SEQUENTIAL_MODE);
  _ram_prepareCommand(WRITE, address);
  for (int y = 1;y <= 240;y++) {
    SPI.transfer((_modeflag << 7) | (_comflag << 6) | (_clearflag << 5));
    SPI.transfer(lcd_line[y]);
    for (int x = 0;x < LINE_LENGTH;x++) {
      SPI.transfer(0x00);
    }
  }
  _ram_deselect();
  _ram_writeStatus(BYTE_MODE);
}
これはチョット複雑かつ長いプログラムなので、今は説明を割愛させていただきます。


「ヘッダ部分」と「初期化部分」と「メイン部分」と「drawDot命令」と「みんなのラボで準備したプログラム」を1つのプログラムに合体すれば、画面上の任意の場所(ランダム)でドットを書くDEMOプログラムの完成です!

drawDotを元にdrawLineやdrawArc等を作ると良いでしょう。
画面外にドットを書かないようにクリッピング等の処理も作ると良いでしょう。


mbed用ライブラリも「みんなのラボで準備したプログラム」は全く同じ作りになっています。

11 件のコメント:

  1. 突然コメントでの質問失礼いたします。
    先日マルツ店頭で購入させていただいたきました。
    基盤を確認したところ、セラコンの立ち上がりがあり、他にも半田不良が内か確認したところ、コネクタのEXTCOMINとDISPがブリッジしていました。これは、元からこのような仕様でしょうか?
    リワークの参考までにご教授願いますようお願い致します。

    返信削除
    返信
    1. 不良品、誠に申し訳ありません。
      現在返信作成中です、今しばらくお待ちください。

      リワークお待ちいただけたらと思います。
      お手元の商品を確認したいので、交換でお願いしたく考えております。

      因みにコネクタは何箇所かブリッジしてて問題ない場所があります。

      あと全品チェックをしているはずなので気になった次第でございます。
      とにかく申し訳ありません、もう少しお待ちください。

      削除
    2. このコメントは投稿者によって削除されました。

      削除
    3. 追記:
      そのコネクタのブリッジは問題ありません。
      「みんなのラボ」としては、その部分がブリッジしていても正しい商品として出荷しています。
      当社としては交換を希望します。
      お手間おかけして申し訳ありませんが、下記メールアドレスに御一報頂ければと思います。
      宜しくお願いします。
      minnanolab@gmail.com

      削除
  2. 私は今ArduinoとRTC-8564を使ってデジタル時計を作っています。ですが、Arduinoとメモリ液晶ブレークアウト基板のつなげ方に苦戦しています。ピンソケットでどの部分を繋げたらいいのか教えて頂きたくコメントさせていただきました。

    返信削除
    返信
    1. GND GND
      SCK 13
      MOSI 11
      MISO 12
      5V 5V
      CSR 10
      CSL 9

      これでわかりますか?

      削除
    2. 左がメモリ液晶に繋ぐピンの場所名、右がArduinoに繋ぐピンの場所名です。

      削除
  3. このコメントは投稿者によって削除されました。

    返信削除
  4. r4minimaと繋いで見ましたが、アナログ時代の停波時間帯のテレビのような感じになってしまいました。
    どぎゃんしたらヨカとですか?

    ちなみに、SPI.hが新しくなっていたので、

    SPI.setBitOrder(MSBFIRST);
    SPI.setClockDivider(SPI_CLOCK_DIV2);
    SPI.setDataMode(SPI_MODE0);
    が使えず、

    // LCD ----------------------------------------
    SPISettings mySPISettings = SPISettings(8000000, MSBFIRST, SPI_MODE0);
    void AkiSpiLcd() {
    pinMode(CSR, OUTPUT);
    pinMode(CSL, OUTPUT);
    _ram_deselect();
    _lcd_deselect();
    SPI.begin();
    // SPI.setBitOrder(MSBFIRST);
    // SPI.setClockDivider(SPI_CLOCK_DIV2);
    // SPI.setDataMode(SPI_MODE0);
    _comflag = _modeflag = _clearflag = 0;
    }

    としています。
    また、合わせて、beginTransaction、endTransactionを下記の通り追加しています。

    void _lcd_select() {
    SPI.beginTransaction(mySPISettings);
    digitalWrite(CSL, HIGH);
    delayMicroseconds(5);
    }

    void _lcd_deselect() {
    delayMicroseconds(5);
    digitalWrite(CSL, LOW);
    SPI.endTransaction();
    }

    void _ram_select() {
    SPI.beginTransaction(mySPISettings);
    digitalWrite(CSR, LOW);
    }

    void _ram_deselect() {
    digitalWrite(CSR, HIGH);
    SPI.endTransaction();
    }

    返信削除
  5. 質問ありがとうございます。

    あいにくこちらではArduino R4を持っていないので、想像になりますが、送受信がうまく行っていないように思います。以下で切り分けられると思います。

    1.クロックを1Mまで下げてください。8Mでは液晶のスペックを超えている可能性があります。
    2.他にSPIスレーブを接続しないのであれば、endTransactionは不要と思います。削除(またはコメント)するとどうなりますか。

    1と2をひとつずつ独立に行なって確認してください。

    返信削除
    返信
    1. ありがとうございます!
      1.のクロック下げでうまくいきました。
      4Mで動作しました。
      動作して初めて気づいたのですが、サンプルスケッチは、黒地に白い点が表示されるサンプルなんですね。
      最初、真っ暗になったので、予想外でした。

      削除