Ver Tikutaikai

Dependencies:   mbed SBDBT BNO055 YKNCT_MD YKNCT_I2C

main.cpp

Committer:
TakushimaYukimasa
Date:
2021-10-25
Revision:
1:fa3652aca312
Parent:
0:c527a14d991a

File content as of revision 1:fa3652aca312:

/* Includes -----------------------------------------------------------------*/
#include <main.h>
#include <target.h>

/* 型定義
 * ---------------------------------------------------------------------*/

/* ロボットの加速度 */
LOCATION NowAcc;
/* ロボットの座標 */
LOCATION NowLoc;

/* 定数定義
 * -------------------------------------------------------------------*/

/* 関数プロトタイプ宣言
 * --------------------------------------------------------*/
void uartRX(void);

void IT_CallBack(void);

/* PID */
double LocationPID(double, double);

/* 値制限 */
double Range(double, double);

/* 角速度算出関数 */
int RotationPerWhile(int, int, int);

/* 足回り */
void InverseTriOmni(int *, double, double, double);

/* 自己位置推定関数 */
void LocEstimate(void);

/* 変数定義
 * -------------------------------------------------------------------*/

/* 足回り値保存変数 */
int MovMotor[MotorMAX] = {0};

/* 周期保存用 */
int whileTime_us = 0;

/* コントローラ */
int operate = 0;

/* ジャグリング機構 動かすか否か */
int isMoveInjection = 0;

/* リボン回転機構 動かすか否か */
int isMoveRibbon = 0;

/* 足回りエンコーダー */
double EncoderDeg[EncoderMAX - 6] = {0};

/* 手動オムニ目標角 */
double TarTheta = 0;

/* 目標番号 */
int targetNum = 0;

/* タイマー関連 */
/* 自身の状況 1-warning 2-normal */
int statious = 2;

/* 時間[ms] */
double msTimer = 0;

/* メインタイマー制御 0-pause 1-start */
bool timerMode = 0;

/* クラス定義 ----------------------------------------------------------------*/

/* 割り込み用クラス */
Ticker flipper;

/* UART (Tx,Rx) */
Serial pc(USBTX, USBRX, 115200);

/* XBee送受信 */
Serial UART(PC_10, PC_11, 9600);

/* コントローラー */
SBDBT DS3(PA_0, PA_1, 9600);

/* I2C MDのクラス定義 */
YKNCT_MD_I2C MD(PB_9, PB_8);

/* ジャイロのピン設定 */
BNO055 bno(PB_9, PB_8);

DigitalOut led(LED2);
/* リボン射出ピン */
DigitalOut Rod(PC_2);

/* エンコーダーピン設定 */
DigitalOut CS(PB_4);
DigitalOut CL(PA_10);
DigitalIn Do[EncoderMAX] = {PB_12, PB_2, PB_1, PB_15, PB_14, PB_13, PC_4, PB_5};
// DigitalIn Do[EncoderMAX] = {PB_15, PB_14, PB_13, PB_12, PB_2, PB_1, PC_4,
// PB_5};

/* カタパルト二台分の領域確保 */
INJECTION injection[2] = {INJECTION(PB_10, PA_8, PA_9),
                          INJECTION(PA_4, PB_0, PC_1)};

DigitalIn limitRibbonTop(PC_0);
DigitalIn limitRibbonBottom(PC_3);

Timer WaitNextShot;

