人気ブログランキング | 話題のタグを見る

Arduinoで鉄道模型PWMコントローラーを自作 ~v2.4jc1ソース公開~

2017/4/25に公開させていただいたArduinoでのパワーパックのソースコードですが、
・コメント間違ってるな~
・他の車種の音も出したいな~
等々あってちょこちょこいじっていたので、現(2018/8/5)時点のを皆さんにもお見せします。

現時点でも
・E231(通勤車、三菱製VVVF)の再現は難しいので改良したい。
・12種類しか設定できないのは少ないので増やせるようにしたい。
・加速時と減速時で鳴らす音鳴らさない音を可変にしたい。
という思いを実現できていないので、その辺はいずれ。。

とりあえず音は中央線を走る車輛を中心に設定してみたんですが、「この値のほうがより似ている」というものがあれば、皆さんご教示いただきたく、よろしくお願いします。

それと、原作であるしろくま様のソースコードは「商用利用不可。オークションも含む。必ずご一報下さい。」ですので、私もしろくま様に倣うことになります。
これで稼いじゃダメよ、趣味の範囲でね、ってことですね。その点お忘れなきようお願いします。



/* 
商用利用不可。オークションも含む。必ずご一報下さい。
byしろくま様
*/
// ***************************************************************************************
// WhiteBear Controller R Powered by Arduino
// ***************************************************************************************
// ・PWM制御による鉄道模型用コントローラー。
// ・12段階のロータリースイッチを用い、ワンハンドルマスコンを模擬する。
// ・非常ブレーキ1段、常用ブレーキ5段、惰行1段、力行5段。
// ・各種状況はキャラクタLCD(16文字x2段)で表示。
// ・力行各段の最高速度を設定可能。設定はソースコード内であらかじめ設定。
// ・PWM周波数の変調により、VVVFなど制御機器の音を再現。
// ・力行速度や音の車輛データは最大12種類。
// ・マスコンEBで電源投入すると、起動アニメ表示。
// ・マスコンP5で電源投入すると、開発モード。
// ・開発モード(isDev=True)の時、現在/目標スピード値と加速率/常点灯ダイヤルの値も表示。
// ***************************************************************************************
// ~~~素材~~~~~~~~~~~~~~~~~~秋月電子・マルツURL~~~~~~~~~~~~入数~税込
// ・Arduino相当 Arduino UNO→ http://akizukidenshi.com/catalog/g/gM-07385/ 1 2940
// ・Atmel社 AVRマイコン ATMEGA328P-PU http://www.marutsu.co.jp/shohin_130671/ 1 453
// ・ICソケット 28P 300mil http://www.marutsu.co.jp/shohin_59240/ 1 54
// ・クリスタル(水晶発振子) 16MHz http://akizukidenshi.com/catalog/g/gP-08671/ 1 30
// ・三端子レギュレーター 5V1A http://akizukidenshi.com/catalog/g/gI-06312/ 1 40
// ・電解コンデンサー 100μF http://akizukidenshi.com/catalog/g/gP-03122/ 1 10
// ・積層セラミックコンデンサー 22pF×2 http://akizukidenshi.com/catalog/g/gP-03620/ 20 100
// ・積層セラミックコンデンサー 0.1μF×5 http://akizukidenshi.com/catalog/g/gP-00090/ 10 100
// ・一般整流用ダイオード 1N4001 50V1A http://akizukidenshi.com/catalog/g/gI-08332/ 1 10
// ↑代替 1N4007 1kV1A
// (Arduino相当ここまで)
//
// ・パワーMOS-FET 2SK2232 http://akizukidenshi.com/catalog/g/gI-02414/ 1 50
// ・トランジスタ 2SA1015Y×2 http://akizukidenshi.com/catalog/g/gI-02612/ 10 100
// ・電解コンデンサー 470μF http://akizukidenshi.com/catalog/g/gP-03129/ 1 20
// ・積層セラミックコンデンサー 0.1μF×3 上記参照
// ・セラミックコンデンサー 470pF http://akizukidenshi.com/catalog/g/gP-05243/ 10 150
// ・カーボン抵抗 1kΩ×15 http://akizukidenshi.com/catalog/g/gR-25102/ 100 100
// ・カーボン抵抗 470Ω×7 http://akizukidenshi.com/catalog/g/gR-25471/ 100 100
// ・カーボン抵抗 2.2kΩ×2 http://akizukidenshi.com/catalog/g/gR-25222/ 100 100
// ・カーボン抵抗 10kΩ http://akizukidenshi.com/catalog/g/gR-25103/ 100 100
// ・RotarySW(1回路12接点ストッパ付,NonShortType) 私は札幌 梅澤無線にて購入。型番ZX-RS1112。ShortTypeのようです。
// └同一製品と思われる。 http://www.bonton-nagoya.com/index.php?main_page=product_info&products_id=2012
// ・小型ボリューム 10kΩBカーブ×2 http://akizukidenshi.com/catalog/g/gP-00246/ 1 40
// ・トグルスイッチ1回路2接点 http://akizukidenshi.com/catalog/g/gP-03774/ 1 80
// ・トグルスイッチ2回路2接点中点OFFあり http://akizukidenshi.com/catalog/g/gP-07399/ 1 100
// ・LED 緑 赤 各1
// ・キャラクタLCD(16文字2行) SC1602 http://akizukidenshi.com/catalog/g/gP-02919/ 1 800
// ・整流用ショットキーダイオード 1S4 40V1A http://akizukidenshi.com/catalog/g/gI-00127/ 1 20
// ・汎用小信号高速スイッチング・ダイオード
// 1N4148×3 http://akizukidenshi.com/catalog/g/gI-00941/ 50 100
// ・ポリスイッチ 0.65A(1.3Aで遮断) http://akizukidenshi.com/catalog/g/gP-00777/ 1 30
// ・2.1mm標準DCジャック http://akizukidenshi.com/catalog/g/gC-06299/ 1 40
// ・ピンヘッダ(オス)2列 http://akizukidenshi.com/catalog/g/gC-00166/ 1 20
// ・ユニバーサル基板 http://akizukidenshi.com/catalog/g/gP-03229/ 1 60
// ・ACアダプター12V1.5A http://akizukidenshi.com/catalog/g/gM-02194/ 1 950
// ***********************************************
#include <avr/io.h> // ATmega328P用のヘッダファイル
#include <LiquidCrystal.h> // キャラクタ液晶ディスプレイ制御用のヘッダファイル

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

