
/**
 ******************************************************************************
 *
 * @file        MG32_BLDC_API.c
 * @brief       Drive BLDC c Code. 
 *
 * @par         Project
 *              MG32
 * @version     V1.02
 * @date        2021/05/28
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2017 MegaWin Technology Co., Ltd.
 *              All rights reserved.
 *
 ******************************************************************************* 
 * @par Disclaimer
 * The Demo software is provided "AS IS" without any warranty, either
 * expressed or implied, including, but not limited to, the implied warranties
 * of merchantability and fitness for a particular purpose. The author will
 * not be liable for any special, incidental, consequential or indirect
 * damages due to loss of data or any other reason.
 * These statements agree with the world wide and local dictated laws about
 * authorship and violence against these laws.
 *******************************************************************************
 *******************************************************************************
 */


/* Includes ------------------------------------------------------------------*/
#include "MG32_BLDC_API.h"
#include "MG32_APB_MID.h"
#include "MG32_BLDC_SinewaveTable.h"
#include "MG32_BLDC_System.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static TM_HandleTypeDef    mTM36;
static TM_HandleTypeDef    mTM10;
static TM_HandleTypeDef    mTM16;
static TM_OC_InitTypeDef   sConfig;

static volatile uint16_t   BLDC_PhaseA_IDX;
static volatile uint16_t   BLDC_PhaseB_IDX;
static volatile uint16_t   BLDC_PhaseC_IDX;
        
static volatile uint16_t   BLDC_SineTable1[192];
static volatile uint16_t   BLDC_SineTable2[192];

// PID control
static volatile int32_t    Verror, Acci;
//static volatile int32_t    Vpi;
//static volatile int32_t    p,e,d;
static volatile int32_t    p,e;

/* Private function prototypes -----------------------------------------------*/
void TM3x_IRQHandler(void);
void TM10_IRQHandler(void);

/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/
extern volatile     Motor_HandlerDef hMotor;