/*----------------------------------- main ----------------------------------*/
int main() {
    /* I2CMDの設定 */
    for (int i = 0; i < MotorMAX; i++) MD.Init(i, MD_SMB);
    for (int i = 0; i < EncoderMAX; i++) Do[i].mode(PullDown);

    /* 割り込みの設定
     * IT_CallBack関数を0.1msで割り込み */
    flipper.attach_us(&IT_CallBack, 100);

    /* uart受信割込み設定 */
    UART.attach(uartRX, Serial::RxIrq);

    /* ジャイロの設定 */
    wait(0.5);
    bno.setmode(OPERATION_MODE_IMUPLUS);

    /* 動作時間計測用 */
    Timer whileTimer;
    Timer yawCnt;

    /* 計測開始 */
    whileTimer.start();
    yawCnt.start();
    WaitNextShot.start();

    /* 小さくすると深くなる */
    injection[0].initalDeg = 5;
    injection[1].initalDeg = 52;

    injection[0].targetRps[0] = -27;
    injection[0].targetRps[1] = 27
    ;
    injection[1].targetRps[0] = -27;
    injection[1].targetRps[1] = 27;

    /* メインループ ----------------------------------------------------------*/
    while (1) {
        led = !led;

        for (int i = 0; i < MotorMAX; i++) {
            MD.Set(i, MovMotor[i]);
            MovMotor[i] = 0;
        }
        for (int i = 0; i < 3; i++) {
            injection[0].moveMotor[i] = injection[1].moveMotor[i] = 0;
        }
        MD.Exe();

        /* 自己位置計算 */
        LocEstimate();

        /* 操縦権変更 ×停止 〇射出自動 △足回り手動 */
        if (DS3.CROSS) operate = 0;
        if (DS3.CIRCLE) operate = 1;
        if (DS3.TRIANGLE) operate = 2;
        if (DS3.SQUARE) operate = 3;

        /* STARTで座標系リセット */
        if (DS3.START) {
            NowLoc.reset();
            TarTheta = 0.0;
        }

        /* CS送信ステータス更新 */
        statious = (operate == 1) + 1;

        /* 停止以外でジャグリングとリボンの処理 */
        if (operate != 0) {
            static int nextCnt = 0;
            if (isMoveInjection && WaitNextShot.read_ms() > STEP(targetNum) &&
                !injection[!nextCnt].isMove) {
                injection[nextCnt].isMove = 1;
                nextCnt = !nextCnt;
            }
            //            if(isMoveInjection) {
            //                injection[0].isMove = injection[1].isMove = 1;
            //            }

            for (int i = 0; i < 2; i++) {
                /* 動作状態を決める */
                injection[i].MainFunction();
            }

            /* 出力 */
            for (int i = 0; i < 6; i++) {
                MovMotor[i] = Range(injection[i / 3].moveMotor[i % 3], 100);
            }

            /* リボン */
            if (isMoveRibbon) {
                if (!limitRibbonTop)
                    MovMotor[9] = 73;
                else if (isMoveRibbon == 2)
                    MovMotor[10] = 50;
            } else if (!limitRibbonBottom)
                MovMotor[9] = -60;
        }

        /* 停止処理 */
        if (operate == 0) {
            for (int i = 0; i < MotorMAX; i++) MovMotor[i] = 0;
            for (int i = 0; i < 2; i++)
                injection[0].moveMotor[i] = injection[1].moveMotor[i] = 0;
            for (int i = 0; i < 2; i++)
                injection[0].updateVal[i][1] = injection[1].updateVal[i][1] = 0;
            Rod = 0;
            TarTheta = NowLoc.Theta;
        }

        /* 足回り自動 */
        else if (operate == 1) {
            targetNum = 1;
            while (Target[targetNum].Time < msTimer && targetNum < TargetMAX)
                targetNum++;

            if (targetNum == TargetMAX)
                operate = 0;
            else {
                double divTim = 0;
                divTim = (double)(msTimer - Target[targetNum - 1].Time) /
                         (double)(Target[targetNum].Time -
                                  Target[targetNum - 1].Time);

                /* 時間毎の目標を算出する */
                LOCATION NowTar;
                NowTar.X =
                    Target[targetNum - 1].X +
                    (Target[targetNum].X - Target[targetNum - 1].X) * divTim;
                NowTar.Y =
                    Target[targetNum - 1].Y +
                    (Target[targetNum].Y - Target[targetNum - 1].Y) * divTim;
                NowTar.Theta =
                    Target[targetNum - 1].Theta +
                    (Target[targetNum].Theta - Target[targetNum - 1].Theta) *
                        divTim;

                /* 目標までの必要諸量 */
                LOCATION moveGain;
                moveGain.X = LocationPID(NowTar.X, NowLoc.X);
                moveGain.Y = LocationPID(NowTar.Y, NowLoc.Y);
                moveGain.Theta = (NowTar.Theta - NowLoc.Theta) * -4;

                /* 三角オムニに出力する */
                InverseTriOmni(MovMotor, moveGain.X, moveGain.Y,
                               moveGain.Theta);
                for (int i = 6; i < 9; i++)
                    MovMotor[i] = Range(MovMotor[i], 100);

                isMoveInjection = Target[targetNum].optionInjection;
                isMoveRibbon = Target[targetNum].optionRibbon;
                Rod = Target[targetNum].optionRod;

                //                pc.printf("num:%d ",targetNum);
                //                pc.printf("rod:%d
                //                ",Target[targetNum].optionRod);
                // pc.printf("Tar:%4.0f %4.0f %1.2f
                // ",Target[targetNum].X,Target[targetNum].Y,Target[targetNum].Theta);
                // pc.printf("NowTar:%4.0f %4.0f %1.2f
                // ",NowTar.X,NowTar.Y,NowTar.Theta);
            }
        }

        /* 手動 */
        else if (operate == 2) {
            int x_val = (DS3.LX - 64) * 100 / 64;
            int y_val = (64 - DS3.LY) * 100 / 64;
            int r_val = (DS3.RX - 64) * 100 / 64;

            /* 目標角更新 */
            if (DS3.RX != 64) yawCnt.reset();
            if (yawCnt.read_ms() < 1000) TarTheta = NowLoc.Theta;
            /* gyro値による補正 */
            r_val += (TarTheta - NowLoc.Theta) * -4.0;

            InverseTriOmni(MovMotor, x_val, y_val, r_val);

            isMoveInjection = DS3.R1;
            isMoveRibbon = DS3.R2 + DS3.L2;
            Rod = DS3.L1;
        }

        else if (operate == 3) {
            isMoveInjection = DS3.R1;
            isMoveRibbon = DS3.R2 + DS3.L2;
            Rod = DS3.L1;
        }

        /* 改行を入れる */
        pc.printf("\n\r");
        //         pc.printf("Loc:%4.0f %4.0f %1.2f
        //         ",NowLoc.X,NowLoc.Y,NowLoc.Theta); pc.printf("isRibbon:%d ",
        //         isMoveRibbon); pc.printf("isIn:%d ", isMoveInjection);
        /* データチェック */
        //         pc.printf("ope:%d  ", operate);
        //         pc.printf("tim:%6d ",msTimer);
        //         pc.printf("tim:%6d ",whileTime_us);
        // pc.printf("act[0]:%4d %4d", injection[0].actNum,
        //           injection[0].WaitNextShot.read_ms());
        // pc.printf("act[1]:%4d %4d", injection[1].actNum,
        //           injection[1].WaitNextShot.read_ms());

        // pc.printf("BallShotTim:%d  ", injection[1].BallShotTim.read_ms());
        // pc.printf("lim:%4d  ",injection[1].limitMagazine!=0);
        // pc.printf("isBallLoading:%d  ", injection[1].isBallLoading);
        //            pc.printf("Motor %d %d %d ", MovMotor[6], MovMotor[7],
        //            MovMotor[8]);

        //        pc.printf("Motor %d %d ", MovMotor[9], MovMotor[10]);

        //         pc.printf("Enc %3.0f %3.0f ",EncoderDeg[0],EncoderDeg[1]);
        for (int i = 0; i < 2; i++) {
            //            pc.printf("inj[%d]is:%d ", i, injection[i].isMove);
            //            pc.printf("BallLoad:%4d %d ", injection[i].BallLoadTim.read_ms(),
            //           injection[i].isBallLoading);
                        pc.printf("Enc %3.0f %3.0f %3.0f ",
                        injection[i].encDeg[0],
                                  injection[i].encDeg[1],
                                  injection[i].encDeg[2]);
            //  pc.printf("Enc %5.0f %5.0f %5.0f ", injection[i].encDeg[0],
            //  injection[i].encDeg[1], injection[i].encDeg[2]);
            // pc.printf("dif %5.0f ",
            // injection[i].encDeg[0]+injection[i].encDeg[1]);
            //                 pc.printf("Lim %d %d ",
            //                injection[i].limitTop!=0,injection[i].limitBottom!=0);
            //            pc.printf("Mo %d %d %d ", injection[i].moveMotor[0],
            //            injection[i].moveMotor[1], injection[i].moveMotor[2]);
            //            pc.printf("is %d ",
            //            injection[i].BallLoadTim.read_ms()); pc.printf("Ribbon
            //            %d ",isMoveRibbon);
        }

        /* 周期を保存 */
        whileTime_us = whileTimer.read_us();
        if (timerMode) msTimer += (double)whileTimer.read() * 1000.0;
        if (whileTime_us > 50000) I2C hoge(PB_9, PB_8);
        /* 周期リセット */
        whileTimer.reset();
    }  // while
}  // main