const char *MOJI1 = "Welcome aboard! "; // Welcomeメッセージ。LCD1段目。16桁以内にすること。
const char *MOJI2 = "WhiteBearR2.4jc1"; // Welcomeメッセージ。LCD2段目。16桁にすること。

// オリジナル絵文字:DD51
byte dd51[8] = {
B01110,
B01010,
B01010,
B11011,
B10001,
B10001,
B11111
};

// オリジナル絵文字:コキ
byte koki[8] = {
B11111,
B10001,
B10001,
B10001,
B10001,
B11111,
B11111
};

const int P_MASCON = 3; // Arduino接続ピン番号:(Analog) ロータリースイッチ = ATMEGA328 pin #26
const int P_LIGHT_DIAL = 4; // Arduino接続ピン番号:(Analog) 常点灯調節ダイアル = ATMEGA328 pin #27
const int P_ACCEL_DIAL = 5; // Arduino接続ピン番号:(Analog) 加速率調節ダイアル = ATMEGA328 pin #28

const int P_PWM2B = 3; // Arduino接続ピン番号:(Digital) OCR2B出力 to Rail = ATMEGA328 pin #5
const int P_LCD_RS = 5; // Arduino接続ピン番号:(Digital) LCD-REGISTER = ATMEGA328 pin #11
const int P_LCD_D4 = 6; // Arduino接続ピン番号:(Digital) LCD-D4 = ATMEGA328 pin #12
const int P_LCD_D5 = 7; // Arduino接続ピン番号:(Digital) LCD-D5 = ATMEGA328 pin #13
const int P_LCD_D6 = 8; // Arduino接続ピン番号:(Digital) LCD-D6 = ATMEGA328 pin #14
const int P_LCD_D7 = 9; // Arduino接続ピン番号:(Digital) LCD-D7 = ATMEGA328 pin #15
const int P_PWM1B = 10; // Arduino接続ピン番号:(Digital) OCR1B出力 to Rail = ATMEGA328 pin #16
const int P_PWM2A = 11; // Arduino接続ピン番号:(Digital) OCR2A出力 to Rail = ATMEGA328 pin #17
const int P_LCD_EN = 12; // Arduino接続ピン番号:(Digital) LCD-ENABLE = ATMEGA328 pin #18

