| /***************************************************************************//** |
| * \file cy_scb_i2c.c |
| * \version 2.20 |
| * |
| * Provides I2C API implementation of the SCB driver. |
| * |
| ******************************************************************************** |
| * \copyright |
| * Copyright 2016-2018, Cypress Semiconductor Corporation. All rights reserved. |
| * SPDX-License-Identifier: Apache-2.0 |
| |
| |
| |
| *******************************************************************************/ |
| |
| #include "cy_scb_i2c.h" |
| |
| #ifdef CY_IP_MXSCB |
| |
| #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 |
| * Ensure that the SCB block is disabled 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)); |
| |
| /* Configure the I2C interface */ |
| base->CTRL = _BOOL2FLD(SCB_CTRL_ADDR_ACCEPT, config->acceptAddrInFifo) | |
| _BOOL2FLD(SCB_CTRL_EC_AM_MODE, config->enableWakeFromSleep) | |
| SCB_CTRL_BYTE_MODE_Msk; |
| |
| base->I2C_CTRL = _BOOL2FLD(SCB_I2C_CTRL_S_GENERAL_IGNORE, !config->ackGeneralAddr) | |
| _VAL2FLD(CY_SCB_I2C_CTRL_MODE, (uint32_t) config->i2cMode); |
| |
| /* Configure the RX direction */ |
| base->RX_CTRL = CY_SCB_I2C_RX_CTRL; |
| base->RX_FIFO_CTRL = (config->useRxFifo ? (CY_SCB_I2C_FIFO_SIZE - 1UL) : 0UL); |
| |
| /* Set the default address and mask */ |
| base->RX_MATCH = _VAL2FLD(SCB_RX_MATCH_ADDR, ((uint32_t) config->slaveAddress << 1UL)) | |
| _VAL2FLD(SCB_RX_MATCH_MASK, (uint32_t) config->slaveAddressMask); |
| |
| /* Configure the TX direction */ |
| base->TX_CTRL = CY_SCB_I2C_TX_CTRL; |
| base->TX_FIFO_CTRL = (config->useTxFifo ? CY_SCB_I2C_HALF_FIFO_SIZE : 1UL); |
| |
| /* Configure interrupt sources */ |
| base->INTR_SPI_EC_MASK = 0UL; |
| base->INTR_I2C_EC_MASK = 0UL; |
| base->INTR_RX_MASK = 0UL; |
| base->INTR_TX_MASK = 0UL; |
| base->INTR_M_MASK = 0UL; |
| |
| base->INTR_S_MASK = (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 before calling this function. |
| * |
| *******************************************************************************/ |
| void Cy_SCB_I2C_DeInit(CySCB_Type *base) |
| { |
| /* Returns block registers into the default state */ |
| base->CTRL = CY_SCB_CTRL_DEF_VAL; |
| base->I2C_CTRL = CY_SCB_I2C_CTRL_DEF_VAL; |
| base->I2C_CFG = CY_SCB_I2C_CFG_DEF_VAL; |
| |
| base->RX_CTRL = CY_SCB_RX_CTRL_DEF_VAL; |
| base->RX_FIFO_CTRL = 0UL; |
| base->RX_MATCH = 0UL; |
| |
| base->TX_CTRL = CY_SCB_TX_CTRL_DEF_VAL; |
| base->TX_FIFO_CTRL = 0UL; |
| |
| base->INTR_SPI_EC_MASK = 0UL; |
| base->INTR_I2C_EC_MASK = 0UL; |
| base->INTR_RX_MASK = 0UL; |
| base->INTR_TX_MASK = 0UL; |
| base->INTR_M_MASK = 0UL; |
| base->INTR_S_MASK = 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 address and communicates 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) |
| { |
| base->CTRL &= (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 following behavior of the I2C SCB depends on whether the SCB block is |
| * wakeup-capable or not: |
| * * The SCB <b>wakeup-capable</b>: on the 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. |
| * * The SCB is <b>not wakeup-capable</b>: the I2C is disabled. It is enabled when |
| * the device failed to enter Deep Sleep mode or when it is awoken from Deep |
| * Sleep mode. While the I2C is disabled, it does 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_DeepSleep. |
| * To do it, register this function as a callback before calling |
| * \ref Cy_SysPm_DeepSleep : 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. |
| * |
| * \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) |
| { |
| 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(callbackParams->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, locBase->CTRL)) |
| { |
| /* 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, locBase->CTRL)) |
| { |
| /* 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, locBase->CTRL)) |
| { |
| /* 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 */ |
| locBase->I2C_CFG &= (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, locBase->CTRL)) |
| { |
| /* Enable SCB clock */ |
| locBase->I2C_CFG |= 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: |
| 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 I2C is enabled. After the I2C 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_Hibernate. |
| * To do it, register this function as a callback before calling |
| * \ref Cy_SysPm_Hibernate : 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. |
| * |
| * \return |
| * \ref cy_en_syspm_status_t |
| * |
| *******************************************************************************/ |
| cy_en_syspm_status_t Cy_SCB_I2C_HibernateCallback(cy_stc_syspm_callback_params_t *callbackParams) |
| { |
| 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(callbackParams->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: |
| 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. |
| * |
| * \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 actualDataRate = 0UL; |
| |
| if (((uint32_t) CY_SCB_I2C_SLAVE) == _FLD2VAL(CY_SCB_I2C_CTRL_MODE, base->I2C_CTRL)) |
| { |
| actualDataRate = Cy_SCB_I2C_GetDataRate(base, scbClockHz); |
| |
| /* Use an analog filter for the slave */ |
| base->RX_CTRL &= (uint32_t) ~SCB_RX_CTRL_MEDIAN_Msk; |
| base->I2C_CFG = CY_SCB_I2C_ENABLE_ANALOG_FITLER; |
| } |
| else |
| { |
| if ((scbClockHz > 0U) && (dataRateHz > 0U)) |
| { |
| uint32_t sclLow; |
| uint32_t sclHigh; |
| uint32_t lowPhase; |
| uint32_t highPhase; |
| |
| /* Convert SCB clock and data rate in kHz */ |
| uint32_t scbClockKHz = scbClockHz / 1000UL; |
| uint32_t dataRateKHz = dataRateHz / 1000UL; |
| |
| /* Get period of the SCB clock in ns */ |
| uint32_t period = 1000000000UL / scbClockHz; |
| |
| /* Get duration of SCL low and high for the selected data rate */ |
| if (dataRateHz <= CY_SCB_I2C_STD_DATA_RATE) |
| { |
| sclLow = CY_SCB_I2C_MASTER_STD_SCL_LOW; |
| sclHigh = CY_SCB_I2C_MASTER_STD_SCL_HIGH; |
| } |
| else if (dataRateHz <= CY_SCB_I2C_FST_DATA_RATE) |
| { |
| sclLow = CY_SCB_I2C_MASTER_FST_SCL_LOW; |
| sclHigh = CY_SCB_I2C_MASTER_FST_SCL_HIGH; |
| } |
| else |
| { |
| sclLow = CY_SCB_I2C_MASTER_FSTP_SCL_LOW; |
| sclHigh = CY_SCB_I2C_MASTER_FSTP_SCL_HIGH; |
| } |
| |
| /* Get low phase minimum value in SCB clocks */ |
| lowPhase = sclLow / period; |
| while (((period * lowPhase) < sclLow) && (lowPhase < CY_SCB_I2C_LOW_PHASE_MAX)) |
| { |
| ++lowPhase; |
| } |
| |
| /* Get high phase minimum value in SCB clocks */ |
| highPhase = sclHigh / period; |
| while (((period * highPhase) < sclHigh) && (highPhase < CY_SCB_I2C_HIGH_PHASE_MAX)) |
| { |
| ++highPhase; |
| } |
| |
| /* Get actual data rate */ |
| actualDataRate = scbClockKHz / (lowPhase + highPhase); |
| |
| uint32_t idx = 0UL; |
| while ((actualDataRate > dataRateKHz) && |
| ((lowPhase + highPhase) < CY_SCB_I2C_DUTY_CYCLE_MAX)) |
| { |
| /* Increase low and high phase to reach desired data rate */ |
| if (0UL != (idx & 0x1UL)) |
| { |
| if (highPhase < CY_SCB_I2C_HIGH_PHASE_MAX) |
| { |
| highPhase++; |
| } |
| } |
| else |
| { |
| if (lowPhase < CY_SCB_I2C_LOW_PHASE_MAX) |
| { |
| lowPhase++; |
| } |
| } |
| |
| idx++; |
| |
| /* Update actual data rate */ |
| actualDataRate = scbClockKHz / (lowPhase + highPhase); |
| } |
| |
| /* Set filter configuration based on actual data rate */ |
| if (actualDataRate > CY_SCB_I2C_FST_DATA_RATE) |
| { |
| /* Use a digital filter */ |
| base->RX_CTRL |= (uint32_t) SCB_RX_CTRL_MEDIAN_Msk; |
| base->I2C_CFG = CY_SCB_I2C_DISABLE_ANALOG_FITLER; |
| } |
| else |
| { |
| /* Use an analog filter */ |
| base->RX_CTRL &= (uint32_t) ~SCB_RX_CTRL_MEDIAN_Msk; |
| base->I2C_CFG = CY_SCB_I2C_ENABLE_ANALOG_FITLER; |
| } |
| |
| /* Set phase low and high */ |
| Cy_SCB_I2C_MasterSetLowPhaseDutyCycle (base, lowPhase); |
| Cy_SCB_I2C_MasterSetHighPhaseDutyCycle(base, highPhase); |
| |
| /* Convert actual data rate in Hz */ |
| actualDataRate = scbClockHz / (lowPhase + highPhase); |
| } |
| } |
| |
| return (actualDataRate); |
| } |
| |
| |
| /******************************************************************************* |
| * 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. |
| * |
| *******************************************************************************/ |
| 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, base->I2C_CTRL)) |
| { |
| /* 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, base->I2C_CTRL) + |
| _FLD2VAL(SCB_I2C_CTRL_HIGH_PHASE_OVS, base->I2C_CTRL) + |
| 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 is 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 "abort operation" are 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. |
| */ |
| base->I2C_CTRL |= 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 is 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 is |
| * 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. The hardware will not generate the Start while the I2C bus is busy. |
| * 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); |
| base->I2C_M_CMD = 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. |
| */ |
| base->I2C_M_CMD = (SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, base->I2C_STATUS) ? |
| 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 */ |
| base->I2C_CTRL |= 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 read 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 */ |
| base->I2C_M_CMD = (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 TX FIFO. |
| * |
| * * \ref Cy_SCB_I2C_MasterWrite requests the SCB hardware to generate a Start |
| * Condition. The hardware will not generate the Start while the I2C bus is busy. |
| * 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); |
| base->I2C_M_CMD = 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. |
| */ |
| base->I2C_M_CMD = (SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, base->I2C_STATUS) ? |
| 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 */ |
| base->I2C_M_CMD = (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)); |
| base->I2C_M_CMD = 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. |
| */ |
| base->I2C_M_CMD = SCB_I2C_M_CMD_M_START_Msk | (_FLD2BOOL(SCB_I2C_STATUS_M_READ, base->I2C_STATUS) ? |
| SCB_I2C_M_CMD_M_NACK_Msk : 0UL); |
| |
| /* Previous transfer was a write */ |
| if (false == _FLD2BOOL(SCB_I2C_STATUS_M_READ, base->I2C_STATUS)) |
| { |
| /* 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 & base->I2C_M_CMD))) |
| { |
| 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 */ |
| base->I2C_M_CMD = (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. |
| * The corresponding non-blocking function is \ref Cy_SCB_I2C_MasterRead. |
| * |
| * \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 */ |
| base->I2C_M_CMD = 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. |
| * The corresponding non-blocking function is \ref Cy_SCB_I2C_MasterWrite. |
| * |
| * \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, base->CTRL)) |
| { |
| 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, base->I2C_CTRL)) |
| { |
| 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 */ |
| base->I2C_S_CMD = (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,base->I2C_STATUS); |
| |
| /* 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 */ |
| base->I2C_CTRL |= 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 */ |
| base->I2C_CTRL |= 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 |
| { |
| base->I2C_CTRL |= 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 */ |
| base->I2C_S_CMD = 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 */ |
| base->I2C_S_CMD = 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 */ |
| base->I2C_CTRL &= (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 != base->I2C_M_CMD) |
| { |
| base->I2C_M_CMD = 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]; |
| base->I2C_M_CMD = 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 */ |
| base->I2C_CTRL &= (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 != base->I2C_M_CMD) |
| { |
| /* 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 */ |
| base->I2C_M_CMD = (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 */ |
| |
| /* Disalbe auto data ACK option */ |
| base->I2C_CTRL &= (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, base->I2C_CTRL)) |
| { |
| 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. |
| * |
| * \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; |
| |
| base->I2C_M_CMD = 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, base->I2C_CTRL); |
| |
| 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 trasaction */ |
| 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 */ |
| |