MTB CAT1 Peripheral driver library

General Description

Driver API for EZI2C Slave Peripheral.

The functions and other declarations used in this part of the driver are in cy_scb_ezi2c.h. You can also include cy_pdl.h to get access to all functions and declarations in the PDL.

I2C - The Inter-Integrated Circuit (I2C) bus is an industry-standard.

The EZI2C slave peripheral driver provides an API to implement the I2C slave device based on the SCB hardware block. This slave device emulates a common I2C EEPROM interface that acts like dual-port memory between the external master and your code. I2C devices based on the SCB hardware are compatible with the I2C Standard mode, Fast mode, and Fast mode Plus specifications, as defined in the I2C bus specification.

EZI2C slave is a special implementation of the I2C that handles all communication between the master and slave through ISR (interrupt service routine) and requires no interaction with the main program flow from the slave side. The EZI2C should be used when a shared memory model between the I2C master and I2C slave is needed.

Features:

Configuration Considerations

The EZI2C slave driver configuration can be divided to number of sequential steps listed below:

Note
EZI2C slave 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 EZI2C slave

To set up the EZI2C slave driver, provide the configuration parameters in the cy_stc_scb_ezi2c_config_t structure. The primary slave address slaveAddress1 must be provided. The other parameters are optional for operation. To initialize the driver, call Cy_SCB_EZI2C_Init function providing a pointer to the populated cy_stc_scb_ezi2c_config_t structure and the allocated cy_stc_scb_ezi2c_context_t structure.

/* Allocate context for EZI2C operation */
/* Populate configuration structure */
const cy_stc_scb_ezi2c_config_t ezI2cConfig =
{
.slaveAddress1 = 0x08U,
.slaveAddress2 = 0x00U,
.subAddressSize = CY_SCB_EZI2C_SUB_ADDR8_BITS,
.enableWakeFromSleep = false,
};
/* Configure EZI2C slave */
(void) Cy_SCB_EZI2C_Init(SCB3, &ezI2cConfig, &ezI2cContext);

Set up the EZI2C slave buffer before enabling its operation by using Cy_SCB_EZI2C_SetBuffer1 for the primary slave address and Cy_SCB_EZI2C_SetBuffer2 for the secondary (if the secondary is enabled).

/* Allocate buffer for EZI2C operation */
#define BUFFER_SIZE (128UL)
uint8_t bufferAddr1[BUFFER_SIZE];
/* Configure buffer for communication with master */
Cy_SCB_EZI2C_SetBuffer1(SCB3, bufferAddr1, BUFFER_SIZE, BUFFER_SIZE, &ezI2cContext);

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 pin configuration implies usage of external pull-up resistors):

/* Assign pins for EZI2C on SCB3: P6[0] and P6[1] */
#define EZI2C_PORT (P6_0_PORT)
#define EZI2C_SCL_NUM (P6_0_NUM)
#define EZI2C_SDA_NUM (P6_1_NUM)
/* Connect SCB3 I2C function to pins */
Cy_GPIO_SetHSIOM(EZI2C_PORT, EZI2C_SCL_NUM, P6_0_SCB3_I2C_SCL);
Cy_GPIO_SetHSIOM(EZI2C_PORT, EZI2C_SDA_NUM, P6_1_SCB3_I2C_SDA);
/* Configure pins for I2C operation */
Cy_GPIO_SetDrivemode(EZI2C_PORT, EZI2C_SCL_NUM, CY_GPIO_DM_OD_DRIVESLOW);
Cy_GPIO_SetDrivemode(EZI2C_PORT, EZI2C_SDA_NUM, CY_GPIO_DM_OD_DRIVESLOW);
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 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.

/* Assign divider type and number for EZI2C */
#define EZI2C_CLK_DIV_TYPE (CY_SYSCLK_DIV_8_BIT)
#define EZI2C_CLK_DIV_NUMBER (0UL)
/* Connect assigned divider to be a clock source for EZI2C */
Cy_SysClk_PeriphAssignDivider(PCLK_SCB3_CLOCK, EZI2C_CLK_DIV_TYPE, EZI2C_CLK_DIV_NUMBER);

Configure Data Rate

To get EZI2C slave to operate at 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.

Refer to the technical reference manual (TRM) section I2C sub-section Oversampling and Bit Rate to get information about how to configure the I2C to run at the desired data rate.

