SUBボードの製作

エンドレスループを増やすためにSUBボードを作ります。

KiCadの回路図

・SUBボードは中点の6VとPIC用電源3.3Vを作る部分がありません。これらの電源はメインボードから10PINのコネクターでもらいます。また、PCと接続する232Cのコネクタもありません。

・また、Mainボードと同期を取るためにMainボードはTimerInterrupt開始時にRB10に同期パルスを出します。SUBボードはこの同期パルスをRB10で受けて外部Interruptをかけます。

PIC32のソフト

PIC32のソフトはメイン・サブの両方で使えるようにコンパイラ擬似命令で切り替えられるように作ります。

Harmonyの設定

 

【ADC】

【Timer】

【I2C】

【USART】

【External Interrupt】

【PinDiagram】

【PinSettings】

・コードの編集前に文字コードをShift-Jisに変更します。

・MainボードとSubボードのコードをコンパイラ擬似命令で切り替えます。ポートの設定が異なるのでsys_ports_static.cの以下の部分をコメントアウトしておいて、system_init.cで切り替えます。

・system_init.cを編集します。

    /* Initialize System Services */
    SYS_PORTS_Initialize();

#if ROSEN_NUM == 0
    _TRISB10 = 0;
#else
    PLIB_PORTS_RemapInput(PORTS_ID_0, INPUT_FUNC_INT1, INPUT_PIN_RPB10);
#endif

    /*** Interrupt Service Initialization Code ***/
    SYS_INT_Initialize();

#if ROSEN_NUM != 0
    /*Setup the INT_SOURCE_EXTERNAL_1 and Enable it*/
    SYS_INT_VectorPrioritySet(INT_VECTOR_INT1, INT_PRIORITY_LEVEL6);
    SYS_INT_VectorSubprioritySet(INT_VECTOR_INT1, INT_SUBPRIORITY_LEVEL0);
    SYS_INT_ExternalInterruptTriggerSet(INT_EXTERNAL_INT_SOURCE1, INT_EDGE_TRIGGER_FALLING);
    SYS_INT_SourceEnable(INT_SOURCE_EXTERNAL_1);
#endif

atc.hにある

#define ROSEN_NUM 0    //路線(ボード)番号

でメインボードとそれ以外のSUBボードをきりかえます。0がメインボードで1以降がSUBボードです。上の部分でRB10ポートをメインの時OUTポートに、SUBの時外部INTポートに切り替えています。

下の部分でSUBボードの時のみ、外部INTの設定を有効にしています。

・3つのファイルをプロジェクトに追加します。

  • atc.h
  • timer_int.c
  • lcd.c

・まずヘッダーファイル’atc.h’を追加します。

作ったプロジェクトのあるフォルダー 3sections(プロジェクト名)-firmware-src のフォルダーに追加するファイルを入れておいて、Header Filesのappを右クリック、Add Existing Itemでプロジェクトに追加します。

 

・’timer_int.c’ と’lcd.c’ はSource Filesのappを右クリック、Add Existing Itemでプロジェクトに追加します。

または、New->xc32_header.h…で新規に作成し、名前を’atc.h’にします。出来たコードをすべて削除し、以下のコードをコピペします。’timer_int.c’と’lcd.c’も同様にNew->xc32_newfile.cで作成します。

atc.h

//
//2021_0821

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

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


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

bool ext_int_enable;

//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 SP_MANU : 1; //スピード制御
    unsigned char DUMY : 3; //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 err_th;

    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;

};





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;

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; //ポイントリクエスト

int err_buff[2][16]; 
union byte_access err_avr[2];

timer_int.c

//
//2021_0821

#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, unsigned char);
void speed_pulse_off();
void section_change_monitor(unsigned char, unsigned char); //区間変化監視
void next_section_on(unsigned char, 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); //ボード間通信
void comm_data_set_tx(void); //通信データ設定TX
void comm_data_set_rx(void); //通信データ設定RX
void point_cont(void);
void yard_cont(void);

