マトリクスＬＥＤパネルの使い方（３）

#はじめに
**共立エレショップ**で販売販売されている「<a href="https://eleshop.jp/shop/g/gEB8411/">32×16ドットマトリクスLEDパネル</a>」を使ったプログラムを紹介して欲しいとの依頼があり、「<a href="https://qiita.com/Yukiya_Ishioka/items/fd791498bb5c02244546">aitendoマトリクスＬＥＤパネルの使い方（１）</a>」、「<a href="https://qiita.com/Yukiya_Ishioka/items/221077ad507bf5110e64">aitendoマトリクスＬＥＤパネルの使い方（２）</a>」という記事を投稿していて、今回は３つめの記事になります。
なお、前回まではタイトルに **aitendo**と付けていましたが、現在、aitendoでは販売されていないので、「マトリクスＬＥＤパネルの使い方（３）」とシンプルにしました。

![matled_ai_03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/17781f32-3ad1-6dfe-c2a0-2b4a0c1fcb74.jpeg)
![matled_ai_04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/665e6105-bae1-c2f0-e9dd-082aabdeae82.jpeg)

今回紹介するプログラムはこのマトリクスＬＥＤを使ってプログラム内に定義した文字列をスライドさせながら表示します。色は文字単位で 赤、緑、橙の３色を指定できます。文字列をドットデータへ変換するため、前回と同様にＣＱ出版のInterface誌 <a href="https://interface.cqpub.co.jp/wp-content/uploads/if10_156.pdf">2018年10月号「ESP実験コーナー」</a>で作成したプログラムを流用します。

ハードウェアは今までと同様に **ESP32-DevKitC**を使って制御します。「<a href="https://eleshop.jp/shop/g/gEB8411/">32×16ドットマトリクスLEDパネル</a>」の「ハードウェア」の項で説明しているので、そちらを参照してください。
また、**Arduino IDE** を使って作成やビルドを行うので、Arduino IDEとESP32をビルドできる環境を準備しておいてください。「**arduino ide esp32-devkit**」で検索すると環境の構築方法を説明したページが見つけられると思います。

今回のプログラムは表示に動きがあるため、プログラムの動作結果の動画を youtube へアップしたので、<a href="https://youtu.be/TKK3CAs6z4o">こちら</a>も併せてご覧ください。
　youtube「**ESP32を使った電光掲示板**」
　　https://youtu.be/TKK3CAs6z4o

その他のリンク情報を以下に記しておきます。

- **共立エレショップ**の商品ページ
　32×16ドットマトリクスLEDパネル(赤/橙/緑) 32x16DOT-0158-DJK
　https://eleshop.jp/shop/g/gEB8411/

- **aitendoマトリクスＬＥＤパネルの使い方（１）**
　https://qiita.com/Yukiya_Ishioka/items/fd791498bb5c02244546

- **aitendoマトリクスＬＥＤパネルの使い方（２）**
　https://qiita.com/Yukiya_Ishioka/items/221077ad507bf5110e64

- **Interface誌 2018年10月号**
　https://interface.cqpub.co.jp/magazine/201810/
　**「ESP実験コーナー」**
　https://interface.cqpub.co.jp/wp-content/uploads/if10_156.pdf


#流用プログラムの取り出し
文字コードからドットフォントへの変換プログラムはInterface誌に執筆した際に利用したプログラムを流用します。
プログラムの内容などはInterface誌 2018年10月号「ESP実験コーナー」を参照してください。
ここではダウンロードサービスから必要なプログラムをダウンロードして利用する方法を記します。

まず、Interface誌のダウンロードサイトから <a href="https://www.cqpub.co.jp/interface/download/2018/10/IF1810EE.zip">IF1810EE.zip</a> をダウンロードします。
**IF1810EE.zip**を展開すると複数のディレクトリが出てきますが、**matled64-2**ディレクトリのプログラムを流用します。
**matled64-2**ディレクトリには４つのファイルがありますが、今回は以下の３つのファイルを流用します。これらのファイルを後で作成する **matled_ai.ino**と同じディレクトリへコピーしてください。
ちなみにドットフォントのサイズは全角文字が **横１６ドット×縦１６ドット**、**半角文字が横８ドット×縦１６ドット** です。

