/**
*     Copyright (c) 2023, Nations Technologies Inc.
* 
*     All rights reserved.
*
*     This software is the exclusive property of Nations Technologies Inc. (Hereinafter 
* referred to as NATIONS). This software, and the product of NATIONS described herein 
* (Hereinafter referred to as the Product) are owned by NATIONS under the laws and treaties
* of the People's Republic of China and other applicable jurisdictions worldwide.
*
*     NATIONS does not grant any license under its patents, copyrights, trademarks, or other 
* intellectual property rights. Names and brands of third party may be mentioned or referred 
* thereto (if any) for identification purposes only.
*
*     NATIONS reserves the right to make changes, corrections, enhancements, modifications, and 
* improvements to this software at any time without notice. Please contact NATIONS and obtain 
* the latest version of this software before placing orders.

*     Although NATIONS has attempted to provide accurate and reliable information, NATIONS assumes 
* no responsibility for the accuracy and reliability of this software.
* 
*     It is the responsibility of the user of this software to properly design, program, and test 
* the functionality and safety of any application made of this information and any resulting product. 
* In no event shall NATIONS be liable for any direct, indirect, incidental, special,exemplary, or 
* consequential damages arising in any way out of the use of this software or the Product.
*
*     NATIONS Products are neither intended nor warranted for usage in systems or equipment, any
* malfunction or failure of which may cause loss of human life, bodily injury or severe property 
* damage. Such applications are deemed, "Insecure Usage".
*
*     All Insecure Usage shall be made at user's risk. User shall indemnify NATIONS and hold NATIONS 
* harmless from and against all claims, costs, damages, and other liabilities, arising from or related 
* to any customer's Insecure Usage.

*     Any express or implied warranty with regard to this software or the Product, including,but not 
* limited to, the warranties of merchantability, fitness for a particular purpose and non-infringement
* are disclaimed to the fullest extent permitted by law.

*     Unless otherwise explicitly permitted by NATIONS, anyone may not duplicate, modify, transcribe
* or otherwise distribute this software for any purposes, in whole or in part.
*
*     NATIONS products and technologies shall not be used for or incorporated into any products or systems
* whose manufacture, use, or sale is prohibited under any applicable domestic or foreign laws or regulations. 
* User shall comply with any applicable export control laws and regulations promulgated and administered by 
* the governments of any countries asserting jurisdiction over the parties or transactions.
**/

/**
*\*\file n32g05x_led.c
*\*\author Nations 
*\*\version v1.0.0
*\*\copyright Copyright (c) 2023, Nations Technologies Inc. All rights reserved.
**/

#include "n32g05x_led.h"
#include "n32g05x.h"

const uint32_t LED_TrimAddrFromNVR[]  = { NVR_ADDR_90H_TRIM,NVR_ADDR_91H_TRIM,NVR_ADDR_92H_TRIM,NVR_ADDR_93H_TRIM,\
                                          NVR_ADDR_94H_TRIM,NVR_ADDR_95H_TRIM,NVR_ADDR_96H_TRIM,NVR_ADDR_97H_TRIM };

const uint16_t COM_Number[] = {0x0000,0x0041,0x00C2,0x01C3,0x03C4,0x07C5,0x0FC6,0x1FC7,0x3FC8,0x7FC9,0xFFCA};

const GPIO_Module* SEG_PORT[] = {SEG0_2PORT, SEG0_2PORT, SEG0_2PORT, SEG3_PORT, SEG4_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, \
                                 SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT, SEG5_15_PORT };

const uint32_t SEG_PIN[] = {SEG0_PIN, SEG1_PIN, SEG2_PIN, SEG3_PIN, SEG4_PIN, SEG5_PIN, SEG6_PIN, SEG7_PIN, \
                            SEG8_PIN, SEG9_PIN, SEG10_PIN, SEG11_PIN, SEG12_PIN, SEG13_PIN, SEG14_PIN, SEG15_PIN };
                        