/*******************************************************************************
 * @概要  射出機構のメイン関数
 * @引数  なし
 * @返り値 なし
 *******************************************************************************/
void INJECTION::MainFunction(void) {
    /* 上のリミットと接触したらEncリセット */
    if (limitTop) encDeg[0] = encDeg[1] = 0;
    if (limitBottom) encDeg[0] = -1140, encDeg[1] = 1140;

    /* 下のリミットと接触したらエンコーダー角度を合わせる */
    //        if(limitBottom) {
    //            if(abs(encDeg[0]) > abs(encDeg[1])) encDeg[1] = -encDeg[0];
    //            else if(abs(encDeg[0]) < abs(encDeg[1])) encDeg[0] =
    //            -encDeg[1];
    //        }

    /* 装填機構のリミットと接触したら装填処理*/
    if (limitMagazine != 0) BallLoadTim.start();

    double rps[2] = {0};
    for (int i = 0; i < 2; i++) {
        double angularVel = RotationPerWhile(encDeg[i], i);  //差分
        rps[i] = (angularVel * 1000000.0 / whileTime_us) / 360.0;
    }

    /* 装填機構動作 */
    moveMotor[2] = LoadingPID((double)initalDeg, encDeg[2]);

    switch (actNum) {
        case 0:
            /* 上昇したら終了 */
            if (encDeg[1] > 800) {
                for (int i = 0; i < 2; i++) {
                    velDev[i][0] = velDev[i][1] = velDev[i][2] = 0;
                    moveMotor[i] = 0;
                    updateVal[i][0] = updateVal[i][1] = 0;
                }
                actNum++;

            } else if (BallShotTim.read_ms() > 180) {

                /* 操作量を更新 */
                for (int i = 0; i < 2; i++) {
                    updateVal[i][0] = updateVal[i][1];
                    updateVal[i][1] =
                        updateVal[i][0] + VelocityPID(targetRps[i], rps[i], i);
                    /* 値を制限する */
                    moveMotor[i] = Range(updateVal[i][1], 100);
                }
            }
            break;

        case 1:
            /* 目標更新,装填機構動作 */
            if (BallLoadTim.read_ms() > 200 && isMove) { 
                isBallLoading = 1;
                initalDeg -= 120;
                isMove = 0;
                WaitNextShot.reset();
            }

            /* 装填機構を動作させる */
            if (isBallLoading) {
                BallLoadTim.stop();
                BallLoadTim.reset();

                if (limitTop) {
                    /* 誤差範囲なら終了フラグ立て */
                    /* 装填機構の目標角度更新 */
                    if (abs(initalDeg - encDeg[2]) < 5.0) {
                        isBallLoading = 0;
                        BallShotTim.reset();
                        actNum++;
                    }
                }
                /* 射出機構戻ってなかったら途中までで上書き */
                else {
                    /* 下降中に途中まで回す */
                    moveMotor[2] = LoadingPID(initalDeg + 40, encDeg[2]);
                }
            }

            /* リミットに触れるまで下降 */
            //     if (limitTop) {
            //         for (int i = 0; i < 2; i++) {
            //             velDev[i][0] = velDev[i][1] = velDev[i][2] = 0;
            //             moveMotor[i] = 0;
            //             updateVal[i][0] = updateVal[i][1] = 0;
            //         }
            //     }
            //    else if (encDeg[1] < 0) {
            //         /* 固定値で一番下まで下降 */
            //        for (int i = 0; i < 2; i++) moveMotor[i] = -15 * (i * 2 -
            //        1);

            //    }
            //     else{
            //         double tarRps[2]={0};
            //         tarRps[1] =  -(encDeg[1] - 200)/100.0;
            //         if(tarRps[1] < 0 && tarRps[1] > -1.5) tarRps[1] = -1.5;
            //         if(tarRps[1] > 0 && tarRps[1] < 1.5) tarRps[1] = 1.5;
            //         tarRps[0] = -tarRps[1];
            //         pc.printf("%f,%f,",tarRps[0],tarRps[1]);

            //         /* 操作量を更新 */
            //         for (int i = 0; i < 2; i++) {
            //             updateVal[i][0] = updateVal[i][1];
            //             updateVal[i][1] = updateVal[i][0] +
            //             VelocityPID(tarRps[i], rps[i], i);

            //             /* 値を制限する */
            //             moveMotor[i] = Range(updateVal[i][1], 100);
            //         }
            //     }
            if (limitTop) {
                for (int i = 0; i < 2; i++) moveMotor[i] = 0;
            } else if (encDeg[1] > 500) {
                for (int i = 0; i < 2; i++) {
                    moveMotor[i] = -400 - encDeg[i];
                    moveMotor[i] = Range(moveMotor[i], 50);
                }
            } else {
                /* 固定値で一番下まで下降 */
                for (int i = 0; i < 2; i++) moveMotor[i] = -15 * (i * 2 - 1);
            }
            break;

        default:
            actNum = 0;
            break;
    }  // switch
}

