/**
 ******************************************************************************
 *
 * @file        MG32_LCD_MID.c
 * @brief       The code is LCD middleware C file.
 *
 * @par         Project
 *              MG32
 * @version     V1.02
 * @date        2025/04/14
 * @author      Megawin Software Center
 * @copyright   Copyright (c) 2024 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.
 *******************************************************************************
 *******************************************************************************
 */

/* Wizard menu ---------------------------------------------------------------*/

/* Includes ------------------------------------------------------------------*/
#include "MG32.h"
#if defined(ModuleExist_LCD)
#include "MG32_LCD_MID.h"

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

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/** @defgroup LCD_Private_Defines LCD Private Defines
  * @{
  */

#define LCD_TIMEOUT_VALUE             1000

/**
  * @}
  */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private user code ---------------------------------------------------------*/

/**
  *******************************************************************************
  * @brief  DeInitializes the LCD peripheral.
  * @param  mlcd LCD handle
  * @retval MID status
  *******************************************************************************
  */
MID_StatusTypeDef MID_LCD_DeInit(LCD_HandleTypeDef *mlcd)
{
    /* Check the LCD handle allocation */
    if(mlcd == NULL)
    {
        return MID_ERROR;
    }

    /* Check the parameters */
    assert_param(IS_LCD_ALL_INSTANCE(mlcd->Instance));

    /* Check the LCD peripheral state */
    if(mlcd->State == MID_LCD_STATE_BUSY)
    {
        return MID_BUSY;
    }

    mlcd->State = MID_LCD_STATE_BUSY;

    mlcd->Instance->CR2.W |= LCD_CR2_SEG_OFF_mask_w;        /*!< LCD Display OFF  */

    /* Disable the peripheral */
    __DRV_LCD_DISABLE(mlcd);

    /* Disable the Charge-Pump Disable */
    __DRV_LCD_CHARGEPUMP_DISABLE(mlcd);

    /* VT Power-OFF */
    mlcd->Instance->CR0.W = (mlcd->Instance->CR0.W & (~LCD_CR0_VS_SEL_mask_w));

    mlcd->Instance->CR2.W &= (~LCD_CR2_SEG_OFF_mask_w);        /*!< LCD Display ON  */

    /*Disable Highdrive by default*/
    __DRV_LCD_HIGHDRIVER_DISABLE(mlcd);

    /* OVD Disable */
    mlcd->Instance->CR1.W = (mlcd->Instance->CR1.W | LCD_CR1_OVD_DIS_mask_w);

    /* DeInit the low level hardware */
    MID_LCD_MspDeInit(mlcd);

    mlcd->ErrorCode = MID_LCD_ERROR_NONE;
    mlcd->State = MID_LCD_STATE_RESET;

    /* Release Lock */
    __MID_UNLOCK(mlcd);

    return MID_OK;
}

/**
  *******************************************************************************
  * @brief  Initializes the LCD peripheral according to the specified parameters 
  *         in the LCD_InitStruct.
  * @note   This function can be used only when the LCD is disabled.  
  *         The LCD HighDrive can be enabled/disabled using related macros up to user.
  * @param  mlcd LCD handle
  * @retval None
  *******************************************************************************
  */
