



/**
 ******************************************************************************
 *
 * @file        BSP_19_LCD.c
 * @brief       This is LCD C file.
 
 * @par         Project
 *              MG32
 * @version     V1.02
 * @date        2023/12/26
 * @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.
 *******************************************************************************
 *******************************************************************************
 */
 
/*==============================================================================
                                 User Notes
How To use this function:
-----------------------
   1. Use "BSP_LCM_Init" function to initial.
   2. Use "BSP_LCM_ShowString" to show that want to show string.
   3. Use "BSP_LCM_GetStatus" to get LCM update whether has ended or not.
   
Driver architecture:
--------------------
   + MG32_GPIO_DRV
   + MG32_TM_DRV
   
Known Limitations:
------------------

Require parameter
------------------
    Require module : CSC / GPIO / TM16 
    
    GPIO pin configuration : 
        Pin / IO mode / AFS
        ---  --------  -----
        PD4 / PPO     / GPIO
        PD5 / PPO     / GPIO
        PD9 / PPO     / GPIO
        PD10/ PPO     / GPIO
        PD11/ PPO     / GPIO
        PD12/ PPO     / GPIO
        PE9 / PPO     / GPIO
        PE12/ PPO     / GPIO
        PA3 / PPO     / GPIO
        PA9 / PPO     / GPIO
        PA11/ PPO     / GPIO

    TM16 Module :
        Mode              : Cascade mode
        Overflow time     : 1ms ( in TM00 clock source is 48Mhz)
       
Example codes:
------------------

==============================================================================*/

/* Includes ------------------------------------------------------------------*/ 
#include "BSP_19_LCD.h"

/* Wizard menu ---------------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
//GPIO
#define LCM_D0_PIN                  PX_Pin_4
#define LCM_D1_PIN                  PX_Pin_5
#define LCM_D2_PIN                  PX_Pin_9
#define LCM_D3_PIN                  PX_Pin_10
#define LCM_D4_PIN                  PX_Pin_11
#define LCM_D5_PIN                  PX_Pin_12
#define LCM_D6_PIN                  PX_Pin_9
#define LCM_D7_PIN                  PX_Pin_12
#define LCM_D012345_IOM             IOMD
#define LCM_D67_IOM                 IOME
#define LCM_D012345_PORT            GPIOD
#define LCM_D67_PORT                GPIOE
#define LCM_DX_MODE                 PINX_Mode_PushPull_O
#define LCM_DX_AFS                  0
                                    
#define LCM_RS_PIN                  PX_Pin_3
#define LCM_RS_IOM                  IOMA
#define LCM_RS_PORT                 GPIOA
#define LCM_RS_MODE                 PINX_Mode_PushPull_O
#define LCM_RS_AFS                  0
                                    
#define LCM_RW_PIN                  PX_Pin_9
#define LCM_RW_IOM                  IOMA
#define LCM_RW_PORT                 GPIOA
#define LCM_RW_MODE                 PINX_Mode_PushPull_O
#define LCM_RW_AFS                  0
                                    
#define LCM_E_PIN                   PX_Pin_11
#define LCM_E_IOM                   IOMA
#define LCM_E_PORT                  GPIOA
#define LCM_E_MODE                  PINX_Mode_PushPull_O
#define LCM_E_AFS                   0

//Delay use URT module 
#define LCD_TM                      TM16

//LCM Signal control
#define LCM_RS_CMD()                LCM_RS_PORT->SC.H[1] = LCM_RS_PIN
#define LCM_RS_DATA()               LCM_RS_PORT->SC.H[0] = LCM_RS_PIN
#define LCM_RS_LOW()                LCM_RS_PORT->SC.H[1] = LCM_RS_PIN
#define LCM_RS_High()               LCM_RS_PORT->SC.H[0] = LCM_RS_PIN
                                    
#define LCM_RW_WRITE()              LCM_RW_PORT->SC.H[1] = LCM_RW_PIN
#define LCM_RW_READ()               LCM_RW_PORT->SC.H[0] = LCM_RW_PIN
#define LCM_RW_LOW()                LCM_RW_PORT->SC.H[1] = LCM_RW_PIN
#define LCM_RW_HIGH()               LCM_RW_PORT->SC.H[0] = LCM_RW_PIN
                                    
#define LCM_E_DISABLE()             LCM_E_PORT->SC.H[1]  = LCM_E_PIN
#define LCM_E_ENABLE()              LCM_E_PORT->SC.H[0]  = LCM_E_PIN
#define LCM_E_LOW()                 LCM_E_PORT->SC.H[1]  = LCM_E_PIN
#define LCM_E_HIGH()                LCM_E_PORT->SC.H[0]  = LCM_E_PIN

//LCM Command
#define CLEAR_DISPLAY               0x01
#define RETURN_HOME                 0x02
#define ENTRY_MODE_SET              0x04
    #define I_D_MOVING_DEC              0x00
    #define I_D_MOVING_INC              0x02
    #define I_D_DISPLAY_SHIFT_OFF       0x00
    #define I_D_DISPLAY_SHIFT_ON        0x01
#define DISPLAY_ON_OFF_CONTROL      0x08
    #define DISPLAY_OFF                 0x00
    #define DISPLAY_ON                  0x04
    #define CURSOR_OFF                  0x00
    #define CURSOR_ON                   0x02
    #define BLINKING_CURSOR_OFF         0x00
    #define BLINKING_CURSOR_ON          0x01
#define CURSOR_DISPLAY_SHIFT        0x10
#define FUNCTION_SET                0x20
    #define DL_4BIT                     0x00
    #define DL_8BIT                     0x10
    #define N_1LINE                     0x00
    #define N_2LINE                     0x08
    #define F_5X8_DOTS                  0x00
    #define F_5X11_DOTS                 0x04
#define SET_CGRAM_ADDRESS           0x40
#define SET_DDRAM_ADDRESS           0x80

//LCM control status
#define LCM_STATE_MASK              0xF0
#define LCM_STATE_IDLE              0x00
#define LCM_STATE_UPDATE_1LINE      0x20
#define LCM_STATE_UPDATE_2LINE      0x30

#define LCM_STRING_MAX              16

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/ 
static const char    *LCM_Line1StringBuf;
static const char    *LCM_Line2StringBuf;
static       uint8_t LCM_Line1StringCnt;
static       uint8_t LCM_Line2StringCnt;
static       uint8_t LCM_State;

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

static void LCM_16X2_Init(void);
static void LCM_WriteCmd(uint8_t Cmd);
static void LCM_WriteData(uint8_t Data);
static void LCM_DelayTime(uint16_t LCM_Delay);

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

/**
 *******************************************************************************
 * @brief	TM1x IRQHandler   
 * @details Handle LCM update.
 * @return      
 * @note       
 *******************************************************************************
 */
