/******************************************************************************
 * @file     main.c
 * @brief    Main program body
 * @version  V1.8.0
 * @date     31-Dec-2021
 *
 * @note
 * Copyright (C) 2021 Spintrol Limited Corporation. All rights reserved.
 *
 * @attention
 * THIS SOFTWARE JUST PROVIDES CUSTOMERS WITH CODING INFORMATION REGARDING
 * THEIR PRODUCTS, WHICH AIMS AT SAVING TIME FOR THEM. SPINTROL SHALL NOT BE
 * LIABLE FOR THE USE OF THE SOFTWARE. SPINTROL DOES NOT GUARANTEE THE
 * CORRECTNESS OF THIS SOFTWARE AND RESERVES THE RIGHT TO MODIFY THE SOFTWARE
 * WITHOUT NOTIFICATION.
 *
 ******************************************************************************/

#include "spc1168.h"
#include "spd1178.h"
#include <stdio.h>

/* This macro is used to get the PWM period with a specified frequency */
#define                     PWMPeriod(u32PWMFreqHz)           ((CLOCK_GetModuleClock(PWM_MODULE))/u32PWMFreqHz)/2;

#define                     PRE_DRIVER0_PWM_U                             PWM2
#define                     PRE_DRIVER0_PWM_V                             PWM1
#define                     PRE_DRIVER0_PWM_W                             PWM0
#define                     PRE_DRIVER1_PWM_U                             PWM5
#define                     PRE_DRIVER1_PWM_V                             PWM4
#define                     PRE_DRIVER1_PWM_W                             PWM3
#define                     PRE_DRIVER0_LINK_PWM_U                        SEL_PWM2
#define                     PRE_DRIVER1_LINK_PWM_U                        SEL_PWM5
#define                     PRE_DRIVER0_PWM_FREQ                          1000                  /* 1kHz PWM */
#define                     PRE_DRIVER1_PWM_FREQ                          1000                  /* 1kHz PWM */
#define                     PWM_DB_NS                                     1000                  /* 1us */
#define                     VDS_REFERENCE_VOLTAGE                         0xf                   /* 2.4V */
#define                     PWM_BlankWIN_Size                             100                   /* 100 TBCNT CLK */
#define                     PWM_Blank_Offset                              50                    /* 50 TBCNT CLK */
#define                     EPWR_TH_VAL_NS                                10                    /* 10 ns */
#define                     EPWR_WIN_VAL_NS                               10                    /* 10 ns */

uint32_t                    u3232PWMPeriod;
uint8_t                     u8PREDRIID                                    = 0;                 /* PRE-DRIVER mode ID */
uint8_t                     u8PREDRIDATA;                                                       /* Data read from PRE-DRIVER */
ErrorStatus                 eErrorState;
uint32_t                    u32SSPBaundrate                               = 1000000;           /* The baundrate of SSP communicating with PRE-DRIVER mode */
uint32_t                    u32Timeout                                    = 500;               /* The number of times try to get PRE-DRIVER mode ID */

void PWMx_Set_Digiter_Compare(PWM_REGS * PWMx)
{
  /* 
   * The following step can is the coding style of Digital Compare sub-module structure in PWM module,
   * for more information, please refer to Figure 10-16 of SPC1158 TRM.
   */

  /* Step1. Set EPWRTZ0 as the DCAH input */
  PWM_EnableDCAHTripEvent(PWMx, DC_TRIP_EPWRTZ0 | DC_TRIP_EPWRTZ1);

  /* Step2. Trip DCAEVT0 event when DCAH is high(be carefull about the DCH and DCL polarity) */
  PWM_SetRawDCAEVT0(PWMx, DCH_HIGH_DCL_X);

  /*
   * Step3. Config the Event filter function.
   *
   * <1>.Configure DC event filter
   *
   * <2>.Input is DCAEVT0, apply blank window with original polarity
   *
   * <3>.Set the blank window position relative to TBCNT=0
   */
  PWM_SetDCFilter(PWMx, DCF_FROM_RAW_DCAEVT0, DCF_ALIGN_ON_ZERO);

  /* Set blank window size as 100 TBCLK and offset as 50 TBCLK */
  PWM_SetDCFilterBlankWindow(PWMx, PWM_BlankWIN_Size, PWM_Blank_Offset);

  /* Enbale PWM DC filter blank function */
  PWM_EnableDCFilterBlank(PWMx);

  /* 
   * Step4. Set the event sent to Event processing logic.
   *
   * Set the Filtered DCAEVT0 event or the Raw DCAEVT0 as the event which
   * will be sent to Event processing logic. 
   */
  PWM_SetDCAEVT0(PWMx, DCEVT_FILTERED);

  /* Step5. Enable the Event processing logic output EVT0 signal(DCAEVT0 int) to Trip-zone sub-module */
  PWM_EnableDCAEVT0TripInt(PWMx);

  /* Step blow is the configuration of Trip-zone sub-module, nothing to do with Digital compare sub-module */
  /* Set Trip-Zone as one-shot mode */
  PWM_SetOneShotTripEvent(PWMx, TRIP_EVENT_DCAEVT, TRIP_OUTPUT_LATCH);

  /* Set PWM output tri-state upon DCAEVT0 trip event */
  PWM_SetCHAOutputWhenTrip(PWMx, TZU_TRIP_DO_NOTHING |
                                 TZD_TRIP_DO_NOTHING |
                                 DCEVT0U_TRIP_AS_TRI_STATE |
                                 DCEVT0D_TRIP_AS_TRI_STATE |
                                 DCEVT1U_TRIP_DO_NOTHING |
                                 DCEVT1D_TRIP_DO_NOTHING);

  PWM_SetCHBOutputWhenTrip(PWMx, TZU_TRIP_DO_NOTHING |
                                 TZD_TRIP_DO_NOTHING |
                                 DCEVT0U_TRIP_AS_TRI_STATE |
                                 DCEVT0D_TRIP_AS_TRI_STATE |
                                 DCEVT1U_TRIP_DO_NOTHING |
                                 DCEVT1D_TRIP_DO_NOTHING);
}

