/****************************************/ /* MP3プレーヤー MK-138を制御 */ /****************************************/ // Arduino 1.6.5でコンパイル,確認 // MK-138仕様 // シリアルモード 4800BPS 8bit PN // コマンドは16進で1バイト // 再生ファイルはルートフォルダに199 // サブフォルダ名02~15に各199ファイル // 合計15x199=2985ファイルを再生 // 0x01~0xC7 MP3ファイル再生(001~199) // 0xC8~0xE7 音量指定 VR(0~31) 200~231 // 0xEB 一時停止 // 0xED 再生 // 0xEF 停止 // 0xF1 フォルダ1(ルート)指定 // 0xF2~0xFF フォルダ02~15指定 // リセット起動時、新たな乱数の種をセット // 2018-02-24 開始 // 26 再生中断、開始時のプチ音対策 // 27 フォルダ(ディレクトリ)を設定できるように // 28 スタンバイ継続でバックライト消灯 // 03-01 停止コマンドとVR↓送出処理 // 02 乱数を使わない順再生のジャンパを追加。 // SDメモリーカードのファイル構造でうまく再生 // 出来ない時がある。 // 1フォルダ当たりのファイル数が怪しい。 // 仕様では199。123以降アウトな場合が出てきた。 // アウトな時は、ファイルが見つからないとして、 // はファイル番号01を再生する。 // 全ファイル数指定のジャンパを追加 // オフなら指定フォルダ(DIR)のファイルを再生 // onにすると、全ファイルを再生する。 // ただし、1フォルダ内のファイル数は仕様の199ではなく // 100に。 (多くすると、どうも不安定) // 曲番号は 1から設定値(max)まで。 // DIRは02から15まで。 FILEは001~100まで。 // DIR = 02 + (曲番号-1) / 100 // FILE = 01 + (曲番号-1) % 100 // ピン割り当て // PD0 RXD in - (pull up) // PD1 TXD out データ送信 // PD2 IO2 in BUSY(再生中 L)入力 // PD3 IO3 in befor SW (設定+1) // PD4 IO4 in next SW (長押し:設定) // PD5 IO5 in 再生on/off SW // PD6 IO6 out - // PD7 IO7 out - // PB0 IO8 out LCD RS // PB1 IO9 out LCD E // PB2 IO10 out LCD D4 // PB3 IO11 out LCD D5 // PB4 IO12 out LCD D6 // PB5 IO13 out LCD D7 // PC0 AD0 out 再生中LED Lで点灯 // PC1 AD1 out バックライトLED Hで点灯 // PC2 AD2 out - // PC3 AD3 in JP3 Lで全ファイル一括再生 // PC4 AD4 in JP2 Lで乱数再生しない // PC5 AD5 in JP1 Lで10秒再生 /***** ライブラリ ****/ #include #include #include #include #include #include "wiring_private.h" // sbi,cbi命令用 // http://www.arduino.cc/en/Tutorial/LiquidCrystal #include // 8文字×2行液晶を4bitモードで使用 /***** タイトル *****/ const char pgm_ttl1[] PROGMEM = "MK138ctrl"; const char pgm_ttl2[] PROGMEM = "18-03-02"; // 01234567 /***** 液晶ピン指定 *****/ #define LCD_RS 8 // デジタルピン指定 #define LCD_E 9 // 4bitモードで使用 #define LCD_D4 10 #define LCD_D5 11 #define LCD_D6 12 #define LCD_D7 13 // initialize the library with the numbers of the interface pins LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); /***** 配列のデータ数を返すマクロ *****/ #define DIMSIZ(a) (sizeof(a)/sizeof(*a)) typedef unsigned long ulong; // unsigned longの略 /***** マクロ *****/ #define tx(c) (Serial.write((char)c)) // 1バイト送信 /***** ピン指定 *****/ // MP3再生中入力 (busyで1) #define INP_BUSY ((~PIND) & (1 << PORTD2)) // busy入力 再生中:L、待機中:H // 再生中LED点灯 busy入力に従う #define LON_PLAY (cbi(PORTC,PORTC0)) // PC0 L #define LOFF_PLAY (sbi(PORTC,PORTC0)) // PC0 H // バックライトLED制御 #define LON_BKLT (sbi(PORTC,PORTC1)) // PC1 H #define LOFF_BKLT (cbi(PORTC,PORTC1)) // PC0 L // 10秒再生ジャンパー(onで1) #define JP_10SEC ((~PINC) & (1 << PORTC5)) // JP1 Lでオン // 乱数再生しないジャンパー(onで1) #define JP_NORAND ((~PINC) & (1 << PORTC4)) // JP2 Lでオン // ファイル一括再生 #define JP_ALLPLAY ((~PINC) & (1 << PORTC3)) // JP3 Lでオン // SW入力 on,offチェック(onで1) #define INP_SWBFOR ((~PIND) & (1 << PORTD3)) // befor SW #define INP_SWNEXT ((~PIND) & (1 << PORTD4)) // next SW #define INP_SWPLAY ((~PIND) & (1 << PORTD5)) // on/off SW // SWコード (0でオフ) #define SW_BFOR 1 // PD3 前曲に 設定:+1 #define SW_NEXT 2 // PD4 次曲に 設定:次桁 #define SW_SET 3 // 設定(長押し) // テストピン #define PD6_H (sbi(PORTD,PORTD6)) // (!!!) PD6 12pin #define PD6_L (cbi(PORTD,PORTD6)) // #define PD7_H (sbi(PORTD,PORTD7)) // (!!!) PD7 13pin #define PD7_L (cbi(PORTD,PORTD7)) // /***** データ指定 *****/ #define MAX_FILE 999 // 1dir内の最大ファイル数 // フォルダーは02~15 // 全体では2985ファイルだが // ルートフォルダ01は含まないので // 2786曲となる。 // JP_ALLPLAYの時は1フォルダ100ファイル #define TM_BKOFF 15 // スタンバイでSW操作しない時の // バックライトオフまでの時間(秒) // PLAY中はバックライトLEDはオン /***** EEPROMデータ *****/ // ATmega328は1024バイト // 参考:https://projectgus.com/2010/07/eeprom-access-with-arduino/ // http://forum.arduino.cc/index.php?topic=128816.0 #define eeprom_read_to(dst_p, eeprom_field, dst_size) eeprom_read_block(dst_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(dst_size, sizeof((__eeprom_data*)0)->eeprom_field)) #define eeprom_read(dst, eeprom_field) eeprom_read_to(&dst, eeprom_field, sizeof(dst)) #define eeprom_write_from(src_p, eeprom_field, src_size) eeprom_write_block(src_p, (void *)offsetof(__eeprom_data, eeprom_field), MIN(src_size, sizeof((__eeprom_data*)0)->eeprom_field)) #define eeprom_write(src, eeprom_field) { typeof(src) x = src; eeprom_write_from(&x, eeprom_field, sizeof(x)); } #define MIN(x,y) ( x > y ? y : x ) // EEPROM保存データ struct __eeprom_data { word ep_mgcnbr; // 0 magic number 0x1234(初回起動チェック) long ep_randseed; // 2 乱数発生の種 word ep_filemax; // 6 設定した最大ファイル数 word ep_dirnbr; // 8 DIR 番号 2~15 }; #define MGCNBR 0x1234 // EEPROM初期化のmagic number // この値以外は初期化 /***** データ *****/ word file_max; // 設定した最大ファイル数 // ep_filemaxをリード // NEXT SW長押しで設定 word dir_nbr; // DIR番号 2~15 // ep_dirnbrをリード word play_file; // 再生ファイル番号 word play_cnt; // 再生ファイル数 // シャッフル用データ byte play_flag[((MAX_FILE | 7) + 1) / 8]; // シャッフル再生用フラグ // 前曲保持 word play_bfor[20]; // 20曲分の前回再生ファイル番号 word playbfor_p; // 前回再生記録用ポインタ /******************************/ /* スイッチ入力 */ /******************************/ // SWスキャンデータ SWは3コ #define SWSU 3 // 物理的SWの数 #define SW_BFOR 1 // PD3 前曲に 設定:+1 #define SW_NEXT 2 // PD4 次曲に 設定:次桁 #define SW_SET 3 // 設定(長押し) #define SW_PLAY 4 // PD5 再生開始 // PD3 SW_BFOR : 前曲に 設定:+1 // PD4 SW_NEXT : 次曲に 設定:次桁 // SW_SET : 長押しで設定 // PD5 SW_PLAY : 再生開始 // bit0,1,2にデータが入る byte sw_chk[10]; // onエッジチェックデータ byte sw_inp; // SWのon/off状態 byte sw_on; // onエッジ 1でon // 長押しスイッチの有無 byte sw_longp = 0b00000010; // 長押しSW位置 // ||+--- PD3 // |+---- PD4 // +----- PD5 byte sw_tmlongp[SWSU]; // 長押し検出タイマー // 10mSでダウンカウント byte sw_flongp; // 長押し検出フラグ // SWビット位置が1で処理中 // オートリピート処理の有無 // 長押しチェックとは相反するので同時指定できない byte sw_rept = 0b00000001; // リピートSW位置 // ||+--- PD3 // |+---- PD4 // +----- PD5 byte sw_tmrept[SWSU]; // リピート検出タイマー // 10mSでダウンカウント byte sw_frept; // リピート検出フラグ // SWビット位置が1で処理中 // スイッチ コード データ struct st_swcode{ const byte sht PROGMEM; // ちょん押し(リピートSW) const byte lng PROGMEM; // 長押し }; const st_swcode sw_code[] PROGMEM = { SW_BFOR, SW_BFOR, // 0:前曲に SW_NEXT, SW_SET, // 1:次曲に 設定 SW_PLAY, SW_PLAY, // 2:再生on/off }; /***** スイッチ入力 *****/ // PD3,PD4,PD5 onでL byte swinp(void) { byte d = 0; // SW on/off if(INP_SWBFOR) d |= 0b00000001; // 0:前曲 if(INP_SWNEXT) d |= 0b00000010; // 1:次曲 if(INP_SWPLAY) d |= 0b00000100; // 2:on/off return d; } /***** スイッチ入力スキャン *****/ // 1mSサイクルでチェック, onエッジを見つける // タイマー割り込み内で処理 void swscan(void) { static byte p = 0; // 最新入力書き込み位置 byte i, m, d0, d1; sw_chk[p] = swinp(); // 最新SWデータ Hでon d0 = 0x00; // Lチェック d1 = 0xFF; // Hチェック (1bit) for(i = 0; i < DIMSIZ(sw_chk); i++){ // H,L安定確認 d0 |= sw_chk[i]; d1 &= sw_chk[i]; } m = (~d0) | d1; // 1のところが安定データ d0 = sw_inp; // 1ms前の状態 sw_inp = (d0 & (~m)) | (sw_chk[p] & m); // 確定 sw_on |= (~d0) & sw_inp; // onエッジをチェック p++; // 次書き込み位置 if(p >= DIMSIZ(sw_chk)) p = 0; // ポインタ一周 } /***** SW 長押しチェック *****/ // ちょん押しと長押し(1.5秒)を検出 // n : スイッチビット番号 0~7 // ret : 0=onスイッチ無し 1~SWコード byte swlongp(byte n) { byte r = 0; // リターンコード byte m; m = 1 << n; // ビット位置 if((sw_longp & m) != 0){ // 長押しSW ? if(sw_flongp & m){ // 長押し処理中? if(sw_tmlongp[n] == 0){ // 長押しタイマー=0? sw_flongp &= ~m; // 処理中フラグクリア r = pgm_read_byte(&sw_code[n].lng); // 長押しSW } else{ // タイマー計時中 if(!(sw_inp & m)){ // offしたらちょん押し sw_flongp &= ~m; // 処理中フラグクリア r = pgm_read_byte(&sw_code[n].sht); // 短押しSW } } } else{ // 初めてのonをチェック if(sw_on & m){ sw_on &= ~m; // onフラグクリア sw_tmlongp[n] = 150; // 長押しタイマー 1.5秒 sw_flongp |= m; // 処理中フラグon } } } return r; } /***** SW オートリピートチェック *****/ // 0.6秒待ちで0.2秒サイクル // 長押しチェックとは相反するので同時指定できない // n : スイッチビット番号 0~7 // ret : 0=onスイッチ無し 1~SWコード byte swrept(byte n) { byte r = 0; // リターンコード byte m; m = 1 << n; // ビット位置 if((sw_rept & m) != 0){ // リピートするSW ? if(sw_frept & m){ // リピート処理中? if(!(sw_inp & m)){ // offしたらおわり sw_frept &= ~m; // 処理中フラグクリア } else{ // on継続 if(sw_tmrept[n] == 0){ // リピートタイマー=0? sw_frept &= ~m; // 処理中フラグクリア sw_tmrept[n] = 20; // 次タイマー0.2秒セット r = pgm_read_byte(&sw_code[n].sht); // 短押しSWあり } } } else{ // 初めてのonをチェック if(sw_on & m){ sw_on &= ~m; // onフラグクリア sw_tmrept[n] = 60; // 処理中リピートタイマー 0.6秒 sw_frept |= m; // 処理中フラグon r = pgm_read_byte(&sw_code[n].sht); // 短押しSWあり } } } return r; } /***** SW on をチェック *****/ // オフなら0、オンならswコードを返す // sw_onのbit 1,0をチェック byte swonchk(void) { byte m, i; byte r = 0; // 0:SW onなし for(i = 0; i < SWSU ;i++){ // SWは3つ r = swlongp(i); // 長押しチェック if(r) break; // 有効SWあり r = swrept(i); // リピートチェック if(r) break; // 有効SWあり m = 1 << i; // bit0から if(sw_on & m){ // SW on? sw_on &= ~m; r = pgm_read_byte(&sw_code[i].sht); // 短押しSW break; } } return r; // SWコード } /***** SW入力バッファ用データ *****/ byte sw_data[10]; // 有効なスイッチデータ byte sw_dcnt; // データ数 byte sw_dwp; // 書き込み位置 byte sw_drp; // 読み出し位置 /***** SWデータをバッファリング *****/ // swonchkを10msサイクルで機動 void swonwr(void) { byte i, sw; for(i = 0; i < SWSU; i++){ if(sw_tmlongp[i]) sw_tmlongp[i]--; // 長押し検出タイマー if(sw_tmrept[i]) sw_tmrept[i]--; // オートリピートタイマー } sw = swonchk(); // SW onチェック SWコードが返る if(sw){ // 0は入力無し if(sw_dcnt < DIMSIZ(sw_data)){ // バッファに空きがなければ捨てる sw_data[sw_dwp] = sw; sw_dcnt++; // データ数+1 sw_dwp++; // 書き込みポインタ if(sw_dwp >= DIMSIZ(sw_data)) sw_dwp = 0; } } } /***** SWデータの有無をチェック *****/ // バッファのデータ数を返す 0ならデータ無し byte swdchk(void) { return sw_dcnt; } /***** SWデータをバッファから読み出し *****/ // SWコードを返す 0ならデータ無し byte swdrd(void) { byte r = 0; if(sw_dcnt != 0){ // SWデータあり? r = sw_data[sw_drp]; // バッファから sw_dcnt--; // データ数-1 sw_drp++; // 読み出しポインタ if(sw_drp >= DIMSIZ(sw_data)) sw_drp = 0; } return r; } /***** スイッチデータをクリア *****/ void swdclr(void) { sw_dcnt = // データ数 sw_dwp = // 書き込み位置 sw_drp = 0; // 読み出し位置 } /******************************/ /* 1ms タイマー割り込み */ /******************************/ /***** タイマーデータ *****/ volatile byte f_1ms; // 1msフラグ volatile byte tm_1ms; // 1msタイマー volatile byte f_10ms; // 10msフラグ volatile byte tm_10ms; // 10ms計時用ダウンカウント volatile byte tm_1scnt; // 1秒計数 volatile byte tm_1sec; // 1秒ダウンカウント volatile byte f_blink; // 点滅表示タイミングフラグ // 1秒に2回オン volatile byte f_blank; // 点滅表示 表示/ブランクフラグ // 1:ブランクに /***** 割り込み実行 *****/ void int1ms(void) { static byte cnt10 = 0; static byte cntbl = 0; // PD7_H; // (!!!) 13pin f_1ms = 1; // 1ms経過 if(tm_1ms) tm_1ms--; // 1msダウンカウント // スイッチ入力スキャン swscan(); // SW onエッジを見つける // 10msタイマー cnt10++; if(cnt10 >= 10){ cnt10 = 0; f_10ms = 1; // 10ms経過 if(tm_10ms) tm_10ms--; // 10mS ダウンカウント // 点滅処理 cntbl++; // 点滅フラグ+10ms if(f_blank){ // ブランク中 if(cntbl >= 20){ // 0.2秒 f_blink = 1; // 表示タイミング f_blank = 0; // 表示に cntbl = 0; } } else{ if(cntbl >= 80){ // 0.8秒 f_blink = 1; // 表示タイミング f_blank = 1; // ブランクに cntbl = 0; } } // 1secタイマー tm_1scnt++; if(tm_1scnt >= 100){ // 1秒? tm_1scnt = 0; if(tm_1sec) tm_1sec--; // 1S ダウンカウント } } // PD7_L; // (!!!) 13pin } /**************************/ /* 液晶表示 */ /**************************/ /***** 書式付液晶表示 *****/ void lcdprintf(const char *s, ...) { va_list vp; char bff[40]; // バッファを確保 va_start(vp, s); vsprintf(bff, s, vp); lcd.print(bff); va_end(vp); } /***** メッセージ出力 *****/ void lcdputs_P(PGM_P s) { char c; while(1){ c = pgm_read_byte(s); if(c == '\x0') break; lcd.write(c); s++; } } /******************************/ /* セットアップ */ /******************************/ /***** セットアップ *****/ // ATmega328Pのレジスタを直接制御 void setup() { // I/Oイニシャル PORTB = 0b00000000; // out data, pull up DDRB = 0b00111111; // portI/O // |||||+---- PB0 IO8 out LCD RS // ||||+----- PB1 IO9 out LCD E // |||+------ PB2 IO10 out LCD D4 // ||+------- PB3 IO11 out LCD D5 // |+-------- PB4 IO12 out LCD D6 // +--------- PB5 IO13 out LCD D7 PORTC = 0b00111011; // out data, pull up DDRC = 0b00000111; // portI/O // |||||+---- PC0 AD0 out 再生中LED Lでon // ||||+----- PC1 AD1 out バックライトLED Hでon // |||+------ PC2 AD2 out - // ||+------- PC3 AD3 in JP3 曲全再生ジャンパ // |+-------- PC4 AD4 in JP2 乱数再生無しジャンパ // +--------- PC5 AD5 on JP1 10秒再生ジャンパ PORTD = 0b00111111; // out data, pull up DDRD = 0b11000010; // portI/O // |||||||+---- PD0 RXD in // ||||||+----- PD1 TXD out データ送信 // |||||+------ PD2 IO2 in MP3 BUSY // ||||+------- PD3 IO3 in SW1 前曲 // |||+-------- PD4 IO4 in SW2 次曲 // ||+--------- PD5 IO5 in SW3 on/off // |+---------- PD6 IO6 out (テストパルス) // +----------- PD7 IO7 out - // シリアル Serial.begin(4800); // シリアル通信 // タイマー割り込み MsTimer2::set(1, int1ms); // 1ms割り込み MsTimer2::start(); // 割り込み処理開始 // LCD lcd.begin(16, 2); // 16文字×2行 } /*****************************************/ /* 制御用下請けルーチン */ /*****************************************/ /***** MP3 再生コマンドデータ *****/ // 20msサイクルで送出 byte txmp3_data[32]; // XPLAY処理で設定 // 停止コマンド+フォルダ+ファイル番号 byte txmp3_cnt; // データ送出カウンタ byte txmp3_wrp; // r/wポインタ byte txmp3_rdp; byte txmp3_tm; // 1msでダウンカウント const byte mp3_vr[] PROGMEM = { // VR u/d データ 0xC8,0xCC,0xD0,0xD4,0xD8,0xDC,0xE0,0xE4,0xE7, // 0→31(max) }; /***** MP3制御データ送出 *****/ // 1msごとに起動 // 20mSサイクルでシリアル出力 void txmp3data(void) { if(txmp3_tm) txmp3_tm--; // MP3データ送出タイミング if(txmp3_cnt){ // 送出データあり? if(txmp3_tm == 0){ // 20ms経過 tx(txmp3_data[txmp3_rdp]); // 4800BPSで送信 txmp3_tm = 20; // 20msセット txmp3_cnt--; // データ数-1 txmp3_rdp++; // 読み出しポインタ進める if(txmp3_rdp >= DIMSIZ(txmp3_data)) txmp3_rdp = 0; } } } /***** MP3制御データ書き込み *****/ // txmp3_dataリングバッファに書き込み void txmp3wr(byte c) { if(txmp3_cnt < DIMSIZ(txmp3_data)){ // バッファに空きがある時だけ txmp3_data[txmp3_wrp] = c; txmp3_cnt++; // データ数+1 txmp3_wrp++; // 書き込みポインタ進める if(txmp3_wrp >= DIMSIZ(txmp3_data)) txmp3_wrp = 0; } } /***** MP3 VR up *****/ // 音量 徐々に大きく // 20msサイクルで送られる void mp3vrup(void) { int i; for(i = 0; i < (int)DIMSIZ(mp3_vr); i++){ txmp3wr(pgm_read_byte(&mp3_vr[i])); // VRデータ } } /***** MP3 VR down *****/ // 音量 徐々に小さく void mp3vrdn(void) { int i; for(i = (int)DIMSIZ(mp3_vr) - 1; i >= 0; i--){ txmp3wr(pgm_read_byte(&mp3_vr[i])); // VRデータ } } /***** MP3再生を停止 *****/ // 再生中のみ停止コマンドを void mp3stop(void) { mp3vrdn(); // 音量を最低に if(INP_BUSY){ // 再生中? // mp3vrdn(); // 音量を最低に txmp3wr(0xEF); // 停止コマンド } } /***** DIR番号、最大曲数設定用データ *****/ byte smax_pos; // 入力位置 // 0:DIR十桁 1:D一桁 // 2:File百桁 3:F十桁 4:F一桁 word smax_num[5]; // Dir 10,1 File 100,10,1 設定数値 struct st_smax{ word *num; // 入力データ 0~9 byte cur; // 表示カーソル位置 }; st_smax smax_data[] = { &smax_num[0], 1, // Dir 十桁 &smax_num[1], 2, // 一桁 &smax_num[2], 5, // File 百桁 &smax_num[3], 6, // 十桁 &smax_num[4], 7, // 一桁 }; /***** 再生ファイル表示 *****/ void disppfile(void) { lcd.clear(); lcd.setCursor(0, 0); // 1行目 lcdprintf("%3d %3d", play_file, // 再生ファイル番号 play_cnt); // 再生したファイル数 lcd.setCursor(0, 1); // 2行目 lcdprintf("d%02d f%03d", dir_nbr, file_max); // DIR番号と設定した最大ファイル数 } /***** 最大ファイル設定表示 *****/ void dispsmax(void) { lcd.setCursor(0, 0); // 1行目 lcd.print(F("Dir File")); lcd.setCursor(0, 1); // 2行目 lcdprintf(" %d%d %d%d%d", smax_num[0], // D 十桁 smax_num[1], // 一桁 smax_num[2], // F 百桁 smax_num[3], // 十桁 smax_num[4]); // 一桁 } /*****************************************/ /* 制御実行処理 */ /*****************************************/ /***** 制御実行区分 *****/ byte exc_step; // 実行区分 #define X_STBY 4 // スタンバイ #define X_NEW 6 // 新再生パターン #define X_RAND 7 // 乱数を発生してファイルを決定 #define X_PLAY 8 // 再生開始 #define X_BFOR 11 // 前曲 #define X_NEXT 12 // 次曲 #define X_SETMAX 13 // 最大曲数セット // MK138起動時間待ち byte wait_mk138; // 1秒ごとに[.]表示 // Wait.... ........ 12コ /***** タイトル表示 *****/ // 装置名,バージョン表示 void xttl1(void) { word d1, d2, d3; LON_BKLT; // バックライトon // タイトル lcd.setCursor(0, 0); // タイトル lcdputs_P(pgm_ttl1); lcd.setCursor(0, 1); lcdputs_P(pgm_ttl2); // EEPROMチェック eeprom_read(d1, ep_mgcnbr); // ダミーをリード eeprom_read(d2, ep_filemax); // 最大ファイル数 eeprom_read(d3, ep_dirnbr); // DIR番号 if((d1 != MGCNBR) || // チェックデータ以外は初期化 (d2 > MAX_FILE) || (d3 < 2) || (d3 > 15)){ // データ数異常 d1 = MGCNBR; eeprom_write(MGCNBR, ep_mgcnbr); // チェックデータ eeprom_write(0, ep_filemax); // ファイル数 eeprom_write(0, ep_dirnbr); // DIR番号 } // EEPROMデータ読み出し eeprom_read(file_max, ep_filemax); // ファイル数 eeprom_read(dir_nbr, ep_dirnbr); // DIR番号 tm_10ms = 200; // 2.0秒 exc_step++; // 次exc } /***** タイトル表示 #2 *****/ // 乱数値を表示して新乱数をセット void xttl2(void) { long d1, d2; if(tm_10ms == 0){ // タイムアップ // mp3stop(); // MP3再生停止 // 乱数の種 eeprom_read(d1, ep_randseed); // 乱数の種 randomSeed(d1); d2 = random(0x10000); // 新乱数 16bit eeprom_write(d2, ep_randseed); lcd.clear(); lcd.setCursor(0, 0); lcdprintf("Rnd %04X" ,(word)d1); // 旧乱数 lcd.setCursor(0, 1); lcdprintf("new %04X" ,(word)d2); // 新乱数 tm_10ms = 200; // 2.0秒 exc_step++; // 次exc } } /***** タイトル表示 #3 *****/ // 乱数表示完了でMK138起動時間待ち void xttl3(void) { if(tm_10ms == 0){ lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("Wait")); tm_10ms = 65; // 0.65秒 exc_step++; // 次exc } } /***** タイトル表示 #4 *****/ // MK138の安定時間待ち [.]を12コ 7.8秒待ち void xttl4(void) { byte x, y; if(f_blink){ // 表示タイミング f_blink = 0; lcd.setCursor(0, 0); if(f_blank) lcd.print(F(" ")); else lcd.print(F("Wait")); } // 0.8秒ごとに[.]表示 if(tm_10ms == 0){ tm_10ms = 65; // 0.65秒 if(wait_mk138 < 4){ // 0~3 1行目 x = 4 + wait_mk138; y = 0; } else{ // 4~11 2行目 x = wait_mk138 - 4; y = 1; } lcd.setCursor(x, y); lcd.write('.'); wait_mk138++; if(wait_mk138 > 12){ // [.]12コ swdclr(); // SW入力on クリア exc_step++; // 次exc } } } /***** スタンバイ *****/ void xstby(void) { lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("Stanby ")); tm_1sec = TM_BKOFF; // バックライト消灯時間 exc_step++; // 次exc } /***** スタンバイ #2 *****/ // SW入力を待つ // 1秒タイマータイムアップでバックライト消灯 void xstby2(void) { static byte bf = 0; if(tm_1sec) bf = 0; // 1秒カウント中 else{ // 0秒になった if(bf == 0){ // はじめての0? bf = 1; LOFF_BKLT; // バックライト消灯 } } // push sw点滅 if(f_blink){ // 表示タイミング f_blink = 0; lcd.setCursor(0, 1); if(f_blank) lcd.print(F(" ")); else lcd.print(F(" push sw")); } // スイッチ チェック switch(swdrd()){ case SW_PLAY: // PLAY on/off LON_BKLT; // バックライトon exc_step = X_NEW; // 新シャッフルパターンで break; case SW_BFOR: // 前曲 case SW_NEXT: // 次曲 case SW_SET: // 設定 (長押し) LON_BKLT; // バックライトon exc_step = X_SETMAX; // 最大曲数設定へ break; } } /***** 新再生パターン *****/ void xnew(void) { if((file_max == 0) || // ファイル数0ならファイル数設定へ (file_max > MAX_FILE) || // ファイル数は1~999 (dir_nbr < 2) || // DIR番号 0,1はダメ (dir_nbr > 15)){ // DIR番号は2~15が有効 exc_step = X_SETMAX; // 異常値なら設定へ } else{ // OK memset(play_flag, 0, sizeof(play_flag)); // シャッフル再生用フラグクリア play_cnt = 0; // 再生数クリア exc_step++; // 次exc } } /***** 乱数を発生してファイルを決定 *****/ // 前再生ファイルと同じ番号のは除く void xrand(void) { word d1; // 1~999 word p; // シャッフル位置 byte m; // bitマスク if(file_max < 2) d1 = 1; // ファイル数1つだけ else{ if(JP_NORAND){ // 乱数無しジャンパーあり d1 = play_cnt +1 ; // 再生数をファイル番号に } else{ // 乱数で d1 = random(file_max) + 1; // 乱数でファイル番号 if(d1 == play_file) return; // 同番号なら再選択 } } // シャッフルチェック while(1){ // lcd.setCursor(0, 1); // lcdprintf(" %03d", d1); // delay(1000); p = (d1 - 1) / 8; // 配列 m = 1 << ((d1 - 1) % 8); // ビットマスク if(((play_flag[p] & m) == 0) || // まだ再生してない (JP_NORAND != 0)) // 乱数無しジャンパーあり break; d1++; // 次曲に if(d1 > file_max) d1 = 1; // 最後なら先頭に } // 新再生ファイル決定 play_file = d1; play_flag[p] |= m; // シャッフルデータ play_cnt++; // 再生数 // 前曲として保存 play_bfor[playbfor_p] = play_file; playbfor_p++; // ファイル番号保存ポインタ if(playbfor_p >= DIMSIZ(play_bfor)) playbfor_p = 0; // 一周で0に exc_step++; // 次exc } /***** 再生開始 *****/ // MK-138に再生データ送出 // 再生中なら停止コマンドを先に送出 void xplay(void) { word d, f; if(JP_ALLPLAY){ // 全ファイル再生 d = 2 + ((play_file - 1) / 100); // DIR 2~15 f = 1 + ((play_file - 1) % 100); // FILE 1~100 } else{ // 指定フォルダー再生 d = dir_nbr; // 設定したDIR f = play_file; // 再生ファイル番号 } LON_BKLT; // バックライトon disppfile(); // 再生ファイル番号表示 mp3stop(); // MP3再生停止 // 再生中はVRを絞ってから停止 txmp3wr(0xF0 + d); // DIR番号(フォルダ) 2~15 txmp3wr(0x00 + f); // ファイル番号 1~199(max) tm_10ms = 250; // 2.5秒 tm_1sec = 10; // 10秒 (JP_10SEC) exc_step++; // 次exc } /***** 音量up *****/ // 再生開始時にプチ音軽減できるか? // 再生が始まり、BUSY信号がH→Lになるタイミングでプチ音発生 void xvrup(void) { if((INP_BUSY == 0) || // ready あるいは (txmp3_cnt !=0)) tm_1ms = 20; // 送信中なら20ms if((tm_1ms == 0) || // readyからbusyへのタイムアップ? (tm_10ms == 0) || // ready連続2.5秒(SDカード無し) (swdchk())){ // SW入力あり? mp3vrup(); // 最大音量に tm_10ms = 100; // 1.0秒 exc_step++; // 次exc } } /***** 再生完了確認 *****/ // 途中でswを押したら前曲、次曲 void xendck(void) { if(swdchk()){ // SW onあり switch(swdrd()){ case SW_BFOR: // 前曲(経過保存曲) exc_step = X_BFOR; // 前曲再生へ break; case SW_NEXT: // 次曲(ランダムになる) exc_step = X_NEXT; // 次曲へ break; case SW_SET: // 設定 (長押し) exc_step = X_SETMAX; // 最大曲数設定へ break; case SW_PLAY: //PLAY on/off mp3stop(); // MP3再生停止 exc_step = X_STBY; // スタンバイに break; } } // 再生おわりチェック else{ if(INP_BUSY) tm_10ms = 100; // busy中は1秒セット if(tm_10ms == 0) exc_step = X_NEXT; // busy解除で次の曲 else{ if(JP_10SEC){ // JP 10秒 on? if(tm_1sec == 0) exc_step = X_NEXT; // 再生10秒で次曲に } } } } /***** 前曲へ *****/ void xbefore(void) { word p; if(JP_NORAND){ // 乱数無しジャンパー if(play_file < 2) play_file = file_max; else play_file--; play_cnt = play_file; // 再生数 } else{ p = playbfor_p; // ファイル番号保存ポインタ p--; // 前曲 if(p >= DIMSIZ(play_bfor)) p = DIMSIZ(play_bfor) - 1; // 一周で最後に if(play_bfor[p] != 0){ // 曲あり? play_file = play_bfor[p]; // 曲番号 playbfor_p = p; // 曲保存ポインタ } } exc_step = X_PLAY; // 曲再生へ } /***** 次曲へ *****/ // 最大数なら新シャッフルで再生再開 void xnext(void) { if(play_cnt >= file_max) exc_step = X_NEW; // 新シャッフルパターンで else exc_step = X_RAND; // 次曲から } /**** 再生DIR番号,最大曲数セット *****/ void xsetmax(void) { lcd.clear(); smax_pos = 0; // 入力位置 smax_num[0] = (dir_nbr / 10) % 10; // D 10桁 smax_num[1] = dir_nbr % 10; // 1桁 smax_num[2] = (file_max / 100) % 10; // F 100桁 smax_num[3] = (file_max / 10) % 10; // 10桁 smax_num[4] = file_max % 10; // 1桁 dispsmax(); // 設定値表示 exc_step++; // 次exc } /***** 再生DIR番号,最大曲数セット#2 *****/ void xsetmax2(void) { word *p; if(f_blink){ // 表示タイミング f_blink = 0; lcd.setCursor(smax_data[smax_pos].cur, 1); // 2行目 if(f_blank) lcd.write(' '); else lcdprintf("%d", *smax_data[smax_pos].num); } // スイッチ チェック switch(swdrd()){ case SW_BFOR: // 数値+1 p = smax_data[smax_pos].num; *p += 1; // +1 if(*p > 9) *p = 0; // 0~9 dispsmax(); // 設定値表示 f_blink = 1; // 表示指令 break; case SW_NEXT: // 次タイマー位置 dispsmax(); // 設定値表示 smax_pos++; if(smax_pos >= DIMSIZ(smax_data)) // Fileの一桁 smax_pos = 0; // DIR十桁に戻す break; break; case SW_PLAY: // PLAY SWで 設定完了 dir_nbr = (smax_num[0] * 10) + // DIR番号 smax_num[1]; file_max = (smax_num[2] * 100) + // ファイル数 (smax_num[3] * 10) + smax_num[4]; eeprom_write(dir_nbr, ep_dirnbr); // EEPROMに eeprom_write(file_max, ep_filemax); exc_step = X_NEW; // 新シャッフルパターンで break; } } /********************************/ /* 制御実行テーブル */ /********************************/ /***** 制御実行 *****/ void (*runexc[])(void)={ xttl1, // 0 タイトル表示 xttl2, // 1 タイトル表示 #2 xttl3, // 2 タイトル表示 #3 xttl4, // 3 タイトル表示 #4 xstby, // * 4 スタンバイ xstby2, // 5 スタンバイ xnew, // * 6 新再生パターン xrand, // * 7 乱数を発生してファイルを決定 xplay, // * 8 再生開始 xvrup, // 9 音量アップ xendck, // 10 再生完了確認 xbefore, // * 11 前曲へ xnext, // * 12 次曲へ xsetmax, // * 13 DIR番号、最大曲数セット xsetmax2, // 14 曲数セット続き }; /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { if(f_1ms){ // 1ms f_1ms = 0; txmp3data(); // MP3データ送出タイミング } if(f_10ms){ // 10ms f_10ms = 0; swonwr(); // スイッチ入力チェック // onデータをバッファリング if(INP_BUSY) LON_PLAY; // 再生中LED on/off else LOFF_PLAY; } runexc[exc_step](); // 制御実行区分 } /*==== end of "mp3_mk138.ino" ====*/