LED_StatusType LED_Status = {0};
SPI_InitType SPI_InitStructure;

/**
*\*\name    SPI_SendDataToLED.
*\*\fun     Sends a half-word data through the SPI interface and return the data received from the SPI bus
*\*\param   data  
*\*\         - data to send
*\*\return  The value of the received data
**/
uint16_t SPI_SendDataToLED(uint16_t data)
{
    /* Loop while DAT register in not emplty */
   __IO uint32_t timeout  = 0xFFF;
    
    while((SPI_GetStatus(SPI_LED,SPI_TE_FLAG) == RESET)&& (timeout !=0))
    {
       timeout--;
    } ;
    SPI_TransmitData(SPI_LED,data);
    
    /* Send byte through the SPI1 peripheral */
    timeout = 0xFFF;
    while((SPI_GetStatus(SPI_LED,SPI_RNE_FLAG) == RESET)&& (timeout !=0))
    {
       timeout--;
    } ;

    /* Return the byte read from the SPI bus */
    return SPI_ReceiveData(SPI_LED);
}
void LED_TrimLoad(LED_StatusType *led_Status)
{
    uint32_t tmpreg  = 0;
    uint16_t tmpreg1 = 0;
    uint16_t index   = 0;
    for(index = 0;index<6;index++)
    {
        tmpreg1 = 0;
        tmpreg = *(uint32_t*)LED_TrimAddrFromNVR[index];
        led_Status->Trim_SEG[index] = (uint8_t)tmpreg;
        tmpreg1 = (uint8_t)tmpreg;
        tmpreg1 |= (0x9000 + (0x100*index) );
        SPI_SendDataToLED(tmpreg1);
    }
    tmpreg1 = 0;
    tmpreg = *(uint32_t*)LED_TrimAddrFromNVR[6];
    led_Status->Trim_Rset = (uint8_t)tmpreg;
    tmpreg1 = (uint8_t)(tmpreg&0x1F);
    tmpreg1 |= (LED_CMD_RSET_TRIM_REG_WRITE);
    SPI_SendDataToLED(tmpreg1);
    
    tmpreg1 = 0;
    tmpreg = *(uint32_t*)LED_TrimAddrFromNVR[7];
    led_Status->Trim_BG = (uint8_t)tmpreg;
    tmpreg1 = (uint8_t)(tmpreg&0x1F);
    tmpreg1 |= (LED_CMD_BG_TRIM_REG_WRITE);
    SPI_SendDataToLED(tmpreg1);
}
/**
*\*\name    LED_Init.
*\*\fun     Initializes the LED peripheral according to the specified parameters in the
*\*\        LED_InitStruct.
*\*\param   LED_InitStruct .
*\*\          - COM_WorkNum
*\*\          - SEG_WorkCurrent
*\*\          - ScanMode
*\*\          - SEG_WorkStatus
*\*\          - GCLK_LowLevelNum
*\*\return  none
**/
void LED_Init(LED_InitType* LED_InitStruct)
{
    uint16_t tmpreg1 = 0;
    
	/* this value must greater than 1 */
    RCC_SetLEDLowLevelCycles(LED_InitStruct->GCLK_LowLevelNum);
    /* config LED_CMD_COM_FORCE_SHUTDOWM register */
    SPI_SendDataToLED(LED_CMD_COM_FORCE_SHUTDOWM);
    /* config LED_CMD_CHIP_WAKEUP register */
    SPI_SendDataToLED(LED_CMD_CHIP_WAKEUP);
    /* config LED_CMD_COM_NUM_COM0_1_WORK_STATUS register */
    tmpreg1 |= LED_CMD_COM_NUM_COM0_1_WORK_STATUS;
    tmpreg1 |= (COM_Number[LED_InitStruct->COM_WorkNum]&0x00FFU);
    SPI_SendDataToLED(tmpreg1);
    tmpreg1 = 0;
    /* config LED_CMD_COM2_9_WORK_STATUS register */
    tmpreg1 |= LED_CMD_COM2_9_WORK_STATUS;
    tmpreg1 |= COM_Number[LED_InitStruct->COM_WorkNum]>>8;
    SPI_SendDataToLED(tmpreg1);
    tmpreg1 = 0;
    /* config LED_CMD_SEG_CUR_DISP_MODE_CHIP_EN register */
    /* set work current */
    tmpreg1 |= LED_CMD_SEG_CUR_DISP_MODE_CHIP_EN;
    tmpreg1 |= (uint16_t)LED_InitStruct->SEG_WorkCurrent | 0x0020U;
    /* set dispaly mode */
    tmpreg1 |= LED_CMD_SEG_CUR_DISP_MODE_CHIP_EN;
    tmpreg1 |= ((uint16_t)LED_InitStruct->ScanMode<<6);
    tmpreg1 |= (0x0001U)<<7;
    SPI_SendDataToLED(tmpreg1);
    tmpreg1 = 0;
    /* config LED_CMD_SEG0_7_WORK_STATUS register */
    /* set seg0 ~ seg7 work status  */
    tmpreg1 |= LED_CMD_SEG0_7_WORK_STATUS;
    tmpreg1 |= (LED_InitStruct->SEG_WorkStatus&0x00ffU);
    SPI_SendDataToLED(tmpreg1);
    tmpreg1 = 0;
    /* set seg8 ~ seg15 work status  */
    tmpreg1 |= LED_CMD_SEG8_15_WORK_STATUS;
    tmpreg1 |= (LED_InitStruct->SEG_WorkStatus>>8);
    SPI_SendDataToLED(tmpreg1);
    tmpreg1 = 0;
    /* Load the trim value from NVR address for LED*/
    LED_TrimLoad(&LED_Status);       
}

