wiki:KanjiROM

日本語フォントROM GT20L16J1Y の使い方

日本語フォントROM GT20L16J1Y Breakoutでも販売している日本語フォントROM「GT20L16J1Y」の詳細説明と、サンプルプログラムです。
データシートもありますが、中国語でわかりにくいのと内容に若干不足があるので補足説明をします。 こちらのpdfは出所が不明ですが、上記のデータシートより一部詳しい情報が乗っています。
検索によって出てくるデータシートごとに記載内容が若干異なりますが、
本ページに記載されているROMの内容は実際の商品を用いて調べたものです。


収録内容

1.全角文字

1文字あたり32byteの容量を持ちます。
JIS X 0208の第一水準及び第二水準の漢字に対応しています。
JIS X 0208に含まれない、いわゆる機種依存文字(全角1文字の「メートル」やローマ数字などの表外字)も収録されていますが、
独自のアドレスに配置されているようでJISコードと同じ変換プログラムではうまく任意の文字を読み出せませんでした。

番号アドレス(10進数)内容説明
00(空白)JISX 1区1点
132JISX 1区2点
136143552JISX 15区46点
126143584JISX 16区1点
7716246912JISX 84区6点
7717246944(空白)1区と同等の内容
7810249920
78112499524区と同等の内容
79052529605区と同等の内容
7998255936(空白)

アドレスの計算式

メーカーPDFより抜粋です。一部記載ミスと思われる点を変更しています。(138463->138464)
JIS X 0208の区をMSB、点をLSBとしたときのアドレスを求めています。
アドレスは最大18bitになるので、変数の宣言は32bitで行うほうが安全です。(Arduinoのintは16bit)

if(MSB >=1 && MSB <= 15 && LSB >=1 && LSB <= 94)
Address =( (MSB - 1) * 94 + (LSB - 01))*32;
else if(MSB >=16 && MSB <= 47 && LSB >=1 && LSB <= 94)
Address =( (MSB - 16) * 94 + (LSB - 1))*32+43584;
else if(MSB >=48 && MSB <=84 && LSB >=1 && LSB <= 94)
Address = ((MSB - 48) * 94 + (LSB - 1))*32+ 138464;
else if(MSB ==85 && LSB >=0x01 && LSB <= 94)
Address = ((MSB - 85) * 94 + (LSB - 1))*32+ 246944;
else if(MSB >=88 && MSB <=89 && LSB >=1 && LSB <= 94)
Address = ((MSB - 88) * 94 + (LSB - 1))*32+ 249952;

2.半角文字

一文字あたり16byteの容量を持ちます。
独自の配列(?)で半角ひらがなと半角カタカナに対応しています。
メモリアドレスのオフセットは10進数で255968です。下記の表にはアドレスを通しで載せてあります。

番号アドレス(10進数)内容説明
0255968(空白)ASCIIコード準拠
95257488(空白)
02575040下記の表1を参照してください
1282595360下記の表2を参照してください
261261680)

アドレスの計算式

メーカーpdfより抜粋です。ASCIIコードを与えるとアドレスを返します。

if(ASCIICODE >=0x20 && ASCIICODE <=0x7F)
Address =( ASCIICODE - 0x20)*16+255968;

表1

左上から順番に記録されています。「空」となっているところは空です。
全部半角サイズです。全128文字。

0123456789:;()
*#/

表2

左上から順番に記録されています。「空」となっているところは空です。
全部半角サイズです。全134文字。
こちらに記録されているアルファベットはサンセリフです。(ASCII互換の部分はセリフ体)

0123456789_:;,.
*ABCDEFGHIJKLMNO
PQRSTUVWXYZ
#abcdefghijklmno
pqrstuvwxyz@! ?
&/()

接続方法

本製品は3.3V動作なので、下の配線図では1.8KΩと3.3KΩで分圧して入力につないでいます。

kanjirom-board2


通信方法

本製品におけるSPI通信では、MODE0またはMODE3に対応しています。
動作速度は最大30MHzです。