ErrorStatus Set_PRE_DRIVER_Vds_Ref_Vol(uint8_t ref_vol)
{
  /* Read HV_REG_PDRV0CTL1 register value */
  eErrorState = HV_ReadReg(HV_REG_PDRV0CTL1, &u8PREDRIDATA);
  if(eErrorState == ERROR)
  {
    printf("Read HV_REG_PDRV0CTL1 FAIL\n");
    return ERROR;
  }
  
  /* Set the PRE-DRIVER0 reference voltage(2.4V is recommanded) for high-side Vds monitor */
  eErrorState = HV_WriteReg(HV_REG_PDRV0CTL1, u8PREDRIDATA | ref_vol);
  if(eErrorState == ERROR)
  {
    printf("Set PRE-DRIVER0 high-side Vds reference voltage FAIL\n");
    return ERROR;
  }
  
  /* Read HV_REG_PDRV1CTL1 register value */
  eErrorState = HV_ReadReg(HV_REG_PDRV1CTL1, &u8PREDRIDATA);
  if(eErrorState == ERROR)
  {
    printf("Read HV_REG_PDRV1CTL1 FAIL\n");
    return ERROR;
  }
  
  /* Set the PRE-DRIVER1 reference voltage(2.4V is recommanded) for high-side Vds monitor */
  eErrorState = HV_WriteReg(HV_REG_PDRV1CTL1, u8PREDRIDATA | ref_vol);
  if(eErrorState == ERROR)
  {
    printf("Set PRE-DRIVER1 high-side Vds reference voltage FAIL\n");
    return ERROR;
  }
  return SUCCESS;
}

ErrorStatus Enable_PRE_DRIVER_OC_TZ()
{
  /* Enable U/V/W-phase over-current TZ of PRE-DRIVER0 */
  eErrorState = HV_WriteReg(HV_REG_PDRV0TZEN, PDRV0TZEN_ALL_OCU_ENABLE
                                                          | PDRV0TZEN_ALL_OCV_ENABLE
                                                          | PDRV0TZEN_ALL_OCW_ENABLE);
  if(eErrorState == ERROR)
  {
    printf("Enable U/V/W-phase over-current interruput of PRE-DRIVER0 FAIL\n");
    return ERROR;
  }
  
  /* Enable U/V/W-phase over-current TZ of PRE-DRIVER1 */
  eErrorState = HV_WriteReg(HV_REG_PDRV0TZEN, PDRV1TZEN_ALL_OCU_ENABLE
                                                          | PDRV1TZEN_ALL_OCV_ENABLE
                                                          | PDRV1TZEN_ALL_OCW_ENABLE);
  if(eErrorState == ERROR)
  {
    printf("Enable U/V/W-phase over-current interruput of PRE-DRIVER1 FAIL\n");
    return ERROR;
  }
  
  return SUCCESS;
}