| \IF1810EE\matled64-2  |
|:------------------------|
| fx_8x16rk_fnt.c |
| jiskan16_fnt.c |
| Utf8Sjis_tbl.c |

- **Interface誌のダウンロードサイト「2018年」** <BR>
https://www.cqpub.co.jp/interface/download/contents2018.htm
- **10月号　特集　IT農耕実験」連載 定番IoTマイコンESP実験コーナ〈第1回〉** <BR>
IF1810EE.zip <BR>
https://www.cqpub.co.jp/interface/download/2018/10/IF1810EE.zip


#プログラムの概要

表示用バッファとしてunsigned char型の２次元配列 **dat_buffer[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ]**を準備して、ここに文字列からドットフォントへ変換したデータを配置し、マトリクスＬＥＤへ表示を行います。配列の **[ DEF_FONT_SIZE ]**が縦方向、**[ DEF_OUTDATA_NUM ]**が横方向の並びになります。
配列へ入れるデータは **0～3** で以下のように表示させたい色に該当する値を設定します。

| 番号 |表示色|
|:------:|:--------:|
| 0 | 黒（消灯） |
| 1 | 赤 |
| 2 | 緑 |
| 3 | 橙 |

表示用バッファと実際の表示との関係は以下のようになります。
![clip_13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370759/cf7ede35-df6a-24dd-f87d-a79cd4bd46fa.jpeg)
**１）表示用バッファの先頭から横３２ドットは常に消灯**
**※１部分**ですが、これは表示開始時にマトリクスＬＥＤ上にいきなり文字を出すと先頭部分が読みにくいことと、見てる側を少しビックリさせてしまうと考えたからです。このため最初は消灯状態で右端から１ドットずつ表示か開始されるようにするためです。

**２）表示用データは横方向の３３ドット目から配置**
プログラム内で設定した表示したい文字列は文字コードからドットフォントへ変換され**３３ドット目**から配置していきます。文字列が長くて表示用バッファに入りきらない場合、文字単位で表示用バッファへの書き込みを切ります。

**３）表示データの終端横３２ドットは常に消灯**
**※２部分**ですが、先頭同様に表示の終端部分も３２ドット分は常に消灯します。これは最後の文字が右から左へスライドして左端からすべてが消えるまで表示処理を行うためです。

**４）文字列のスライド**
文字列が右から左へスライドされるように表示するため、表示のタイミングで表示バッファの先頭から１ドットずつ右方向へずらしながら表示しています。
表示タイミングは loop()関数の最後にある delay()関数で指定する時間で早くしたり遅くしたりします。
表示用データの最後の列が左端へ消えたら、再び表示用バッファの先頭へ戻してスライド表示を繰り返します。

#プログラムの説明
プログラムの多くの部分は前回の「<a href="https://qiita.com/Yukiya_Ishioka/items/221077ad507bf5110e64">aitendoマトリクスＬＥＤパネルの使い方（２）</a>」からの流用なので、これからの説明は今回用に作成や修正をした内容を説明します。

##ボタン操作
**ESP32-DevKitC**にはオンボードでプログラムからオン／オフを知ることができるボタンが付いています。
今回はこのボタンを使って表示のスライドスピードを変更する機能をつけました。このためボタンの状態を取得するための定義や処理を追加しています。

```c++
#define BTNPIN     0
  :
pinMode(BTNPIN,  INPUT);
  :
if( led_speed_flg != 0 && digitalRead( BTNPIN ) == LOW ) {
  :
if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
```

##表示データ
マトリクスＬＥＤ上に表示したい文字列を以下の部分で設定します。