/**
*\*\name    LED_SEG_DutyDataSet.
*\*\fun     Set the LED seg duty data.
*\*\param   SegId .
*\*\          - this value must be range of 0~15
*\*\param   duty .
*\*\          - this value must be range of 0~63
*\*\return  none
**/
void LED_SEG_DutyDataSet(uint8_t SegId, uint8_t duty)
{
    uint16_t tmpreg1;

    if(SegId < MAX_LED_SEG_ID)
    {
        tmpreg1  = (LED_CMD_SEG0_DUTY_DATA+ (SegId *0x100)) ;
        if(duty < MAX_LED_SEG_DUTY)
        {
            tmpreg1 |= ((uint16_t)duty & MAX_LED_SEG_DUTY);
        }
        else
        {
            tmpreg1 |= MAX_LED_SEG_DUTY;
        }
        SPI_SendDataToLED(tmpreg1);
    }
    else
    {
        /* no process */
    }
}


/**
*\*\name    LED_Get_RegData.
*\*\fun     Get the status of LED,including triming data, work current, ect.
*\*\param   none .
*\*\return  none
**/
void LED_Get_RegData(void)
{
    uint16_t tmpreg1, tmpreg2;
    uint16_t i;

    /* Read back the RSET command */
    tmpreg1 = LED_CMD_READ_REG;
    tmpreg1 |= 0x16U;
    SPI_SendDataToLED(tmpreg1) ;
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
    LED_Status.Trim_Rset = (uint8_t)(tmpreg2&0x1FU) ;
    tmpreg1 = 0;
    
    /* Read back the BG command */
    tmpreg1 |= LED_CMD_READ_REG;
    tmpreg1 |= 0x17U;
    SPI_SendDataToLED(tmpreg1);
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
    LED_Status.Trim_BG = (uint8_t)(tmpreg2&0x1FU);
    tmpreg1 = 0;  
    
    /* Read back the Trim duty commands */
    for(i = 0;i<6;i++)
    {
        tmpreg1 |= LED_CMD_READ_REG;
        tmpreg1 |= 0x10 + i;
        SPI_SendDataToLED(tmpreg1);
        tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
        LED_Status.Trim_SEG[i] = (uint8_t)(tmpreg2 &0xFFU);
        tmpreg1 = 0;
    }
    
    /* Read back the work current / display mode/ chip enable commands */
    tmpreg1 |= LED_CMD_READ_REG;
    tmpreg1 |= 0x20U;
    SPI_SendDataToLED(tmpreg1) ;
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
    LED_Status.SEG_WorkCurrent = 50U + (5* (tmpreg2 &0x1FU)) ;
    LED_Status.ScanMode =  ((tmpreg2 & 0x0040U) != 0)? 0x01U : 0x00U ;
    LED_Status.EnableStaus = ((tmpreg2 & 0x80U) != 0)? 0x01U : 0x00U ;
    tmpreg1 = 0; 
    
    /* Read back the the number of COM commands */
    tmpreg1 |= LED_CMD_READ_REG;
    tmpreg1 |= 0x21U;
    SPI_SendDataToLED(tmpreg1);
    SPI_LED->DAT;
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);;
    LED_Status.ComNum = (uint8_t)(tmpreg2 &0x0FU) ;
    tmpreg1 = 0;
    
    /* Read back the the working status of SEG0 -SEG7 commands */
    tmpreg1 |= LED_CMD_READ_REG;
    tmpreg1 |= 0x23U;
    SPI_SendDataToLED(tmpreg1);
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);;
    LED_Status.SEG_Status = (uint8_t)(tmpreg2&0xFFU) ;
    tmpreg1 = 0;
    
    /* Read back the the working status of SEG8 -SEG15 commands */
    tmpreg1 |= LED_CMD_READ_REG;
    tmpreg1 |= 0x24U;
    SPI_SendDataToLED(tmpreg1);
    tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
    LED_Status.SEG_Status |= ((tmpreg2&0xFFU)<<8) ;
    tmpreg1 = 0;
    
    /* Read back the the duty status of SEG0 -SEG15 */
    for(i = 0;i<16;i++)
    {
        tmpreg1 |= LED_CMD_READ_REG;
        tmpreg1 |= 0x30U + i;
        SPI_SendDataToLED(tmpreg1);
        tmpreg2= SPI_SendDataToLED(SPI_DUMMY_DATA);
        LED_Status.SEG_Duty[i] = (uint8_t)(tmpreg2&0xFFU);
        tmpreg1 = 0;
    }

    /* Close the read back command */
    SPI_SendDataToLED(LED_CMD_READ_OFF);
}
/**
*\*\name    LED_InitStruct.
*\*\fun     Fills each LED_InitType member with its default value.
*\*\param   LED_InitType (The input parameters must be the following values):
*\*\          - COM_WorkNum
*\*\          - GCLK_LowLevelNum
*\*\          - SEG_WorkCurrent
*\*\          - SEG_WorkStatus
*\*\          - ScanMode    
*\*\return  none
**/
void LED_InitStruct(LED_InitType* LED_InitStruct)
{
    /* Initialize the COM_WorkNum member */
    LED_InitStruct->COM_WorkNum             = 0;
    /* initialize the GCLK_LowLevelNum member */
    LED_InitStruct->GCLK_LowLevelNum        = 20;
    /* initialize the SEG_WorkCurrent member */
    LED_InitStruct->SEG_WorkCurrent         = 5;
    /* Initialize the SEG_WorkStatus member */
    LED_InitStruct->SEG_WorkStatus          = 0;
    /* Initialize the ScanMode member */
    LED_InitStruct->ScanMode                = 0;
}

