/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    X-WL55_WLE5_53L0X.c
  * @author  Yunhorn (r) Technology Limited Application Team
  * @brief   implement X-WL55_WLE5_53L0X BSP
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 Yunhorn Technology Limited.
  * Copyright (c) 2023 Shenzhen Yunhorn Technology Co., Ltd.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */


#include <string.h>
#include  "X-WL55_WLE5_53L0X.h"

#include "stm32wlxx_hal.h"
#include "main.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




/****************************************************
 *@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;


/**
 * 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(I2C2_SCL_PORT, I2C2_SCL_PIN, GPIO_PIN_SET);
    HAL_GPIO_WritePin(I2C2_SDA_PORT, I2C2_SDA_PIN, GPIO_PIN_SET);
    GPIO_InitStruct.Pin = I2C2_SCL_PIN|I2C2_SDA_PIN ;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(I2C2_SDA_PORT, &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(I2C2_SCL_PORT, I2C2_SCL_PIN, GPIO_PIN_RESET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(I2C2_SCL_PORT, I2C2_SCL_PIN, GPIO_PIN_SET);
            HAL_Delay(1);
        }
//        if( HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9) == 0 ){
//            static int RetryRecover;
//            RetryRecover++;
//        }
    }while(HAL_GPIO_ReadPin(I2C2_SDA_PORT, I2C2_SDA_PIN) == 0 && nRetry++<7);

    if( HAL_GPIO_ReadPin(I2C2_SDA_PORT, I2C2_SDA_PIN) == 0 ){
        __GPIOA_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(LED1_GPIO_Port, &GPIO_InitStruct);
        do{
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
            HAL_Delay(33);
            HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
            HAL_Delay(33*20);
        }while(1);
    }
}



int XWL55_WLE5_53L0X_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
     *I2C2 GPIO configuration
     PA12	-------> I2C2_SCL
     PA11	-------> I2C2_SDA
     */
    GPIO_InitStruct.Pin = I2C2_SCL_PIN | I2C2_SDA_PIN;
    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(I2C2_SDA_PORT, &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 XWL55_WLE5_53L0X_SetIntrStateId(int EnableIntr, int DevNo)
{
    int status;
    IRQn_Type IntrNo;
    int IntrPin;
    switch (DevNo){
    case	1:
    	IntrNo = VL53L0X_GPIO1_L_INTx;
    	IntrPin= VL53L0X_GPIO1_L_GPIO_PIN;
    	break;
    case 	0:
    	IntrNo = VL53L0X_GPIO1_C_INTx;
    	IntrPin= VL53L0X_GPIO1_C_GPIO_PIN;
    	break;
    case 	2:
    	IntrNo = VL53L0X_GPIO1_R_INTx;
    	IntrPin= VL53L0X_GPIO1_R_GPIO_PIN;
    	break;

    default:
    	break;

    }
    /*
    IntrNo = VL53L0X_GPIO1_C_INTx;
    IntrPin= VL53L0X_GPIO1_C_GPIO_PIN;
    */
    status = 0;

    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);
    }


    return status;
}

int	XWL55_WLE5_53L0X_Init(void)
{
    int status;
    status = XWL55_WLE5_53L0X_I2C2Configure();
    return status;
}


void XWL55_WLE5_53L0X_ResetId(uint8_t DevNo, int state)
{

	switch(DevNo) {
		case XNUCLEO53L0X_LEFT:
#if	defined(TOF_2)
			HAL_GPIO_WritePin(TOF_L_XSHUT_GPIO_Port, TOF_L_XSHUT_Pin, ((state == 1)?GPIO_PIN_SET:GPIO_PIN_RESET));
#endif
		break;
#if	defined(TOF_1)
		case XNUCLEO53L0X_CENTER:
			HAL_GPIO_WritePin(TOF_C_XSHUT_GPIO_Port, TOF_C_XSHUT_Pin, ((state == 1)?GPIO_PIN_SET:GPIO_PIN_RESET));
		break;
#endif
		case XNUCLEO53L0X_RIGHT:
#if	defined(TOF_3)
			HAL_GPIO_WritePin(TOF_R_XSHUT_GPIO_Port, TOF_R_XSHUT_Pin, ((state == 1)?GPIO_PIN_SET:GPIO_PIN_RESET));
#endif
		break;

		default:
		break;
	}
}

/**
 *
 * @}  X-WL55_WLE5_53L0X_top
 */