```c++
struct  msg_type {
  int  color;
  const char  *msg;
};

/*
 *  color = 0:black  1:red  2:green  3:orange
 */
struct  msg_type  msg_text[] = {
  { 1, "赤色" },
  { 0, " " },
  { 2, "緑色" },
  { 0, " " },
  { 3, "橙色" },
  { 0, " " },
  { 1, "RED" },
  { 0, " " },
  { 2, "GREEN" },
  { 0, " " },
  { 3, "ORANGE" },
  { 0, " " },
  { 1, "0123456789" },
  { 2, "abcdefghijklmnopqrstuvwxyz" },
  { 3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" },
  { 0, NULL }    /* terminator */
};
``` 

構造体配列 **msg_text**内に表示色（ 0～3 ）と文字列を列記することで色を変えながら自身で設定した文字列を表示することができます。
構造体配列は「**msg_text[]**」と記述することで配列の個数は特に決まっていません。配列の最後に「 **{ 0, NULL }** 」を付けることでデータの終端を認識します。データを変更して使う場合にこの終端のデータをつけ忘れないよう注意してください。
**msg_text**内のデータの意味は以下の通りです。最初に表示色の値、次に表示したい文字列を設定します。

```
   
  表示色  表示したい文字列
    ↓      ↓
  { 1,   "赤色" },
```

単語や文字間の空白（スペース）は、今回のプログラムでは**※３**のように「黒」で明示的に記述していますが、空白自体は点灯するデータは含まれず色の指定は意味をもたないため、前の文字列の終端部分**※４**、もしくは次の文字列の先頭部分**※５**に含めても構いません。

```
  { 1, "赤色" },
  { 0, " " },     ←※３
  { 2, "緑色" },
```


```
  { 1, "赤色 " }, ←※４
  { 2, "緑色" },
```


```
  { 1, "赤色" },
  { 2, " 緑色" }, ←※５
```

##スライドスピード
表示のスライドスピードはloop()関数の最後にある delay()関数で調整するようになっていますが、このdelay()関数へ設定するための定義が以下になります。
配列 **led_speed_tbl[]** に定義されている値は delay()関数へ与える待ち時間のミリ秒です。

```c++
#define DEF_SPEED_NUM  8
#define DEF_SPEED_INI  3
int  led_speed_tbl[ DEF_SPEED_NUM ] = {
  500, 200, 100, 70, 50, 30, 20, 10
};
int  led_speed_val;
int  led_speed_flg;
```

##make_message()
文字列から表示用のドットデータの変換を行います。
前回は make_message()の呼び出し１回で１つ分の表示データを作成していましたが、今回は複数の文字列を結合して表示用データとするため、引数 bitposを追加して表示用バッファの横方向のどの位置から文字列を書き込むかを指定できるようにしました。
また、黒も指定できるようにするため、引数を明確に colorとし、値も 0～3で指定できるようにしました。

##setup()
文字列から表示用のドットデータへ変換する処理を、構造体配列 **msg_text**に設定されている複数の表示色と文字列の組で表示用バッファへデータを展開する処理へ変更しています。
また、スライドスピードの変数の初期化する処理を追加しています。

```c++
  clear_message();
  bitpos = DEF_DISP_WIDTH;
  num = 0;
  while( (msg = (unsigned char *)msg_text[ num ].msg) != NULL ) {
    bitpos = make_message( msg, strlen( (const char*)msg ), msg_text[ num ].color, bitpos );
    num++;
  }
  led_msg_pos = 0;
  led_msg_len = bitpos;
  Serial.printf( "data width = %d\n", led_msg_len );

  led_speed_val = DEF_SPEED_INI;
  led_speed_flg = 1;
  Serial.printf( "delay time = %3dmsec\n", led_speed_tbl[led_speed_val] );
```


##loop()
loop()関数が呼び出されるごとに表示用データがスライドするよう、表示用バッファのどの位置から表示を行うかの表示処理の変更と、表示位置変数の更新処理を追加しています。