/*******************************************************************************
 * @概要  速度型PID制御
 * @引数  目標値, 現在値, モーター番号
 * @返り値 操作量
 *******************************************************************************/
double INJECTION::VelocityPID(double Target_value, double Current_value,
                              int motorNum) {
    /* 比例定数,積分定数,微分定数 */
    const double kp = 1.0;
    const double ki = 0.3;
    const double kd = 0.05;
    /* 比例項,積分項,微分項の計算結果を保存する変数*/

    /* 偏差を更新 */
    velDev[motorNum][2] = velDev[motorNum][1];
    velDev[motorNum][1] = velDev[motorNum][0];
    velDev[motorNum][0] = Target_value - Current_value;

    /* 比例項,積分項,微分項をそれぞれ求める */
    double P = (velDev[motorNum][0] - velDev[motorNum][1]) * kp;
    double I = velDev[motorNum][0] * ki;
    double D = ((velDev[motorNum][0] - velDev[motorNum][1]) -
                (velDev[motorNum][1] - velDev[motorNum][2])) *
               kd;

    /* 合計値を操作量として返す */
    return P + I + D;
}

/*******************************************************************************
 * @概要  位置型PID制御
 * @引数  目標値, 現在値
 * @返り値 操作量
 *******************************************************************************/
double LocationPID(double Target_value, double Current_value) {
    /* 比例定数,積分定数,微分定数 */
    const double kp = 0.5;
    const double ki = 0;
    const double kd = 0;
    /* 比例項,積分項,微分項の計算結果を保存する変数*/
    static double P = 0;
    static double I = 0;
    static double D = 0;
    /* 偏差の一時保存領域 */
    static double dev[2] = {0};

    /* 偏差を更新 */
    dev[1] = dev[0];
    dev[0] = Target_value - Current_value;

    /* 比例項,積分項,微分項をそれぞれ求める */
    P = dev[0] * kp;
    I += dev[0] * ki;
    D = (dev[0] - dev[1]) * kd;

    /* ワインドアップ */
    if ((dev[0] < 0 && dev[1] > 0) || (dev[0] > 0 && dev[1] < 0)) I = 0;

    /* 合計値を操作量として返す */
    return P + I + D;
}

