プリント基板で製作2(ソフト)

ソフト

今回作った基板はPCなどと通信するために232C用端子があります。ここに秋月で売っている FT234X 超小型USBシリアル変換モジュール をつけて、まずはPCからリモートします。

PIC32のソフト

Harmonyの設定

Harmonyで設定するのは

  • ADC
  • TIMER
  • I2C
  • USART
  • PinDiagram
  • PinSettings

です。

※MPLABのプロジェクトの作り方はLEDぴかぴか

【ADC】

【Timer】

【I2C】

【USART】

【PinDiagram】

【PinSettings】

コードの編集

追加するコード
atc.h

Header Files-app のところに追加します。

//I2C用変数    
DRV_HANDLE hi2c;
DRV_I2C_BUFFER_HANDLE bhi2c;
//USART用変数 
DRV_HANDLE handleUSART0;

//16bitのINT型の上位H byte、下位L byte読み込み用共用体

union byte_access {
    int INT; // Int Access

    struct { // byte Access
        unsigned char L;
        unsigned char H;
    } BYTE;
};

//位置情報の構造体

struct PSI_BIT {
    unsigned char KUKAN : 3; /* 区間 Bit 0-2 */
    unsigned char ROSEN : 3; /* 路線 Bit 3-5 */
    unsigned char SOU : 1; /* 相   Bit 6   */
    unsigned char DIR : 1; /* 方向 Bit 7   */
};

struct STATUS_BIT {
    unsigned char CHG : 1; //区間変化
    unsigned char REV : 1; //逆転
    unsigned char DUMY : 4; //DUMY
    unsigned char SLOW : 1; //減速
    unsigned char SAFE : 1; //安全
};
//列車の位置情報の構造体

struct st_position {
    //現在位置

    union { /* Position */
        unsigned char BYTE; /* Byte Access */
        struct PSI_BIT BIT; /* Bit  Access */
    } NOW;
    //前の位置

    union { /* Position */
        unsigned char BYTE; /* Byte Access */
        struct PSI_BIT BIT; /* Bit  Access */
    } BEFORE;

    //次の位置

    union { /* Position */
        unsigned char BYTE; /* Byte Access */
        struct PSI_BIT BIT; /* Bit  Access */
    } NEXT;

    //次々の位置

    union { /* Position */
        unsigned char BYTE; /* Byte Access */
        struct PSI_BIT BIT; /* Bit  Access */
    } ANEXT;

    union {
        unsigned char BYTE; /* Byte Access */
        struct STATUS_BIT BIT; /* Bit  Access */
    } STATUS;

    int speed;

    union byte_access speed_cont;

    int speed_err;
    int speed_peak;

    int mascon;

    unsigned char henka;
    //unsigned char safe;
    union byte_access speed_ret; //読み取りスピード
    union byte_access speed_ret_rx; //読み取りスピード
    union byte_access speed_ret_tx; //読み取りスピード

    unsigned char point;
    unsigned char yard;

    union byte_access speed_rx;
    
    unsigned char REC_NOW;
    unsigned char REC_NEXT;
    unsigned char REC_BEFORE;
};


//監視区間設定用の共用体

union scan_port {
    unsigned char BYTE; /* Byte Access */

    struct {
        unsigned char DUMMY : 2; /* Bit 0-1 */
        unsigned char KUKAN : 3; /* Bit 2-4 監視区間*/
        unsigned char DISABLE : 1; /* Bit 5   */
        unsigned char B6 : 1; /* Bit 6   */
    } BIT;

};



#define TR_COUNT 2      //列車数
#define ROSEN_NUM 0     //路線(ボード)番号
#define BOARD_COUNT 3     //ボード数

#define RX_BYTE 128     //RS232C受信バイト数
#define TX_BYTE 128     //RS232C送信バイト数
#define RX_DATA_BYTE 12 //1列車当たりの受信バイト数
#define TX_DATA_BYTE 12 //1列車当たりの送信バイト数

unsigned char EnableBit[] = {1, 2, 4, 8, 16, 32, 64, 128};
unsigned int int_counter; //インタラプトカウンタ
unsigned int speed_pw[2]; //スピードパルス幅
struct st_position train[TR_COUNT]; //列車位置情報
struct st_position* train_sou[2];
unsigned char cont_train[2]; //制御する列車番号
unsigned char kukan[2]; //区間
unsigned char kanshi_now[2]; //スピード読み込み用
unsigned char kanshi_before[2]; //スピード読み込み用
union scan_port kanshi; //区間監視用

union byte_access adc_ret; //ADC値
union byte_access debug1; //ADC値

int gnd_adc;
//int gnd_sum;
union byte_access gnd_level;

unsigned char rx_data[RX_BYTE];
unsigned char tx_data[TX_BYTE];

unsigned char receive_data; //通信用受信データ
unsigned char send_data; //通信用送信データ
unsigned char comm_data[128]; //通信用メモリ
unsigned int comm_counter; //通信用インタラプトカウンタ
unsigned char board_num; //ボード番号

unsigned char point_req; //ポイントリクエスト
unsigned char yard_req; //ポイントリクエスト
lcd.c

Source Files-app に追加します。

#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

Source Files-app に追加します。

#include "system/common/sys_common.h"
#include "app.h"
#include "system_definitions.h"


void kukan_ON(unsigned char, unsigned char);
void kukan_OFF(unsigned char);
void speed_pulse_on(unsigned char);
void speed_pulse_off();
void section_change_monitor(unsigned char); //区間変化監視
void next_section_on(unsigned char); //次区間ON
void read_generated_voltage(unsigned char, unsigned char);
void speed_cont(unsigned char);
void section_change(void);

void board_comm(unsigned char, unsigned char*); //ボード間通信
void comm_data_set_tx(void); //通信データ設定TX
void comm_data_set_rx(void); //通信データ設定RX

