Just a regular publish

Dependencies:   mbed imu_driver

main.cpp

Committer:
open4416
Date:
2020-03-04
Revision:
21:e01a019fae2f
Parent:
20:e9daae390513
Child:
22:4f764f7cd0b3

File content as of revision 21:e01a019fae2f:

#include "mbed.h"
#include "main.h"

// define this for newversion test mode
//#define IMU_direct

#ifndef IMU_direct
#include "imu_driver.hpp"
#else
typedef struct AhrsRawData {
    uint16_t    status;
    int16_t     rate[3];
    int16_t     accel[3];
    uint16_t    temperature;
    int16_t     attitude[3];
} AhrsData;
#define Read_VG (0x3D)
#define ACCL2F  (4000.0f)
#define GYRO2F  (100.0f)
#define AHRS2F  (90.0f)
#define VG_len  11U
#endif

#define pi  3.14159265359f
#define d2r 0.01745329252f
#define dt  0.01f

DigitalOut  Aux_Rly(PC_10,0);                       //Control aux relay, 1 active
DigitalOut  Fault_Ind(PC_12,0);                     //Indicate fault bt flashing, 1 active
DigitalOut  LED(D13, 0);                            //Internal LED output, general purpose
AnalogIn    AUX_1(PC_0);                            //Auxilaru analog sensor
AnalogIn    AUX_2(PC_3);
AnalogIn    AUX_3(PC_2);
AnalogIn    AUX_4(PC_1);
AnalogIn    SDn_sense(PB_0);                        //Shutdown circuit driving end detection
AnalogIn    Brk_sense(PA_4);                        //Brake sensor readings
CAN         can1(PB_8, PB_9, 1000000);              //1Mbps, contain critical torque command message
SPI         spi2(PB_15, PB_14, PB_13);              //1Mbps default, MOSI MISO SCLK, forIMU
#ifndef IMU_direct
ImuDriver   <spi2, PC_4, PB_2, PB_1> imu(ImuExtiRcvBothMsg);  //SPI instance, reset, data ready, slave select
#else
AhrsData    ahrsdata;
DigitalOut  cs(PB_1,1);
InterruptIn drdy(PB_2);
#endif
Serial      pc(USBTX, USBRX, 115200);
Ticker      ticker1;                                //100Hz task
CANMessage  can_msg_Rx;
CANMessage  can_msg_Tx;

void timer1_interrupt(void)
{
    HSTick += 1;
    LSTick += 1;
    if (HSTick > 9) {           // 100Hz
        HST_EXFL = 1;
        HSTick = 0;
    }
    if (LSTick > 99) {          // 10Hz
        LST_EXFL = 1;
        LSTick = 0;
    }
}