1.SPI通信で0x03を書き込みます。
これによりROMがデータ読み出しモードになります。
2.SPI通信でデータを読みたいアドレスを8bit*3回で送ります。最下位ビット詰めです。
3.送ったデータにしたがってダミーデータ0x00を16回又は32回送ります。この時に受信されるデータがROMの内容です。
最初に書き込んだ0x03のモードでの説明です。0x0Bを書き込んだ際の「高速読み出しモード」ではこの通りではありません。

受信内容

半角または全角によって受信するデータのサイズが違います。半角は16Byte、全角は32Byteです。
どちらも少し変わった配列になっていますので、表示先によって一工夫必要になります。

全角文字

左上から縦に8ドット(=1Byte分)ずつ送られてきます。
今回のサンプルプログラムでは、「送られてきた1~16Byte目のLSBから順に表示→17~32Byte目のLSBから順に表示」としています。
16x16

半角文字

左上から縦に8ドット(=1Byte分)ずつ送られてきます。
今回のサンプルプログラムでは、「送られてきた1~8Byte目のLSBから順に表示→9~16Byte目のLSBから順に表示」としています。
16x8


サンプルプログラム

Arduino IDE のシリアルモニタから送った日本語を、 ROMから読みだしてシリアルモニタに返すプログラムです。 半角カナには対応していません。 GT20L16J1Y_sample.zip

下記の全角小文字oフォントの改善を行ったスケッチをGitHubで公開しています

/* GT20L16J1Y用サンプルプログラム
 * Arduinoのシリアルモニタから送信されたSJISの文字を
 * ROMから読みだしてシリアルモニタに返す
 * 半角カナまわりはデータにはあるがROM内の順とJISX規格での順が
 * 一致しないため未実装
 *
 * memo:9/30 00:00
 * memo:10/7 18:30
 */

#include <SPI.h>
unsigned char matrixdata32[32]; //16×16用表示データ
unsigned char matrixdata16[16]; //16×8用表示データ

void setup (void)
{
  pinMode (SS, OUTPUT);
  Serial.begin(115200);
  /*SPI通信の設定*/
  SPI.begin ();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(3);
  SPI.setClockDivider(SPI_CLOCK_DIV16);

  Serial.println("input word which you want to show");

}

void loop (void)
{

  if (Serial.available() > 0)
  {
    /*Arduinoのシリアルモニタから送られてくる日本語がSJISであることに注意*/
    delayMicroseconds(200);//最低でも2文字受信したい
    uint8_t msbdata = Serial.read();//1バイト目 //10/07
    /*SJISの1バイトコードか否か*/
    if ( (msbdata < 0x80) || ((0xA0 < msbdata) && (msbdata <= 0xdF)) )
    {
      showSJIS1byte(msbdata);
    } else
    {
      uint8_t lsbdata = Serial.read();//2バイト目 //10/07 
      uint16_t data =  ((msbdata << 8) + lsbdata); //2
      showSJIS2byte(data);
    }
  }
}

/*1byteのSJISを表示する*/
void showSJIS1byte(uint8_t code)
{
  readFontASCII(code);
  sendDotsToSerial16();
}

/*2byteのSJISを表示する*/
//showSJIS2byte(SJIS文字コード)
void showSJIS2byte(unsigned short code)
{
  /*Arduinoのシリアルで日本語はSJIS送信なのでSJIS->JISx0208変換をする*/
  Serial.print("SJIS, 0x"); Serial.print(code, HEX); Serial.print("\t");
  uint8_t c1 = ((code & 0xff00) >> 8);
  uint8_t c2 = (code & 0xFF);
  if (c1 >= 0xe0)
  {
    c1 = c1 - 0x40;
  }
  if (c2 >= 0x80)
  {
    c2 = c2 - 1;
  }
  if (c2 >= 0x9e)
  {
    c1 = (c1 - 0x70) * 2;
    c2 = c2 - 0x7d;
  } else
  {
    c1 = ((c1 - 0x70) * 2) - 1;
    c2 = c2 - 0x1f;
  }
  /*読み出し*/
  readFontJIS(c1, c2);
  /*表示*/
  sendDotsToSerial32();
}