/**
 *******************************************************************************
 * @brief       Initial TM36 PWM output & Sine Wave Table
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_Init(void)
{
    volatile uint32_t tmp;
    volatile uint16_t CNT_Rot;    
    TM_IC_InitTypeDef iConfig;
    TM_BreakDeadTimeConfigTypeDef BKConfig;
    TM_ClockConfigTypeDef CKConfig;

    // ------------------------------------------------------------------------
    // check Hall pattern
    // ------------------------------------------------------------------------
    if ((API_BLDC_GET_HALL() == 0x0000) || (API_BLDC_GET_HALL() == 0x0070))
    {
        hMotor.MotorState = Motor_Hall_Fault;
        return;
    }

    // ------------------------------------------------------------------------
    // Generate duty cycle of sinewave with motor parameters
    // ------------------------------------------------------------------------
    for (BLDC_PhaseA_IDX=0; BLDC_PhaseA_IDX<192; BLDC_PhaseA_IDX++)
    {
        if(BLDC_PhaseA_IDX < 128)
        {
            tmp = SineWave[BLDC_PhaseA_IDX]*INIT_DUTY;
            tmp = tmp / 1024;
        }
        else
            tmp = 0;
        BLDC_SineTable1[BLDC_PhaseA_IDX] = (uint16_t) tmp;
        BLDC_SineTable2[BLDC_PhaseA_IDX] = (uint16_t) tmp;
    }
    
    // ------------------------------------------------------------------------
    // TM16 confiuration
    // ------------------------------------------------------------------------
    // TM16 init for counting time of MOTOR rotation
    mTM16.Instance                  = TM16;
    mTM16.Init.TM_CounterMode       = TM_CASCADE_UP;
    mTM16.Init.TM_Period            = 65535;
    mTM16.Init.TM_Prescaler         = (SineSteps - 1);
    MID_TM_Base_Init(&mTM16);

    CKConfig.TM_ClockSource         = TM_INTERNAL_CLOCK;
    CKConfig.TM_InternalClockSource = TM_INTERNALCLOCK_PROC;
    CKConfig.TM_INTClockDivision    = TM_INTERNALCLOCK_DIVDER_DIV1;
#if defined ( __GNUC__ )
    CKConfig.TM_ExternalClockSource = 0;
#else
    CKConfig.TM_ExternalClockSource = NULL;
#endif
    MID_TM_ConfigClockSource(&mTM16, &CKConfig);

    MID_TM_Base_Start(&mTM16);

    // ------------------------------------------------------------------------
    // TM36 confiuration with clock
    // ------------------------------------------------------------------------
    // initial TM36 for 10-bit resolution of PWM
    mTM36.Instance                  = TM36;
    mTM36.Init.TM_CounterMode       = TM_CASCADE_UP;
    mTM36.Init.TM_Period            = 1023;
    mTM36.Init.TM_Prescaler         = 0;
    MID_TM_Base_Init(&mTM36);

    MID_TM_ConfigClockSource(&mTM36, &CKConfig);
    // ------------------------------------------------------------------------
    // Pin initial for PWM 2's complment (Channel-0~2)
    MID_TM_OC_Struct_Init(&sConfig);
    sConfig.OCMode = TM_CH_Disable;
    sConfig.Pulse = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);

    sConfig.Pulse = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);

    sConfig.Pulse = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);

    // ------------------------------------------------------------------------
    // Config Dead-Timer parameters with Central-align
    __DRV_TM_ENABLE_PWM_CENTRAL(&mTM36);

    // ------------------------------------------------------------------------
    // Set Break in cycle-by-cycle mode
    BKConfig.BreakMode              = TM_BK_CYCLE_BY_CYCLE;
    BKConfig.BreakCHxOutputMode     = MID_BREAK_STOP_STATE;
    BKConfig.BreakSourceSel         = MID_BK_ExtPin | MID_BK_ClockFailure | MID_BK_CPULOCKUP | MID_BK_BOD1;
    BKConfig.DeatTimeClockDivision  = MID_TM_CKDTG_DIV1;
    BKConfig.DeadTime               = DeadTime_Set;
    MID_TM_ConfigBreakDeadTime(&mTM36, &BKConfig);

    // Enable TM36 Break IT
    __DRV_TM_ENABLE_IT(&mTM36, TM_IT_BREAK);

    // ------------------------------------------------------------------------
    // Enable PWM output
    MID_TM_PWM_DTG_Start(&mTM36, MID_TM_Channel0);
    MID_TM_PWM_DTG_Start(&mTM36, MID_TM_Channel1);
    MID_TM_PWM_DTG_Start(&mTM36, MID_TM_Channel2);

    // ------------------------------------------------------------------------
    // TM10 confiuration
    // ------------------------------------------------------------------------
    // TM10 initial for change Motor duty cycle
    mTM10.Instance                  = TM10;
    mTM10.Init.TM_CounterMode       = TM_CASCADE_UP;
    mTM10.Init.TM_Period            = T_MIN_RPM;
    mTM10.Init.TM_Prescaler         = 0;
    MID_TM_Base_Init(&mTM10);

    MID_TM_ConfigClockSource(&mTM10, &CKConfig);

    // manual enable TM10 interrupt
    NVIC_EnableIRQ(TM10_IRQn);
    NVIC_SetPriority(TM10_IRQn, 0xC0);                  // The Lowest priority

    // ------------------------------------------------------------------------
    // initial TM36_XOR pin
    iConfig.ICSelection             = MID_TM_INPUTMUX_PIN;
    iConfig.ICPolarity              = TM_ICPOLARITY_DISABLE;
    MID_TM_IC_ConfigChannel(&mTM36, &iConfig, MID_TM_Channel0);
    MID_TM_IC_ConfigChannel(&mTM36, &iConfig, MID_TM_Channel1);
    MID_TM_IC_ConfigChannel(&mTM36, &iConfig, MID_TM_Channel2);

    iConfig.ICSelection = MID_TM_INPUTMUX_LINE3;        // TM36_XOR trigger signal connect IC channel
    iConfig.ICPolarity = TM_ICPOLARITY_DUALEDGE;

    MID_TM_IC_ConfigChannel(&mTM36, &iConfig, MID_TM_Channel3);
    MID_TM_IC_Start_IT(&mTM36, MID_TM_Channel3);

    NVIC_EnableIRQ(TM3x_IRQn);
    NVIC_SetPriority(TM3x_IRQn, 0x00);                  // The Highest priority

    // ------------------------------------------------------------------------
    MID_TM_Base_Start_IT(&mTM10);

    // ------------------------------------------------------------------------
    // reset TM16 & TM10
    // ------------------------------------------------------------------------
    __DRV_TM_RESET_TIMER(&mTM16);
    __DRV_TM_RESET_TIMER(&mTM10);

    // ------------------------------------------------------------------------
    // ITR7 confiuration (select TM36_XOR)
    // ------------------------------------------------------------------------
    __DRV_APB_ITR7_ENABLE(APB_ITR7_TM36_XOR);

    // ------------------------------------------------------------------------
    // Startup : PWM
    // ------------------------------------------------------------------------
    // Enable PWM output
    NVIC_DisableIRQ(TM10_IRQn);


    // output PWM in Motor_Startup state.
//    sConfig.Pulse = INIT_DUTY;          // initial duty cycle in
    hMotor.MotorState               = Motor_Starup;

    // Disable all PWM output
    API_BLDC_STARTUP_CHx_DISABLE();

    //
    for(CNT_Rot=0; CNT_Rot < 24; CNT_Rot++)
    {
        API_BLDC_Startup(INIT_DUTY);    // output PWM depend on Hall position
                                        // (like 6-step BLDC drive mode)

        MID_ClearTick();                // clear Systick

        hMotor.commutate_flag = 0;
        while(1)
        {
            if(hMotor.commutate_flag == 1) break;   // wait for motor commutate

            if(MID_GetTick()  > 3000)                // Motor speed < 5rpm (That means stuck state happened)
            {
                API_BLDC_STOP();
                hMotor.MotorState = Motor_Stuck;
                return;
            }
            if(hMotor.MotorState == Motor_OverCurrent)
            {
                API_BLDC_STOP();
                return;
            }
        }
    }

    // check hall pattern
    if(hMotor.HallUsedPattern != 0x003F)            // Hall pattern is not {0x5, 0x4, 0x6, 0x2, 0x3, 0x1}
    {
        API_BLDC_STOP();
        hMotor.MotorState = Motor_Hall_Fault;
        return;
    }



    // ------------------------------------------------------------------------
    // Motor running
    // ------------------------------------------------------------------------
    NVIC_EnableIRQ(TM10_IRQn);

    API_BLDC_Running_Init();            // initial BLDC_PhaseA_IDX, B_IDX and C_IDX

    // reset Channel0~2 to PWM output
    sConfig.OCMode                  = TM_CH_16bit_PWM_COMPLEMENT;
    sConfig.OCIdleState             = TM_OCIDLESTATE_RESET;
    sConfig.OCNIdleState            = TM_OCIDLESTATE_RESET;
    sConfig.Pulse                   = BLDC_SineTable1[BLDC_PhaseA_IDX];   // setting channel0 duty cycle
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);

    sConfig.Pulse                   = BLDC_SineTable1[BLDC_PhaseB_IDX];   // setting channel1 duty cycle
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);

    sConfig.Pulse                   = BLDC_SineTable1[BLDC_PhaseC_IDX];   // setting channel2 duty cycle
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);

    // Motot running ...
    hMotor.MotorState               = Motor_Running;
    hMotor.commutate_time           = 0;
    
}

/**
 *******************************************************************************
 * @brief       Calculate Duty cycle of SineWaveTable.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_UpdateTable(void)
{
    uint32_t CalMath;
    static uint16_t CalcIDX = 0;

    if(hMotor.TableState == Table_Calculate)
    {
        CalcIDX ++;
        CalMath = SineWave[CalcIDX]*((uint32_t) hMotor.CurrentDuty);
        CalMath = CalMath / 1024;
        if(hMotor.SineWaveTable_IDX == 1)
            BLDC_SineTable2[CalcIDX] = (uint16_t) CalMath;
        else
            BLDC_SineTable1[CalcIDX] = (uint16_t) CalMath;

        if(CalcIDX == 127)
        {
            CalcIDX = 0;
            hMotor.TableState = Table_Calculate_End;
        }
    }
    // if calculate complete then switch to another SineWave table
    else if(hMotor.TableState == Table_Calculate_End)
    {
        if(hMotor.SineWaveTable_IDX == 1)
            hMotor.SineWaveTable_IDX = 2;
        else
            hMotor.SineWaveTable_IDX = 1;
        // reset TableState to idle state
        hMotor.TableState = Table_IDLE;
    }


}

/**
 *******************************************************************************
 * @brief       TM10 interrupt routine.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void TM10_IRQHandler(void)
{
    // total spend ~1.936us
    BLDC_PhaseA_IDX ++;
    BLDC_PhaseB_IDX ++;
    BLDC_PhaseC_IDX ++;

    if(BLDC_PhaseA_IDX > 191) BLDC_PhaseA_IDX = 0;
    if(BLDC_PhaseB_IDX > 191) BLDC_PhaseB_IDX = 0;
    if(BLDC_PhaseC_IDX > 191) BLDC_PhaseC_IDX = 0;

    __DRV_TM_CLEAR_FLAG(&mTM10, TM_FLAG_UPDATE_UP);

    if (hMotor.SineWaveTable_IDX == 1)
    {
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel0, BLDC_SineTable1[BLDC_PhaseA_IDX]);
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel1, BLDC_SineTable1[BLDC_PhaseB_IDX]);
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel2, BLDC_SineTable1[BLDC_PhaseC_IDX]);
    }
    else
    {
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel0, BLDC_SineTable2[BLDC_PhaseA_IDX]);
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel1, BLDC_SineTable2[BLDC_PhaseB_IDX]);
        __DRV_TM_SET_COMPARE_B(&mTM36, MID_TM_Channel2, BLDC_SineTable2[BLDC_PhaseC_IDX]);
    }

}

/**
 *******************************************************************************
 * @brief       TM36 interrupt routine.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void TM3x_IRQHandler(void)
{
    static uint16_t CNT6Step = 0;

    // XOR event
    if((TM36->STA.W & (TM_FLAG_CC3A | TM_FLAG_CC3B)) != 0x00)
    {
        // Get TM16 main counter value and replace TM10 counter
        if((++CNT6Step) > 11)
        {
            hMotor.MotorSpeed = __DRV_TM_GET_COUNTER(&mTM16) >> 1;
            __DRV_TM_SET_AUTORELOAD(&mTM10, hMotor.MotorSpeed);

            CNT6Step = 0;

            // reset TM16 & TM10
            __DRV_TM_RESET_TIMER(&mTM16);
            __DRV_TM_RESET_TIMER(&mTM10);
        }

        // synchronization index at 160th step
        API_BLDC_SynchronizationUVW();

        // clear TM36 flag
        __DRV_TM_CLEAR_FLAG(&mTM36, (TM_FLAG_CC3A | TM_FLAG_CC3B));
        __DRV_TM_CLEAR_FLAG(&mTM10, TM_FLAG_UPDATE_UP);
        __DRV_TM_CLEAR_FLAG(&mTM16, TM_FLAG_UPDATE_UP);

        // setting commutate flag & record commutate time
        hMotor.commutate_flag = 1;

        return;
    }

    // Break event happened
    if(__DRV_TM_GET_FLAG(&mTM36, TM_FLAG_BREAK) != 0x00)
    {
        hMotor.MotorState = Motor_OverCurrent;
        __DRV_TM_CLEAR_FLAG(&mTM36, TM_FLAG_BREAK);
//        TM36->BS.MBIT.BKSW_EN = 0;
    }

}

/**
 *******************************************************************************
 * @brief       BLDC Startup procedure.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_Startup (uint16_t sDutyControl)
{
    volatile uint16_t GetHallSenesor;

    GetHallSenesor = API_BLDC_GET_HALL();

    if (hMotor.Direction == MOTOR_FORWARD)
    {
        // forward direction
        switch (GetHallSenesor)
        {
        case 0x0050:    // 0x50
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (201)
                PhaseA output (T:0, B:1)
                PhaseB output (T:PWM, B:/PWM)
                PhaseC output (T:0, B:0)
            */
            API_BLDC_STARTUP_CH2_DISABLE();
            API_BLDC_STARTUP_CH0_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH1_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0001;

            return;

        case 0x0040:    // 0x40
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (021)
                PhaseA output (T:0, B:0)
                PhaseB output (T:PWM, B:/PWM)
                PhaseC output (T:0, B:1)
            */
            API_BLDC_STARTUP_CH0_DISABLE();
            API_BLDC_STARTUP_CH2_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH1_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0020;
            return;

        case 0x0060:    // 0x60
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (120)
                PhaseA output (T:PWM, B:/PWM)
                PhaseB output (T:0, B:0)
                PhaseC output (T:0, B:1)
            */
            API_BLDC_STARTUP_CH1_DISABLE();
            API_BLDC_STARTUP_CH2_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH0_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0010;
            return;

        case 0x0020:    // 0x20
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (210)
                PhaseA output (T:PWM, B:/PWM)
                PhaseB output (T:0, B:1)
                PhaseC output (T:0, B:0)
            */
            API_BLDC_STARTUP_CH2_DISABLE();
            API_BLDC_STARTUP_CH1_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH0_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0008;
            return;

        case 0x0030:    // 0x30
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (012)
                PhaseA output (T:0, B:0)
                PhaseB output (T:0, B:1)
                PhaseC output (T:PWM, B:/PWM)
            */
            API_BLDC_STARTUP_CH0_DISABLE();
            API_BLDC_STARTUP_CH1_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH2_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0004;
            return;

        case 0x0010:    // 0x10
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (102)
                PhaseA output (T:0, B:1)
                PhaseB output (T:0, B:0)
                PhaseC output (T:PWM, B:/PWM)
            */
            API_BLDC_STARTUP_CH1_DISABLE();
            API_BLDC_STARTUP_CH0_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH2_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0002;
            return;

        default:
            return;
        }
    }

    else if (hMotor.Direction == MOTOR_REVERSE)
    {
        // Reverse direction
        switch (GetHallSenesor)
        {
        case 0x0050:    // 0x50
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (210)
                PhaseA output (T:PWM, B:/PWM)
                PhaseB output (T:0, B:1)
                PhaseC output (T:0, B:0)
            */
            API_BLDC_STARTUP_CH2_DISABLE();
            API_BLDC_STARTUP_CH1_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH0_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0001;
            return;
        case 0x0010:    // 0x10
//            API_BLDC_STARTUP_CHx_DISABLE();

            /* NOTE : (120)
                PhaseA output (T:PWM, B:/PWM)
                PhaseB output (T:0, B:0)
                PhaseC output (T:0, B:1)
            */
            API_BLDC_STARTUP_CH1_DISABLE();
            API_BLDC_STARTUP_CH2_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH0_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0002;
            return;
        case 0x0030:    // 0x30
//            API_BLDC_STARTUP_CHx_DISABLE();

            /* NOTE : (021)
                PhaseA output (T:0, B:0)
                PhaseB output (T:PWM, B:/PWM)
                PhaseC output (T:0, B:1)
             */
            API_BLDC_STARTUP_CH0_DISABLE();
            API_BLDC_STARTUP_CH2_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH1_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0004;
            return;
        case 0x0020:    // 0x20
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (201)
                PhaseA output (T:0, B:1)
                PhaseB output (T:PWM, B:/PWM)
                PhaseC output (T:0, B:0)
            */
            API_BLDC_STARTUP_CH2_DISABLE();
            API_BLDC_STARTUP_CH0_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH1_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0008;
            return;
        case 0x0060:    // 0x60
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (102)
                PhaseA output (T:0, B:1)
                PhaseB output (T:0, B:0)
                PhaseC output (T:PWM, B:/PWM)
            */
            API_BLDC_STARTUP_CH1_DISABLE();
            API_BLDC_STARTUP_CH0_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH2_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0010;
            return;
        case 0x0040:    // 0x40
//            API_BLDC_STARTUP_CHx_DISABLE();
            /* NOTE : (012)
                PhaseA output (T:0, B:0)
                PhaseB output (T:0, B:1)
                PhaseC output (T:PWM, B:/PWM)
            */
            API_BLDC_STARTUP_CH0_DISABLE();
            API_BLDC_STARTUP_CH1_TOP0_BOTTOM1();
            API_BLDC_STARTUP_CH2_PWM(sDutyControl);

            hMotor.HallUsedPattern |= 0x0020;
            return;
        default:
            return;
        }
    }
    else
    {
        API_BLDC_STOP();
        return;
    }
}