ErrorStatus Set_FILTER_BLANKING_Time(uint8_t prdv0_filttime, uint8_t prdv0_blanktime, uint8_t prdv1_filttime, uint8_t prdv1_blanktime)
{
  /* Set over voltage FILTER time for PRE-DRIVER0 */
  eErrorState = HV_WriteReg(HV_REG_PDRV0CTL1, prdv0_filttime);
  if(eErrorState == ERROR)
  {
    printf("Set over voltage FILTER time for PRE-DRIVER0 FAIL\n");
    return ERROR;
  }
  
  /* Set over voltage BLANKING time for PRE-DRIVER0 */
  eErrorState = HV_WriteReg(HV_REG_PDRV0CTL1, prdv0_blanktime);
  if(eErrorState == ERROR)
  {
    printf("Set over voltage BLANKING time for PRE-DRIVER0 FAIL\n");
    return ERROR;
  }
  
  /* Set over voltage FILTER time for PRE-DRIVER1 */
  eErrorState = HV_WriteReg(HV_REG_PDRV1CTL1, prdv1_filttime);
  if(eErrorState == ERROR)
  {
    printf("Set over voltage FILTER time for PRE-DRIVER1 FAIL\n");
    return ERROR;
  }
  
  /* Set over voltage BLANKING time for PRE-DRIVER1 */
  eErrorState = HV_WriteReg(HV_REG_PDRV1CTL1, prdv1_blanktime);
  if(eErrorState == ERROR)
  {
    printf("Set over voltage BLANKING time for PRE-DRIVER1 FAIL\n");
    return ERROR;
  }
  
  return SUCCESS;
}


 /*************************************************************************************************************************
 *
 * @brief      This case show how to use Vds monitor function of PRE-DRIVER, TZ event will be triggered and crossponding PWM
 *             channel waveform will stop immediately and the infomation in INT function will print when Vds too high event
 *             happended.
 *
 *                                                        SPD1178 CHIP
 *                            --------------------------------------------------------------------------------
 *                           |               MCU                                                              |
 *                           |    -------------------------------------                     --------------    |
 *                           |   |      -------------                  |                   |              |   |
 *                           |   |     |  --------   |TZ signal  ------|      TZ signal    |              |   |
 *                           |   |     | |  DC SUB|<-|----------|      |<------------------X              |   |
 *                           |   |     | |        |  |          |      |                   |              |   |
 *                           |   |     | |  MODE  |  |          | EPWR |                   | High voltage |   |
 *                           |   |     |  --------   |           ------|    PWM waveform   |  analog part |   |
 *                           |   |     |     PWM     |-----------------x------------------>|              |   |
 *                           |   |      -------------                  |                   |              |   |
 *                           |    -------------------------------------                     --------------    |
 *                            --------------------------------------------------------------------------------
 *             X : Chip internal PAD
 *            
 *             Key Point:
 *                       <1> Two step need for TZ function ��Enable TZ function on the EPWR side, Enable TZ function on the
 *                           MCU side.
 *
 *             Key point:
 *             PRE-DRIVER init include two step,
 *                                              <1> Init the communication circuits(SSP circyits) with MCU, we call this
 *                                                  step 'Init PRE-DRIVER module'.
 *                                              <2> Init PRE-DRIVER socket itself, we call this step 'Power up PRE-DRIVER mode'.
 *
 *************************************************************************************************************************/