void timer_int_func() {

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

    rec_bit = _RB11;

#if ROSEN_NUM==0
    _RB10 = 0; //ext_int 同期パルス(master board))
#else
    _RB14 = 1; //OSC
#endif


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



#if ROSEN_NUM!=0
    //子ボードは同期パルス幅が大きいときcomm_counterをリセット
    if (_RB10 == 0) {
        comm_counter = 0;
    }
#endif
    //int_counterはcomm_counterの下位9bitの0-0x1ffでカウントします。
    int_counter = comm_counter & 0x1ff;

    _TRISB11 = 1;

    switch (int_counter) {
        case 0:
            section_change();
            //インタラプトカウンタが0のときA相のspeedパルスON
            //if (train_sou[0]) {
            //if (!train_sou[0]->NOW.BIT.DIR) 
            speed_pulse_on(0, 0); //sou,dir
            //}

            speed_pulse_on(1, 0);
            break;

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

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

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

        case 0x100:
            //インタラプトカウンタが0x100のときB相のspeedパルスON
            speed_pulse_on(0, 1);
            speed_pulse_on(1, 1);
            break;
        case 0x102:
            //インタラプトカウンタが0x2のときA相の区間変化監視(B相は0x102のとき)
            section_change_monitor(0, 1);
            section_change_monitor(1, 1);
            break;

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

        case 0x1fa:
            speed_cont(1);

            break;

    }

    //A相 スピード読み込み 0xf0 ? 0xf7 8回
    if (int_counter >= 0xf0 && int_counter < 0xf8) {
        read_generated_voltage(int_counter & 1, 0); //int_counter & 1
    }
    //B相 スピード読み込み 0x1f0 ? 0x1f7 8回
    if (int_counter >= 0x1f0 && int_counter < 0x1f8) {
        read_generated_voltage(int_counter & 1, 1);
    }


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

    switch (comm_counter) {
#if ROSEN_NUM == 0//-----ポイント yard 制御----------       
        case 0x10:
            point_cont();
            break;
        case 0x11:
            yard_cont();
            break;
#endif
        case 0x3f0:
            comm_data_set_tx();
            break;
        case 0x3f8:
            comm_data_set_rx();
            break;

    }


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


#if ROSEN_NUM==0
    //幅広同期パルスOFF
    if (comm_counter == 0) {
        _RB10 = 1;
    }
#else
    _RB14 = 0; //OSC
#endif

}

void speed_pulse_on(unsigned char sou, unsigned char dir) {

    if (train_sou[sou]) {
        if (train_sou[sou]->NOW.BIT.DIR == dir) {
            kukan[sou] = 0; //kukan clr

            if (train_sou[sou]->STATUS.BIT.SP_MANU == 1)
                speed_pw[sou] = train_sou[sou]->speed; //manual speed
            else
                speed_pw[sou] = train_sou[sou]->speed_cont.INT; //auto speed

            //区間メモリに現・前位置の区間を設定し区間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, unsigned char dir) {
    static char sum_count;
    static int gnd_sum;

    if (train_sou[sou]) {
        if (train_sou[sou]->NOW.BIT.DIR == dir) {
            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 >= 0x280) {
                                train_sou[sou]->henka = 0x80; //区間変化
                            }

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

                    }
                }
        }
    }

}