/**
*\*\name    LED_GclkConfiguration.
*\*\fun     Configures GCLK clocks frequency and init configuration.
*\*\param   LED_clksrc(LED clock source):
*\*\         - LED_GCLK_SRC_HSI    HSI clock selected 
*\*\         - LED_GCLK_SRC_HSE    HSE clock selected 
*\*\param   LED_prescaler(LED clock prescaler):
*\*\         - LED_GCLK_DIV1 
*\*\         - LED_GCLK_DIV32
*\*\         - LED_GCLK_DIV40
*\*\         - LED_GCLK_DIV50
*\*\         - LED_GCLK_DIV64
*\*\         - LED_GCLK_DIV80
*\*\         - LED_GCLK_DIV160
*\*\return  none
*\*\note  LED GCLK must be in the range of 50kHz ~ 250kHz.
**/
void LED_GclkConfiguration(uint32_t LED_clksrc, uint32_t LED_prescaler)
{
    /* Configures the LED clock frequency */
    RCC_ConfigLEDClk(LED_clksrc,LED_prescaler);
    /* this value must be fix at 130 */
    RCC_SetLEDClockCycles(LED_CLOCK_CYCLE_NUMBER);
}

/**
*\*\name    LED_SPI_Init.
*\*\fun     Configures the different system clocks.
*\*\return  none
*\*\note  LED SPI Baud-rate cannot greater than 3M.
**/
void LED_SpiInit(void)
{
    SPI_InitStruct(&SPI_InitStructure);
    SPI_InitStructure.DataDirection = SPI_DIR_DOUBLELINE_FULLDUPLEX;   
    SPI_InitStructure.SpiMode       = SPI_MODE_MASTER;
    SPI_InitStructure.DataLen       = SPI_DATA_SIZE_16BITS;
    SPI_InitStructure.CLKPOL        = SPI_CLKPOL_LOW;
    SPI_InitStructure.CLKPHA        = SPI_CLKPHA_FIRST_EDGE;
    SPI_InitStructure.NSS           = SPI_NSS_HARD; 
    SPI_InitStructure.BaudRatePres  = SPI_BR_PRESCALER_32;
    SPI_InitStructure.FirstBit      = SPI_FB_MSB;
    SPI_InitStructure.CRCPoly       = 7;
    SPI_Init(SPI_LED, &SPI_InitStructure);
    
    SPI_SSOutputEnable(SPI_LED, ENABLE);

    SPI_Enable(SPI_LED, ENABLE);
}