/*漢字ROMとやりとり*/
//readFontJIS(JIS上位8bit,JIS下位8bit);
void readFontJIS(uint8_t c1, uint8_t c2)
{
  /*jisx変換後の表示*/
  Serial.print("jisx up8 = 0x"); Serial.print(c1, HEX); Serial.print("\t");
  Serial.print("jisx down8 = 0x"); Serial.print(c2, HEX); Serial.print("\t");
  /*jisxの区点を求める*/
  uint32_t MSB = c1 - 0x20;//区
  uint32_t LSB = c2 - 0x20;//点
  /*JISの句点番号で分類*/
  uint32_t Address = 0;
  Serial.print("MSB = d"); Serial.print(MSB, DEC); Serial.print("\t");
  Serial.print("LSB = d"); Serial.print(LSB, DEC); Serial.print("\t");
  /*各種記号・英数字・かな(一部機種依存につき注意,㍍などWindowsと互換性なし)*/
  if (MSB >= 1 && MSB <= 15 && LSB >= 1 && LSB <= 94)
  {
    Address = ( (MSB - 1) * 94 + (LSB - 1)) * 32;
  }
  /*第一水準*/
  if (MSB >= 16 && MSB <= 47 && LSB >= 1 && LSB <= 94)
  {
    Address = ( (MSB - 16) * 94 + (LSB - 1)) * 32 + 43584;
  }
  /*第二水準*/
  if (MSB >= 48 && MSB <= 84 && LSB >= 1 && LSB <= 94)
  {
    Address = ((MSB - 48) * 94 + (LSB - 1)) * 32 + 138464;
  }
  //
  /*GT20L16J1Y内部では1区と同等の内容が収録されている*/
  if (MSB == 85 && LSB >= 0x01 && LSB <= 94)
  {
    Address = ((MSB - 85) * 94 + (LSB - 1)) * 32 + 246944;
  }
  /*GT20L16J1Y内部では2区、3区と同等の内容が収録されている*/
  if (MSB >= 88 && MSB <= 89 && LSB >= 1 && LSB <= 94)
  {
    Address = ((MSB - 88) * 94 + (LSB - 1)) * 32 + 249952;
  }
  /*漢字ROMにデータを送信*/
  digitalWrite(SS, HIGH);
  Serial.print("Address = "); Serial.println(Address, HEX);
  digitalWrite(SS, LOW);  //通信開始
  SPI.transfer(0x03);
  SPI.transfer(Address >> 16  & 0xff);
  SPI.transfer(Address >> 8   & 0xff);
  SPI.transfer(Address        & 0xff);
  /*漢字ROMからデータを受信*/
  for (int i = 0; i < 32; i++)
  {
    matrixdata32[i] = SPI.transfer(0x00);
  }
  digitalWrite(SS, HIGH); //通信終了
}//spireadfont

/*漢字ROMとやりとり*/
//readFontASCII(ASCIIコード);
void readFontASCII(uint8_t ASCIICODE)
{
  Serial.print("ASCII,0x");Serial.print(ASCIICODE, HEX); //10/07
  uint32_t Address = 0;
  /*ASCII文字*/
  if (ASCIICODE >= 0x20 && ASCIICODE <= 0x7F)
  {
    Address = ( ASCIICODE - 0x20) * 16 + 255968;
  }
  /*漢字ROMにデータを送信*/
  digitalWrite(SS, HIGH);
  Serial.print("  Address = "); Serial.println(Address, HEX);
  digitalWrite(SS, LOW);  //通信開始
  SPI.transfer(0x03);
  SPI.transfer(Address >> 16  & 0xff);
  SPI.transfer(Address >> 8   & 0xff);
  SPI.transfer(Address        & 0xff);
  /*漢字ROMからデータを受信*/
  for (int i = 0; i < 16; i++)
  {
    matrixdata16[i] = SPI.transfer(0x00);
  }
  digitalWrite(SS, HIGH); //通信終了

}