MID_StatusTypeDef MID_LCD_Init(LCD_HandleTypeDef *mlcd)
{
    uint32_t lTickstart = 0x00;
    uint32_t lTemp32 = 0;

    /* Check the LCD handle allocation */
    if(mlcd == NULL)
        return MID_ERROR;

    /* Check function parameters */
    assert_param(IS_LCD_ALL_INSTANCE(mlcd->Instance));
    assert_param(IS_LCD_DIVIDER(mlcd->Init.Divider));
    assert_param(IS_LCD_PRESCALER(mlcd->Init.Prescaler));
    assert_param(IS_LCD_DUTY(mlcd->Init.Duty));
    assert_param(IS_LCD_BIAS(mlcd->Init.Bias));
    assert_param(IS_LCD_VOLTAGE_SOURCE(mlcd->Init.VoltageSource));
    assert_param(IS_LCD_PULSE_ON_DURATION(mlcd->Init.PulseOnDuration));
    assert_param(IS_LCD_HIGHDRIVE(mlcd->Init.HighDrive));
    assert_param(IS_LCD_DEAD_TIME(mlcd->Init.DeadTime));
    assert_param(IS_LCD_CONTRAST(mlcd->Init.Contrast));
    assert_param(IS_LCD_BLINK_DIVIDER(mlcd->Init.BlinkDivider));
    assert_param(IS_LCD_BLINK_COUNT(mlcd->Init.BlinkCount));
    assert_param(IS_LCD_COM0PIN(mlcd->Init.COM0PIN));
    assert_param(IS_LCD_COM1PIN(mlcd->Init.COM1PIN));
    assert_param(IS_LCD_COM2PIN(mlcd->Init.COM2PIN));
    assert_param(IS_LCD_COM3PIN(mlcd->Init.COM3PIN));
    assert_param(IS_LCD_COM4PIN(mlcd->Init.COM4PIN));
    assert_param(IS_LCD_COM5PIN(mlcd->Init.COM5PIN));
    assert_param(IS_LCD_COM6PIN(mlcd->Init.COM6PIN));
    assert_param(IS_LCD_COM7PIN(mlcd->Init.COM7PIN));

    if(mlcd->State == MID_LCD_STATE_RESET)
    {
        /* Allocate lock resource and initialize it */
        mlcd->Lock = MID_UNLOCKED;

        /* Initialize the low level hardware (MSP) */
        MID_LCD_MspInit(mlcd);
    }

    mlcd->State = MID_LCD_STATE_BUSY;

    /* Disable the peripheral */
    __DRV_LCD_DISABLE(mlcd);

    /* Set the LCD_MDn registers and enable the display request by setting the UDR bit in the LCD_MDm register */
    mlcd->Instance->MD0.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD1.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD2.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD3.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD4.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD5.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD6.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD7.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD8.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD9.W = 0xFFFFFFFFUL;
    mlcd->Instance->MD10.W = 0xFFFFFFFFUL;

    mlcd->Instance->MUX0.W = 0x00000000UL; 
    mlcd->Instance->MUX1.W = 0x00000000UL;

    /* Configure the LCD COM0 of MUX0/1 and MDn Registselect register */
    {
        if(mlcd->Init.COM0PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM0PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM0PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM0PIN)) = (uint8_t)0x01;
    }

    /* Configure the LCD COM1 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_2)
    {
        if(mlcd->Init.COM1PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM1PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM1PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM1PIN)) = (uint8_t)0x02;
    }

    /* Configure the LCD COM2 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_3)
    {
        if(mlcd->Init.COM2PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM2PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM2PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM2PIN)) = (uint8_t)0x04;
    }

    /* Configure the LCD COM3 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_4)
    {
        if(mlcd->Init.COM3PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM3PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM3PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM3PIN)) = (uint8_t)0x08;
    }

    /* Configure the LCD COM4 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_5)
    {
        if(mlcd->Init.COM4PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM4PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM4PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM4PIN)) = (uint8_t)0x10;
    }

    /* Configure the LCD COM5 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_6)
    {
        if(mlcd->Init.COM5PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM5PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM5PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM5PIN)) = (uint8_t)0x20;
    }

    /* Configure the LCD COM6 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty > LCD_DUTY_1_7)
    {
        if(mlcd->Init.COM6PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM6PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM6PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM6PIN)) = (uint8_t)0x40;
    }

    /* Configure the LCD COM7 of MUX0/1 and MDn Registselect register */
    if(mlcd->Init.Duty == LCD_DUTY_1_8)
    {
        if(mlcd->Init.COM7PIN < LCD_P32)
            mlcd->Instance->MUX0.W |= (0x00000001UL << mlcd->Init.COM7PIN);
        else
            mlcd->Instance->MUX1.W |= (0x00000001UL << (mlcd->Init.COM7PIN - LCD_P32));

        *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM7PIN)) = (uint8_t)0x80;
    }

    /* Configure the LCD Clock Divider, Prescaler and Blink divider:
        Set PSC[4:0] bits according to mlcd->Init.Prescaler value
        Set DIV[2:0] bits according to mlcd->Init.Divider value
        Set BDIV[1:0] bits according to mlcd->Init.Blink divider value */
    WRITE_REG(mlcd->Instance->CLK.W, \
        (mlcd->Init.BlinkDivider | mlcd->Init.Prescaler | LCD_CLK_CK_PDIV_div16_w | mlcd->Init.Divider | LCD_CLK_CK_CDIV_div1_w | LCD_CLK_CK_SEL_ck_ls_w));

    /* Configure the LCD Control Register 0, DeatTime, DUTY, BIAS and Voltage Source:
        Set DEAD[3:0] bits according to mlcd->Init.DeadTime value
        Set DUTY[2:0] bits according to mlcd->Init.Duty value
        Set BIAS[1:0] bits according to mlcd->Init.Bias value
        Set VS_SEL[1:0] bits according to mlcd->Init.Voltage Source value */
    WRITE_REG(mlcd->Instance->CR0.W, \
        (mlcd->Init.DeadTime | mlcd->Init.Duty | LCD_CR0_RL_SEL_int_w | mlcd->Init.Bias | LCD_CR0_CS_INV_normal_w | LCD_CR0_DT_MDS_frame_w | LCD_CR0_FRM_MDS_typeb_w | mlcd->Init.VoltageSource));

    /* Configure the LCD Control Register 1, Drive Contrast, DUTY, BIAS and Voltage Source:
        Set PW[2:0] bits according to mlcd->Init.PulseOnDuration value
        Set VT_SEL[3:0] bits according to mlcd->Init.Contrast value
        Set OVD_DIS[1:0] bits according to mlcd->Init.OverVoltageDetect value */
    lTemp32 = mlcd->Init.PulseOnDuration | mlcd->Init.Contrast | LCD_CR1_VT_CTL_pin1_w | LCD_CR1_CP_SYNC_disable_w | LCD_CR1_OFF_CTL_hiz_w | LCD_CR1_DRV_MDS_high_w | mlcd->Init.OverVoltageDetect | LCD_CR1_CP_EN_disable_w;

    if(mlcd->Init.Bias == LCD_BIAS_1_4)
        lTemp32 |= (LCD_CR1_V1_CTL_pin1_w | LCD_CR1_V2_CTL_pin1_w | LCD_CR1_V3_CTL_pin1_w);
    else if(mlcd->Init.Bias == LCD_BIAS_1_3)
        lTemp32 |= (LCD_CR1_V1_CTL_pin1_w | LCD_CR1_V2_CTL_pin1_w);
    else if(mlcd->Init.Bias == LCD_BIAS_1_2)
        lTemp32 |= LCD_CR1_V1_CTL_pin1_w;

    WRITE_REG(mlcd->Instance->CR1.W, lTemp32);

    /* Configure the LCD Control Register 2, BlinkCounter: 
        Set BCNT[4:0] bits according to mlcd->Init.BlinkCount value */
    WRITE_REG(mlcd->Instance->CR2.W, ((mlcd->Init.BlinkCount << LCD_CR2_BCNT_shift_w) | LCD_CR2_SEG_OFF_on_w | LCD_CR2_BLK_MDS_disable_w | LCD_CR2_MEM_CLR_no_w));

    /* Disable the peripheral */
    __DRV_LCD_ENABLE(mlcd); __ISB();

    __DRV_LCD_HIGHDRIVER_ENABLE(mlcd);

    lTickstart = MID_GetTick();          /* Get timeout */

    /* Charge Pump Eanble */
    if(mlcd->Init.VoltageSource == LCD_VOLTAGESOURCE_CHARGEPUMP)
    {
        __DRV_LCD_CLEAR_FLAG(mlcd, LCD_FLAG_CPR);

        __DRV_LCD_CHARGEPUMP_ENABLE(mlcd);
    }

    /*!< Wait Until the LCD Charge Pump is ready */
    while(__DRV_LCD_GET_FLAG(mlcd, LCD_FLAG_CPR) == NONE)
    {
        if((MID_GetTick() - lTickstart ) > LCD_TIMEOUT_VALUE)
        {
            __DRV_LCD_HIGHDRIVER_DISABLE(mlcd);
            mlcd->ErrorCode = MID_LCD_ERROR_RDY;  
            return MID_TIMEOUT;
        } 
    }

    if(mlcd->Init.HighDrive == LCD_HIGHDRIVE_0)
        __DRV_LCD_HIGHDRIVER_DISABLE(mlcd);

    /* Initialize the LCD state */
    mlcd->ErrorCode = MID_LCD_ERROR_NONE;
    mlcd->State = MID_LCD_STATE_READY;

    /* Process unLocked */
    __MID_UNLOCK(mlcd);

    return MID_OK;
}

