Arduinoで実験 (OSDモジュール)

2013/06/19


◆ オン・スクリーン・ディスプレイ モジュール

aitendo で MAX7456 を使用したオン・スクリーン・ディスプレイ・モジュールを購入したので実験してみる。

スイッチサイエンスでも Sparkfunの同等品を取り扱っているが、aitendo の方が少し安い(少しでかい)。 Sparkfun の商品ページを参考に実験をしてみる。

OSDはコンポジット出力に任意の画面をオーバーレイ(合成)するモジュールで、大昔の「パソコンテレビ X1」を実現できるモジュールです。

映像に文字を重ねられるので、よく記録映像に利用されますが、自分は車の CAN 情報を車内のディスプレイに表示したかったので購入しましたが、車内の時計がナビ内の小さい画面で良く見えないので、時計を表示することを目標にします。


◆ SPI での制御

とりあえず Sparkfun にある Arduino Library を使用するために配線を行います。

SPIでの制御なので配線先は変更不可。

aitendo のモジュールには Reset 端子がありませんが、ライブラリのサンプルでは、Arduino のリセット端子と接続しているようなのでプログラムには影響ないと思います。

と、ここまで書いてライブラリのサンプルを実行したけど画面に何も出てこない。 色々といじくってみたけど真黒い画面でビデオ信号が出力されていないため、モニタがスリープモードに移行してしまう。

入力側のビデオ信号がディスプレイへ送られていないので、プログラムや配線を何度もチェックしましたがおかしいところがなかったので、最後の手段である基板のパターンチェックを行ったところ、ありました。 以下の写真のところでショートしていました。(さすが aitendo クォリティ)

カッターナイフでハンダを切り、テスターで断線を確認してからサンプルプログラムを実行したところ、画面が灰色になりとりあえず、何かしらの信号が出ていることを確認出来たので、 Sparkfun のライブラリ付属サンプルを実行したところ問題なく実行できました。

 


◆ Sparkfun のライブラリで制御

ダウンロードしたライブラリを、Arduino IDE の \libraries フォルダへ置いたら一通りサンプルを実行して見ましょう。

なお、Sparkfun のサンプルは mega でのピン設定になっているので const byte osdChipSelect = 43 を 10 に変更してください。

以下はサンプルにもある Hello World を簡素化したものです。

MAX7456.h で SPI.h を使用しているがライブラリ内でのインクルードされていないのでプログラムの方でインクルードと初期化を行う。

#include <SPI.h>
#include <MAX7456.h>

