はじめに
共立エレショップで販売販売されている「32×16ドットマトリクスLEDパネル」を使ったプログラムを紹介して欲しいとの依頼があり、「aitendoマトリクスLEDパネルの使い方(1)」、「aitendoマトリクスLEDパネルの使い方(2)」という記事を投稿していて、今回は3つめの記事になります。
なお、前回まではタイトルに aitendoと付けていましたが、現在、aitendoでは販売されていないので、「マトリクスLEDパネルの使い方(3)」とシンプルにしました。
今回紹介するプログラムはこのマトリクスLEDを使ってプログラム内に定義した文字列をスライドさせながら表示します。色は文字単位で 赤、緑、橙の3色を指定できます。文字列をドットデータへ変換するため、前回と同様にCQ出版のInterface誌 2018年10月号「ESP実験コーナー」で作成したプログラムを流用します。
ハードウェアは今までと同様に ESP32-DevKitCを使って制御します。「32×16ドットマトリクスLEDパネル」の「ハードウェア」の項で説明しているので、そちらを参照してください。
また、Arduino IDE を使って作成やビルドを行うので、Arduino IDEとESP32をビルドできる環境を準備しておいてください。「arduino ide esp32-devkit」で検索すると環境の構築方法を説明したページが見つけられると思います。
今回のプログラムは表示に動きがあるため、プログラムの動作結果の動画を youtube へアップしたので、こちらも併せてご覧ください。
youtube「ESP32を使った電光掲示板」
https://youtu.be/TKK3CAs6z4o
その他のリンク情報を以下に記しておきます。
-
共立エレショップの商品ページ
32×16ドットマトリクスLEDパネル(赤/橙/緑) 32x16DOT-0158-DJK
https://eleshop.jp/shop/g/gEB8411/ -
aitendoマトリクスLEDパネルの使い方(1)
https://qiita.com/Yukiya_Ishioka/items/fd791498bb5c02244546 -
aitendoマトリクスLEDパネルの使い方(2)
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誌のダウンロードサイトから 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
プログラムの概要
表示用バッファとしてunsigned char型の2次元配列 **dat_buffer[ DEF_FONT_SIZE ][ DEF_OUTDATA_NUM ]**を準備して、ここに文字列からドットフォントへ変換したデータを配置し、マトリクスLEDへ表示を行います。配列の **[ DEF_FONT_SIZE ]が縦方向、[ DEF_OUTDATA_NUM ]**が横方向の並びになります。
配列へ入れるデータは 0~3 で以下のように表示させたい色に該当する値を設定します。
番号 | 表示色 |
---|---|
0 | 黒(消灯) |
1 | 赤 |
2 | 緑 |
3 | 橙 |
表示用バッファと実際の表示との関係は以下のようになります。
1)表示用バッファの先頭から横32ドットは常に消灯
※1部分ですが、これは表示開始時にマトリクスLED上にいきなり文字を出すと先頭部分が読みにくいことと、見てる側を少しビックリさせてしまうと考えたからです。このため最初は消灯状態で右端から1ドットずつ表示か開始されるようにするためです。
2)表示用データは横方向の33ドット目から配置
プログラム内で設定した表示したい文字列は文字コードからドットフォントへ変換され33ドット目から配置していきます。文字列が長くて表示用バッファに入りきらない場合、文字単位で表示用バッファへの書き込みを切ります。
3)表示データの終端横32ドットは常に消灯
※2部分ですが、先頭同様に表示の終端部分も32ドット分は常に消灯します。これは最後の文字が右から左へスライドして左端からすべてが消えるまで表示処理を行うためです。
4)文字列のスライド
文字列が右から左へスライドされるように表示するため、表示のタイミングで表示バッファの先頭から1ドットずつ右方向へずらしながら表示しています。
表示タイミングは loop()関数の最後にある delay()関数で指定する時間で早くしたり遅くしたりします。
表示用データの最後の列が左端へ消えたら、再び表示用バッファの先頭へ戻してスライド表示を繰り返します。
プログラムの説明
プログラムの多くの部分は前回の「aitendoマトリクスLEDパネルの使い方(2)」からの流用なので、これからの説明は今回用に作成や修正をした内容を説明します。
ボタン操作
ESP32-DevKitCにはオンボードでプログラムからオン/オフを知ることができるボタンが付いています。
今回はこのボタンを使って表示のスライドスピードを変更する機能をつけました。このためボタンの状態を取得するための定義や処理を追加しています。
# define BTNPIN 0
:
pinMode(BTNPIN, INPUT);
:
if( led_speed_flg != 0 && digitalRead( BTNPIN ) == LOW ) {
:
if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
表示データ
マトリクスLED上に表示したい文字列を以下の部分で設定します。
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, "赤色" },
単語や文字間の空白(スペース)は、今回のプログラムでは**※3のように「黒」で明示的に記述していますが、空白自体は点灯するデータは含まれず色の指定は意味をもたないため、前の文字列の終端部分※4**、もしくは次の文字列の先頭部分**※5**に含めても構いません。
{ 1, "赤色" },
{ 0, " " }, ←※3
{ 2, "緑色" },
{ 1, "赤色 " }, ←※4
{ 2, "緑色" },
{ 1, "赤色" },
{ 2, " 緑色" }, ←※5
スライドスピード
表示のスライドスピードはloop()関数の最後にある delay()関数で調整するようになっていますが、このdelay()関数へ設定するための定義が以下になります。
配列 led_speed_tbl[] に定義されている値は delay()関数へ与える待ち時間のミリ秒です。
# 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()の呼び出し1回で1つ分の表示データを作成していましたが、今回は複数の文字列を結合して表示用データとするため、引数 bitposを追加して表示用バッファの横方向のどの位置から文字列を書き込むかを指定できるようにしました。
また、黒も指定できるようにするため、引数を明確に colorとし、値も 0~3で指定できるようにしました。
setup()
文字列から表示用のドットデータへ変換する処理を、構造体配列 msg_textに設定されている複数の表示色と文字列の組で表示用バッファへデータを展開する処理へ変更しています。
また、スライドスピードの変数の初期化する処理を追加しています。
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()関数が呼び出されるごとに表示用データがスライドするよう、表示用バッファのどの位置から表示を行うかの表示処理の変更と、表示位置変数の更新処理を追加しています。
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 );
}
led_msg_pos++;
if( led_msg_pos >= led_msg_len ) {
led_msg_pos = 0;
}
また、ボタンを押下することでスライドスピードが変化するよう、ボタンオン/オフのチェック処理とボタンが押下された場合にスライドスピードを配列 **led_speed_tbl[]**から取得する処理を追加しています。
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 */
なお、以下の処理はボタンを押しっぱなしにしたときに連続してスライドスピードが変化しないよう、ボタンが離された(オフされた)ことを検知するための処理です。
if( led_speed_flg == 0 && digitalRead( BTNPIN ) == HIGH ) {
led_speed_flg = 1;
}
表示プログラム
表示プログラムの matled_a3i.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 */
}
以上