/**
 * @file X-NUCLEO-53L1A1.c
 *
 * implement X-NUCLEO-53L1A1 Nucleo BSP
 */

#include "main.h"
#include <string.h>
#include  "X-NUCLEO-53L1A1.h"

#include "stm32wlxx_hal.h"


#ifndef HAL_I2C_MODULE_ENABLED
#define HAL_I2C_MODULE_ENABLED
#pragma message("hal conf should enable i2c")
#endif

/* when not customized by application define dummy one */
#ifndef XNUCLEO53L1A1_GetI2cBus
/**
 * macro that can be overloaded by user to enforce i2c sharing in RTOS context
 */
#define XNUCLEO53L1A1_GetI2cBus(...) (void)0
#endif

#ifndef XNUCLEO53L1A1_PutI2cBus
/** macro can be overloaded by user to enforce i2c sharing in RTOS context
 */
#   define XNUCLEO53L1A1_PutI2cBus(...) (void)0
#endif



/**
 * Expander 0 i2c address[7..0] format
 */
#define I2cExpAddr0 ((int)(0x43*2))
/**
 * Expander 1 i2c address[7..0] format
 */
#define I2cExpAddr1 ((int)(0x42*2))
/** @} XNUCLEO53L1A1_I2CExpanders*/


/**
 * GPIO monitor pin state register
 * 16 bit register LSB at lowest offset (little endian)
 */
#define GPMR    0x10
/**
 * STMPE1600 GPIO set pin state register
 * 16 bit register LSB at lowest offset (little endian)
 */
#define GPSR    0x12
/**
 * STMPE1600 GPIO set pin direction register
 * 16 bit register LSB at lowest offset
 */
#define GPDR    0x14


/** @} */ /* defgroup  XNUCLEO53L1A1_Board */


/****************************************************
 *@defgroup  XNUCLEO53L1A1_globals
 *@{
 */

/**
 *  i2c handle to be  use of all i2c access
 * end user shall provide it to
 * can be @a XNUCLEO53L1A1_I2C1Configure() @sa XNUCLEO53L1A1_usage
 * @warning do not use any XNUCLEO53L1A1_xxx prior to a first init with valid i2c handle
 */
I2C_HandleTypeDef  XNUCLEO53L1A1_hi2c;


/**
 * cache the full set of expanded GPIO values to avoid i2c reading
 */
static union CurIOVal_u {
    uint8_t bytes[4];   /*!<  4 bytes array i/o view */
    uint32_t u32;       /*!<  single dword i/o view */
}
/** cache the extended IO values */
CurIOVal;

/**
 * lookup table for for digit  to bit position in @a CurIOVal u32
 */
//static int  DisplayBitPos[4]={0, 7, 16, 16+7};

/** @} XNUCLEO53L1A1_globals*/

/* Forward definition of private function */

static int _ExpanderRd(int I2cExpAddr, int index, uint8_t *data, int n_data);
static int _ExpanderWR(int I2cExpAddr, int index, uint8_t *data, int n_data);
//static int _ExpandersSetAllIO(void);

/**
 * Expansion board i2c bus recovery
 *
 * We may get reset in middle of an i2c access (h/w reset button, debug or f/w load)
 * hence some agent on bus may be in middle of a transaction and can create issue or even prevent starting (SDA is low)
 * this routine does use gpio to manipulate and recover i2c bus line in all cases.
 */
static void _I2cFailRecover(){
    GPIO_InitTypeDef GPIO_InitStruct;
    int i, nRetry=0;


    // We can't assume bus state based on SDA and SCL state (we may be in a data or NAK bit so SCL=SDA=1)
    // by setting SDA high and toggling SCL at least 10 time we ensure whatever agent and state
    // all agent should end up seeing a "stop" and bus get back to an known idle i2c  bus state

    // Enable I/O
    __GPIOA_CLK_ENABLE();
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
    GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    //TODO we could do this faster by not using HAL delay 1ms for clk timing
    do{
        for( i=0; i<10; i++){
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_RESET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(GPIOA, GPIO_PIN_12, GPIO_PIN_SET);
            HAL_Delay(1);
        }
//        if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 ){
//            static int RetryRecover;
//            RetryRecover++;
//        }
    }while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0 && nRetry++<7);

    if( HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_11) == 0 ){
        __GPIOB_CLK_ENABLE();
        //We are still in bad i2c state warm user by blinking led but stay here
        GPIO_InitStruct.Pin = LED1_Pin ;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(LED3_GPIO_Port, &GPIO_InitStruct);
        do{
            HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_SET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, GPIO_PIN_RESET);
            HAL_Delay(33*20);
        }while(1);
    }
}