//ピン設定
const byte osdChipSelect = 10; //CS
const byte masterOutSlaveIn = 11; //MOSI
const byte masterInSlaveOut = 12; //MISO
const byte slaveClock = 13; //SCK
const byte osdReset = 0;
//ライブラリの宣言
MAX7456 OSD( osdChipSelect );
void setup() {
//SPIの開始と設定     SPI.begin();     SPI.setClockDivider( SPI_CLOCK_DIV2 ); // 10MHz.     OSD.begin();  //MAX7456ライブラリの開始     OSD.display();  //テキスト画面の表示     OSD.clear();  //画面の消去     delay(1000);     while (OSD.notInVSync());  //タイミング待ち     OSD.setCursor( 2, 3 );  //表示位置指定     OSD.print( "Hello World!" );  //表示 } void loop() { }

他にもいろいろコマンドはあるが、サンプルやヘッダファイルを参照して学習する。

ハードウェアでのリセットが出来ないため、おかしな表示がされることがある。また、notInVSync 等の同期命令が無いと表示がおかしくなることがある。

 


◆ ライブラリを使用しない制御

まぁ、ついでなのでライブラリに頼らず一から制御してゆきたいと思います。 ここに書いてるのはほぼ Maxim のアプリケーションノートの受け売りです。 日本語のアプリケーションノートがあるので参考にしてください。

MAX7456オンスクリーンディスプレイでSPIインタフェースを使用するためのCコードソフトウェアルーチン
MAX7456のメモリとEVキットファイルのフォーマットを用いてカスタム文字とグラフィックスを生成

ウェブ上にある maxim のアプリケーションノートを参考にします。 MAX7456 のサンプルコードは色々検索で拾えますがほとんどが自前の SPI 送受信コマンドで記述しています。 ハードウェアに頼らない応用のきくプログラムなのですが、どうしてもビット操作を行うため、自分の様な素人にはピンとこない呪文に見えてしまうので、通信系のコマンドは SPI.h に丸投げします。

MAX7456 は最大 10MHz のクロックまでサポートしているので、16MHz の Arduino UNO であれば、SPI.CLOCK.DIV2 とすれば 8MHz の通信速度となります。

データは、bit7 → bit0 の順での送信なので、MSBFIRST。 on=HIGH、off=LOW の立ち上がり同期なので、SPI_MODE0 と設定します。

データ送信は MAX7456 に対してのレジスタ書き込みで、2バイトを送信します。 CS を LOW にして 2バイト送信後、CS を HIGH にするとレジスタがラッチされます。

レジスタ書き込みはアドレスとデータがセットで2バイトの書込みが基本となりますが、自動インクリメントモードの場合は 1バイトデータのみの書込みとなります。

データ受信はアドレスの送信後 1バイトのデータを受信します。


レジスタ書込み


自動インクリメントモードでのレジスタ書き込み

データの読込みは、レジスタのアドレスを送信後、1バイトの読込みとなります。


レジスタ読込み

上図を見ると SDIN の bit7 がフラグとして使用されていますが、MAX7456 のコマンドが書込みが 0x7F まで、読み込みが 0x80以上となっているので余り意識する必要はないと思います。

aitendo のサンプルを元にプログラムしてみます。

//#include 

#define pin_CS 10
#define pin_OUT 11
#define pin_IN 12
#define pin_CLK 13

void setup() {
//SPI.begin();

//SPI.setBitOrder(MSBFIRST);
//SPI.setClockDivider(SPI_CLOCK_DIV2);
//SPI.setDataMode(SPI_MODE0);
  pinMode(pin_CS, OUTPUT);
  pinMode(pin_OUT, OUTPUT);
  pinMode(pin_IN, INPUT);
  pinMode(pin_CLK, OUTPUT);

  delay(1000);
  spi_write (0x00, 0x08);
  delay(1);

  byte ans = spi_read(0xEC);
  ans &= 0xEF;
  spi_write(0x6C, ans);
  delay(1);

  spi_write(0x04, 0x00);
  ans = 25;

  spi_write(0x05, 0x01);

  spi_write(0x06, ans);
  spi_write(0x07, 0x1D); //S

  spi_write(0x06, ans + 1);
  spi_write(0x07, 0x0B); //A

  spi_write(0x06, ans + 2);
  spi_write(0x07, 0x17); //M

  spi_write(0x06, ans + 3);
  spi_write(0x07, 0x1A); //P

  spi_write(0x06, ans + 4);
  spi_write(0x07, 0x16); //L

  spi_write(0x06, ans + 5);
  spi_write(0x07, 0x0F); //E
}

void loop() {
  spi_write(0x00, 0x08);
  delay(1000);
  spi_write(0x00, 0x00);
  delay(1000);
}

void data_Output(unsigned char clk_pin, unsigned char data_pin, unsigned char data) {
  unsigned char count;

  for (count = 0; count < 8; count++) {
    if ((data << count) & 0x80) {
      digitalWrite(data_pin, HIGH);
    } else {
      digitalWrite(data_pin, LOW);
    }

//clock
    digitalWrite(clk_pin, HIGH);
    digitalWrite(clk_pin, LOW);
  }

  digitalWrite(clk_pin, LOW);
  digitalWrite(data_pin, LOW);
}

unsigned char data_Input(unsigned char clk_pin, unsigned char data_pin) {
  unsigned char count;
  unsigned char data = 0;

  for (count = 0; count < 8; count++) {
    data <<= 1;
    digitalWrite(clk_pin, HIGH);
    data += digitalRead(data_pin);
    digitalWrite(clk_pin, LOW);
  }

  digitalWrite(clk_pin, LOW);
  digitalWrite(data_pin, LOW);

  return data;
}

void spi_write(unsigned char address, unsigned char data) {
  digitalWrite(pin_CS, LOW);

  data_Output(pin_CLK, pin_OUT, address);
  data_Output(pin_CLK, pin_OUT, data);

//SPI.transfer(address);
//SPI.transfer(data);

  digitalWrite(pin_CS, HIGH);
}

unsigned char spi_read(unsigned char address) {
  unsigned char data;
  unsigned char count;

  digitalWrite(pin_CS, LOW);
  data_Output(pin_CLK, pin_OUT, address);
  data = data_Input(pin_CLK, pin_IN);

//data = SPI.transfer(address);
  digitalWrite(pin_CS, HIGH);

  return data;
}

色々と試してみるのだがソフトウェア・リセットがうまく働いてくれない。 基板のパターンで MAX7456 の 19番端子(RESET)と 21番端子(VDD)がつながっているところをカットし、MAX7456 の足に直接はんだ付けしてリセット端子を儲けようかどうか迷っている。


◆ MAX7456のコマンド

Write Read Name   def
00 80 VM0 Video Mode 0 00
01 81 VM1 Video Mode 1 47
02 82 HOS Horizontal Offset 10
03 83 VOS Vertical Offset 00
04 84 DMM Display Memory Mode 00
05 85 DMAH Display Memory Address High 00
06 86 DMAL Display Memory Address Low 00
07 87 DMDI Display Memory Data In 00
08 88 CMM Character Memory Mode 00
09 89 CMAH Character Memory Address High 00
0A 8A CMAL Character Memory Address Low 00
0B 8B CMDI Character Memory Data In NA
0C 8C OSDM OSD Insertion Mux 1B
10
|
1F
90
|
9F
RB0
|
RB15
Row 0 Brightness
      |
Row 15 Brightness
01
6C EC OSDBL OSD Black Level 1x
Ax STAT Status NA
Bx DMDO Display Memory Data Out NA
Cx CMDO Character Memory Data Out NA

● Video Mode

画面の設定に関するコマンド。 ビデオ信号は NTSC とする。

Video Mode 0 ( W=00H : R=80H )

  bit 6    ビデオ信号選択
               0 = NTSC
               1 = PAL
  bit 5-4  同期モード
              0x = 自動同期
              10 = 外部信号で同期
              11 = 内部信号で同期
  bit 3    画面の出力
               0 = off
               1 = on
  bit 2    Vertical Synchronization of On-Screen Data
               0 = Enbale on-screen display immediately
               1 = Enable on-screen display at the next VSYNC
  bit 1    ソフトウェア・リセット
               1 でリセット
  bit 0    ビデオバッファ
               0 = Enable
               1 = Disable
Video Mode 1 ( W=01H : R=81H)

  bit 7    背景モード (Local Background Control bit / LBC)
               表示モード参照
  bit 6-4  背景のブライトネス(ホワイト・レベル)
             000 =  0 %
             001 =  7 %
             010 = 14 %
             011 = 21 %
             100 = 28 % (初期値)
             101 = 35 %
             110 = 42 %
             111 = 49 %
  bit 3-2  点滅間隔(BT)
              00 =  33ms
              01 =  67ms (初期値)
              10 = 100ms
              11 = 133ms
  bit 1-0  点灯と消灯の割合(点灯 : 消灯)
              00 = BT : BT
              01 = BT : (2 x BT)
              10 = BT : (3 x BT)
              11 = (3 x BT) : BT(初期値)         
Status ( R=AxH )

  bit 6    リセット
  bit 5    キャラクタ・メモリ
  bit 4    VSYNC
  bit 3    HSYNC
  bit 2    Los of Sync
  bit 1    NTSC signal
  bit 0    PAL signal

●ソフトウェアリセット

aitendo のモジュールは、リセット端子が無いのでソフトウェアでのリセットが必須となる。 VMM0 に 02H を書き込めばリセットとなる。 200us 程度かかるので STAT のリセットフラグを参照するか十分な待機時間を取る必要がある。

●画面のオン/オフ

画面のオン/オフは VM0 の bit 3 で制御する。 背景の状態かかわるので表示モードの項を参照。

●画面消去

DMM の bit 2 でディスプレイメモリを 00H で埋める。

●背景モード

背景に外部ビデオ信号を表示するには、 VMM1 の bit 7 を 0 にする。(デフォルト)。

bit 7 を 1 にすると背景がグレイ表示となる。 グレイ表示の濃淡は bit 6,5,4 の 3 ビットで指定する。

◆表示モードのまとめ

外部より入力されたビデオ信号は、背景の処理として取り扱われる。 以下は、テキスト画面と背景の関係。

  External Sync Mode and
Local Background Control Bit = 0
Internal Sync Mode or
Local Background Control Bit = 1
Invert Bit = 0
A

B

C

D
Invert Bit = 1
E

F

G

H
A,B  VMM1.bit7 = 0,  DMM bit3 = 0
C,D  VMM1.bit7 = 1,  DMM bit3 = 0
E,F  VMM1.bit7 = 0,  DMM bit3 = 1
G,H  VMM1.bit7 = 1,  DMM bit3 = 1

ここまでの命令を使用したサンプルスケッチをあげておく。 ライブラリよりもかなり遅いプログラムです。

//ピン
#define pin_CS 10
#define pin_MOSI 11
#define pin_MISO 12
#define pin_CLK 13

//MAX7456 レジストリ
#define OSD_VMM0 0x00
#define OSD_VMM1 0x01
#define OSD_HOS 0x02
#define OSD_VOS 0x03
#define OSD_DMM 0x04
#define OSD_DMAH 0x05
#define OSD_DMAL 0x06
#define OSD_DMDI 0x07
#define OSD_CMM 0x08
#define OSD_CMAH 0x09
#define OSD_CMAL 0x0A
#define OSD_CMDI 0x0B
#define OSD_OSDM 0x0C
#define OSD_RB 0x0D
#define OSD_BL 0x6C
#define OSD_STAT 0x20
#define OSD_DMDO 0x30
#define OSD_CMDO 0x40

void setup() {
  Serial.begin(9600);
  Serial.println("OSD test start");

//ピンモード
  pinMode (pin_CS, OUTPUT);
  pinMode (pin_MOSI, OUTPUT);
  pinMode (pin_MISO, INPUT);
  pinMode (pin_CLK, OUTPUT);

  osd_init(); //初期化
  osd_display(1); //テキスト画面オン
  osd_setBT(3, 3); //点滅時間の設定
  osd_video(0); //背景ビデオオン

  osd_attr(0, 0, 1); //文字属性の設定

  spiWriteReg(OSD_DMAH, 0x00);
  spiWriteReg(OSD_DMAL, 32);
  spiWriteReg(OSD_DMDI, 1); //S
}


void loop() {
}


//SPI 書込みコマンド
void spiWriteReg(byte regAddr, byte regData) {
  byte count;
  byte data;

  digitalWrite(pin_CS, HIGH);
  digitalWrite(pin_CLK, LOW);
  data = regAddr;

  digitalWrite(pin_CS, LOW);

  for (count = 0; count < 8; count ++) {
    if (data & 0x80) {
      digitalWrite(pin_MOSI, HIGH);
    } else {
      digitalWrite(pin_MOSI, LOW);
    }
    digitalWrite(pin_CLK, HIGH);
    digitalWrite(pin_CLK, LOW);

    data <<= 1;
  }

  data = regData;
  for (count = 0; count < 8; count ++) {
    if (data & 0x80) {
      digitalWrite(pin_MOSI, HIGH);
    } else {
      digitalWrite(pin_MOSI, LOW);
    }
    digitalWrite(pin_CLK, HIGH);
    delay(1);
    digitalWrite(pin_CLK, LOW);
    delay(1);
    data <<= 1;
  }
  digitalWrite(pin_MOSI, LOW);
  digitalWrite(pin_CS, HIGH);
}

//SPI 読込みコマンド
byte spiReadReg(byte regAddr) {
  byte count;
  byte data;

  digitalWrite(pin_CS, HIGH);
  digitalWrite(pin_CLK, LOW);
  data = (regAddr | 0x80);

  digitalWrite(pin_CS, LOW);

  for (count = 0; count < 8; count ++) {
    if (data & 0x80) {
      digitalWrite(pin_MOSI, HIGH);
    } else {
      digitalWrite(pin_MOSI, LOW);
    }
    digitalWrite(pin_CLK, HIGH);
    digitalWrite(pin_CLK, LOW);
    data <<= 1;
  }
  digitalWrite(pin_MOSI, LOW);

  data = 0;
  for (count = 0; count < 8; count ++) {
    data <<= 1;
    digitalWrite(pin_CLK, HIGH);
    data += digitalRead(pin_MISO);
    digitalWrite(pin_CLK, LOW);
  }
  digitalWrite(pin_CS, HIGH);

  return data;
}

//MAX7456 初期化
void osd_init(void) {
  byte ans;
  delay(1000);
  spiWriteReg(OSD_VMM0, 0x02); //reset
  delay(100);
  ans = spiReadReg(OSD_BL); //read black level
  ans &= 0xEF;
  spiWriteReg(OSD_BL, ans); //black level
  delay(1);
  spiWriteReg(OSD_DMM, 0x04); //display clear
  delay(100);
  spiWriteReg(OSD_VMM0, 0x08); //display on
  delay(1);
}

//MAX7456 テキスト画面のオン/オフ
void osd_display(boolean sw) {
  byte ans = spiReadReg(OSD_VMM0);
  if (sw == 1) {
    ans |= 0x08;
  } else {
    ans &= ~0x08;
  }
  spiWriteReg(OSD_VMM0, ans);
}

//MAX7456 テキスト画面の全消去
void osd_clear(void) {
  spiWriteReg(OSD_DMM, 0x04); //display clear
  delay(100);
}

//MAX7456 文字属性の設定
void osd_attr(boolean bg, boolean blnk, boolean inv) {
  byte ans = spiReadReg(OSD_DMM);
  if (bg == 1) {
    ans |= 0x20;
  } else {
    ans &= ~0x20;
  }
  if (blnk == 1) {
    ans |= 0x10;
  } else {
    ans &= ~0x10;
  }
  if (inv == 1) {
    ans |= 0x08;
  } else {
    ans &= ~0x08;
  }
  spiWriteReg(OSD_DMM, ans);
  ans = spiReadReg(OSD_DMM);
}

//MAX7456 点滅時間の設定
void osd_setBT(byte bt, byte dt) {
  byte ans = spiReadReg(OSD_VMM1);
  ans &= ~(0xF0);
  bt &= ~(0xFC);
  dt &= ~(0xFC);

  bt <<= 2;
  bt += dt;
  ans += bt;

  spiWriteReg(OSD_VMM1, ans);
}

//MAX7456 背景ビデオのオン/オフ
void osd_video(boolean sw) {
  byte ans = spiReadReg(OSD_VMM1);
  if (sw == 0) {
    ans |= 0x80; //video on
  } else {
    ans &= ~0x80; //video off
  }
    spiWriteReg(OSD_VMM1, ans);
}

void osd_bgLevel(byte bg) {
  byte ans = spiReadReg(OSD_VMM1);
  bg &= ~(0x07);
  bg <<= 4;
  ans &= ~(0x70);
  ans |= bg;
  spiWriteReg(OSD_VMM1, ans);
}

◆ 表示範囲

テキスト画面は、横30文字×16行の表示ができるのだが全てが画面内に収まらない。 使用するディスプレイのもよるが上下左右が画面からはみ出してしまう。

メモリは480バイトあるので 9 ビットでの指定となる、 下位 8ビットは DMAL で指定し 9 ビット目は DMAH の bit 0 で指定する。

表示の微調整は縦横のオフセット値で調整し、好みの位置を探る必要がある。 自分はオフセットの調整なしで 26文字×12行として使用している。

アドレスの指定はソフトウェアで変換を行う必要がある。 正直、OSDの使用ではほぼ決まった位置に文字を表示するので位置指定は余り必要ないかもしれない。

工場出荷時のフォントはアスキーコードと関係なく、数字を先頭にアルファベット、カナ、かな、漢字、アイコンと並んでいるので文字列をそのまま表示することはできない。

Horizontal Offset ( W=02H : R=82H )

  bit 5-0  水平方向の余白量
           00 0000 = 00H  -32 pixels
           10 0000 = 20H    0 pixels
           11 1111 = 3FH   31 pixe;s
Vertical Offset ( W=03H : R=83H )

  bit 4-0  垂直方向の余白量
            0 0000 = 00H   16 pixels
            1 0000 = 10H    0 pixels
            1 1111 = 1FH  -15 pixels

◆ 文字表示

Display Memory Address にアドレスをセットして DMDI へデータを書き込むと目的の位置に文字が表示される。

Display Memory Mode ( W=04H : R=84H )

  bit 6    Operation Mode Selection
               0 = 16-bit operation mode
               1 =  8-bit operation mode
  bit 5    背景モード (Local Background Control bit / LBC)
               表示モード参照
  bit 4    文字の点滅
               0 = off
               1 = on
  bit 3    文字の反転
               0 = off
               1 = on
  bit 2    画面消去 (clear display)
               1 でクリア。クリアに 20us 必要。
  bit 1    Vertical Sync Clear
               0 = Immediately applies the clear display-memory command, DMM[2] = 1
               1 = Applies the clear display-memory command, DMM[2] = 1, at the next VSYNC time
  bit 0    Auto Increment Mode
               0 = Disabled
               1 = Enabled
Display Memory Address High ( W=05H : R=85H )

  bit 1    Display Memory Data In で指定するデータを指定
               0 = キャラクタコードを指定
               1 = 文字属性を指定
  bit 0    Display Memory Address bit 8
               書込み/読出しアドレスの上位8ビット目
Display Memory Address Low ( W=06H : R=86H )

  bit 7-0  Display Memory Address bit 7-0
               書込み/読出しアドレスの下位1バイト
Display Memory Data In ( W=07H : R=87H )

  bit 7-0      書込み/読出しデータ1バイト

●Display Memory Address
いわゆる テキスト用のRAMアドレス。 30×16の480個のアドレスがあるので9ビットのデータとなる。 そのため上位1ビットは、DMAH の bit 0 で指定し、残りは DMAL で指定する。

●点滅と反転

文字ひとつずつに属性を持つ為、キャラクタ液晶などはブリンク命令で画面全てが点滅するのに対して、点滅する文字と点滅しない文字を混合して表示することができる。 反転表示も同じく通常の文字と反転文字を同時に表示することができる。

文字属性は、LBC(Local Background Control)、BLK(Blink Control)、INV(Invert Control) があり DMM(84H) で設定できる。

点滅間隔は VM1 の BT と BDC で決定する。

●文字毎の背景属性

背景を外部ビデオ信号の出力時に、文字の背景のみグレイにすることにより、文字を読みやすくすることができ、DMM の bit 5 で制御する。

●表示データの読み取り

DMAH の bit 1 を 1 にすることによりアドレスで指定された位置の文字属性を DMDI で読み出す事が出来る。その場合 データの bit 7 が背景属性、 bit 6 が点滅、 bit 5 が反転のフラグとなる。

DMAH の bit 1 を 0 で表示されているアドレスで指定された位置の文字を読み出せる。

正直、OSD の表示では余り使用することはないと思う機能。


◆ フォントの定義

MAX7456 は 256個のフォントデータを内蔵 EEPROM へ規則することができる。

一文字は横12ドット、縦18ドットで構成されるが、ドット当たり白/黒/透過色と 2ビットのデータが必要となるので一文字当たり54バイトのデータ量となる。

Character Memory Mode ( W=08H : R=88H )

  1010xxxx Writing to NVM
  0101xxxx Reading from NVM
Character Memory Address High ( W=09H : R=89H )

  bit 7-0  書込み/読出しを行うキャラクタコード
Character Memory Address Low ( W=0AH : R=8AH )

  bit 5-0  文字を構成するデータの位置 0 ~ 53
Character Memory Data In ( W=0BH : R=8BH )

  bit 7-0      書込み/読出しデータ1バイト

●NVM

NVM とは nonvolatile character memory でEEPROMの事。 CMAH, CMAL, CMDI で 54バイトのデータを読み書きするのだが、このデータは直接 EEPROM とアクセスしているわけではない。

メモリ内のワークスペース(シャドウRAM)が64バイトあり、 CMM を指定することにより、EEPROM ←→ シャドウRAM のデータ複写が行われる。

つまり EEPROM内のフォントを読み込むには、初めに CMAH でアドレスを指定し CMM で EEPROM → シャドウRAM へ複写し、 CMAL 指定後 CMDI で読込む。 CMAL ,CMDI を54回繰り返し一つの文字データを取得する。

同じくCMAH を指定し 0 ~ 53 の CMAL,CMDI を書込み、全てのデータを書き終わったら CMM で シャドウRAM → EEPROM と複写する。

シャドウRAMは一文字分の記憶範囲しか持たないので、一文字アクセスしたら必ず CMM の操作を行う。

フォントの読み取り

0.STAT の bit 5 が busy でない事を確認。
  VMM0 の bit 3 を 0 (off) にする。
1.CMAH に読み取りたい文字の番号を格納する。
2.CMM に 50H (0101 0000) を格納し、NVM (EEPROM) より文字データをシャドウRAMへ転送する。
3.CMAL に 0 ~ 53 のアドレスをセットする。
4.CMDO よりデータを読み込む。
3.4.を 54 回繰り返すと一つの文字データとなる
フォントの書込み

0.STAT の bit 5 が busy でない事を確認。
  VMM0 の bit 3 を 0 (off) にする。
1.CMAL に 0 ~ 53 のアドレスをセットする。
2.CMDI にフォントデータを格納する。
1.2.を 54 回繰り返しシャドウRAMに一つの文字データを格納する。
3.CMAH に書き込みたい文字の番号を格納する。
4.CMM に A0H (1010 0000) を格納し、NVM (EEPROM) へシャドウRAMの内容を転送する。

サンプルを作りたかったが、読み取りデータに異常が出るのでうまくゆかないし、ソフトウェアでのリセット動作も、納得できない部分がある。

どこに問題があるのか突き止められていないが、表示については思い通りの動作ができているので、心残りではあるがここまでで切り上げることにする。


データ読み込みがうまくいかない原因は、SPIでの送信は信号の立ち上がりで行っているのに対し、受信は立下りで行っているためだろうか。 受信したフォントのデータを見ると bit 0 がおかしいデータとなっている。