/*******************************************************************************
 * @概要  位置型PID制御(装填機構)
 * @引数  目標値, 現在値
 * @返り値 操作量
 *******************************************************************************/
double INJECTION::LoadingPID(double Target_value, double Current_value) {
    /* 比例定数,積分定数,微分定数 */
    const double kp = 3.5;
    const double ki = 0.08;
    const double kd = 0.5;
    /* 比例項,積分項,微分項の計算結果を保存する変数*/
    static double P = 0;
    static double I = 0;
    static double D = 0;

    /* 偏差を更新 */
    loadDev[1] = loadDev[0];
    loadDev[0] = Target_value - Current_value;

    /* 比例項,積分項,微分項をそれぞれ求める */
    P = loadDev[0] * kp;
    I += loadDev[0] * ki;
    D = (loadDev[0] - loadDev[1]) * kd;

    /* ワインドアップ */
    if ((loadDev[0] < 0 && loadDev[1] > 0) ||
        (loadDev[0] > 0 && loadDev[1] < 0))
        I = 0;

    /* 合計値を操作量として返す */
    return Range(P + Range(I, 6) + D, 70);
}

/*******************************************************************************
 * @概要  制限する
 * @引数  対象,限界値
 * @返り値 制限後の値
 *******************************************************************************/
double Range(double value, double limit) {
    if (value > limit)
        return limit;
    else if (value < -limit)
        return -limit;
    else
        return value;
}

