Nゲージ自動運転パワーパックの製作
電車が動いたところで実用的なNゲージ(HOゲージ)のパワーパックを製作してみます。
- 列車の運転台のようにマスコンとブレーキで操作します。


こんなのを作ります。3Dプリンターでケースを作りました。マスコンとブレーキのつまみはカトーのパワーパックからひっこぬきました。

- 組み立て

- 回路図

・LCD表示器は秋月のAQM0802の5P変換キットを使用
・3.3V3端子DC-DCレギュレータはR-78E3.3-0.5を使用
基板はサンハヤトのICB-93S-2を使用
PIC32のソフト
- MPLAB Xでソフトを作ります。プロジェクトの作り方はLEDぴかぴか
- 新規プロジェクトでPowerPacというプロジェクトを作ります。
- Harmonyが開くので基本のハードウエア設定をします。

プリスケーラ64、インタラプトの優先度はLevel7で最優先にします。


●Pin Diagramのタブで17,18PINをI2C用に設定します。

Pin Settingsのタブで、RA1をモードSW用にデジタル入力にし、RA4をパルス出力用デジタルOUTに設定します。

以上でHarmonyの設定は終了で、GenarateCodeのボタンでコードを作成します。
コードの編集
編集の前にプロジェクト名(PowerPac)を右クリックのプロパティのGeneralでEncodingをShift-Jisに変更します。こうしないと注釈のかな漢字が文字化けします。

・app.hの編集

DRV_HANDLE hi2c; DRV_I2C_BUFFER_HANDLE bhi2c; int read_speed; int power;
APP_DATAの下にグローバルで使う変数を挿入します。
次はLCD用のCファイルを作ります。SorceFiles-appを右クリックでlcd.cを作ります。New -> xc32_newfile.c