// キャラクタ液晶
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 = 50; // (加速率調整小型ボリュームからの入力値) × ACCEL_RATIO ⇒ 加速率
const int BRAKE_RATIO = 20; // (加速率調整小型ボリュームからの入力値) × 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

// マスコン位置名称を保持する配列
const char* masconPosName[] = {"EB", "B5", "B4", "B3", "B2", "B1", "N ", "P1", "P2", "P3", "P4", "P5"};

const int MD_STOP = 0; // モード 停止
const int MD_BRAKE = 1; // モード 減速
const int MD_NTRL = 2; // モード 惰行
const int MD_ACCEL = 3; // モード 加速

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 mode; // 走行状態。0=停止, 1=減速, 2=惰行, 3=加速

const int SPD_CONV_RATIO = 10000; // 速度変換比率(表示速度=内部速度*速度変換比率)
long spd; // 内部速度
int addSpd; // Arduinoのloop処理1回ごとに足し込むor引き去る速度
long orderSpd; // 指示速度(内部速度)
long notch1spd; // ノッチ1での最高速度(内部速度)
long notch2spd; // ノッチ2での最高速度(内部速度)
long notch3spd; // ノッチ3での最高速度(内部速度)
long notch4spd; // ノッチ4での最高速度(内部速度)
long notch5spd; // ノッチ5での最高速度(内部速度)
long dispSpd; // 表示速度
long dispOrderSpd; // 指示速度(表示速度)
long sttDSpd; // 開始速度(表示速度)
long endDSpd; // 終了速度(表示速度)
long stopDSpd; // 走行用出力CUT速度(表示速度)
long sttFrq; // 開始周波数
long endFrq; // 終了周波数
long frq; // 周波数
int i; // ループカウンター
int j; // ループカウンター
int f; // ループカウンター

// 走行音名称
const char* soundName[12] = {
"NoSound(max250) ",
"201 ",
"209 ",
"E231-0,500,800 ",
"E233 ",
"E257-0 ",
"E257-500 ",
"E351 ",
"E353 ",
"EH200 ",
"Metro05(Chopper)",
"Metro05(PMSM) "
};

// 各パターンの各ノッチ位置での最高速度値を配列で保持。
// 左から順に 1ノッチ, 2ノッチ, 3ノッチ, 4ノッチ, 5ノッチ。
// 5×12パターン=60個の値を持つ。
int maxDSpdData[60] = {
31, 71, 121, 181, 251, // NoSound (max250)
21, 41, 61, 86, 111, // 201,203
21, 41, 61, 86, 121, // 207-0,209
//21, 41, 61, 91, 131, // 233-2000
21, 41, 61, 86, 121, // E231-0,500,800
//21, 41, 61, 91, 131, // E231-1000
21, 41, 61, 86, 121, // E233
21, 51, 81, 111, 141, // E257-0
21, 51, 81, 111, 141, // E257-500
21, 51, 81, 111, 161, // E351
21, 51, 81, 111, 131, // E353
//21, 41, 61, 91, 131, // E501
21, 41, 61, 91, 121, // EH200,EH500
//21, 41, 61, 91, 121, // EF210,EF510
21, 41, 61, 81, 111, // Metro05chopper
21, 41, 61, 81, 111 // Metro05vvvfPMSM
};

// 走行用の出力を止める速度値を配列で保持。ピタッと止めるための値。
// 12パターン分の値を持つ。
int stopDSpdData[12] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