int main()
{
    //Init CAN network
    CAN_init();                 // Note now in Gloable test mode only for testing 2019/11/17

    //Start House keeping task
    printf("VDU start up, pend for module online\n");
#ifdef IMU_direct
    drdy.fall(&IMU_isr);                            //IMU interrupt service
    spi2.format(16, 3);
    //spi2.frequency();                             //As default
#endif

    wait_ms(2500);
    ticker1.attach_us(&timer1_interrupt, 1000);     //1 ms Systick
    while(1) {

        // Do high speed loop
        if (HST_EXFL == 1) {
            HST_EXFL = 0;

            // Get IMU, Auxs, Max Temperature
            Cooler();
            IMU_read();
            Aux_read();
            Module_WD();

            // Run state machine
            switch (VDU_STAT) {

                case VDU_PowerOn:
                    /* Power on state
                     * Description:
                     *  Simple start up sequence will be done here
                     * Do:
                     *  VDU internal POST
                     *  Wait till modules + PSU online
                     * To VDU_Idle (RTD off):
                     *  Prepare for 4WD main program
                     * To VDU_Fault:
                     *  Run the error handling service
                     */

                    //Basic IMU test
                    POST();

                    //Check if state transition only when all module online
                    if (VDU_FLT != 0) {                 //Check if any error
                        VDU_STAT = VDU_Fault;
                        RST_HMI = 0;                    //Ensure new RST action after error
                        printf("POST Fault\n");
                        FLT_print = 1;
                    } else if ((FL_online*FR_online*RL_online*RR_online*PSU_online)!=0) {
//                    } else if (1) {                     //2019/11/30 only use for force online debug
                        //All module online & POST pass
                        VDU_STAT = VDU_Idle;
                        printf("All module online, VDU now Idle\n");
                    }                                   //Else keep in state VDU_PowerOn
                    break;

                case VDU_Idle:
                    /* Controller Idle state
                     * Description:
                     *  Normal latched state, wait for RTD_HMI set from PSU
                     *  4WD in running but output mask to 0
                     * Do:
                     *  4WD controller
                     * Check:
                     *  RUN faults if any
                     * To VDU_Run:
                     *  Initialize parameters for start up, set RTD_cmd
                     * To VDU_Fault:
                     *  Run the error handling service
                     */

                    //Forced RTD_HMI for debug purpose 2019/11/14
//                    RTD_HMI = 1;                        //Should be set if can bus received data
                    //Forced RTD_HMI for debug purpose 2019/11/14

                    RUNT();                             //Run test
                    AWD();                              //AWD main program

                    if (VDU_FLT != 0) {                 //Check if any error
                        VDU_STAT = VDU_Fault;
                        RST_HMI = 0;                    //Ensure new RST action after error
                        printf("Idle 2 Fault\n");
                        FLT_print = 1;
                    } else if (RTD_HMI != 0) {          //Or command to run threw PSU
                        //Prepare to send out RTD and start motor
                        Idle2Run();
                        VDU_STAT = VDU_Run;
                        printf("Idle 2 Run\n");
                    }                                   //Else keep in state
                    break;

                case VDU_Run:
                    /* Controller Run state
                     * Description:
                     *  Normal latched state, after RTD_HMI is set from PSU
                     *  Same to Idle state except RTD_cmd is set
                     * Do:
                     *  4WD controller
                     * Check:
                     *  RUN faults if any
                     * To VDU_Idle:
                     *  Initialize parameters for idling, reset RTD_cmd
                     * To VDU_Fault:
                     *  Run the error handling service
                     */

                    RUNT();                             //Run test
                    AWD();                              //AWD main program

                    //Temporary debug posting area 2019/11/14
                    //printf("%d,%d\n", Encoder_cnt, Encoder_del);
                    //printf("%d\n\r", (int16_t)Tmodule);//
                    //printf("%d\n\r", (int16_t)Vbus);

                    if (VDU_FLT != 0) {                 //Check if any error
                        VDU_STAT = VDU_Fault;
                        RST_HMI = 0;                    //Ensure new RST action after error
                        printf("Run 2 Fault\n");
                        FLT_print = 1;
                    } else if (RTD_HMI != 1) {          //Or command to stop threw can bus
                        Run2Idle();
                        VDU_STAT = VDU_Idle;
                        printf("Run 2 Idle\n");
                    }                                   //Else keep in state
                    break;

                case VDU_Fault:
                    /* Controller Fault state
                     * Description:
                     *  Fault latched state if any faults is detected
                     *  Same to Idle state except keep till RTD_HMI reset
                     * Do:
                     *  Nothing, like a piece of shit
                     * Check:
                     *  RUN faults if any
                     * To VDU_PowerOn:
                     *  Restart VDU
                     */

                    RUNT();                             //Run test

                    if (RST_HMI == 1) {                 //PSU reset to clear error
                        RST_HMI = 0;
                        RST_cmd = 1;
                        FLT_print = 0;                  //Stop error printing
                        VDU_STAT = VDU_PowerOn;
                        printf("VDU rebooting...\n");
                        printf("QDrive rebooting...\n");
                    }                                   //Else keep in state
                    break;
            }

            // Shit out torque distribution and special command
            if(VDU_STAT == VDU_Run) {
                //Allow output torque
                Tx_Tcmd_CAN1();
            } else if(RST_cmd != 0) {
                //Send out reset cmd once
                Tx_CLRerr_CAN1();
            } else {
                //Force RTD off when not in VDU_Run
                Tx_Estop_CAN1();
            }

            //2019/12/18 Add here to test IMU, newer version use inturrupt get data
//            pc.printf("%.3f,%.3f,%.3f\n\r", imu.ahrsProcessedData.attitude[0], imu.ahrsProcessedData.attitude[1], imu.ahrsProcessedData.attitude[2]);
//            pc.printf("%.3f,%.3f,%.3f\n\r", imu.imuProcessedData.rate[0], imu.imuProcessedData.rate[1], imu.imuProcessedData.rate[2]);
//            pc.printf("%.3f,%.3f,%.3f\n\r", imu.imuProcessedData.accel[0], imu.imuProcessedData.accel[1], imu.imuProcessedData.accel[2]);
//            pc.printf("%.3f,%.3f,%.3f\n\r", YR_imu, Ax_imu, Ay_imu);
//            pc.printf("%.3f,%.3f,%.3f\n\r", Roll_imu, Pitch_imu, Yaw_imu);

        }
        // End of high speed loop

        // Do low speed state reporting 10 Hz
        if (LST_EXFL == 1) {
            LST_EXFL = 0;
            //Cooling
            Cooler();

            //Print low speed data on CAN
            Tx_Qdrv_CAN1();

            // Print out error mesagge if exist, 0.5Hz repeative
            if(FLT_print != 0) {
                //Merge Faults
                FLT_Post = FL_FLT_Post|FR_FLT_Post|RL_FLT_Post|RR_FLT_Post;
                FLT_Run = FL_FLT_Run|FR_FLT_Run|RL_FLT_Run|RR_FLT_Run;

                //UART
                FLT_print_cnt += FLT_print;
                if(FLT_print_cnt > 19) {
                    if(FLT_Post!=0)printf("POST FL,FR,RL,RR\n0x%04X 0x%04X 0x%04X 0x%04X\n", FL_FLT_Post,FR_FLT_Post,RL_FLT_Post,RR_FLT_Post);
                    if(FLT_Run!=0)printf("RUN FL,FR,RL,RR\n0x%04X 0x%04X 0x%04X 0x%04X\n", FL_FLT_Run,FR_FLT_Run,RL_FLT_Run,RR_FLT_Run);
                    if(VDU_FLT!=0)printf("VDU\n0x%04X\n\n", VDU_FLT);
                    FLT_print_cnt = 0;
                }

                //LED
                if(Ind_refresh) {
                    // Refresh data for LED indication after run threw
                    FLT_Post_ind = FLT_Post;
                    FLT_Run_ind = FLT_Run;
                    VDU_FLT_ind = VDU_FLT;
                    Ind_refresh = 0;    // Set after run threw all error bits
                }
            } else {
                // Clear & stop LED when no faults
                FLT_Post_ind = 0;
                FLT_Run_ind = 0;
                VDU_FLT_ind = 0;
                Repeat = 0U;
                Phase = 0U;
                Blk_n = 0U;
            }

            // Blinky output
            if (VDU_STAT != VDU_PowerOn) {
                // Case after poweron (all module online) or fault(s) occur
                Ind_refresh = IndicationKernel(
                                  &Pattern,
                                  &Repeat,
                                  &Phase,
                                  &FLT_Post_ind,
                                  &FLT_Run_ind,
                                  &VDU_FLT_ind);
                LED = Indication(Pattern, &Repeat, &Blk_n);
                Fault_Ind = LED;
            } else {
                // Case no fault while waiting for all module online
                LED = !LED;     //Fast 5Hz blinky indicate the activity
                Fault_Ind = LED;
            }
        }
        // End of low speed state reporting

    } // end of while
}