void timer_int_func() {

    unsigned char rec_bit; //受信BIT用
    unsigned char send_flg; //送信PORT用


    _RB10 = 0; //ext_int 同期パルス
    rec_bit = _RB11;

    //通信用comm_counterはインタラプトを0-0x3ffでカウントします。
    if (++comm_counter > 0x3ff) {
        comm_counter = 0;
    } else
        _RB10 = 1;

    //int_counterはcomm_counterの下位9bitの0-0x1ffでカウントします。
    int_counter = comm_counter & 0x1ff;


    switch (int_counter) {
        case 0:
            section_change();
            //インタラプトカウンタが0のときA相のspeedパルスON
            if (cont_train[0] != 0xff) {
                train_sou[0] = &train[cont_train[0]];
                
                speed_pulse_on(0);
            }
            break;

        case 2:
            //インタラプトカウンタが0x2のときA相の区間変化監視(B相は0x102のとき)
            section_change_monitor(0);
            break;

        case 3:
            //インタラプトカウンタが3のときNEXT区間パルスON(B相は0x103のとき)
            //区間変化確認後に次区間もON
            next_section_on(0);
            break;

        case 0xfa:
            //スピードコントロール
            speed_cont(0);
            break;

        case 0xf8:
            //スピード読み込み
            read_generated_voltage(0, 0);
            break;

        case 0xf9:
            //スピード読み込み
            read_generated_voltage(1, 0);
            break;

        case 0x100:
            //インタラプトカウンタが0x100のときB相のspeedパルスON
            if (cont_train[1] != 0xff) {
                train_sou[1] = &train[cont_train[1]];
                //section_change(1);
                speed_pulse_on(1);
            }
            break;
        case 0x102:
            //インタラプトカウンタが0x2のときA相の区間変化監視(B相は0x102のとき)
            section_change_monitor(1);
            break;

        case 0x103:
            //インタラプトカウンタが3のときNEXT区間パルスON(B相は0x103のとき)
            //区間変化確認後に次区間もON
            next_section_on(1);
            break;


        case 0x1f8:
            //スピード読み込み
            read_generated_voltage(0, 1);
            break;
        case 0x1f9:
            //スピード読み込み
            read_generated_voltage(1, 1);
            break;


        case 0x1fa:
            speed_cont(1);

            break;

    }

    //スピードパルスOFF
    speed_pulse_off();



    int i;
    for (i = 0; i < 16; i++) {
        comm_data[i] = i * 0x10 + i;
    }
    comm_data[0] = 0x55;

    //ボード間の通信
    board_comm(rec_bit, &send_flg);

    //送信時OUTPUT
    _TRISB11 = send_flg;

    //幅広同期パルスOFF
    if (comm_counter == 0) {
        _RB10 = 1;
    }

}

void speed_pulse_on(unsigned char sou) {

    if (train_sou[sou]) {
        kukan[sou] = 0; //kukan clr

        //speed_pw[sou] = train_sou[sou]->speed;
        speed_pw[sou] = train_sou[sou]->speed_cont.INT;

        //区間メモリに現・前位置の区間を設定し区間ON
        if (train_sou[sou]->NOW.BIT.ROSEN == ROSEN_NUM) {
            kukan[sou] = EnableBit[train_sou[sou]->NOW.BIT.KUKAN];
            kanshi_now[sou] = train_sou[sou]->NOW.BIT.KUKAN;
        }
        if (train_sou[sou]->BEFORE.BIT.ROSEN == ROSEN_NUM) {
            kukan[sou] |= EnableBit[train_sou[sou]->BEFORE.BIT.KUKAN];
            kanshi_before[sou] = train_sou[sou]->BEFORE.BIT.KUKAN;
        }

        // 区間ON
        kukan_ON(kukan[sou], train_sou[sou]->NOW.BIT.DIR);

    }



}

//区間ON用

void kukan_ON(unsigned char kukan, unsigned char d) {
    // d=0:正回転, 1:負回転
    if (d == 0) {
        LATB |= kukan;
        _LATB12 = 0; //Port RB12は共通側
    } else {
        LATB &= ~kukan;
        _LATB12 = 1;
    }

    TRISB &= ~kukan;
    _TRISB12 = 0;

}

//区間OFF用

void kukan_OFF(unsigned char kukan) {
    if (kukan) {
        TRISB |= kukan;
        _TRISB12 = 1;
    }
}

void speed_pulse_off() {
    //インタラプトごとにspeedをマイナス1して0になったらパルスOFF
    unsigned char n;

    for (n = 0; n < 2; n++) {

        if (train_sou[n]) {
            if (speed_pw[n] == 0) {
                //speedが0で区間パルスOFF
                if (kukan[n]) {
                    TRISB |= kukan[n];
                    if ((TRISB & 0xff) == 0xff)
                        _TRISB12 = 1;

                    kukan[n] = 0;
                }


            } else {
                speed_pw[n]--; //speedをマイナス1
            }
        }
    }
}

void section_change_monitor(unsigned char sou) {
    static char sum_count;
    static int gnd_sum;


    if (train_sou[sou]) {
        if (train_sou[sou]->STATUS.BIT.SAFE)
            if (!train_sou[sou]->henka) {
                if (train_sou[sou]->NEXT.BIT.ROSEN == ROSEN_NUM) {
                    //次位置区間の監視
                    kanshi.BIT.KUKAN = train_sou[sou]->NEXT.BIT.KUKAN;
                    LATA &= 0xC3;
                    LATA |= kanshi.BYTE;

                    //ADC読み込み

                    PLIB_ADC_SamplingStart(ADC_ID_1);
                    while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1));
                    adc_ret.INT = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0);



                    if (train_sou[sou]->NOW.BIT.DIR == 0) {
                        //正方向のとき
                        if (adc_ret.INT >= 0x2c0) {
                            train_sou[sou]->henka = 0x80; //区間変化
                            if (sou == 0)debug1.INT = adc_ret.INT;
                        }

                    } else {
                        //逆方向のとき
                        if (adc_ret.INT < 0x140) {
                            train_sou[sou]->henka = 0x80; //区間変化
                            if (sou == 0)debug1.INT = adc_ret.INT;
                        }
                    }

                    //区間変化でないときGND値とする。
                    if (!train_sou[sou]->henka) {
                        gnd_adc = adc_ret.INT;

                        //区間変更なしのときのADC値をGND Levelにします。
                        //16回たして4bit右シフトで平均をとります。
                        gnd_sum += adc_ret.INT;
                        if (sum_count++ == 15) {
                            gnd_level.INT = gnd_sum >> 4;
                            sum_count = 0;
                            gnd_sum = 0;
                        }

                    }

                }
            }
    }

}

void next_section_on(unsigned char sou) {
    //次区間をON
    if (train_sou[sou]) {
        if (train_sou[sou]->STATUS.BIT.SAFE) {
            //安全でないときは次区間に他の列車がかかっているのでONしない。
            if (train_sou[sou]->NEXT.BIT.ROSEN == ROSEN_NUM)
                kukan[sou] |= EnableBit[train_sou[sou]->NEXT.BIT.KUKAN];

            kukan_ON(kukan[sou], train_sou[sou]->NOW.BIT.DIR);
        }

    }
}

