Driver API for I2C Bus Peripheral.
I2C - The Inter-Integrated Circuit (I2C) bus is an industry-standard.
The functions and other declarations used in this part of the driver are in cy_scb_i2c.h. You can also include cy_pdl.h to get access to all functions and declarations in the PDL.
The I2C peripheral driver provides an API to implement I2C slave, master, or master-slave devices based on the SCB hardware block. I2C devices based on SCB hardware are compatible with I2C Standard-mode, Fast-mode, and Fast-mode Plus specifications as defined in the I2C-bus specification.
Features:
- An industry-standard I2C bus interface
- Supports slave, master, and master-slave operation
- Supports standard data rates of 100/400/1000 kbps
- Hardware Address Match, multiple addresses
- Wake from Deep Sleep on Address Match
- Note
- I2C supports clock stretching. This occurs when a slave device is not capable of processing data, it holds the SCL line by driving a '0'. The master device monitors the SCL line and detects it when it cannot generate a positive clock pulse ('1') on the SCL line. It then reacts by delaying the generation of a positive edge on the SCL line, effectively synchronizing with the slave device that is stretching the clock. Clock stretching can occur in the case of externally clocked address matching until the internally clocked logic takes over. The largest reason for clock stretching is when the master tries to write to the slave and the slave's RX FIFO is full, the slave will then clock stretch until the FIFO is no longer full. For more information on FIFO size and clock stretching see the architecture TRM.
Configuration Considerations
The I2C driver configuration can be divided to number of sequential steps listed below:
- Note
- I2C driver is built on top of the SCB hardware block. The SCB3 instance is used as an example for all code snippets. Modify the code to match your design.
Configure I2C
To set up the I2C driver, provide the configuration parameters in the cy_stc_scb_i2c_config_t structure. Provide i2cMode to the select operation mode slave, master or master-slave. The useRxFifo and useTxFifo parameters specify if RX and TX FIFO is used during operation. Typically, both FIFOs should be enabled to reduce possibility of clock stretching. However, using RX FIFO has side effects that needs to be taken into account (see useRxFifo field description in cy_stc_scb_i2c_config_t structure). For master modes, parameters lowPhaseDutyCycle, highPhaseDutyCycle and enableDigitalFilter can be used to define output data rate (refer to section Configure Data Rate for more information). For slave mode, provide the slaveAddress and slaveAddressMask. The other parameters are optional for operation.
To initialize the driver, call Cy_SCB_I2C_Init function providing a pointer to the populated cy_stc_scb_i2c_config_t structure and the allocated cy_stc_scb_i2c_context_t structure.
#if (USE_I2C_SLAVE)
{
.useRxFifo = false,
.useTxFifo = true,
.slaveAddress = 0x08U,
.slaveAddressMask = 0xFEU,
.acceptAddrInFifo = false,
.ackGeneralAddr = false,
.enableWakeFromSleep = false,
.enableDigitalFilter = false,
.lowPhaseDutyCycle = 0U,
.highPhaseDutyCycle = 0U,
};
#else
{
.useRxFifo = false,
.useTxFifo = true,
.slaveAddress = 0U,
.slaveAddressMask = 0U,
.acceptAddrInFifo = false,
.ackGeneralAddr = false,
.enableWakeFromSleep = false,
.enableDigitalFilter = false,
.lowPhaseDutyCycle = 8U,
.highPhaseDutyCycle = 8U,
};
#endif
cy_en_scb_i2c_mode_t i2cMode
Specifies the mode of operation.
Definition: cy_scb_i2c.h:439
I2C configuration structure.
Definition: cy_scb_i2c.h:437
I2C context structure.
Definition: cy_scb_i2c.h:562
@ CY_SCB_I2C_SLAVE
Configures SCB for I2C Slave operation.
Definition: cy_scb_i2c.h:387
@ CY_SCB_I2C_MASTER
Configures SCB for I2C Master operation.
Definition: cy_scb_i2c.h:388
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)
Initializes the SCB for the I2C operation.
Definition: cy_scb_i2c.c:80
Set up I2C slave read and write buffer before enabling its operation using Cy_SCB_I2C_SlaveConfigReadBuf and Cy_SCB_I2C_SlaveConfigWriteBuf appropriately. Note that the master reads data from the slave read buffer and writes data into the slave write buffer.
#define BUFFER_SIZE (128UL)
uint8_t i2cReadBuffer[BUFFER_SIZE];
uint8_t i2cWriteBuffer[BUFFER_SIZE];
void Cy_SCB_I2C_SlaveConfigReadBuf(CySCB_Type const *base, uint8_t *buffer, uint32_t size, cy_stc_scb_i2c_context_t *context)
Configures the buffer pointer and the read buffer size.
Definition: cy_scb_i2c.c:840
void Cy_SCB_I2C_SlaveConfigWriteBuf(CySCB_Type const *base, uint8_t *buffer, uint32_t size, cy_stc_scb_i2c_context_t *context)
Configures the buffer pointer and size of the write buffer.
Definition: cy_scb_i2c.c:1019
Assign and Configure Pins
Only dedicated SCB pins can be used for I2C operation. The HSIOM register must be configured to connect dedicated SCB I2C pins to the SCB block. Also the I2C pins must be configured in Open-Drain, Drives Low mode (this pins configuration implies usage of external pull-up resistors):
#define I2C_PORT (P8_0_PORT)
#define I2C_SCL_NUM (P8_0_NUM)
#define I2C_SDA_NUM (P8_1_NUM)
#define CY_GPIO_DM_OD_DRIVESLOW
Open Drain, Drives Low.
Definition: cy_gpio.h:518
__STATIC_INLINE void Cy_GPIO_SetDrivemode(GPIO_PRT_Type *base, uint32_t pinNum, uint32_t value)
Configures the pin output buffer drive mode and input buffer enable.
Definition: cy_gpio.h:1284
__STATIC_INLINE void Cy_GPIO_SetHSIOM(GPIO_PRT_Type *base, uint32_t pinNum, en_hsiom_sel_t value)
Configures the HSIOM connection to the pin.
Definition: cy_gpio.h:923
- Note
- The alternative pins configuration is Resistive Pull-ups which implies usage internal pull-up resistors. This configuration is not recommended because resistor value is fixed and cannot be used for all supported data rates. Refer to the device datasheet parameter RPULLUP for resistor value specifications.
Assign Clock Divider
A clock source must be connected to the SCB block to oversample input and output signals, in this document this clock will be referred as clk_scb. You must use one of the 8-bit or 16-bit dividers. Use the SysClk (System Clock) driver API to do this.
#define I2C_CLK_DIV_TYPE (CY_SYSCLK_DIV_8_BIT)
#define I2C_CLK_DIV_NUMBER (0U)
cy_en_sysclk_status_t Cy_SysClk_PeriphAssignDivider(en_clk_dst_t ipBlock, cy_en_divider_types_t dividerType, uint32_t dividerNum)
Assigns a programmable divider to a selected IP block, such as a TCPWM or SCB.
Definition: cy_sysclk_v2.c:2175
Configure Data Rate
To get I2C slave operation with the desired data rate, the clk_scb must be fast enough to provide sufficient oversampling. Use the SysClk (System Clock) driver API to do this.
cy_en_sysclk_status_t Cy_SysClk_PeriphEnableDivider(cy_en_divider_types_t dividerType, uint32_t dividerNum)
Enables the selected divider.
Definition: cy_sysclk_v2.c:2189
cy_en_sysclk_status_t Cy_SysClk_PeriphSetDivider(cy_en_divider_types_t dividerType, uint32_t dividerNum, uint32_t dividerValue)
Sets one of the programmable clock dividers.
Definition: cy_sysclk_v2.c:2142
To get I2C master operation with the desired data rate, the source clock frequency and SCL low and high phase duration must be configured. Use the SysClk (System Clock) driver API to configure source clock frequency. Then call Cy_SCB_I2C_SetDataRate to set the SCL low, high phase duration and digital filter. This function sets SCL low and high phase settings based on source clock frequency.
uint32_t dataRate;
#define I2C_DESIRED_DATA_RATE_HZ (100000U)
if ((dataRate > I2C_DESIRED_DATA_RATE_HZ) || (dataRate == 0U))
{
CY_ASSERT(0U);
}
uint32_t Cy_SCB_I2C_SetDataRate(CySCB_Type *base, uint32_t dataRateHz, uint32_t scbClockHz)
Configures the SCB to work at the desired data rate.
Definition: cy_scb_i2c.c:541
uint32_t Cy_SysClk_PeriphGetFrequency(cy_en_divider_types_t dividerType, uint32_t dividerNum)
Reports the frequency of the output of a given peripheral divider.
Definition: cy_sysclk_v2.c:2132
Alternatively, the low, high phase and digital filter can be set directly using configuration structure cy_stc_scb_i2c_config_t fields lowPhaseDutyCycle, highPhaseDutyCycle and enableDigitalFilter appropriately.
Refer to the technical reference manual (TRM) section I2C sub-section Oversampling and Bit Rate to get information how to configure I2C to run with the desired data rate.
- Note
- For I2C slave, the analog filter is used for all supported data rates.
For I2C master, the analog filter is used for Standard and Fast modes and the digital filter for Fast Plus mode.
Configure Interrupt
The interrupt is mandatory for I2C operation. The exception is the when only the Master Low-Level functions are used. The driver provides three interrupt functions: Cy_SCB_I2C_Interrupt, Cy_SCB_I2C_SlaveInterrupt, and Cy_SCB_I2C_MasterInterrupt. One of these functions must be called in the interrupt handler for the selected SCB instance. Call Cy_SCB_I2C_SlaveInterrupt when I2C is configured to operate as a slave, Cy_SCB_I2C_MasterInterrupt when I2C is configured to operate as a master and Cy_SCB_I2C_Interrupt when I2C is configured to operate as master and slave. Using the slave- or master-specific interrupt function allows reducing the flash consumed by the I2C driver. Also this interrupt must be enabled in the NVIC otherwise it will not work.
- Note
- The I2C driver documentation refers to the Cy_SCB_I2C_Interrupt function when interrupt processing is mandatory for the operation. This is done to simplify the readability of the driver's documentation. The application should call the slave- or master-specific interrupt functions Cy_SCB_I2C_SlaveInterrupt or Cy_SCB_I2C_MasterInterrupt, when appropriate.
void I2C_Isr(void)
{
}
void Cy_SCB_I2C_Interrupt(CySCB_Type *base, cy_stc_scb_i2c_context_t *context)
This is the interrupt function for the SCB configured in the I2C mode.
Definition: cy_scb_i2c.c:2161
#define I2C_INTR_NUM scb_3_interrupt_IRQn
#define I2C_INTR_PRIORITY (7UL)
{
.intrPriority = I2C_INTR_PRIORITY,
};
NVIC_EnableIRQ(I2C_INTR_NUM);
IRQn_Type intrSrc
Interrupt source.
Definition: cy_sysint.h:227
Initialization configuration structure for a single interrupt channel.
Definition: cy_sysint.h:225
cy_en_sysint_status_t Cy_SysInt_Init(const cy_stc_sysint_t *config, cy_israddress userIsr)
Initializes the referenced interrupt by setting the priority and the interrupt vector.
Definition: cy_sysint_v2.c:80
Enable I2C
Finally, enable the I2C operation by calling Cy_SCB_I2C_Enable. Then I2C slave starts respond to the assigned address and I2C master ready to execute transfers.
__enable_irq();
__STATIC_INLINE void Cy_SCB_I2C_Enable(CySCB_Type *base)
Enables the SCB block for the I2C operation.
Definition: cy_scb_i2c.h:1139
Common Use Cases
Master Operation
The master API is divided into two categories: Master High-Level and Master Low-Level. Therefore, there are two methods for initiating I2C master transactions using either Low-Level or High-Level API. These two methods are described below. Only one method should be used at a time. They should not be mixed.
Use High-Level Functions
Call Cy_SCB_I2C_MasterRead or Cy_SCB_I2C_MasterWrite to communicate with the slave. These functions do not block and only start a transaction. After a transaction starts, the Cy_SCB_I2C_Interrupt handles further data transaction until its completion (successfully or with error occurring). To monitor the transaction, use Cy_SCB_I2C_MasterGetStatus or register callback function using Cy_SCB_I2C_RegisterEventCallback to be notified about I2C Callback Events.
uint8_t readBuffer [5UL];
uint8_t writeBuffer[2UL] = {0U, 5U};
transfer.
buffer = writeBuffer;
{
}
{
}
uint8_t * buffer
The pointer to the buffer for data to read from the slave or data to write into the slave.
Definition: cy_scb_i2c.h:616
bool xferPending
The transfer operation is pending - the stop condition will not be generated.
Definition: cy_scb_i2c.h:635
uint32_t bufferSize
The size of the buffer.
Definition: cy_scb_i2c.h:619
uint8_t slaveAddress
The 7-bit right justified slave address to communicate with.
Definition: cy_scb_i2c.h:610
The I2C Master transfer structure.
Definition: cy_scb_i2c.h:608
#define CY_SCB_I2C_MASTER_BUSY
The master is busy executing operation started by Cy_SCB_I2C_MasterRead or Cy_SCB_I2C_MasterWrite.
Definition: cy_scb_i2c.h:822
uint32_t Cy_SCB_I2C_MasterGetStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context)
Returns the current I2C master status.
Definition: cy_scb_i2c.c:1184
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)
This function configures the master to automatically read an entire buffer of data from the slave dev...
Definition: cy_scb_i2c.c:1244
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)
This function configures the master to automatically write an entire buffer of data to a slave device...
Definition: cy_scb_i2c.c:1472
Use Low-Level Functions
Call Cy_SCB_I2C_MasterSendStart to generate a start, send an address with the Read/Write direction bit, and receive acknowledgment. After the address is ACKed by the slave, the transaction can be continued by calling Cy_SCB_I2C_MasterReadByte or Cy_SCB_I2C_MasterWriteByte depending on its direction. These functions handle one byte per call. Therefore, they should be called for each byte in the transaction. Note that for the Read transaction, the last byte must be NAKed. To complete the current transaction, call Cy_SCB_I2C_MasterSendStop or call Cy_SCB_I2C_MasterSendReStart to complete the current transaction and start a new one. Typically, do a restart to change the transaction direction without releasing the bus from the master control. The Low-Level functions are blocking and do not require calling Cy_SCB_I2C_Interrupt inside the interrupt handler. Using these functions requires extensive knowledge of the I2C protocol to execute transactions correctly.
Master Write Operation
uint8_t buffer[] = {0x55, 0x44, 0x33};
uint32_t timeout = 100UL;
{
uint32_t cnt = 0UL;
do
{
++cnt;
}
}
{
{
}
}
else
{
}
cy_en_scb_i2c_status_t
I2C status codes.
Definition: cy_scb_i2c.h:332
@ CY_SCB_I2C_WRITE_XFER
Current transaction is Write.
Definition: cy_scb_i2c.h:395
@ CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK
The slave NACKed the address.
Definition: cy_scb_i2c.h:355
@ CY_SCB_I2C_SUCCESS
Operation completed successfully.
Definition: cy_scb_i2c.h:334
@ CY_SCB_I2C_MASTER_MANUAL_NAK
The slave NACKed the data byte.
Definition: cy_scb_i2c.h:360
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)
Generates a Start condition and sends a slave address with the Read/Write bit.
Definition: cy_scb_i2c.c:1744
cy_en_scb_i2c_status_t Cy_SCB_I2C_MasterSendStop(CySCB_Type *base, uint32_t timeoutMs, cy_stc_scb_i2c_context_t *context)
Generates a Stop condition to complete the current transaction.
Definition: cy_scb_i2c.c:1942
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)
Sends one byte to a slave.
Definition: cy_scb_i2c.c:2108
Master Read Operation
uint8_t buffer[5U];
uint32_t timeout = 100UL;
{
uint32_t cnt = 0UL;
do
{
if (cnt == (sizeof(buffer) - 1UL))
{
}
++cnt;
}
}
{
{
}
}
else
{
}
cy_en_scb_i2c_command_t
I2C Command ACK / NAK.
Definition: cy_scb_i2c.h:401
@ CY_SCB_I2C_NAK
Send NAK to current byte.
Definition: cy_scb_i2c.h:403
@ CY_SCB_I2C_ACK
Send ACK to current byte.
Definition: cy_scb_i2c.h:402
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)
Reads one byte from a slave and generates an ACK or prepares to generate a NAK.
Definition: cy_scb_i2c.c:2016
Slave Operation
Slave operation requires the Cy_SCB_I2C_Interrupt be called inside the interrupt handler. The read and write buffers must be provided for the slave to enable communication with the master. Use Cy_SCB_I2C_SlaveConfigReadBuf and Cy_SCB_I2C_SlaveConfigWriteBuf for this purpose. Note that after transaction completion the buffer must be configured again. Otherwise, the same buffer is used starting from the point where the master stopped a previous transaction. For example: The read buffer is configured to be 10 bytes and the master reads 8 bytes. If the read buffer is not configured again, the next master read will start from the 9th byte. To monitor the transaction status, use Cy_SCB_I2C_SlaveGetStatus or use Cy_SCB_I2C_RegisterEventCallback to register a callback function to be notified about I2C Callback Events.
Get Slave Events Notification
__STATIC_INLINE void Cy_SCB_I2C_RegisterEventCallback(CySCB_Type const *base, cy_cb_scb_i2c_handle_events_t callback, cy_stc_scb_i2c_context_t *context)
Registers a callback function that notifies that I2C Callback Events occurred in the Cy_SCB_I2C_Inter...
Definition: cy_scb_i2c.h:1345
void I2C_SlaveEventHandler(uint32_t events)
{
{
{
}
}
{
{
}
}
}
#define CY_SCB_I2C_SLAVE_ERR_EVENT
Indicates the I2C hardware detected an error.
Definition: cy_scb_i2c.h:900
#define CY_SCB_I2C_SLAVE_WR_CMPLT_EVENT
Indicates the master completed writing to the slave (set by the master Stop or Restart)
Definition: cy_scb_i2c.h:894
#define CY_SCB_I2C_SLAVE_RD_CMPLT_EVENT
Indicates the master completed reading from the slave (set by the master NAK or Stop)
Definition: cy_scb_i2c.h:888
Polling Slave Completion Events
NVIC_DisableIRQ(I2C_INTR_NUM);
{
}
NVIC_EnableIRQ(I2C_INTR_NUM);
#define CY_SCB_I2C_SLAVE_WR_CMPLT
The master has finished writing data into the slave.
Definition: cy_scb_i2c.h:791
uint32_t Cy_SCB_I2C_SlaveClearWriteStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t *context)
Clears the write status flags and error condition flags and returns their values.
Definition: cy_scb_i2c.c:1140
uint32_t Cy_SCB_I2C_SlaveGetStatus(CySCB_Type const *base, cy_stc_scb_i2c_context_t const *context)
Returns the current I2C slave status.
Definition: cy_scb_i2c.c:791
- Note
- All slave API (except Cy_SCB_I2C_SlaveAbortRead and Cy_SCB_I2C_SlaveAbortWrite) are not interrupt-protected and to prevent a race condition, they should be protected from the I2C interruption in the place where they are called. The code snippet Polling Slave Completion Events above shows how to prevent a race condition when detect transfer completion and update I2C slave write buffer. The simple example of race condition is: application updates slave read buffer the I2C master starts read transfer. The I2C interrupts read buffer update and I2C interrupt loads current read buffer content in the TX FIFO . After I2C interrupt returns the application updates remaining part of the read buffer. As a result the mater read partly updated buffer.
Low Power Support
The I2C driver provides callback functions to handle power mode transition. The callback Cy_SCB_I2C_DeepSleepCallback must be called during execution of Cy_SysPm_CpuEnterDeepSleep Cy_SCB_I2C_HibernateCallback must be called during execution of Cy_SysPm_SystemEnterHibernate. To trigger the callback execution, the callback must be registered before calling the power mode transition function. Refer to SysPm (System Power Management) driver for more information about power mode transitions and callback registration.