void Idle2Run(void)
{
    RTD_cmd = 1;
}

void Run2Idle(void)
{
    RTD_cmd = 0;
}

void POST(void)
{
    //Check IMU status abnormal
    if(fabsf(YR_imu) > 250) {                   //half turn per sec, strange
        VDU_FLT |= IMUSTA_VDUFLTCode;           //IMU status error
    }
}

void RUNT(void)
{
    //POST
    POST();

    //Check module response timeout
    if((FL_online*FR_online*RL_online*RR_online) == 0) {
        VDU_FLT |= MODOFL_VDUFLTCode;           //Module timeout
    }
    if(PSU_online == 0) {
        VDU_FLT |= PSUOFL_VDUFLTCode;           //PSU timeout
    }

    //Check ShutDrv voltage when running
    if(VDU_STAT == VDU_Run) {                
        if(SDn_voltage < 8.0f) {
            VDU_FLT |= ShDVol_VDUFLTCode;       //Shutdown circuit unclosed or uv
        }
    }
}

void Aux_read(void)
{
    //Capture analog in at once imu_driver ver
    AUX_1_u16 = AUX_1.read_u16() >> 4U;
    AUX_2_u16 = AUX_2.read_u16() >> 4U;
    AUX_3_u16 = AUX_3.read_u16() >> 4U;
    AUX_4_u16 = AUX_4.read_u16() >> 4U;
    SDn_voltage = 18.81f*SDn_sense.read();      //Read in Shutdown circuit voltage in output end
    Brk_voltage = 5.5f*Brk_sense.read();
}

