/**
*   @file    Base_Task.c
*   @version 1.0.0
*
*   @brief   Base task init and runtime
*   @details The base task includes the following:
*            1) toggling LED1(PTF8) every 100ms;
*            2) sample ADC0/ADC1/ADC2 channels;
*            3) use PTF11_EMIOS2_CH11 to control PWM duty for LED4(PTF11);
*            4) monitor user buttons and toggle LED2(PTF9)/LED3(PTF10) respectively;
*               Btn1=PTC20_SIUL2_EIRQ16,
*               Btn2=PTC21_SIUL2_EIRQ17,
*
*   This file contains sample code only. It is not part of the production code deliverables.
*
*   @addtogroup Base_Task
*   @{
*/

#ifdef __cplusplus
extern "C" {
#endif

/*==================================================================================================
*                                        INCLUDE FILES
* 1) Basic RTD modules includes
* 2) MCU registers definitions includes
* 3) needed interfaces from external units
==================================================================================================*/

#include "Mcal.h"
#include "OsIf.h"
#include "Siul2_Port_Ip.h"
#include "Siul2_Dio_Ip.h"
#include "Lpspi_Ip.h"
#include "Emios_Icu_Ip.h"
#include "IntCtrl_Ip.h"
#include "retarget.h"
#include "string.h"
#include "NCK2910_SpiCom.h"


/*==================================================================================================
*                          LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
==================================================================================================*/
typedef enum
{
	/** @brief Receive length of the frame = 0xFF*/
	lzSpiComStatus_RecvFrameLengthFF = 0u,
	/** @brief Receive length of the frame = 0x00*/
	lzSpiComStatus_RecvFrameLength00,
	/** @brief Receive data complete status */
	lzSpiComStatus_RecvDataComplete,
	/** @brief Receive data incomplete status */
	lzSpiComStatus_RecvDataUnComplete,
	/** @brief Receive data overflow status */
	lzSpiComStatus_RecvDataOverflow,
} phscaCendricCadsLizard_SpiCom_Status_t;

/*==================================================================================================
*                                       LOCAL MACROS
==================================================================================================*/
#ifndef EMIOS1_CH6
#define EMIOS1_CH6	6
#endif

#ifndef EMIOS1_INST
#define EMIOS1_INST 1
#endif

/*! Delay for RST pin toggle during boot routine. */
#define LZ_RST_HOLD_US              5000U
/*! Delay till the boot routine is complete. */
#define LZ_BOOT_DELAY_US            15000U
/* NCK2910 CS - PTE4_LPSPI0_PCS0 port and pin index */
#define LZ_CS_PORT		4
#define LZ_CS_PIN		4
/* NCK2910 INT - PTF22_EMIOS1_CH6 port and pin index */
#define LZ_INT_PORT		5
#define LZ_INT_PIN		22
/* NCK2910 RDY - PTF21_EMIOS1_CH5 port and pin index */
#define LZ_RDY_PORT		5
#define LZ_RDY_PIN		21
/* NCK2910 RST - PTF21_EMIOS1_CH5 port and pin index */
#define LZ_RST_PORT		6
#define LZ_RST_PIN		13

/* SCK to CS pin delay in usec */
#define LZ_COM_CS_RELEASE_DELAY        20U

#define LZ_SPI_TIMEOUT 5000U /*!< Timeout for blocking transfer. */

/*! Delay between consecutive transfers. */
#define LZ_COM_INTER_TRANSFER_DELAY_US 5000U
/*! Delay after CS pin is asserted low to make sure that the device is ready. */
#define LZ_COM_RTS_DELAY_US            20U
/*! Timeout used to wait for a level of RDY pin after previous transfer. */
#define LZ_COM_RDY_CLR_TIMEOUT_US      100000U
/*! Timeout used to wait for a level of RDY pin after request to send. */
#define LZ_COM_RDY_RTS_TIMEOUT_US      100000U
/*! Timeout used to wait for a level of INT pin. */
#define LZ_COM_INT_TIMEOUT_US          1000000U
/*! Timeout measurement is split into steps. This macro defines value of the
 *  step in us. */
#define LZ_COM_TIMEOUT_STEP_US         20U
/*! Active level of RDY_N pin. */
#define LZ_COM_RDY_PIN_ACTIVE          0U
/*! Active level of INT_N pin. */
#define LZ_COM_INT_PIN_ACTIVE          0U

/*==================================================================================================
*                                      LOCAL CONSTANTS
==================================================================================================*/

/*==================================================================================================
*                                      LOCAL VARIABLES
==================================================================================================*/

/*==================================================================================================
*                                      GLOBAL CONSTANTS
==================================================================================================*/


/*==================================================================================================
*                                      GLOBAL VARIABLES
==================================================================================================*/

uint8_t lzSpiTxBuf[LZ_MAX_BUFFER_LENGTH];
uint8_t lzSpiRxBuf[LZ_MAX_BUFFER_LENGTH];
uint8_t lzSpiTxLen;
uint8_t lzSpiRxLen;

/* This global var is set when MCU detects NCK2910 INT signal */
uint8_t g_IsLzIntAppear;

/*==================================================================================================
*                                   LOCAL FUNCTION PROTOTYPES
==================================================================================================*/

/*==================================================================================================
*                                       External FUNCTIONS
==================================================================================================*/
extern void EMIOS1_4_IRQ(void);
extern void OsIf_Delay_Us(uint32_t nUsec);
extern void OsIf_Delay_Ms(uint32_t nMsec);

/*==================================================================================================
*                                       LOCAL FUNCTIONS
==================================================================================================*/

/*==================================================================================================
*                                       Interrupts callback FUNCTIONS
==================================================================================================*/

/*================================================================================================*/
/**
 * @brief          PTF22 EMIOS1 CH6 ICU notification function for monitoring NCK2910 INTB signal
 * @details        PTF22 EMIOS1 CH6 ICU notification function for monitoring NCK2910 INTB signal
 * @param[in]      none
 * @return         none
 */
void __attribute__((weak)) NCK2910_Int_Notification(void){
	g_IsLzIntAppear = 1;
	return;
}

/*==================================================================================================
*                                       GLOBAL FUNCTIONS
==================================================================================================*/
/*FUNCTION**********************************************************************
 *
 * Function Name : LZ_SetupGPIOs
 * Description   : Configures GPIOs used by the Lizard driver.
 *
 *END**************************************************************************/
status_t LZ_SetupGPIOs(void)
{
#if ENABLE_NCK2910_IRQ_HANDLE
    /* Set INT pin as input. */
	Emios_Icu_Ip_Init(EMIOS1_INST, &eMios_Icu_Ip_1_Config_PB_BOARD_INITPERIPHERALS);
	Emios_Icu_Ip_EnableEdgeDetection(EMIOS1_INST, EMIOS1_CH6);
	Emios_Icu_Ip_EnableNotification(EMIOS1_INST, EMIOS1_CH6);

    /* install IRQ handler for NCK2910 INT pin */
    IntCtrl_Ip_InstallHandler(EMIOS1_4_IRQn, EMIOS1_4_IRQ, NULL_PTR);
	IntCtrl_Ip_EnableIrq(EMIOS1_4_IRQn);
#endif

    /* Set CSB pin as output with default value high (active low). */
	/* Configure PTE4 LPSPI0_PCS0 as GPIO output high. This should be already done on Port_Init. */
	Siul2_Dio_Ip_WritePin(NCK_CS_PORT, NCK_CS_PIN, 1);
	Siul2_Port_Ip_SetOutputBuffer(PORTE_L_HALF, 4, TRUE, PORT_MUX_AS_GPIO);

    /* Set RDY pin as input. */
	/* Configure PTF21 EMIOS1_CH5 as GPIO input to monitor NCK2910 RDY signal. This should be already done on Port_Init.*/
	Siul2_Port_Ip_SetInputBuffer(PORTF_H_HALF, (21-16), TRUE, 0, PORT_INPUT_MUX_NO_INIT);		//NCK_RDY pin

    /* Set RST pin as output. */
	/* Configure PTG13 GPIO205 as GPIO output for NCK2910 RST control. This should be already done on Port_Init. */
	Siul2_Dio_Ip_WritePin(NCK_RST_PORT, NCK_RST_PIN, 1);
	Siul2_Port_Ip_SetOutputBuffer(PORTG_L_HALF, 13, TRUE, PORT_MUX_AS_GPIO);

    return STATUS_SUCCESS;
}

/*================================================================================================*/
/**
 * @brief          Set NCK2910 Reset pin
 * @details        Set NCK2910 Reset pin
 * @param[in]      pinLevel - 0=low, 1=high
 * @return         none
 */
void LZ_SetRstPin(uint8_t pinLevel){
	Siul2_Dio_Ip_WritePin(NCK_RST_PORT, NCK_RST_PIN, pinLevel);
}

/*================================================================================================*/
/**
 * @brief          Set NCK2910 CS pin
 * @details        Set NCK2910 CS pin
 * @param[in]      pinLevel - 0=low, 1=high
 * @return         none
 */
void LZ_SetCsPin(uint8_t pinLevel){
	Siul2_Dio_Ip_WritePin(NCK_CS_PORT, NCK_CS_PIN, pinLevel);
}

/*================================================================================================*/
/**
 * @brief          Get NCK2910 Rdy pin status
 * @details        Get NCK2910 Rdy pin status
 * @param[in]      none
 * @return         0=low, 1=high
 */
uint8_t LZ_GetRdyPin(void){
	return (Siul2_Dio_Ip_ReadPin(NCK_RDY_PORT, NCK_RDY_PIN));
}

/*================================================================================================*/
/**
 * @brief          Setup the SPI interface for NCK2910
 * @details        It's already done in BSP initialization calling LPSPI0 init.
 * @param[in]      none
 * @return         E_OK
 */
status_t LZ_SetupSPI(void)
{
    status_t status = STATUS_SUCCESS;
    /* LPSPI1 is already initialized before calling this function. */
    /* Configure PTE4 (LPSPI0_PCS0) as GPIO */
    Siul2_Dio_Ip_WritePin(NCK_CS_PORT, NCK_CS_PIN, 1);
    Siul2_Port_Ip_SetOutputBuffer(PORTE_L_HALF, 4, TRUE, PORT_MUX_AS_GPIO);
    return status;
}

/*================================================================================================*/
/**
 * @brief          Set NCK2910 CS pin low
 * @details        Set NCK2910 CS pin low
 * @param[in]      none
 * @return         none
 */
static void LZ_COM_RequestBus(void)
{
	LZ_SetCsPin(0);
}

/*================================================================================================*/
/**
 * @brief          Set NCK2910 CS pin high
 * @details        Set NCK2910 CS pin high
 * @param[in]      none
 * @return         none
 */
static void LZ_COM_ReleaseBus(void)
{
	OsIf_Delay_Us(LZ_COM_CS_RELEASE_DELAY);
    LZ_SetCsPin(1);
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Rdy pin
 * @details        Polls NCK2910 Rdy pin until it matches the given level.
 * @param[in]      pinLevel - 0=low, 1=high.
 * @return         none
 */
static void LZ_WaitRdyPin(uint8_t pinLevel){
	while(pinLevel != Siul2_Dio_Ip_ReadPin(NCK_RDY_PORT, NCK_RDY_PIN));
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin
 * @details        Polls NCK2910 Int pin until it matches the given level.
 * @param[in]      pinLevel - 0=low, 1=high.
 * @return         none
 */
static void LZ_WaitIntPin(uint8_t pinLevel){
	while(pinLevel != Siul2_Dio_Ip_ReadPin(NCK_INT_PORT, NCK_INT_PIN));
}

/*================================================================================================*/
/**
 * @brief          Clear NCK2910 Int global var flag
 * @details        Clear NCK2910 Int global var flag
 * @param[in]      none
 * @return         none
 */
static void LZ_ClearIntFlag(void) {
	g_IsLzIntAppear = 0u;
}

/*================================================================================================*/
/**
 * @brief          Operations before data transfer start
 * @details        wait for RDY pin high, wait for INT pin low, set CS pin low, wait for RDY pin low
 * @param[in]      wait - set the event to wait.
 * @return         none
 */
static status_t LZ_COM_RequestToSend(lz_wait_t wait)
{
    status_t status = STATUS_SUCCESS;

	if ((lzWaitRDY == wait) || (lzWaitRDY_INT == wait)) {
		LZ_WaitRdyPin(1);
    }

     if (lzWaitRDY_INT == wait) {
    	 LZ_WaitIntPin(0);
    	 LZ_ClearIntFlag();
     }

	LZ_COM_RequestBus();	/* Set CS pin low. (request to send) */

	LZ_WaitRdyPin(0);		/* wait for RDY pin low */

    return status;
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Rdy pin status
 * @details        Polls NCK2910 Rdy pin status
 * @param[in]      none
 * @return         actual Rdy pin level
 */
bool LZ_IsDeviceReady(void) {
	return (Siul2_Dio_Ip_ReadPin(NCK_RDY_PORT, NCK_RDY_PIN)==0);
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin status
 * @details        Polls NCK2910 Int pin status
 * @param[in]      none
 * @return         actual Int pin level
 */
bool LZ_HasDeviceDataPending(void) {
	return (Siul2_Dio_Ip_ReadPin(NCK_INT_PORT, NCK_INT_PIN)==0);
}

/*================================================================================================*/
/**
 * @brief          Send a frame to NCK2910
 * @details        Exchange data with NCK2910 - read and write.
 * @param[in]      pExtSpiDevice - pointer to the SPI external device configuration structure, it's in the generated code.
 * @param[in]      wait - signal that need be monitored before sending data.
 * @param[in]      sendFrame - the data to send from MCU to NCK2910
 * @param[out]     recvFrame - the buffer to receive data from NCK2910
 *                 sendLen - length of the sendFrame
 *                 pRecvLen	- the actual received data length
 * @return         STATUS_SUCCESS or STATUS_ERROR
 */
status_t LZ_SpiCom_ReadWriteFrame(const Lpspi_Ip_ExternalDeviceType *pExtSpiDevice, lz_wait_t wait, uint8_t *sendFrame, uint8_t *recvFrame, uint8_t sendLen, uint8_t *pRecvLen)
{
    status_t result = STATUS_SUCCESS;
    uint8_t recvLen = 0U;  /* Length of the received RCI frame. */
    uint8_t byteToSend = 1U;

	/* wait till NCK2910 control the LPSPI0 resource */
	while(1){
		result = (status_t) Lpspi_Ip_UpdateFrameSize(pExtSpiDevice, 8);
		if(result == STATUS_SUCCESS)
			break;
	}

    result = LZ_COM_RequestToSend(wait);

	/* send one byte to get the data size to receive */
	result = (status_t) Lpspi_Ip_SyncTransmit(pExtSpiDevice, sendFrame, recvFrame, byteToSend, LZ_SPI_TIMEOUT);

    if (E_OK == result) {   /* Update the SPI data structure to receive the rest of the response frame. */
        recvLen = recvFrame[0];

        if (0xFFU == recvLen) {   /* The device is not ready. */
            LZ_COM_ReleaseBus();
            result = STATUS_ERROR;
        }
        else if (0x00U == recvLen) {   /* There is no response. */
        	byteToSend = sendLen > 0 ? (sendLen - 1U) : 0U;
        	*pRecvLen = 0;
        } else  {   /* The length is specified. */
            if ((recvLen + 1U) > LZ_MAX_BUFFER_LENGTH) {
            	result = STATUS_ERROR;//kStatus_LZ_ComRecvBufOverflow;
            }
            else if ((sendLen - 1U) > recvLen) { /* if send length > receive length */
            	byteToSend = sendLen - 1U;
            } else {
            	byteToSend = recvLen;
            }
            /* Length of the received frame including the length byte. */
            *pRecvLen = recvLen + 1;
        }
    }
    /* Receive or write the rest of the data. */
    if ((E_OK == result) && (byteToSend > 0U)) {
    	result = (status_t) Lpspi_Ip_SyncTransmit(
    									pExtSpiDevice,
									  sendFrame+1,
									  recvFrame+1,
									  byteToSend,
									  LZ_SPI_TIMEOUT);
    }
    /* SPI bus must be released even if an error occurred. */
    LZ_COM_ReleaseBus();
    if(result!=E_OK){
    	while(1);
    }

    return result;
}

/* print spi tx and rx messages to UART. */
/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin status
 * @details        Polls NCK2910 Int pin status
 * @param[in]      none
 * @return         actual Int pin level
 */
void LZ_Print_Spi_Log(uint8_t nSize) {
	(void)nSize;
#if LZ_SPI_LOG_ENABLE
	char str[192];
	uint8_t len=0;
	uint8_t *pt;
	pt = &lzSpiTxBuf[0];
	for(uint8_t i=0; i<nSize; i++){
		len = sprintf(str+i*3, " %02X", *pt++);
	}
	LOGI("LZ", "SPI Tx: %s", str);
	vTaskDelay(10);
	pt = &lzSpiRxBuf[0];
	for(uint8_t i=0; i<nSize; i++){
		len = sprintf(str+i*3, " %02X", *pt++);
	}
	LOGI("LZ", "SPI Rx: %s", str);
	vTaskDelay(10);
#endif
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin status
 * @details        Polls NCK2910 Int pin status
 * @param[in]      none
 * @return         actual Int pin level
 */
status_t LZ_SpiCom_Readframe(const Lpspi_Ip_ExternalDeviceType *pExtSpiDevice,	lz_wait_t wait, uint8_t*receiveBuff, uint8_t *framelength)
{
	status_t result = STATUS_SUCCESS;
	memset(lzSpiTxBuf, 0x00u, LZ_MAX_BUFFER_LENGTH);
	result = LZ_SpiCom_ReadWriteFrame(pExtSpiDevice,	wait, lzSpiTxBuf, receiveBuff, 1, framelength); //&lzSpiRxLen
	LZ_Print_Spi_Log(lzSpiRxLen);
	return result;
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin status
 * @details        Polls NCK2910 Int pin status
 * @param[in]      none
 * @return         actual Int pin level
 */
status_t LZ_SpiCom_WriteFrame(const Lpspi_Ip_ExternalDeviceType *pExtSpiDevice, lz_wait_t wait, uint8_t*sendFrame, uint8_t *recvFrame, uint8_t byteToSend)
{
	status_t result = STATUS_SUCCESS;
	//memset(lzSpiTxBuf, 0x00u, LZ_MAX_BUFFER_LENGTH);
	result = LZ_SpiCom_ReadWriteFrame(pExtSpiDevice,	wait, sendFrame, recvFrame, byteToSend, &lzSpiRxLen);
	LZ_Print_Spi_Log(byteToSend>lzSpiRxLen?byteToSend:lzSpiRxLen);
	return result;
}

/*================================================================================================*/
/**
 * @brief          Polls NCK2910 Int pin status
 * @details        Polls NCK2910 Int pin status
 * @param[in]      none
 * @return         actual Int pin level
 */
status_t LZ_Init(void)
{
    status_t            status = STATUS_SUCCESS;
    status = LZ_SetupGPIOs();

    /* RST to low for t_rst_hold. */
    LZ_SetRstPin(0);
    OsIf_Delay_Us(LZ_RST_HOLD_US);
    LZ_SetRstPin(1);

    /* Boot routine completed after t_boot_rci. */
    OsIf_Delay_Us(LZ_BOOT_DELAY_US);

    /* Read and check the Reboot event. */
    status = LZ_SpiCom_Readframe(LZ_SPI_INST, lzWaitRDY_INT, lzSpiRxBuf, &lzSpiRxLen);
    /*
    if(lzSpiRxBuf[1]==0x80) {
    	LOGI("LZ", "Reboot reason is 0x%02X, status is 0x%02X.\n", lzSpiRxBuf[6], lzSpiRxBuf[5]);
    }
    else {
    	LOGI("LZ", "Error. Event code should be 0x80 but it is 0x%02X.\n", lzSpiRxBuf[1]);
    }*/

    return status;
}

#ifdef __cplusplus
}
#endif

/** @} */

/*******************************************************************************
 * EOF
 ******************************************************************************/