void next_section_on(unsigned char sou, unsigned char dir) {
    //次区間をON
    if (train_sou[sou]) {
        if (train_sou[sou]->NOW.BIT.DIR == dir) {
            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 unsigned char count[2], p[2], count_B[2];
    static int speed_buf[2][32]; //16
    static int read_speed[2];
    static int speed_sou[2]; //相手のスピード

    char m;

    if (train_sou[sou]) {

        char sou_b = 0;
        if (sou == 0) sou_b = 1;

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

        //        LATA &= 0xC3;
        //        LATA |= kanshi.BYTE;
        PORTA = 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;
        //            return; //ここで終了
        //        }


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

        speed_sou[sou] = read_speed[sou];

        //        p[sou]++;
        //        if (p[sou] == 16) p[sou] = 0;
        //
        //        speed_buf[sou][p[sou]] = read_speed[sou]; //0xf
        speed_buf[sou][++p[sou] & 0xf] = read_speed[sou]; //0xf

        //peek reset
        if (++count[sou] == 8) {
            read_speed[sou] = adc_ret.INT;
            count[sou] = 0;
        }


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

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

        avr >>= 4; //4
        //avr = read_speed[sou];

        //読み取り電圧補正
        //        if (volt_kb.INT < 0x100 || volt_kb.INT > 0x280)
        //            volt_kb.INT = 0x150;
        //
        //        avr = avr * volt_kb.INT;
        //        avr = avr / 0x100;

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


        if (count_B[sou]++ == 15) {
            count_B[sou] = 0;

            if (avr < train_sou[sou]->speed_ret.INT) {
                train_sou[sou]->speed_ret.INT--;
            } else if (avr > train_sou[sou]->speed_ret.INT)
                train_sou[sou]->speed_ret.INT++;
        }


        if (train_sou[sou]->NOW.BIT.ROSEN == ROSEN_NUM) {
            if (train_sou[sou]->NOW.BIT.ROSEN != train_sou[sou]->BEFORE.BIT.ROSEN) {
                if (train_sou[sou]->speed_ret_rx.INT > train_sou[sou]->speed_ret.INT) {
                    train_sou[sou]->speed_ret.INT = train_sou[sou]->speed_ret_rx.INT;
                }
            }
        }


    }
}

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];
    static char count_avr[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;

    err_buff[sou][count_avr[sou]++ & 0xF] = err; //err_abs

    //---error avrage---
    int err_sum = 0;
    char c;
    for (c = 0; c < 16; c++) {
        err_sum += err_buff[sou][c];
    }
    err_avr[sou].INT = err_sum / 0x10; //>>4

    err_abs = err_avr[sou].INT;
    if (err_abs < 0)err_abs = -err_abs;

    //--test--
    //d = (err * err_avr[sou].INT) / 0x80;
    d = err_avr[sou].INT / 0x10;
    int d_max = 0x20;

    if (d > d_max)d = d_max;
    else if (d < -d_max)d = -d_max;

    char cont_t = 10; //40

    //スピード設定が0のとき補正値が負でない場合
    //完全に停止させるため補正値を-1にします
    if (train_sou[sou]->speed == 0) {
        cont_t = 2; //減速早く
        if (d>-0x1)
            d = -0x1; //-0x10
    } else {
        //if (err_avr[sou].INT < train_sou[sou]->err_th)return;
        //if (err_abs < train_sou[sou]->err_th)return;
    }


    //if (d < 0) cont_t = 20; //減速は早く


    //スピードコントロール値をエラー補正値で補正
    if (++count[sou] > cont_t) {//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;

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

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


}

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 (cont_train[0] == n) {
                cont_train[0] = 0xff;
                train_sou[0] = NULL;
            }
            if (cont_train[1] == n) {
                cont_train[1] = 0xff;
                train_sou[1] = NULL;
            }

            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].henka = 0;
            train[n].STATUS.BIT.CHG = 0;

        }



    }






}

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

    data_p = (comm_counter - 1) & 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
        //データ受信
        _TRISB11 = 1;

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

        if (bit_p == 0x80) {

            comm_data[data_p] = receive_data;
        }

    }


    //ボード間データ通信
    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
        //データ送信
        _TRISB11 = 0;

        send_data = comm_data[data_p];

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

    }
}

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.BYTE.H; //speed_ret_tx
        comm_data[data_p + 2] = train[n].speed_ret.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;

        //comm_data[data_p + 7] = debug1.BYTE.L;

    } 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.BYTE.H; //speed_ret_tx
        comm_data[data_p + 2] = train[n].speed_ret.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];

                if (train[n].NOW.BIT.ROSEN != ROSEN_NUM) {
                    if (train[n].NEXT.BIT.ROSEN == ROSEN_NUM)
                        //ポイント切り替え時次区間でspeed_cont渡し
                        train[n].speed_cont.INT = train[n].speed_rx.INT;
                }


            }
            //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 (train[n].NOW.BIT.ROSEN != ROSEN_NUM) {
                    if (train[n].NEXT.BIT.ROSEN == ROSEN_NUM)
                        //ポイント切り替え時次区間でspeed_cont渡し
                        train[n].speed_cont.INT = train[n].speed_rx.INT;
                }


            }
        }
    }


#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

}

//ポイント制御

void point_cont() {
    unsigned char n;

    if (!point_req) {
        for (n = 0; n < TR_COUNT; n++) {
            if ((train[n].point & 0x80) != 0) {
                point_req = train[n].point;
                train[n].point = 0;
                break;
            }
        }
    }
}

//ヤード制御

void yard_cont() {
    unsigned char n;

    if (!yard_req) {
        for (n = 0; n < TR_COUNT; n++) {
            if ((train[n].yard & 0x80) != 0) {
                yard_req = train[n].yard;
                train[n].yard = 0;
                break;
            }
        }
    }
}