// 音データ。
// 設定可能周波数 123Hz~8MHz
// DUTY比の分解能確保のため30kHzぐらいを上限。整数に限る。
// 音を出す場合必ず2段階以上入力すること。減速時1段階目は音を止めるため。
//
// ~~~(例) 以下のパターンの場合~~~
// 1800, 1800, 7, // 1 開始周波数 終了周波数 切替スピード:速度1から7まで1800Hzで音程変化なし
// 500, 1800, 18, // 2 開始周波数 終了周波数 切替スピード:速度8から18にかけて500Hzから1800Hzまで変化する
// 500, 750, 31, // 3 開始周波数 終了周波数 切替スピード
// 600, 800, 41, // 4 開始周波数 終了周波数 切替スピード
// 550, 600, 51, // 5 開始周波数 終了周波数 切替スピード
// 250, 700, 121, // 6 開始周波数 終了周波数 切替スピード
// -1, // 終了コード
int soundData[] = {
// NoSound (max250)
-1,

// 201系、203系 (電機子チョッパ)
600, 600, 1,
600, 600, 101,
-1,

// 207系0番代、209系 (三菱 GTO-VVVF)
380, 380, 3,
380, 960, 11,
554, 1254, 31,
800, 1200, 51,
440, 800, 101,
-1,

// 223系2000番代
// 1800, 1800, 7,
// 500, 1800, 18,
// 500, 750, 31,
// 600, 800, 41,
// 550, 600, 51,
// 250, 700, 121,
// -1,

// E231系0番代、500番代、800番代 (通勤型 : 三菱 IGBT-VVVF)
380, 380, 11,
380, 1500, 21,
500, 1050, 41,
300, 700, 101,
-1,

// E231系1000番代 (近郊型 : 日立 IGBT-VVVF)
// 1050, 1050, 6,
// 1050, 1050, 21,
// 1050, 700, 45,
// 700, 1800, 51,
// 380, 600, 101,
// -1,

// E233系
1568, 1568, 6,
1568, 1568, 41,
525, 900, 56,
380, 600, 101,
-1,

// E257系0番代
1050, 1050, 6,
1050, 1050, 41,
1050, 950, 61,
380, 600, 101,
-1,

// E257系500番代
1050, 1050, 6,
1050, 1050, 31,
1050, 1800, 56,
380, 600, 101,
-1,

// E351系(量産車 IGBT-VVVF)
1666, 1666, 11,
1666, 1666, 21,
1666, 2200, 31,
950, 1800, 56,
-1,

// E353系(≒E233通勤型 三菱 IGBT-VVVF)
1568, 1568, 6,
1568, 1568, 41,
525, 900, 56,
380, 600, 101,
-1,

// E501
// 300, 300, 10,
// 350, 350, 11,
// 392, 392, 12,
// 440, 440, 13,
// 466, 466, 14,
// 523, 523, 15,
// 587, 587, 16,
// 622, 622, 17,
// 698, 698, 18,
// 783, 783, 19,
// 900, 900, 35,
// 900, 1000, 37,
// 900, 1000, 41,
// 300, 500, 101,
// -1,

// EH200形、EH500形
1000, 1000, 1, // 開始周波数 終了周波数 切替スピード
1000, 1000, 101, // 開始周波数 終了周波数 切替スピード
-1, // 終了コード

// EF210形, EF510形
// 784, 784, 11, // 開始周波数 終了周波数 切替スピード
// 784, 784, 21, // 開始周波数 終了周波数 切替スピード
// 784, 1480, 56, // 開始周波数 終了周波数 切替スピード
// 1480, 1480, 101, // 開始周波数 終了周波数 切替スピード
// -1, // 終了コード

// 05系(電機子チョッパ)
300, 300, 2,
300, 300, 9,
600, 600, 13,
900, 900, 41,
600, 600, 66,
900, 900, 69,
19000, 19000, 101,
-1,

// 05系(PMSM-VVVF)
1250, 1250, 1,
1250, 1250, 10,
2000, 2000, 61,
-1

//パターン 新幹線性能版 GTO
// 800, 800, 11, // 開始周波数 終了周波数 切替スピード
// 380, 900, 31, // 開始周波数 終了周波数 切替スピード
// 500, 750, 41, // 開始周波数 終了周波数 切替スピード
// 500, 800, 81, // 開始周波数 終了周波数 切替スピード
// 800, 800, 101, // 開始周波数 終了周波数 切替スピード
// -1, // 終了コード

//パターン 新幹線性能版 IGBT
// 1800, 1800, 7, // 開始周波数 終了周波数 切替スピード
// 500, 1800, 18, // 開始周波数 終了周波数 切替スピード
// 500, 750, 31, // 開始周波数 終了周波数 切替スピード
// 600, 800, 41, // 開始周波数 終了周波数 切替スピード
// 550, 600, 51, // 開始周波数 終了周波数 切替スピード
// 250, 700, 121, // 開始周波数 終了周波数 切替スピード
// -1, // 終了コード

//パターン 新幹線性能版 SIEMENS
// 300, 300, 10, // 開始周波数 終了周波数 切替スピード
// 350, 350, 11, // 開始周波数 終了周波数 切替スピード
// 392, 392, 12, // 開始周波数 終了周波数 切替スピード
// 440, 440, 13, // 開始周波数 終了周波数 切替スピード
// 466, 466, 14, // 開始周波数 終了周波数 切替スピード
// 523, 523, 15, // 開始周波数 終了周波数 切替スピード
// 587, 587, 16, // 開始周波数 終了周波数 切替スピード
// 622, 622, 17, // 開始周波数 終了周波数 切替スピード
// 698, 698, 18, // 開始周波数 終了周波数 切替スピード
// 783, 783, 19, // 開始周波数 終了周波数 切替スピード
// 900, 900, 35, // 開始周波数 終了周波数 切替スピード
// 900, 1000, 37, // 開始周波数 終了周波数 切替スピード
// 900, 1000, 41, // 開始周波数 終了周波数 切替スピード
// 300, 500, 101, // 開始周波数 終了周波数 切替スピード
// -1 // 終了コード
};