/*******************************************************************************
 * @概要  回転角度を求める関数
 * @引数  角度, エンコーダー番号, 機体番号
 * @返り値 周期あたりの角速度
 *******************************************************************************/
double INJECTION::RotationPerWhile(double degree, int num) {
    /* 前回の値更新 今回の値更新 */
    EncDegDiff[num][1] = EncDegDiff[num][0];
    EncDegDiff[num][0] = degree;

    /* 前回と現在の値を比べる */
    return EncDegDiff[num][0] - EncDegDiff[num][1];
}

/*******************************************************************************
 * @概要   自己位置推定関数
 * @引数   なし
 * @返り値 なし
 *******************************************************************************/
void LocEstimate(void) {
    static double GyroDeg[2] = {0};
    static double EncDeg[2][2] = {0};
    static double disp[3] = {0};

    /* ジャイロの値取得 */
    bno.get_angles();
    GyroDeg[1] = GyroDeg[0];
    GyroDeg[0] = bno.euler.yaw;

    if (GyroDeg[0] != 0) {
        /* 359→0を跨いだ時,前回の値を0から逆回転で負の値で表記 */
        if (GyroDeg[1] < 90 && GyroDeg[0] > 270) GyroDeg[1] += 360;
        /* 0→359を跨いだ時,前回の値を360以上の値で表記 */
        else if (GyroDeg[1] > 270 && GyroDeg[0] < 90)
            GyroDeg[1] -= 360;
        /* 差を求める*/
        disp[2] = GyroDeg[1] - GyroDeg[0];
    }
    /* Enc2つの差分求める */
    for (int i = 0; i < 2; i++) {
        EncDeg[i][1] = EncDeg[i][0];
        EncDeg[i][0] = EncoderDeg[i];
        disp[i] = DEG_TO_DIS(EncDeg[i][1] - EncDeg[i][0]);
    }
    /* 差分を加速度として保存 */
    NowAcc.Theta = disp[2];
    NowAcc.X = -disp[0] * cos(DEG_TO_RAD(NowLoc.Theta)) +
               disp[1] * sin(DEG_TO_RAD(NowLoc.Theta));
    NowAcc.Y = -disp[0] * sin(DEG_TO_RAD(NowLoc.Theta)) -
               disp[1] * cos(DEG_TO_RAD(NowLoc.Theta));
    /* 差分を累積して現在位置を保存 */
    NowLoc.X += NowAcc.X / 2.0;
    NowLoc.Y += NowAcc.Y / 2.0;
    NowLoc.Theta += NowAcc.Theta;
}

/*******************************************************************************
 * @概要  回転角度を求める関数
 * @引数  角度, エンコーダー番号, 機体番号
 * @返り値 周期あたりの角速度
 *******************************************************************************/
int Rotation(int degree, int num, int robotNum) {
    static int EncDeg[2][2][2] = {0};
    static int NowRotate[2][2] = {0};

    /* 前回の値更新 今回の値更新 */
    EncDeg[robotNum][num][1] = EncDeg[robotNum][num][0];
    EncDeg[robotNum][num][0] = degree;

    /* 前回と現在の値を比べる */
    NowRotate[robotNum][num] =
        EncDeg[robotNum][num][0] - EncDeg[robotNum][num][1];
    return NowRotate[robotNum][num];
}