FileNameをlcd.cにして作成すると出来たものには、ごちゃごちゃいろいろ書いてありますが全部消して、以下のコードを書きます。
#include "system/common/sys_common.h"
#include "app.h"
#include "system_definitions.h"
#define ADR 0x7C
void LCD_cmd(char);
void LCD_int(void);
void LCD_str(char *);
void LCD_dat(char);
void LCD_hex(char);
void LCD_posyx(char, char);
void BSP_DelayMs(unsigned short);
void BSP_DelayUs(unsigned short);
uint8_t data[5];
//遅延調整用関数(ms)
void BSP_DelayMs(unsigned short milliseconds) {
unsigned long time;
time = _CP0_GET_COUNT(); //Read Core Timer
time += (SYS_CLK_FREQ / 2 / 1000) * milliseconds; //calc the Stop Time
while ((long) (time - _CP0_GET_COUNT()) > 0) {
};
}
//遅延調整用関数(μs)
void BSP_DelayUs(unsigned short microseconds) {
unsigned long time;
time = _CP0_GET_COUNT(); //Read Core Timer
time += (SYS_CLK_FREQ / 2 / 1000000) * microseconds; //calc the Stop Time
while ((long) (time - _CP0_GET_COUNT()) > 0) {
};
}
void LCD_cmd(char cmd) {
data[0] = 0x80;
data[1] = cmd;
bhi2c = DRV_I2C_Transmit(hi2c, ADR, data, 2, NULL);//I2C送信
while (DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE); /*I2C完了待ち*/
if (cmd & 0xFC) // LCDのコマンドにより待ち時間が違う
BSP_DelayUs(60); // 60usec
else
BSP_DelayMs(3); // 3msec
}
void LCD_int(void) {
BSP_DelayMs(100);
LCD_cmd(0x38);
LCD_cmd(0x39);
LCD_cmd(0x14);
LCD_cmd(0x73); //7A(forAQM1602)
LCD_cmd(0x56); //54(forAQM1602)
LCD_cmd(0x6C);
BSP_DelayMs(200);
LCD_cmd(0x0C); //Disp ON/OFF
LCD_cmd(0x01);// Clear Display
BSP_DelayUs(1100);
}
//文字列表示
void LCD_str(char *str) {
while (*str) //0x00まで繰り返し
LCD_dat(*str++); //1文字表示
}
//1文字表示
void LCD_dat(char chr) {
data[0] = 0x40;
data[1] = chr;
//I2C送信
bhi2c = DRV_I2C_Transmit(hi2c, ADR, data, 2, NULL);
while (DRV_I2C_TransferStatusGet(hi2c, bhi2c) != DRV_I2C_BUFFER_EVENT_COMPLETE);
BSP_DelayUs(60); // 60usec
}
//-------- 16進文字変換表示 ----------------
void LCD_hex(char c) {
const char hexch[] = "0123456789ABCDEF";
LCD_dat(hexch[c >> 4]); //上位4bit表示
LCD_dat(hexch[c & 0xF]); //下位4bit表示
}
//--------3桁 10進文字表示 ----------------
void LCD_dec(int i) {
const char decch[] = "0123456789 ";
char n[3];
n[2] = i / 100;
n[1] = (i % 100) / 10;
n[0] = i % 10;
if (n[2] == 0){
n[2] = 10;//100の桁は0ならスペース
if (n[1] == 0){
n[1] = 10;//100の桁0で10の桁0ならスペース
}
}
LCD_dat(decch[n[2]]);
LCD_dat(decch[n[1]]);
LCD_dat(decch[n[0]]);
}
//-------- カーソル位置指定 --------------------------------------
void LCD_posyx(char ypos, char xpos) {
unsigned char pcode;
switch (ypos & 0x03) { // 縦位置を取得
case 0: pcode = 0x80;
break; // 1行目
case 1: pcode = 0xC0;
break; // 2行目
case 2: pcode = 0x94;
break; // 3行目
case 3: pcode = 0xD4;
break; // 4行目
}
LCD_cmd(pcode += xpos); // 横位置を加える
}
/*******************************************************************************
End of File
*/
同様にして、timer_int.cファイルを作ります。
#include "system/common/sys_common.h"
#include "app.h"
#include "system_definitions.h"
int int_counter;
//int max_speed;
int mascon;
int speed_pw;
int set_speed;
int brake;
int accel;
int cont_count;
int gensoku;
int speed_sum;
int speed_peak;
int speed_adc;
char sum_count;
void timer_int_func() {
//タイマーインタラプトを0-0x1ffでカウントします。
switch (++int_counter) {
case 0x200:
int_counter = 0;
_RA4 = 1; //speed pulse ON
/* MUX A Positive Input Select */
PLIB_ADC_MuxChannel0InputPositiveSelect(DRV_ADC_ID_1, ADC_MUX_A, ADC_INPUT_POSITIVE_AN0);
PLIB_ADC_SamplingStart(ADC_ID_1); //ADCで読み込み
while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1)); //変換終了待ち
mascon = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0); //結果をadc_retへ
mascon >>= 1; //最大0x1ffに変更
if (--cont_count < 0) {
cont_count = 10;
set_speed += accel; //加速
if (set_speed > 0x1F0)
set_speed = 0x1F0; //最高速度
//加速値の設定
if (set_speed > mascon)
accel = 0; //マスコン以上加速しない
else {
accel = mascon - set_speed;
accel /= 50;
if (accel == 0)
accel = 1;
}
brake = brake * 20 / 0x3ff;
set_speed -= brake; //減速
//自然減速
if (--gensoku < 0) {
gensoku = 5;//この値を大きくすると減速が遅くなります。
set_speed--;
}
if (set_speed < 0)
set_speed = 0; //マイナスの時0
}
speed_pw = set_speed;
power = speed_pw;
break;
case 1:
PLIB_ADC_MuxChannel0InputPositiveSelect(DRV_ADC_ID_1, ADC_MUX_A, ADC_INPUT_POSITIVE_AN10);
PLIB_ADC_SamplingStart(ADC_ID_1); //ADCで読み込み
while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1)); //変換終了待ち
brake = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0); //結果をadc_retへ
break;
case 0x1ff:
//実際のスピード読み込み
PLIB_ADC_MuxChannel0InputPositiveSelect(DRV_ADC_ID_1, ADC_MUX_A, ADC_INPUT_POSITIVE_AN12);
PLIB_ADC_SamplingStart(ADC_ID_1); //ADCで読み込み
while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1)); //変換終了待ち
speed_adc = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0); //結果をadc_retへ
//ピーク検出
if (speed_peak < speed_adc) {
speed_peak = speed_adc;
}
//読み取り速度平均
if ((sum_count-- & 0xf) == 0) {
//16回ピークをとる
speed_sum += speed_peak;
speed_peak = 0;
if (sum_count < 0) {
//4回の平均
sum_count = 0x3f;
read_speed = speed_sum >> 2;
speed_sum = 0;
}
}
break;
}
if (speed_pw == 0) {
_RA4 = 0; //speed pulse OFF
} else {
speed_pw--;
}
}
/* *****************************************************************************
End of File
*/
・次にapp.cを編集します。

APP_TasksのAPP_STATE_INIT:のところに以下を挿入。
DRV_TMR0_Start(); //TimerStart
DRV_ADC_Open(); //Enable ADC
//I2C Open
hi2c = DRV_I2C_Open(DRV_I2C_INDEX_0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
if (hi2c != DRV_HANDLE_INVALID) {
//LCD初期化
LCD_int();
}
APP_STATE_SERVICE_TASKS:に同様に挿入。

BSP_DelayMs(20);
//Power表示
int hex;
LCD_posyx(0, 0);
LCD_str("PWR:");
int pow = power * 100 / 0x1f0;
hex = pow;
LCD_dec(hex);
//実際のスピード表示
LCD_posyx(1, 0);
hex = read_speed * 400 / 0x1ff;
LCD_dec(hex);
LCD_str("km/h");
BSP_DelayMs(20);
break;
・最後にsystem_interrupt.cを編集します。

TIMER_1_VECTORのところに一行挿入します。
// timer_int_func();
以上で出来上がりです。