// 走行音パターン12種soundDataの各パターンデータ開始位置インデックスを保持。
// 実際の値設定は、setup処理内にて終了コード(-1)を検出し動的に設定する。
int dataNum[12] = {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) {
// マスコン位置名を右下2桁へ。
lcd.setCursor(14, 1);
lcd.print(masconPosName[masPos - 1]);

// スピード値を2行目5~7文字目へ。
dispSpd = spd / SPD_CONV_RATIO;
lcd.setCursor(4, 1);
if (dispSpd < 100 && dispSpd >= 10) lcd.print(" ");
else if (dispSpd < 10) lcd.print(" ");
lcd.print(dispSpd);

if (isDev) {
// 目標スピード値を2行目9~11文字目へ。
dispOrderSpd = orderSpd / SPD_CONV_RATIO;
lcd.setCursor(8, 1);
if (dispOrderSpd < 100 && dispOrderSpd >= 10) lcd.print(" ");
else if (dispOrderSpd < 10) lcd.print(" ");
lcd.print(dispOrderSpd);

// 常点灯ダイヤル値を1行目14~16文字目に表示。
lcd.setCursor(12, 0);
lcd.print(" ");
if (lightVol < 100 && lightVol >= 10) lcd.print(" ");
else if (lightVol < 10) lcd.print(" ");
lcd.print(lightVol);

// 加速率ダイヤル値を2行目13文字目に表示。
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); // 常点灯用PWM出力

// WELCOMEメッセージ表示
lcd.createChar(0, dd51);
lcd.createChar(1, koki);
lcd.begin(16, 2); // 16桁、2行タイプと宣言。
lcd.clear();
lcd.print(MOJI1);
lcd.setCursor(0, 1); // 1文字目、2行目。
lcd.print(MOJI2);
lcd.print(" ");
lcd.write((byte)0); // DD51
for (i=0; i<6; i++) lcd.write((byte)1); // コキx6
lcd.write((byte)0); // DD51

delay(4000);

isDev = (MC_P5 == getMasconVol(analogRead(P_MASCON))); // マスコンがP5の時だけ開発モード。

// マスコンがEBの時だけ石北臨貨アニメーション表示。
if (MC_EB == getMasconVol(analogRead(P_MASCON))) {
for (i = 0; i < 24; i++) {
lcd.scrollDisplayLeft();
switch (i) {
case 0:
delay(900);
break;
case 1:
delay(750);
break;
case 2:
delay(550);
break;
case 3:
delay(400);
break;
default:
delay(260);
break;
}
}
}
lcd.clear();

// 走行音パターン各開始位置取得処理。
// soundData配列のうち、各パターンの最初の値となる配列インデックスをdataNumで保持する。
// パターン1は常に先頭(=インデックス0)なので、dataNumにセットするのは省略。
int idx = 0;
for (i = 1; i < 12; i++) {
while (soundData[idx] != -1) {
idx++;
}
idx++;
dataNum[i] = idx;
}

// 走行音パターン取得
lcd.home(); // 1文字目、1行目
for (i = 0; i < 2; i++) {
lcd.clear();
delay(180);
lcd.print("Select SoundPtn.");
delay(480);
}
delay(1000);
lcd.clear();
lcd.print("SoundPtn.");
lcd.setCursor(0, 1);
lcd.print(" Revolve MasCon.");
//delay(2000);

// ロータリースイッチを DECISION_TIME 秒動かさないと決定。
// ただし、何も動かしてないときはずっと待ち続ける。
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;
notch1spd = (long) maxDSpdData[vvvfPtn * 5 ] * SPD_CONV_RATIO - 1;
notch2spd = (long) maxDSpdData[vvvfPtn * 5 + 1] * SPD_CONV_RATIO - 1;
notch3spd = (long) maxDSpdData[vvvfPtn * 5 + 2] * SPD_CONV_RATIO - 1;
notch4spd = (long) maxDSpdData[vvvfPtn * 5 + 3] * SPD_CONV_RATIO - 1;
notch5spd = (long) maxDSpdData[vvvfPtn * 5 + 4] * SPD_CONV_RATIO - 1;
//stopDSpd = (long) stopDSpdData[vvvfPtn] * 1000 - 1;
stopDSpd = (long) stopDSpdData[vvvfPtn] * SPD_CONV_RATIO - 1;

// 決定したパターンを点滅表示
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(1000);

// マスコン位置を惰行か制動にしてもらう注意喚起。急に走り出さないようにするため。
// マスコン位置が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;
addSpd = accelVol * BRAKE_RATIO * 5;
mode = MD_BRAKE;
break;
case 3:
// 制動4
orderSpd = 0;
addSpd = accelVol * BRAKE_RATIO * 4;
mode = MD_BRAKE;
break;
case 4:
// 制動3
orderSpd = 0;
addSpd = accelVol * BRAKE_RATIO * 3;
mode = MD_BRAKE;
break;
case 5:
// 制動2
orderSpd = 0;
addSpd = accelVol * BRAKE_RATIO * 2;
mode = MD_BRAKE;
break;
case 6:
// 制動1
orderSpd = 0;
addSpd = accelVol * BRAKE_RATIO;
mode = MD_BRAKE;
break;
case 7:
// 惰行
orderSpd = 0;
addSpd = accelVol * BRAKE_RATIO;
mode = MD_NTRL;
break;
case 8:
// 力行1
orderSpd = notch1spd;
addSpd = accelVol * ACCEL_RATIO;
mode = MD_ACCEL;
break;
case 9:
// 力行2
orderSpd = notch2spd;
addSpd = accelVol * ACCEL_RATIO;
mode = MD_ACCEL;
break;
case 10:
// 力行3
orderSpd = notch3spd;
addSpd = accelVol * ACCEL_RATIO;
mode = MD_ACCEL;
break;
case 11:
// 力行4
orderSpd = notch4spd;
addSpd = accelVol * ACCEL_RATIO;
mode = MD_ACCEL;
break;
case 12:
// 力行5
orderSpd = notch5spd;
addSpd = accelVol * ACCEL_RATIO;
mode = MD_ACCEL;
break;
}

