※このバージョンは最新版ではありません。
ピンアサインは秋月電子の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;
}
}
}