3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

aitendoマトリクスLEDパネルの使い方(2)

Last updated at Posted at 2020-04-29

はじめに

共立エレショップで販売販売されている「32×16ドットマトリクスLEDパネル」を使ったプログラムを紹介して欲しいとの依頼があり、「aitendoマトリクスLEDパネルの使い方(1)」という記事を投稿しました。
この時はESP32-DevKitCを使ってプログラム内に表示したいドットデータを配列定数で用意して表示を行っていましたが、今回はこの時のプログラムを改造して文字列をドットデータへ変換して表示するプログラムを紹介します。
ハードウェアもこの時に紹介した構成で動かすので、以前の記事を参照ください。
5_all_s.jpg
プログラムは前回と同様に Arduino IDE を使って作成やビルドを行うので、Arduino IDEとESP32をビルドできる環境を準備しておいてください。
arduino ide esp32-devkit」で検索すると環境の構築方法を説明したページが見つけられると思います。

文字列をドットデータへの変換はドットフォントのデータを用意し、文字コードからドットデータを変換する方法で実現します。
全てのプログラムやデータをここへ記すのは難しいため、CQ出版のInterface誌 2018年10月号「ESP実験コーナー」で作成したプログラムを流用します。

流用プログラムの取り出し

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

まず、Interface誌のダウンロードサイトから IF1810EE.zip をダウンロードします。
IF1810EE.zipを展開すると複数のディレクトリが出てきますが、matled64-2ディレクトリのプログラムを流用します。
matled64-2ディレクトリには4つのファイルがありますが、今回は以下の3つのファイルを流用します。これらのファイルを後で作成する matled_ai.inoと同じディレクトリへコピーしてください。
ちなみにドットフォントのサイズは全角文字が 横16ドット×縦16ドット半角文字が横8ドット×縦16ドット です。

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

表示プログラムの作成

今回、matled_ai.ino とういソースプログラムを作成します。
ベースは前回の記事「aitendoマトリクスLEDパネルの使い方(1)」の matled_ai2.ino をベースにしています。
matled_ai2.inoから追加・改造した箇所を簡単に説明します。
ソースプログラム matled_ai.ino 全体はこの記事の最後に全文を添付してあるので、以下の説明はソースプログラムを参照しながら読んでください。

プログラムの概要

今回のプログラムは、msg_init1、msg_init2、msg_init3というchar型のポインタ変数に設定された文字列をマトリクスLEDへ表示します。
表示は3秒間隔で3つの表示を切り替えながら繰り返し表示します。
表示色、表示文字は以下のようにしています。

変数名 表示色 表示文字
msg_init1 "赤色"
msg_init2 "緑色"
msg_init3 "橙色" or "Oran"
0_red_s.jpg 1_green_s.jpg
2_daidai_s.jpg 2_orange_s.jpg
データの持ち方や loop()関数内での表示の仕方を工夫すれば文字列をスライドさせて表示することも可能ですが、今回は全角2文字固定での表示としています。
流用元のInterface誌の記事では異なるLEDパネルですが、文字列をスライドさせて表示しているので、そちらの記事やプログラムを参考に改造してみてはいかがでしょうか。

表示データ

前回はマトリクスLEDへ表示したいデータを配列定数で定義していましたが、今回は文字列で定義します。
msg_init1、msg_init2、msg_init3へ定義している文字列の内容を変更すれば異なる文字を表示させることができます。
なお、プログラムで「"橙色"」の行をコメントアウトしていますが、これは次の行の「"Oran"」で半角文字が表示できることの確認のためです。
配列変数 dat_buffer1、dat_buffer2、dat_buffer3は3つの文字列をドットへ変換後のデータを保持するためにあります。
前回はこの配列が固定のデータとなっていました。

const char  *msg_init1 = "赤色";
const char  *msg_init2 = "緑色";
//const char  *msg_init3 = "橙色";
const char  *msg_init3 = "Oran";
  :
unsigned char  dat_buffer1[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];
unsigned char  dat_buffer2[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];
unsigned char  dat_buffer3[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];

フォントデータ

以下の変数や定義はフォントデータへアクセスするためのものです。

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

# define DEF_FONT_SIZE        16
# define DEF_FONT_A16_VAR     fx_8x16rk_fnt
# define DEF_FONT_K16_VAR     jiskan16_fnt

clear_message()

表示用の配列変数の内容をクリアするための関数です。
表示用のドットデータ作成時に呼び出されます。

set_font()

フォントデータアドレスから表示用配列変数へフォントデータをコピーするための関数です。

UTF8_To_SJIS_cnv()

文字コードを UTF8 から SJIS へ変換する関数です。
Arduino IDEでは文字コードは UTF8で保持されますが、今回利用するドットフォントデータは SJISで定義されているため、文字コードの変更が必要となります。

utf8_to_sjis()

UTF8の文字データのアドレスから SJISの文字コードを返す関数です。

get_fontx2_a()

半角文字用のドットフォントデータ群の先頭アドレスと SJISコードから、該当するフォント単体のデータのアドレスを返します。

get_fontx2_k()

全角文字用のドットフォントデータ群の先頭アドレスと SJISコードから、該当するフォント単体のデータのアドレスを返します。

make_message()