#ifdef IMU_direct
void IMU_isr(void)
{
    //Start data transfer
    uint8_t data_rx = 0;
    uint16_t reg_VG = Read_VG<<8U;
    cs = 0;
    spi2.write(reg_VG);
    while(data_rx < VG_len) {
        uint16_t spi_data = spi2.write(0x0000);
        switch(data_rx) {
            case 0:
                ahrsdata.status = spi_data;
                break;
            case 1:
            case 2:
            case 3:
                ahrsdata.rate[data_rx - 1] = spi_data;
                break;
            case 4:
            case 5:
            case 6:
                ahrsdata.accel[data_rx - 4] = spi_data;
                break;
            case 7:
                ahrsdata.temperature = spi_data;
                break;
            case 8:
            case 9:
            case 10:
                ahrsdata.attitude[data_rx - 8] = spi_data;
                break;
            default:
                break;
        }
        ++data_rx;
    }
    cs = 1;
}
#endif

void IMU_read(void)
{
#ifndef IMU_direct
    //Get readings threw back ground, direction not checked 2019/12/20
    YR_imu = imu.imuProcessedData.rate[2];
    Ax_imu = imu.imuProcessedData.accel[0];
    Ay_imu = imu.imuProcessedData.accel[1];
    Roll_imu = imu.ahrsProcessedData.attitude[0];
    Pitch_imu = imu.ahrsProcessedData.attitude[1];
    Yaw_imu = imu.ahrsProcessedData.attitude[2];
#else
    YR_imu = ahrsdata.rate[2]/GYRO2F;
    Ax_imu = ahrsdata.accel[0]/ACCL2F;
    Ay_imu = ahrsdata.accel[1]/ACCL2F;
    Roll_imu = ahrsdata.attitude[0]/AHRS2F;
    Pitch_imu = ahrsdata.attitude[1]/AHRS2F;
#endif
}

void AWD(void)
{
    if(AWD_HMI) {
        // A simple version is put here for reading
        Vb_est = 0.25f * (FL_W_ele + FR_W_ele + RL_W_ele + RR_W_ele);
        YR_ref = Steer_HMI*d2r*Vb_est/(Base+EG*Vb_est*Vb_est);
        Mz_reg = (YR_ref - YR_imu) * K_yaw;         //K_yaw unfinished 2019/11/15
        sig = 0.5f - HCG*Trq_HMI/(Base*Rwhl*Mtot*g0);
        FL_Tcmd = (0.5f*Trq_HMI - Mz_reg*Rwhl/Track)*sig;
        FR_Tcmd = (0.5f*Trq_HMI + Mz_reg*Rwhl/Track)*sig;
        RL_Tcmd = (0.5f*Trq_HMI - Mz_reg*Rwhl/Track)*(1.0f-sig);
        RR_Tcmd = (0.5f*Trq_HMI + Mz_reg*Rwhl/Track)*(1.0f-sig);
    } else {
        FL_Tcmd = 0.25f*Trq_HMI;
        FR_Tcmd = 0.25f*Trq_HMI;
        RL_Tcmd = 0.25f*Trq_HMI;
        RR_Tcmd = 0.25f*Trq_HMI;
    }
}

void ASL(void)
{
    //Filter out each motor W_ele and get approximate accel, compare with IMU
    //Policy is set as "degrade only" coefficient exp(K_ASL*delAccel)
    //Fill out if enough time
}