void read_generated_voltage(unsigned char n, unsigned char sou) {
    static char count[2], p[2];
    static int speed_buf[2][16];
    static int read_speed[2];


    if (train_sou[sou]) {

        //現位置と前位置を交互に読み込み
        if (n == 0) {
            kanshi.BIT.KUKAN = kanshi_now[sou];
        } else {
            kanshi.BIT.KUKAN = kanshi_before[sou];
        }

        LATA &= 0xC3;
        LATA |= kanshi.BYTE;

        //ADC読み込み
        PLIB_ADC_SamplingStart(ADC_ID_1);
        while (!PLIB_ADC_ConversionHasCompleted(ADC_ID_1));
        adc_ret.INT = PLIB_ADC_ResultGetByIndex(ADC_ID_1, 0);

        //ADC値からGND値を引いて絶対値をとります。
        adc_ret.INT = adc_ret.INT - gnd_level.INT;

        if (adc_ret.INT < 0)
            adc_ret.INT = -adc_ret.INT;


        //8回のピーク値をスピードデータにします。
        if (count[sou]++ == 7) {
            read_speed[sou] = adc_ret.INT;
            count[sou] = 0;
        } else if (adc_ret.INT > read_speed[sou])
            read_speed[sou] = adc_ret.INT;

        //train_sou[sou]->speed_peak = read_speed;

        speed_buf[sou][++p[sou] & 0xf] = read_speed[sou];

        //移動平均
        char m;
        unsigned int avr = 0;

        for (m = 0; m < 16; m++) {
            avr += speed_buf[sou][m];
        }

        train_sou[sou]->speed_ret.INT = avr >> 4;
    }
}

void speed_cont(unsigned char sou) {
    int err; //スピードのエラー
    int err_abs; //err絶対値
    int err_def; //errの差
    int k1 = 0x55; //感度1 0x10
    int d; //エラー補正値

    static char count[2];

    if (!train_sou[sou])return;

    // スピード設定値と実際スピードの比較
    err = train_sou[sou]->speed - train_sou[sou]->speed_ret.INT;

    //エラーの絶対値をとります
    if (err < 0)
        err_abs = -err;
    else
        err_abs = err;

    //前回のエラーと比較
    err_def = err_abs - train_sou[sou]->speed_err;
    train_sou[sou]->speed_err = err_abs;

    k1 += err_def;

    //エラー補正値の計算
    d = (err * k1) / 0x100;

    //スピード設定が0のとき補正値が負でない場合
    //完全に停止させるため補正値を-1にします
    if (train_sou[sou]->speed == 0) {
        if (d>-0x5)
            d = -0x5;
    }


    //スピードコントロール値をエラー補正値で補正
    if (++count[sou] > 10) {
        count[sou] = 0;
        train_sou[sou]->speed_cont.INT += d;
    } else {
        return;
    }



    //結果が負にならないようにする
    if (train_sou[sou]->speed_cont.INT < 0)
        train_sou[sou]->speed_cont.INT = 0;

    //接触不良時の制限(0x90)
    if (train_sou[sou]->speed_ret.INT < 0x8)
        if (train_sou[sou]->speed_cont.INT > 0x90)
            train_sou[sou]->speed_cont.INT = 0x90;

    //Speedの最大値を制限(0x1F0)
    if (train_sou[sou]->speed_cont.INT > 0xc0) //1f0
        train_sou[sou]->speed_cont.INT = 0xc0;


}

void section_change() {
    char n;

    
    for (n = 0; n < TR_COUNT; n++) {
        if (train[n].STATUS.BIT.CHG == 1) {
            //受信データに置き換え
            train[n].BEFORE.BYTE = train[n].REC_BEFORE;
            train[n].NOW.BYTE = train[n].REC_NOW;
            train[n].NEXT.BYTE = train[n].REC_NEXT;

            
            if ((train[n].NOW.BIT.ROSEN == ROSEN_NUM)
                    || (train[n].NEXT.BIT.ROSEN == ROSEN_NUM)
                    || (train[n].BEFORE.BIT.ROSEN == ROSEN_NUM)) {
                //各相の列車登録
                cont_train[train[n].NOW.BIT.SOU] = n;
                train_sou[train[n].NOW.BIT.SOU] = &train[n];
            } else {
                train[n].speed_cont.INT = 0;
            }
            
            train[n].STATUS.BIT.CHG = 0;

        }



    }






}

