※このバージョンは最新版ではありません。

 

<更新内容>

(V2.00)

・速度に応じて加速度が変わる様に変更し、より実車に近い挙動になりました。

・音データを3種追加しました。

→新1000形(シーメンスIGBT)、新1000形(三菱IGBT)、新AE形

・その他、挙動の問題を一部修正

(V2.10)

・音データが正常に出力されないことがあるバグを修正

 

(V2.20)

・マスコン(ロータリSW)のチャタリング対策を強化

 

音データを追加しましたので、PPO for PCも併せて更新しております。

パワーパックの動作確認にご利用頂けます。自己責任でご利用ください。

PPO for PC(V1.30)

 

加速度の詳細

(以下における[km/h]はスケールスピードとします)

0km/h時の加速度及び減速度(V1.20以前の加速度)を1として、速度が上がるごとに加速度及び減速度が減衰するよう、プログラムを修正しました。

この修正により、模型の運転感覚がより実車に近づいています。

 

<加速度について>

音データごとに定加速度領域が設定可能になりました。定加速度の終わる速度をコードに入力して設定します。

定加速度領域を超えると、速度が上がるにつれて

f(x) = x^(3/2) (xはスケールスピード)

の曲線に沿って加速度が減衰していきます。

そのため、最高速度に対して定加速度領域が低すぎると加速できなくなりますので注意してください。

※あくまで実車の「ような」挙動です。このパワーパックでは簡単の為、定出力領域と特性領域をまとめて適当な曲線に置き換えています。

 

<減速度について>

速度が下がるにつれて、

g(x) = 1 ー ((x*0.15) / (P5の最高速度))^(1/2) (xはスケールスピード)

の曲線に沿って減速度が増加していきます。つまり適当。

スケールスピードにおいて、加速度を3.5km/h/sに合わせたとき120km/hから約560m~570mで止まるよう調整するとこのくらいになりました。

 

式だと非常にわかりにくいので、グラフを用意しました。なおこのグラフは新1000形(シーメンスGTO)の設定値です。

※「当パワーパックの」設定値です!実車の性能ではありません!

 

ソースコード

/*
 ***************************************************************************************
  WhiteBear Controller Next Generation
 ***************************************************************************************
  ・PWM制御による鉄道模型用コントローラー。
  ・12段階のロータリースイッチを用い、ワンハンドルマスコンを模擬する。
  ・マスコンはP5-B5 + EB の12段。
  ・各種状況はキャラクタLCD(16文字x2段)で表示。
  ・力行各段の最高速度を設定可能。設定はソースコード内であらかじめ設定。
  ・ノッチによる加速度の違いをついでに再現。
  ・速度に応じた加速度の変化もだいたい再現。
  ・PWM周波数の変調により、VVVFなど制御機器の音を再現。
  ・変調音は最大255種類までプログラム可能。
  ・加速時と減速時で異なる変調音を設定可能。
 ・外部制御モードを追加。ノッチ反応速度と走行音を外部から変更可能に。
 ***************************************************************************************
更新履歴
2019/07/30 V1.00 初期版。
2019/10/11 V1.10 音データをフラッシュメモリ格納に変更し、RAM不足を解消。
2020/01/30 V1.20 USBによる外部制御モードの追加
2020/02/21 V2.00 速度制御方式の変更(加速度変動機能)及びバグ修正
2020/04/25 V2.10 指定した周波数で出力されないことがあるバグを修正
2020/05/10 V2.20 マスコンのチャタリング対策を強化
 ***************************************************************************************
*/

#include <avr/io.h> // ATmega328P用のヘッダファイル
#include <avr/pgmspace.h>
#include <LiquidCrystal.h> // キャラクタ液晶ディスプレイ制御用のヘッダファイル

// ***********************************************
// 定数・変数定義 ここから
// ***********************************************