int XNUCLEO53L1A1_I2C2Configure() {
    int status;
    GPIO_InitTypeDef GPIO_InitStruct;

    _I2cFailRecover();

    /* Peripheral clock enable */
    __GPIOA_CLK_ENABLE();
    __I2C2_CLK_ENABLE();

    /**I2C1 GPIO Configuration\n
     PB8     ------> I2C1_SCL\n
     PB9     ------> I2C1_SDA
     */
    GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    XNUCLEO53L1A1_hi2c.Instance = I2C2;
    //XNUCLEO53L1A1_hi2c.Init.Timing = 0x00300F38; /* set 400KHz fast mode i2c*/
    XNUCLEO53L1A1_hi2c.Init.Timing = 0x2010091A; //0x2010091A = 400K Fast Mode, 0x20303E5D, 100K Standard mode, 0x20000209 Fast Mode Plus, 1Mbps

    XNUCLEO53L1A1_hi2c.Init.OwnAddress1 = 0;
    XNUCLEO53L1A1_hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    XNUCLEO53L1A1_hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
    XNUCLEO53L1A1_hi2c.Init.OwnAddress2 = 0;
    XNUCLEO53L1A1_hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
    XNUCLEO53L1A1_hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;
    status = HAL_I2C_Init(&XNUCLEO53L1A1_hi2c);
    return status;
}

int XNUCLEO53L1A1_SetIntrStateId(int EnableIntr, int DevNo){
    int status;
    IRQn_Type IntrNo;
    int IntrPin;


    switch( DevNo ){
    case XNUCLEO53L1A1_DEV_CENTER :
    case 'c' :
        IntrNo = VL53L1A1_GPIO1_C_INTx;
        IntrPin= VL53L1A1_GPIO1_C_GPIO_PIN;
        status = 0;
        break;

    case XNUCLEO53L1A1_DEV_LEFT :
    case 'l' :

        break;
    case 'r' :
    case XNUCLEO53L1A1_DEV_RIGHT :


        break;

    default:
        XNUCLEO53L1A1_ErrLog("Invalid DevNo %d",DevNo);
        status = -1;
        goto done;
    }

    if( EnableIntr ){
        __HAL_GPIO_EXTI_CLEAR_IT(IntrPin);
        NVIC_ClearPendingIRQ(IntrNo);
        HAL_NVIC_EnableIRQ(IntrNo);
        /**
         * @note  When enabling interrupt end user shall check actual state of the line and soft trigger event if active
         * Alternatively user can use API and device feature to clear device Interrupt status to possibly generates a new edge.
         * on shared pin configuration this must be repeated for all device.
         * The same shall be done after clearing a condition in device and interrupt remain active.
         */
    }
    else{
        HAL_NVIC_DisableIRQ(IntrNo);
        __HAL_GPIO_EXTI_CLEAR_IT(IntrPin);
        NVIC_ClearPendingIRQ(IntrNo);
    }

done:
    return status;
}


int XNUCLEO53L1A1_Init(void) {
    int status;
//    uint8_t ExpanderData[2];
//    XNUCLEO53L1A1_USART2_UART_Init();
    status = XNUCLEO53L1A1_I2C2Configure();
    return status;
}