void board_comm(unsigned char rec_bit, unsigned char* send_flg) {
    unsigned char bit_p;
    unsigned int data_p;

    //ボード間データ通信
    data_p = comm_counter & 0x3ff;
    bit_p = EnableBit[data_p & 7]; //bit位置set
    data_p >>= 3;
    board_num = data_p;
    board_num >>= 4;

#if ROSEN_NUM == 0
    //親ボードのみポイントなどのボードに送信
    if (board_num == ROSEN_NUM || board_num == 6) {
#else
    if (board_num == ROSEN_NUM) {
#endif
        //データ送信
        *send_flg = 0; //port_output

        data_p = comm_counter;
        bit_p = EnableBit[data_p & 7]; //bit位置set
        data_p >>= 3;


        send_data = comm_data[data_p];

        //送信PORTデータセット
        if (send_data & bit_p)
            _RB11 = 1;
        else
            _RB11 = 0;

    } else {
        //データ受信
        *send_flg = 1; //port_input

        data_p = (comm_counter - 1) & 0x3ff;
        bit_p = EnableBit[data_p & 7]; //bit位置set
        data_p >>= 3;

        if (rec_bit)
            receive_data |= bit_p;
        else
            receive_data &= ~bit_p;

        if (bit_p == 0x80) {

            comm_data[data_p] = receive_data;
        }
    }
}

void comm_data_set_tx() {
    unsigned char n;
    unsigned int data_p;
    //comm_data 送信データSET
    data_p = ROSEN_NUM << 4;
    //A相
    n = cont_train[0];
    if (n != 0xff) {
        comm_data[data_p] = n + 0xA0;
        comm_data[data_p + 1] = train[n].speed_ret_tx.BYTE.H;
        comm_data[data_p + 2] = train[n].speed_ret_tx.BYTE.L;
        comm_data[data_p + 3] = train[n].henka;
        comm_data[data_p + 4] = train[n].speed_cont.BYTE.H;
        comm_data[data_p + 5] = train[n].speed_cont.BYTE.L;

        comm_data[data_p + 6] = train[n].NOW.BYTE;

    } else
        comm_data[data_p] = 0xFF;


    //B相
    n = cont_train[1];
    data_p += 0x8;

    if (n != 0xff) {
        comm_data[data_p] = n + 0xA0;
        comm_data[data_p + 1] = train[n].speed_ret_tx.BYTE.H;
        comm_data[data_p + 2] = train[n].speed_ret_tx.BYTE.L;
        comm_data[data_p + 3] = train[n].henka;
        comm_data[data_p + 4] = train[n].speed_cont.BYTE.H;
        comm_data[data_p + 5] = train[n].speed_cont.BYTE.L;

        comm_data[data_p + 6] = train[n].NOW.BYTE;
    } else
        comm_data[data_p] = 0xFF;

}

void comm_data_set_rx() {
    //受信データSET
    unsigned char n;
    unsigned int data_p;

    for (board_num = 0; board_num < BOARD_COUNT; board_num++) {
        if (board_num != ROSEN_NUM) {
            //自ボード以外受信
            data_p = board_num << 4;
            //A相
            n = comm_data[data_p];
            if ((n & 0xF0) == 0xA0) {
                n &= 0xF;

                train[n].speed_ret_rx.BYTE.H = comm_data[data_p + 1];
                train[n].speed_ret_rx.BYTE.L = comm_data[data_p + 2];

                if (train[n].NOW.BIT.ROSEN != ROSEN_NUM) {
                    if (train[n].NOW.BIT.ROSEN == train[n].BEFORE.BIT.ROSEN)
                        //現位置および前位置が他路線のとき実スピード受信
                        train[n].speed_ret.INT = train[n].speed_ret_rx.INT;
                }

                if (ROSEN_NUM == 0)//Masterボードのみ区間変化受信
                    if (train[n].NEXT.BIT.ROSEN != 0) {
                        //NextがMasterボード以外のとき受信
                        if (train[n].NEXT.BIT.ROSEN == board_num)//次区間のボードの区間変化を受信
                            train[n].henka = comm_data[data_p + 3];
                    }




                train[n].speed_rx.BYTE.H = comm_data[data_p + 4];
                train[n].speed_rx.BYTE.L = comm_data[data_p + 5];

            }
            //B相
            data_p += 0x8;
            n = comm_data[data_p];
            if ((n & 0xF0) == 0xA0) {
                n &= 0xF;

                train[n].speed_ret_rx.BYTE.H = comm_data[data_p + 1];
                train[n].speed_ret_rx.BYTE.L = comm_data[data_p + 2];

                if (train[n].NOW.BIT.ROSEN != ROSEN_NUM) {
                    if (train[n].NOW.BIT.ROSEN == train[n].BEFORE.BIT.ROSEN)
                        //現位置および前位置が他路線のとき実スピード受信
                        train[n].speed_ret.INT = train[n].speed_ret_rx.INT;
                }

                if (ROSEN_NUM == 0)//Masterボードのみ区間変化受信
                    if (train[n].NEXT.BIT.ROSEN != 0) {
                        //NextがMasterボード以外のとき受信
                        if (train[n].NEXT.BIT.ROSEN == board_num)//次区間のボードの区間変化を受信
                            train[n].henka = comm_data[data_p + 3];
                    }

                train[n].speed_rx.BYTE.H = comm_data[data_p + 4];
                train[n].speed_rx.BYTE.L = comm_data[data_p + 5];


            }
        }
    }


#if ROSEN_NUM == 0
    //point_request
    if (point_req) {
        if (!comm_data[0x70]) {
            //ready
            comm_data[0x60] = point_req; //point_data
        } else
            //busy
            if (comm_data[0x60]) {
            //リクエストクリア
            point_req = 0;
            comm_data[0x60] = 0;
        }
    }

    //yardt_request
    if (yard_req) {
        if (!comm_data[0x71]) {
            //ready
            comm_data[0x61] = yard_req; //point_data
        } else
            //busy
            if (comm_data[0x61]) {
            //リクエストクリア
            yard_req = 0;
            comm_data[0x61] = 0;
        }
    }
#endif

}


/* *****************************************************************************
 End of File
 */
app.hの編集

#include “atc.h”

の一行を追加します。

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include "system_config.h"
#include "system_definitions.h"
#include "atc.h"//追加
app.c の編集

丸ごとすげ替えます。

/*******************************************************************************
  MPLAB Harmony Application Source File
  
  Company:
    Microchip Technology Inc.
  
  File Name:
    app.c

  Summary:
    This file contains the source code for the MPLAB Harmony application.

  Description:
    This file contains the source code for the MPLAB Harmony application.  It 
    implements the logic of the application's state machine and it may call 
    API routines of other MPLAB Harmony modules in the system, such as drivers,
    system services, and middleware.  However, it does not call any of the
    system interfaces (such as the "Initialize" and "Tasks" functions) of any of
    the modules in the system or make any assumptions about when those functions
    are called.  That is the responsibility of the configuration-specific system
    files.
 *******************************************************************************/

// DOM-IGNORE-BEGIN
/*******************************************************************************
Copyright (c) 2013-2014 released Microchip Technology Inc.  All rights reserved.

Microchip licenses to you the right to use, modify, copy and distribute
Software only when embedded on a Microchip microcontroller or digital signal
controller that is integrated into your product or third party product
(pursuant to the sublicense terms in the accompanying license agreement).

You should refer to the license agreement accompanying this Software for
additional information regarding your rights and obligations.

SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY OF
MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED UNDER
CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF WARRANTY, OR
OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR EXPENSES
INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT, PUNITIVE OR
CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF PROCUREMENT OF
SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY THIRD PARTIES
(INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER SIMILAR COSTS.
 *******************************************************************************/
// DOM-IGNORE-END


// *****************************************************************************
// *****************************************************************************
// Section: Included Files 
// *****************************************************************************
// *****************************************************************************

#include "app.h"

// *****************************************************************************
// *****************************************************************************
// Section: Global Data Definitions
// *****************************************************************************
// *****************************************************************************

// *****************************************************************************
/* Application Data

  Summary:
    Holds application data

  Description:
    This structure holds the application's data.

  Remarks:
    This structure should be initialized by the APP_Initialize function.
    
    Application strings and buffers are be defined outside this structure.
 */

APP_DATA appData;

// *****************************************************************************
// *****************************************************************************
// Section: Application Callback Functions
// *****************************************************************************
// *****************************************************************************

/* TODO:  Add any necessary callback functions.
 */

// *****************************************************************************
// *****************************************************************************
// Section: Application Local Functions
// *****************************************************************************
// *****************************************************************************


/* TODO:  Add any necessary local functions.
 */


// *****************************************************************************
// *****************************************************************************
// Section: Application Initialization and State Machine Functions
// *****************************************************************************
// *****************************************************************************

/*******************************************************************************
  Function:
    void APP_Initialize ( void )

  Remarks:
    See prototype in app.h.
 */

void APP_Initialize(void) {
    /* Place the App state machine in its initial state. */
    appData.state = APP_STATE_INIT;


    /* TODO: Initialize your application's state machine and other
     * parameters.
     */
}


/******************************************************************************
  Function:
    void APP_Tasks ( void )

  Remarks:
    See prototype in app.h.
 */

void ini_train(void);
void train_set(unsigned char, unsigned char, unsigned char, unsigned char, unsigned char);

void APP_Tasks(void) {

    /* Check the application's current state. */
    switch (appData.state) {
            /* Application's initial state. */
        case APP_STATE_INIT:
        {
            bool appInitialized = true;

            DRV_TMR0_Start(); //TimerStart
            DRV_ADC_Open(); //Enable ADC

            //USART
            if (handleUSART0 == DRV_HANDLE_INVALID) {
                handleUSART0 = DRV_USART_Open(0, DRV_IO_INTENT_READWRITE | DRV_IO_INTENT_NONBLOCKING);
                appInitialized &= (DRV_HANDLE_INVALID != handleUSART0);
            }




            //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();
                LCD_str("PC_REM");
            }

            //-----ATC-------
            cont_train[0] = 0xff;
            cont_train[1] = 0xff;

            train_sou[0] = NULL;
            train_sou[1] = NULL;

            ini_train();

            //列車A相
            //            train[0].mascon = 0x18; //スピードを変える時はここを変更
            //            train_set(0, 0, 1, 0, 0); //n,rosen,section,dir,sou

            //列車B相
            //            train[1].mascon = 0x1c; //スピードを変える時はここを変更
            //            train_set(1, 0, 5, 0, 1); //n,rosen,section,dir,sou

            if (appInitialized) {

                appData.state = APP_STATE_SERVICE_TASKS;
            }
            break;
        }

        case APP_STATE_SERVICE_TASKS:
        {

            unsigned char n, p;
            unsigned char ret;
            union byte_access time_out;
            long count;

            rx_data[0] = 0;
            rx_data[1] = 0;
            time_out.INT = 0;

            U1STAbits.OERR = 0; //受信バッファ・エラークリア

            ret = 0;
            while (ret != 0x55) {
                //0x55が来るまでずーと待つ
                while (!DRV_USART_ReceiverBufferIsEmpty(DRV_USART_INDEX_0)) {
                    ret = DRV_USART_ReadByte(DRV_USART_INDEX_0);
                }
            }


            for (n = 1; n < RX_BYTE; n++) {
                count = 0;
                while (DRV_USART_ReceiverBufferIsEmpty(DRV_USART_INDEX_0)) {
                    if (count++ > 0xfffff)break;
                }
                if (count > 0xfffff)break; //TimeOutで外へ

                rx_data[n] = DRV_USART_ReadByte(DRV_USART_INDEX_0);
            }

            if (rx_data[1] == 0xAA) {
                //ヘッダー正常(0x55 0xAA)
                time_out.INT = count;

                for (n = 0; n < TR_COUNT; n++) {
                    p = n * RX_DATA_BYTE + 2;
                    train[n].mascon = rx_data[p];
                    train[n].speed = train[n].mascon;
                    train[n].REC_NOW = rx_data[p + 1];
                    train[n].REC_BEFORE = rx_data[p + 2];
                    train[n].REC_NEXT = rx_data[p + 3];

                    //区間変更フラグクリア
                    if (rx_data[p + 4] & 1) {
                        train[n].henka = 0;
                        train[n].STATUS.BIT.CHG = 1;
                    }

                    //安全フラグ
                    if (rx_data[p + 4] & 0x80)
                        train[n].STATUS.BIT.SAFE = 0x1;
                    else
                        train[n].STATUS.BIT.SAFE = 0;

                }

                //232送信
                tx_data[0] = 0x55;//ヘッダー0x55,0xAA
                tx_data[1] = 0xAA;

                for (n = 0; n < TR_COUNT; n++) {
                    p = n * TX_DATA_BYTE + 2;
                    //スピード読み取り値の送信
                    tx_data[p] = train[n].speed_ret.BYTE.H;
                    tx_data[p + 1] = train[n].speed_ret.BYTE.L;
                    //区間変化情報の送信
                    tx_data[p + 2] = train[n].henka;
                    tx_data[p + 3] = train[n].point;
                    tx_data[p + 4] = train[n].yard;
                    if (train[n].NOW.BIT.ROSEN == ROSEN_NUM) {
                        tx_data[p + 5] = train[n].speed_cont.BYTE.H;
                        tx_data[p + 6] = train[n].speed_cont.BYTE.L;
                    } else {
                        //他のボードのときcomm_dataのcont_speedを送る
                        tx_data[p + 5] = train[n].speed_rx.BYTE.H;
                        tx_data[p + 6] = train[n].speed_rx.BYTE.L;
                    }
                }


                // 送信用メモリデータをPCに転送します
                for (n = 0; n < TX_BYTE; n++) {
                    DRV_USART_WriteByte(DRV_USART_INDEX_0, tx_data[n]);
                }

            }


            BSP_DelayMs(20);

            //Debug用LCD表示
            LCD_posyx(0, 0);
            LCD_hex(train[0].mascon);
            LCD_str(" ");
            LCD_hex(train[1].mascon);

            LCD_posyx(1, 0);
            LCD_hex(train[0].NOW.BYTE);
            LCD_hex(train[0].NEXT.BYTE);

            LCD_hex(train[1].NOW.BYTE);
            LCD_hex(train[1].NEXT.BYTE);

        }
            /* TODO: implement your application state machine.*/


            /* The default state should never be executed. */
        default:
        {
            /* TODO: Handle error in application's state machine. */
            break;
        }
    }
}