for ( j = 0; j < UNTI_CHAT_RATE; j++ ) { // チャタリング防止ループ
dispMasPos(masconPos);

if (spd < orderSpd) {
if (orderSpd - spd <= addSpd) {
spd = orderSpd;
} else {
spd = spd + addSpd;
}
}

if (spd > orderSpd) {
if (masconPos >= MC_N) {
// 惰行or力行
if (spd - orderSpd <= addSpd / BRAKE_RATIO) {
spd = orderSpd;
} else {
spd = spd - addSpd / BRAKE_RATIO;
}
} else {
// 制動
if ( spd - orderSpd <= addSpd ) {
spd = orderSpd;
} else {
spd = spd - addSpd;
}
}
}

if (spd < stopDSpd && masconPos <= MC_N) {
// 惰行or制動ノッチのとき、ピタ停止速度値よりも現行速度が下回ったらピタッと停止させる。
spd = 0;
mode = MD_STOP;
}

// 常点灯用PWM波形を出力。
// 出力周波数は31.2kHz = CPU周波数16MHz / ( 分周比256 * TOP値 )
// OCR2Aはこの数値からTOP255がDUTY比
OCR2A = 255 - lightVol;

// 走行用PWM
// SPD / 128 / 4 / 10 / DUTY比 ( 1.5 + speed / 390)
OCR2B = (int) (spd / ( 7680 + spd / 390 ));

// soundDataは開始周波数 終了周波数 切替スピードの3つの値で構成されているので+3ずつカウントアップ。
// soundDataの定義数は無制限(-1を終了コードとしている)
for (i = 0; ;i = i + 3) {
sttFrq = soundData[dataNum[vvvfPtn] + i];
endFrq = soundData[dataNum[vvvfPtn] + i + 1];

if (i == 0) {
sttDSpd = 0;
} else {
sttDSpd = soundData[dataNum[vvvfPtn] + i - 1];
}
endDSpd = soundData[dataNum[vvvfPtn] + i + 2];

if (i == 0 && sttFrq == -1) { // 走行音が設定されていない場合の処理
OCR1A = 421; // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数) = (16M / 2 / 1 / 19k) = 421
OCR1B = 421 - 421 * (word)lightVol / 245 ; // 音を出すPWMのDUTY比
TCCR1B = B00010001; // ┐分周比1、タイマー波形三角波、
TCCR1A = B00110001; // ┘位相・周波数基準PWMモードに設定
TCCR2B = B00000001; // ┐分周比1、タイマー波形三角波、
TCCR2A = B00100001; // ┘位相基準PWMモードに設定
break;
}

if (mode == MD_STOP) { // 停止時 音停止
OCR1A = 421; // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数) = (16M / 2 / 1 / 19k) = 421
OCR1B = 421 - 421 * (word)lightVol / 255 ; // 音を出すPWMのDUTY比
TCCR1B = B00010001;
TCCR1A = B00110001;
TCCR2B = B00000001;
TCCR2A = B00000001; // PWM 2A(常点灯部分 代わりに1Bで常点灯出力 19kHzで出力したいから)と2B停止
break;
}

// if (i == 0 && spd <= endDSpd * SPD_CONV_RATIO && mode == MD_BRAKE) { // 減速のとき1段階目の音停止
// OCR1A = 421; // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数) = (16M / 2 / 1 / 19k) = 421
// OCR1B = 421 - 421 * (word)lightVol / 255 ; // 音を出すPWMのDUTY比
// TCCR1B = B00010001;
// TCCR1A = B00110001;
// TCCR2B = B00000001;
// TCCR2A = B00100001; // PWM 2A停止(常点灯部分 代わりに1Bで常点灯出力) 2B出力
// break;
// }

// if (mode == MD_NTRL) { // 惰行 VVVF音停止
// OCR1A = 421; // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数) = (16M / 2 / 1 / 19k) = 421
// OCR1B = 421 - 421 * (word)lightVol / 245 ; // 音を出すPWMのDUTY比
// TCCR1B = B00010001;
// TCCR1A = B00110001;
// TCCR2B = B00000001;
// TCCR2A = B00100001; // PWM 2A停止(常点灯部分 代わりに1Bで常点灯出力) 2B出力
// break;
// }

if (sttFrq == -1) { // 設定された音設定上の最高速より速いときVVVF音停止
frq = 19000;
OCR1A = (word)(8000000 / frq); // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数)
OCR1B = (word)(8000000 / frq - 8000000 / frq / 99); // 音を出すPWMのDUTY比 100/99 = 1%
TCCR1B = B00010001;
TCCR1A = B00110001;
TCCR2B = B00000001; // PWM 2A/2B 分周1
TCCR2A = B11100001; // PWM 2A/2B 出力
break;
}

if ((spd >= sttDSpd * SPD_CONV_RATIO) && (spd < endDSpd * SPD_CONV_RATIO)) {
frq = ( endFrq * 10 - sttFrq * 10 ) / ( endDSpd - sttDSpd ) * ( spd - sttDSpd * SPD_CONV_RATIO ) / 100000 + sttFrq;
if (frq < 123) frq = 123;
if (frq > 31500) frq = 31500;
if (mode == MD_NTRL) frq = 19000; // 惰行のとき。

OCR1A = (word)(8000000 / frq); // TOP値 = (CPUの動作周波数 / 2 / 分周 / 希望周波数)
OCR1B = (word)(8000000 / frq - 8000000 / frq / 15); // 音を出すPWMのDUTY比 100/15 = 6.6%
TCCR1B = B00010001;
TCCR1A = B00110001;
TCCR2B = B00000001; // PWM 2A/2B 分周1
TCCR2A = B11100001; // PWM 2A/2B 出力
break;
}
}
}
}

