/***************************************************************************//** * \file cy_scb_i2c.c * \version 2.80 * * Provides I2C API implementation of the SCB driver. * ******************************************************************************** * \copyright * Copyright 2016-2021 Cypress Semiconductor Corporation * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ #include "cy_device.h" #if defined (CY_IP_MXSCB) #include "cy_scb_i2c.h" #if defined(__cplusplus) extern "C" { #endif /*************************************** * Function Prototypes ***************************************/ static void SlaveHandleAddress (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void SlaveHandleDataReceive (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void SlaveHandleDataTransmit(CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void SlaveHandleStop (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void MasterHandleEvents (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void MasterHandleDataTransmit(CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void MasterHandleDataReceive (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void MasterHandleStop (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static void MasterHandleComplete (CySCB_Type *base, cy_stc_scb_i2c_context_t *context); static cy_en_scb_i2c_status_t HandleStatus(CySCB_Type *base, uint32_t status, cy_stc_scb_i2c_context_t *context); static uint32_t WaitOneUnit(uint32_t *timeout); /******************************************************************************* * Function Name: Cy_SCB_I2C_Init ****************************************************************************//** * * Initializes the SCB for the I2C operation. * * \param base * The pointer to the I2C SCB instance. * * \param config * The pointer to the configuration structure \ref cy_stc_scb_i2c_config_t. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * If SCB is already enabled, ensure that the SCB block is disabled \ref Cy_SCB_I2C_Disable * before calling this function. *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_Init(CySCB_Type *base, cy_stc_scb_i2c_config_t const *config, cy_stc_scb_i2c_context_t *context) { /* Input parameters verification */ if ((NULL == base) || (NULL == config) || (NULL == context)) { return CY_SCB_I2C_BAD_PARAM; } CY_ASSERT_L3(CY_SCB_I2C_IS_MODE_VALID(config->i2cMode)); CY_ASSERT_L2((config->useRxFifo) ? (!config->acceptAddrInFifo) : true); CY_ASSERT_L2(CY_SCB_IS_I2C_ADDR_VALID (config->slaveAddress)); CY_ASSERT_L2(CY_SCB_I2C_IS_ADDR_MASK_VALID(config->slaveAddressMask)); CY_ASSERT_L2(CY_SCB_I2C_IS_PHASE_OVERSAMPLE_VALID(config->highPhaseDutyCycle)); CY_ASSERT_L2(CY_SCB_I2C_IS_PHASE_OVERSAMPLE_VALID(config->lowPhaseDutyCycle)); /* Configure the I2C interface */ SCB_CTRL(base) = _BOOL2FLD(SCB_CTRL_ADDR_ACCEPT, config->acceptAddrInFifo) | _BOOL2FLD(SCB_CTRL_EC_AM_MODE, config->enableWakeFromSleep); #if(CY_IP_MXSCB_VERSION==1) SCB_CTRL(base) |= SCB_CTRL_BYTE_MODE_Msk; #endif /* CY_IP_MXSCB_VERSION */ SCB_I2C_CTRL(base) = _BOOL2FLD(SCB_I2C_CTRL_S_GENERAL_IGNORE, !config->ackGeneralAddr) | _VAL2FLD(SCB_I2C_CTRL_HIGH_PHASE_OVS, (config->highPhaseDutyCycle - 1U)) | _VAL2FLD(SCB_I2C_CTRL_LOW_PHASE_OVS, (config->lowPhaseDutyCycle - 1U)) | _VAL2FLD(CY_SCB_I2C_CTRL_MODE, (uint32_t) config->i2cMode); { /* Enable digital filter for only for master modes */ bool enableDigFilter = (CY_SCB_I2C_SLAVE != config->i2cMode) && (config->enableDigitalFilter); /* Configure the RX direction */ SCB_RX_CTRL(base) = _BOOL2FLD(SCB_RX_CTRL_MEDIAN, enableDigFilter) | CY_SCB_I2C_RX_CTRL; /* Configure an analog filter */ SCB_I2C_CFG(base) = (enableDigFilter) ? CY_SCB_I2C_DISABLE_ANALOG_FITLER : CY_SCB_I2C_ENABLE_ANALOG_FITLER; } SCB_RX_FIFO_CTRL(base) = (config->useRxFifo ? (CY_SCB_I2C_FIFO_SIZE - 1UL) : 0UL); /* Set the default address and mask */ SCB_RX_MATCH(base) = _VAL2FLD(SCB_RX_MATCH_ADDR, ((uint32_t) config->slaveAddress << 1UL)) | _VAL2FLD(SCB_RX_MATCH_MASK, (uint32_t) config->slaveAddressMask); /* Configure the TX direction */ SCB_TX_CTRL(base) = CY_SCB_I2C_TX_CTRL; SCB_TX_FIFO_CTRL(base) = (config->useTxFifo ? CY_SCB_I2C_HALF_FIFO_SIZE : 1UL); /* Configure interrupt sources */ SCB_INTR_SPI_EC_MASK(base) = 0UL; SCB_INTR_I2C_EC_MASK(base) = 0UL; SCB_INTR_RX_MASK(base) = 0UL; SCB_INTR_TX_MASK(base) = 0UL; SCB_INTR_M_MASK(base) = 0UL; SCB_INTR_S_MASK(base) = (CY_SCB_I2C_MASTER != config->i2cMode) ? CY_SCB_I2C_SLAVE_INTR : 0UL; /* Initialize the context */ context->useRxFifo = config->useRxFifo; context->useTxFifo = config->useTxFifo; context->state = CY_SCB_I2C_IDLE; /* Master-specific */ context->masterStatus = 0UL; context->masterBufferIdx = 0UL; /* Slave-specific */ context->slaveStatus = 0UL; context->slaveRxBufferIdx = 0UL; context->slaveRxBufferSize = 0UL; context->slaveTxBufferIdx = 0UL; context->slaveTxBufferSize = 0UL; /* Unregister callbacks */ context->cbEvents = NULL; context->cbAddr = NULL; return CY_SCB_I2C_SUCCESS; } /******************************************************************************* * Function Name: Cy_SCB_I2C_DeInit ****************************************************************************//** * * De-initializes the SCB block and returns register values to default. * * \param base * The pointer to the I2C SCB instance. * * \note * Ensure that the SCB block is disabled \ref Cy_SCB_I2C_Disable before calling this function. * *******************************************************************************/ void Cy_SCB_I2C_DeInit(CySCB_Type *base) { /* Returns block registers into the default state */ SCB_CTRL(base) = CY_SCB_CTRL_DEF_VAL; SCB_I2C_CTRL(base) = CY_SCB_I2C_CTRL_DEF_VAL; SCB_I2C_CFG(base) = CY_SCB_I2C_CFG_DEF_VAL; SCB_RX_CTRL(base) = CY_SCB_RX_CTRL_DEF_VAL; SCB_RX_FIFO_CTRL(base) = 0UL; SCB_RX_MATCH(base) = 0UL; SCB_TX_CTRL(base) = CY_SCB_TX_CTRL_DEF_VAL; SCB_TX_FIFO_CTRL(base) = 0UL; SCB_INTR_SPI_EC_MASK(base) = 0UL; SCB_INTR_I2C_EC_MASK(base) = 0UL; SCB_INTR_RX_MASK(base) = 0UL; SCB_INTR_TX_MASK(base) = 0UL; SCB_INTR_M_MASK(base) = 0UL; SCB_INTR_S_MASK(base) = 0UL; } /******************************************************************************* * Function Name: Cy_SCB_I2C_Disable ****************************************************************************//** * * Disables the SCB block and clears context statuses. * Note that after the block is disabled, the TX and RX FIFOs and hardware * statuses are cleared. Also, the hardware stops driving the output and * ignores the input. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \note * Calling this function when I2C is busy (master preforms transaction or slave * was addressed and is communicating with master) may cause transaction corruption * because the hardware stops driving the outputs and ignores the inputs. * Ensure that I2C is not busy before calling this function. * *******************************************************************************/ void Cy_SCB_I2C_Disable(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { SCB_CTRL(base) &= (uint32_t) ~SCB_CTRL_ENABLED_Msk; /* Set the state to default and clear statuses */ context->state = CY_SCB_I2C_IDLE; context->masterStatus = 0UL; context->slaveStatus = 0UL; } /******************************************************************************* * Function Name: Cy_SCB_I2C_DeepSleepCallback ****************************************************************************//** * * This function handles the transition of the I2C SCB into and out of * Deep Sleep mode. It prevents the device from entering Deep Sleep * mode if the I2C slave or master is actively communicating. * The behavior of the I2C SCB in Deep Sleep depends on whether the SCB block is * wakeup-capable or not: * * <b>Wakeup-capable</b>: during Deep Sleep mode on incoming I2C slave address * the slave receives address and stretches the clock until the device is * awoken from Deep Sleep mode. If the slave address occurs before the device * enters Deep Sleep mode, the device will not enter Deep Sleep mode. * Only the I2C slave can be configured to be a wakeup source from Deep Sleep * mode. * * <b>Not wakeup-capable</b>: the SCB is disabled in Deep Sleep mode. * It is re-enabled if the device fails to enter Deep Sleep mode or when the * device is awoken from Deep Sleep mode. While the SCB is disabled it stops * driving the outputs and ignores the inputs. The slave NACKs all incoming * addresses. * * This function must be called during execution of \ref Cy_SysPm_CpuEnterDeepSleep. * To do it, register this function as a callback before calling * \ref Cy_SysPm_CpuEnterDeepSleep : specify \ref CY_SYSPM_DEEPSLEEP as the callback * type and call \ref Cy_SysPm_RegisterCallback. * * \param callbackParams * The pointer to the callback parameters structure * \ref cy_stc_syspm_callback_params_t. * * \param mode * Callback mode, see \ref cy_en_syspm_callback_mode_t * * \return * \ref cy_en_syspm_status_t * * \note * Only applicable for <b>rev-08 of the CY8CKIT-062-BLE</b>. * For proper operation, when the I2C slave is configured to be a wakeup source * from Deep Sleep mode, this function must be copied and modified by the user. * The I2C clock disable code must be inserted in the \ref CY_SYSPM_BEFORE_TRANSITION * and clock enable code in the \ref CY_SYSPM_AFTER_TRANSITION mode processing. * *******************************************************************************/ cy_en_syspm_status_t Cy_SCB_I2C_DeepSleepCallback(cy_stc_syspm_callback_params_t *callbackParams, cy_en_syspm_callback_mode_t mode) { CySCB_Type *locBase = (CySCB_Type *) callbackParams->base; cy_stc_scb_i2c_context_t *locContext = (cy_stc_scb_i2c_context_t *) callbackParams->context; cy_en_syspm_status_t retStatus = CY_SYSPM_FAIL; switch(mode) { case CY_SYSPM_CHECK_READY: { /* Disable the slave interrupt sources to protect the state */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_CLEAR_ALL_INTR_SRC); /* If the I2C is in the IDLE state, it is ready for Deep Sleep mode * (either the master or the slave is not busy), * otherwise return fail and restore the slave interrupt sources. */ if (CY_SCB_I2C_IDLE == locContext->state) { if (_FLD2BOOL(SCB_CTRL_EC_AM_MODE, SCB_CTRL(locBase))) { /* The SCB is wakeup-capable: do not restore the address * match and general call interrupt sources. The next * transaction intended to the slave will be paused * (SCL is stretched) before the address is ACKed because * the corresponding interrupt source is disabled. */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR_NO_ADDR); } else { /* The SCB is NOT wakeup-capable: disable the I2C. The slave * stops responding to the master and the master stops * driving the bus until the I2C is enabled. This happens * when the device failed to enter into Deep Sleep mode or it * is awaken from Deep Sleep mode. */ Cy_SCB_I2C_Disable(locBase, locContext); Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR); } retStatus = CY_SYSPM_SUCCESS; } else { /* Restore the slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR); } } break; case CY_SYSPM_CHECK_FAIL: { /* The other driver is not ready for Deep Sleep mode. Restore * Active mode configuration. */ if (_FLD2BOOL(SCB_CTRL_EC_AM_MODE, SCB_CTRL(locBase))) { /* The SCB is wakeup-capable: restore the slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR); } else { /* The SCB is NOT wakeup-capable: enable the I2C to operate */ Cy_SCB_I2C_Enable(locBase); } retStatus = CY_SYSPM_SUCCESS; } break; case CY_SYSPM_BEFORE_TRANSITION: { /* This code executes inside the critical section. Enabling the * active interrupt source makes the interrupt pending in the NVIC. * However, the interrupt processing is delayed until the code exits * the critical section. The pending interrupt force WFI instruction * does nothing and the device remains in Active mode. */ if (_FLD2BOOL(SCB_CTRL_EC_AM_MODE, SCB_CTRL(locBase))) { /* The SCB is wakeup-capable: enable the I2C wakeup interrupt * source. If any transaction was paused, the I2C interrupt * becomes pending and prevents entering Deep Sleep mode. * The transaction continues as soon as the global interrupts * are enabled. */ Cy_SCB_SetI2CInterruptMask(locBase, CY_SCB_I2C_INTR_WAKEUP); /* Disable SCB clock */ SCB_I2C_CFG(locBase) &= (uint32_t) ~CY_SCB_I2C_CFG_CLK_ENABLE_Msk; /* IMPORTANT (replace line above for the CY8CKIT-062 rev-08): * for proper entering Deep Sleep mode the I2C clock must be disabled. * This code must be inserted by the user because the driver * does not have access to the clock. */ } retStatus = CY_SYSPM_SUCCESS; } break; case CY_SYSPM_AFTER_TRANSITION: { if (_FLD2BOOL(SCB_CTRL_EC_AM_MODE, SCB_CTRL(locBase))) { /* Enable SCB clock */ SCB_I2C_CFG(locBase) |= CY_SCB_I2C_CFG_CLK_ENABLE_Msk; /* IMPORTANT (replace line above for the CY8CKIT-062 rev-08): * for proper exiting Deep Sleep, the I2C clock must be enabled. * This code must be inserted by the user because the driver * does not have access to the clock. */ /* The SCB is wakeup-capable: disable the I2C wakeup interrupt * source and restore slave interrupt sources. */ Cy_SCB_SetI2CInterruptMask (locBase, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR); } else { /* The SCB is NOT wakeup-capable: enable the I2C to operate */ Cy_SCB_I2C_Enable(locBase); } retStatus = CY_SYSPM_SUCCESS; } break; default: /* Unknown state */ break; } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_HibernateCallback ****************************************************************************//** * * This function handles the transition of the I2C SCB block into Hibernate * mode. It prevents the device from entering Hibernate mode if the I2C slave or * master is actively communicating. * If the I2C is ready to enter Hibernate mode it is disabled. If the device * failed to enter Hibernate mode, the SCB is enabled. After the SCB is disabled, * it stops driving the outputs and ignores the inputs. The slave NACKs all * incoming addresses. * * This function must be called during execution of \ref Cy_SysPm_SystemEnterHibernate. * To do it, register this function as a callback before calling * \ref Cy_SysPm_SystemEnterHibernate : specify \ref CY_SYSPM_HIBERNATE as the callback * type and call \ref Cy_SysPm_RegisterCallback. * * \param callbackParams * The pointer to the callback parameters structure * \ref cy_stc_syspm_callback_params_t. * * \param mode * Callback mode, see \ref cy_en_syspm_callback_mode_t * * \return * \ref cy_en_syspm_status_t * *******************************************************************************/ cy_en_syspm_status_t Cy_SCB_I2C_HibernateCallback(cy_stc_syspm_callback_params_t *callbackParams, cy_en_syspm_callback_mode_t mode) { CySCB_Type *locBase = (CySCB_Type *) callbackParams->base; cy_stc_scb_i2c_context_t *locContext = (cy_stc_scb_i2c_context_t *) callbackParams->context; cy_en_syspm_status_t retStatus = CY_SYSPM_FAIL; switch(mode) { case CY_SYSPM_CHECK_READY: { /* Disable the slave interrupt sources to protect the state */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_CLEAR_ALL_INTR_SRC); /* If the I2C is in the IDLE state, it is ready for Hibernate mode * (either the master or the slave is not busy). * Otherwise, return fail and restore the slave interrupt sources. */ if (CY_SCB_I2C_IDLE == locContext->state) { /* Disable the I2C. The slave stops responding to the master and * the master stops driving the bus until the I2C is enabled. * This happens if the device failed to enter Hibernate mode. */ Cy_SCB_I2C_Disable(locBase, locContext); retStatus = CY_SYSPM_SUCCESS; } /* Restore the slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(locBase, CY_SCB_I2C_SLAVE_INTR); } break; case CY_SYSPM_CHECK_FAIL: { /* The other driver is not ready for Hibernate mode. Restore the * Active mode configuration. */ /* Enable the I2C to operate */ Cy_SCB_I2C_Enable(locBase); retStatus = CY_SYSPM_SUCCESS; } break; case CY_SYSPM_BEFORE_TRANSITION: case CY_SYSPM_AFTER_TRANSITION: { /* The SCB is not capable of waking up from Hibernate mode: do nothing */ retStatus = CY_SYSPM_SUCCESS; } break; default: /* Unknown state */ break; } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SetDataRate ****************************************************************************//** * * Configures the SCB to work at the desired data rate. * * \param base * The pointer to the I2C SCB instance. * * \param dataRateHz * The desired data Rate in Hz. * * \param scbClockHz * The frequency of the clock connected to the SCB in Hz. * * \return * The achieved data rate in Hz. \n * When zero value is returned there is an error in the input parameters: * data rate or clk_scb is out of valid range. * * \note * This function does not change the values of the clock divider connected * to the SCB, it changes only the SCB clock oversample registers. If this * function is not able to achieve the desired data rate, then the clock * divider must be adjusted. Call this function only while the SCB is * disabled. For the slave, this function only checks that the attached clock is * fast enough to meet the desired data rate. It does not change any registers. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SetDataRate(CySCB_Type *base, uint32_t dataRateHz, uint32_t scbClockHz) { CY_ASSERT_L2(scbClockHz > 0UL); CY_ASSERT_L2(CY_SCB_I2C_IS_DATA_RATE_VALID(dataRateHz)); uint32_t actualDataRateHz = 0UL; if (((uint32_t) CY_SCB_I2C_SLAVE) == _FLD2VAL(CY_SCB_I2C_CTRL_MODE, SCB_I2C_CTRL(base))) { actualDataRateHz = Cy_SCB_I2C_GetDataRate(base, scbClockHz); /* Use an analog filter for the slave */ SCB_RX_CTRL(base) &= (uint32_t) ~SCB_RX_CTRL_MEDIAN_Msk; SCB_I2C_CFG(base) = CY_SCB_I2C_ENABLE_ANALOG_FITLER; } else { bool errorRange = true; uint32_t sclLow; uint32_t sclHigh; uint32_t lowPhase = 8U; uint32_t highPhase = 8U; bool enableMedian = false; /* Get duration of SCL low and high for the selected data rate */ if ((0U == dataRateHz) || (dataRateHz > CY_SCB_I2C_FSTP_DATA_RATE)) { errorRange = true; } else if (dataRateHz <= CY_SCB_I2C_STD_DATA_RATE) { /* Check SCB clock ranges for Standard rate */ if ((scbClockHz >= CY_SCB_I2C_MASTER_STD_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_MASTER_STD_CLK_MAX)) { sclLow = CY_SCB_I2C_MASTER_STD_SCL_LOW; sclHigh = CY_SCB_I2C_MASTER_STD_SCL_HIGH; enableMedian = false; errorRange = false; } } else if (dataRateHz <= CY_SCB_I2C_FST_DATA_RATE) { /* Check SCB clock ranges for Fast rate */ if ((scbClockHz >= CY_SCB_I2C_MASTER_FST_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_MASTER_FST_CLK_MAX)) { sclLow = CY_SCB_I2C_MASTER_FST_SCL_LOW; sclHigh = CY_SCB_I2C_MASTER_FST_SCL_HIGH; enableMedian = false; errorRange = false; } } else { /* Check SCB clock ranges for Fast rate */ if ((scbClockHz >= CY_SCB_I2C_MASTER_FSTP_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_MASTER_FSTP_CLK_MAX)) { /* Check SCB clock ranges for Fast rate */ sclLow = CY_SCB_I2C_MASTER_FSTP_SCL_LOW; sclHigh = CY_SCB_I2C_MASTER_FSTP_SCL_HIGH; enableMedian = true; errorRange = false; } } /* Calculate data rate if data rate and clock ranges are valid */ if (!errorRange) { bool updateLowPhase; /* Get period of the SCB clock in ns */ uint32_t period = 1000000000U / scbClockHz; /* Get low phase minimum value in SCB clocks */ lowPhase = sclLow / period; if ((period * lowPhase) < sclLow) { ++lowPhase; } if (lowPhase > CY_SCB_I2C_LOW_PHASE_MAX) { lowPhase = CY_SCB_I2C_LOW_PHASE_MAX; } /* Define if update low phase */ updateLowPhase = (lowPhase < CY_SCB_I2C_LOW_PHASE_MAX); /* Get high phase minimum value in SCB clocks */ highPhase = sclHigh / period; if ((period * highPhase) < sclHigh) { ++highPhase; } if (highPhase > CY_SCB_I2C_HIGH_PHASE_MAX) { highPhase = CY_SCB_I2C_HIGH_PHASE_MAX; } /* Get actual data rate */ actualDataRateHz = scbClockHz / (lowPhase + highPhase); /* Find desired data rate */ while ((actualDataRateHz > dataRateHz) && ((lowPhase + highPhase) < CY_SCB_I2C_DUTY_CYCLE_MAX)) { /* Increase low and high phase to reach desired data rate */ if (updateLowPhase) { if (lowPhase < CY_SCB_I2C_LOW_PHASE_MAX) { /* Update low phase */ lowPhase++; updateLowPhase = false; } } else { if (highPhase < CY_SCB_I2C_HIGH_PHASE_MAX) { /* Update high phase */ highPhase++; updateLowPhase = (lowPhase < CY_SCB_I2C_LOW_PHASE_MAX); } } /* Update actual data rate */ actualDataRateHz = scbClockHz / (lowPhase + highPhase); } /* Set filter configuration based on actual data rate */ if (enableMedian) { /* Use a digital filter */ SCB_RX_CTRL(base) |= (uint32_t) SCB_RX_CTRL_MEDIAN_Msk; SCB_I2C_CFG(base) = CY_SCB_I2C_DISABLE_ANALOG_FITLER; } else { /* Use an analog filter */ SCB_RX_CTRL(base) &= (uint32_t) ~SCB_RX_CTRL_MEDIAN_Msk; SCB_I2C_CFG(base) = CY_SCB_I2C_ENABLE_ANALOG_FITLER; } /* Set phase low and high */ Cy_SCB_I2C_MasterSetLowPhaseDutyCycle (base, lowPhase); Cy_SCB_I2C_MasterSetHighPhaseDutyCycle(base, highPhase); } } return (actualDataRateHz); } /******************************************************************************* * Function Name: Cy_SCB_I2C_GetDataRate ****************************************************************************//** * * Returns the data rate for the selected SCB block. * * \param base * The pointer to the I2C SCB instance. * * \param scbClockHz * The frequency of the clock connected to the SCB in Hz. * * \return * The data rate in Hz. \n * For slave mode when zero value is returned the clk_scb is out of valid * range. * *******************************************************************************/ uint32_t Cy_SCB_I2C_GetDataRate(CySCB_Type const *base, uint32_t scbClockHz) { CY_ASSERT_L2(scbClockHz > 0UL); uint32_t actualDataRate = 0UL; if (((uint32_t) CY_SCB_I2C_SLAVE) == _FLD2VAL(CY_SCB_I2C_CTRL_MODE, SCB_I2C_CTRL(base))) { /* Check the clock frequency range to get maximum supported data rate */ if ((scbClockHz >= CY_SCB_I2C_SLAVE_FST_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_SLAVE_FST_CLK_MAX)) { actualDataRate = CY_SCB_I2C_FST_DATA_RATE; } else if ((scbClockHz >= CY_SCB_I2C_SLAVE_STD_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_SLAVE_STD_CLK_MAX)) { actualDataRate = CY_SCB_I2C_STD_DATA_RATE; } else if ((scbClockHz >= CY_SCB_I2C_SLAVE_FSTP_CLK_MIN) && (scbClockHz <= CY_SCB_I2C_SLAVE_FSTP_CLK_MAX)) { actualDataRate = CY_SCB_I2C_FSTP_DATA_RATE; } else { /* The clock frequency is too low or it gets to the gap between * Fast and Fast Plus data rates. */ actualDataRate = 0UL; } } else { if (scbClockHz > 0U) { uint32_t dutyCycle; /* Get number of clocks in one SCL period */ dutyCycle = _FLD2VAL(SCB_I2C_CTRL_LOW_PHASE_OVS, SCB_I2C_CTRL(base)) + _FLD2VAL(SCB_I2C_CTRL_HIGH_PHASE_OVS, SCB_I2C_CTRL(base)) + 2UL; /* Calculate the actual data rate */ actualDataRate = (scbClockHz / dutyCycle); } } return (actualDataRate); } /******************************************************************************* * I2C Slave API *******************************************************************************/ /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveGetStatus ****************************************************************************//** * * Returns the current I2C slave status. * This status is a bit mask and the value returned may have multiple bits set. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref group_scb_i2c_macros_slave_status. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SlaveGetStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context) { /* Suppress a compiler warning about unused variables */ (void) base; return (context->slaveStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveConfigReadBuf ****************************************************************************//** * * Configures the buffer pointer and the read buffer size. This is the buffer * from which the master reads data. After this function is called, data * transfer from the read buffer to the master is handled by * \ref Cy_SCB_I2C_Interrupt. * * When the Read transaction is completed (master generated Stop, ReStart or * error occurred), the \ref CY_SCB_I2C_SLAVE_RD_BUSY status is cleared and * the \ref CY_SCB_I2C_SLAVE_RD_CMPLT is set. Also * the \ref CY_SCB_I2C_SLAVE_RD_CMPLT_EVENT event is generated. * * \param base * The pointer to the I2C SCB instance. * * \param buffer * The pointer to the buffer with data to be read by the master. * * \param size * Size of the buffer. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \note * * The Read buffer must not be modified and stay allocated until it has been * read by the master. * * If this function has not been called, and the master tries to read data * from the slave a \ref CY_SCB_I2C_DEFAULT_TX is returned to the master. * * If the master tries to read more bytes than available in the Read buffer, * a \ref CY_SCB_I2C_SLAVE_RD_BUF_EMPTY_EVENT event occurs. The * \ref CY_SCB_I2C_DEFAULT_TX is returned to the master if the buffer remains * empty after an event notification. * *******************************************************************************/ void Cy_SCB_I2C_SlaveConfigReadBuf(CySCB_Type const *base, uint8_t *buffer, uint32_t size, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L1(CY_SCB_IS_I2C_BUFFER_VALID(buffer, size)); /* Suppress a compiler warning about unused variables */ (void) base; context->slaveTxBuffer = buffer; context->slaveTxBufferSize = size; context->slaveTxBufferIdx = 0UL; context->slaveTxBufferCnt = 0UL; } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveAbortRead ****************************************************************************//** * * Aborts the configured slave read buffer to be read by the master. * If the master reads and "abort operation" is requested, the * \ref CY_SCB_I2C_SLAVE_RD_BUF_EMPTY_EVENT event occurs. The * \ref CY_SCB_I2C_DEFAULT_TX is returned to the master if the buffer remains * empty after the event notification. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \sideeffect * If the TX FIFO is used, this function clears it. * The TX FIFO clear operation also clears the shift register, thus * the shifter can be cleared in the middle of a data element transfer, * corrupting it. The data element corruption means that all bits that have * not been transmitted are transmitted as "ones" on the bus. * *******************************************************************************/ void Cy_SCB_I2C_SlaveAbortRead(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t intrState; /* Suppress a compiler warning about unused variables */ (void) base; intrState = Cy_SysLib_EnterCriticalSection(); /* Reset index to make write buffer empty */ context->slaveTxBufferSize = 0UL; if ((context->useTxFifo) && (0UL != (CY_SCB_I2C_SLAVE_RD_BUSY & context->slaveStatus))) { /* Clear TX FIFO from available data */ Cy_SCB_ClearTxFifo(base); } Cy_SysLib_ExitCriticalSection(intrState); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveGetReadTransferCount ****************************************************************************//** * * Returns the number of bytes read by the master since the last time * \ref Cy_SCB_I2C_SlaveConfigReadBuf was called. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * The number of bytes read by the master. * * \note * * This function returns an invalid value if a read transaction was * aborted or any listed event occurs during the transaction: * \ref CY_SCB_I2C_SLAVE_ARB_LOST, \ref CY_SCB_I2C_SLAVE_BUS_ERR. * * This number is updated only when a transaction completes, either through * an error or successfully. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SlaveGetReadTransferCount(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context) { /* Suppress a compiler warning about unused variables */ (void) base; return (context->slaveTxBufferCnt); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveClearReadStatus ****************************************************************************//** * * Clears the read status and error conditions flags and returns their values. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref group_scb_i2c_macros_slave_status. * * \note * The \ref CY_SCB_I2C_SLAVE_RD_BUSY flag is not cleared. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SlaveClearReadStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t *context) { uint32_t retStatus; /* Suppress a compiler warning about unused variables */ (void) base; retStatus = (context->slaveStatus & CY_SCB_I2C_SLAVE_RD_CLEAR); context->slaveStatus &= (uint32_t) ~CY_SCB_I2C_SLAVE_RD_CLEAR; return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveConfigWriteBuf ****************************************************************************//** * * Configures the buffer pointer and size of the write buffer. This is the buffer * that the master writes data to. After this function is called data transfer * from the master into the write buffer is handled by \ref Cy_SCB_I2C_Interrupt. * * When write transaction is completed (master generated Stop, ReStart or * error occurred) the \ref CY_SCB_I2C_SLAVE_WR_BUSY status is cleared and * the \ref CY_SCB_I2C_SLAVE_WR_CMPLT is set, also * the \ref CY_SCB_I2C_SLAVE_WR_CMPLT_EVENT event is generated. * * \param base * The pointer to the I2C SCB instance. * * \param buffer * The pointer to buffer to store data written by the master. * * \param size * Size of the buffer. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \note * * The write buffer must not be modified and must stay allocated until it has been * written by the master. * * If this function has not been called and the master tries to write data, * the first byte is NAKed and discarded. * * If the master writes more bytes than the slave can store in the write buffer, * the \ref CY_SCB_I2C_SLAVE_WR_OVRFL status is set and the slave will NACK last * byte, unless the RX FIFO is used. Then the slave will NAK only after * RX FIFO becomes full. * * If the RX FIFO is used, the minimum write buffer size is automatically * the size of the RX FIFO. If a write buffer is less than the RX FIFO size, extra * bytes are ACKed and stored into RX FIFO but ignored by firmware. * *******************************************************************************/ void Cy_SCB_I2C_SlaveConfigWriteBuf(CySCB_Type const *base, uint8_t *buffer, uint32_t size, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L1(CY_SCB_IS_I2C_BUFFER_VALID(buffer, size)); /* Suppress a compiler warning about unused variables */ (void) base; context->slaveRxBuffer = buffer; context->slaveRxBufferSize = size; context->slaveRxBufferIdx = 0UL; } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveAbortWrite ****************************************************************************//** * * Aborts the configured slave write buffer to be written by the master. * If master writes and an "abort operation" is requested, the next incoming * byte will be NAKed. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \note * If the RX FIFO is used, the NAK will not be sent until RX FIFO * becomes full, however bytes accepted after an abort request are ignored. * *******************************************************************************/ void Cy_SCB_I2C_SlaveAbortWrite(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t intrState; /* Suppress a compiler warning about unused variables */ (void) base; intrState = Cy_SysLib_EnterCriticalSection(); /* Reset index to make read buffer empty */ context->slaveRxBufferSize = 0UL; if ((context->useRxFifo) && (0UL != (CY_SCB_I2C_SLAVE_WR_BUSY & context->slaveStatus))) { /* Configure to NACK when RX FIFO is full and disable RX level * interrupt sources to stop getting data from RX FIFO. */ SCB_I2C_CTRL(base) |= SCB_I2C_CTRL_S_NOT_READY_DATA_NACK_Msk; Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); } Cy_SysLib_ExitCriticalSection(intrState); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveGetWriteTransferCount ****************************************************************************//** * * Returns the number of bytes written by the master since the last time * \ref Cy_SCB_I2C_SlaveConfigWriteBuf was called. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * Number of bytes written by the master. * * \note * * This function returns an invalid value if write transaction was * aborted or any listed event occurs during the transaction: * \ref CY_SCB_I2C_SLAVE_ARB_LOST, \ref CY_SCB_I2C_SLAVE_BUS_ERR. * * This number is updated only when the transaction completes, either through * an error or successfully. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SlaveGetWriteTransferCount(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context) { /* Suppress a compiler warning about unused variables */ (void) base; return (context->slaveRxBufferIdx); } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveClearWriteStatus ****************************************************************************//** * * Clears the write status flags and error condition flags and returns their * values. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref group_scb_i2c_macros_slave_status. * * \note * The \ref CY_SCB_I2C_SLAVE_WR_BUSY flag is not cleared. * *******************************************************************************/ uint32_t Cy_SCB_I2C_SlaveClearWriteStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t *context) { uint32_t retStatus; /* Suppress a compiler warning about unused variables */ (void) base; retStatus = (context->slaveStatus & CY_SCB_I2C_SLAVE_WR_CLEAR); context->slaveStatus &= (uint32_t) ~CY_SCB_I2C_SLAVE_WR_CLEAR; return (retStatus); } /******************************************************************************* * I2C Master API: High level *******************************************************************************/ /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterGetStatus ****************************************************************************//** * * Returns the current I2C master status. * This status is a bit mask and the value returned may have multiple bits set. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref group_scb_i2c_macros_master_status. * Note that not all I2C master statuses are returned by this function. Refer to * more details of each status. * * \note * Status is cleared by calling \ref Cy_SCB_I2C_MasterRead or * \ref Cy_SCB_I2C_MasterWrite. * *******************************************************************************/ uint32_t Cy_SCB_I2C_MasterGetStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context) { /* Suppress a compiler warning about unused variables */ (void) base; return (context->masterStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterRead ****************************************************************************//** * * This function configures the master to automatically read an entire buffer * of data from the slave device. After the transaction is initiated by this * function it returns and \ref Cy_SCB_I2C_Interrupt manages further data * transfer. * * When a read transaction is completed (requested number of bytes are read or * error occurred) the \ref CY_SCB_I2C_MASTER_BUSY status is cleared and * the \ref CY_SCB_I2C_MASTER_RD_CMPLT_EVENT event is generated. * * Note that the master must read at least one byte. * * \param base * The pointer to the I2C SCB instance. * * \param xferConfig * Master transfer configuration structure * \ref cy_stc_scb_i2c_master_xfer_config_t. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * * The buffer must not be modified and must stay allocated until read operation * completion. * * * \ref Cy_SCB_I2C_MasterRead requests the SCB hardware to generate a start * condition when there is no pending transfer and returns (does not wait * until hardware generate a start condition). If the I2C bus is busy the * hardware will not generate the until bus becomes free. * The SCB hardware sets the busy status after the Start detection, and clears * it on the Stop detection. Noise caused by the ESD or other events may cause * an erroneous Start condition on the bus. Then, the master will never generate * a Start condition because the hardware assumes the bus is busy. If this occurs, * the \ref Cy_SCB_I2C_MasterGetStatus returns \ref CY_SCB_I2C_MASTER_BUSY * status and the transaction will never finish. The option is to implement a * timeout to detect the transfer completion. If the transfer never completes, * the SCB needs a reset by calling the \ref Cy_SCB_I2C_Disable and * \ref Cy_SCB_I2C_Enable functions. The \ref Cy_SCB_I2C_MasterAbortRead * function will not work, the block must be reset. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterRead(CySCB_Type *base, cy_stc_scb_i2c_master_xfer_config_t *xferConfig, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L1(xferConfig != NULL); CY_ASSERT_L1(CY_SCB_IS_BUFFER_VALID (xferConfig->buffer, xferConfig->bufferSize)); CY_ASSERT_L2(CY_SCB_IS_I2C_ADDR_VALID(xferConfig->slaveAddress)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; /* Disable I2C slave interrupt sources to protect state */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); if (0UL != (CY_SCB_I2C_IDLE_MASK & context->state)) { uint32_t intrState; /* Set address byte (bit0 = 1, read direction) */ uint32_t address = _VAL2FLD(CY_SCB_I2C_ADDRESS, xferConfig->slaveAddress) | (uint32_t) CY_SCB_I2C_READ_XFER; /* Setup context */ context->masterStatus = CY_SCB_I2C_MASTER_BUSY; context->masterBuffer = xferConfig->buffer; context->masterBufferSize = xferConfig->bufferSize; context->masterBufferIdx = 0UL; context->masterNumBytes = 0UL; context->masterPause = xferConfig->xferPending; context->masterRdDir = true; /* Clean-up hardware before transfer. Note RX FIFO is empty at here. */ Cy_SCB_ClearMasterInterrupt(base, CY_SCB_I2C_MASTER_INTR_ALL); Cy_SCB_ClearTxFifo(base); if (CY_SCB_I2C_IDLE == context->state) { /* Put the address in the TX FIFO, then generate a Start condition. * This sequence ensures that after the Start condition generation * the address is available to be sent onto the bus. */ Cy_SCB_WriteTxFifo(base, address); SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_START_ON_IDLE_Msk; } else { /* Generate a ReStart condition. * If the previous transfer was read, NACK is generated before * ReStart to complete the previous transfer. */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, SCB_I2C_STATUS(base)) ? SCB_I2C_M_CMD_M_NACK_Msk : 0UL)); /* Put address in TX FIFO */ Cy_SCB_WriteTxFifo(base, address); } /* Configure interrupt for data reception */ if ((context->useRxFifo) && (!context->masterPause) && (context->masterBufferSize >= 2UL)) { uint32_t fifoSize = CY_SCB_I2C_FIFO_SIZE; /* Enable Auto data ACK */ SCB_I2C_CTRL(base) |= SCB_I2C_CTRL_M_READY_DATA_ACK_Msk; /* Adjust level in RX FIFO */ Cy_SCB_SetRxFifoLevel(base, (context->masterBufferSize <= fifoSize) ? (context->masterBufferSize - 2UL) : ((fifoSize / 2UL) - 1UL)); context->state = CY_SCB_I2C_MASTER_RX1; } else { /* Adjust level in RX FIFO */ Cy_SCB_SetRxFifoLevel(base, 0UL); context->state = CY_SCB_I2C_MASTER_RX0; } /* Enable interrupt sources to continue transfer. * Requires critical section to not cause race condition between RX and Master * interrupt sources. */ intrState = Cy_SysLib_EnterCriticalSection(); Cy_SCB_SetRxInterruptMask (base, CY_SCB_RX_INTR_LEVEL); Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR); Cy_SysLib_ExitCriticalSection(intrState); retStatus = CY_SCB_I2C_SUCCESS; } /* Enable I2C slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_I2C_SLAVE_INTR); return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterAbortRead ****************************************************************************//** * * This function requests master to abort read operation by NAKing the next byte * and generating a Stop condition. The function does not wait until these * actions are completed. Therefore the next operation can be initiated only * after the \ref CY_SCB_I2C_MASTER_BUSY is cleared. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * ******************************************************************************/ void Cy_SCB_I2C_MasterAbortRead(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t intrState; intrState = Cy_SysLib_EnterCriticalSection(); if (0UL != (CY_SCB_I2C_MASTER_BUSY & context->masterStatus)) { /* Catch state to abort read operation */ if ((CY_SCB_I2C_MASTER_RX0 == context->state) || (CY_SCB_I2C_MASTER_RX1 == context->state)) { if (context->useRxFifo) { /* Disable RX processing */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); /* Change state to request Stop generation */ context->state = CY_SCB_I2C_MASTER_STOP; /* Enable ACK interrupt source to generate Stop after Start was generated */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR_ALL); } else { /* Reduce buffer size to minimum */ context->masterBufferSize = 1UL; } /* Cancel pending read operation if it was requested */ context->masterPause = false; } } else { /* There are two possible states when master is not busy: * CY_SCB_I2C_MASTER_WAIT and CY_SCB_I2C_IDLE. * Do nothing for CY_SCB_I2C_IDLE. */ if (CY_SCB_I2C_MASTER_WAIT == context->state) { /* Clear master previous transaction results: * - status to indicate that master is busy; * - number of bytes (only Stop is generated); * - cancel previous pending operation. */ context->masterStatus = CY_SCB_I2C_MASTER_BUSY; context->masterNumBytes = 0UL; context->masterPause = false; /* Enable master interrupt sources to catch Stop condition */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR); /* Complete transaction generating Stop */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_STOP_Msk | SCB_I2C_M_CMD_M_NACK_Msk); context->state = CY_SCB_I2C_MASTER_WAIT_STOP; } } Cy_SysLib_ExitCriticalSection(intrState); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterWrite ****************************************************************************//** * * This function configures the master to automatically write an entire buffer * of data to a slave device. After the transaction is initiated by this * function it returns and \ref Cy_SCB_I2C_Interrupt manages further data * transfer. * * When a write transaction is completed (requested number of bytes are written * or error occurred) the \ref CY_SCB_I2C_MASTER_BUSY status is cleared and * the \ref CY_SCB_I2C_MASTER_WR_CMPLT_EVENT event is generated. * * \param base * The pointer to the I2C SCB instance. * * \param xferConfig * Master transfer configuration structure * \ref cy_stc_scb_i2c_master_xfer_config_t. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * * The buffer must not be modified and must stay allocated until data has been * copied into the TX FIFO. * * * \ref Cy_SCB_I2C_MasterWrite requests the SCB hardware to generate a start * condition when there is no pending transfer and returns (does not wait * until hardware generate a start condition). If the I2C bus is busy the * hardware will not generate the until bus becomes free. * The SCB hardware sets the busy status after the Start detection, and clears * it on the Stop detection. Noise caused by the ESD or other events may cause * an erroneous Start condition on the bus. Then, the master will never generate * a Start condition because the hardware assumes the bus is busy. If this occurs, * the \ref Cy_SCB_I2C_MasterGetStatus returns \ref CY_SCB_I2C_MASTER_BUSY * status and the transaction will never finish. The option is to implement a * timeout to detect the transfer completion. If the transfer never completes, * the SCB needs a reset by calling the \ref Cy_SCB_I2C_Disable and * \ref Cy_SCB_I2C_Enable functions. The \ref Cy_SCB_I2C_MasterAbortWrite * function will not work, the block must be reset. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterWrite(CySCB_Type *base, cy_stc_scb_i2c_master_xfer_config_t *xferConfig, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L1(xferConfig != NULL); CY_ASSERT_L1(CY_SCB_IS_I2C_BUFFER_VALID(xferConfig->buffer, xferConfig->bufferSize)); CY_ASSERT_L2(CY_SCB_IS_I2C_ADDR_VALID (xferConfig->slaveAddress)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; /* Disable I2C slave interrupt sources to protect state */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); if (0UL != (CY_SCB_I2C_IDLE_MASK & context->state)) { uint32_t intrState; /* Set address byte (bit0 = 0, write direction) */ uint32_t address = _VAL2FLD(CY_SCB_I2C_ADDRESS, xferConfig->slaveAddress); /* Setup context */ context->masterStatus = CY_SCB_I2C_MASTER_BUSY; context->masterBuffer = xferConfig->buffer; context->masterBufferSize = xferConfig->bufferSize; context->masterBufferIdx = 0UL; context->masterNumBytes = 0UL; context->masterPause = xferConfig->xferPending; context->masterRdDir = false; /* Clean-up hardware before transfer. Note RX FIFO is empty at here. */ Cy_SCB_ClearMasterInterrupt(base, CY_SCB_I2C_MASTER_INTR_ALL); Cy_SCB_ClearTxFifo(base); if (CY_SCB_I2C_IDLE == context->state) { /* Put the address in the TX FIFO, then generate a Start condition. * This sequence ensures that after the Start condition generation * the address is available to be sent onto the bus. */ Cy_SCB_WriteTxFifo (base, address); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_UNDERFLOW); SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_START_ON_IDLE_Msk; } else { /* Generate a ReStart condition. * If the previous transfer was read, NACK is generated before * ReStart to complete the previous transfer. */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, SCB_I2C_STATUS(base)) ? SCB_I2C_M_CMD_M_NACK_Msk : 0UL)); if (0U == context->masterBufferSize) { /* The address is the last byte to transfer. * Put the address byte in the TX FIFO and clear the TX * Underflow interrupt source inside the critical section * to ensure that the TX Underflow interrupt will trigger * after the address byte is sent onto the bus. */ intrState = Cy_SysLib_EnterCriticalSection(); /* Put address in TX FIFO */ Cy_SCB_WriteTxFifo (base, address); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_UNDERFLOW); Cy_SysLib_ExitCriticalSection(intrState); } else { /* Put address in TX FIFO */ Cy_SCB_WriteTxFifo(base, address); } } context->state = CY_SCB_I2C_MASTER_TX; /* TX FIFO is empty. Set level to start transfer */ Cy_SCB_SetTxFifoLevel(base, (context->useTxFifo) ? CY_SCB_I2C_HALF_FIFO_SIZE : (1UL)); /* Enable interrupt sources to continue transfer. * Requires critical section to not cause race condition between TX and Master * interrupt sources. */ intrState = Cy_SysLib_EnterCriticalSection(); Cy_SCB_SetTxInterruptMask (base, CY_SCB_TX_INTR_LEVEL); Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR); Cy_SysLib_ExitCriticalSection(intrState); retStatus = CY_SCB_I2C_SUCCESS; } /* Enable I2C slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_I2C_SLAVE_INTR); return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterAbortWrite ****************************************************************************//** * * This function requests the master to abort write operation by generating a Stop * condition. The function does not wait until this action is completed. * Therefore next write operation can be initiated only after the * \ref CY_SCB_I2C_MASTER_BUSY is cleared. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \sideeffect * If the TX FIFO is used, it is cleared before Stop generation. * The TX FIFO clear operation also clears shift register. Thus, the shifter * could be cleared in the middle of a data element transfer, corrupting it. * The remaining bits to transfer within corrupted data element are * complemented with ones.\n * If the clear operation is requested while the master transmits the address, * the direction of transaction is changed to read and one byte is read * before Stop is issued. This byte is discarded. * *******************************************************************************/ void Cy_SCB_I2C_MasterAbortWrite(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t intrState; intrState = Cy_SysLib_EnterCriticalSection(); if (0UL != (CY_SCB_I2C_MASTER_BUSY & context->masterStatus)) { /* Disable TX processing */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); if (context->useTxFifo) { /* Clear TX FIFO to allow Stop generation */ Cy_SCB_ClearTxFifo(base); } if ((CY_SCB_I2C_MASTER_TX == context->state) || (CY_SCB_I2C_MASTER_TX_DONE == context->state)) { /* Change state to request Stop generation */ context->state = CY_SCB_I2C_MASTER_STOP; /* Enable ACK interrupt source to trigger Stop generation */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR_ALL); } /* Cancel pending write operation if it was requested */ context->masterPause = false; } else { /* There are two possible states when master is not busy: * CY_SCB_I2C_MASTER_WAIT and CY_SCB_I2C_IDLE. * Do nothing for CY_SCB_I2C_IDLE. */ if (CY_SCB_I2C_MASTER_WAIT == context->state) { /* Clear master previous transaction results: * - status to indicate that master is busy; * - number of bytes (only Stop is generated); * - cancel previous pending operation. */ context->masterStatus = CY_SCB_I2C_MASTER_BUSY; context->masterNumBytes = 0UL; context->masterPause = false; /* Enable master interrupt sources to catch Stop condition */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR); /* Complete transaction generating Stop */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_STOP_Msk | SCB_I2C_M_CMD_M_NACK_Msk); context->state = CY_SCB_I2C_MASTER_WAIT_STOP; } } Cy_SysLib_ExitCriticalSection(intrState); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterGetTransferCount ****************************************************************************//** * * Returns the number of bytes transferred since the last call of * \ref Cy_SCB_I2C_MasterWrite or \ref Cy_SCB_I2C_MasterRead function. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * Number of bytes read or written by the master. * * \note * * This function returns an invalid value if read or write transaction was * aborted or any listed event occurs during the transaction: * \ref CY_SCB_I2C_MASTER_ARB_LOST, \ref CY_SCB_I2C_MASTER_BUS_ERR or * \ref CY_SCB_I2C_MASTER_ABORT_START. * * * This number is updated only when the transaction completes, either through * an error or successfully. * *******************************************************************************/ uint32_t Cy_SCB_I2C_MasterGetTransferCount(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context) { /* Suppress a compiler warning about unused variables */ (void) base; return (context->masterNumBytes); } /******************************************************************************* * I2C Master API: Low level *******************************************************************************/ /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterSendStart ****************************************************************************//** * * Generates a Start condition and sends a slave address with the Read/Write bit. * This function is blocking. It does not return until the Start condition * and address byte are sent and a ACK/NAK is received, or an error or timeout * occurs. * * \param base * The pointer to the I2C SCB instance. * * \param address * 7 bit right justified slave address. * * \param bitRnW * This sets the value of the Read/Write bit in the address, thus defining * the direction of the following transfer. * See \ref cy_en_scb_i2c_direction_t for the set of constants. * * \param timeoutMs * Defines in milliseconds the time for which this function can block. * If that time expires, the function returns. If a zero is passed, * the function waits forever for the action to complete. If a timeout occurs, * the SCB block is reset. Note The maximum value is UINT32_MAX/1000. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * After a read transaction is initiated and the slave ACKs the address, at * least one byte must be read before completing the transaction or changing * its direction. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterSendStart(CySCB_Type *base, uint32_t address, cy_en_scb_i2c_direction_t bitRnW, uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L2(CY_SCB_IS_I2C_ADDR_VALID (address)); CY_ASSERT_L2(CY_SCB_I2C_IS_TIMEOUT_VALID(timeoutMs)); CY_ASSERT_L3(CY_SCB_I2C_IS_RW_BIT_VALID (bitRnW)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; /* Disable the I2C slave interrupt sources to protect the state */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); if (CY_SCB_I2C_IDLE == context->state) { uint32_t locStatus; uint32_t timeout = CY_SCB_I2C_CONVERT_TIMEOUT_TO_US(timeoutMs); /* Set the read or write direction */ context->state = CY_SCB_I2C_MASTER_ADDR; context->masterRdDir = (CY_SCB_I2C_READ_XFER == bitRnW); /* Clean up the hardware before a transfer. Note RX FIFO is empty at here */ Cy_SCB_ClearMasterInterrupt(base, CY_SCB_I2C_MASTER_INTR_ALL); Cy_SCB_ClearRxInterrupt (base, CY_SCB_RX_INTR_NOT_EMPTY); Cy_SCB_ClearTxFifo(base); /* Generate a Start and send address byte */ Cy_SCB_WriteTxFifo(base, (_VAL2FLD(CY_SCB_I2C_ADDRESS, address) | (uint32_t) bitRnW)); SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_START_ON_IDLE_Msk; /* Wait for a completion event from the master or slave */ do { locStatus = (CY_SCB_I2C_MASTER_TX_BYTE_DONE & Cy_SCB_GetMasterInterruptStatus(base)); locStatus |= (CY_SCB_I2C_SLAVE_ADDR_DONE & Cy_SCB_GetSlaveInterruptStatus(base)); locStatus |= WaitOneUnit(&timeout); } while (0UL == locStatus); /* Convert the status from register plus timeout to the return status */ retStatus = HandleStatus(base, locStatus, context); } /* Enable I2C slave interrupt sources */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_I2C_SLAVE_INTR); return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterSendReStart ****************************************************************************//** * * Generates a ReStart condition and sends a slave address with the Read/Write * bit. * This function is blocking. It does not return until the ReStart condition * and address byte are sent and an ACK/NAK is received, or an error or timeout * occurs. * * \param base * The pointer to the I2C SCB instance. * * \param address * A 7-bit right-justified slave address. * * \param bitRnW * This sets the value of the Read/Write bit in the address, thus defining * the direction of the following transfer. * See \ref cy_en_scb_i2c_direction_t for the set of constants. * * \param timeoutMs * Defines in milliseconds the time for which this function can block. * If that time expires, the function returns. If a zero is passed, * the function waits forever for the action to complete. If a timeout occurs, * the SCB block is reset. Note The maximum value is UINT32_MAX/1000. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * * A successful transaction must be initiated by \ref Cy_SCB_I2C_MasterSendStart * before calling this function. If this condition is not met, this function * does nothing and returns \ref CY_SCB_I2C_MASTER_NOT_READY. * * After a read transaction is initiated and the slave ACKs the address, * at least one byte must be read before completing the transaction or * changing its direction. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterSendReStart(CySCB_Type *base, uint32_t address, cy_en_scb_i2c_direction_t bitRnW, uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L2(CY_SCB_IS_I2C_ADDR_VALID (address)); CY_ASSERT_L2(CY_SCB_I2C_IS_TIMEOUT_VALID(timeoutMs)); CY_ASSERT_L3(CY_SCB_I2C_IS_RW_BIT_VALID (bitRnW)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; if (0UL != (CY_SCB_I2C_MASTER_ACTIVE & context->state)) { uint32_t locStatus = 0U; uint32_t timeout = CY_SCB_I2C_CONVERT_TIMEOUT_TO_US(timeoutMs); /* Set the read or write direction */ context->state = CY_SCB_I2C_MASTER_ADDR; context->masterRdDir = (CY_SCB_I2C_READ_XFER == bitRnW); /* Generate ReStart condition. * If previous transfer was read, NACK is generated before ReStart to * complete previous transfer. */ SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, SCB_I2C_STATUS(base)) ? SCB_I2C_M_CMD_M_NACK_Msk : 0UL); /* Previous transfer was a write */ if (false == _FLD2BOOL(SCB_I2C_STATUS_M_READ, SCB_I2C_STATUS(base))) { /* Cypress ID #295908: Wait until ReStart is generated to complete * the previous write transfer. This ensures that the address byte * will not be interpreted as the data byte of the previous * transfer. */ while ((0U == locStatus) && (0U != (SCB_I2C_M_CMD_M_START_Msk & SCB_I2C_M_CMD(base)))) { locStatus = WaitOneUnit(&timeout); } } /* Check for timeout and continue */ if (0U == locStatus) { /* Send the address byte */ Cy_SCB_WriteTxFifo(base, (_VAL2FLD(CY_SCB_I2C_ADDRESS, address) | (uint32_t) bitRnW)); /* Wait for a completion event from the or slave */ do { locStatus = (CY_SCB_I2C_MASTER_TX_BYTE_DONE & Cy_SCB_GetMasterInterruptStatus(base)); locStatus |= WaitOneUnit(&timeout); } while (0UL == locStatus); } /* Convert the status from register plus timeout to the return status */ retStatus = HandleStatus(base, locStatus, context); } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterSendStop ****************************************************************************//** * * Generates a Stop condition to complete the current transaction. * This function is blocking. It does not return until the Stop condition * is generated, or an error or timeout occurs. * * \param base * The pointer to the I2C SCB instance. * * \param timeoutMs * Defines in milliseconds the time for which this function can block. * If that time expires, the function returns. If a zero is passed, * the function waits forever for the action to complete. If a timeout occurs, * the SCB block is reset. Note The maximum value is UINT32_MAX/1000. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * * A successful transaction must be initiated by * \ref Cy_SCB_I2C_MasterSendStart or \ref Cy_SCB_I2C_MasterSendReStart * before calling this function. If this condition is not met, this function * does nothing and returns. * \ref CY_SCB_I2C_MASTER_NOT_READY. * * Even after the slave NAKs the address, this function must be called * to complete the transaction. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterSendStop(CySCB_Type *base,uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L2(CY_SCB_I2C_IS_TIMEOUT_VALID(timeoutMs)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; if (0UL != (CY_SCB_I2C_MASTER_ACTIVE & context->state)) { uint32_t locStatus; uint32_t timeout = CY_SCB_I2C_CONVERT_TIMEOUT_TO_US(timeoutMs); /* Generate a stop (for Write direction) and NACK plus stop for the Read direction */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_STOP_Msk | SCB_I2C_M_CMD_M_NACK_Msk); /* Wait for a completion event from the master or slave */ do { locStatus = (CY_SCB_I2C_MASTER_STOP_DONE & Cy_SCB_GetMasterInterruptStatus(base)); locStatus |= WaitOneUnit(&timeout); } while (0UL == locStatus); /* Convert the status from register plus timeout to the return status */ retStatus = HandleStatus(base, locStatus, context); } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterReadByte ****************************************************************************//** * * Reads one byte from a slave and generates an ACK or prepares to generate * a NAK. The NAK will be generated before a Stop or ReStart condition by * \ref Cy_SCB_I2C_MasterSendStop or \ref Cy_SCB_I2C_MasterSendReStart function * appropriately. * This function is blocking. It does not return until a byte is * received, or an error or timeout occurs. * * \param base * The pointer to the I2C SCB instance. * * \param ackNack * A response to a received byte. * See \ref cy_en_scb_i2c_command_t for the set of constants. * * \param byte * The pointer to the location to store the Read byte. * * \param timeoutMs * Defines in milliseconds the time for which this function can block. * If that time expires, the function returns. If a zero is passed, * the function waits forever for the action to complete. If a timeout occurs, * the SCB block is reset. Note The maximum value is UINT32_MAX/1000. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * A successful transaction must be initiated by \ref Cy_SCB_I2C_MasterSendStart * or \ref Cy_SCB_I2C_MasterSendReStart before calling this function. If this * condition is not met, this function does nothing and returns * \ref CY_SCB_I2C_MASTER_NOT_READY. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterReadByte(CySCB_Type *base, cy_en_scb_i2c_command_t ackNack, uint8_t *byte, uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L1(CY_SCB_IS_BUFFER_VALID (byte, 1UL)); CY_ASSERT_L2(CY_SCB_I2C_IS_TIMEOUT_VALID (timeoutMs)); CY_ASSERT_L3(CY_SCB_I2C_IS_RESPONSE_VALID(ackNack)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; if (CY_SCB_I2C_MASTER_RX0 == context->state) { bool rxNotEmpty; uint32_t locStatus; uint32_t timeout = CY_SCB_I2C_CONVERT_TIMEOUT_TO_US(timeoutMs); /* Wait for ACK/NAK transmission and data byte reception */ do { rxNotEmpty = (0UL != (CY_SCB_RX_INTR_NOT_EMPTY & Cy_SCB_GetRxInterruptStatus(base))); locStatus = (CY_SCB_I2C_MASTER_RX_BYTE_DONE & Cy_SCB_GetMasterInterruptStatus(base)); locStatus |= WaitOneUnit(&timeout); } while ((!rxNotEmpty) && (0UL == locStatus)); if (rxNotEmpty) { /* Get the received data byte */ *byte = (uint8_t) Cy_SCB_ReadRxFifo(base); Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_NOT_EMPTY | CY_SCB_RX_INTR_LEVEL); } /* Convert the status from register plus timeout to the return status */ retStatus = HandleStatus(base, locStatus, context); if (CY_SCB_I2C_SUCCESS == retStatus) { if (CY_SCB_I2C_ACK == ackNack) { /* Generate ACK */ SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_ACK_Msk; } else { /* NAK is generated by SendStop() or SendReStart() */ } } } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterWriteByte ****************************************************************************//** * * Sends one byte to a slave. * This function is blocking. It does not return until a byte is * transmitted, or an error or timeout occurs. * * \param base * The pointer to the I2C SCB instance. * * \param byte * The byte to write to a slave. * * \param timeoutMs * Defines in milliseconds the time for which this function can block. * If that time expires, the function returns. If a zero is passed, * the function waits forever for the action to complete. If a timeout occurs, * the SCB block is reset. Note The maximum value is UINT32_MAX/1000. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * * \note * A successful transaction must be initiated by \ref Cy_SCB_I2C_MasterSendStart * or \ref Cy_SCB_I2C_MasterSendReStart before calling this function. If this * condition is not met, this function does nothing and returns * \ref CY_SCB_I2C_MASTER_NOT_READY. * *******************************************************************************/ cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterWriteByte(CySCB_Type *base, uint8_t byte, uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context) { CY_ASSERT_L2(CY_SCB_I2C_IS_TIMEOUT_VALID(timeoutMs)); cy_en_scb_i2c_status_t retStatus = CY_SCB_I2C_MASTER_NOT_READY; if (CY_SCB_I2C_MASTER_TX == context->state) { uint32_t locStatus; uint32_t timeout = CY_SCB_I2C_CONVERT_TIMEOUT_TO_US(timeoutMs); /* Send the data byte */ Cy_SCB_WriteTxFifo(base, (uint32_t) byte); /* Wait for a completion event from the master or slave */ do { locStatus = (CY_SCB_I2C_MASTER_TX_BYTE_DONE & Cy_SCB_GetMasterInterruptStatus(base)); locStatus |= WaitOneUnit(&timeout); } while (0UL == locStatus); /* Convert the status from register plus timeout to the API status */ retStatus = HandleStatus(base, locStatus, context); } return (retStatus); } /******************************************************************************* * Function Name: Cy_SCB_I2C_Interrupt ****************************************************************************//** * * This is the interrupt function for the SCB configured in the I2C mode. * The interrupt is mandatory for I2C operation and this function must be called * inside the user-defined interrupt service. The exception is the I2C master, * which uses only the \ref group_scb_i2c_master_low_level_functions functions. * To reduce the flash consumed by the I2C driver call * \ref Cy_SCB_I2C_SlaveInterrupt when I2C mode is the slave and * \ref Cy_SCB_I2C_MasterInterrupt when I2C mode is the master. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ void Cy_SCB_I2C_Interrupt(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { if (0UL != (CY_SCB_I2C_MASTER_ACTIVE & context->state)) { /* Execute a transfer as the master */ Cy_SCB_I2C_MasterInterrupt(base, context); } else { /* Execute a transfer as the slave */ Cy_SCB_I2C_SlaveInterrupt(base, context); } } /******************************************************************************* * Function Name: Cy_SCB_I2C_SlaveInterrupt ****************************************************************************//** * * This is the interrupt function for the SCB configured in I2C mode as the * slave. This function should be called inside the user-defined interrupt * service routine to make any of the slave functions to work. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ void Cy_SCB_I2C_SlaveInterrupt(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t slaveIntrStatus; /* Handle an I2C wake-up event */ if (0UL != (CY_SCB_I2C_INTR_WAKEUP & Cy_SCB_GetI2CInterruptStatusMasked(base))) { /* Move from IDLE state, the slave was addressed. Following address match * interrupt continue transfer. */ context->state = CY_SCB_I2C_SLAVE_ACTIVE; Cy_SCB_ClearI2CInterrupt(base, CY_SCB_I2C_INTR_WAKEUP); } /* Handle the slave interrupt sources */ slaveIntrStatus = Cy_SCB_GetSlaveInterruptStatusMasked(base); /* Handle the error conditions */ if (0UL != (CY_SCB_I2C_SLAVE_INTR_ERROR & slaveIntrStatus)) { /* Update the status */ context->slaveStatus |= (0UL != (CY_SCB_SLAVE_INTR_I2C_BUS_ERROR & slaveIntrStatus)) ? CY_SCB_I2C_SLAVE_BUS_ERR : CY_SCB_I2C_SLAVE_ARB_LOST; /* Disable the RX interrupt source to drop data into RX FIFO if any */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); /* Add the stop status to back into the default state and set completion statuses */ slaveIntrStatus |= CY_SCB_SLAVE_INTR_I2C_STOP; } else { if (0UL != (CY_SCB_SLAVE_INTR_I2C_STOP & slaveIntrStatus)) { /* Get data from the RX FIFO after a stop is generated if there is * space to store it. */ if ((Cy_SCB_GetNumInRxFifo(base) > 0UL) && (context->slaveRxBufferSize > 0UL)) { Cy_SCB_SetRxInterrupt (base, CY_SCB_RX_INTR_LEVEL); Cy_SCB_SetRxInterruptMask(base, CY_SCB_RX_INTR_LEVEL); } } } /* Handle the receive direction (master writes data) */ if (0UL != (CY_SCB_RX_INTR_LEVEL & Cy_SCB_GetRxInterruptStatusMasked(base))) { SlaveHandleDataReceive(base, context); Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_LEVEL); } /* Handle the transfer completion */ if (0UL != (CY_SCB_SLAVE_INTR_I2C_STOP & slaveIntrStatus)) { SlaveHandleStop(base, context); Cy_SCB_ClearSlaveInterrupt(base, CY_SCB_SLAVE_INTR_I2C_STOP); /* Update the slave interrupt status */ slaveIntrStatus = Cy_SCB_GetSlaveInterruptStatusMasked(base); } /* Handle the address reception */ if (0UL != (CY_SCB_I2C_SLAVE_INTR_ADDR & slaveIntrStatus)) { SlaveHandleAddress(base, context); Cy_SCB_ClearI2CInterrupt(base, CY_SCB_I2C_INTR_WAKEUP); Cy_SCB_ClearSlaveInterrupt(base, CY_SCB_I2C_SLAVE_INTR_ADDR); } /* Handle the transmit direction (master reads data) */ if (0UL != (CY_SCB_I2C_SLAVE_INTR_TX & Cy_SCB_GetTxInterruptStatusMasked(base))) { SlaveHandleDataTransmit(base, context); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_LEVEL); } } /******************************************************************************* * Function Name: SlaveHandleAddress ****************************************************************************//** * * Prepares the slave for the following Read or Write transfer after the * matched address was received. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void SlaveHandleAddress(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { /* The default command is the ACK address. It can be overridden in an address callback */ cy_en_scb_i2c_command_t cmd = CY_SCB_I2C_ACK; /* The callback for the address in RX FIFO or a general call */ if (NULL != context->cbAddr) { uint32_t events = 0UL; /* Set an address in the FIFO event if the address accept is enabled */ if (_FLD2BOOL(SCB_CTRL_ADDR_ACCEPT, SCB_CTRL(base))) { events = (0UL != (CY_SCB_SLAVE_INTR_I2C_ADDR_MATCH & Cy_SCB_GetSlaveInterruptStatusMasked(base))) ? CY_SCB_I2C_ADDR_IN_FIFO_EVENT : 0UL; } /* Set a general call event if "ignore general call" is disabled */ if (!_FLD2BOOL(SCB_I2C_CTRL_S_GENERAL_IGNORE, SCB_I2C_CTRL(base))) { events |= (0UL != (CY_SCB_SLAVE_INTR_I2C_GENERAL_ADDR & Cy_SCB_GetSlaveInterruptStatusMasked(base))) ? CY_SCB_I2C_GENERAL_CALL_EVENT : 0UL; } /* Check presence of events before involve callback */ if (0UL != events) { /* Involve a callback for the address phase and get the ACK/NACK command */ cmd = context->cbAddr(events); /* Clear RX level interrupt after address reception */ Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_LEVEL); if (cmd == CY_SCB_I2C_ACK) { /* Clear the stall stop status and enable the stop interrupt source */ Cy_SCB_ClearSlaveInterrupt(base, CY_SCB_SLAVE_INTR_I2C_STOP); Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_I2C_SLAVE_INTR); } else { /* Disable the stop interrupt source */ Cy_SCB_SetSlaveInterruptMask(base, CY_SCB_I2C_SLAVE_INTR_NO_STOP); } } } /* Clear the TX FIFO before continue the transaction */ Cy_SCB_ClearTxFifo(base); /* Set the command to an ACK or NACK address */ SCB_I2C_S_CMD(base) = (cmd == CY_SCB_I2C_ACK) ? SCB_I2C_S_CMD_S_ACK_Msk : SCB_I2C_S_CMD_S_NACK_Msk; if (cmd == CY_SCB_I2C_ACK) { bool readDirection = _FLD2BOOL(SCB_I2C_STATUS_S_READ,SCB_I2C_STATUS(base)); /* Notify the user about start of transfer */ if (NULL != context->cbEvents) { context->cbEvents(readDirection ? CY_SCB_I2C_SLAVE_READ_EVENT : CY_SCB_I2C_SLAVE_WRITE_EVENT); } /* Prepare for a transfer */ if (readDirection) { context->state = CY_SCB_I2C_SLAVE_TX; context->slaveStatus |= CY_SCB_I2C_SLAVE_RD_BUSY; /* Prepare to transmit data */ context->slaveTxBufferIdx = context->slaveTxBufferCnt; context->slaveRdBufEmpty = false; Cy_SCB_SetTxInterruptMask(base, CY_SCB_TX_INTR_LEVEL); } else { uint32_t level = 0UL; context->state = CY_SCB_I2C_SLAVE_RX; context->slaveStatus |= CY_SCB_I2C_SLAVE_WR_BUSY; /* Prepare to receive data */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_RX_INTR_LEVEL); if (context->useRxFifo) { if (context->slaveRxBufferSize > 0UL) { uint32_t fifoSize = CY_SCB_I2C_FIFO_SIZE; /* ACK data automatically until RX FIFO is full */ SCB_I2C_CTRL(base) |= SCB_I2C_CTRL_S_READY_DATA_ACK_Msk; if (context->slaveRxBufferSize > fifoSize) { /* Set a level in RX FIFO to trigger the receive interrupt source */ level = (context->slaveRxBufferSize - fifoSize); level = ((level > fifoSize) ? (fifoSize / 2UL) : level) - 1UL; } else { /* Set a level in RX FIFO to read the number of bytes */ level = (context->slaveRxBufferSize - 1UL); /* NACK when RX FIFO becomes full */ SCB_I2C_CTRL(base) |= SCB_I2C_CTRL_S_NOT_READY_DATA_NACK_Msk; /* Disable the RX level interrupt and wait until RX FIFO is full or stops */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); } } } Cy_SCB_SetRxFifoLevel(base, level); } } } /******************************************************************************* * Function Name: SlaveHandleDataReceive ****************************************************************************//** * * Reads data from RX FIFO into the buffer provided by * \ref Cy_SCB_I2C_SlaveConfigWriteBuf. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void SlaveHandleDataReceive(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { /* Check whether there is space to put data */ if (context->slaveRxBufferSize > 0UL) { if (context->useRxFifo) { uint32_t level; uint32_t fifoSize = CY_SCB_I2C_FIFO_SIZE; /* Get the number of bytes to read from RX FIFO */ uint32_t numToCopy = Cy_SCB_GetRxFifoLevel(base) + 1UL; /* Get data from RX FIFO */ numToCopy = Cy_SCB_ReadArray(base, context->slaveRxBuffer, numToCopy); context->slaveRxBufferIdx += numToCopy; context->slaveRxBufferSize -= numToCopy; context->slaveRxBuffer = &context->slaveRxBuffer[numToCopy]; /* Prepare to read a next chunk of data */ if (context->slaveRxBufferSize > fifoSize) { level = context->slaveRxBufferSize - fifoSize; level = ((level > fifoSize) ? (fifoSize / 2UL) : level) - 1UL; } else { SCB_I2C_CTRL(base) |= SCB_I2C_CTRL_S_NOT_READY_DATA_NACK_Msk; level = (context->slaveRxBufferSize == 0UL) ? (0UL) : (context->slaveRxBufferSize - 1UL); Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); } /* Set the RX level to trigger an interrupt */ Cy_SCB_SetRxFifoLevel(base, level); } else { /* Continue the transfer: send an ACK */ SCB_I2C_S_CMD(base) = SCB_I2C_S_CMD_S_ACK_Msk; /* Put data into the RX buffer */ context->slaveRxBuffer[context->slaveRxBufferIdx] = (uint8_t) Cy_SCB_ReadRxFifo(base); ++context->slaveRxBufferIdx; --context->slaveRxBufferSize; } } else { /* Finish a transfer: send a NACK and discard the received byte */ SCB_I2C_S_CMD(base) = SCB_I2C_S_CMD_S_NACK_Msk; Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); } } /******************************************************************************* * Function Name: SlaveHandleDataTransmit ****************************************************************************//** * * Loads TX FIFO with data provided by \ref Cy_SCB_I2C_SlaveConfigReadBuf. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void SlaveHandleDataTransmit(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t numToCopy; /* Notify the user that there is no data to send to the master. * This event triggers once in scope of a transfer. */ if ((!context->slaveRdBufEmpty) && (0UL == context->slaveTxBufferSize)) { /* Involve a callback if registered: no data to send */ if (NULL != context->cbEvents) { context->cbEvents(CY_SCB_I2C_SLAVE_RD_BUF_EMPTY_EVENT); } /* Update the Read buffer empty status after a callback is involved */ context->slaveRdBufEmpty = (0UL == context->slaveTxBufferSize); /* Enable the TX level interrupt source to continue sending data */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_TX_INTR_LEVEL); } /* Check whether the Read buffer was updated in the callback */ if (context->slaveRdBufEmpty) { /* The Read buffer is empty: copy CY_SCB_I2C_DEFAULT_TX into TX FIFO */ numToCopy = (context->useTxFifo) ? Cy_SCB_GetFifoSize(base) : 1UL; numToCopy = Cy_SCB_WriteDefaultArray(base, CY_SCB_I2C_DEFAULT_TX, numToCopy); context->slaveTxBufferIdx += numToCopy; context->slaveStatus |= CY_SCB_I2C_SLAVE_RD_UNDRFL; } else { if (context->slaveTxBufferSize > 1UL) { /* Get the number of bytes to copy into TX FIFO */ numToCopy = (context->useTxFifo) ? (context->slaveTxBufferSize - 1UL) : (1UL); /* Write data into TX FIFO */ numToCopy = Cy_SCB_WriteArray(base, context->slaveTxBuffer, numToCopy); context->slaveTxBufferIdx += numToCopy; context->slaveTxBufferSize -= numToCopy; context->slaveTxBuffer = &context->slaveTxBuffer[numToCopy]; } /* Put the last byte */ if ((CY_SCB_I2C_FIFO_SIZE != Cy_SCB_GetNumInTxFifo(base)) && (1UL == context->slaveTxBufferSize)) { uint32_t intrStatus; /* Put the last data byte in the TX FIFO and clear the TX Underflow * interrupt source inside the critical section to ensure that the * TX Underflow interrupt will trigger after all data bytes from the * TX FIFO are transferred onto the bus. */ intrStatus = Cy_SysLib_EnterCriticalSection(); Cy_SCB_WriteTxFifo (base, (uint32_t) context->slaveTxBuffer[0UL]); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_UNDERFLOW); Cy_SysLib_ExitCriticalSection(intrStatus); /* Move the pointers */ ++context->slaveTxBufferIdx; context->slaveTxBufferSize = 0UL; context->slaveTxBuffer = &context->slaveTxBuffer[1UL]; /* Enable the TX underflow interrupt to catch when there is no data to send */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_TX_INTR_UNDERFLOW); if (context->useTxFifo) { /* Data is copied into TX FIFO */ context->slaveStatus |= CY_SCB_I2C_SLAVE_RD_IN_FIFO; /* Involve a callback if registered: data copied into TX FIFO */ if (NULL != context->cbEvents) { context->cbEvents(CY_SCB_I2C_SLAVE_RD_IN_FIFO_EVENT); } } } } } /******************************************************************************* * Function Name: SlaveHandleStop ****************************************************************************//** * * Handles transfer completion. It is triggered by a stop or restart * condition on the bus. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void SlaveHandleStop(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t locEvents; if (CY_SCB_I2C_SLAVE_RX == context->state) { /* If any data is left in RX FIFO, this is an overflow */ if (Cy_SCB_GetNumInRxFifo(base) > 0UL) { context->slaveStatus |= CY_SCB_I2C_SLAVE_WR_OVRFL; if (context->useRxFifo) { Cy_SCB_ClearRxFifo(base); } else { (void) Cy_SCB_ReadRxFifo(base); } } locEvents = (uint32_t) CY_SCB_I2C_SLAVE_WR_CMPLT_EVENT; context->slaveStatus |= (uint32_t) CY_SCB_I2C_SLAVE_WR_CMPLT; context->slaveStatus &= (uint32_t) ~CY_SCB_I2C_SLAVE_WR_BUSY; /* Clean up the RX direction */ SCB_I2C_CTRL(base) &= (uint32_t) ~(SCB_I2C_CTRL_S_READY_DATA_ACK_Msk | SCB_I2C_CTRL_S_NOT_READY_DATA_NACK_Msk); Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_LEVEL); } else { /* The number of bytes left in TX FIFO */ uint32_t size = Cy_SCB_GetNumInTxFifo(base) + Cy_SCB_GetTxSrValid(base); /* Get the number of bytes transferred from the read buffer */ context->slaveTxBufferCnt = (context->slaveTxBufferIdx - size); /* Update buffer pointer and its size if there is no overflow */ if (0UL == (CY_SCB_I2C_SLAVE_RD_UNDRFL & context->slaveStatus)) { context->slaveTxBufferSize += size; context->slaveTxBuffer -= size; } locEvents = (uint32_t) CY_SCB_I2C_SLAVE_RD_CMPLT_EVENT; context->slaveStatus |= (uint32_t) CY_SCB_I2C_SLAVE_RD_CMPLT; context->slaveStatus &= (uint32_t) ~CY_SCB_I2C_SLAVE_RD_BUSY; /* Clean up the TX direction */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); } /* Return scb into the known state after an error */ if (0UL != (CY_SCB_I2C_SLAVE_INTR_ERROR & Cy_SCB_GetSlaveInterruptStatusMasked(base))) { /* After scb IP is reset, the interrupt statuses are cleared */ Cy_SCB_FwBlockReset(base); locEvents |= CY_SCB_I2C_SLAVE_ERR_EVENT; } /* After a stop or error, set the state to idle */ context->state = CY_SCB_I2C_IDLE; /* Call a completion callback if registered */ if (NULL != context->cbEvents) { context->cbEvents(locEvents); } } /******************************************************************************* * Function Name: Cy_SCB_I2C_MasterInterrupt ****************************************************************************//** * * This is the interrupt function for the SCB configured in I2C mode as the * master. This function should be called inside the user-defined interrupt * service routine to make \ref group_scb_i2c_master_high_level_functions * functions to work. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ void Cy_SCB_I2C_MasterInterrupt(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t intrCause = Cy_SCB_GetInterruptCause(base); /* Check whether the slave is active. It can be addressed during the master set-up transfer */ if (0UL != (CY_SCB_SLAVE_INTR & intrCause)) { /* Abort the transfer due to slave operation */ if (0UL != SCB_I2C_M_CMD(base)) { SCB_I2C_M_CMD(base) = 0UL; context->masterStatus |= CY_SCB_I2C_MASTER_ABORT_START; } context->state = CY_SCB_I2C_MASTER_CMPLT; } /* Check for master error conditions */ if (0UL != (CY_SCB_MASTER_INTR & intrCause)) { MasterHandleEvents(base, context); /* Any master event does not require further TX processing */ intrCause &= (uint32_t) ~CY_SCB_TX_INTR; } if (0UL != (CY_SCB_RX_INTR & intrCause)) { MasterHandleDataReceive(base, context); Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_LEVEL); } if (0UL != (CY_SCB_TX_INTR & intrCause)) { MasterHandleDataTransmit(base, context); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_LEVEL); } /* Complete the transfer */ if (CY_SCB_I2C_MASTER_CMPLT == context->state) { MasterHandleComplete(base, context); } /* Generate stop to complete transfer */ if (CY_SCB_I2C_MASTER_STOP == context->state) { MasterHandleStop(base, context); } } /******************************************************************************* * Function Name: MasterHandleMasterEvents ****************************************************************************//** * * Reads data from RX FIFO into the buffer provided by \ref Cy_SCB_I2C_MasterRead. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \note * The master CY_SCB_MASTER_INTR_I2C_ACK interrupt source is used for Stop * generation or request to abort transfer. * *******************************************************************************/ static void MasterHandleEvents(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t masterIntrStatus = Cy_SCB_GetMasterInterruptStatusMasked(base); /* The master has not received the acknowledgment for slave */ if (0UL != (CY_SCB_MASTER_INTR_I2C_NACK & masterIntrStatus)) { /* Clear NAK interrupt source */ Cy_SCB_ClearMasterInterrupt(base, CY_SCB_MASTER_INTR_I2C_NACK); /* Update status to indicate address or data was NACKed */ context->masterStatus |= (0UL != (CY_SCB_MASTER_INTR_I2C_ACK & Cy_SCB_GetMasterInterruptStatus(base))) ? CY_SCB_I2C_MASTER_DATA_NAK : CY_SCB_I2C_MASTER_ADDR_NAK; /* Check whether Stop generation was requested before */ if (CY_SCB_I2C_MASTER_WAIT_STOP != context->state) { context->state = (context->masterPause) ? CY_SCB_I2C_MASTER_CMPLT : CY_SCB_I2C_MASTER_STOP; } } /* The master detected a bus error condition */ if (0UL != (CY_SCB_MASTER_INTR_I2C_BUS_ERROR & masterIntrStatus)) { context->masterStatus |= CY_SCB_I2C_MASTER_BUS_ERR; } /* The master detected an arbitration lost condition */ if (0UL != (CY_SCB_MASTER_INTR_I2C_ARB_LOST & masterIntrStatus)) { context->masterStatus |= CY_SCB_I2C_MASTER_ARB_LOST; } /* Complete the transfer: stop, bus error or arbitration lost */ if (0UL != (CY_SCB_I2C_MASTER_INTR_CMPLT & masterIntrStatus)) { context->state = CY_SCB_I2C_MASTER_CMPLT; } } /******************************************************************************* * Function Name: MasterHandleDataReceive ****************************************************************************//** * * Reads data from RX FIFO into the buffer provided by \ref Cy_SCB_I2C_MasterRead. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void MasterHandleDataReceive(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { switch (context->state) { case CY_SCB_I2C_MASTER_RX0: { /* Put data into the component buffer */ context->masterBuffer[0UL] = (uint8_t) Cy_SCB_ReadRxFifo(base); ++context->masterBufferIdx; --context->masterBufferSize; if (context->masterBufferSize > 0UL) { /* Continue the transaction: move pointer send an ACK */ context->masterBuffer = &context->masterBuffer[1UL]; SCB_I2C_M_CMD(base) = SCB_I2C_M_CMD_M_ACK_Msk; } else { /* Complete the transaction */ context->state = (context->masterPause) ? CY_SCB_I2C_MASTER_CMPLT : CY_SCB_I2C_MASTER_STOP; } } break; case CY_SCB_I2C_MASTER_RX1: { uint32_t numToCopied; /* Get data from RX FIFO */ numToCopied = Cy_SCB_ReadArray(base, context->masterBuffer, context->masterBufferSize); context->masterBufferIdx += numToCopied; context->masterBufferSize -= numToCopied; context->masterBuffer = &context->masterBuffer[numToCopied]; if (context->masterBufferSize < 2UL) { /* Stop ACKing data */ SCB_I2C_CTRL(base) &= (uint32_t) ~SCB_I2C_CTRL_M_READY_DATA_ACK_Msk; if (1UL == context->masterBufferSize) { /* Catch the last byte */ Cy_SCB_SetRxFifoLevel(base, 0UL); context->state = CY_SCB_I2C_MASTER_RX0; } else { /* Stop RX processing */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); context->state = CY_SCB_I2C_MASTER_STOP; } } else { uint32_t halfFifoSize = CY_SCB_I2C_HALF_FIFO_SIZE; /* Continue the transfer: Adjust the level in RX FIFO */ Cy_SCB_SetRxFifoLevel(base, (context->masterBufferSize <= halfFifoSize) ? (context->masterBufferSize - 2UL) : (halfFifoSize - 1UL)); } } break; default: /* Do nothing: drop data into RX FIFO */ break; } } /******************************************************************************* * Function Name: MasterHandleDataTransmit ****************************************************************************//** * * Loads TX FIFO with data provided by \ref Cy_SCB_I2C_MasterWrite. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void MasterHandleDataTransmit(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { if (CY_SCB_I2C_MASTER_TX_DONE == context->state) { context->state = CY_SCB_I2C_MASTER_CMPLT; } else if (CY_SCB_I2C_MASTER_TX == context->state) { if (context->masterBufferSize > 1UL) { /* Get the number of bytes to copy into TX FIFO */ uint32_t NumToCopy = (context->useTxFifo) ? (context->masterBufferSize - 1UL) : (1UL); /* Write data into TX FIFO */ NumToCopy = Cy_SCB_WriteArray(base, context->masterBuffer, NumToCopy); context->masterBufferIdx += NumToCopy; context->masterBufferSize -= NumToCopy; context->masterBuffer = &context->masterBuffer[NumToCopy]; } /* Put the last byte */ if ((CY_SCB_I2C_FIFO_SIZE != Cy_SCB_GetNumInTxFifo(base)) && (1UL == context->masterBufferSize)) { uint32_t intrStatus; /* Put the last data byte in the TX FIFO and clear the TX Underflow * interrupt source inside the critical section to ensure that the * TX Underflow interrupt will trigger after all data bytes from the * TX FIFO are transferred onto the bus. */ intrStatus = Cy_SysLib_EnterCriticalSection(); Cy_SCB_WriteTxFifo (base, (uint32_t) context->masterBuffer[0UL]); Cy_SCB_ClearTxInterrupt(base, CY_SCB_TX_INTR_UNDERFLOW); Cy_SysLib_ExitCriticalSection(intrStatus); ++context->masterBufferIdx; context->masterBufferSize = 0UL; } /* Complete the transfer */ if (0UL == context->masterBufferSize) { if (context->masterPause) { /* Wait until data is transfered onto the bus */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_TX_INTR_UNDERFLOW); context->state = CY_SCB_I2C_MASTER_TX_DONE; } else { /* Disable TX processing */ Cy_SCB_SetTxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); /* Request Stop generation */ context->state = CY_SCB_I2C_MASTER_STOP; } if (context->useTxFifo) { /* Notify the user that data is in TX FIFO */ context->masterStatus |= CY_SCB_I2C_MASTER_WR_IN_FIFO; if (NULL != context->cbEvents) { context->cbEvents(CY_SCB_I2C_MASTER_WR_IN_FIFO_EVENT); } } } } else { /* Do nothing */ } } /******************************************************************************* * Function Name: MasterHandleStop ****************************************************************************//** * * Handles the stop condition generation * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void MasterHandleStop(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { /* Stop RX and TX processing */ Cy_SCB_SetRxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_SetTxInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); if (0UL != SCB_I2C_M_CMD(base)) { /* Enable ACK interrupt source: it triggers after ACK response to * address was received. */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR_ALL); } else { /* Disable ACK interrupt source */ Cy_SCB_SetMasterInterruptMask(base, CY_SCB_I2C_MASTER_INTR); /* Complete transaction generating Stop */ SCB_I2C_M_CMD(base) = (SCB_I2C_M_CMD_M_STOP_Msk | SCB_I2C_M_CMD_M_NACK_Msk); context->state = CY_SCB_I2C_MASTER_WAIT_STOP; } } /******************************************************************************* * Function Name: MasterHandleComplete ****************************************************************************//** * * Handles the transfer completion on a stop or restart - the normal case or * completion due to an error on the bus or lost arbitration. * * \param base * The pointer to the I2C SCB instance. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * *******************************************************************************/ static void MasterHandleComplete(CySCB_Type *base, cy_stc_scb_i2c_context_t *context) { uint32_t masterIntrStatus = Cy_SCB_GetMasterInterruptStatusMasked(base); /* Clean-up hardware */ /* Disable auto data ACK option */ SCB_I2C_CTRL(base) &= (uint32_t) ~SCB_I2C_CTRL_M_READY_DATA_ACK_Msk; /* Disable the interrupt source for master operation */ Cy_SCB_SetRxInterruptMask (base, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_SetTxInterruptMask (base, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_SetMasterInterruptMask(base, CY_SCB_CLEAR_ALL_INTR_SRC); Cy_SCB_ClearMasterInterrupt(base, CY_SCB_I2C_MASTER_INTR_ALL); /* Operation complete - master is not busy anymore */ context->masterStatus &= (uint32_t) ~CY_SCB_I2C_MASTER_BUSY; /* Get number of byte transferred on the bus */ if (context->masterRdDir) { context->masterNumBytes = context->masterBufferIdx; } else { context->masterNumBytes = context->masterBufferIdx - (Cy_SCB_GetNumInTxFifo(base) + Cy_SCB_GetTxSrValid(base)); } /* Clean up after a not completed transfer */ if (0UL != (CY_SCB_I2C_MASTER_INTR_ERR & masterIntrStatus)) { /* Reset the scb IP block when: * 1. Master mode: Reset IP when arbitration is lost or a bus error occurs. * 2. Master-Slave mode: Reset IP if it is not the address phase (ACK is 0). * Otherwise, reset only on a bus error. If "lost arbitration" happens, the slave * can be addressed, so let the slave accept the address. */ bool resetIp = true; /* Check the Master-Slave address an ACK/NACK */ if (((uint32_t) CY_SCB_I2C_MASTER_SLAVE) == _FLD2VAL(CY_SCB_I2C_CTRL_MODE, SCB_I2C_CTRL(base))) { resetIp = ((0UL != (CY_SCB_MASTER_INTR_I2C_ACK & masterIntrStatus)) ? true : ((0UL != (CY_SCB_MASTER_INTR_I2C_BUS_ERROR & masterIntrStatus)) ? true : false)); } if (resetIp) { /* Reset to get it back in an known state */ Cy_SCB_FwBlockReset(base); } /* Back to the idle state. The master is not active anymore */ context->state = CY_SCB_I2C_IDLE; } else { if (context->useRxFifo) { /* Clear RX FIFO from remaining data and level interrupt source */ Cy_SCB_ClearRxFifo(base); Cy_SCB_ClearRxInterrupt(base, CY_SCB_RX_INTR_LEVEL); } context->state = (context->masterPause) ? CY_SCB_I2C_MASTER_WAIT : CY_SCB_I2C_IDLE; } /* An operation completion callback */ if (NULL != context->cbEvents) { /* Get completion events based on the hardware status */ uint32_t locEvents = context->masterRdDir ? CY_SCB_I2C_MASTER_RD_CMPLT_EVENT : CY_SCB_I2C_MASTER_WR_CMPLT_EVENT; /* Add errors if any */ locEvents |= (0UL != (CY_SCB_I2C_MASTER_ERR & context->masterStatus)) ? CY_SCB_I2C_MASTER_ERR_EVENT : 0UL; context->cbEvents(locEvents); } } /****************************************************************************** * Function Name: WaitOneUnit ****************************************************************************//** * * Waits for one unit before unblock code execution. * Note that if a timeout value is 0, this function does nothing and returns 0. * * \param timeout * The pointer to a timeout value. * * \return * Returns 0 if a timeout does not expire or the timeout mask. * *******************************************************************************/ static uint32_t WaitOneUnit(uint32_t *timeout) { uint32_t status = 0UL; /* If the timeout equal to 0. Ignore the timeout */ if (*timeout > 0UL) { Cy_SysLib_DelayUs(CY_SCB_WAIT_1_UNIT); --(*timeout); if (0UL == *timeout) { status = CY_SCB_I2C_MASTER_TIMEOUT_DONE; } } return (status); } /****************************************************************************** * Function Name: HandleStatus ****************************************************************************//** * * Converts passed status into the cy_en_scb_i2c_status_t. * * \param base * The pointer to the I2C SCB instance. * * \param status * The status to covert. * * \param context * The pointer to the context structure \ref cy_stc_scb_i2c_context_t allocated * by the user. The structure is used during the I2C operation for internal * configuration and data retention. The user must not modify anything * in this structure. * * \return * \ref cy_en_scb_i2c_status_t * *******************************************************************************/ static cy_en_scb_i2c_status_t HandleStatus(CySCB_Type *base, uint32_t status, cy_stc_scb_i2c_context_t *context) { cy_en_scb_i2c_status_t retStatus; bool resetBlock = false; /* Convert the master status to the API status */ if (0UL != (CY_SCB_I2C_MASTER_TIMEOUT_DONE & status)) { retStatus = CY_SCB_I2C_MASTER_MANUAL_TIMEOUT; resetBlock = true; } else if (0UL != (CY_SCB_I2C_SLAVE_ADDR_DONE & status)) { /* Abort the master operation, the slave was addressed first */ retStatus = CY_SCB_I2C_MASTER_MANUAL_ABORT_START; SCB_I2C_M_CMD(base) = 0UL; context->state = CY_SCB_I2C_IDLE; } else if (0UL != (CY_SCB_MASTER_INTR_I2C_BUS_ERROR & status)) { retStatus = CY_SCB_I2C_MASTER_MANUAL_BUS_ERR; resetBlock = true; } else if (0UL != (CY_SCB_MASTER_INTR_I2C_ARB_LOST & status)) { retStatus = CY_SCB_I2C_MASTER_MANUAL_ARB_LOST; if (CY_SCB_I2C_MASTER_ADDR == context->state) { /* This is the address phase: * 1. Master mode: Reset IP when "arbitration lost" occurs. * 2. Master-Slave mode: If "lost arbitration" occurs, the slave * can be addressed to let the slave accept the address; do not * reset IP. */ resetBlock = !_FLD2BOOL(SCB_I2C_CTRL_SLAVE_MODE, SCB_I2C_CTRL(base)); context->state = CY_SCB_I2C_IDLE; } else { resetBlock = true; } } else if (0UL != (CY_SCB_MASTER_INTR_I2C_NACK & status)) { /* An address or data was NAKed */ retStatus = (CY_SCB_I2C_MASTER_ADDR == context->state) ? CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK : CY_SCB_I2C_MASTER_MANUAL_NAK; } else { retStatus = CY_SCB_I2C_SUCCESS; if (0UL != (CY_SCB_MASTER_INTR_I2C_STOP & status)) { /* End of transaction, go to idle state */ context->state = CY_SCB_I2C_IDLE; } else { /* Continue transaction */ if (CY_SCB_I2C_MASTER_ADDR == context->state) { /* Switch from address to data state */ context->state = (context->masterRdDir) ? CY_SCB_I2C_MASTER_RX0 : CY_SCB_I2C_MASTER_TX; } } } if (resetBlock) { /* Back block into default state */ Cy_SCB_FwBlockReset(base); context->state = CY_SCB_I2C_IDLE; } else { Cy_SCB_ClearMasterInterrupt(base, CY_SCB_I2C_MASTER_INTR_ALL); } return (retStatus); } #if defined(__cplusplus) } #endif #endif /* CY_IP_MXSCB */ /* [] END OF FILE */