※このバージョンは最新版ではありません。
<V3.10の更新内容>
(V3.10)
・スケールスピード調整機能の強化
→起動速度を調整するパラメータに加え、表示速度に対するDuty比(=実速度)の変化率を調整するパラメータを追加しました。
パラメータを変えることで滑らかに発車・停車させる調整と表示速度に対する実速度の調整を容易に実現できるようになりました。
詳しい内容及び設定方法についてはソースコード内のコメントを参照して下さい。
誤発進防止機能が正常に動作しない場合(パワーパックが動作しなくなる等が頻発する場合)、お手数ですがV2.00(安定版)を使用してください。→V2.00(安定版)
/* *************************************************************************************** TSマスコン(master controller)パワーパック *************************************************************************************** ・PWM制御による鉄道模型用コントローラー。 ・TSマスコン(ワンハンドルのやつ)を接続して運転する。 ・段数はTSマスコンの設定(TYPE)に準じ、TSマスコンのボタン操作でTYPEを選択する仕様。 ・各種状況はキャラクタLCD(16文字x2段)で表示。 ・力行各段の最高速度を設定可能。設定はソースコード内であらかじめ設定。 ・ノッチによる加速度の違いをついでに再現。 ・速度に応じた加速度の変化もだいたい再現。 ・PWM周波数の変調により、VVVFなど制御機器の音を再現。 ・変調音は最大255種類までプログラム可能。(メモリ容量によってはプログラム可能な最大数が少なくなります) ・加速時と減速時で異なる変調音を設定可能。 ・変調音の選択はTSマスコンのボタン操作で行う。 *************************************************************************************** 更新履歴 2020/05/22 V0.10 初期版。動作確認済 2020/05/22 V1.00 段数読替設定追加 2020/09/17 V2.00 TSマスコン2に対応 2020/09/22 V2.10 誤発進防止機能追加、加速度調節の細分化(8段階→16段階)、RAM使用量の削減 2020/11/17 V3.00 PWM周波数および出力設定の変更、加速度調節の細分化(16段階→32段階)、 常点灯の感度調整機能追加、スケールスピード調整機能追加、 最高速度400km/hに変更(モーターの性能により400km/hに届かない場合もあります) 2021/07/03 V3.10 加速曲線と減速度を音データ毎に決められるようになりました。 表示速度の変化率に対するDuty比の変化率を調整できるようにしました。 *************************************************************************************** */ #include <avr/io.h> // ATmega328P用のヘッダファイル #include <avr/pgmspace.h> #include <LiquidCrystal.h> // キャラクタ液晶ディスプレイ制御用のヘッダファイル // *********************************************** // ユーザー設定領域 // 環境に合わせて各値を適宜調整してください。 // *********************************************** #define SOUNDNUM 16 //走行音データの数 #define STOPSPD 9 //列車をピタッと止めるための値。環境に合わせて調整可能 #define SDATANUM 45 //3n(n=最も行数が多い音データの行数)以上の値に設定して下さい。あまり数字を大きくし過ぎるとメモリ不足になるため、むやみに大きくしないように。 #define ANTI_CHAT_RATE 40 //マスコン読み込み待ち時間 const int ACCEL_RATIO = 5; // (加速率調整小型ボリュームからの入力値) × ACCEL_RATIO ⇒ 加速率 const int BRAKE_RATIO = 2; // (加速率調整小型ボリュームからの入力値) × BRAKE_RATIO × マスコンブレーキ値 ⇒ 減速率 const float lmax = 2; //常点灯ボリュームの最大値。lmax=1→出力MAX10%まで、1.5→出力MAX15%まで、のようにボリュームの範囲を調整可能。選択可能な範囲を狭くするほど微調整が可能となる。 const float sscale = 3.1; //スケールスピード変換値。環境に合わせ実測して調整。 const float adrate = 0.5; //加速度及び減速度の変化に対するDuty比の変化率。環境に合わせ実測して調整。 /* sscaleとadrateの関係について: sscaleは表示速度に対するduty比を全域にわたって調整します。グラフをそのまま上下に動かすイメージです。 主に列車の起動速度調整に使用します。 一方、adrateは表示速度の変化に対するDuty比の変化率を調整します。 加速時を例とすると、表示速度の上昇速度に対するDuty比の上昇速度を調整します。 加速度ボリュームは表示速度の変化率を調整するのに対し、adrateは表示速度に対する実速度の変化率を調整する項目であるといえます。 */ boolean viewduty = false; //(開発用)走行中、走行音名の代わりにDUTY比を表示します。 //モジュール論理選択 boolean relay = false; //true:LOW=リレーON(負論理),false:LOW=リレーOFF(正論理)で設計。使用するモジュールの論理により切り替えてください。 //boolean relay = true; // *********************************************** // 走行音データ領域 // データの追加や変更を行う場合は慎重に行ってください。 // *********************************************** // 走行音名称。16桁で指定すること const char* soundName[SOUNDNUM] = { " 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", "MITSUBISHI Chop.", "N1000 (SUS-IGBT)", "N1000 (SI-IGBT) ", " Toyo-IGBT(AE) " }; // 各パターンの各ノッチ位置での最高速度値を配列で保持。 // 左から順に 1ノッチ, 2ノッチ, 3ノッチ, 4ノッチ, 5ノッチ。 const int maxSpdDataBase[] PROGMEM = { 31, 46, 101, 111, 111, //パターン 1 201 41, 56, 101, 111, 111, //パターン 2 209,70-000,etc 41, 56, 111, 121, 121, //パターン 3 E231-0,500,etc 41, 56, 111, 121, 121, //パターン 4 E231-1000 41, 56, 111, 121, 121, //パターン 5 E233, etc 51, 66, 121, 131, 131, //パターン 6 KQ 2100 (SI-GTO) 51, 66, 121, 131, 131, //パターン 7 N1000 (SI-GTO) 51, 66, 121, 131, 131, //パターン 8 MITSUBISHI-GTO 51, 66, 121, 131, 131, //パターン 9 Toyo-GTO 51, 66, 121, 131, 131, //パターン10 Toyo-IGBT 41, 56, 101, 111, 111, //パターン11 Toei 5300(Akuma) 41, 56, 101, 111, 111, //パターン12 TOSHIBA 3LevIGBT 41, 56, 101, 111, 111, //パターン13 MITSUBISHI Chop. 51, 66, 121, 131, 131, //パターン14 N1000 (SUS-IGBT) 51, 66, 121, 131, 131, //パターン15 N1000 (SI-IGBT) 51, 66, 151, 161, 161 //パターン16 Toyo-IGBT(AE) }; //定加速度領域値。 //指定した速度まで一定の加速度を保ちます。 const int keepac[SOUNDNUM] = {37, 39, 39, 39, 39, 55, 55, 55, 55, 59, 50, 50, 45, 59, 55, 85}; //加速曲線。 //数値を大きくすると加速性能が良くなります。お好みで調整して下さい。 const float Aracurve[SOUNDNUM] = {1.2, 1.3, 1.4, 1.4, 1.5, 1.55, 1.55, 1.5, 1.5, 1.4, 1.4, 1.45, 1.45, 1.5, 1.5, 1.4}; //減速度。 //数値を大きくすると減速性能が良くなります。お好みで調整して下さい。 const float Arbcurve[SOUNDNUM] = {0.13, 0.15, 0.15, 0.15, 0.16, 0.14, 0.14, 0.14, 0.15, 0.17, 0.14, 0.14, 0.14, 0.14, 0.14, 0.17}; // 音データ。 // 設定可能周波数 150Hz~100kHz(値は整数で指定) // ~~~(例) パターン3の場合~~~ // 1行目 速度0~ 2の間:380Hzで音程変化なし // 2行目 速度3~11の間:380Hzから980Hzまで変化する //加速時用の音データ。 const int AsoundDataBase[] PROGMEM = { //パターン1 201 590, 590, 66, // 1 開始周波数 終了周波数 切替スピード 550, 980, 101, // 2 開始周波数 終了周波数 切替スピード -1, // 3 終了コード //パターン2 209,70-000,etc 230, 230, 2, // 1 開始周波数 終了周波数 切替スピード 90, 90, 3, // 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, 2, // 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 760, 760, 31, // 1 開始周波数 終了周波数 切替スピード 350, 1150, 101, // 2 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン6 KQ 2100 (SI-GTO) 350, 350, 2, // 1 開始周波数 終了周波数 切替スピード 390, 390, 3, // 2 開始周波数 終了周波数 切替スピード 440, 440, 4, // 3 開始周波数 終了周波数 切替スピード 490, 490, 5, // 4 開始周波数 終了周波数 切替スピード 530, 530, 6, // 5 開始周波数 終了周波数 切替スピード 580, 580, 7, // 6 開始周波数 終了周波数 切替スピード 650, 650, 8, // 7 開始周波数 終了周波数 切替スピード 700, 700, 9, // 8 開始周波数 終了周波数 切替スピード 780, 780, 23, // 9 開始周波数 終了周波数 切替スピード 510, 980, 111, //10 開始周波数 終了周波数 切替スピード 580, 580, 131, //11 開始周波数 終了周波数 切替スピード -1, //12 終了コード //パターン7 N1000 (SI-GTO) 350, 350, 2, // 1 開始周波数 終了周波数 切替スピード 390, 390, 3, // 2 開始周波数 終了周波数 切替スピード 440, 440, 4, // 3 開始周波数 終了周波数 切替スピード 490, 490, 5, // 4 開始周波数 終了周波数 切替スピード 530, 530, 6, // 5 開始周波数 終了周波数 切替スピード 580, 580, 7, // 6 開始周波数 終了周波数 切替スピード 650, 650, 8, // 7 開始周波数 終了周波数 切替スピード 700, 700, 9, // 8 開始周波数 終了周波数 切替スピード 780, 780, 16, // 9 開始周波数 終了周波数 切替スピード 950, 970, 18, //10 開始周波数 終了周波数 切替スピード 850, 970, 21, //11 開始周波数 終了周波数 切替スピード 800, 970, 23, //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, 19, // 1 開始周波数 終了周波数 切替スピード 490, 780, 31, // 2 開始周波数 終了周波数 切替スピード 420, 1250, 110, // 3 開始周波数 終了周波数 切替スピード 1250, 1250, 131, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン10 Toyo-IGBT 1050, 1050, 26, // 1 開始周波数 終了周波数 切替スピード 630, 1300, 41, // 2 開始周波数 終了周波数 切替スピード 650, 1150, 111, // 3 開始周波数 終了周波数 切替スピード 1150, 1150, 131, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン11 Toei 5300(Akuma) 780, 780, 11, // 1 開始周波数 終了周波数 切替スピード 450, 450, 16, // 2 開始周波数 終了周波数 切替スピード 500, 780, 22, // 3 開始周波数 終了周波数 切替スピード 430, 580, 25, // 4 開始周波数 終了周波数 切替スピード 400, 1150, 105, // 5 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 6 開始周波数 終了周波数 切替スピード -1, // 7 終了コード //パターン12 TOSHIBA 3LevIGBT 730, 730, 11, // 1 開始周波数 終了周波数 切替スピード 510, 730, 16, // 2 開始周波数 終了周波数 切替スピード 600, 1150, 101, // 3 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン13 MITSUBISHI Chop. 590, 590, 4, // 1 開始周波数 終了周波数 切替スピード 900, 900, 36, // 2 開始周波数 終了周波数 切替スピード 590, 590, 41, // 3 開始周波数 終了周波数 切替スピード 390, 390, 46, // 4 開始周波数 終了周波数 切替スピード 390, 1150, 101, // 5 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 6 開始周波数 終了周波数 切替スピード -1, // 7 終了コード //パターン14 N1000 (SUS-IGBT) 710, 710, 31, // 1 開始周波数 終了周波数 切替スピード 350, 1150, 101, // 2 開始周波数 終了周波数 切替スピード 1150, 1150, 131, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン15 N1000 (SI-IGBT) 1180, 1180, 16, // 1 開始周波数 終了周波数 切替スピード 1180, 1960, 33, // 2 開始周波数 終了周波数 切替スピード 460, 1150, 101, // 3 開始周波数 終了周波数 切替スピード 1150, 1150, 131, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン16 Toyo-IGBT(AE) 1000, 1000, 26, // 1 開始周波数 終了周波数 切替スピード 580, 1300, 41, // 2 開始周波数 終了周波数 切替スピード 650, 1150, 141, // 3 開始周波数 終了周波数 切替スピード 1150, 1150, 161, // 4 開始周波数 終了周波数 切替スピード -1 // 5 終了コード }; //減速時用の音データ。 const int BsoundDataBase[] PROGMEM = { //パターン1 201 10000, 10000, 6, // 1 開始周波数 終了周波数 切替スピード 590, 590, 61, // 2 開始周波数 終了周波数 切替スピード 590, 650, 76, // 3 開始周波数 終了周波数 切替スピード 550, 980, 101, // 4 開始周波数 終了周波数 切替スピード -1, // 5 終了コード //パターン2 209,70-000,etc 150, 150, 6, // 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 760, 760, 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, // 1 開始周波数 終了周波数 切替スピード 440, 460, 22, // 2 開始周波数 終了周波数 切替スピード 460, 570, 36, // 3 開始周波数 終了周波数 切替スピード 500, 1150, 105, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 121, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン12 TOSHIBA 3LevIGBT 730, 730, 11, // 1 開始周波数 終了周波数 切替スピード 510, 1150, 101, // 2 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン13 MITSUBISHI Chop. 900, 900, 36, // 1 開始周波数 終了周波数 切替スピード 590, 590, 41, // 2 開始周波数 終了周波数 切替スピード 390, 390, 46, // 3 開始周波数 終了周波数 切替スピード 390, 1150, 101, // 4 開始周波数 終了周波数 切替スピード 1150, 1150, 111, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン14 N1000 (SUS-IGBT) 710, 710, 31, // 1 開始周波数 終了周波数 切替スピード 350, 1150, 101, // 2 開始周波数 終了周波数 切替スピード 1150, 1150, 131, // 3 開始周波数 終了周波数 切替スピード -1, // 4 終了コード //パターン15 N1000 (SI-IGBT) 1180, 1180, 16, // 1 開始周波数 終了周波数 切替スピード 1180, 2050, 31, // 2 開始周波数 終了周波数 切替スピード 780, 1150, 36, // 3 開始周波数 終了周波数 切替スピード 560, 980, 111, // 4 開始周波数 終了周波数 切替スピード 580, 580, 131, // 5 開始周波数 終了周波数 切替スピード -1, // 6 終了コード //パターン16 Toyo-IGBT(AE) 1000, 1000, 26, // 1 開始周波数 終了周波数 切替スピード 600, 880, 41, // 2 開始周波数 終了周波数 切替スピード 600, 950, 95, // 3 開始周波数 終了周波数 切替スピード 950, 950, 106, // 4 開始周波数 終了周波数 切替スピード 580, 580, 161, // 5 開始周波数 終了周波数 切替スピード -1 // 6 終了コード }; // *********************************************** // ここから先はみだりに変更しないで下さい。 // 誤動作や故障の原因となります。 // *********************************************** const char *MOJI1 = "TSmascon PPUNIT"; // Welcomeメッセージ。LCD1段目。16桁にすること。 const char *MOJI2 = "Program Ver 3.00"; // Welcomeメッセージ。LCD2段目。16桁にすること。 //使用ピン指定 const int P_LIGHT_DIAL = A0; // Arduino接続ピン番号:(Analog) 常点灯調節ダイアル = A0 const int P_ACCEL_DIAL = A1; // Arduino接続ピン番号:(Analog) 加速率調節ダイアル = A1 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 = D11 (MOSI) const int P_BOSEN = 12; // Arduino接続ピン番号:(Digital) 母線リレー制御 = D12 (MISO) const int P_DIREC = 13; // Arduino接続ピン番号:(Digital) 方向リレー制御 = D13 (SCK) //const int P_BOSEN = 18; // Arduino接続ピン番号:(Digital) 母線リレー制御 = D18 (A4) //const int P_DIREC = 19; // Arduino接続ピン番号:(Digital) 方向リレー制御 = D19 (A5) //マスコン関連 boolean changeS = true; //走行音変更モード判定 boolean EB = false; //非常停止ボタン boolean masok = false; //ブレーキ投入判定 boolean revok = false; //レバーサ位置確認 const int MC_EB = 1; // マスコン位置 非常 const int MC_B8 = 2; // マスコン位置 制動8 const int MC_B7 = 3; // マスコン位置 制動7 const int MC_B6 = 4; // マスコン位置 制動6 const int MC_B5 = 5; // マスコン位置 制動5 const int MC_B4 = 6; // マスコン位置 制動4 const int MC_B3 = 7; // マスコン位置 制動3 const int MC_B2 = 8; // マスコン位置 制動2 const int MC_B1 = 9; // マスコン位置 制動1 const int MC_N = 10; // マスコン位置 惰行 const int MC_P1 = 11; // マスコン位置 力行1 const int MC_P2 = 12; // マスコン位置 力行2 const int MC_P3 = 13; // マスコン位置 力行3 const int MC_P4 = 14; // マスコン位置 力行4 const int MC_P5 = 15; // マスコン位置 力行5 const int MC_P6 = 16; // マスコン位置 力行5 const int RE_F = 1; // レバーサ位置 前 const int RE_N = 2; // レバーサ位置 切 const int RE_R = 3; // レバーサ位置 後 const int TS_1 = 1; //TSマスコン1(ワンハンドル) const int TS_2 = 2; //TSマスコン2(ツーハンドル) const int TYPE_A = 1; // TYPEA const int TYPE_B = 2; // TYPEB const int TYPE_C = 3; // TYPEC const int TYPE_D = 4; // TYPED const int TYPE_E = 5; // TYPEE const int TYPE_F = 6; // TYPEF const int TYPE_G = 7; // TYPEG const int TYPE_H = 8; // TYPEH // マスコン位置名称を保持する配列 const char* masconPosName[16] = {"EB", "B8", "B7", "B6", "B5", "B4", "B3", "B2", "B1", "N ", "P1", "P2", "P3", "P4", "P5", "P6"}; //レバーサ位置名称を保持する配列 const char* reverserPosName[3] = {"F", "N", "R"}; //マスコンタイプ名称を保持する配列 const char* typeName[8] = {"A", "B", "C", "D", "E", "F", "G", "H"}; //変数 const int MD_STOP = 1; // モード 停止 const int MD_BRAKE = 2; // モード 減速 const int MD_NTRL = 3; // モード 惰行 const int MD_ACCEL = 4; // モード 加速 int vvvfPtn; // 走行音パターン。 int disp_masconPos = MC_EB; // マスコンの位置(表示用)。加減速力確保の為、マスコンタイプによっては指定の段数より強い段数を内部処理用に指定することから。 int masconPos = MC_EB; // マスコンの位置を保持。1~16となる。 int reverserPos = RE_N; // レバーサの位置を保持。1~3となる。 int notchnum = 0; //マスコン段数 int brakenum = 0; //ブレーキ段数 int tstype = 0; //TSマスコン1or2 int type = 0; //マスコンタイプ int lightVol; // 常点灯ダイアルから拾った値を0~127に変換して保持する。変換処理はloop()内にて。 int accelVol; // 加速率ダイアルから拾った値を1~8に変換して保持する。変換処理はloop()内にて。 int kasoku; // 走行用出力する加減速率 int mode; // 走行状態。1=停止, 2=減速, 3=惰行, 4=加速 int ReceiveData[12] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; //受信データ格納用 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; // 指示スピード float duty; //duty比 float acurve; //加速曲線 float bcurve; //減速度 int i; // ループカウンター int j; // ループカウンター int k; // ループカウンター int maxSpdData[5]; int AsoundData[SDATANUM]; // 走行音パターンの各パターンデータ開始位置インデックスを保持。 // 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。 int AdataNum[SOUNDNUM + 1]; int BsoundData[SDATANUM]; // 走行音パターンの各パターンデータ開始位置インデックスを保持。 // 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。 int BdataNum[SOUNDNUM + 1]; // キャラクタ液晶 LiquidCrystal lcd(P_LCD_RS, P_LCD_EN, P_LCD_D4, P_LCD_D5, P_LCD_D6, P_LCD_D7); // ********************************************************************************************** // 関数定義 ここから // ********************************************************************************************** //マスコン及びレバーサ位置の表示 void dispPos() { lcd.setCursor(12, 1); lcd.print(reverserPosName[reverserPos - 1]); lcd.setCursor(14, 1); lcd.print(masconPosName[disp_masconPos - 1]); } //スケールスピード表示 void dispSpd() { int disp; disp = spd / 10000; lcd.setCursor(4, 1); if (disp / 100 > 0) { // 3digits. } else { if (disp / 10 > 0) { // 2digits. lcd.print(" "); } else { // 1digit. lcd.print(" "); } } lcd.print(disp); } //走行音設定用 void SetSound(){ lcd.clear(); i = 1; k = 0; //受信データ初期化 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } for(k=0;k<6;k++){ ReceiveData[k] = {0x00}; } k = 0; lcd.setCursor(0, 0); lcd.print(F("SET SoundPattern")); delay(3000); lcd.clear(); lcd.setCursor(0, 1); lcd.print(F("<A Enter:S C> ")); lcd.setCursor(0, 0); lcd.print(soundName[0]); //1番目の走行音データ名をさっさと表示 do{ Receive(4); } while(k == 0); vvvfPtn = i - 1; acurve = Aracurve[vvvfPtn]; bcurve = Arbcurve[vvvfPtn]; k = 0; for (i = vvvfPtn * 5; i < vvvfPtn * 5 + 5; i++) { maxSpdData[k] = pgm_read_word_near(&maxSpdDataBase[i]); k++; } notch1 = (long) maxSpdData[0] * 10000 - 1; notch2 = (long) maxSpdData[1] * 10000 - 1; notch3 = (long) maxSpdData[2] * 10000 - 1; notch4 = (long) maxSpdData[3] * 10000 - 1; notch5 = (long) maxSpdData[4] * 10000 - 1; stopSpd = STOPSPD * 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++; } changeS = false; // 決定したパターンを点滅表示 lcd.clear(); delay(180); lcd.setCursor(0, 0); lcd.print(F("SET SoundPattern")); lcd.setCursor(0, 1); lcd.print(soundName[vvvfPtn]); delay(2000); lcd.clear(); lcd.setCursor(1, 0); lcd.print(F("CATION:Push C")); lcd.setCursor(2, 1); lcd.print(F("in emergensy.")); delay(3000); lcd.clear(); //誤発進防止動作 do{ SetStatus(); }while(i == 0); lcd.clear(); lcd.setCursor(0, 0); lcd.print(F(" Start Controll ")); delay(1000); lcd.clear(); lcd.setCursor(0, 0); lcd.print(soundName[vvvfPtn]); lcd.setCursor(0, 1); if(tstype==TS_1){ lcd.print(F("< > km/h< >")); lcd.setCursor(1, 1); lcd.print(typeName[type - 1]); //TYPE表示もつける。無駄に。 } else{ lcd.print(F(" km/h< >")); } dispPos(); } //シリアル受信用。受信データを基にマスコン及びレバーサ位置の判定も同時に行う。 void Receive(int SETTYPE){ while (Serial.available() >= 6) { // コマンドを完全に受けた j = 0; do{ ReceiveData[j] = Serial.read(); if(ReceiveData[j] == 0x0D) { break; } if(j < 5) { j++; } else { j = 0; } } while (Serial.available() > 0); if(ReceiveData[0] == 0x54) { //ヘッダが先頭である if(ReceiveData[1] == 0x53) { //パケットを正常に受信している if(SETTYPE == 0) { //マスコン設定時 switch(ReceiveData[2]) { case 0x58: //Aボタン if(ReceiveData[3] == 0x39) { if(i == 0) { //一巡処理 i = 1; lcd.setCursor(0, 0); lcd.print(' '); lcd.setCursor(8, 0); lcd.print('>'); } else { i--; lcd.setCursor(0, 0); lcd.print('>'); lcd.setCursor(8, 0); lcd.print(' '); } } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { if(i == 1) { //一巡処理 i = 0; lcd.setCursor(0, 0); lcd.print('>'); lcd.setCursor(8, 0); lcd.print(' '); } else { i++; lcd.setCursor(0, 0); lcd.print(' '); lcd.setCursor(8, 0); lcd.print('>'); } } break; case 0x4B: //Sボタン tstype = i + 1; //マスコンタイプ決定 lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("SET Handle")); lcd.setCursor(4, 0); if(i==0){ lcd.print('1'); } else{ lcd.print('2'); } delay(2000); lcd.clear(); return; default: break; } } else if(SETTYPE == 1) { //(TSマスコン用)マスコンタイプ設定時 switch(ReceiveData[2]) { case 0x58: //Aボタン if(ReceiveData[3] == 0x39) { if(i == 0) { //一巡処理 i = 7; } else { i--; } lcd.setCursor(15, 0); lcd.print(typeName[i]); } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { if(i == 7) { //一巡処理 i = 0; } else { i++; } lcd.setCursor(15, 0); lcd.print(typeName[i]); } break; case 0x4B: //Sボタン type = i + 1; //マスコンタイプ決定 lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("SET TYPE")); lcd.setCursor(9, 0); lcd.print(typeName[i]); delay(2000); lcd.clear(); return; default: break; } } else if(SETTYPE == 2) { //(TSマスコン2用)マスコン段数設定 switch(ReceiveData[2]) { case 0x58: //Aボタン if(ReceiveData[3] == 0x39) { if(i == 3) { //一巡処理。(P3~P6までの範囲) i = 6; } else { i--; } lcd.setCursor(15, 0); lcd.print(i); } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { if(i == 6) { //一巡処理 i = 3; } else { i++; } lcd.setCursor(15, 0); lcd.print(i); } break; case 0x4B: //Sボタン notchnum = i; //マスコンタイプ決定 lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("SET MAX-NOTCH:P ")); lcd.setCursor(15, 0); lcd.print(i); delay(2000); lcd.clear(); return; default: break; } } else if(SETTYPE == 3) { //(TSマスコン2用)ブレーキ段数設定 switch(ReceiveData[2]) { case 0x58: //Aボタン if(ReceiveData[3] == 0x39) { if(i == 5) { //一巡処理。(B5~B8までの範囲) i = 8; } else { i--; } lcd.setCursor(15, 0); lcd.print(i); } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { if(i == 8) { //一巡処理 i = 5; } else { i++; } lcd.setCursor(15, 0); lcd.print(i); } break; case 0x4B: //Sボタン brakenum = i; //マスコンタイプ決定 lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("SET MAX-BRAKE:B ")); lcd.setCursor(15, 0); lcd.print(i); delay(2000); lcd.clear(); return; default: break; } } else if(SETTYPE == 4) { //走行音設定時 switch(ReceiveData[2]) { case 0x58: //Aボタン if(ReceiveData[3] == 0x39) { if(i == 1) { //一巡処理 i = SOUNDNUM; } else { i--; } lcd.setCursor(0, 0); lcd.print(soundName[i-1]); } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { if(i == SOUNDNUM) { //一巡処理 i = 1; } else { i++; } lcd.setCursor(0, 0); lcd.print(soundName[i-1]); } break; case 0x4B: //Sボタン if(ReceiveData[3] == 0x39) { k = 1; return; } break; default: break; } } else if(SETTYPE == 5) { //非常停止時 switch(ReceiveData[2]) { case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { EB = false; } return; default: break; } } else { //通常運転時 if(tstype == TS_1){//TSマスコン1用 switch(ReceiveData[2]) { case 0x42: switch(ReceiveData[3]) { case 0x32: //共通EB masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); break; case 0x33: //共通B8 if(type==TYPE_B || type==TYPE_D){ masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); } else if(type==TYPE_G){ masconPos = MC_B8; disp_masconPos = MC_B7; bosenON(); } else{ masconPos = MC_B8; disp_masconPos = MC_B8; bosenON(); } break; case 0x34: //共通B7 if(type==TYPE_B || type==TYPE_D || type==TYPE_G){ masconPos = MC_B8; disp_masconPos = MC_B7; bosenON(); } else{ masconPos = MC_B7; disp_masconPos = MC_B7; bosenON(); } break; default: //TYPEHの(P6)~(P8)はP5扱い masconPos = MC_P6; disp_masconPos = MC_P5; bosenON(); } break; case 0x45: //共通B6 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); } else if(type==TYPE_B || type==TYPE_D || type==TYPE_G){ masconPos = MC_B7; disp_masconPos = MC_B6; bosenON(); } else{ masconPos = MC_B6; disp_masconPos = MC_B6; bosenON(); } break; case 0x41: switch(ReceiveData[3]) { case 0x30: //共通B5 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B8; disp_masconPos = MC_B5; bosenON(); } else if(type==TYPE_G){ masconPos = MC_B6; disp_masconPos = MC_B5; bosenON(); } else { masconPos = MC_B5; disp_masconPos = MC_B5; bosenON(); } break; case 0x31: //共通B4 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B7; disp_masconPos = MC_B4; bosenON(); } else if(type==TYPE_G){ masconPos = MC_B5; disp_masconPos = MC_B4; bosenON(); } else { masconPos = MC_B4; disp_masconPos = MC_B4; bosenON(); } break; case 0x32: //共通B3 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B6; disp_masconPos = MC_B3; bosenON(); } else if(type==TYPE_G){ masconPos = MC_B4; disp_masconPos = MC_B3; bosenON(); } else { masconPos = MC_B3; disp_masconPos = MC_B3; bosenON(); } break; case 0x33: //共通B2 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B4; disp_masconPos = MC_B2; bosenON(); } else if(type==TYPE_G){ masconPos = MC_B3; disp_masconPos = MC_B2; bosenON(); } else { masconPos = MC_B2; disp_masconPos = MC_B2; bosenON(); } break; case 0x34: //共通B1 if(type==TYPE_A || type==TYPE_C || type==TYPE_G){ masconPos = MC_B2; disp_masconPos = MC_B1; bosenON(); } else { masconPos = MC_B1; disp_masconPos = MC_B1; bosenON(); } break; case 0x35: switch(ReceiveData[4]) { case 0x30: //共通N masconPos = MC_N; disp_masconPos = MC_N; bosenON(); break; case 0x35: //共通P1 masconPos = MC_P2; disp_masconPos = MC_P1; bosenON(); break; } break; case 0x36: //共通P2 masconPos = MC_P3; disp_masconPos = MC_P2; bosenON(); break; case 0x37: //共通P3 if(type==TYPE_B){ masconPos = MC_P5; disp_masconPos = MC_P3; bosenON(); } else { masconPos = MC_P4; disp_masconPos = MC_P3; bosenON(); } break; case 0x38: //共通P4 if(type==TYPE_A || type==TYPE_D || type==TYPE_E){ masconPos = MC_P6; disp_masconPos = MC_P4; bosenON(); } else { masconPos = MC_P5; disp_masconPos = MC_P4; bosenON(); } break; case 0x39: //共通P5 masconPos = MC_P6; disp_masconPos = MC_P5; bosenON(); break; } break; case 0x47: //レバーサ switch(ReceiveData[3]) { case 0x39: //前 reverserPos = RE_F; forward(); bosenON(); break; case 0x35: //切 bosenOFF(); reverserPos = RE_N; break; case 0x30: //後 reverserPos = RE_R; reverse(); bosenON(); break; } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { bosenOFF(); spd = 0; mode = MD_STOP; masconPos = MC_EB; reverserPos = RE_N; EB = true; return; } break; case 0x4B: //Sボタン if(ReceiveData[3] == 0x39) { if(spd == 0) { //走行音設定へ bosenOFF(); changeS = true; return; } } break; //default: } } else{//TSマスコン2用 switch(ReceiveData[2]) { case 0x42: switch(ReceiveData[3]) { case 0x32: //共通EB masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); break; case 0x33: //共通B8 if(brakenum <= 7){ masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); } else{ masconPos = MC_B8; disp_masconPos = MC_B8; bosenON(); } break; case 0x34: //共通B7 if(brakenum <= 6){ masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); } else if(brakenum == 7){ masconPos = MC_B8; disp_masconPos = MC_B7; bosenON(); } else{ masconPos = MC_B7; disp_masconPos = MC_B7; bosenON(); } break; default: //P6 masconPos = MC_P6; disp_masconPos = MC_P6; bosenON(); } break; case 0x45: //共通B6 if(brakenum == 5){ masconPos = MC_EB; disp_masconPos = MC_EB; bosenON(); } else if(brakenum == 6){ masconPos = MC_B8; disp_masconPos = MC_B6; bosenON(); } else{ masconPos = MC_B6; disp_masconPos = MC_B6; bosenON(); } break; case 0x41: switch(ReceiveData[3]) { case 0x30: //共通B5 if(brakenum == 5){ masconPos = MC_B8; disp_masconPos = MC_B5; bosenON(); } else if(brakenum == 6){ masconPos = MC_B7; disp_masconPos = MC_B5; bosenON(); } else { masconPos = MC_B5; disp_masconPos = MC_B5; bosenON(); } break; case 0x31: //共通B4 if(brakenum == 5){ masconPos = MC_B7; disp_masconPos = MC_B4; bosenON(); } else if(brakenum == 6){ masconPos = MC_B5; disp_masconPos = MC_B4; bosenON(); } else { masconPos = MC_B4; disp_masconPos = MC_B4; bosenON(); } break; case 0x32: //共通B3 if(brakenum == 5){ masconPos = MC_B6; disp_masconPos = MC_B3; bosenON(); } else if(brakenum == 6){ masconPos = MC_B4; disp_masconPos = MC_B3; bosenON(); } else { masconPos = MC_B3; disp_masconPos = MC_B3; bosenON(); } break; case 0x33: //共通B2 if(brakenum == 5){ masconPos = MC_B4; disp_masconPos = MC_B2; bosenON(); } else { masconPos = MC_B2; disp_masconPos = MC_B2; bosenON(); } break; case 0x34: //共通B1 if(brakenum == 5){ masconPos = MC_B2; disp_masconPos = MC_B1; bosenON(); } else { masconPos = MC_B1; disp_masconPos = MC_B1; bosenON(); } break; case 0x35: switch(ReceiveData[4]) { case 0x30: //共通N masconPos = MC_N; disp_masconPos = MC_N; bosenON(); break; case 0x35: //共通P1 if(notchnum == 6){ masconPos = MC_P1; disp_masconPos = MC_P1; bosenON(); } else{ masconPos = MC_P2; disp_masconPos = MC_P1; bosenON(); } break; } break; case 0x36: //共通P2 if(notchnum == 6){ masconPos = MC_P2; disp_masconPos = MC_P2; bosenON(); } else{ masconPos = MC_P3; disp_masconPos = MC_P2; bosenON(); } break; case 0x37: //共通P3 if(notchnum == 6){ masconPos = MC_P3; disp_masconPos = MC_P3; bosenON(); } else if(notchnum == 3){ masconPos = MC_P5; disp_masconPos = MC_P3; bosenON(); } else{ masconPos = MC_P4; disp_masconPos = MC_P3; bosenON(); } break; case 0x38: //共通P4 if(notchnum == 6){ masconPos = MC_P4; disp_masconPos = MC_P4; bosenON(); } else if(notchnum == 4){ masconPos = MC_P6; disp_masconPos = MC_P4; bosenON(); } else{ masconPos = MC_P5; disp_masconPos = MC_P4; bosenON(); } break; case 0x39: //共通P5 if(notchnum == 6){ masconPos = MC_P5; disp_masconPos = MC_P5; bosenON(); } else{ masconPos = MC_P6; disp_masconPos = MC_P5; bosenON(); } break; } break; case 0x47: //レバーサ switch(ReceiveData[3]) { case 0x39: //前 reverserPos = RE_F; forward(); bosenON(); break; case 0x35: //切 bosenOFF(); reverserPos = RE_N; break; case 0x30: //後 reverserPos = RE_R; reverse(); bosenON(); break; } break; case 0x5A: //Cボタン if(ReceiveData[3] == 0x39) { bosenOFF(); spd = 0; mode = MD_STOP; masconPos = MC_EB; reverserPos = RE_N; EB = true; return; } break; case 0x4B: //Sボタン if(ReceiveData[3] == 0x39) { if(spd == 0) { //走行音設定へ bosenOFF(); changeS = true; return; } } break; //default: } } } } } } } //誤発進防止(始業検査のようなもの)。TSマスコンの現在ノッチ位置、レバーサ位置の状態を確認しパワーパック起動時の急発進を防ぐ。 void SetStatus(){ i = 0; for(k=0;k<6;k++){ ReceiveData[k] = {0x00}; } k = 0; //受信データ初期化 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } //ステータス送信要求 do{ Serial.write(0x0D); } while (Serial.available() >= 12); //受信完了まで待機 delay(50); for(j = 0; j < 12; j++){ ReceiveData[j] = Serial.read(); } if(ReceiveData[0] == 0x54) { //ヘッダが先頭である if(ReceiveData[1] == 0x53) { //パケットを正常に受信している if(tstype == TS_1){//TSマスコン1用 switch(ReceiveData[2]) { case 0x42: switch(ReceiveData[3]) { case 0x32: //共通EB masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; break; case 0x33: //共通B8 if(type==TYPE_B || type==TYPE_D){ masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; } else if(type==TYPE_G){ masconPos = MC_B8; disp_masconPos = MC_B7; masok = true; } else{ masconPos = MC_B8; disp_masconPos = MC_B8; masok = true; } break; case 0x34: //共通B7 if(type==TYPE_B || type==TYPE_D || type==TYPE_G){ masconPos = MC_B8; disp_masconPos = MC_B7; masok = true; } else{ masconPos = MC_B7; disp_masconPos = MC_B7; masok = true; } break; default: masok = false; } break; case 0x45: //共通B6 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; } else if(type==TYPE_B || type==TYPE_D || type==TYPE_G){ masconPos = MC_B7; disp_masconPos = MC_B6; masok = true; } else{ masconPos = MC_B6; disp_masconPos = MC_B6; masok = true; } break; case 0x41: switch(ReceiveData[3]) { case 0x30: //共通B5 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B8; disp_masconPos = MC_B5; masok = true; } else if(type==TYPE_G){ masconPos = MC_B6; disp_masconPos = MC_B5; masok = true; } else { masconPos = MC_B5; disp_masconPos = MC_B5; masok = true; } break; case 0x31: //共通B4 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B7; disp_masconPos = MC_B4; masok = true; } else if(type==TYPE_G){ masconPos = MC_B5; disp_masconPos = MC_B4; masok = true; } else { masconPos = MC_B4; disp_masconPos = MC_B4; masok = true; } break; case 0x32: //共通B3 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B6; disp_masconPos = MC_B3; masok = true; } else if(type==TYPE_G){ masconPos = MC_B4; disp_masconPos = MC_B3; masok = true; } else { masconPos = MC_B3; disp_masconPos = MC_B3; masok = true; } break; case 0x33: //共通B2 if(type==TYPE_A || type==TYPE_C){ masconPos = MC_B4; disp_masconPos = MC_B2; masok = true; } else if(type==TYPE_G){ masconPos = MC_B3; disp_masconPos = MC_B2; masok = true; } else { masconPos = MC_B2; disp_masconPos = MC_B2; masok = true; } break; case 0x34: //共通B1 if(type==TYPE_A || type==TYPE_C || type==TYPE_G){ masconPos = MC_B2; disp_masconPos = MC_B1; masok = true; } else { masconPos = MC_B1; disp_masconPos = MC_B1; masok = true; } break; default: masok = false; } break; default: masok = false; } if(ReceiveData[6] == 0x54) { //ヘッダが先頭である if(ReceiveData[7] == 0x53) { //パケットを正常に受信している switch(ReceiveData[8]) { case 0x47: //レバーサ switch(ReceiveData[9]) { case 0x39: //前 reverserPos = RE_F; revok = true; break; case 0x35: //切 reverserPos = RE_N; revok = true; break; case 0x30: //後 reverserPos = RE_R; revok = true; break; default: revok = false; } break; default: revok = false; } } } } else{ //TSマスコン2用 switch(ReceiveData[2]) { case 0x42: switch(ReceiveData[3]) { case 0x32: //共通EB masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; break; case 0x33: //共通B8 if(brakenum <= 7){ masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; } else{ masconPos = MC_B8; disp_masconPos = MC_B8; masok = true; } break; case 0x34: //共通B7 if(brakenum <= 6){ masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; } else if(brakenum == 7){ masconPos = MC_B8; disp_masconPos = MC_B7; masok = true; } else{ masconPos = MC_B7; disp_masconPos = MC_B7; masok = true; } break; default: //P6 masok = false; } break; case 0x45: //共通B6 if(brakenum == 5){ masconPos = MC_EB; disp_masconPos = MC_EB; masok = true; } else if(brakenum == 6){ masconPos = MC_B8; disp_masconPos = MC_B6; masok = true; } else{ masconPos = MC_B6; disp_masconPos = MC_B6; masok = true; } break; case 0x41: switch(ReceiveData[3]) { case 0x30: //共通B5 if(brakenum == 5){ masconPos = MC_B8; disp_masconPos = MC_B5; masok = true; } else if(brakenum == 6){ masconPos = MC_B7; disp_masconPos = MC_B5; masok = true; } else { masconPos = MC_B5; disp_masconPos = MC_B5; masok = true; } break; case 0x31: //共通B4 if(brakenum == 5){ masconPos = MC_B7; disp_masconPos = MC_B4; masok = true; } else if(brakenum == 6){ masconPos = MC_B5; disp_masconPos = MC_B4; masok = true; } else { masconPos = MC_B4; disp_masconPos = MC_B4; masok = true; } break; case 0x32: //共通B3 if(brakenum == 5){ masconPos = MC_B6; disp_masconPos = MC_B3; masok = true; } else if(brakenum == 6){ masconPos = MC_B4; disp_masconPos = MC_B3; masok = true; } else { masconPos = MC_B3; disp_masconPos = MC_B3; masok = true; } break; case 0x33: //共通B2 if(brakenum == 5){ masconPos = MC_B4; disp_masconPos = MC_B2; masok = true; } else { masconPos = MC_B2; disp_masconPos = MC_B2; masok = true; } break; case 0x34: //共通B1 if(brakenum == 5){ masconPos = MC_B2; disp_masconPos = MC_B1; masok = true; } else { masconPos = MC_B1; disp_masconPos = MC_B1; masok = true; } break; default: masok = false; } break; default: masok = false; } if(ReceiveData[6] == 0x54) { //ヘッダが先頭である if(ReceiveData[7] == 0x53) { //パケットを正常に受信している switch(ReceiveData[8]) { case 0x47: //レバーサ switch(ReceiveData[9]) { case 0x39: //前 reverserPos = RE_F; revok = true; break; case 0x35: //切 reverserPos = RE_N; revok = true; break; case 0x30: //後 reverserPos = RE_R; revok = true; break; default: revok = false; } break; default: revok = false; } } } } } } else{ masok = false; revok = false; } if (masok == false){ lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("Pls Apply BRAKE.")); } for(k=0;k<12;k++){ ReceiveData[k] = {0x00}; } k = 0; if (masok == true && revok == true){ i = 1; if(reverserPos == RE_R){ reverse(); } else if(reverserPos == RE_F){ forward(); } else{ bosenOFF(); } } delay(50); } //加速度調整。現在速度が0km/hのとき1倍、各ノッチの最高速度以上のとき0倍になるよう調整。 void kaccel(int notch) { float rate, rate2; switch(notch){ case 1: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch1 - spd > 0) { //各ノッチの最高速度未満 rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度以上 rate = 0; } kasoku = accelVol * ACCEL_RATIO * 0.2 * rate; break; case 2: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch1 - spd > 0) { //各ノッチの最高速度未満 rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度以上 rate = 0; } kasoku = accelVol * ACCEL_RATIO * 0.3 * rate; break; case 3: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch2 - spd > 0) { //各ノッチの最高速度以下 rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度未満 rate = 0; } kasoku = accelVol * ACCEL_RATIO * 0.5 * rate; break; case 4: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch3 - spd > 0) { //各ノッチの最高速度未満 rate = ((float)keepac[vvvfPtn]*10000 / (float)spd) * 2.0; rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度以上 rate = 0; } kasoku = accelVol * ACCEL_RATIO * 0.7 * rate; break; case 5: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch4 - spd > 0) { //各ノッチの最高速度未満 rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度以上 rate = 0; } kasoku = accelVol * ACCEL_RATIO * 0.9 * rate; break; case 6: if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下 rate = 1; } else if (notch5 - spd > 0) { //各ノッチの最高速度未満 rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd; rate = pow(rate2, acurve); } else { //各ノッチの最高速度以上 rate = 0; } kasoku = accelVol * ACCEL_RATIO * rate; break; } } //減速度。現在速度が0km/hのとき1倍、P5の最高速度のとき約0.3倍になるよう調整。 void kbrake(int notch) { float rate; if (spd == 0) { //0km/h rate = 1; } else{ rate = 1 - sqrt((float)((spd * bcurve) / notch5)); } kasoku = accelVol * BRAKE_RATIO * notch * rate; } //緊急停止 void stopEB() { //受信データ初期化 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } for(k=0;k<6;k++){ ReceiveData[k] = {0x00}; } k = 0; lcd.clear(); lcd.setCursor(2, 0); lcd.print(F("NOW STOPPING")); lcd.setCursor(0, 1); lcd.print(F("To Reset:Push C")); do { Receive(5); } while (EB == true); //誤発進防止動作 do { SetStatus(); } while (i == 0); lcd.clear(); lcd.setCursor(0, 0); lcd.print(F("ReStart Controll")); delay(1000); lcd.clear(); lcd.setCursor(0, 0); lcd.print(soundName[vvvfPtn]); lcd.setCursor(0, 1); if(tstype==TS_1){ lcd.print(F("< > km/h< >")); lcd.setCursor(1, 1); lcd.print(typeName[type - 1]); //TYPE表示もつける。無駄に。 } else{ lcd.print(F(" km/h< >")); } dispPos(); } //母線引き通し(線路出力制御用)リレー制御 void bosenON() { //リレーON if(reverserPos != RE_N) { if(relay==true) { digitalWrite(P_BOSEN, LOW); } else { digitalWrite(P_BOSEN, HIGH); } } } void bosenOFF() { //リレーOFF if(relay==true) { digitalWrite(P_BOSEN, HIGH); } else { digitalWrite(P_BOSEN, LOW); } } //方向制御 void forward() { //リレーOFF if(relay==true) { digitalWrite(P_DIREC, HIGH); } else { digitalWrite(P_DIREC, LOW); } } void reverse() { //リレーON if(relay==true) { digitalWrite(P_DIREC, LOW); } else { digitalWrite(P_DIREC, HIGH); } } //常点灯および走行制御 void pwm(){ //常点灯用と走行用 TCCR2A = B10100001; TCCR2B = B00000001; OCR2B = (unsigned int)(255 * ((float)(lightVol * lmax) / 10230.0)); OCR2A = (unsigned int)(255 * duty); } //走行音制御 void spwm(int stype){ //走行音用 TCCR1A = B00100001; TCCR1B = B00010001; if(stype == 0){ OCR1A = (unsigned int)(8000000 / Afrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (unsigned int)(8000000 / Afrq / 100); } else if(stype == 1){ OCR1A = (unsigned int)(8000000 / Bfrq); // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq OCR1B = (unsigned int)(8000000 / Bfrq / 100); } else{ OCR1A = (unsigned int)400; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / 20000 = 400 OCR1B = (unsigned int)(1); } } // *********************************************** // setup関数。最初に1度だけ実行。 // *********************************************** void setup() { // PWM PIN設定 pinMode(P_PWM2A, OUTPUT); // 常点灯用PWM出力 pinMode(P_PWM1B, OUTPUT); // 走行音(VVVF音)用PWM出力 pinMode(P_PWM2B, OUTPUT); // 走行用PWM出力 //リレー制御用 pinMode(P_BOSEN, OUTPUT); // 母線引き通し(線路出力制御用)リレー pinMode(P_DIREC, OUTPUT); // 方向指示リレー bosenOFF(); // 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); lcd.clear(); // 走行音パターン各開始位置取得処理。 // soundData配列のうち、各パターンの最初の値となる配列インデックスをdataNumで保持する。 AdataNum[0] = 0; BdataNum[0] = 0; k = 0; for (i=1; i<=SOUNDNUM; i++) { while (pgm_read_word_near(&AsoundDataBase[k]) != -1) { k++; } k++; AdataNum[i] = k; } k = 0; for (i=1; i<=SOUNDNUM; i++) { while (pgm_read_word_near(&BsoundDataBase[k]) != -1) { k++; } k++; BdataNum[i] = k; } k = 0; for (i = 0; i<SDATANUM; i++) { AsoundData[k] = 0; k++; } k = 0; for (i = 0; i<SDATANUM; i++) { BsoundData[k] = 0; k++; } lcd.home(); // 1文字目、1行目 //TSマスコン1(ワンハンドル)とTSマスコン2(ツーハンドル)の選択 lcd.clear(); delay(180); lcd.setCursor(0, 0); lcd.print(F("SELECT TSMASCON.")); delay(480); lcd.clear(); i = 0; Serial.begin(19200); delay(480); lcd.setCursor(0, 0); lcd.print(F(">1Handle 2Handle")); lcd.setCursor(0, 1); lcd.print(F("<A Enter:S C> ")); //いきなり決定してしまう可能性を排除。 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } do { Receive(0); } while(tstype == 0); if(tstype==TS_1){ //マスコンタイプの設定 lcd.clear(); delay(180); lcd.setCursor(0, 0); lcd.print(F("SET MASCONTYPE: ")); lcd.setCursor(0, 1); lcd.print(F("<A Enter:S C> ")); i = 0; //いきなり決定してしまう可能性を排除。 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } lcd.setCursor(15, 0); lcd.print(typeName[0]); //初期状態のTYPE Aを表示しておく do { Receive(1); } while(type == 0); } else{ lcd.clear(); delay(180); lcd.setCursor(0, 0); lcd.print(F("SET MAX-NOTCH:P ")); lcd.setCursor(0, 1); lcd.print(F("<A Enter:S C> ")); delay(480); i = 3; //いきなり決定してしまう可能性を排除。 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } lcd.setCursor(15, 0); lcd.print('3'); //最小であるP3を表示しておく do { Receive(2); //マスコン段数設定 } while(notchnum == 0); lcd.clear(); delay(180); lcd.setCursor(0, 0); lcd.print(F("SET MAX-BRAKE:B ")); lcd.setCursor(0, 1); lcd.print(F("<A Enter:S C> ")); delay(480); i = 5; //いきなり決定してしまう可能性を排除。 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } lcd.setCursor(15, 0); lcd.print('5'); //最小であるB5を表示しておく do { Receive(3); //ブレーキ段数設定 } while(brakenum == 0); } SetSound(); //初期化 while (Serial.available() > 0) { // 受信したデータが存在する Serial.read(); } } // *********************************************** // *********************************************** // loop関数 // *********************************************** // *********************************************** void loop() { //動作速度確保のため、音データをメモリに格納。 accelVol = analogRead(P_ACCEL_DIAL) / 32 + 1; // analogRead 0~1023 → 1~16 lightVol = analogRead(P_LIGHT_DIAL); // analogRead 0~1023 Receive(6); dispPos(); switch (masconPos) { case MC_EB: // 非常 orderSpd = 0; kbrake(9); mode = MD_NTRL; break; case MC_B8: // 制動8 orderSpd = 0; kbrake(8); mode = MD_BRAKE; break; case MC_B7: // 制動7 orderSpd = 0; kbrake(7); mode = MD_BRAKE; break; case MC_B6: // 制動6 orderSpd = 0; kbrake(6); mode = MD_BRAKE; break; case MC_B5: // 制動5 orderSpd = 0; kbrake(5); mode = MD_BRAKE; break; case MC_B4: // 制動4 orderSpd = 0; kbrake(4); mode = MD_BRAKE; break; case MC_B3: // 制動3 orderSpd = 0; kbrake(3); mode = MD_BRAKE; break; case MC_B2: // 制動2 orderSpd = 0; kbrake(2); mode = MD_BRAKE; break; case MC_B1: // 制動1 orderSpd = 0; kbrake(1); mode = MD_BRAKE; break; case MC_N: // 惰行 orderSpd = 0; kasoku = accelVol * BRAKE_RATIO; mode = MD_NTRL; break; case MC_P1: // 力行1 orderSpd = notch1; kaccel(1); mode = MD_ACCEL; break; case MC_P2: // 力行2 orderSpd = notch1; kaccel(2); mode = MD_ACCEL; break; case MC_P3: // 力行3 orderSpd = notch2; kaccel(3); mode = MD_ACCEL; break; case MC_P4: // 力行4 orderSpd = notch3; kaccel(4); mode = MD_ACCEL; break; case MC_P5: // 力行5 orderSpd = notch4; kaccel(5); mode = MD_ACCEL; break; case MC_P6: // 力行6 orderSpd = notch5; kaccel(6); mode = MD_ACCEL; break; } for ( k = 0; k < ANTI_CHAT_RATE; k++ ) { // チャタリング防止ループ if (EB == true) { break; } 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; } duty = ((float)spd * adrate / 4000000.0)* sscale; if (duty > 1.0){ duty = 1.0; } if(viewduty == true){ lcd.setCursor(0, 0); lcd.print("DUTY= "); lcd.setCursor(5, 0); lcd.print(duty); } 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 && AsttFrq == -1) { // 走行音が設定されていない場合の処理 pwm(); spwm(2); break; } if (mode == MD_BRAKE) { // 変調モード切替 break; } if (mode == MD_STOP) { // 変調モード切替 break; } if (mode == MD_NTRL) { // 変調モード切替 break; } if (AsttFrq == -1) { // 高速時 VVVF音停止 pwm(); spwm(2); break; } // VVVF音 if ((spd >= AsttSpd * 10000) && (spd < AendSpd * 10000)) { Afrq = ( AendFrq * 10 - AsttFrq * 10 ) / ( AendSpd - AsttSpd ) * ( spd - AsttSpd * 10000 ) / 100000 + AsttFrq; if ( Afrq < 50 ) Afrq = 50; else if ( Afrq > 100000 ) Afrq = 100000; pwm(); spwm(0); 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) { // 走行音が設定されていない場合の処理 pwm(); spwm(2); break; } if (mode == MD_ACCEL) { // 変調モード切替 break; } if (mode == MD_STOP) { // 変調モード切替 break; } if (mode == MD_NTRL) { // 変調モード切替 break; } if (BsttFrq == -1) { // 高速時 VVVF音停止 pwm(); spwm(2); break; } // VVVF音 if ((spd >= BsttSpd * 10000) && (spd < BendSpd * 10000)) { Bfrq = ( BendFrq * 10 - BsttFrq * 10 ) / ( BendSpd - BsttSpd ) * ( spd - BsttSpd * 10000 ) / 100000 + BsttFrq; if ( Bfrq < 50 ) Bfrq = 50; else if ( Bfrq > 100000 ) Bfrq = 100000; pwm(); spwm(1); break; } } } else if (mode == MD_NTRL) { // 惰行・非常 pwm(); spwm(2); } else { // 停止時 音停止 pwm(); //走行音用 TCCR1A = B00000001; TCCR1B = B00010001; } dispSpd(); } if (changeS == true) { SetSound(); } if (EB == true) { stopEB(); } }