#define SOUNDNUM 16 //走行音データの数
#define STOPSPD 9 //ピタッと止めるための値。
#define SDATANUM 45 //3n(n=最も行数が多い音データの行数)以上の値に設定して下さい。あまり数字を大きくし過ぎるとメモリ不足になるため、むやみに大きくしないように。
const char *MOJI1 = " WCNG POWERPACK ";  // Welcomeメッセージ。LCD1段目。16桁にすること。
const char *MOJI2 = "Program Ver 2.20";  // 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 = 8;    // (加速率調整小型ボリュームからの入力値) × BRAKE_RATIO × マスコンブレーキ値(1~5) ⇒ 減速率
const int MAS_CHAT = 80; //ロータリスイッチが中間で止まった時の異常な指令を回避する。EBに投入した時のアナログ値を少し下回る値を設定。

int UNTI_CHAT_RATE; // チャタリング防止ループの値。ノッチの反応速度調整に使用。USB接続では動的設定可能。


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 isUSB;      // 外部制御モードのときTrue。
int vvvfPtn;        // 走行音パターン。
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表示用スピード値

int Result; //データ受信状況
int ReceiveData[] = {255, 255, 0}; //外部制御モード時に受け取った値
/*
送受信パケットは以下の様に定義する。
・送受信はHEX(16進)で行う。
・送信パケット=0,1,2の1バイト。0=正常受信,1=非常ノッチ未投入,2=受信失敗。
・受信パケット=(ヘッダ)(チャタリング防止ループ値)(走行音データ番号) の3バイト。ヘッダ=0
・チャタリング防止ループ値、走行音データ番号の値は1~255。
・よって走行音データは(理論上は)255種類まで搭載可能だが、増やせば当然メモリ食うので現実には限界がある。
・走行音は用意したデータ数より大きい番号を指定しないように送信側で制御を行うこと。
*/

// 走行音名称
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ノッチ。
int maxSpdData[] = {
  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)
};

//定加速度領域値。
int keepac[SOUNDNUM] = {37, 39, 39, 39, 39, 55, 55, 55, 55, 59, 50, 50, 45, 59, 55, 85};

// 走行用の出力を止める速度値を配列で保持。ピタッと止めるための値。
// 環境に合わせて適宜調整して下さい。
int stopSpdData[SOUNDNUM];

// 音データ。
// 設定可能周波数 123Hz~8000kHz
// DUTY比の分解能確保および低速時のトルク確保のため10kHzぐらいを上限。整数に限る。
// ~~~(例) パターン3の場合~~~
// 1行目 速度0~ 3の間:380Hzで音程変化なし
// 2行目 速度4~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 終了コード

};

int AsoundData[SDATANUM];

// 走行音パターンの各パターンデータ開始位置インデックスを保持。
// 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。
int AdataNum[SOUNDNUM + 1];

//減速時用の音データ。
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
  120, 120, 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 終了コード

};

int BsoundData[SDATANUM];

// 走行音パターンの各パターンデータ開始位置インデックスを保持。
// 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。
int BdataNum[SOUNDNUM + 1];

// ***********************************************
// 定数・変数定義 ここまで
// ***********************************************


// **********************************************************************************************
// 関数定義 ここから
// **********************************************************************************************

// ***********************************************
// マスコン位置を検出する。
// ロータリースイッチからのボリューム値を拾い、
// 適度な値に変換する処理。
// 
// 引数: アナログピンから取得した値(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型に変換。

  if(analogRead(P_MASCON)>MAS_CHAT){
  ans = (int)(((double) analogRead(P_MASCON)) / (1024 / 12) + 0.5);
  return ans;
  }
}

// ***********************************************
// マスコン位置を表示。LCDの右下2桁へ。
// ついでにスピード値も表示。なんとなくKATO103系の
// 動力ユニットでスケールスピード(?)
// 動力ユニットによって同じ電圧でも実速度が異なる
// ので、あくまでも目安値。
// ***********************************************
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);

}

//外部制御用
void SetSound(byte CHAT, byte VVVF){
  // 走行音パターンほか選択したパターンに合わせて設定
  UNTI_CHAT_RATE = (int)CHAT;
  vvvfPtn = (int)VVVF  - 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);
}