/*シリアルモニタへ16*16のデータを表示する*/
void sendDotsToSerial32()
{
  /*上半分*/
  for (int i = 0; i < 8; i++)
  {
    for (int b = 0; b < 16; b++)
    {
      char byteDigit = (1 << i);
      if (matrixdata32[b] & byteDigit)
      {
        Serial.write("XX");
      } else
      {
        Serial.write("--");
      }
    }
    Serial.println();
  }
  /*下半分*/
  for (int i = 0; i < 8; i++)
  {
    for (int b = 16; b < 32 ; b++)
    {
      char byteDigit = (1 << i);
      if (matrixdata32[b] & byteDigit)
      {
        Serial.write("XX");
      } else
      {
        Serial.write("--");
      }
    }
    Serial.println();
  }
  Serial.println();
}//sendDataToSerial16


/*シリアルモニタへ16*8のデータを表示する*/
void sendDotsToSerial16()
{
  /*上半分*/
  for (int i = 0; i < 8; i++)
  {
    for (int b = 0; b < 8; b++)
    {
      char byteDigit = (1 << i);
      if (matrixdata16[b] & byteDigit)
      {
        Serial.write("XX");
      } else
      {
        Serial.write("--");
      }
    }
    Serial.println();
  }
  /*下半分*/
  for (int i = 0; i < 8; i++)
  {
    for (int b = 8; b < 16; b++)
    {
      char byteDigit = (1 << i);
      if (matrixdata16[b] & byteDigit)
      {
        Serial.write("XX");
      } else
      {
        Serial.write("--");
      }
    }
    Serial.println();
  }
  Serial.println();
}//sendDataToSerial32

動作サンプル

Sample1

「技術」と入力してEnterを押した時の動作です。
sample1

Sample2

「Ss」と入力してEnterを押した時の動作です。
sample2

Sample3

ASCII準拠の部分に収録されている「K」と、表2で示されるアドレスの「K」の表示の違いです。
diff_kk

サンプルプログラムの修正(2016/07/25)

全角小文字ののサイズが、大文字のものと変わらないというROM内部のフォントのデザインミスがあるようです。 以下のコードを追加することで正しいサイズのを描画できます。

プログラム冒頭の方に以下の配列を追加してください。

変更済スケッチはGitHubで公開しています

const unsigned char matrixdata32_o[32] = 
{
B00000000,
B00000000,
B00000000,
B00000000,
B11000000,
B00100000,
B00010000,
B00010000,
B00010000,
B00100000,
B11000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000011,
B00000100,
B00001000,
B00001000,
B00001000,
B00000100,
B00000011,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
};

また、void showSJIS2byte(unsigned short code)を以下のコードに置き換えてください。

/*2byteのSJISを表示する*/
//showSJIS2byte(SJIS文字コード)
void showSJIS2byte(unsigned short code)
{ 
  if(code == 0x828F)
  {
    for(int i=0;i<32;i++)
    {
      matrixdata32[i] = matrixdata32_o[i];
    }
    sendDotsToSerial32();
  }else{
    /*Arduinoのシリアルで日本語はSJIS送信なのでSJIS->JISx0208変換をする*/
    Serial.print("SJIS, 0x"); Serial.print(code, HEX); Serial.print("\t");
    uint8_t c1 = ((code & 0xff00) >> 8);
    uint8_t c2 = (code & 0xFF);
    if (c1 >= 0xe0)
    {
      c1 = c1 - 0x40;
    }
    if (c2 >= 0x80)
    {
      c2 = c2 - 1;
    }
    if (c2 >= 0x9e)
    {
      c1 = (c1 - 0x70) * 2;
      c2 = c2 - 0x7d;
    } else
    {
      c1 = ((c1 - 0x70) * 2) - 1;
      c2 = c2 - 0x1f;
    }
    /*読み出し*/
    readFontJIS(c1, c2);
    /*表示*/
    sendDotsToSerial32();
  }
}

付録

mbed - Componentshttps://developer.mbed.org/...

Last modified 11 months ago Last modified on Jul 26, 2016 7:41:02 AM

Attachments (8)

Download all attachments as: .zip