Commented by うどん at 2019-11-26 05:22 x
はじめまして。うどんと申しますこの度angel-express様の
サイトを拝見しまして、早速作ってみようとarduino unoを用意し始めております。さっそくですが、しろくま様の回路図を参考に作って参りたいとおもいます。表示はLCD1602をつかって行きます。
また 途中経過などコメントさせて下さい。
面白そうでワクワクしています。ありがとうございます。
 
Commented by angel-express at 2019-11-27 00:28
うどん様 
はじめまして。コメありがとうございます!
私の記録がお役に立てると良いのですが。
完成まで諦めずに頑張ってくださいね。
ご連絡お待ちしてます!
Commented by Bucky at 2020-10-24 09:29 x
こんにちは。Buckyと申します。
しろくま様とハマカゼ80様のサイトを拝見し、早速コントローラを作成させていただきました。
掲載いただいている回路図とソースコードのおかげでスムーズに組み上げることが出来、大変助かりました。
ありがとうございました。
Commented by angel-express at 2020-10-30 23:41
>Bucky様 
こんにちは。お役に立てて何よりです。お互い楽しい鉄道模型趣味を続けていけると良いですね。更新頻度が高くないですが、今後も当ブログをよろしくお願いいたします。
Commented by かじ at 2021-08-18 07:51 x
原作のシロクマさんのコントローラーを作った後、
この製作情報をフルコピーさせていただきました。
一発で動きました。ありがとうございます。

