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();

以上で出来上がりです。