void TM1x_IRQHandler(void)
{
    uint8_t TX1x_IRQHandlerTmp;

    TX1x_IRQHandlerTmp = (LCM_State & LCM_STATE_MASK );
    
    
    if( TX1x_IRQHandlerTmp == LCM_STATE_UPDATE_1LINE)
    {
        //Get LCD address in TX1x_IRQHandlerTmp in 1 line 
        TX1x_IRQHandlerTmp = LCM_State & (~LCM_STATE_MASK);
        
        if( TX1x_IRQHandlerTmp == 0)
        {
            // Configure LCD address to 1 line
            LCM_WriteCmd(SET_DDRAM_ADDRESS | 0x00);
            TM_Counter_Config(LCD_TM,1,1);
        }
        else if( (TX1x_IRQHandlerTmp - 1) < LCM_Line1StringCnt)
        {
            // Display a character
            LCM_WriteData(LCM_Line1StringBuf[(TX1x_IRQHandlerTmp-1)]);
        }
        else
        {
            LCM_State = (LCM_STATE_UPDATE_2LINE | 0x00);
            TM_ClearFlag( LCD_TM, TMx_TOF);
            return;
        }
        LCM_State = LCM_State + 1;
    }
    else
    {
        //Get LCD address in TX1x_IRQHandlerTmp in 2 line
        TX1x_IRQHandlerTmp = LCM_State & (~LCM_STATE_MASK);
        
        if( TX1x_IRQHandlerTmp == 0)
        {
            //Configure LCD address to 2 line
            LCM_WriteCmd(SET_DDRAM_ADDRESS | 0x40);
        }
        else if( (TX1x_IRQHandlerTmp - 1) < LCM_Line2StringCnt)
        {
            //Display a character
            LCM_WriteData(LCM_Line2StringBuf[(TX1x_IRQHandlerTmp-1)]);
        }
        else
        {
            //Disable TM & interrupt
            TM_Timer_Cmd(LCD_TM,DISABLE);
            TM_ITEA_Cmd( LCD_TM, DISABLE);
            LCM_State = LCM_STATE_IDLE; 
            
            return;     
        }
        //To next LCD address
        LCM_State = LCM_State + 1;
    }
    TM_ClearFlag( LCD_TM, TMx_TOF);
}
/**
 *******************************************************************************
 * @brief	 LCM initial.  
 * @details     
 * @return      
 * @note       
 *******************************************************************************
 */
