はじめに
共立エレショップで販売販売されている「32×16ドットマトリクスLEDパネル」を使ったプログラムを紹介して欲しいとの依頼があり、「aitendoマトリクスLEDパネルの使い方(1)」という記事を投稿しました。
この時はESP32-DevKitCを使ってプログラム内に表示したいドットデータを配列定数で用意して表示を行っていましたが、今回はこの時のプログラムを改造して文字列をドットデータへ変換して表示するプログラムを紹介します。
ハードウェアもこの時に紹介した構成で動かすので、以前の記事を参照ください。
プログラムは前回と同様に Arduino IDE を使って作成やビルドを行うので、Arduino IDEとESP32をビルドできる環境を準備しておいてください。
「arduino ide esp32-devkit」で検索すると環境の構築方法を説明したページが見つけられると思います。
文字列をドットデータへの変換はドットフォントのデータを用意し、文字コードからドットデータを変換する方法で実現します。
全てのプログラムやデータをここへ記すのは難しいため、CQ出版のInterface誌 2018年10月号「ESP実験コーナー」で作成したプログラムを流用します。
-
aitendoマトリクスLEDパネルの使い方(1)
https://qiita.com/Yukiya_Ishioka/items/fd791498bb5c02244546 -
Interface誌 2018年10月号
https://interface.cqpub.co.jp/magazine/201810/ -
「ESP実験コーナー」
https://interface.cqpub.co.jp/wp-content/uploads/if10_156.pdf -
共立エレショップの商品ページ
32×16ドットマトリクスLEDパネル(赤/橙/緑) 32x16DOT-0158-DJK
https://eleshop.jp/shop/g/gEB8411/
流用プログラムの取り出し
文字コードからドットフォントへの変換プログラムは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 |
-
Interface誌のダウンロードサイト「2018年」
https://www.cqpub.co.jp/interface/download/contents2018.htm -
10月号 特集 IT農耕実験」連載 定番IoTマイコンESP実験コーナ〈第1回〉
IF1810EE.zip
https://www.cqpub.co.jp/interface/download/2018/10/IF1810EE.zip
表示プログラムの作成
今回、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つの表示を切り替えながら繰り返し表示します。
表示色、表示文字は以下のようにしています。
表示データ
前回はマトリクス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 のいずれかが指定されます。
出力先の変数配列の番号は 0~2が有効な番号になります。
また、ここで指定した番号で表示される色が決まります。番号と色の関係は以下になります。
番号 | 表示色 |
---|---|
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誌で紹介したスライドする電光掲示板のプログラムからの改造なので、それほど難しくはないかもしれません。こちらもいろいろ試されてはいかがでしょうか。
/*
* 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 */
}
以上