文字列から表示用のドットデータの変換を行います。
表示したい文字列が定義された配列の先頭アドレスと文字列のバイト数、出力先の変数配列の番号を指定します。
今回の場合、文字列としては msg_init1、msg_init2、msg_init3 のいずれかが指定されます。
出力先の変数配列の番号は 02が有効な番号になります。
また、ここで指定した番号で表示される色が決まります。番号と色の関係は以下になります。

番号 表示色
0
1
2

setup()

文字列から表示用のドットデータへ変換するための処理を追加しています。
以下の行で3つ分のデータが準備されます。

  led_msg_len = make_message( (unsigned char *)msg_init1, strlen(msg_init1), 0 );
  Serial.printf( "data width = %d\n", led_msg_len );
  led_msg_len = make_message( (unsigned char *)msg_init2, strlen(msg_init2), 1 );
  Serial.printf( "data width = %d\n", led_msg_len );
  led_msg_len = make_message( (unsigned char *)msg_init3, strlen(msg_init3), 2 );
  Serial.printf( "data width = %d\n", led_msg_len );

loop()

前回固定のデータとして用意した配列と同じ名前で表示用の配列変数へ改造を行ったので、loop()関数に変更はありません。

表示プログラム

表示プログラムの matled_ai.ino の全文を以下に記します。
表示する文字や順番、種類などの変更は容易と思います。いろいろ試してみてください。
文字のスライドに関しては表示用データの持ち方やデータ表示時の位置指定など工夫が必要になりますが、もともとはInterface誌で紹介したスライドする電光掲示板のプログラムからの改造なので、それほど難しくはないかもしれません。こちらもいろいろ試されてはいかがでしょうか。

matled_ai.ino
/*
 *  Copyright(C) by Yukiya Ishioka
 *
 *  http://www.aitendo.com/product/14111
 *  http://aitendo3.sakura.ne.jp/aitendo_data/product_img/LED/HD-0158-RG0019A/HD-0158-RG0019A.rar
 *
 *  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>

           /*  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 */

const char  *msg_init1 = "赤色";
const char  *msg_init2 = "緑色";
//const char  *msg_init3 = "橙色";
const char  *msg_init3 = "Oran";

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

# define DEF_FONT_SIZE        16
# define DEF_FONT_A16_VAR     fx_8x16rk_fnt
# define DEF_FONT_K16_VAR     jiskan16_fnt

# define DEF_DISP_WIDTH       32
# define DEF_DISP_WIDTH_S     0
# define DEF_OUTDATA_NUM      100
# define DEF_INCHAR_NUM       100

unsigned char  dat_buffer1[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];
unsigned char  dat_buffer2[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];
unsigned char  dat_buffer3[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ];

int  led_color;
int  led_msg_len ;


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

    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 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 ] = 1;
                }
                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 line )
{
    int  pos, bitpos;
    int  num;
    unsigned char  *fontdata;
    unsigned int  code;
    unsigned char  code1, code2 ;
    unsigned char  *dat_buffer ;

    switch( line ) {
    case 1:
      dat_buffer = (unsigned char *)dat_buffer2 ;
      break;
    case 2:
      dat_buffer = (unsigned char *)dat_buffer3 ;
      break;
    case 0:
    default:
      dat_buffer = (unsigned char *)dat_buffer1 ;
      break;
    }
    clear_message( (unsigned char *)dat_buffer );

    pos = 0 ;
    bitpos = DEF_DISP_WIDTH_S ;
    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, 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, 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;

  /* init serial */
  Serial.begin(115200);
  Serial.println( "call setup()" );

  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 );

  led_color = 0;
  led_msg_len = make_message( (unsigned char *)msg_init1, strlen(msg_init1), 0 );
  Serial.printf( "data width = %d\n", led_msg_len );
  led_msg_len = make_message( (unsigned char *)msg_init2, strlen(msg_init2), 1 );
  Serial.printf( "data width = %d\n", led_msg_len );
  led_msg_len = make_message( (unsigned char *)msg_init3, strlen(msg_init3), 2 );
  Serial.printf( "data width = %d\n", led_msg_len );
}


void loop( void )
{
  int  a0, a1, a2, a3;
  int  dr, dg;
  int  row, col;
  int  i;
  unsigned char  tmpchar;
  unsigned char  *buff1;
 
  switch( led_color%3 ) {
  case 1:
    dr = 0;
    dg = 1;
    buff1 = (unsigned char *)dat_buffer2 ;
    break;
  case 2:
    dr = 1;
    dg = 1;
    buff1 = (unsigned char *)dat_buffer3 ;
    break;
  case 0:
  default:
    dr = 1;
    dg = 0;
    buff1 = (unsigned char *)dat_buffer1 ;
    break;
  }
  Serial.printf( "color DR=%d  DG=%d\n", dr, dg );

  for( i=0 ; i<16 ; i++ ) {
    row  = DEF_OUTDATA_NUM * i;

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

      if( buff1[ row + col ] ) {
          digitalWrite( DRPIN, dr );
          digitalWrite( DGPIN, dg );
      } else {
          digitalWrite( DRPIN, LOW );
          digitalWrite( DGPIN, LOW );
      }

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

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

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

  led_color++;
  vTaskDelay( 3000 );  /* delay 3000msec */
}

以上

3
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?