void BSP_LCM_Init(void)
{
    PIN_InitTypeDef         LCM_Pin;
    TM_TimeBaseInitTypeDef  LCM_TimeBase;
    
    /*GPIO Initial*/
    // D0 ~ D7 
    GPIO_ClearPortBit(LCM_D012345_PORT , ( LCM_D0_PIN | LCM_D1_PIN | LCM_D2_PIN | LCM_D3_PIN | LCM_D4_PIN | LCM_D5_PIN));
    GPIO_ClearPortBit(LCM_D67_PORT     , ( LCM_D6_PIN | LCM_D7_PIN ));
    GPIO_ClearPortBit(LCM_RW_PORT      , LCM_RW_PIN);
    GPIO_ClearPortBit(LCM_E_PORT       , LCM_E_PIN);
    GPIO_SetPortBit(  LCM_RS_PORT      , LCM_RS_PIN);
    
    //LCM
    LCM_Pin.PINX_Pin                = ( LCM_D0_PIN | LCM_D1_PIN | LCM_D2_PIN | LCM_D3_PIN | LCM_D4_PIN | LCM_D5_PIN);
    LCM_Pin.PINX_Mode               = LCM_DX_MODE;
    LCM_Pin.PINX_PUResistant        = PINX_PUResistant_Disable;
    LCM_Pin.PINX_Speed              = PINX_Speed_Low;
    LCM_Pin.PINX_Inverse            = PINX_Inverse_Disable;
    LCM_Pin.PINX_OUTDrive           = PINX_OUTDrive_Level0;
    LCM_Pin.PINX_FilterDivider      = PINX_FilterDivider_Bypass;
    LCM_Pin.PINX_Alternate_Function = LCM_DX_AFS;                          
    GPIO_PortMode_Config( LCM_D012345_IOM ,&LCM_Pin);
    
    LCM_Pin.PINX_Pin                = ( LCM_D6_PIN | LCM_D7_PIN );
    GPIO_PortMode_Config( LCM_D67_IOM ,&LCM_Pin);

    //RS, RW, RS
    LCM_Pin.PINX_Pin                 = LCM_RS_PIN;
    LCM_Pin.PINX_Alternate_Function  = LCM_RS_AFS;
    LCM_Pin.PINX_Mode                = LCM_RS_MODE;
    GPIO_PortMode_Config( LCM_RS_IOM ,&LCM_Pin);

    LCM_Pin.PINX_Pin                 = LCM_RW_PIN;
    LCM_Pin.PINX_Alternate_Function  = LCM_RW_AFS;
    LCM_Pin.PINX_Mode                = LCM_RW_MODE;
    GPIO_PortMode_Config( LCM_RW_IOM ,&LCM_Pin);
    
    LCM_Pin.PINX_Pin                 = LCM_E_PIN;
    LCM_Pin.PINX_Alternate_Function  = LCM_E_AFS;
    LCM_Pin.PINX_Mode                = LCM_E_MODE;
    GPIO_PortMode_Config( LCM_E_IOM ,&LCM_Pin);


    /*Timer initial*/
    //Timer Default initial
    NVIC_DisableIRQ( TM1x_IRQn);
    TM_DeInit(LCD_TM);
    
    //TimeBase structure initial
    TM_TimeBaseStruct_Init(&LCM_TimeBase);
    
    //Set Step motor rotating speed.
    //Speed = TM clock source freq / ((TM_Prescaler + 1) * (TM_Period + 1));
    LCM_TimeBase.TM_MainClockDirection = TM_UpCount;
    LCM_TimeBase.TM_Prescaler          = 48000 - 1;                     // Prescaler overflow is 1ms in 48MHz.
    LCM_TimeBase.TM_Period             = 1 - 1;                                 
    LCM_TimeBase.TM_CounterMode        = Cascade;
    TM_TimeBase_Init(LCD_TM, &LCM_TimeBase);
    TM_Timer_Cmd(LCD_TM,DISABLE);
    TM_AutoStop_Cmd(LCD_TM,ENABLE);                                     // Timer auto stop in overflow.

    //Interrupt configure
    TM_ClearFlag( LCD_TM, TMx_TOF);
    TM_IT_Config(LCD_TM, TMx_TIE_IE,ENABLE);
    TM_ITEA_Cmd( LCD_TM, DISABLE);
    
    /*Interrupt initial*/
    NVIC_EnableIRQ( TM1x_IRQn);
    NVIC_SetPriority(TM1x_IRQn,1);                                      // Set TM0x interrupt priority = 1 ( 0 is highest)
    
    /*Software Control Relationship Parameter Initial*/
    LCM_State             = LCM_STATE_IDLE;
    LCM_Line1StringCnt    = 0;
    LCM_Line2StringCnt    = 0;
    
    /*LCM Initial*/
    LCM_16X2_Init();

}
/**
 *******************************************************************************
 * @brief	  LCM show string function.   
 * @details     
 * @param[in] LCM_Line1String : LCM 1st line show string data pointer.
 * @param[in] LCM_Line2String : LCM 2nd line show string data pointer.
 * @return      
 * @note       
 *******************************************************************************
 */