/* EZI2C slave desired data rate is 400 kbps.
* To support this data rate the clk_scb frequency must be in range 7.82 – 15.38 MHz.
* Find clk_scb valid ranges in TRM section I2C sub-section Oversampling and Bit Rate.
* For clk_peri = 50 MHz, select divider value 4 and get clk_scb = (50 MHz / 4) = 12.5 MHz.
* This clock frequency meets requirements above.
*/
Cy_SysClk_PeriphSetDivider (EZI2C_CLK_DIV_TYPE, EZI2C_CLK_DIV_NUMBER, 3u);
Cy_SysClk_PeriphEnableDivider(EZI2C_CLK_DIV_TYPE, EZI2C_CLK_DIV_NUMBER);

Configure Interrupt

The interrupt is mandatory for the EZI2C slave operation. The Cy_SCB_EZI2C_Interrupt function must be called in the interrupt handler for the selected SCB instance. Also, this interrupt must be enabled in the NVIC or it will not work.

/* Implement ISR for EZI2C */
void EZI2C_Isr(void)
{
Cy_SCB_EZI2C_Interrupt(SCB3, &ezI2cContext);
}
/* Assign EZI2C interrupt number and priority */
#define EZI2C_INTR_NUM scb_3_interrupt_IRQn
#define EZI2C_INTR_PRIORITY (7UL)
/* Populate configuration structure (code specific for CM4) */
const cy_stc_sysint_t ezI2cIntrConfig =
{
.intrSrc = EZI2C_INTR_NUM,
.intrPriority = EZI2C_INTR_PRIORITY,
};
/* Hook interrupt service routine and enable interrupt */
(void) Cy_SysInt_Init(&ezI2cIntrConfig, &EZI2C_Isr);
NVIC_EnableIRQ(EZI2C_INTR_NUM);

Enable EZI2C slave

Finally, enable the EZI2C slave operation by calling Cy_SCB_EZI2C_Enable. Now the I2C device responds to the assigned address.

/* Enable EZI2C to operate */
/* Enable global interrupts */
__enable_irq();

Common Use Cases

The EZI2C slave operation might not require calling any EZI2C slave function because the I2C master is able to access the slave buffer. The application can directly access it as well. Note that this is an application-level task to ensure the buffer content integrity.

Master Write operation

This operation starts with sending a base address that is one or two bytes, depending on the sub-address size configuration. This base address is retained and will be used for later read operations. Following the base address, there is a sequence of bytes written into the buffer starting from the base address location. The buffer index is incremented for each written byte, but this does not affect the base address that is retained. The length of a write operation is limited by the maximum buffer read/write region size.
When a master attempts to write outside the read/write region or past the end of the buffer, the last byte is NACKed.

scb_ezi2c_write.png

Master Read operation

This operation always starts from the base address set by the most recent write operation. The buffer index is incremented for each read byte. Two sequential read operations start from the same base address no matter how many bytes are read. The length of a read operation is not limited by the maximum size of the data buffer. The EZI2C slave returns 0xFF bytes if the read operation passes the end of the buffer.
Typically, a read operation requires the base address to be updated before starting the read. In this case, the write and read operations must be combined together.

scb_ezi2c_read.png

The I2C master may use the ReStart or Stop/Start conditions to combine the operations. The write operation sets only the base address and the following read operation will start from the new base address. In cases where the base address remains the same, there is no need for a write operation.

scb_ezi2c_set_ba_read.png

Low Power Support

The EZI2C slave provides the callback functions to handle power mode transition. The callback Cy_SCB_EZI2C_DeepSleepCallback must be called during execution of Cy_SysPm_CpuEnterDeepSleep; Cy_SCB_EZI2C_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.

The EZI2C configured to support two addresses can wakeup the device on address match to NACK not supported address. This happens because the hardware address-match-logic uses address bit masking to support to two addresses. The address mask defines which bits in the address are treated as non-significant while performing an address match. One non-significant bit results in two matching addresses; two bits will match 4 and so on. If the two addresses differ by more than a single bit, then the extra addresses that will pass the hardware match and wakeup the device from Deep Sleep mode. Then firmware address matching will to generate a NAK. Due to this reason, it is preferable to select a secondary address that is different from the primary by one bit. The address mask in this case makes one bit non-significant. For example:

Note
Only applicable for rev-08 of the CY8CKIT-062-BLE. For proper operation, when the EZI2C slave is configured to be a wakeup source from Deep Sleep mode, the Cy_SCB_EZI2C_DeepSleepCallback must be copied and modified. Refer to the function description to get the details.

API Reference

 Macros
 
 Functions
 
 Data Structures
 
 Enumerated Types