/**
  *******************************************************************************
  * @brief  LCD MSP DeInit.
  * @param  mlcd LCD handle
  * @retval None
  *******************************************************************************
  */
void MID_LCD_MspInit(LCD_HandleTypeDef *mlcd)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(mlcd);

    /* NOTE: This function Should not be modified, when the callback is needed,
             the HAL_LCD_MspDeInit could be implemented in the user file
    */
}

/**
  *******************************************************************************
  * @brief  LCD MSP Init.
  * @param  mlcd LCD handle
  * @retval None
  *******************************************************************************
  */
void MID_LCD_MspDeInit(LCD_HandleTypeDef *mlcd)
{
    /* Prevent unused argument(s) compilation warning */
    UNUSED(mlcd);

    /* NOTE: This function Should not be modified, when the callback is needed,
             the HAL_LCD_MspInit could be implemented in the user file
   */
}

/**
  *******************************************************************************
  * @brief  Writes a word in the specific LCD RAM.
  * @param  mlcd LCD handle
  * @param  DataIndex specifies LCD Data Value to be written.
  * @retval None
  *******************************************************************************
  */
MID_StatusTypeDef MID_LCD_DisplayDataUpdate(LCD_HandleTypeDef *mlcd, uint32_t *DataIndex)
{
    if((mlcd->State == MID_LCD_STATE_READY) || (mlcd->State == MID_LCD_STATE_BUSY))
    {
        if(mlcd->State == MID_LCD_STATE_READY)
        {
            /* Process Locked */
            __MID_LOCK(mlcd);

            /* Change the LCD state */
            mlcd->State = MID_LCD_STATE_BUSY;

            /* Copy the new Data bytes to LCD RAM register */
            mlcd->Instance->MD0.W = DataIndex[0];
            mlcd->Instance->MD1.W = DataIndex[1];
            mlcd->Instance->MD2.W = DataIndex[2];
            mlcd->Instance->MD3.W = DataIndex[3];
            mlcd->Instance->MD4.W = DataIndex[4];
            mlcd->Instance->MD5.W = DataIndex[5];
            mlcd->Instance->MD6.W = DataIndex[6];
            mlcd->Instance->MD7.W = DataIndex[7];
            mlcd->Instance->MD9.W = DataIndex[8];
            mlcd->Instance->MD10.W = DataIndex[9];

            *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM0PIN)) = (uint8_t)0x01;

            if(mlcd->Init.Duty > LCD_DUTY_1_2)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM1PIN)) = (uint8_t)0x02;

            if(mlcd->Init.Duty > LCD_DUTY_1_3)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM2PIN)) = (uint8_t)0x04;

            if(mlcd->Init.Duty > LCD_DUTY_1_4)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM3PIN)) = (uint8_t)0x08;

            if(mlcd->Init.Duty > LCD_DUTY_1_5)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM4PIN)) = (uint8_t)0x10;

            if(mlcd->Init.Duty > LCD_DUTY_1_6)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM5PIN)) = (uint8_t)0x20;

            if(mlcd->Init.Duty > LCD_DUTY_1_7)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM6PIN)) = (uint8_t)0x40;

            if(mlcd->Init.Duty == LCD_DUTY_1_8)
                *((volatile uint8_t*)(((uint32_t)(mlcd->Instance + 0x30)) + mlcd->Init.COM7PIN)) = (uint8_t)0x80;

            /* Change the LCD state */
            mlcd->ErrorCode = MID_LCD_ERROR_NONE;
            mlcd->State= MID_LCD_STATE_READY;

            /* Process unLocked */
            __MID_UNLOCK(mlcd);

            return MID_OK;
        }
        else
            return MID_BUSY;
    }
    else
        return MID_ERROR;
}