#if 0
int XNUCLEO53L1A1_GetPB1(int *state) {
    int status;
    uint8_t  PortValue;
    status= _ExpanderRd(I2cExpAddr1, GPMR+1, &PortValue,1);
    if( status == 0){
        if( PortValue&=0x40 )
            PortValue=1;
        else
            PortValue=0;
    }
    else{
        XNUCLEO53L1A1_ErrLog("i/o error");
    }
    *state = PortValue;
    return status;
}
#endif
int XNUCLEO53L1A1_ResetId(int DevNo, int state) {
    int status;
    switch( DevNo ){
    case XNUCLEO53L1A1_DEV_CENTER :
    case 'c' :
        CurIOVal.bytes[3]&=~0x80; /* bit 15 expender 1  => byte #3 */
        if( state )
            CurIOVal.bytes[3]|=0x80; /* bit 15 expender 1  => byte #3 */
        status= _ExpanderWR(I2cExpAddr1, GPSR+1, &CurIOVal.bytes[3], 1);
        break;
    case XNUCLEO53L1A1_DEV_LEFT :
    case 'l' :
        CurIOVal.bytes[1]&=~0x40; /* bit 14 expender 0 => byte #1*/
        if( state )
            CurIOVal.bytes[1]|=0x40; /* bit 14 expender 0 => byte #1*/
        status= _ExpanderWR(I2cExpAddr0, GPSR+1, &CurIOVal.bytes[1], 1);
        break;
    case 'r' :
    case XNUCLEO53L1A1_DEV_RIGHT :
        CurIOVal.bytes[1]&=~0x80; /* bit 15 expender 0  => byte #1 */
        if( state )
            CurIOVal.bytes[1]|=0x80; /* bit 15 expender 0 => byte #1*/
        status= _ExpanderWR(I2cExpAddr0, GPSR+1, &CurIOVal.bytes[1], 1);
        break;
    default:
        XNUCLEO53L1A1_ErrLog("Invalid DevNo %d",DevNo);
        status = -1;
        goto done;
    }
//error with valid id
    if( status ){
        XNUCLEO53L1A1_ErrLog("expander i/o error for DevNo %d state %d ",DevNo, state);
    }
done:
    return status;
}

/**
 * Set all i2c expended gpio in one go
 * @return i/o operation status
 */
#if 0
static int _ExpandersSetAllIO(void){
    int status;
    status = _ExpanderWR(I2cExpAddr0, GPSR, &CurIOVal.bytes[0], 2);
    if( status ){
        goto done_err;
    }
    status = _ExpanderWR(I2cExpAddr1, GPSR, &CurIOVal.bytes[2], 2);
done_err:
    return status;
}
#endif
/**
 * STMPE1600  i2c Expender register read
 * @param I2cExpAddr Expender address
 * @param index      register index
 * @param data       read data buffer
 * @param n_data     number of byte to read
 * @return           of if ok else i2c I/O operation status
 */

static int _ExpanderRd(int I2cExpAddr, int index, uint8_t *data, int n_data) {

    int status;
    uint8_t RegAddr;
    RegAddr = index;
    XNUCLEO53L1A1_GetI2cBus();
    do {
        status = HAL_I2C_Master_Transmit(&XNUCLEO53L1A1_hi2c, I2cExpAddr, &RegAddr, 1, 100);
        if (status)
            break;
        status = HAL_I2C_Master_Receive(&XNUCLEO53L1A1_hi2c, I2cExpAddr, data, n_data, n_data * 100);
    } while (0);
    XNUCLEO53L1A1_PutI2cBus();
    return status;
}

/**
 * STMPE1600 i2c Expender register write
 * @param I2cExpAddr Expender address
 * @param index      register index
 * @param data       data buffer
 * @param n_data     number of byte to write
 * @return           of if ok else i2c I/O operation status
 */
static int _ExpanderWR(int I2cExpAddr, int index, uint8_t *data, int n_data) {

    int status;
    uint8_t RegAddr[0x10];
    RegAddr[0] = index;
    memcpy(RegAddr + 1, data, n_data);
    XNUCLEO53L1A1_GetI2cBus();
    status = HAL_I2C_Master_Transmit(&XNUCLEO53L1A1_hi2c, I2cExpAddr, RegAddr, n_data + 1, 100);
    XNUCLEO53L1A1_PutI2cBus();
    return status;
}

/**
 *
 * @}  XNUCLEO53L1A1_top
 */