void ini_train() {
    char n;

    for (n = 0; n < TR_COUNT; n++) {
        train[n].BEFORE.BYTE = 0x38;
        train[n].NOW.BYTE = 0x38;
        train[n].NEXT.BYTE = 0x38;
        train[n].ANEXT.BYTE = 0x38;
        train[n].STATUS.BYTE = 0;
        train[n].speed = 0;
        train[n].henka = 0;
    }

}

void train_set(unsigned char n, unsigned char rosen, unsigned char sec, unsigned char dir, unsigned char sou) {
    train[n].NOW.BIT.ROSEN = rosen;
    train[n].NOW.BIT.KUKAN = sec;
    train[n].NOW.BIT.DIR = dir;
    train[n].NOW.BIT.SOU = sou;

    train[n].BEFORE.BYTE = train[n].NOW.BYTE;
    train[n].NEXT.BYTE = train[n].NOW.BYTE;
    train[n].ANEXT.BYTE = train[n].NOW.BYTE;

    if (dir) {
        //逆転 
        train[n].BEFORE.BIT.KUKAN++;
        train[n].NEXT.BIT.KUKAN--;
        train[n].ANEXT.BIT.KUKAN = train[n].NOW.BIT.KUKAN - 2;
    } else {
        //正転
        train[n].BEFORE.BIT.KUKAN--;
        train[n].NEXT.BIT.KUKAN++;
        train[n].ANEXT.BIT.KUKAN += 2;
    }

    train[n].STATUS.BIT.CHG = 0;
    train[n].STATUS.BIT.SAFE = 1;

}