ノッチのスイッチの代わりにボリュウムを使い、ノッチは機構で作るように考えています。1024を512に変えて角度を狭くしようと思っています。
Commented by angel-express at 2021-08-20 00:31
> かじ様 
動作確認できたとのことでおめでとうございます。私も基礎を作ってくださったしろくまさんに感謝です。アレンジは自分の学習になりますし、それを公開すれば後学の方々にとっても良い情報となりますね。
Commented by やきとり at 2023-02-26 22:44 x
はじめまして。突然の投稿で失礼いたします。
しろくま様とハマカゼ80様のサイトを拝見し、私もコントローラを作成してみたい!と記事やコードを読みこんだりして勉強しております。
昔の記事でのコメントで恐縮てすが、ハマカゼ80様版の回路図を拝見させていただけないでしょうか?
しろくま様版のを見ようとしましたが、公開先サイトが閉鎖されており閲覧不可になっております。
(サムネイルのは見れるのですがちょっと見えにくいのと、参考にしたいのはハマカゼ80様版なので、直接ご相談させていただきました)
Commented by angel-express at 2023-03-02 01:20
やきとり様
私の制作したコントローラーに関心を持っていただきありがとうございます。
残念ながら電子工作素人の私にとってこれが初工作だったので、描ける知識が無く回路図が存在しないのです。
その代わり、以下の記事の5章「C-LCDの接続と表示内容について」
https://angelex.exblog.jp/20751029/
と、
以下記事
https://angelex.exblog.jp/23838530/
にあるソースコード内のpinの変数定義にあるコメント、キャラクタ液晶の定義の引数から読み取ってみてください。
ATMEGA328のピン番号とArduinoのピン番号の関係までコメントとして残してありますので、上記の各情報を組み合わせると、LCDと ATMEGAがどう繋がるのか分かるはずです。
無事に完成させられることをお祈りいたします。
by angel-express | 2018-08-05 09:53 | N_Arduinoコントローラー | Comments(8)

ハマカゼ80の(北海道→首都圏)鉄活動議事録 ~1/1と1/150~


by ハマカゼ80
カレンダー
S M T W T F S
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31