uint8_t BSP_LCM_ShowString( const char *LCM_Line1String, const char *LCM_Line2String)
{
    uint8_t LCM_ShowStringTmp;
    
    if( LCM_State != LCM_STATE_IDLE)
    {
        return(BSP_LCM_BUSY);
    }
    /*LCM 1 Line string*/
    LCM_ShowStringTmp  = 0;
    LCM_Line1StringBuf = LCM_Line1String;
    
    while( LCM_Line1String[LCM_ShowStringTmp]!=0x00)
    {
        LCM_ShowStringTmp = LCM_ShowStringTmp + 1;
    }
    
    if( LCM_ShowStringTmp > LCM_STRING_MAX)
    {
        return(BSP_LCM_FAILURE);
    }
    LCM_Line1StringCnt = LCM_ShowStringTmp;
    
    /*LCM 2 Line string*/
    LCM_ShowStringTmp = 0;
    LCM_Line2StringBuf = LCM_Line2String;

    while( LCM_Line2String[LCM_ShowStringTmp]!=0x00)
    {
        LCM_ShowStringTmp = LCM_ShowStringTmp + 1;
    }
    
    if( LCM_ShowStringTmp > LCM_STRING_MAX)
    {
        return(BSP_LCM_FAILURE);
    }
    LCM_Line2StringCnt = LCM_ShowStringTmp;
    
    /*Display clear*/
    LCM_WriteCmd(CLEAR_DISPLAY);

    /*Enable Timer*/
    LCM_State = (LCM_STATE_UPDATE_1LINE | 0x00);
    
    TM_Counter_Config(LCD_TM,2,2);
    TM_ClearFlag( LCD_TM, TMx_TOF);
    TM_Timer_Cmd(LCD_TM,ENABLE);
    TM_ITEA_Cmd( LCD_TM, ENABLE);
    
    return(BSP_LCM_SUCCESS);
}
/**
 *******************************************************************************
 * @brief	Get LCM update whether has ended or not.   
 * @details 
 * @return  LCM status( 0 : has ended). 
 * @note       
 *******************************************************************************
 */
uint8_t BSP_LCM_GetStatus(void)
{
    return( (uint8_t)((LCM_State>>4)&BSP_LCM_BUSY));
    
}
/**
 *******************************************************************************
 * @brief   Initial 16x2 LCD at power up state   
 * @details 
 * @return      
 * @note       
 *******************************************************************************
 */