/*******************************************************************************
 End of File
 */
SourceFiles-app-system_config-defaultにあるsystem_interrupt.cの編集

最後の方のTIMERインタラプトの部分に timer_int_func();を一行追加します。

void __ISR(_TIMER_1_VECTOR, ipl7AUTO) IntHandlerDrvTmrInstance0(void)
{
    PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_TIMER_1);
    
    timer_int_func();//追加
}
 /*******************************************************************************
 End of File
*/

以上です。

PC側のソフト

PCのソフトはVisualStudioのVBで作ります。

スピードメーターには列車にかけているパワーと実際の速度が表示されます。

VisualStudioでVBのWindowsフォームアプリケーションのプロジェクトを作成します。プロジェクト名はpc_remoteにしました。

フォームにツールボックスから

  • ComboBox
  • Buttonを2つ
  • Timer
  • SerialPort

を追加します。

・プロジェクトのユーザーコントロールの追加でContPanel.vbを追加します。

・ContPanelにコントロールを追加します。

コードの編集

Form1.vbデザインでF7を押すとコードが開くので、すべて削除して以下のコードを入れます。

Imports System.IO.Ports

Public Class Form1

    Const Tx_Byte As Integer = 128
    Const Rx_Byte As Integer = 128
    Const TxData数 As Integer = 12
    Const RxData数 As Integer = 12
    Private 列車数 As Integer = 2
    Const 最大列車数 As Integer = 8
    Public ポイント数 As Integer
    Const yard数 As Integer = 1
    Private TM As Boolean

    Private Remcon As List(Of ContPanel) = New List(Of ContPanel)()


    Class 位置情報
        Public 区間 As Integer
        Public 路線 As Integer
        Public 方向 As Integer
        Public 相 As Integer

        Sub New(ByVal 区 As Integer, ByVal 路 As Integer, ByVal 向 As Integer, ByVal sou As Integer)
            区間 = 区
            路線 = 路
            方向 = 向
            相 = sou
        End Sub

        Public ReadOnly Property Byte情報() As Byte
            Get
                Return 区間 + 路線 * 8 + 方向 * &H80 + 相 * &H40
            End Get

        End Property

        Sub 次区間()
            If 方向 = 0 Then
                区間 = (区間 + 1) And &H7
            Else
                区間 = (区間 - 1) And &H7
            End If
        End Sub
        Sub 前区間()
            If 方向 = 0 Then
                区間 = (区間 - 1) And &H7
            Else
                区間 = (区間 + 1) And &H7
            End If
        End Sub


        Function copy()
            Return New 位置情報(区間, 路線, 方向, 相)
        End Function

        Sub 逆転()
            If 方向 = 0 Then
                方向 = 1
            Else
                方向 = 0
            End If
        End Sub

        ' = 演算子をオーバーロード
        Public Shared Operator =(ByVal c1 As 位置情報, ByVal c2 As 位置情報) As Boolean
            If (c1.路線 = c2.路線) And (c1.区間 = c2.区間) And (c1.方向 = c2.方向) Then
                Return True
            Else
                Return False
            End If
        End Operator

        Public Shared Operator <>(ByVal c1 As 位置情報, ByVal c2 As 位置情報) As Boolean
            Return Not c1 = c2
        End Operator
    End Class

    Class train


        Public 現位置 As 位置情報
        Public 前位置 As 位置情報
        Public 次位置 As 位置情報
        Public 次々位置 As 位置情報

        Public 区間変化 As Boolean
        Public 安全 As Boolean
        Public speed As Single
        Public 逆転 As Integer

        Public 設定speed As Integer
        Public ポイント As Integer
        'Public point処理状況 As p処理
        'Public yard処理状況 As y処理
        Public ポイント番号 As Integer
        Public yard番号 As Integer
        Public 相変更FLAG As Boolean
        Public Power As Integer



        Public yard As Integer



        Sub New()
            'Dummyで登録(路線=7)
            現位置 = New 位置情報(0, 7, 0, 0)
            前位置 = 現位置.copy
            次位置 = 現位置.copy
            次々位置 = 現位置.copy
        End Sub


        Sub New(ByVal 現 As 位置情報)
            現位置 = 現
            前位置 = 現位置.copy
            前位置.前区間()
            次位置 = 現位置.copy
            次位置.次区間()
            次々位置 = 次位置.copy
            次々位置.次区間()

            区間変化 = True
        End Sub

        Sub New(ByVal 現 As 位置情報, ByVal 前 As 位置情報, ByVal 次 As 位置情報, ByVal 次々 As 位置情報)
            現位置 = 現
            前位置 = 前
            次位置 = 次
            次々位置 = 次々
        End Sub

        Sub 区間変更()
            前位置 = 現位置.copy
            現位置 = 次位置.copy
            次位置 = 次々位置.copy
            次々位置.次区間()

            区間変化 = True
        End Sub


        Sub 逆転処理()
            '現位置を前位置に、前位置を現位置に
            現位置.逆転()
            前位置.逆転()

            次位置 = 現位置.copy '前位置に入れるため
            現位置 = 前位置.copy
            前位置 = 次位置.copy

            次位置 = 現位置.copy
            次位置.次区間()
            次々位置 = 次位置.copy
            次々位置.次区間()
        End Sub

        Sub 相変更()

            If 現位置.相 = 0 Then
                現位置.相 = 1
                前位置.相 = 1
                次位置.相 = 1
                次々位置.相 = 1
            Else
                現位置.相 = 0
                前位置.相 = 0
                次位置.相 = 0
                次々位置.相 = 0
            End If


            相変更FLAG = True
        End Sub


        Public ReadOnly Property Status As Byte
            Get
                Dim ret As Byte

                If 区間変化 Then ret = 1
                If Not 相変更FLAG Then
                    '相変更中は区間変更しない
                    If 安全 Then ret += &H80
                End If

                Return ret
            End Get
        End Property

        Public ReadOnly Property Enable As Boolean
            '列車運転中Enable
            Get
                If 現位置.路線 = 7 Then
                    Return False
                Else
                    Return True
                End If
            End Get
        End Property



    End Class



    Private 列車(8) As train


    'デリゲートの宣言
    Public Delegate Sub RsDelegate()

    'データーの受信
    Private Sub DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) _
        Handles SerialPort1.DataReceived

        'データの受信はマルチスレッドで行われる為にデリゲートを使用して
        'メインのスレッドでデータ処理の必要があります。
        Me.Invoke(New RsDelegate(AddressOf receive_data), New Object() {})
    End Sub


    '受信データー
    Public Sub receive_data()
        Dim byteArray(SerialPort1.BytesToRead - 1) As Byte

        Static GND_Level(9) As Integer
        Static p As Integer

        If byteArray.Length <> Rx_Byte Then
            Exit Sub
        End If



        'ポートのバッファから読み込み
        SerialPort1.Read(byteArray, 0, SerialPort1.BytesToRead)

        'ヘッダーチェック
        If (byteArray(0) <> &H55) Or (byteArray(1) <> &HAA) Then Exit Sub


        'Label3.Text = String.Format("{0:X}", byteArray(4))

        Dim 変化 As Byte = byteArray(4)


        For n = 0 To 列車数 - 1
            Dim pos As Integer = RxData数 * n
            Dim speed As Integer = 0
            Dim power As Integer = 0

            If 列車(n).Enable Then
                'スピードメータ-
                speed = byteArray(pos + 2) * 256 + byteArray(pos + 3)
                power = byteArray(pos + 7) * 256 + byteArray(pos + 8)
            End If

            Remcon(n).Speed_Meter = speed
            Remcon(n).Power_Meter = power
            Remcon(n).区間表示 = 列車(n).現位置.Byte情報


            列車(n).Power = power



            '区間変化の確認
            If byteArray(pos + 4) = &H80 Then
                列車(n).区間変更()
                '全ての列車の情報更新
                For m = 0 To 列車数 - 1
                    列車(n).区間変化 = True
                Next
            Else
                    '区間変化フラグクリア
                    列車(n).区間変化 = False
            End If


        Next

        Dim 減速時スピード = 20


        '安全確認
        For n = 0 To 列車数 - 1
            If 列車(n).Enable Then

                Select Case 安全確認(n)
                    Case "停止"
                        列車(n).安全 = False
                        列車(n).設定speed = 0
                        Remcon(n).信号 = "赤"
                    Case "減速"
                        Dim 減速速度 As Integer = 減速時スピード '減速時のスピード

                        列車(n).安全 = True
                        If Remcon(n).Speed_Cont > 減速速度 Then
                            列車(n).設定speed = 減速速度
                        Else
                            列車(n).設定speed = Remcon(n).Speed_Cont
                        End If
                        Remcon(n).信号 = "黄"
                    Case "安全"
                        Remcon(n).信号 = "緑"
                        列車(n).安全 = True

                        If 列車(n).逆転 = 1 Then
                            '逆転フラグが1なら逆転のため停止
                            列車(n).設定speed = 0
                            'ElseIf 列車(n).point処理状況 = p処理.通過待ち Then
                            '    'ポイント通過待ちのため停止
                            '    列車(n).設定speed = 0
                            'ElseIf 列車(n).yard処理状況 = y処理.Yard停止 Then
                            '    'Yardで停止
                            '    列車(n).設定speed = 0
                            '    Remcon(n).Speed_Cont = 0
                        Else
                            列車(n).設定speed = Remcon(n).Speed_Cont
                        End If

                    Case "向合停止"
                        列車(n).安全 = False
                        列車(n).設定speed = 0
                        Remcon(n).信号 = "赤"
                        Remcon(n).CheckBox1.Checked = True  '強制逆転

                End Select

            Else
                Remcon(n).信号 = "灰"
            End If
        Next


    End Sub

    Function 安全確認(ByVal n As Integer) As String

        Dim 停止 As Boolean = False
        Dim 減速 As Boolean = False
        Dim 向合停止 As Boolean = False


        For m As Integer = 0 To 列車数 - 1
            If m <> n Then
                '次位置が他の列車の現、前、次位置と同じなら停止
                If (列車(n).次位置.路線 = 列車(m).現位置.路線) And
                   (列車(n).次位置.区間 = 列車(m).現位置.区間) Then 停止 = True

                If (列車(n).次位置.路線 = 列車(m).前位置.路線) And
                   (列車(n).次位置.区間 = 列車(m).前位置.区間) Then 停止 = True

                If (列車(n).次位置.路線 = 列車(m).次位置.路線) And
                   (列車(n).次位置.区間 = 列車(m).次位置.区間) Then 停止 = True


                '次々位置が他の列車の現、前、次位置と同じなら減速
                If (列車(n).次々位置.路線 = 列車(m).現位置.路線) And
                   (列車(n).次々位置.区間 = 列車(m).現位置.区間) Then 減速 = True

                If (列車(n).次々位置.路線 = 列車(m).前位置.路線) And
                   (列車(n).次々位置.区間 = 列車(m).前位置.区間) Then 減速 = True

                If (列車(n).次々位置.路線 = 列車(m).次位置.路線) And
                   (列車(n).次々位置.区間 = 列車(m).次位置.区間) Then 減速 = True

                If 停止 Then
                    If (n > m) And (列車(n).現位置.方向 <> 列車(m).現位置.方向) Then
                        向合停止 = True '方向逆で優先順位低いとき
                        Return "向合停止"
                    End If
                    Exit For
                End If


            End If


        Next


        If 停止 Then Return "停止"
        If 減速 Then Return "減速"

        Return "安全"

    End Function




    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        'ポート接続処理
        SerialPort1.PortName = ComboBox1.Text

        SerialPort1.BaudRate = 115200

        SerialPort1.Parity = IO.Ports.Parity.None
        SerialPort1.DataBits = 8
        SerialPort1.StopBits = IO.Ports.StopBits.One

        SerialPort1.ReceivedBytesThreshold = Rx_Byte

        SerialPort1.ReadTimeout = 500
        SerialPort1.WriteTimeout = 500

        SerialPort1.Open()

        Timer1.Enabled = True

        Button1.Enabled = False
        Button2.Enabled = True
        ComboBox1.Enabled = False

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        'ポート切断処理
        Timer1.Enabled = False
        SerialPort1.Close()
        ComboBox1.Enabled = True
        Button1.Enabled = True
        Button2.Enabled = False
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        Dim Txdata(Tx_Byte) As Byte
        Dim 加速 As Single = 0.3


        'ヘッダーとして&H55,&HAAを送る
        Txdata(0) = &H55
        Txdata(1) = &HAA



        For n = 0 To 列車数 - 1
            If 列車(n).Enable Then

                'speed 加速・減速
                If 列車(n).設定speed - 列車(n).speed < 0 Then
                    加速 = 0.5 '減速時
                End If

                列車(n).speed += (列車(n).設定speed - 列車(n).speed) * 加速

                '逆転フラグ確認
                If 列車(n).逆転 = 1 Then
                    If Int(列車(n).speed) = 0 Then
                        If 列車(n).Power = 0 Then
                            '列車が停止したら逆転
                            列車(n).逆転処理()
                            列車(n).逆転 = 0
                            列車(n).区間変化 = True  '位置情報更新の為


                            '逆転ボタン戻し
                            Remcon(n).CheckBox1.Checked = False
                            Remcon(n).CheckBox1.Enabled = True
                        End If
                    End If
                End If

            End If
        Next


        For n = 0 To 最大列車数 - 1
            Dim p As Integer = n * TxData数 + 2
            Txdata(p) = Int(列車(n).speed) 'speed data
            Txdata(p + 1) = 列車(n).現位置.Byte情報
            Txdata(p + 2) = 列車(n).前位置.Byte情報
            Txdata(p + 3) = 列車(n).次位置.Byte情報
            Txdata(p + 4) = 列車(n).Status
            Txdata(p + 5) = 列車(n).ポイント
            Txdata(p + 6) = 列車(n).yard

        Next


        Try
            SerialPort1.Write(Txdata, 0, Tx_Byte) 'データ送信

        Catch ex As Exception
            'エラーのとき
            Button2_Click(Nothing, Nothing) '切断
            MsgBox(ex.Message)
        End Try

    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim ports As String() = SerialPort.GetPortNames()

        If ports.Count = 0 Then
            MsgBox("シリアルポートが見つかりません。")
            Button1.Enabled = False
        Else
            '使用可能なシリアルポートをコンボボックスに登録
            ComboBox1.Items.AddRange(ports)
            ComboBox1.SelectedIndex = 0
        End If


        'ComboBox1.Text = "COM5"

        Me.Text = "PC Remote"
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.Fixed3D
        Me.MinimizeBox = False
        Me.MaximizeBox = False

        Button1.Text = "接続"
        Button2.Text = "切断"

        Button2.Enabled = False '切断ボタンdisable



        Timer1.Interval = 200 'タイマーインターバル200ms


        'Dummy Data
        For n = 0 To 最大列車数 - 1
            列車(n) = New train()
        Next

        '列車情報設定 
        列車(0) = New train(New 位置情報(1, 0, 0, 0))
        列車(1) = New train(New 位置情報(4, 0, 0, 1))



        For n As Integer = 0 To 列車数 - 1
            Remcon.Add(New ContPanel(n))
            Remcon(n).Top = 10
            Remcon(n).Left = Remcon(0).Width * n + 2
            Controls.Add(Remcon(n))

            AddHandler Remcon(n).逆転_Click, AddressOf 逆転_Click
            'AddHandler Remcon(n).ポイント_Click, AddressOf ポイント_Click
            'AddHandler Remcon(n).Yard_Click, AddressOf Yard_Click

            'Remcon(n).add_point(ポイント) 'リモコンにポイント番号登録
        Next



    End Sub

    Private Sub 逆転_Click(ByVal index As Integer)
        '逆転ボタン確認
        If Remcon(index).CheckBox1.Checked Then
            '列車逆転のため停止させる
            列車(index).逆転 = 1
            Remcon(index).CheckBox1.Enabled = False
        End If

    End Sub


    Private Sub Button3_Click(sender As Object, e As EventArgs)
        列車(0).区間変化 = True
        列車(1).区間変化 = True
    End Sub