/**
*\*\name    RCC_Configuration.
*\*\fun     Configures the different system clocks.
*\*\return  none
**/
void LED_RccConfiguration(void)
{
    /* PCLK2 = HCLK/2 */
    RCC_ConfigPclk2(RCC_HCLK_DIV1);

    /* Enable GCLK peripheral clocks -------------------------------*/

    /* Enable SPIx and GCLK GPIO clock */
    RCC_EnableAPB2PeriphClk(SPIx_LED_CLK | RCC_APB2_PERIPH_AFIO | RCC_APB2_PERIPH_GPIOA | \
                            RCC_APB2_PERIPH_GPIOB | RCC_APB2_PERIPH_GPIOC | RCC_APB2_PERIPH_GPIOD, ENABLE);

    /* Enable SPIx Periph clock */
    RCC_EnableAPB2PeriphClk(SPIx_CLK, ENABLE);
}

/**
*\*\name    LED_GpioConfiguration.
*\*\fun     Configures the different SPIx GPIO, GCLK ports, LED COM and SEG ports according to the specified params COM_WorkNum and SEG_WorkStatus.
*\*\param   COM_WorkNum. the total number of working COM.  
*\*\          -  this value must be range of 1 ~ 10.
*\*\param   SEG_WorkStatus. selected the working SEG.
*\*\          -  this value must be range of 0 ~ 0xFFFF.Bit0 - SEG0,Bit1 - SEG1...Bit15 - SEG15
*\*\             eg :when SEG_WorkStatus = 0x1841 it means SEG0,SEG6,SEG11,SEG15 will eable to work
*\*\return  none
**/
void LED_GpioConfiguration(uint8_t COM_WorkNum, uint16_t SEG_WorkStatus)
{
    GPIO_InitType GPIO_InitStructure;
    uint8_t  index ;

    GPIO_InitStruct(&GPIO_InitStructure);
    /* Configure Gclk pins: GCLK --------------------------------*/
    GPIO_InitStructure.Pin        = SPIx_LED_GCLK_PIN ;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF5_LED;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPIx_LED_GCLK_PORT, &GPIO_InitStructure);

    /* Configure SPIx pins: NSS ---------------------------------*/
    GPIO_InitStructure.Pin        = SPIx_LED_NSS_PIN ;
    GPIO_InitStructure.GPIO_Alternate = SPIx_LED_NSS_AFIO;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPIx_LED_NSS_PORT, &GPIO_InitStructure);

    /* Configure SPIx pins: SCK ---------------------------------*/
    GPIO_InitStructure.Pin        = SPIx_LED_SCK_PIN ;
    GPIO_InitStructure.GPIO_Alternate = SPIx_LED_SCK_AFIO;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPIx_LED_SCK_PORT, &GPIO_InitStructure);

    /* Configure SPIx pins: MOSI --------------------------------*/
    GPIO_InitStructure.Pin        = SPIx_LED_MOSI_PIN ;
    GPIO_InitStructure.GPIO_Alternate = SPIx_LED_MOSI_AFIO;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitPeripheral(SPIx_LED_MOSI_PORT, &GPIO_InitStructure);

    /* Configure SPIx pins: MISO -------------------------------*/
    GPIO_InitStructure.Pin        = SPIx_LED_MISO_PIN ;
    GPIO_InitStructure.GPIO_Alternate = SPIx_LED_MISO_AFIO;
    GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
    GPIO_InitStructure.GPIO_Pull = GPIO_NO_PULL;
    GPIO_InitPeripheral(SPIx_LED_MISO_PORT, &GPIO_InitStructure);

    /* Configure COM pins---------------------------------------*/
    GPIO_InitStruct(&GPIO_InitStructure);
    if(COM_WorkNum >= 1)
    {
        GPIO_InitStructure.Pin        = COM0_PIN ;
        GPIO_InitStructure.GPIO_Mode = GPIO_MODE_ANALOG;
        GPIO_InitPeripheral(COM0_2_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 2)
    {
        GPIO_InitStructure.Pin        = COM1_PIN ;
        GPIO_InitPeripheral(COM0_2_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 3)
    {
        GPIO_InitStructure.Pin        = COM2_PIN ;
        GPIO_InitPeripheral(COM0_2_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 4)
    {
        GPIO_InitStructure.Pin        = COM3_PIN ;
        GPIO_InitPeripheral(COM3_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 5)
    {
        GPIO_InitStructure.Pin        = COM4_PIN ;
        GPIO_InitPeripheral(COM4_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 6)
    {
        GPIO_InitStructure.Pin        = COM5_PIN ;
        GPIO_InitPeripheral(COM5_9_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 7)
    {
        GPIO_InitStructure.Pin        = COM6_PIN ;
        GPIO_InitPeripheral(COM5_9_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 8)
    {
        GPIO_InitStructure.Pin        = COM7_PIN ;
        GPIO_InitPeripheral(COM5_9_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 9)
    {
        GPIO_InitStructure.Pin        = COM8_PIN ;
        GPIO_InitPeripheral(COM5_9_PORT, &GPIO_InitStructure);
    }
    if(COM_WorkNum >= 10)
    {
        GPIO_InitStructure.Pin        = COM9_PIN ;
        GPIO_InitPeripheral(COM5_9_PORT, &GPIO_InitStructure);
    }
    /* Configure SEG pins---------------------------------------*/
    for(index = 0;index < 16;index ++)
    {
        if((SEG_WorkStatus & ((uint16_t)1<<index)) !=0 )
        {
            GPIO_InitStructure.Pin = SEG_PIN[index];
            GPIO_InitPeripheral((GPIO_Module*)SEG_PORT[index], &GPIO_InitStructure);
        }
    }
}