//シリアル受信用
int Receive(){

  while (Serial.available() > 0) { // 受信したデータが存在する
    if(Serial.available() >= 3) { //パケットを正常に受信している
     ReceiveData[0] = Serial.read();
      if (ReceiveData[0] == 0x00) { //ヘッダが先頭である
       ReceiveData[1] = Serial.read(); //チャタリング防止ループ値
       ReceiveData[2] = Serial.read(); //走行音データ番号
       //キャッシュ解放処理
       while (Serial.available() > 0) {
       Serial.read();
       } 
       return 0; //正常受信
      } else {
       //キャッシュ解放処理
       while (Serial.available() > 0) {
       Serial.read();
       }
      return 1; //受信エラー
      }
    }
  }
  return -1; //未受信
}

//加速度調整。現在速度が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, 1.5);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.3 * rate;
      break;

    case 2:
      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, 1.5);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.5 * rate;
      break;

    case 3:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch3 - spd > 0) {  //各ノッチの最高速度以下
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, 1.5);
      }
      else {  //各ノッチの最高速度未満
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.8 * rate;
      break;

    case 4:
      if (spd == 0 || spd <= (long)keepac[vvvfPtn]*10000) { //現在速度が0km/hまたは定加速度領域以下
        rate = 1;
      }
      else if (notch4 - spd > 0) {  //各ノッチの最高速度未満rate = ((float)keepac[vvvfPtn]*10000 / (float)spd) * 2.0;
        rate2 = (float)keepac[vvvfPtn]*10000 / (float)spd;
        rate = pow(rate2, 1.5);
      }
      else {  //各ノッチの最高速度以上
       rate = 0; 
      }
      kasoku = accelVol * ACCEL_RATIO * 0.9 * rate;
      break;

    case 5:
      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, 1.5);
      }
      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 * 0.15) / notch5));
  }
  
  kasoku = accelVol * BRAKE_RATIO * notch * rate;
}

// ***********************************************
// 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);
  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;
  }

  for (i = 0; i < SOUNDNUM; i++) {
    stopSpdData[i] = STOPSPD;
  }

  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行目
  
  //外部制御モードの設定(USB接続)
  lcd.clear();
  delay(180);
  lcd.setCursor(0, 0);
  lcd.print(" If using USB,  ");
  lcd.setCursor(0, 1);
  lcd.print("Please Set to EB");
  delay(3000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Use USB Connect?");
  delay(480);
  
  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行目
        isUSB = (MC_EB == getMasconVol(analogRead(P_MASCON)));
        if (isUSB == true){
          lcd.print("Yes             "); // Y or N
        } else {
          lcd.print("No              "); // Y or N
        }
      } else {
        if ((millis() - sttTime) > DECISION_TIME * 1000) {
          // 選んでから所定時間が過ぎた。
          break; // ループを抜ける。
        } 
      }
      progress = millis() - sttTime;
      if (progress > 0) {
        lcd.setCursor(15, 1);
        lcd.print((String) (DECISION_TIME - (progress / 1000)));
      }
      delay(1);
    }
  } while (masconPos < MC_EB || masconPos > MC_P5); // 12種類から選択されたらループを抜け次の処理へ。

  //外部制御モード
  if (isUSB == true){
   lcd.clear();
   delay(180);
   lcd.setCursor(0, 0);
   lcd.print("Use USB Connect.");
   delay(2000);
   lcd.setCursor(0, 1);
   lcd.print(" Connecting...  ");
   Serial.begin(9600);
   
   do{
    Result = Receive();
     switch (Result) {
      case -1: //未受信
       break;
      case 0: //正常受信
       Serial.write(0);
       break;
      case 1: //受信エラー
       Serial.write(2);
       break;
     }
   } while (Result != 0);

   SetSound(ReceiveData[1], ReceiveData[2]);
   
  }
    
  //独立モード
  else {
   UNTI_CHAT_RATE = 40; // チャタリング防止ループの値。ノッチの反応速度調整に使用。
   lcd.clear();
   delay(180);
   lcd.setCursor(0, 0);
   lcd.print("  Not Use USB.  ");
   delay(2000);
   
    // 走行音パターン取得
    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);
    
    // ロータリースイッチを5秒動かさないと決定。
    // ただし、何も動かしてないときはずっと待ち続ける。
    
    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);

  }
}