void Rx_CAN1(void)
{
//    LED = 1;
    int16_t tmp;

    if(can1.read(can_msg_Rx)) {
        switch(can_msg_Rx.id) {                                 //Filtered input message
            // Start of 100Hz msg
            case FL_HSB_ID://1
                //HSB from FL motor drive
                FL_DSM = can_msg_Rx.data[6] & 0x03;             //Get DSM_STAT
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                FL_W_ele = tmp*1.0f;
                tmp = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                FL_Trq_fil3 = tmp * 0.01f;
                tmp = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                FL_Trq_est = tmp * 0.01f;
                FL_online = 5;
                //If fault
                if(FL_DSM == 3U) {
                    VDU_FLT |= DSM_VDUFLTCode;                  //DSM Fault
                }
                break;

            case FR_HSB_ID://2
                //HSB from FR motor drive
                FR_DSM = can_msg_Rx.data[6] & 0x03;             //Get DSM_STAT
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                FR_W_ele = tmp*1.0f;
                tmp = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                FR_Trq_fil3 = tmp * 0.01f;
                tmp = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                FR_Trq_est = tmp * 0.01f;
                FR_online = 5;
                if(FR_DSM == 3U) {
                    VDU_FLT |= DSM_VDUFLTCode;                  //DSM Fault
                }
                break;

            case RL_HSB_ID://3
                //HSB from RL motor drive
                RL_DSM = can_msg_Rx.data[6] & 0x03;             //Get DSM_STAT
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                RL_W_ele = tmp*1.0f;
                tmp = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                RL_Trq_fil3 = tmp * 0.01f;
                tmp = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                RL_Trq_est = tmp * 0.01f;
                RL_online = 5;
                if(RL_DSM == 3U) {
                    VDU_FLT |= DSM_VDUFLTCode;                  //DSM Fault
                }
                break;

            case RR_HSB_ID://4
                //HSB from RR motor drive
                RR_DSM = can_msg_Rx.data[6] & 0x03;             //Get DSM_STAT
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                RR_W_ele = tmp*1.0f;
                tmp = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                RR_Trq_fil3 = tmp * 0.01f;
                tmp = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                RR_Trq_est = tmp * 0.01f;
                RR_online = 5;
                if(RR_DSM == 3U) {
                    VDU_FLT |= DSM_VDUFLTCode;                  //DSM Fault
                }
                break;

            case HMI_cmd_ID://5
                //HMI command from PSU
                AWD_HMI = can_msg_Rx.data[6] & 0x01;             //Get AWD switch
                RST_HMI = can_msg_Rx.data[5] & 0x01;             //Get RST request
                RTD_HMI = can_msg_Rx.data[4] & 0x01;             //Get RTD switch
                tmp = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                Steer_HMI = tmp * 0.01f;
                tmp = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                Trq_HMI = tmp * 0.01f;
                PSU_online = 5;
                break;
            // end of 100Hz msg

            // Start of 10Hz msg
            case FL_LSB_ID://6
                //LSB from FL motor drive
                tmp = can_msg_Rx.data[7] << 8 | can_msg_Rx.data[6];
                FL_Tmotor = tmp*0.01f;
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                FL_Tmodule = tmp*0.01f;
                FL_FLT_Run = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                FL_FLT_Post = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                break;

            case FR_LSB_ID://7
                //LSB from FR motor drive
                tmp = can_msg_Rx.data[7] << 8 | can_msg_Rx.data[6];
                FR_Tmotor = tmp*0.01f;
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                FR_Tmodule = tmp*0.01f;
                FR_FLT_Run = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                FR_FLT_Post = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                break;

            case RL_LSB_ID://8
                //LSB from RL motor drive
                tmp = can_msg_Rx.data[7] << 8 | can_msg_Rx.data[6];
                RL_Tmotor = tmp*0.01f;
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                RL_Tmodule = tmp*0.01f;
                RL_FLT_Run = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                RL_FLT_Post = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                break;

            case RR_LSB_ID://9
                //LSB from RR motor drive
                tmp = can_msg_Rx.data[7] << 8 | can_msg_Rx.data[6];
                RR_Tmotor = tmp*0.01f;
                tmp = can_msg_Rx.data[5] << 8 | can_msg_Rx.data[4];
                RR_Tmodule = tmp*0.01f;
                RR_FLT_Run = can_msg_Rx.data[3] << 8 | can_msg_Rx.data[2];
                RR_FLT_Post = can_msg_Rx.data[1] << 8 | can_msg_Rx.data[0];
                break;
                // end of 10Hz msg
        }
    }
//    LED = 0;
}

void Tx_CLRerr_CAN1(void)
{
    Tx_Estop_CAN1();        //disable as default
    RST_cmd = 0;            //clear out one shot
}

void Tx_Estop_CAN1(void)
{
    RTD_cmd = 0;            //force disable
    Tx_Tcmd_CAN1();
}