int main()
{
  FLASH_WALLOW();
  FLASH_SetTiming(200000000);
  /* Disable flash write access after flash operation had done */
  FLASH_WDIS();

  CLOCK_InitWithRCO(CLOCK_HCLK_200MHZ);

  Delay_Init();

  /*
   * Init the UART
   *
   * 1.Set the GPIO34/35 as UART FUNC
   *
   * 2.Enable the UART CLK
   *
   * 3.Set the rest para
   */
  GPIO_SetPinChannel(GPIO_34,GPIO34_UART_TXD);
  GPIO_SetPinChannel(GPIO_35,GPIO35_UART_RXD);
  CLOCK_EnableModule(UART_MODULE);
  UART_Init(UART,38400);

  /* Init PRE-DRIVER module(this step means ) and get the ID */
  eErrorState = HV_Init(u32SSPBaundrate, &u8PREDRIID, u32Timeout);
  if(eErrorState == ERROR)
  {
    printf("Init PRE-DRIVER mode FAIL\n");
    return 0;
  }
  else
  {
    printf("Init PRE-DRIVER mode SUCCESS[ID:%d]\n",u8PREDRIID);
  }

  /* Set high-side Vds reference voltage(2.4V is recommanded) */
  eErrorState = Set_PRE_DRIVER_Vds_Ref_Vol(VDS_REFERENCE_VOLTAGE);
  if(eErrorState == ERROR)
  {
    printf("Set Vds reference voltage FAIL\n");
    return 0;
  }

  /* Set FILTER and BLANKING time to avoid erroneous over current(OC) assertion for PRE-DRIVER0 and 1(3200ns is recommanded) */
  eErrorState = Set_FILTER_BLANKING_Time(PDRV0CTL1_ALL_FILTERWIN_3200_NS, PDRV0CTL1_ALL_BLANKWIN_3200_NS,
                                         PDRV1CTL1_ALL_FILTERWIN_3200_NS, PDRV1CTL1_ALL_BLANKWIN_3200_NS);
  if(eErrorState == ERROR)
  {
    printf("Set over voltage FILTER and BLANKING time FAIL\n");
    return 0;
  }

  /* Enable U/V/W-phase TZ signal of PRE-DRIVER0 and 1 when over current happened */
  eErrorState = Enable_PRE_DRIVER_OC_TZ();
  if(eErrorState == ERROR)
  {
    printf("Set over current INT FAIL\n");
    return 0;
  }

  /* Power up PRE-DRIVER mode(VDDG is enable at this step) */
  eErrorState = HV_WriteReg(HV_REG_PWRUPKEY, PWRUPKEY_ALL_PUKEY_(0x9));
  if(eErrorState == ERROR)
  {
    printf("Power up PRE-DRIVER FAIL\n");
    return 0;
  }

  /* Delay 100us to wait for the VDDG LDO ready */
  Delay_Us(100);
  
  /* After PRE-DRIVER power on, clear all the status */
  eErrorState = HV_WriteReg(HV_REG_GLBSTSCLR, PDRVSTSCLR_ALL_OCU0_CLEAR
                                                          | PDRVSTSCLR_ALL_OCV0_CLEAR
                                                          | PDRVSTSCLR_ALL_OCW0_CLEAR
                                                          | PDRVSTSCLR_ALL_OCU1_CLEAR
                                                          | PDRVSTSCLR_ALL_OCV1_CLEAR
                                                          | PDRVSTSCLR_ALL_OCW1_CLEAR);
  if(eErrorState == ERROR)
  {
    printf("Clear PRE-DRIVER status FAIL\n");
    return 0;
  }

  /* Wait for PRE-DRIVER mode is power ok */
  eErrorState = HV_ReadReg(HV_REG_GLBSTS, &u8PREDRIDATA);
  if((u8PREDRIDATA & 0x3) == 0)
  {
    printf("PRE-DRIVER power on FAIL[%02x]\n",u8PREDRIDATA);
    return 0;
  }

  /* additional delay after VDDG ready, for final power settling */
  Delay_Us(100);
  
  /* Enable write access to the protected EPWR registers */
  EPWR_WALLOW();
  
  /* Enbale EPWR module receive TZ signal from high voltage analog part */
  EPWR_EnableTripEvent(EPWR_TRIP_EVENT_TZ0);
  EPWR_EnableTripEvent(EPWR_TRIP_EVENT_TZ1);

  /* Set the input signal of TZ(bouth TZ 0 and 1) event as high */
  EPWR_SetTripEventInputActiveHigh(EPWR_TRIP_EVENT_TZ0);
  EPWR_SetTripEventInputActiveHigh(EPWR_TRIP_EVENT_TZ1);

  /* 
   * PRE-DRIVER TZ signal jump from one PAD(on PRE-DRIVER side) to another side(on MCU side), this could cause erroneous TZ assertion
   * signal, so, another filter need to be set on the EPWR side(EPWR is a internal mode of MCU).
   */
  EPWR_SetFilterWindowTimeNs(EPWR_TRIP_EVENT_TZ0, EPWR_WIN_VAL_NS, EPWR_TH_VAL_NS);
  EPWR_SetFilterWindowTimeNs(EPWR_TRIP_EVENT_TZ1, EPWR_WIN_VAL_NS, EPWR_TH_VAL_NS);
  
  /* Select TZ output signal(was sent to MCU) type */
  EPWR_SetTripEventOutputType(EPWR_TRIP_EVENT_TZ0, EPWR_OUTPUT_FILTERED);
  EPWR_SetTripEventOutputType(EPWR_TRIP_EVENT_TZ1, EPWR_OUTPUT_FILTERED);
  
  /* Disable write access to the protected EPWR registers */
  EPWR_WDIS();
  
  /* 
   * Init PWM0~5
   *
   * Warnning : 
   *           when PWM is used for PRE-DRIVER, the dead-band of the pair complementary waveform of one PWM channel
   *           must not equal zero, bigger than 600 is recommanded.
   */
  PWM_ComplementaryPairChannelInit(PRE_DRIVER1_PWM_U, PRE_DRIVER0_PWM_FREQ, PWM_DB_NS);
  PWM_ComplementaryPairChannelInit(PRE_DRIVER1_PWM_V, PRE_DRIVER0_PWM_FREQ, PWM_DB_NS);
  PWM_ComplementaryPairChannelInit(PRE_DRIVER1_PWM_W, PRE_DRIVER0_PWM_FREQ, PWM_DB_NS);
  PWM_ComplementaryPairChannelInit(PRE_DRIVER0_PWM_U, PRE_DRIVER1_PWM_FREQ, PWM_DB_NS);
  PWM_ComplementaryPairChannelInit(PRE_DRIVER0_PWM_V, PRE_DRIVER1_PWM_FREQ, PWM_DB_NS);
  PWM_ComplementaryPairChannelInit(PRE_DRIVER0_PWM_W, PRE_DRIVER1_PWM_FREQ, PWM_DB_NS);

  /* 
   * PWM_U set CMPA and loading the same value to PWM_V/PWM_W synchronously, LINK must be set earlier than CAMP value is set,
   * otherwaise, the LINK function will be invalid.
   */
  PWM_LinkCMPA(PRE_DRIVER1_PWM_V, PRE_DRIVER1_LINK_PWM_U);
  PWM_LinkCMPA(PRE_DRIVER1_PWM_W, PRE_DRIVER1_LINK_PWM_U);

  /* Set PWM5A output 75% duty waveform */
  u3232PWMPeriod = PWMPeriod(PRE_DRIVER1_PWM_FREQ);
  PWM_SetCMPA(PRE_DRIVER1_PWM_U, u3232PWMPeriod / 4);
  
  /* PWM_U set CMPA and loading the same value to PWM_V/PWM_W synchronously */
  PWM_LinkCMPA(PRE_DRIVER0_PWM_V, PRE_DRIVER0_LINK_PWM_U);
  PWM_LinkCMPA(PRE_DRIVER0_PWM_W, PRE_DRIVER0_LINK_PWM_U);

  /* Set PWM5A output 75% duty waveform */
  u3232PWMPeriod = PWMPeriod(PRE_DRIVER0_PWM_FREQ);
  PWM_SetCMPA(PRE_DRIVER0_PWM_U, u3232PWMPeriod / 4);

  /* Enable the TZ function on MCU side(each of six PWM is enable) */
  PWMx_Set_Digiter_Compare(PRE_DRIVER0_PWM_U);
  PWMx_Set_Digiter_Compare(PRE_DRIVER0_PWM_V);
  PWMx_Set_Digiter_Compare(PRE_DRIVER0_PWM_W);
  PWMx_Set_Digiter_Compare(PRE_DRIVER1_PWM_U);
  PWMx_Set_Digiter_Compare(PRE_DRIVER1_PWM_V);
  PWMx_Set_Digiter_Compare(PRE_DRIVER1_PWM_W);

  /* Set PWM of PRE-DRIVER 0 start to count */
  PWM_RunCounter(PRE_DRIVER0_PWM_U);
  PWM_RunCounter(PRE_DRIVER0_PWM_V);
  PWM_RunCounter(PRE_DRIVER0_PWM_W);

  /* Set PWM of PRE-DRIVER 1 start to count */
  PWM_RunCounter(PRE_DRIVER1_PWM_U);
  PWM_RunCounter(PRE_DRIVER1_PWM_V);
  PWM_RunCounter(PRE_DRIVER1_PWM_W);

  /* Enabel global INT for WPM TZ(bouth of six PWM TZ) */
  NVIC_EnableIRQ(PWM0TZ_IRQn);
  NVIC_EnableIRQ(PWM1TZ_IRQn);
  NVIC_EnableIRQ(PWM2TZ_IRQn);
  NVIC_EnableIRQ(PWM3TZ_IRQn);
  NVIC_EnableIRQ(PWM4TZ_IRQn);
  NVIC_EnableIRQ(PWM5TZ_IRQn);

  while(1)
  {

  }
}

void PWM0TZ_IRQHandler()
{
  printf("PWM0 TZ INT happened\n");
}

void PWM1TZ_IRQHandler()
{
  printf("PWM1 TZ INT happened\n");
}

void PWM2TZ_IRQHandler()
{
  printf("PWM2 TZ INT happened\n");
}

void PWM3TZ_IRQHandler()
{
  printf("PWM3 TZ INT happened\n");
}

void PWM4TZ_IRQHandler()
{
  printf("PWM4 TZ INT happened\n");
}

void PWM5TZ_IRQHandler()
{
  printf("PWM5 TZ INT happened\n");
}