End Class


・同様にContPanel.vbデザインでF7を押してコードを以下と入れ替えます。

Public Class ContPanel

    Public Index As Integer
    Public Event 逆転_Click(ByVal index As Integer)


    Public Sub New(id As Integer)

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        Index = id
    End Sub

    Public Property Speed_Cont() As Integer 'Speed コントローラ
        Get
            Return TrackBar1.Value
        End Get

        Set(ByVal Value As Integer)
            TrackBar1.Value = Value
        End Set
    End Property

    Public WriteOnly Property 信号() As String
        Set(ByVal value As String)

            Select Case value
                Case "緑"
                    Label1.BackColor = Color.LightGreen
                Case "黄"
                    Label1.BackColor = Color.Yellow
                Case "赤"
                    Label1.BackColor = Color.LightPink
                Case "灰"
                    Label1.BackColor = Color.LightGray
            End Select
        End Set
    End Property

    Public WriteOnly Property 区間表示() As Byte
        Set(ByVal value As Byte)
            Dim 区間 = value And &H7
            Dim 路線 = (value And &H18) / 8

            Label1.Text = String.Format("路線:{0}" + vbCrLf + "区間:{1}", 路線, 区間)

        End Set
    End Property



    Public WriteOnly Property Speed_Meter() As Integer  'Speed メーター
        Set(ByVal Value As Integer)
            Dim speed_max As Integer = &H80

            Dim sp As Integer = (1 - Value / speed_max) * PictureBox1.Height

            Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)

            g.Clear(Color.White)

            Dim pen As Pen = New Pen(Color.Red, 3)
            Dim brush As Brush = Brushes.Pink


            Dim p1 As Point = New Point(0, sp)
            Dim p2 As Point = New Point(PictureBox1.Width, sp)

            Dim rect As Rectangle = New Rectangle(p1, New Size(PictureBox1.Width, PictureBox1.Height - sp))

            g.FillRectangle(brush, rect)


            'PictureBox1.Refresh()

        End Set
    End Property

    Public WriteOnly Property Power_Meter() As Integer  'Power メーター
        Set(ByVal Value As Integer)
            Dim speed_max As Integer = &HC0


            Dim sp As Integer = (1 - Value / speed_max) * PictureBox1.Height

            Dim g As Graphics = Graphics.FromImage(PictureBox1.Image)

            'g.Clear(Color.White)

            Dim pen As Pen = New Pen(Color.Red, 3)
            Dim brush As Brush = Brushes.Pink


            Dim p1 As Point = New Point(0, sp)
            Dim p2 As Point = New Point(PictureBox1.Width, sp)

            g.DrawLine(pen, p1, p2)

            PictureBox1.Refresh()

        End Set
    End Property

    Private Sub ContPanel_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        TrackBar1.Maximum = 50

        'スピードメーター用
        Dim bmp As New Bitmap(PictureBox1.Size.Width, PictureBox1.Size.Height)
        PictureBox1.Image = bmp

    End Sub

    Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBox1.CheckedChanged
        RaiseEvent 逆転_Click(Index)
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        '停止ボタンでスライダーを0にする()
        TrackBar1.Value = 0
    End Sub
End Class

以上です。