void Tx_Tcmd_CAN1(void)     // 100 Hz
{
    int16_t tmp;
    tmp = (int16_t) (FL_Tcmd * 100.0f);
    temp_msg[0] = tmp;
    temp_msg[1] = tmp >> 8U;
    temp_msg[2] = RTD_cmd;
    temp_msg[3] = RST_cmd;
//    temp_msg[3] = 0U;       // 2020/3/4 add to disable HMI reseting Driver
    temp_msg[4] = 0U;
    temp_msg[5] = 0U;
    temp_msg[6] = 0U;
    temp_msg[7] = 0U;
    can_msg_Tx = CANMessage(FL_CMD_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

    tmp = (int16_t) (FR_Tcmd * 100.0f);
    temp_msg[0] = tmp;
    temp_msg[1] = tmp >> 8U;
    can_msg_Tx = CANMessage(FR_CMD_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

    tmp = (int16_t) (RL_Tcmd * 100.0f);
    temp_msg[0] = tmp;
    temp_msg[1] = tmp >> 8U;
    can_msg_Tx = CANMessage(RL_CMD_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

    tmp = (int16_t) (RR_Tcmd * 100.0f);
    temp_msg[0] = tmp;
    temp_msg[1] = tmp >> 8U;
    can_msg_Tx = CANMessage(RR_CMD_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

}

void Tx_Qdrv_CAN1(void)   // 10 Hz
{
    int16_t tmp;
    // Auxilary sensor reading shitting out
    temp_msg[0] = AUX_1_u16;
    temp_msg[1] = AUX_1_u16 >> 8U;
    temp_msg[2] = AUX_2_u16;
    temp_msg[3] = AUX_2_u16 >> 8U;
    temp_msg[4] = AUX_3_u16;
    temp_msg[5] = AUX_3_u16 >> 8U;
    temp_msg[6] = AUX_4_u16;
    temp_msg[7] = AUX_4_u16 >> 8U;
    can_msg_Tx = CANMessage(AUX_sense_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

    // Qdrive internal state shitting out
    temp_msg[0] = VDU_FLT;
    temp_msg[1] = VDU_FLT >> 8U;
    temp_msg[2] = VDU_STAT;
    temp_msg[3] = (int8_t)(SDn_voltage*10.0f);
    temp_msg[4] = (int8_t)(Brk_voltage*10.0f);
    temp_msg[5] = 0U;
    temp_msg[6] = 0U;
    temp_msg[7] = 0U;
    can_msg_Tx = CANMessage(Qdrv_stat_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);

    // IMU attitude readings shitting out, 10Hz on CAN but 100Hz for internal use
    tmp = (int16_t) (YR_imu * 10000.0f);
    temp_msg[0] = tmp;
    temp_msg[1] = tmp >> 8U;
    tmp = (int16_t) (Roll_imu * 100.0f);
    temp_msg[2] = tmp;
    temp_msg[3] = tmp >> 8U;
    tmp = (int16_t) (Pitch_imu * 100.0f);
    temp_msg[4] = tmp;
    temp_msg[5] = tmp >> 8U;
    temp_msg[6] = (int8_t)Ax_imu;
    temp_msg[7] = (int8_t)Ay_imu;
    can_msg_Tx = CANMessage(IMU_sense_ID,temp_msg,8,CANData,CANStandard);
    CANpendTX();
    can1.write(can_msg_Tx);
}

void CANpendTX(void)
{
    //Pend till TX box has empty slot, timeout will generate error
    uint32_t timeout = 0;
    while(!(CAN1->TSR & CAN_TSR_TME_Msk)) {
        //Wait till non empty
        timeout += 1;
        if(timeout > 10000) {
            // Put some timeout error handler
            break;
        }
    }
}

void CAN_init(void)
{
    //Set CAN system
    SET_BIT(CAN1->MCR, CAN_MCR_ABOM);               // Enable auto reboot after bus off
    can1.filter(FL_HSB_ID,0xFFFF,CANStandard,0);    // ID filter listing mode
    can1.filter(FR_HSB_ID,0xFFFF,CANStandard,1);
    can1.filter(RL_HSB_ID,0xFFFF,CANStandard,2);
    can1.filter(RR_HSB_ID,0xFFFF,CANStandard,3);
    can1.filter(FL_LSB_ID,0xFFFF,CANStandard,4);
    can1.filter(FR_LSB_ID,0xFFFF,CANStandard,5);
    can1.filter(RL_LSB_ID,0xFFFF,CANStandard,6);
    can1.filter(RR_LSB_ID,0xFFFF,CANStandard,7);
    can1.filter(HMI_cmd_ID,0xFFFF,CANStandard,8);   // PSU online monitoring
//    can1.mode(CAN::GlobalTest);                     // Add only for testing 2019/11/13
    can1.attach(&Rx_CAN1, CAN::RxIrq);              // CAN1 Recieve Irq
}

void Module_WD(void)
{
    //Module online dissipitive indicator
    if (FL_online != 0) {
        FL_online -= 1;
    }
    if (FR_online != 0) {
        FR_online -= 1;
    }
    if (RL_online != 0) {
        RL_online -= 1;
    }
    if (RR_online != 0) {
        RR_online -= 1;
    }
    if (PSU_online != 0) {
        PSU_online -= 1;
    }
}
uint8_t Indication(                     // Blink indicator in pattern * repeat
    uint8_t pattern,
    uint16_t*repeat,
    uint8_t*blk_n)
{
    uint8_t out = 0;
    if(*repeat==0) return out;          // reject repeat = 0 case, out = 0
    out = (pattern>>(*blk_n)) & 1U;     // blink from LSB to MSB
    if(*blk_n < 7U) {
        *blk_n += 1U;
    } else {                            // a full pattern was passed
        *blk_n = 0U;
        *repeat >>= 1U;
    }
    return out;
}

uint8_t IndicationKernel(               // Generate blink pattern, return 1 if all ind cleared
    uint8_t*pattern,
    uint16_t*repeat,
    uint8_t*phase,
    uint16_t*Post_ind,
    uint16_t*Run_ind,
    uint16_t*VDU_ind)
{
    //Blink indicator in pattern
    //If FLT_Run = 0b0010-0110, pattern will be --......--...--..
    uint8_t refresh = 0;
    if(*repeat!=0) return refresh;              // skip straight to Indication()

    if(*Post_ind != 0) {
        switch(*phase) {
            case 0U:
                *repeat = Post_rep;
                *pattern = L_Pulse;
                *phase = 1U;
                break;

            case 1U:
                *repeat = (*Post_ind)&(-*Post_ind); // extract LSB bit
                *Post_ind &= ~*repeat;              // this bit is used out
                *pattern = S_Pulse;
                *phase = 2U;
                break;

            case 2U:
                *repeat = 1U;
                *pattern = N_Pulse;
                *phase = 0U;
                break;
        }
        return refresh;
    }

    if(*Run_ind != 0) {
        switch(*phase) {
            case 0U:
                *repeat = Run_rep;
                *pattern = L_Pulse;
                *phase = 1U;
                break;

            case 1U:
                *repeat = (*Run_ind)&(-*Run_ind);  // extract LSB bit
                *Run_ind &= ~*repeat;              // this bit is used out
                *pattern = S_Pulse;
                *phase = 2U;
                break;

            case 2U:
                *repeat = 1U;
                *pattern = N_Pulse;
                *phase = 0U;
                break;
        }
        return refresh;
    }

    if(*VDU_ind != 0) {
        switch(*phase) {
            case 0U:
                *repeat = VDU_rep;
                *pattern = L_Pulse;
                *phase = 1U;
                break;

            case 1U:
                *repeat = (*VDU_ind)&(-*VDU_ind);  // extract LSB bit
                *VDU_ind &= ~*repeat;              // this bit is used out
                *pattern = S_Pulse;
                *phase = 2U;
                break;

            case 2U:
                *repeat = 1U;
                *pattern = N_Pulse;
                *phase = 0U;
                break;
        }
        return refresh;
    }

    // else all XXX_ind is cleared out, set refresh
    refresh = 1U;
    return refresh;
}

void Cooler(void)
{
    //Cooling auto control, unfinish 2019/11/15
    Max_Tmotor = max_fval(FL_Tmotor, FR_Tmotor, RL_Tmotor, RR_Tmotor);
    Max_Tmodule = max_fval(FL_Tmodule, FR_Tmodule, RL_Tmodule, RR_Tmodule);
    if(0) {
        Aux_Rly = 1;
    } else {
        Aux_Rly = 0;
    }
}

int16_t max_uval(int16_t i1, int16_t i2, int16_t i3, int16_t i4)
{
    int16_t max = i1;
    if(i2 > max) max = i2;
    if(i3 > max) max = i3;
    if(i4 > max) max = i4;
    return max;
}

float max_fval(float i1, float i2, float i3, float i4)
{
    float max = i1;
    if(i2 > max) max = i2;
    if(i3 > max) max = i3;
    if(i4 > max) max = i4;
    return max;
}
//            pc.printf("SOC: %.2f\n", Module_Total*0.01f);