※このバージョンは最新版ではありません。
ピンアサインは秋月電子のArduino Pro mini互換基板(http://akizukidenshi.com/catalog/g/gK-10347/)
に合わせて変更しております。プログラムのみ更新する場合はピンアサインを以前のコードからコピペして書き換えて下さい。
※2019/10/16 追記
V1.00よりコードを一部変更し、音データの肥大化によりメモリが不足する問題を解消しました。
今回の変更により、メモリ使用量を気にする事無く音データを作りこむことが可能となります。
ソースコード
// *************************************************************************************** // WhiteBear Controller Next Generation // *************************************************************************************** // ・PWM制御による鉄道模型用コントローラー。 // ・12段階のロータリースイッチを用い、ワンハンドルマスコンを模擬する。 // ・マスコンはP5-B5 + EB の12段。 // ・各種状況はキャラクタLCD(16文字x2段)で表示。 // ・力行各段の最高速度を設定可能。設定はソースコード内であらかじめ設定。 // ・ノッチによる加速度の違いもついでに再現。 // ・PWM周波数の変調により、VVVFなど制御機器の音を再現。 // ・変調音は最大12種類までプログラム可能。 // ・加速時と減速時で異なる変調音を設定可能。 // ・マスコンP5で電源投入すると、開発モード。 // ・開発モード(isDev=True)の時、現在/目標スピード値と加速率/常点灯ダイヤルの値も表示。 // *************************************************************************************** //更新履歴 //2019/07/30 V1.00 初期版。 //2019/10/11 V1.10 音データをフラッシュメモリ格納に変更し、RAM不足を解消。 // *************************************************************************************** #include <avr/io.h> // ATmega328P用のヘッダファイル #include <avr/pgmspace.h> #include <LiquidCrystal.h> // キャラクタ液晶ディスプレイ制御用のヘッダファイル // *********************************************** // 定数・変数定義 ここから // *********************************************** const char *MOJI1 = " WCNG Controller"; // Welcomeメッセージ。LCD1段目。16桁にすること。 const char *MOJI2 = "Program Ver 1.10"; // Welcomeメッセージ。LCD2段目。16桁にすること。 const int P_MASCON = 3; // Arduino接続ピン番号:(Analog) ロータリースイッチ = A3 const int P_LIGHT_DIAL = 4; // Arduino接続ピン番号:(Analog) 常点灯調節ダイアル = A4 const int P_ACCEL_DIAL = 5; // Arduino接続ピン番号:(Analog) 加速率調節ダイアル = A5 const int P_PWM2B = 3; // Arduino接続ピン番号:(Digital) OCR2B出力 to Rail = D3 const int P_LCD_RS = 4; // Arduino接続ピン番号:(Digital) LCD-REGISTER = D4 const int P_LCD_EN = 5; // Arduino接続ピン番号:(Digital) LCD-ENABLE = D5 const int P_LCD_D4 = 6; // Arduino接続ピン番号:(Digital) LCD-D4 = D6 const int P_LCD_D5 = 7; // Arduino接続ピン番号:(Digital) LCD-D5 = D7 const int P_LCD_D6 = 8; // Arduino接続ピン番号:(Digital) LCD-D6 = D8 const int P_LCD_D7 = 9; // Arduino接続ピン番号:(Digital) LCD-D7 = D9 const int P_PWM1B = 10; // Arduino接続ピン番号:(Digital) OCR1B出力 to Rail = D10 const int P_PWM2A = 11; // Arduino接続ピン番号:(Digital) OCR2A出力 to Rail = MOSI // キャラクタ液晶 LiquidCrystal lcd(P_LCD_RS, P_LCD_EN, P_LCD_D4, P_LCD_D5, P_LCD_D6, P_LCD_D7); const int DECISION_TIME = 5; // setup時に走行パターンを決める際の、確定までの時間。秒。 const int ACCEL_RATIO = 20; // (加速率調整小型ボリュームからの入力値) × ACCEL_RATIO ⇒ 加速率 const int BRAKE_RATIO = 6; // (加速率調整小型ボリュームからの入力値) × BRAKE_RATIO × マスコンブレーキ値(1~5) ⇒ 減速率 const int UNTI_CHAT_RATE = 40; // チャタリング防止ループの値。大きくし過ぎるとノッチ切替時の反応が悪くなる。 const int MC_EB = 1; // マスコン位置 非常 const int MC_B5 = 2; // マスコン位置 制動5 const int MC_B4 = 3; // マスコン位置 制動4 const int MC_B3 = 4; // マスコン位置 制動3 const int MC_B2 = 5; // マスコン位置 制動2 const int MC_B1 = 6; // マスコン位置 制動1 const int MC_N = 7; // マスコン位置 惰行 const int MC_P1 = 8; // マスコン位置 力行1 const int MC_P2 = 9; // マスコン位置 力行2 const int MC_P3 = 10; // マスコン位置 力行3 const int MC_P4 = 11; // マスコン位置 力行4 const int MC_P5 = 12; // マスコン位置 力行5 // マスコン位置名称を保持する配列 char* masconPosName[] = {"EB", "B5", "B4", "B3", "B2", "B1", "N ", "P1", "P2", "P3", "P4", "P5"}; const int MD_STOP = 1; // モード 停止 const int MD_BRAKE = 2; // モード 減速 const int MD_NTRL = 3; // モード 惰行 const int MD_ACCEL = 4; // モード 加速 //boolean isDev; // 開発モードのときTrue。 int vvvfPtn; // 走行音パターン。0~11となる。 int masconPos; // マスコンの位置を保持。1~12となる。 int inputMascon; // 読み取ったマスコン位置を一時的に保持 int compareMascon; // マスコンの位置前回と比較するため一時的に保持 int lightVol; // 常点灯ダイアルから拾った値を0~127に変換して保持する。変換処理はloop()内にて。 int accelVol; // 加速率ダイアルから拾った値を1~8に変換して保持する。変換処理はloop()内にて。 int kasoku; // 走行用出力する加減速率 int mode; // 走行状態。1=停止, 2=減速, 3=惰行, 4=加速 long notch1; // ノッチ1での最高速度 long notch2; // ノッチ2での最高速度 long notch3; // ノッチ3での最高速度 long notch4; // ノッチ4での最高速度 long notch5; // ノッチ5での最高速度 long stopSpd; // 走行用出力をカットする速度 long AsttFrq; // 開始周波数 long AendFrq; // 終了周波数 long Afrq; // 周波数 long AsttSpd; // 開始スピード long AendSpd; // 終了スピード long BsttFrq; // 開始周波数 long BendFrq; // 終了周波数 long Bfrq; // 周波数 long BsttSpd; // 開始スピード long BendSpd; // 終了スピード long spd; // 内部スピード 0~3,000,000(速度×10,000) long orderSpd; // 指示スピード int i; // ループカウンター int k; // ループカウンター int dispSpd = 0; // LCD表示用スピード値 //long dispOrderSpd = 0; // LCD表示用スピード目標値 // 走行音名称 char* soundName[12] = { " 201 ", " 209,70-000,etc ", " E231-0,500,etc ", " E231-1000 ", " E233, etc ", "KQ 2100 (SI-GTO)", " N1000 (SI-GTO) ", " MITSUBISHI-GTO ", " Toyo-GTO ", " Toyo-IGBT ", "Toei 5300(Akuma)", "TOSHIBA 3LevIGBT" }; // 各パターンの各ノッチ位置での最高速度値を配列で保持。 // 左から順に 1ノッチ, 2ノッチ, 3ノッチ, 4ノッチ, 5ノッチ。 // 5×12パターン=60個の値を持つ。 int maxSpdData[] = { 21, 41, 71, 91, 101, //パターン 1 201 31, 51, 81, 101, 111, //パターン 2 209,70-000,etc 31, 51, 91, 111, 121, //パターン 3 E231-0,500,etc 31, 51, 91, 111, 121, //パターン 4 E231-1000 31, 51, 91, 111, 121, //パターン 5 E233, etc 41, 71, 111, 121, 131, //パターン 6 KQ 2100 (SI-GTO) 41, 71, 111, 121, 131, //パターン 7 N1000 (SI-GTO) 41, 71, 111, 121, 131, //パターン 8 MITSUBISHI-GTO 41, 71, 111, 121, 131, //パターン 9 Toyo-GTO 41, 71, 111, 121, 131, //パターン10 Toyo-IGBT 31, 61, 101, 111, 111, //パターン11 Toei 5300(Akuma) 31, 61, 91, 101, 111, //パターン12 TOSHIBA 3LevIGBT }; // 走行用の出力を止める速度値を配列で保持。ピタッと止めるための値。 // 環境に合わせて適宜調整して下さい。 // 12パターン分の値を持つ。 int stopSpdData[] = {19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19}; // 音データ。 // 設定可能周波数 123Hz~8000kHz // DUTY比の分解能確保のため30kHzぐらいを上限。整数に限る。 // ~~~(例) パターン3の場合~~~ // 1行目 速度0~ 3の間:380Hzで音程変化なし // 2行目 速度4~11の間:380Hzから980Hzまで変化する //加速時用の音データ。 const int AsoundDataBase[] PROGMEM = { //パターン1 201 580, 580, 66, // 1 開始周波数 終了周波数 切替スピード 550, 980, 101, // 2 開始周波数 終了周波数 切替スピード -1, // 3 終了コード //パターン2 209,70-000,etc 230, 230, 3, // 1 開始周波数 終了周波数 切替スピード 90, 90, 4, // 2 開始周波数 終了周波数 切替スピード 230, 950, 11, // 3 開始周波数 終了周波数 切替スピード 600, 1150, 21, // 4 開始周波数 終了周波数 切替スピード 750, 1150, 31, // 5 開始周波数 終了周波数 切替スピード 600, 950, 46, // 6 開始周波数 終了周波数 切替スピード 470, 1150, 91, // 7 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 8 開始周波数 終了周波数 切替スピード -1, // 9 終了コード //パターン3 E231-0,500,etc 380, 380, 3, // 1 開始周波数 終了周波数 切替スピード 380, 980, 11, // 2 開始周波数 終了周波数 切替スピード 560, 780, 26, // 3 開始周波数 終了周波数 切替スピード 480, 1150, 101, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン4 E231-1000 1050, 1050, 11, // 1 開始周波数 終了周波数 切替スピード 1050, 700, 27, // 2 開始周波数 終了周波数 切替スピード 700, 6000, 35, // 3 開始周波数 終了周波数 切替スピード 480, 1150, 101, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン5 E233, etc 750, 750, 30, // 1 開始周波数 終了周波数 切替スピード 350, 1150, 101, // 2 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン6 KQ 2100 (SI-GTO) 350, 350, 3, // 1 開始周波数 終了周波数 切替スピード 390, 390, 4, // 2 開始周波数 終了周波数 切替スピード 440, 440, 5, // 3 開始周波数 終了周波数 切替スピード 490, 490, 6, // 4 開始周波数 終了周波数 切替スピード 530, 530, 7, // 5 開始周波数 終了周波数 切替スピード 580, 580, 8, // 6 開始周波数 終了周波数 切替スピード 650, 650, 9, // 7 開始周波数 終了周波数 切替スピード 700, 700, 10, // 8 開始周波数 終了周波数 切替スピード 780, 780, 22, // 9 開始周波数 終了周波数 切替スピード 510, 980, 111, //10 開始周波数 終了周波数 切替スピード 580, 580, 131, //11 開始周波数 終了周波数 切替スピード -1, //12 終了コード //パターン7 N1000 (SI-GTO) 350, 350, 3, // 1 開始周波数 終了周波数 切替スピード 390, 390, 4, // 2 開始周波数 終了周波数 切替スピード 440, 440, 5, // 3 開始周波数 終了周波数 切替スピード 490, 490, 6, // 4 開始周波数 終了周波数 切替スピード 530, 530, 7, // 5 開始周波数 終了周波数 切替スピード 580, 580, 8, // 6 開始周波数 終了周波数 切替スピード 650, 650, 9, // 7 開始周波数 終了周波数 切替スピード 700, 700, 10, // 8 開始周波数 終了周波数 切替スピード 780, 780, 16, // 9 開始周波数 終了周波数 切替スピード 950, 970, 18, //10 開始周波数 終了周波数 切替スピード 850, 970, 20, //11 開始周波数 終了周波数 切替スピード 800, 970, 22, //12 開始周波数 終了周波数 切替スピード 510, 980, 111, //13 開始周波数 終了周波数 切替スピード 580, 580, 131, //14 開始周波数 終了周波数 切替スピード -1, //15 終了コード //パターン8 MITSUBISHI GTO 490, 710, 16, // 1 開始周波数 終了周波数 切替スピード 710, 710, 25, // 2 開始周波数 終了周波数 切替スピード 600, 780, 31, // 3 開始周波数 終了周波数 切替スピード 420, 1250, 110, // 4 開始周波数 終了周波数 切替スピード 1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン9 Toyo-GTO 490, 780, 18, // 1 開始周波数 終了周波数 切替スピード 490, 780, 30, // 2 開始周波数 終了周波数 切替スピード 420, 1250, 110, // 3 開始周波数 終了周波数 切替スピード 1250, 1250, 131, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン10 Toyo-IGBT 1050, 1050, 25, // 1 開始周波数 終了周波数 切替スピード 630, 1300, 40, // 2 開始周波数 終了周波数 切替スピード 650, 1150, 111, // 3 開始周波数 終了周波数 切替スピード 1150, 1150, 131, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン11 Toei 5300(Akuma) 780, 780, 11, 450, 450, 16, 500, 780, 22, 430, 580, 25, 400, 1150, 105, 1150, 1150, 111, -1, // 6 終了コード //パターン12 TOSHIBA 3LevIGBT 730, 730, 11, 510, 730, 16, 600, 1150, 101, 1150, 1150, 111, -1, // 7 終了コード }; //RAM消費を抑える為の配列。 //[]内の数字を3n(n=上記パターンの中で最も行数が長いパターンの行数)以上に設定して下さい。 //あまり数字を大きくし過ぎるとメモリ不足になるため、むやみに大きくしないように。 //初期値は45。 int AsoundData[45]; // 走行音パターン12種soundDataの各パターンデータ開始位置インデックスを保持。 // 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。 int AdataNum[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; //減速時用の音データ。 const int BsoundDataBase[] PROGMEM = { //パターン1 201 580, 580, 61, // 1 開始周波数 終了周波数 切替スピード 580, 650, 76, // 2 開始周波数 終了周波数 切替スピード 550, 980, 101, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン2 209,70-000,etc 120, 120, 3, // 1 開始周波数 終了周波数 切替スピード 120, 1180, 26, // 2 開始周波数 終了周波数 切替スピード 600, 900, 36, // 3 開始周波数 終了周波数 切替スピード 550, 1800, 96, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン3 E231-0,500,etc 350, 350, 6, // 1 開始周波数 終了周波数 切替スピード 350, 980, 31, // 2 開始周波数 終了周波数 切替スピード 580, 900, 86, // 3 開始周波数 終了周波数 切替スピード 530, 1150, 101, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン4 E231-1000 1050, 1050, 16, // 1 開始周波数 終了周波数 切替スピード 1050, 700, 41, // 2 開始周波数 終了周波数 切替スピード 700, 2900, 44, // 3 開始周波数 終了周波数 切替スピード 780, 880, 46, // 4 開始周波数 終了周波数 切替スピード 580, 900, 86, // 5 開始周波数 終了周波数 切替スピード 530, 1150, 101, // 6 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 7 開始周波数 終了周波数 切替スピード -1, // 8 終了コード //パターン5 E233, etc 750, 750, 31, // 1 開始周波数 終了周波数 切替スピード 300, 1000, 106, // 2 開始周波数 終了周波数 切替スピード 1000, 1000, 121, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン6 KQ 2100 (SI-GTO) 780, 780, 22, // 1 開始周波数 終了周波数 切替スピード 780, 1150, 35, // 2 開始周波数 終了周波数 切替スピード 780, 970, 45, // 3 開始周波数 終了周波数 切替スピード 600, 980, 111, // 4 開始周波数 終了周波数 切替スピード 580, 580, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン7 N1000 (SI-GTO) 780, 780, 16, // 1 開始周波数 終了周波数 切替スピード 780, 970, 19, // 2 開始周波数 終了周波数 切替スピード 780, 820, 22, // 3 開始周波数 終了周波数 切替スピード 780, 1150, 35, // 4 開始周波数 終了周波数 切替スピード 780, 970, 45, // 5 開始周波数 終了周波数 切替スピード 600, 980, 111, // 6 開始周波数 終了周波数 切替スピード 580, 580, 131, // 7 開始周波数 終了周波数 切替スピード -1, // 8 終了コード //パターン8 MITSUBISHI GTO 580, 720, 16, // 1 開始周波数 終了周波数 切替スピード 720, 720, 26, // 2 開始周波数 終了周波数 切替スピード 460, 650, 36, // 3 開始周波数 終了周波数 切替スピード 450, 1250, 110, // 4 開始周波数 終了周波数 切替スピード 1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン9 Toyo-GTO 580, 580, 7, // 1 開始周波数 終了周波数 切替スピード 580, 750, 20, // 2 開始周波数 終了周波数 切替スピード 380, 650, 31, // 3 開始周波数 終了周波数 切替スピード 450, 1250, 110, // 4 開始周波数 終了周波数 切替スピード 1250, 1250, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン10 Toyo-IGBT 1050, 1050, 26, // 1 開始周波数 終了周波数 切替スピード 600, 930, 41, // 2 開始周波数 終了周波数 切替スピード 600, 950, 95, // 3 開始周波数 終了周波数 切替スピード 950, 950, 111, // 4 開始周波数 終了周波数 切替スピード 580, 580, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン11 Toei 5300(Akuma) 780, 780, 18, 440, 460, 22, 460, 570, 36, 500, 1150, 105, 1150, 1150, 111, -1, // 6 終了コード //パターン12 TOSHIBA 3LevIGBT 730, 730, 11, 510, 1150, 101, 1150, 1150, 111, -1, // 7 終了コード }; //RAM消費を抑える為の配列。 //[]内の数字を3n(n=上記パターンの中で最も行数が長いパターンの行数)以上に設定して下さい。 //あまり数字を大きくし過ぎるとメモリ不足になるため、むやみに大きくしないように。 //初期値は45。 int BsoundData[45]; // 走行音パターン12種soundDataの各パターンデータ開始位置インデックスを保持。 // 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。 int BdataNum[13] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; // *********************************************** // 定数・変数定義 ここまで // *********************************************** // ********************************************************************************************** // 関数定義 ここから // ********************************************************************************************** // *********************************************** // マスコン位置を検出する。 // ロータリースイッチからのボリューム値を拾い、 // 適度な値に変換する処理。 // // 引数: アナログピンから取得した値(Max:1023) // 戻値: 1(=EB)~12(=P5) // *********************************************** // ロータリースイッチが12段階切替なので、 // 入力を12段階に分ける。 // 3番ピンからのanalogRead値は・・・(実測値) // ・ロータリースイッチの位置が 1の時:11kΩ ⇒ 83 // ・ロータリースイッチの位置が 2の時:10kΩ ⇒ 169 // ・ロータリースイッチの位置が 3の時: 9kΩ ⇒ 254 // ・ロータリースイッチの位置が 4の時: 8kΩ ⇒ 340 // ・ロータリースイッチの位置が 4の時: 7kΩ ⇒ 425 // ・ロータリースイッチの位置が 4の時: 6kΩ ⇒ 511 // ・ロータリースイッチの位置が 4の時: 5kΩ ⇒ 596 // ・ロータリースイッチの位置が 4の時: 4kΩ ⇒ 682 // ・ロータリースイッチの位置が 4の時: 3kΩ ⇒ 767 // ・ロータリースイッチの位置が 4の時: 2kΩ ⇒ 853 // ・ロータリースイッチの位置が11の時: 1kΩ ⇒ 938 // ・ロータリースイッチの位置が12の時: 0kΩ ⇒ 1023 int getMasconVol(int vol) { int ans = 0; // マスコンロータリースイッチから読み取った値を、小数点も扱える形で // 0~1023に収まるようにする。12接点なので12等分。 // 四捨五入相当の処理をする⇒値に+0.5した上で、小数点以下を切り捨てるためint型に変換。 ans = (int)(((double) analogRead(P_MASCON)) / (1024 / 12) + 0.5); return ans; } // *********************************************** // マスコン位置を表示。LCDの右下2桁へ。 // ついでにスピード値も表示。なんとなくKATO103系の // 動力ユニットでスケールスピード(?) // 動力ユニットによって同じ電圧でも実速度が異なる // ので、あくまでも目安値。 // ----------------------------------------------- // 開発モード(isDev=True)の時は、目標スピード値と // 加速率ダイヤル、常点灯ダイヤルの値も表示する。 // *********************************************** void dispMasPos(int masPos) { lcd.setCursor(14, 1); lcd.print(masconPosName[masPos - 1]); // spdも表示 dispSpd = spd / 10000; lcd.setCursor(4, 1); if (dispSpd / 100 > 0) { // 3digits. } else { if (dispSpd / 10 > 0) { // 2digits. lcd.print(" "); } else { // 1digit. lcd.print(" "); } } lcd.print(dispSpd); /* if (isDev) { // "0123456789012345" // ついでにorderSpdも表示。"SPD:nnn>nnn XX" dispOrderSpd = orderSpd / 10000; lcd.setCursor(8, 1); if (dispOrderSpd / 100 > 0) { // 3digits. } else { if (dispOrderSpd /10 > 0) { // 2digits. lcd.print(" "); } else { // 1digit. lcd.print(" "); } } lcd.print(dispOrderSpd); // ---------------------- "0123456789012LLL" ※LLL=常点灯ダイヤル値 // ついでにorderSpdも表示。"Spd:nnn>nnn A XX" ※A=加速率ダイヤル値 XX=マスコン値 lcd.setCursor(12, 0); lcd.print(" "); if (lightVol < 100 && lightVol >= 10) { lcd.print(" "); } else if (lightVol < 10) { lcd.print(" "); } lcd.print(lightVol); lcd.setCursor(12, 1); lcd.print(accelVol); } */ } // *********************************************** // setup関数。最初に1度だけ実行。 // *********************************************** void setup() { // PWM PIN設定 pinMode(P_PWM2A, OUTPUT); // 常点灯用PWM出力 pinMode(P_PWM2B, OUTPUT); // 走行用PWM出力 pinMode(P_PWM1B, OUTPUT); // 何用? // WELCOMEメッセージ表示 lcd.begin(16, 2); // 16桁、2行タイプと宣言。 lcd.clear(); lcd.setCursor(0, 0); lcd.print(MOJI1); delay(2000); lcd.setCursor(0, 1); lcd.print(MOJI2); delay(2000); // isDev = (MC_P5 == getMasconVol(analogRead(P_MASCON))); // マスコンがP5の時だけ開発モード。 lcd.clear(); // 走行音パターン各開始位置取得処理。 // soundData配列のうち、各パターンの最初の値となる配列インデックスをdataNumで保持する。 // パターン1は常に先頭(=インデックス0)なので、dataNumにセットするのは省略。 int idx = 0; for (i=1; i<13; i++) { while (pgm_read_word_near(&AsoundDataBase[idx]) != -1) { idx++; } idx++; AdataNum[i] = idx; } idx = 0; for (i=1; i<13; i++) { while (pgm_read_word_near(&BsoundDataBase[idx]) != -1) { idx++; } idx++; BdataNum[i] = idx; } // 走行音パターン取得 lcd.home(); // 1文字目、1行目 for (i=0; i<2; i++) { lcd.clear(); delay(180); lcd.print("Select SoundPtn."); delay(480); } delay(240); lcd.clear(); lcd.print("SoundPtn."); lcd.setCursor(0, 1); lcd.print(" Revolve MasCon."); //delay(2000); // ロータリースイッチを10秒動かさないと決定。 // ただし、何も動かしてないときはずっと待ち続ける。 unsigned long sttTime = 0; unsigned long progress = 0; boolean isMoved = false; // マスコンを動かしたらTrueにする masconPos = getMasconVol(analogRead(P_MASCON)); // 初期値 while (!isMoved) { inputMascon = getMasconVol(analogRead(P_MASCON)); // 現在値 // 読み取った最新マスコン位置が、以前に覚えていたマスコン位置と異なる場合 // =ロータリースイッチを動かしたと判断。 isMoved = (masconPos != inputMascon); } sttTime = millis(); // ロータリースイッチをいじったので、初期化。 do { while (true) { inputMascon = getMasconVol(analogRead(P_MASCON)); // 現在値 if (masconPos != inputMascon) { // 読み取った最新マスコン位置が、以前に覚えていたマスコン位置と // 異なる場合=ロータリースイッチを動かしたと判断。 masconPos = inputMascon;// 新たなマスコン位置を覚えておく。 sttTime = millis(); // ロータリースイッチをいじったので、初期化。 lcd.setCursor(0, 1); // 1文字目、2行目 lcd.print(soundName[masconPos - 1]); // LCDに走行音名称を表示させる。 } else { if ((millis() - sttTime) > DECISION_TIME * 1000) { // 選んでから所定時間が過ぎた。 break; // ループを抜ける。 } } progress = millis() - sttTime; if (progress > 0) { lcd.setCursor(15, 0); lcd.print((String) (DECISION_TIME - (progress / 1000))); } delay(1); } } while (masconPos < MC_EB || masconPos > MC_P5); // 12種類から選択されたらループを抜け次の処理へ。 // 走行音パターンほか選択したパターンに合わせて設定 vvvfPtn = masconPos - 1; notch1 = (long) maxSpdData[vvvfPtn * 5 ] * 10000 - 1; notch2 = (long) maxSpdData[vvvfPtn * 5 + 1] * 10000 - 1; notch3 = (long) maxSpdData[vvvfPtn * 5 + 2] * 10000 - 1; notch4 = (long) maxSpdData[vvvfPtn * 5 + 3] * 10000 - 1; notch5 = (long) maxSpdData[vvvfPtn * 5 + 4] * 10000 - 1; stopSpd = (long) stopSpdData[vvvfPtn] * 1000 - 1; k = 0; for (i = AdataNum[vvvfPtn]; i<AdataNum[vvvfPtn+1]; i++) { AsoundData[k] = pgm_read_word_near(&AsoundDataBase[i]); k++; } k = 0; for (i = BdataNum[vvvfPtn]; i<BdataNum[vvvfPtn+1]; i++) { BsoundData[k] = pgm_read_word_near(&BsoundDataBase[i]); k++; } // 決定したパターンを点滅表示 for (i=0; i<3; i++) { lcd.clear(); delay(180); lcd.print("Sound Pattern is"); lcd.setCursor(0, 1); // 1文字目、2行目 lcd.print(soundName[vvvfPtn]); delay(480); } delay(240); // マスコン位置を惰行か制動にしてもらう注意喚起。急に走り出さないようにするため。 // マスコン位置が1~6、つまり 非常 か 制動5~1 に設定しないと // 注意喚起され続け、次の処理へ進めない。 masconPos = getMasconVol(analogRead(P_MASCON)); while (masconPos < MC_EB || masconPos > MC_B1) { lcd.clear(); lcd.print("Caution!"); lcd.setCursor(0, 1); lcd.print("Apply Brake."); delay(240); masconPos = getMasconVol(analogRead(P_MASCON)); } lcd.clear(); lcd.print("Ready."); delay(720); lcd.clear(); lcd.print(soundName[vvvfPtn]); lcd.setCursor(0, 1); lcd.print("Spd:"); lcd.setCursor(7, 1); // if (isDev) lcd.write(0x7E); // 右向き矢印 } // *********************************************** // *********************************************** // loop関数 // *********************************************** // *********************************************** void loop() { //動作速度確保のため、音データをメモリに格納。 accelVol = analogRead(P_ACCEL_DIAL) / 128 + 1; // analogRead 0~1023 → 1~8 lightVol = analogRead(P_LIGHT_DIAL) / 8; // analogRead 0~1023 → 0~127 inputMascon = getMasconVol(analogRead(P_MASCON)); if (inputMascon >= MC_EB && inputMascon <= MC_P5) { // 選択し得る12段階のマスコン位置であると判断。 if (compareMascon == inputMascon) { masconPos = inputMascon; } else if (compareMascon > inputMascon) { compareMascon--; } else if (compareMascon < inputMascon) { compareMascon++; } } switch (masconPos) { case 1: // 非常 orderSpd = 0; spd = 0; mode = MD_STOP; break; case 2: // 制動5 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 5; mode = MD_BRAKE; break; case 3: // 制動4 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 4; mode = MD_BRAKE; break; case 4: // 制動3 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 3; mode = MD_BRAKE; break; case 5: // 制動2 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 2; mode = MD_BRAKE; break; case 6: // 制動1 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 1; mode = MD_BRAKE; break; case 7: // 惰行 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO * 2; mode = MD_NTRL; break; case 8: // 力行1 orderSpd = notch1; kasoku = accelVol * ACCEL_RATIO * 0.5; mode = MD_ACCEL; break; case 9: // 力行2 orderSpd = notch2; kasoku = accelVol * ACCEL_RATIO * 0.6; mode = MD_ACCEL; break; case 10: // 力行3 orderSpd = notch3; kasoku = accelVol * ACCEL_RATIO * 0.7; mode = MD_ACCEL; break; case 11: // 力行4 orderSpd = notch4; kasoku = accelVol * ACCEL_RATIO * 0.9; mode = MD_ACCEL; break; case 12: // 力行5 orderSpd = notch5; kasoku = accelVol * ACCEL_RATIO; mode = MD_ACCEL; break; } for ( k = 0; k < UNTI_CHAT_RATE; k++ ) { // チャタリング防止ループ dispMasPos(masconPos); if (spd < orderSpd) { if (orderSpd - spd <= kasoku) { spd = orderSpd; } else { spd = spd + kasoku; } } if ( spd > orderSpd ) { if ( masconPos >= MC_N ) { // 惰行or力行 if ( spd - orderSpd <= kasoku / BRAKE_RATIO ) { spd = orderSpd; } else { spd = spd - kasoku / BRAKE_RATIO; } } else { // 制動 if ( spd - orderSpd <= kasoku ) { spd = orderSpd; } else { spd = spd - kasoku; } } } if ( spd < stopSpd && masconPos < MC_P1 ) { // 惰行or制動ノッチのとき、ピタ停止速度値よりも現行速度が下回ったらピタッと停止させる。 spd = 0; mode = MD_STOP; } // 走行時の常点灯PWM DUTY比 // 周波数は31.2kHz = CPU周波数(16MHz) / 2 / 分周256 // OCR2Aはこの数値からTOP255がDUTY比 OCR2A = 255 - lightVol; // 走行用出力。 // SPD / 128 / 4 / 10 / DUTY比 ( 1.5 + speed / 390) OCR2B = (int) (spd / ( 7680 + spd / 390 )); // soundDataは開始周波数 終了周波数 切替スピードの3つの値で構成されているので+3ずつカウントアップ。 // soundDataの定義数は無制限(-1を終了コードとしている) //int j; // ループカウンター if (mode == MD_ACCEL) { for (i = 0; ;i = i + 3) { AsttFrq = AsoundData[i]; AendFrq = AsoundData[i + 1]; if (i == 0) { AsttSpd = 0; AendSpd = AsoundData[i + 2]; } else { AsttSpd = AsoundData[i - 1]; AendSpd = AsoundData[i + 2]; } if (i == 0 && BsttFrq == -1) { // 走行音が設定されていない場合の処理 OCR1A = 421; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 19000 = 421 OCR1B = 421 - 421 * (word)lightVol / 245 ; // 音を出すPWMのDUTY比 TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; TCCR2A = B00100001; break; } if (mode == MD_BRAKE) { // 変調モード切替 break; } if (mode == MD_STOP) { // 変調モード切替 break; } if (mode == MD_NTRL) { // 変調モード切替 break; } if (AsttFrq == -1) { // 高速時 VVVF音停止 Afrq = 31500; OCR1A = (word)(8000000 / Afrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (word)(8000000 / Afrq - 8000000 / Afrq / 99); // 音を出すPWMのDUTY比 100/99 = 1% TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; // PWM 2A/2B 分周1 TCCR2A = B11100001; // PWM 2A/2B 出力 break; } // VVVF音 if ((spd >= AsttSpd * 10000) && (spd < AendSpd * 10000)) { Afrq = ( AendFrq * 10 - AsttFrq * 10 ) / ( AendSpd - AsttSpd ) * ( spd - AsttSpd * 10000 ) / 100000 + AsttFrq; if ( Afrq < 123 ) Afrq = 123; else if ( Afrq > 31500 ) Afrq = 31500; if (mode == MD_NTRL) Afrq = 123; // 惰行のとき。 OCR1A = (word)(8000000 / Afrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (word)(8000000 / Afrq - 8000000 / Afrq / 15); // 音を出すPWMのDUTY比 100/15 = 6.6% TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; // PWM 2A/2B 分周1 TCCR2A = B11100001; // PWM 2A/2B 出力 break; } } } else if (mode == MD_BRAKE) { for (i = 0; ;i = i + 3) { BsttFrq = BsoundData[i]; BendFrq = BsoundData[i + 1]; if (i == 0) { BsttSpd = 0; BendSpd = BsoundData[i + 2]; } else { BsttSpd = BsoundData[i - 1]; BendSpd = BsoundData[i + 2]; } if (i == 0 && BsttFrq == -1) { // 走行音が設定されていない場合の処理 OCR1A = 421; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 19000 = 421 OCR1B = 421 - 421 * (word)lightVol / 245 ; // 音を出すPWMのDUTY比 TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; TCCR2A = B00100001; break; } if (mode == MD_ACCEL) { // 変調モード切替 break; } if (mode == MD_STOP) { // 変調モード切替 break; } if (mode == MD_NTRL) { // 変調モード切替 break; } if (BsttFrq == -1) { // 高速時 VVVF音停止 Bfrq = 31500; OCR1A = (word)(8000000 / Bfrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (word)(8000000 / Bfrq - 8000000 / Bfrq / 99); // 音を出すPWMのDUTY比 100/99 = 1% TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; // PWM 2A/2B 分周1 TCCR2A = B11100001; // PWM 2A/2B 出力 break; } // VVVF音 if ((spd >= BsttSpd * 10000) && (spd < BendSpd * 10000)) { Bfrq = ( BendFrq * 10 - BsttFrq * 10 ) / ( BendSpd - BsttSpd ) * ( spd - BsttSpd * 10000 ) / 100000 + BsttFrq; if ( Bfrq < 123 ) Bfrq = 123; else if ( Bfrq > 31500 ) Bfrq = 31500; if (mode == MD_NTRL) Bfrq = 123; // 惰行のとき。 OCR1A = (word)(8000000 / Bfrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (word)(8000000 / Bfrq - 8000000 / Bfrq / 15); // 音を出すPWMのDUTY比 100/15 = 6.6% TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; // PWM 2A/2B 分周1 TCCR2A = B11100001; // PWM 2A/2B 出力 break; } } } else if (mode == MD_NTRL) { // 惰行 VVVF音停止 OCR1A = 421; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 19000 = 421 OCR1B = 421 - 421 * (word)lightVol / 245 ; // 音を出すPWMのDUTY比 TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; TCCR2A = B00100001; // PWM 2A停止(常点灯部分 代わりに1Bで常点灯出力) 2B出力 } else { // 停止時 音停止 OCR1A = 421; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 19000 = 421 OCR1B = 421 - 421 * (word)lightVol / 255 ; // 音を出すPWMのDUTY比 TCCR1B = B00010001; TCCR1A = B00110001; TCCR2B = B00000001; TCCR2A = B00000001; // PWM 2A(常点灯部分 代わりに1Bで常点灯出力 19kHzで出力したいから)と2B停止 break; } } }