```c++
    for( col=0 ; col<DEF_DISP_WIDTH ; col++ ) {

      data = dat_buffer[ row ][ led_msg_pos + col ];
      digitalWrite( DRPIN, data & 0x01 );
      digitalWrite( DGPIN, data & 0x02 );

      digitalWrite( CLKPIN, HIGH );
      digitalWrite( CLKPIN, LOW );
    }
```

```c++
  led_msg_pos++;
  if( led_msg_pos >= led_msg_len ) {
    led_msg_pos = 0;
  }
```

また、ボタンを押下することでスライドスピードが変化するよう、ボタンオン／オフのチェック処理とボタンが押下された場合にスライドスピードを配列 **led_speed_tbl[]**から取得する処理を追加しています。

```c++
  if( led_speed_flg != 0 && digitalRead( BTNPIN ) == LOW ) {
    led_speed_val++;
    if( led_speed_val >= DEF_SPEED_NUM ) {
      led_speed_val = 0;
    }
    Serial.printf( "delay time = %3dmsec\n", led_speed_tbl[led_speed_val] );
    led_speed_flg = 0;
  }
  if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
    led_speed_flg = 1;
  }

  delay( led_speed_tbl[led_speed_val] );  /* delay msec */
```

なお、以下の処理はボタンを押しっぱなしにしたときに連続してスライドスピードが変化しないよう、ボタンが離された（オフされた）ことを検知するための処理です。

```c++
  if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
    led_speed_flg = 1;
  }
```

#表示プログラム
表示プログラムの **matled_a3i.ino** の全文を以下に記します。