/* *****************************************************************************
 End of File
 */

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
 */

・次に
‘app.h’
‘app.c’
‘system_interrupt.c’
を編集します。

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"//この行を追加

// DOM-IGNORE-BEGIN
app.cの編集

void APP_Tasks ( void )以下の部分をすべて以下のコードにすげ替えれば大丈夫だと思います。

//
//2021_0821

/******************************************************************************
  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) {
    static unsigned char buf_p;

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


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


            gnd_level.INT = 0x200;

#if ROSEN_NUM==0
            DRV_TMR0_Start(); //TimerStart
#else
            //マスターボード以外はEXT_INT
            ext_int_enable = true;
            _TRISB14 = 0;
#endif

            //-----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];
                    train[n].err_th = 0x10;

                    //区間変更フラグクリア
                    if (rx_data[p + 4] & 1) {
                        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;

                    //MANU SPEED
                    if (rx_data[p + 4] & 0b10) {
                        train[n].STATUS.BIT.SP_MANU = 0x1;
                        train[n].speed = train[n].mascon * 0xE8 / 100;
                    } else
                        train[n].STATUS.BIT.SP_MANU = 0;



                    //point_request
                    if (rx_data[p + 5])
                        train[n].point = rx_data[p + 5];
                    //yard_request
                    if (rx_data[p + 6])
                        train[n].yard = rx_data[p + 6];


                }

                //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 + 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] = train[n].speed_ret.BYTE.H;
                        tx_data[p + 1] = train[n].speed_ret.BYTE.L;
                        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] = train[n].speed_ret_rx.BYTE.H;
                        tx_data[p + 1] = train[n].speed_ret_rx.BYTE.L;
                        tx_data[p + 5] = train[n].speed_rx.BYTE.H;
                        tx_data[p + 6] = train[n].speed_rx.BYTE.L;
                    }
                }



                for (n = 0; n < 128; n++) {
                    tx_data[100 + n] = comm_data[n];
                }

                //Degug
                tx_data[100 + 0x30] = 55;
                if (train_sou[0]) tx_data[100 + 0x31] = train_sou[0]->STATUS.BYTE;
                else tx_data[100 + 0x31] = 0xff;
                if (train_sou[1]) tx_data[100 + 0x32] = train_sou[1]->STATUS.BYTE;
                else tx_data[100 + 0x32] = 0xff;


                // 送信用メモリデータを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(debug1.BYTE.H);
            //            LCD_hex(debug1.BYTE.L);
            //            LCD_hex(train[0].henka);
            union byte_access err_abs;
            err_abs.INT = err_avr[0].INT;
            if (err_abs.INT < 0)err_abs.INT = -err_abs.INT;

            LCD_hex(err_abs.BYTE.H);
            LCD_hex(err_abs.BYTE.L);

            if (err_abs.INT < train[0].err_th)
                LCD_str(" OFF ");
            else
                LCD_str(" ON  ");

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

            break;
        }
            /* 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
 */
system_interrupt.cの編集

上がExternalInterruptの割り込みで、下がTimerInterruptの割り込みです。

各部にtimer_int_func()を追加します。

void __ISR(_EXTERNAL_1_VECTOR, IPL6AUTO) _IntHandlerExternalInterruptInstance0(void)
{
    PLIB_INT_SourceFlagClear(INT_ID_0, INT_SOURCE_EXTERNAL_1);
    
    if (ext_int_enable)
        timer_int_func();
}
 

void __ISR(_TIMER_1_VECTOR, ipl7AUTO) IntHandlerDrvTmrInstance0(void)
{
    PLIB_INT_SourceFlagClear(INT_ID_0,INT_SOURCE_TIMER_1);
    
    timer_int_func();
}

 

PC側ソフト

VBプロジェクトファイルのダウンロードpic32_remote

・このようなレイアウト用の設定になっています。最初の列車位置は

路線0 区間1

路線0 区間4

路線1 区間1

・ポイントの進入位置は4つで

正方向 路線0 区間4 と 路線1 区間4

逆方向 路線0 区間5 と路線1 区間5

です。

ボード間通信

各ボードはメインボードのTimerインタラプトの先頭で作るINT Clock信号とData信号で通信します。この信号で各ボードは128バイトの通信データを共有します。