/**
 *******************************************************************************
 * @brief       BLDC running initial procedure.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_Running_Init (void)
{

    API_BLDC_STARTUP_CHx_DISABLE();

    if (hMotor.Direction == MOTOR_FORWARD)
    {
        // forward direction
        switch (API_BLDC_GET_HALL())
        {
        case 0x0050:    // 0x50
            BLDC_PhaseA_IDX = 160 + IDXOffset;
            BLDC_PhaseB_IDX = 32 + IDXOffset;
            BLDC_PhaseC_IDX = 96 + IDXOffset;
            break;
        case 0x0040:    // 0x40
            BLDC_PhaseA_IDX = 0 + IDXOffset;
            BLDC_PhaseB_IDX = 64 + IDXOffset;
            BLDC_PhaseC_IDX = 128 + IDXOffset;
            break;
        case 0x0060:    // 0x60
            BLDC_PhaseA_IDX = 32 + IDXOffset;
            BLDC_PhaseB_IDX = 96 + IDXOffset;
            BLDC_PhaseC_IDX = 160 + IDXOffset;
            break;
        case 0x0020:    // 0x20
            BLDC_PhaseA_IDX = 64 + IDXOffset;
            BLDC_PhaseB_IDX = 128 + IDXOffset;
            BLDC_PhaseC_IDX = 0 + IDXOffset;
            break;
        case 0x0030:    // 0x30
            BLDC_PhaseA_IDX = 96 + IDXOffset;
            BLDC_PhaseB_IDX = 160 + IDXOffset;
            BLDC_PhaseC_IDX = 32 + IDXOffset;
            break;
        case 0x0010:    // 0x10
            BLDC_PhaseA_IDX = 128 + IDXOffset;
            BLDC_PhaseB_IDX = 0 + IDXOffset;
            BLDC_PhaseC_IDX = 64 + IDXOffset;
            break;
        default:
            break;
        }
    }


    if (hMotor.Direction == MOTOR_REVERSE)
    {
        // Reverse direction
        switch (API_BLDC_GET_HALL())
        {
        case 0x0050:    // 0x50
            BLDC_PhaseA_IDX = 32 + IDXOffset;
            BLDC_PhaseB_IDX = 160 + IDXOffset;
            BLDC_PhaseC_IDX = 96 + IDXOffset;
            break;
        case 0x0010:    // 0x10
            BLDC_PhaseA_IDX = 64 + IDXOffset;
            BLDC_PhaseB_IDX = 0 + IDXOffset;
            BLDC_PhaseC_IDX = 128 + IDXOffset;
            break;
        case 0x0030:    // 0x30
            BLDC_PhaseA_IDX = 96 + IDXOffset;
            BLDC_PhaseB_IDX = 32 + IDXOffset;
            BLDC_PhaseC_IDX = 160 + IDXOffset;
            break;
        case 0x0020:    // 0x20
            BLDC_PhaseA_IDX = 128 + IDXOffset;
            BLDC_PhaseB_IDX = 64 + IDXOffset;
            BLDC_PhaseC_IDX = 0 + IDXOffset;
            break;
        case 0x0060:    // 0x60
            BLDC_PhaseA_IDX = 160 + IDXOffset;
            BLDC_PhaseB_IDX = 96 + IDXOffset;
            BLDC_PhaseC_IDX = 32 + IDXOffset;
            break;
        case 0x0040:    // 0x40
            BLDC_PhaseA_IDX = 0 + IDXOffset;
            BLDC_PhaseB_IDX = 128 + IDXOffset;
            BLDC_PhaseC_IDX = 64 + IDXOffset;
            break;
        default:
            break;
        }
    }

}

/**
 *******************************************************************************
 * @brief       Synchronization. U,V,W synchronization & switch table.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void  API_BLDC_SynchronizationUVW (void)
{
    volatile uint16_t GetHallSenesor;

    GetHallSenesor = API_BLDC_GET_HALL();

    if (hMotor.Direction == MOTOR_FORWARD)
    {

        // forward direction
        switch (GetHallSenesor)
        {
        case 0x0050:    // 0x50
            BLDC_PhaseA_IDX = 160 + IDXOffset;
            break;
        case 0x0060:    // 0x60
            BLDC_PhaseC_IDX = 160  + IDXOffset;
            break;
        case 0x0030:    // 0x30
            BLDC_PhaseB_IDX = 160  + IDXOffset;
            break;
        default:
            break;
        }
    }


    if (hMotor.Direction == MOTOR_REVERSE)
    {
        // Reverse direction
        switch (GetHallSenesor)
        {
        case 0x0050:    // 0x50
            BLDC_PhaseB_IDX = 160  + IDXOffset;
            break;
        case 0x0030:    // 0x30
            BLDC_PhaseC_IDX = 160  + IDXOffset;
            break;
        case 0x0060:    // 0x10
            BLDC_PhaseA_IDX = 160  + IDXOffset;
            break;
        default:
            break;
        }
    }


}

/**
 *******************************************************************************
 * @brief       BLDC Stop running state.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STOP (void)
{
    // Stop TM36 PWM output
    __DRV_TM_DISABLE_IT(&mTM36, TM_IT_BREAK);           // disable TM36 break interrupt
    __DRV_TM_SOFTWARE_ENABLE_BREAK(&mTM36);             // software enable break state then TM36 all channel output stop state

    // Disable IRQ handle
    NVIC_DisableIRQ(TM3x_IRQn);
    NVIC_DisableIRQ(TM10_IRQn);

    // DeInit all timer
    MID_TM_Base_Stop(&mTM16);
    MID_TM_Base_DeInit(&mTM16);

    MID_TM_Base_Stop_IT(&mTM10);
    MID_TM_Base_DeInit(&mTM10);

    MID_TM_Base_Stop(&mTM36);
    MID_TM_IC_Stop_IT(&mTM36, MID_TM_Channel3);

    MID_TM_PWM_DTG_Stop(&mTM36, MID_TM_Channel0);
    MID_TM_PWM_DTG_Stop(&mTM36, MID_TM_Channel1);
    MID_TM_PWM_DTG_Stop(&mTM36, MID_TM_Channel2);

    API_BLDC_STARTUP_CH0_DISABLE();
    API_BLDC_STARTUP_CH1_DISABLE();
    API_BLDC_STARTUP_CH2_DISABLE();

    __DRV_TM_SOFTWARE_DISABLE_BREAK(&mTM36);            // exit TM36 break state
}

/**
 *******************************************************************************
 * @brief       BLDC Stop running state.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_ReverseRotating (void)
{
    uint32_t CompareHallPattern;

    // Stop TM36 PWM output
    API_BLDC_STOP();

    // wait for motor halt
    CompareHallPattern = API_BLDC_GET_HALL();
    while(1)
    {
        if(CompareHallPattern != API_BLDC_GET_HALL())
        {
            hMotor.commutate_flag = 0;
            hMotor.commutate_time = MID_GetTick();
            CompareHallPattern = API_BLDC_GET_HALL();
        }
        if((MID_GetTick()- hMotor.commutate_time) > 500)
        {
            break;
        }
    }

    MID_Delay(500);

    // Change Motor direction
    if(hMotor.Direction == MOTOR_REVERSE)
        hMotor.Direction = MOTOR_FORWARD;
    else
        hMotor.Direction = MOTOR_REVERSE;

}

/**
 *******************************************************************************
 * @brief       saturate().
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
int32_t saturate (int32_t i, int32_t l)
{
    if(i > +l)                         // if greater than upper limit
    {
        i = +l;                        // set to upper limit
    }
    else if (i < -l)                  // if less than lower limit
    {
        i = -l;                        // set to lower limit
    }
    return i;                         // return saturated value
}

/**
 *******************************************************************************
 * @brief       PID().
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void PI_Cal_KpKi_routine (void)
{
    int32_t T;

    e = 0;
    if(KPINIT!=0)                               // check for zero
    {
        p = Verror * KPINIT;
        p = saturate(p, KpStatution);
    }

    if(KIINIT!=0)                               // check for zero
    {
        Acci += Verror;
        Acci = saturate(Acci, KiStatution);     // avoid Acci too large

        T = Acci * KIINIT;
        T = saturate(T, KiStatution);
    }

    e = p + T;
}

/**
 *******************************************************************************
 * @brief       for caluate PI function.
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void PI_Calculate (void)
{
    volatile int32_t tmp;

    Verror =  hMotor.DesiredDuty - hMotor.CurrentDuty;
    if(Verror == 0) return;

    PI_Cal_KpKi_routine ();

    e = e >> 5;
    if (e == 0) return;

    // motor speed can't lower the MIN_RPM
    if(hMotor.MinSpeed_flag == MOTOR_LOWER_MIN_RPM)
        if (e < 0) return;
    if(hMotor.MaxSpeed_flag == MOTOR_HIGHER_MAX_RPM)
        if (e > 0) return;

    //
    tmp = hMotor.CurrentDuty + e;
    if(tmp < 0) tmp = 0;
    if(tmp > 1023) tmp = 1023;
    hMotor.CurrentDuty = tmp;
    hMotor.TableState = Table_Calculate;        // change TableState to calculate mode
}


/**
 *******************************************************************************
 * @brief       Channel0 output PWM with complement
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH0_PWM(uint16_t sDutyControl)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = sDutyControl;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);
}

/**
 *******************************************************************************
 * @brief       Channel0: Disable PWM output & output all 'Low' Only
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH0_DISABLE(void)
{
    sConfig.OCMode          = TM_CH_Disable;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);
}

/**
 *******************************************************************************
 * @brief       Channel0: Disable PWM output & Top:'Low' & Bottom: 'High'
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH0_TOP0_BOTTOM1(void)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);
}

/**
 *******************************************************************************
 * @brief       Channel1 output PWM with complement
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH1_PWM(uint16_t sDutyControl)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = sDutyControl;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);
}

/**
 *******************************************************************************
 * @brief       Channel1: Disable PWM output & output all 'Low' Only
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH1_DISABLE(void)
{
    sConfig.OCMode          = TM_CH_Disable;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);
}

/**
 *******************************************************************************
 * @brief       Channel1: Disable PWM output & Top:'Low' & Bottom: 'High'
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH1_TOP0_BOTTOM1(void)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);
}

/**
 *******************************************************************************
 * @brief       Channel2 output PWM with complement
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH2_PWM(uint16_t sDutyControl)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = sDutyControl;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);
}

/**
 *******************************************************************************
 * @brief       Channel2: Disable PWM output & output all 'Low' Only
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH2_DISABLE(void)
{
    sConfig.OCMode          = TM_CH_Disable;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);
}

/**
 *******************************************************************************
 * @brief       Channel2: Disable PWM output & Top:'Low' & Bottom: 'High'
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CH2_TOP0_BOTTOM1(void)
{
    sConfig.OCMode          = TM_CH_16bit_PWM_COMPLEMENT;
//    sConfig.OCIdleState     = TM_OCIDLESTATE_RESET;
//    sConfig.OCNIdleState    = TM_OCNIDLESTATE_RESET;
    sConfig.Pulse           = 0;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);
}


/**
 *******************************************************************************
 * @brief       Channel0~2: Disable PWM output
 * @param[in]   None
 * @return		None
 *******************************************************************************
 */
void API_BLDC_STARTUP_CHx_DISABLE(void)
{
    sConfig.OCMode = TM_CH_Disable;
    sConfig.OCIdleState = TM_OCIDLESTATE_RESET;
    sConfig.OCNIdleState = TM_OCNIDLESTATE_RESET;
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel0);
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel1);
    MID_TM_PWM_ConfigChannel(&mTM36, &sConfig, MID_TM_Channel2);
}