/**
  *******************************************************************************
  * @brief Clears the LCD RAM registers.
  * @param mlcd: LCD handle
  * @retval None
  *******************************************************************************
  */
MID_StatusTypeDef MID_LCD_Clear(LCD_HandleTypeDef *mlcd)
{
    mlcd->Instance->CR2.W = LCD_CR2_MEM_CLR_mask_w;
    return MID_OK;
}


/**
  *******************************************************************************
  * @brief Clears the LCD RAM registers.
  * @param mlcd: LCD handle
  * @param State:
  * @retval None
  *******************************************************************************
  */
MID_StatusTypeDef MID_LCD_Blink(LCD_HandleTypeDef *mlcd, FunctionalState State)
{
    if((mlcd->State == MID_LCD_STATE_READY) || (mlcd->State == MID_LCD_STATE_BUSY))
    {
        if(mlcd->State == MID_LCD_STATE_READY)
        {
            /* Process Locked */
            __MID_LOCK(mlcd);

            /* Change the LCD state */
            mlcd->State = MID_LCD_STATE_BUSY;

            if(State == DISABLE)
                mlcd->Instance->CR2.W = mlcd->Instance->CR2.W | LCD_CR2_BLK_MDS_disable_w;
            else
            {
                mlcd->Instance->CLK.W = ((mlcd->Instance->CLK.W & (~LCD_CLK_CK_BDIV_mask_w)) | mlcd->Init.BlinkDivider);
                mlcd->Instance->CR2.W = ((mlcd->Instance->CR2.W & (~LCD_CR2_BCNT_mask_w)) | (mlcd->Init.BlinkCount << LCD_CR2_BCNT_shift_w));
                mlcd->Instance->CR2.W = mlcd->Instance->CR2.W | LCD_CR2_BLK_MDS_all_w;
            }

            /* Change the LCD state */
            mlcd->ErrorCode = MID_LCD_ERROR_NONE;
            mlcd->State= MID_LCD_STATE_READY;

            /* Process Unlocked */
            __MID_UNLOCK(mlcd);

            return MID_OK;
        }
        else
            return MID_BUSY;
    }
    else
        return MID_ERROR;
}


/**
  *******************************************************************************
  * @brief Returns the LCD state.
  * @param mlcd: LCD handle
  * @retval Middleware state
  *******************************************************************************
  */
MID_LCD_StateTypeDef MID_LCD_GetState(LCD_HandleTypeDef *mlcd)
{
    return mlcd->State;
}

/**
  *******************************************************************************
  * @brief Return the LCD error code
  * @param mlcd: LCD handle
  * @retval LCD Error Code
  *******************************************************************************
  */
uint32_t MID_LCD_GetError(LCD_HandleTypeDef *mlcd)
{
    return mlcd->ErrorCode;
}

#endif