static void LCM_16X2_Init(void)
{
    //=============================================================
    //Power up - initial flow
    LCM_DelayTime(16);
    LCM_WriteCmd( (FUNCTION_SET | DL_8BIT ));
    LCM_DelayTime(5);
    LCM_WriteCmd( (FUNCTION_SET | DL_8BIT ));
    LCM_DelayTime(1);
    LCM_WriteCmd( (FUNCTION_SET | DL_8BIT ));
    LCM_DelayTime(1);
    
    //=============================================================
    //Function Set (Specify the number of display lines and font.)
    LCM_WriteCmd((FUNCTION_SET | DL_8BIT | N_2LINE | F_5X8_DOTS));
    LCM_DelayTime(1);

    //=============================================================
    //Display Off 
    LCM_WriteCmd((DISPLAY_ON_OFF_CONTROL | DISPLAY_OFF | CURSOR_OFF | BLINKING_CURSOR_OFF));
    LCM_DelayTime(1);

    //=============================================================
    //Display Clear
    LCM_WriteCmd(CLEAR_DISPLAY);
    LCM_DelayTime(2);
    
    //=============================================================
    //Entry Mode Set
    LCM_WriteCmd((ENTRY_MODE_SET | I_D_MOVING_INC | I_D_DISPLAY_SHIFT_OFF));
    LCM_DelayTime(1);
    
    //=============================================================
    LCM_WriteCmd(RETURN_HOME);
    LCM_DelayTime(2);
    
    //=============================================================
    //Display On
    LCM_WriteCmd((DISPLAY_ON_OFF_CONTROL | DISPLAY_ON | CURSOR_OFF | BLINKING_CURSOR_OFF));
    LCM_DelayTime(1); 
}

/**
 *******************************************************************************
 * @brief   Write command to LCD   
 * @details Output data with control LCD_RS, LCD_RW & LCD_E state.
 * @return      
 * @note       
 *******************************************************************************
 */ 
static void LCM_WriteCmd(uint8_t Cmd)
{
    uint16_t li;

    LCM_D012345_PORT->SC.H[1] = (uint16_t)((( (~Cmd) & 0x03) << 4) | (((~Cmd) & 0x3c) << 7));
    LCM_D67_PORT->SC.H[1]     = (uint16_t)((( (~Cmd) & 0x40) << 3) | (((~Cmd) & 0x80) << 5));
    
    
    LCM_D012345_PORT->SC.H[0] = (uint16_t)((( Cmd & 0x03) << 4) | ((Cmd & 0x3c) << 7));
    LCM_D67_PORT->SC.H[0]     = (uint16_t)((( Cmd & 0x40) << 3) | ((Cmd & 0x80) << 5));
    
    __ISB();
    LCM_RS_CMD();
    __ISB();
    LCM_RW_WRITE();
    __ISB();
    LCM_E_ENABLE();
    __ISB();
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
    LCM_E_DISABLE();
    __ISB();
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
    for(li = 0; li < 100; li++);
} 
/**
 *******************************************************************************
 * @brief    Write data to LCD  
 * @details  Output data with control LCD_RS, LCD_RW, LCD_E state
 * @return      
 * @note       
 *******************************************************************************
 */
static void LCM_WriteData(uint8_t Data)
{
    LCM_D012345_PORT->SC.H[1] = (uint16_t)((( (~Data) & 0x03) << 4) | (((~Data) & 0x3c) << 7));
    LCM_D67_PORT->SC.H[1]     = (uint16_t)((( (~Data) & 0x40) << 3) | (((~Data) & 0x80) << 5));
    
    
    LCM_D012345_PORT->SC.H[0] = (uint16_t)((( Data & 0x03) << 4) | ((Data & 0x3c) << 7));
    LCM_D67_PORT->SC.H[0]     = (uint16_t)((( Data & 0x40) << 3) | ((Data & 0x80) << 5));

    __ISB();
    LCM_RS_DATA();
    __ISB();
    LCM_RW_WRITE();
    __ISB();
    LCM_E_ENABLE();
    __ISB();
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
    LCM_E_DISABLE();
    __ISB();
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
    __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); __NOP(); __ISB(); 
}
/**
 *******************************************************************************
 * @brief	 Delay a time ( unit 1ms)  
 * @details  Delay time with TM   
 * @return      
 * @note     
 *******************************************************************************
 */
static void LCM_DelayTime(uint16_t LCM_Delay)
{
    // Disable TM interrupt
    TM_ITEA_Cmd( LCD_TM, DISABLE);
    
    // Reconfig TM counter
    TM_Counter_Config(LCD_TM,0,(LCM_Delay-1));
    
    // Clear TM flag and restart it
    TM_ClearFlag( LCD_TM, TMx_TOF);
    TM_Timer_Cmd(LCD_TM,ENABLE);
    
    //Until complete
    while( (TM_GetAllFlagStatus(LCD_TM) & TMx_TOF)==0);
}


//}






