/** * @file hv_drv_GpioSmutI2c.c * @brief gpio smulitaion i2c driver layer file. * @details This file provides the following functions: \n * (1) gpio simulate i2c master tx/rx \n * (2) gpio simulate i2c init \n * * @author HiView SoC Software Team * @version 1.0.0 * @date 2023-06-06 * @copyright Copyright(c),2023-6, Hiview Software. All rights reserved. * @par History: * <table> * <tr><th>Author <th>Date <th>Change Description * </table> */ #include "Common/hv_comm_DataType.h" #include "Common/Assert/hv_comm_Assert.h" #include "hv_comm_Define.h" #include "hv_cal_Gpio.h" #include "hv_vos_Comm.h" #include "hv_drv_GpioSimI2c.h" static GpioSimI2c s_stgpiosimi2c[SIMI2CMAX-SIMI2C0] = { {INVALID_PARAM_UHCAR8, INVALID_PARAM_UHCAR8}, {INVALID_PARAM_UHCAR8, INVALID_PARAM_UHCAR8}, {INVALID_PARAM_UHCAR8, INVALID_PARAM_UHCAR8} }; #define SET_SCL_OUT(uiIndex) Hv_Cal_Gpio_SetDirection(s_stgpiosimi2c[uiIndex].ucSclPin, GPIO_DIR_OUTPUT) #define SET_SCL_IN(uiIndex) Hv_Cal_Gpio_SetDirection(s_stgpiosimi2c[uiIndex].ucSclPin, GPIO_DIR_INPUT) #define SET_SDA_OUT(uiIndex) Hv_Cal_Gpio_SetDirection(s_stgpiosimi2c[uiIndex].ucSdaPin, GPIO_DIR_OUTPUT) #define SET_SDA_IN(uiIndex) Hv_Cal_Gpio_SetDirection(s_stgpiosimi2c[uiIndex].ucSdaPin, GPIO_DIR_INPUT) #define SET_SCL_HIGH(uiIndex) Hv_Cal_Gpio_SetPinLevel(s_stgpiosimi2c[uiIndex].ucSclPin, GPIO_LEVEL_HIGH) #define SET_SCL_LOW(uiIndex) Hv_Cal_Gpio_SetPinLevel(s_stgpiosimi2c[uiIndex].ucSclPin, GPIO_LEVEL_LOW) #define SET_SDA_HIGH(uiIndex) Hv_Cal_Gpio_SetPinLevel(s_stgpiosimi2c[uiIndex].ucSdaPin, GPIO_LEVEL_HIGH) #define SET_SDA_LOW(uiIndex) Hv_Cal_Gpio_SetPinLevel(s_stgpiosimi2c[uiIndex].ucSdaPin, GPIO_LEVEL_LOW) #define GET_SDA_LEVEL(uiIndex) Hv_Cal_Gpio_GetPinLevel(s_stgpiosimi2c[uiIndex].ucSdaPin) #define GPIOSIMI2C_DELAY 2U #define GPIOSIMI2C_WAIT_ACK_TIMES 200U #define GPIOSIMI2C_ADDR_WRITE 0x00 #define GPIOSIMI2C_ADDR_READ 0x01 /** Initialize i2c * @param pstInitParam pointer to i2c configuration parameters * @return SUCCESS/FAILURE */ Status Hv_Drv_GpioSimI2c_Init(I2cBusID enBus, const GpioSimI2c *pstGpioSimI2c) { HV_ASSERT_VALID_PTR_RET(pstGpioSimI2c, HV_FAILURE); if (enBus < SIMI2C0) { return HV_FAILURE; } UINT32 uiIndex = enBus - SIMI2C0; memcpy(&s_stgpiosimi2c[uiIndex], pstGpioSimI2c, sizeof(GpioSimI2c)); SET_SCL_OUT(uiIndex); SET_SDA_OUT(uiIndex); SET_SCL_HIGH(uiIndex); SET_SDA_HIGH(uiIndex); return HV_SUCCESS; } /** I2c start signal *@SCL keep high, SDA change from high to low */ static VOID Hv_GpioSimI2c_Start(UINT32 uiIndex) { SET_SDA_OUT(uiIndex); SET_SCL_HIGH(uiIndex); SET_SDA_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SDA_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); } /** I2c stop signal *@SCL keep high, SDA change from low to high */ static VOID Hv_GpioSimI2c_Stop(UINT32 uiIndex) { SET_SDA_OUT(uiIndex); SET_SDA_LOW(uiIndex); SET_SCL_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SDA_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); } /** master wait Ack signal *@Pull up SCL high, to read SDA level high or low */ static Status Hv_GpioSimI2c_Wait_Ack(UINT32 uiIndex) { UINT32 uiWaitTime = 0; SET_SDA_IN(uiIndex); SET_SCL_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); while (GET_SDA_LEVEL(uiIndex)) { if (uiWaitTime > GPIOSIMI2C_WAIT_ACK_TIMES) { Hv_GpioSimI2c_Stop(uiIndex); HV_LOGW("wait ack timeout!\n"); return HV_FAILURE; } uiWaitTime++; } Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_LOW(uiIndex); return HV_SUCCESS; } /** send Ack signal *@SCL keep high, SDA create a valid low level */ static VOID Hv_GpioSimI2c_Send_Ack(UINT32 uiIndex) { SET_SDA_OUT(uiIndex); SET_SCL_LOW(uiIndex); SET_SDA_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_LOW(uiIndex); } /** send NAck signal *@SCL keep high, SDA create a valid high level */ static VOID Hv_GpioSimI2c_Send_Nack(UINT32 uiIndex) { SET_SDA_OUT(uiIndex); SET_SCL_LOW(uiIndex); SET_SDA_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_LOW(uiIndex); } /** send data 1 byte *@ Send data bit by bit and MSB first; *@ When SCL keep high, data is valid; *@ when SCL change to low, SDA permited to change. */ static VOID Hv_GpioSimI2c_Write_Byte(UINT32 uiIndex, UCHAR8 ucByte) { UCHAR8 ucStep = 0; SET_SDA_OUT(uiIndex); SET_SCL_LOW(uiIndex); for (ucStep = 0;ucStep < 8; ucStep++) { if ((ucByte<<ucStep)&0x80) { SET_SDA_HIGH(uiIndex); } else { SET_SDA_LOW(uiIndex); } Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); } } /** receive 1 byte *@Pull SCL high, to read SDA data from slave device bit by bit */ static UCHAR8 Hv_GpioSimI2c_Read_Byte(UINT32 uiIndex) { UCHAR8 ucByte = 0x00; UCHAR8 ucStep = 0; SET_SDA_IN(uiIndex); for (ucStep = 0; ucStep < 8; ucStep++) { SET_SCL_LOW(uiIndex); Hv_Vos_Delayus(GPIOSIMI2C_DELAY); SET_SCL_HIGH(uiIndex); ucByte = ucByte<<1; if (GET_SDA_LEVEL(uiIndex)) { ucByte = ucByte | 0x01; } Hv_Vos_Delayus(GPIOSIMI2C_DELAY); } SET_SCL_LOW(uiIndex); return ucByte; } /** Send in master mode an amount of data * @param ucDevAddr target device address * @param pucRegAddr pointer to register address * @param usRegAddrSize register address length * @param pucData pointer to data buffer to be transmited * @param usDataSize amount of data to be sent * @retval Status SUCESS/FAILURE */ Status Hv_Drv_GpioSimI2c_SendData(I2cBusID enBus, UCHAR8 ucDevAddr, UCHAR8 *pucRegAddr, USHORT16 usRegAddrSize, UCHAR8 *pcData, UINT32 uiDatasize) { if (enBus < SIMI2C0) { return HV_FAILURE; } UINT32 uiSize = 0; UINT32 uiIndex = enBus - SIMI2C0; //Start Hv_GpioSimI2c_Start(uiIndex); //Write write device addr Hv_GpioSimI2c_Write_Byte(uiIndex, ucDevAddr + GPIOSIMI2C_ADDR_WRITE); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); //Write register addr for (uiSize = 0; uiSize < usRegAddrSize; uiSize++) { Hv_GpioSimI2c_Write_Byte(uiIndex, pucRegAddr[uiSize]); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); } //Write Data for (uiSize = 0; uiSize < uiDatasize; uiSize++) { Hv_GpioSimI2c_Write_Byte(uiIndex, pcData[uiSize]); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); } //Stop Hv_GpioSimI2c_Stop(uiIndex); return HV_SUCCESS; } /** Receive in master mode an amount of data * @param ucDevAddr target device address * @param pucRegAddr pointer to register address * @param usRegAddrSize register address length * @param pucData pointer to data buffer to be transmited * @param usDataSize amount of data to be receive * @retval Status SUCESS/FAILURE */ Status Hv_Drv_GpioSimI2c_RecvData(I2cBusID enBus, UCHAR8 ucDevAddr, UCHAR8 *pucRegAddr, USHORT16 usRegAddrSize, UCHAR8 *pucData, USHORT16 usDataSize) { if (enBus < SIMI2C0) { return HV_FAILURE; } UINT32 uiSize = 0; UINT32 uiIndex = enBus - SIMI2C0; //Start Hv_GpioSimI2c_Start(uiIndex); //Write write device addr Hv_GpioSimI2c_Write_Byte(uiIndex, ucDevAddr + GPIOSIMI2C_ADDR_WRITE); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); //Write register addr for (uiSize = 0; uiSize < usRegAddrSize; uiSize++) { Hv_GpioSimI2c_Write_Byte(uiIndex, pucRegAddr[uiSize]); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); } //Start Hv_GpioSimI2c_Start(uiIndex); //Write read device addr Hv_GpioSimI2c_Write_Byte(uiIndex, ucDevAddr + GPIOSIMI2C_ADDR_READ); HV_ASSERT_SUCCESS_RET(Hv_GpioSimI2c_Wait_Ack(uiIndex), HV_FAILURE); for (uiSize = 0; uiSize < usDataSize; uiSize++) { pucData[uiSize] = Hv_GpioSimI2c_Read_Byte(uiIndex); if (usDataSize && ((usDataSize - 1) == uiSize))//last one byte, Nack { Hv_GpioSimI2c_Send_Nack(uiIndex); } else { Hv_GpioSimI2c_Send_Ack(uiIndex); } } //Stop Hv_GpioSimI2c_Stop(uiIndex); return HV_SUCCESS; }