
/**
 ******************************************************************************
 *
 * @file        main.C
 * @brief       MG32 demo main c Code. (square driver)
 *
 * @par         Project
 *              MG32
 * @version     V1.02
 * @date        2025/04/17
 * @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_System.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
        Motor_HandlerDef hMotor;
    #pragma clang diagnostic pop
#else
    Motor_HandlerDef hMotor;
#endif

/* Private function prototypes -----------------------------------------------*/
void Display_Motor_Direction(void);


/* Exported variables --------------------------------------------------------*/
/* Exported functions --------------------------------------------------------*/
/* External vairables --------------------------------------------------------*/

/**
 *******************************************************************************
 * @brief       Display "(FOR)" or "(REV)" on 16x2 LCD depending on hMotor.Direction
 * 				option.
 * @return		No
 *******************************************************************************
 */
void Display_Motor_Direction(void)
{
    if(hMotor.Direction == MOTOR_FORWARD)
        API_LCM_Display(firstLine+11, (uint8_t*) "(FOR)");
    else
        API_LCM_Display(firstLine+11, (uint8_t*) "(REV)");
}


/**
 *******************************************************************************
 * @brief       Controlling BLDC with sine wave drive.
 * @details     The hMotor structure controls the BLDC state.
 * 				The 16x2 LCD displays the BLDC state.
 * 				Buttons:
 * 					1. Press the 'START' button to startup motor.
 * 					2. Press the 'STOP' button to stop motor.
 * 					3. Press the 'DIRECTION' button to reverse motor direction.
 *				The variable resistor controls Motor speed.
 *				The motor has the minimum duty cycle, and user must control VR
 *				greater than the minimum duty cycle to control the motor speed.
 * @return		No
 *******************************************************************************
 */