/*******************************************************************************
 * @概要  逆三角オムニ関数
 * @引数  モータの配列・x,y,r ベクトル
 * @返り値 なし
 ******************************************************************************/
void InverseTriOmni(int *duty, double x, double y, double r) {
    int duty_max = 0;

    double rad = DEG_TO_RAD(NowLoc.Theta);
    duty[6] = x * cos(rad) + y * sin(rad);
    duty[7] = x * cos(rad + PI / 3.0 * 2) + y * sin(rad + PI / 3.0 * 2);
    duty[8] = -x * cos(rad + PI / 3.0) - y * sin(rad + PI / 3.0);

    duty_max = 0;
    for (int i = 6; i < 9; i++) {
        duty[i] += r;
        duty_max = max(duty_max, abs(duty[i]));
    }
    for (int i = 6; i < 9; i++)
        if (duty_max > 99) duty[i] = duty[i] * 99 / duty_max;
}

/* UART受信割り込み
 * *************************************************************/
void uartRX() {
    static int num = 0;
    static uint8_t buf[3] = {0};

    /* 受信データ */
    uint8_t data = UART.getc();
    if ((data & 0x80) != 0) {
        timerMode = (data & 0x40) != 0;

        //ビット結合
        uint32_t tmp = 0;
        for (int i = 0; i < 3; i++) tmp |= buf[i] << (14 - i * 7);

        //チェックサム計算
        uint8_t checkSum = 0;
        for (int i = 0; i < 7; i++)
            checkSum += tmp / (int)pow(10.0, (double)i) % 10;

        //誤り検知陰性
        if (checkSum == (data & 0x3F) && (DS3.SELECT == 0)) msTimer = tmp;

        num = 0;
    } else {
        buf[num] = data;
        num++;
    }

    /* 送信データ */
    char sendData = 2 << 2;
    sendData |= (statious & 0b11);
    UART.putc(sendData);
}

/* 割り込み(100us)
 * *************************************************************/
void IT_CallBack(void) {
    static int cnt = 0;
    static int data[EncoderMAX] = {0};
    static double EncDeg[EncoderMAX][2] = {0};

    switch (cnt) {
        /* 最初の処理 */
        case 0:
            for (int i = 0; i < EncoderMAX; i++) data[i] = 0;
            CS = 0;
            CL = 1;
            break;
        /* 最後の処理 */
        case 25:
            CS = 1;

            for (int i = 0; i < EncoderMAX; i++) {
                /* 前回の値更新
                 * 今回の値更新(エンコーダの値(0~4096)を角度(0~360)に) */
                EncDeg[i][1] = EncDeg[i][0];
                EncDeg[i][0] = (double)data[i] * 360.0 / 4096;
                /* 359→0を跨いだ時,前回の値を0から逆回転で負の値で表記 */
                if ((270 <= EncDeg[i][1]) && (EncDeg[i][0] < 90))
                    EncDeg[i][1] -= 360;
                /* 0→359を跨いだ時,前回の値を360以上の値で表記 */
                else if ((EncDeg[i][1] < 90) && (270 <= EncDeg[i][0]))
                    EncDeg[i][1] += 360;
                /* 差を求め,適当な変数に代入 */
                if (i < 6)
                    injection[i / 3].encDeg[i % 3] +=
                        EncDeg[i][0] - EncDeg[i][1];
                else
                    EncoderDeg[i - 6] -= EncDeg[i][0] - EncDeg[i][1];
            }
            break;
        /* 通常の処理 */
        default:
            CL = !CL;
            /* 最初でも最後でもなく奇数回で最初以外の時読み取り処理 */

            for (int i = 0; i < EncoderMAX; i++)
                if (cnt != 1 && cnt % 2) {
                    data[i] |= (Do[i] == 1);
                    data[i] = data[i] << 1;
                }
            break;
    }
    cnt++;
    cnt %= 26;
}