```c++:matled_ai3.ino 
/*
 *  Copyright(C) by Yukiya Ishioka
 *
 *  https://eleshop.jp/shop/g/gEB8411/
 *  http://www.kyohritsu.jp/eclib/DIGIT/JNK/32x16dot0158.pdf
 *
 *  https://www.cqpub.co.jp/interface/download/2018/10/IF1810EE.zip
 *    fx_8x16rk_fnt.c
 *    jiskan16_fnt.c
 *    Utf8Sjis_tbl.c
 */

#include <stdio.h>
#include <string.h>

#define BTNPIN     0

           /*  ESP32   PANEL */
#define DRPIN     12  /* 11p */
#define DGPIN     13  /*  8p */
#define A0PIN     32  /*  6p */
#define A1PIN     33  /*  5p */
#define A2PIN     15  /*  4p */
#define A3PIN     19  /*  3p */
#define CLKPIN    25  /*  9p */
#define ALEPIN    26  /* 12p */
#define WEPIN     27  /* 10p */
#define SEPIN     14  /*  1p */
#define ABBPIN    18  /*  2p */

extern  const unsigned char  fx_8x16rk_fnt[];
extern  const unsigned char  jiskan16_fnt[];
extern  const unsigned char  Utf8Sjis_tbl[];

#define DEF_FONT_A16_VAR     fx_8x16rk_fnt
#define DEF_FONT_K16_VAR     jiskan16_fnt
#define DEF_FONT_SIZE        16
#define DEF_DISP_WIDTH       32
#define DEF_OUTDATA_NUM      1024

unsigned char  dat_buffer[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];

struct  msg_type {
  int  color;
  const char  *msg;
};

/*
 *  color = 0:black  1:red  2:green  3:orange
 */
struct  msg_type  msg_text[] = {
  { 1, "赤色" },
  { 0, " " },
  { 2, "緑色" },
  { 0, " " },
  { 3, "橙色" },
  { 0, " " },
  { 1, "RED" },
  { 0, " " },
  { 2, "GREEN" },
  { 0, " " },
  { 3, "ORANGE" },
  { 0, " " },
  { 1, "0123456789" },
  { 2, "abcdefghijklmnopqrstuvwxyz" },
  { 3, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" },
  { 0, NULL }    /* terminator */
};

int  led_msg_len ;
int  led_msg_pos ;

#define DEF_SPEED_NUM  8
#define DEF_SPEED_INI  3
int  led_speed_tbl[ DEF_SPEED_NUM ] = {
  500, 200, 100, 70, 50, 30, 20, 10
};
int  led_speed_val;
int  led_speed_flg;

/*
 *  clear display buffer
 */
void  clear_message( void )
{
    int  i, j ;
    unsigned char *buff = (unsigned char *)dat_buffer;

    for( i=0 ; i<DEF_FONT_SIZE ; i++ ) {
        for( j=0 ; j<DEF_OUTDATA_NUM ; j++ ) {
            *buff++ = 0x00;
        }
    }
}


/*
 *  set font data to display buffer
 */
void  set_font( unsigned char *font, unsigned char *buff, int  color, int pos, int width )
{
    int  i, j, k;
    int  row;
    int  w = (width/8);   /* font width byte */
    unsigned char  pat;

    /* row */
    for( i=0 ; i<DEF_FONT_SIZE ; i++ ) {
        row = DEF_OUTDATA_NUM * i;
        /* col */
        for( j=0 ; j<w ; j++ ) {
            pat = 0x80;
            for( k=0 ; k<8 ; k++ ) {
                if( (font[ i * w + j ] & pat) != 0 ) {
                    /*         base up/low  offset */
                    buff[ row + pos + j*8 + k ] = color;
                }
                pat >>= 1; /* bit shift */
            }
        }
    }
}


void UTF8_To_SJIS_cnv(unsigned char utf8_1, unsigned char utf8_2, unsigned char utf8_3, unsigned int* spiffs_addrs)
{
  unsigned int  UTF8uint = utf8_1*256*256 + utf8_2*256 + utf8_3;
   
  if(utf8_1>=0xC2 && utf8_1<=0xD1){
    *spiffs_addrs = ((utf8_1*256 + utf8_2)-0xC2A2)*2 + 0xB0;
  }else if(utf8_1==0xE2 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE28090)*2 + 0x1EEC;
  }else if(utf8_1==0xE3 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE38080)*2 + 0x9DCC;
  }else if(utf8_1==0xE4 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE4B880)*2 + 0x11CCC;
  }else if(utf8_1==0xE5 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE58085)*2 + 0x12BCC;
  }else if(utf8_1==0xE6 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE6808E)*2 + 0x1AAC2;
  }else if(utf8_1==0xE7 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE78081)*2 + 0x229A6;
  }else if(utf8_1==0xE8 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE88080)*2 + 0x2A8A4;
  }else if(utf8_1==0xE9 && utf8_2>=0x80){
    *spiffs_addrs = (UTF8uint-0xE98080)*2 + 0x327A4;
  }else if(utf8_1>=0xEF && utf8_2>=0xBC){
    *spiffs_addrs = (UTF8uint-0xEFBC81)*2 + 0x3A6A4;
    if(utf8_1==0xEF && utf8_2==0xBD && utf8_3==0x9E){
      *spiffs_addrs = 0x3A8DE;
    }
  }
}


int  utf8_to_sjis( unsigned char *buff, unsigned char *code1, unsigned char *code2 )
{
  unsigned char  utf8_1, utf8_2, utf8_3;
  unsigned char  sjis[2];
  unsigned int  sp_addres;
  int  pos = 0;
  int fnt_cnt = 0;
 
  if(buff[pos]>=0xC2 && buff[pos]<=0xD1){
    /* UTF8 2bytes */
    utf8_1 = buff[pos];
    utf8_2 = buff[pos+1];
    utf8_3 = 0x00;
    fnt_cnt = 2;
  }else if(buff[pos]>=0xE2 && buff[pos]<=0xEF){
    /* UTF8 3bytes */
    utf8_1 = buff[pos];
    utf8_2 = buff[pos+1];
    utf8_3 = buff[pos+2];
    fnt_cnt = 3;
  }else{
    utf8_1 = buff[pos];
    utf8_2 = 0x00;
    utf8_3 = 0x00;
    fnt_cnt = 1;
  }

  /* UTF8 to Sjis change table position */
  UTF8_To_SJIS_cnv( utf8_1, utf8_2, utf8_3, &sp_addres );
  *code1 = Utf8Sjis_tbl[ sp_addres   ];
  *code2 = Utf8Sjis_tbl[ sp_addres+1 ];

  return  fnt_cnt;
}


unsigned char  *get_fontx2_a( unsigned char *font, unsigned int code )
{
    unsigned char  *address = NULL ;
    unsigned int  fontbyte ;

    fontbyte = (font[14] + 7) / 8 * font[15] ;
    address = &font[17] + fontbyte * code ;

    return  address ;
}


unsigned char  *get_fontx2_k( unsigned char *font, unsigned int code )
{
    unsigned char  *address = NULL ;
    unsigned char  *tmp ;
    unsigned int  blknum, i, fontnum ;
    unsigned int  bstart, bend ;
    unsigned int  fontbyte ;

    fontbyte = (font[14] + 7) / 8 * font[15] ;
    fontnum = 0 ;

    blknum = (unsigned int)font[17] * 4 ;
    tmp = &font[18] ;
    for( i=0 ; i<blknum ; i+=4 ) {
        bstart = tmp[i]   + ((unsigned int)tmp[i+1] << 8) ;
        bend   = tmp[i+2] + ((unsigned int)tmp[i+3] << 8) ;
        if( code >= bstart && code <= bend ) {
            address = tmp + (fontnum + (code - bstart)) * fontbyte + blknum ;
            break ;
        }

        fontnum += (bend - bstart) + 1 ;
    }

    return  address ;
}


int  make_message( unsigned char *strbuff, unsigned int size, int color, int bitpos )
{
    int  pos;
    int  num;
    unsigned char  *fontdata;
    unsigned int  code;
    unsigned char  code1, code2 ;

    pos = 0 ;
    while( pos < size ) {
        code = strbuff[ pos ] ;  /* get 1st byte */
        if( code < 0x80 ) {
            /* for ASCII code */
            if( code == 0x0d || code == 0x0a ) {
                code = ' ' ;
            }
            fontdata = get_fontx2_a( (unsigned char *)DEF_FONT_A16_VAR, code );
            set_font( fontdata, (unsigned char *)dat_buffer, color, bitpos, DEF_FONT_SIZE/2 );
            bitpos += DEF_FONT_SIZE/2 ;
            pos++ ;
        } else {
            /* for KANJI code */
            num = utf8_to_sjis( &strbuff[ pos ], &code1, &code2 );
            code = (code1<<8) + code2 ;  /* get 2nd byte and marge */
            fontdata = get_fontx2_k( (unsigned char *)DEF_FONT_K16_VAR, code );
            set_font( fontdata, (unsigned char *)dat_buffer, color, bitpos, DEF_FONT_SIZE );
            bitpos += DEF_FONT_SIZE;
            pos += num;
        }

        if( bitpos >= (DEF_OUTDATA_NUM - DEF_FONT_SIZE - DEF_DISP_WIDTH) ) {
            break ;
        }
    }

    return  bitpos;
}


void led_addr_wr( int a0, int a1, int a2, int a3 )
{
  digitalWrite( A0PIN, a0 );
  digitalWrite( A1PIN, a1 );
  digitalWrite( A2PIN, a2 );
  digitalWrite( A3PIN, a3 );
  digitalWrite( ALEPIN, HIGH );
  digitalWrite( WEPIN, HIGH );
  digitalWrite( WEPIN, LOW );
  digitalWrite( ALEPIN, LOW );
}


void setup( void )
{
  int  col, row;
  int  a0, a1, a2, a3;
  int  bitpos;
  int  num;
  unsigned char  *msg;

  /* init serial */
  Serial.begin(115200);
  Serial.println( "call setup()" );
  pinMode(BTNPIN,  INPUT);

  pinMode( DRPIN,  OUTPUT );
  pinMode( DGPIN,  OUTPUT );
  pinMode( A0PIN,  OUTPUT );
  pinMode( A1PIN,  OUTPUT );
  pinMode( A2PIN,  OUTPUT );
  pinMode( A3PIN,  OUTPUT );
  pinMode( CLKPIN, OUTPUT );
  pinMode( ALEPIN, OUTPUT );
  pinMode( WEPIN,  OUTPUT );
  pinMode( SEPIN,  OUTPUT );
  pinMode( ABBPIN, OUTPUT );
  digitalWrite( CLKPIN, LOW );
  digitalWrite( ALEPIN, LOW );
  digitalWrite( WEPIN, LOW );

  digitalWrite( SEPIN,  LOW );
  digitalWrite( ABBPIN, LOW );

  /* dummy address set */
  led_addr_wr( LOW, LOW, LOW, LOW );

  /* clear MATLED */
  for( row=0 ; row<16; row++ ) {
    for( col=0 ; col<DEF_DISP_WIDTH ; col++ ) {
      digitalWrite( DRPIN, LOW );
      digitalWrite( DGPIN, LOW );
      digitalWrite( CLKPIN, HIGH );
      digitalWrite( CLKPIN, LOW );
    }
    a0 = a1 = a2 = a3 = 0;
    if( row & 0x1 ) a0 = 1;
    if( row & 0x2 ) a1 = 1;
    if( row & 0x4 ) a2 = 1;
    if( row & 0x8 ) a3 = 1;
    led_addr_wr( a0, a1, a2, a3 );
  }

  /* dummy address set */
  led_addr_wr( LOW, LOW, LOW, LOW );

  clear_message();
  bitpos = DEF_DISP_WIDTH;
  num = 0;
  while( (msg = (unsigned char *)msg_text[ num ].msg) != NULL ) {
    bitpos = make_message( msg, strlen( (const char*)msg ), msg_text[ num ].color, bitpos );
    num++;
  }
  led_msg_pos = 0;
  led_msg_len = bitpos;
  Serial.printf( "data width = %d\n", led_msg_len );

  led_speed_val = DEF_SPEED_INI;
  led_speed_flg = 1;
  Serial.printf( "delay time = %3dmsec\n", led_speed_tbl[led_speed_val] );
}


void loop( void )
{
  int  a0, a1, a2, a3;
  int  data;
  int  row, col;
  unsigned char  tmpchar;
 
  for( row=0 ; row<16 ; row++ ) {
    for( col=0 ; col<DEF_DISP_WIDTH ; col++ ) {

      data = dat_buffer[ row ][ led_msg_pos + col ];
      digitalWrite( DRPIN, data & 0x01 );
      digitalWrite( DGPIN, data & 0x02 );

      digitalWrite( CLKPIN, HIGH );
      digitalWrite( CLKPIN, LOW );
    }

    /* set access row position */
    a0 = a1 = a2 = a3 = 0;
    if( row & 0x1 ) a0 = 1;
    if( row & 0x2 ) a1 = 1;
    if( row & 0x4 ) a2 = 1;
    if( row & 0x8 ) a3 = 1;
    led_addr_wr( a0, a1, a2, a3 );
  }

  led_msg_pos++;
  if( led_msg_pos >= led_msg_len ) {
    led_msg_pos = 0;
  }

  /* dummy address set */
  led_addr_wr( LOW, LOW, LOW, LOW );

  if( led_speed_flg != 0 && digitalRead( BTNPIN ) == LOW ) {
    led_speed_val++;
    if( led_speed_val >= DEF_SPEED_NUM ) {
      led_speed_val = 0;
    }
    Serial.printf( "delay time = %3dmsec\n", led_speed_tbl[led_speed_val] );
    led_speed_flg = 0;
  }
  if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
    led_speed_flg = 1;
  }

  delay( led_speed_tbl[led_speed_val] );  /* delay msec */
}
```

以上