// ***********************************************
// ***********************************************
// 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;
    kbrake(5);
    mode = MD_BRAKE;
    break;
  case 3:
    // 制動4
    orderSpd = 0;
    kbrake(4);
    mode = MD_BRAKE;
    break;
  case 4:
    // 制動3
    orderSpd = 0;
    kbrake(3);
    mode = MD_BRAKE;
    break;
  case 5:
    // 制動2
    orderSpd = 0;
    kbrake(2);
    mode = MD_BRAKE;
    break;
  case 6:
    // 制動1
    orderSpd = 0;
    kbrake(1);
    mode = MD_BRAKE;
    break;
  case 7:
    // 惰行
    orderSpd = 0;
    kasoku = accelVol * BRAKE_RATIO;
    mode = MD_NTRL;
    break;
  case 8:
    // 力行1
    orderSpd = notch1;
    kaccel(1);
    mode = MD_ACCEL;
    break;
  case 9:
    // 力行2
    orderSpd = notch2;
    kaccel(2);
    mode = MD_ACCEL;
    break;
  case 10:
    // 力行3
    orderSpd = notch3;
    kaccel(3);
    mode = MD_ACCEL;
    break;
  case 11:
    // 力行4
    orderSpd = notch4;
    kaccel(4);
    mode = MD_ACCEL;
    break;
  case 12:
    // 力行5
    orderSpd = notch5;
    kaccel(5);
    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を終了コードとしている)
    
    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) { // 走行音が設定されていない場合の処理
          OCR1A = (word)800; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 10000 = 800
          OCR1B = (word)(800 - 800 / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
          TCCR1B = B00010001;
          TCCR1A = B00110001;
          TCCR2B = B00000001; // PWM 2A/2B 分周1
          TCCR2A = B11100001; // PWM 2A/2B 出力
          break;
        }

        if (mode == MD_BRAKE) { // 変調モード切替
        break; 
        }
        
        if (mode == MD_STOP) { // 変調モード切替
        break; 
        }

        if (mode == MD_NTRL) { // 変調モード切替
        break; 
        }
  
        if (AsttFrq == -1) { // 高速時 VVVF音停止
          OCR1A = (word)800; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 10000 = 800
          OCR1B = (word)(800 - 800 / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
          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 > 10000 ) Afrq = 10000;
          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 = (word)800; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 10000 = 800
          OCR1B = (word)(800 - 800 / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
          TCCR1B = B00010001;
          TCCR1A = B00110001;
          TCCR2B = B00000001; // PWM 2A/2B 分周1
          TCCR2A = B11100001; // PWM 2A/2B 出力
          break;
        }

        if (mode == MD_ACCEL) { // 変調モード切替
        break; 
        }
        
        if (mode == MD_STOP) { // 変調モード切替
        break; 
        }
     
        if (mode == MD_NTRL) { // 変調モード切替
        break; 
        }
         
        if (BsttFrq == -1) { // 高速時 VVVF音停止
          OCR1A = (word)800; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 10000 = 800
          OCR1B = (word)(800 - 800 / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
          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 > 10000 ) Bfrq = 10000;
          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 = (word)800; // TOP値 = CPUの動作周波数 / 2 / 分周 / 希望周波数 = 16,000,000 / 2 / 1 / frq 10000 = 800
      OCR1B = (word)(800 - 800 / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
      TCCR1B = B00010001;
      TCCR1A = B00110001;
      TCCR2B = B00000001; // PWM 2A/2B 分周1
      TCCR2A = B11100001; // PWM 2A/2B 出力
      break;
    }

    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;
    }
  }

  if (isUSB == true) {
   Result = Receive();
   switch (Result) {
    case -1: //未受信
     break;
    case 0: //正常受信
     if (mode == MD_STOP) { //列車が停止している
      Serial.write(0);
      SetSound(ReceiveData[1], ReceiveData[2]);
     } else {
      Serial.write(1);
     }
     break;
    case 1: //受信エラー
     Serial.write(2);
     break;
   }
  }
  
}