int main(void)
{
    uint8_t Str[20];								// String display to LCD
    volatile uint32_t RPMSpeed, tmp;				// RPMSpeed: motor speed, tmp: normal variable
    volatile uint32_t TimeLCDPeriod, TimePIPeriod;	// TimeLCDPeriod: to display LCD period counter
    volatile uint8_t TimeLCDFlag;					// TimeLCDFlag: trigger event for display LCD
    volatile uint8_t TimePIFlag;					// TimePIFlag: trigger event for PI calculation
    volatile uint32_t CurrentSysTickCNT;			// SysTick counter

    // ------------------------------------------------------------------------
    // chip initial (CSC, GPIO, IRQ)
    // ------------------------------------------------------------------------
    ChipInit();

    // ------------------------------------------------------------------------
    // Systick initial & setting priority
    // ------------------------------------------------------------------------
    MID_Init();

    // ------------------------------------------------------------------------
    // LCM initial
    // ------------------------------------------------------------------------
    API_LCM_Init();

    // ------------------------------------------------------------------------
    // ADC initial (sample Variable-Resistor)
    // ------------------------------------------------------------------------
    API_ADC_Init();

    // --------------------------------------------------------------------
    // initial motor state in idle state
    // --------------------------------------------------------------------
    hMotor.DesiredDuty          = API_ADC_GetChannel0();    // desire the motor duty state
    hMotor.CurrentDuty          = INIT_DUTY;        // record motor output duty cycle
    hMotor.MotorState           = Motor_IDLE;       // initial motor in idle state
    hMotor.MotorSpeed           = 100;              // initial motor speed
    hMotor.Direction            = MOTOR_FORWARD;    // motor direction (Forward / Reverse)    
    hMotor.commutate_flag       = 0;                // motor commutate flag
    hMotor.commutate_time       = 0;                // record the motor commutate time (clocks)
    hMotor.MinSpeed_flag        = 0;                // detect motor speed smaller than the mimun speed
    hMotor.HallUsedPattern      = 0x0000;           // prevent HALL sensor fail
    
    // --------------------------------------------------------------------
    // Motor rotating (loop)
    // --------------------------------------------------------------------
    while(1)
    {

        // --------------------------------------------------------------------
        // initial LCM & display
        // --------------------------------------------------------------------
        API_LCM_Display(firstLine+0x00, (uint8_t*) "Motor idle       ");
        API_LCM_Display(SecondLine+0x00, (uint8_t*) "ADC         ");

        // --------------------------------------------------------------------
        // initial motor state in idle state
        // --------------------------------------------------------------------
        hMotor.DesiredDuty          = API_ADC_GetChannel0();
        
        // modify MotorState
        if (hMotor.MotorState != Motor_ReverseRotating)     // User press "Reverse button" in motor running state
        {
            hMotor.MotorState       = Motor_IDLE;                            
        }
        
        hMotor.CurrentDuty          = INIT_DUTY;
        hMotor.commutate_flag       = 0;
        hMotor.commutate_time       = 0;
        hMotor.MinSpeed_flag        = 0;
        hMotor.HallUsedPattern      = 0x0000;               // It will check in "API_BLDC_Init()" function
        
        // --------------------------------------------------------------------
        // wait for press 'START' button to startup motor
        // --------------------------------------------------------------------
        if (hMotor.MotorState != Motor_ReverseRotating)
        {

            // Is press START_BUTTON ?
            while(START_BUTTON)
            {
                // Is press DIRECTION_BUTTON ?
                if(DIRECTION_BUTTON == 0)
                {
                    // key debounce ~200ms
                    TimeLCDPeriod = MID_GetTick();
                    while((MID_GetTick() - TimeLCDPeriod) < 100)
                    {
                        if(DIRECTION_BUTTON == 0)
                            MID_ClearTick();
                        if(MID_GetTick() < TimeLCDPeriod)
                            TimeLCDPeriod = MID_GetTick();
                    }

                    // wait for release DIRECTION_BUTTON
                    TimeLCDPeriod = MID_GetTick();
                    while((MID_GetTick() - TimeLCDPeriod) < 100)
                    {
                        if(DIRECTION_BUTTON == 0)
                            TimeLCDPeriod = MID_GetTick();
                    }

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

                }

                // Display Motor direction
                Display_Motor_Direction();
                
                // Show ADC value (10 bit)
                hMotor.DesiredDuty = API_ADC_GetChannel0();
                sprintf((char*)Str, "%4d", hMotor.DesiredDuty);
                API_LCM_Display(SecondLine+0x03, (uint8_t*) Str);
            }
        }

        // --------------------------------------------------------------------
        // PWM & Hall sensor initial
        // Commutate in expection routine
        // --------------------------------------------------------------------
        API_BLDC_Square_Init();

        // --------------------------------------------------------------------
        // check Motor State (Error state routine)
        // --------------------------------------------------------------------
        if(hMotor.MotorState != Motor_Running)
        {
            // for [-Wswitch-enum] warning 
            #if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 */
                #pragma clang diagnostic push
                #pragma clang diagnostic ignored "-Wswitch-enum"
                    switch(hMotor.MotorState)
                    {
                    case Motor_Hall_Fault:
                        API_LCM_Display(firstLine+0x00, (uint8_t*) "Hall Error");
                        while(1);
                    case Motor_Stuck:
                        API_LCM_Display(firstLine+0x00, (uint8_t*) "! Startup FAIL !");
                        while(1);
                    default:
                        API_LCM_Display(firstLine+0x00, (uint8_t*) "Unknow Fault");
                        break;
                    }
                #pragma clang diagnostic pop
            #else
                switch(hMotor.MotorState)
                {
                case Motor_Hall_Fault:
                    API_LCM_Display(firstLine+0x00, (uint8_t*) "Hall Error");
                    while(1);
                    // no break
                case Motor_Stuck:
                    API_LCM_Display(firstLine+0x00, (uint8_t*) "! Startup FAIL !");
                    while(1);
                    // no break
                default:
                    API_LCM_Display(firstLine+0x00, (uint8_t*) "Unknow Fault");
                    break;
                }
            #endif
                    
            continue;
        }


        // --------------------------------------------------------------------
        // clear first line of LCM
        // --------------------------------------------------------------------
        API_LCM_Display(firstLine+0x00, (uint8_t*) "                ");

        TimeLCDPeriod = TimePIPeriod = 0;               // clear Time-solt
        CurrentSysTickCNT = MID_GetTick();
        TimeLCDFlag = 1; TimePIFlag = 0;                // clear Time-flag

        // --------------------------------------------------------------------
        // Display desire PWM output duty
        // --------------------------------------------------------------------
		sprintf((char*)Str, "%4d", hMotor.DesiredDuty);
        API_LCM_Display(SecondLine+0x03, (uint8_t*) Str);

        // --------------------------------------------------------------------
        // Motor in running state
        // --------------------------------------------------------------------
        while(1)
        {
            // ----------------------------------------------------------------
            // 1sec period
            //   1. calculate & display morot speed
            //   2. show QEI position
            // ----------------------------------------------------------------
            if(TimeLCDFlag == 1)
            {
                TimeLCDFlag = 0; 
                // Show Motor speed (unit:RPM)
                RPMSpeed = ConvertMotorSpeed();
                if(RPMSpeed < MIN_RPM)
                    hMotor.MinSpeed_flag = MOTOR_LOWER_MIN_RPM;
                else
                    hMotor.MinSpeed_flag = MOTOR_HIGHER_MIN_RPM;

                sprintf((char*)Str, "%04drpm D:%04d", RPMSpeed, hMotor.CurrentDuty);
                API_LCM_Display(firstLine+0x00, (uint8_t*) Str);

                // ----------------------------------------------------------------
                // Show ADC value (10 bit)
                // ----------------------------------------------------------------
                tmp = API_ADC_GetChannel0();
                if ((int32_t) tmp != hMotor.DesiredDuty)
                {
                    hMotor.DesiredDuty = (int32_t) tmp;
                    sprintf((char*)Str, "%4d", hMotor.DesiredDuty);
                    API_LCM_Display(SecondLine+0x03, (uint8_t*) Str);
                }
                
            }
            


            // ----------------------------------------------------------------
            // ~100ms period (PI calculate)
            // ----------------------------------------------------------------
            if(TimePIFlag == 1)
            {
                TimePIFlag = 0;
                // PI controller - calculate
                PI_Calculate();
            }

            // ----------------------------------------------------------------
            // if press STOP_BUTTON then exit running state & stop motor
            // ----------------------------------------------------------------
            if(STOP_BUTTON == 0)
            {
                // Stop motor
                API_BLDC_Square_STOP();
                hMotor.MotorState = Motor_Stop;

                break;
            }

            // ----------------------------------------------------------------
            // if Motor over current (Cycle by Cycle)
            // ----------------------------------------------------------------
            if(hMotor.MotorState == Motor_OverCurrent)
            {
                // display "Over Current" ~2sec
                API_LCM_Display(firstLine+0x00, (uint8_t*) "  Over Current  ");
                MID_Delay(2000);
                hMotor.MotorState = Motor_Running;
            }

            // ----------------------------------------------------------------
            // Check motor stuck state (if the motor can't commutate within 500ms)
            // ----------------------------------------------------------------
            if(hMotor.commutate_flag == 1)
            {
                hMotor.commutate_flag = 0;
                hMotor.commutate_time = MID_GetTick();
            }
            if((MID_GetTick()- hMotor.commutate_time) > 500)
            {                
                API_BLDC_Square_STOP();
                hMotor.MotorState = Motor_Stuck;
                API_LCM_Display(firstLine+0x00, (uint8_t*) " !!! Stuck !!!  ");
                while(1);
            }
            
            
            // ----------------------------------------------------------------
            // Press DIRECTION_BUTTON to reverse motor
            // ----------------------------------------------------------------
            if(DIRECTION_BUTTON == 0)
            {
                hMotor.MotorState = Motor_ReverseRotating;                
                API_BLDC_Square_ReverseRotating();
                break;
            }

            // ----------------------------------------------------------------
            // Time Slot Manager (TimeLCDPeriod, TimePIPeriod)
            // flag (TimeLCDFlag, TimePIFlag)
            // ----------------------------------------------------------------
            if((MID_GetTick() - TimePIPeriod) > PID_peroid)
            {
                // update TimePIPeriod
                TimePIPeriod = MID_GetTick();
                // Needs PI calculator
                TimePIFlag = 1;
            }
            if((MID_GetTick() - TimeLCDPeriod) > 1000)
            {
                // update TimeLCDPeriod & commutate_time
                TimeLCDPeriod = MID_GetTick();
                hMotor.commutate_time = 0;
                // Show LCD event
                TimeLCDFlag = 1;
                // update CurrentSysTickCNT
                CurrentSysTickCNT = MID_GetTick();
            }
            // ----------------------------------------------------------------
            // Systick overflow
            // ----------------------------------------------------------------
            if (MID_GetTick() < CurrentSysTickCNT)
            {
                // Clear: PI calculator & Show LCD event
                TimePIFlag = TimeLCDFlag = 0;
                // update CurrentSysTickCNT, TimeLCDPeriod & TimePIPeriod
                CurrentSysTickCNT = MID_GetTick();
                TimeLCDPeriod = CurrentSysTickCNT;
                TimePIPeriod = CurrentSysTickCNT;
            }
        }
    }
}
