5.10 I2C Guide

This chapter describes the file layout, the 43 public functions, and the 7 examples (EEPROM, I2C_Master, I2C_Master_Int, I2C_Master_Dualaddr, I2C_Slave, I2C_Slave_Int, and I2C_Slave_Dualaddr) of the I2C driver in the N32G033 standard peripheral library. Read the “I2C” chapter of the user manual for the bus timing, clock stretching, and digital/analog filters, and the errata sheet for known chip issues.

5.10.1 Related files

Relative path Purpose
firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h Defines the I2C_InitType structure and the macros for bus mode, addressing, acknowledge, clock stretching, digital and analog filters, PEC, and timeout detection, and declares 43 public functions in sections such as reset and initialization, start/stop and slave address, and interrupt and data transfer
firmware/n32g033_std_periph_driver/src/n32g033_i2c.c Implements those 43 functions: I2C reset and initialization, start/stop and address transfer, SMBus and PEC, clock stretching and digital/analog filters, timeout detection, and event flag and interrupt handling
projects/n32g033_EVAL/examples/I2C/ I2C example root directory, with 7 examples: EEPROM, I2C_Master, I2C_Master_Int, I2C_Master_Dualaddr, I2C_Slave, I2C_Slave_Int, and I2C_Slave_Dualaddr
projects/n32g033_EVAL/examples/I2C/EEPROM/ Project layout of this example: MDK-ARM/, EWARM/, inc/, src/, and readme.txt
projects/n32g033_EVAL/bsp/ Board support files shared by the examples: the SysTick delay in delay.h and the UART log interface in log.h

5.10.2 API reference

Search API43 functions

The following 43 public functions are listed in the declaration order of n32g033_i2c.h in 8 groups; each entry gives a summary and the original comment above the function in n32g033_i2c.c.

Reset and initialization

I2C_DeInit, I2C_Init, I2C_InitStruct, I2C_Enable

Start/stop, acknowledge, and slave address

I2C_GenerateStart, I2C_GenerateStop, I2C_ConfigAck, I2C_SendAddr7bit, I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_EnableGeneralCall

Interrupts, data transfer, and DMA

I2C_ConfigInt, I2C_SendData, I2C_RecvData, I2C_EnableDMA, I2C_EnableDmaLastSend

Software reset and NACK/PEC position

I2C_EnableSoftwareReset, I2C_ConfigNackLocation, I2C_ConfigPecLocation

SMBus, PEC, and ARP

I2C_ConfigSmbusAlert, I2C_SendPEC, I2C_ComputePec, I2C_GetPec, I2C_EnableArp

Clock stretching, digital and analog filters

I2C_EnableExtendClk, I2C_SetSDADigitalFilterWidth, I2C_SetSCLDigitalFilterWidth, I2C_SetSDAAnalogFilterWidth, I2C_EnableSDAAnalogFilter, I2C_SetSCLAnalogFilterWidth, I2C_EnableSCLAnalogFilter

Events, flags, and interrupt status

I2C_CheckEvent, I2C_GetLastEvent, I2C_GetFlag, I2C_ClrFlag, I2C_GetIntStatus, I2C_ClrIntPendingBit

Timeout detection

I2C_SetTimeoutLowLimit, I2C_SetTimeoutHighLimit, I2C_ConfigTimeoutInt, I2C_EnableTimeoutDetect, I2C_GetTimeoutFlag, I2C_ClrTimeoutFlag

void I2C_DeInit(I2C_Module* I2Cx)Deinitializes the I2Cx peripheral registers to their default reset values.
Source comment
name    I2C_DeInit.
fun     Deinitializes the I2Cx peripheral registers to their default reset values.
param   I2Cx :
          - I2C1
          - I2C2
return  none
void I2C_Init(I2C_Module* I2Cx, I2C_InitType* I2C_InitParam)Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
Source comment
name    I2C_Init.
fun     Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_InitParam :
          - ClkSpeed(common speed:100000(100KHz) - 400000(400KHz) - 1000000(1MHz)):
            - between 1 to 1000000
          - BusMode:
            - I2C_BUSMODE_I2C
            - I2C_BUSMODE_SMBDEVICE
            - I2C_BUSMODE_SMBHOST
          - FmDutyCycle(If the ClkSpeed does not exceed 100KHz,duty is fixed at 1:1,
                      otherwise it can be configured as 2:1 or 16:9):
            - I2C_FMDUTYCYCLE_16_9
            - I2C_FMDUTYCYCLE_2
          - OwnAddr1:
            - between 0 to 0x3FF
          - AckEnable:
            - I2C_ACKEN
            - I2C_ACKDIS
          - AddrMode:
            - I2C_ADDR_MODE_7BIT
            - I2C_ADDR_MODE_10BIT
return  none
void I2C_InitStruct(I2C_InitType* I2C_InitParam)Fills each I2C_InitParam member with its default value.
Source comment
name    I2C_Initializes_Structure.
fun     Fills each I2C_InitParam member with its default value.
param   (The input parameters must be the following values):
          - ClkSpeed
          - BusMode
          - FmDutyCycle
          - OwnAddr1
          - AckEnable
          - AddrMode
return  none

Use note: The comment names the function I2C_Initializes_Structure., which differs from the function name; line 491 of the header declares it as I2C_InitStruct.

void I2C_Enable(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C peripheral.
Source comment
name    I2C_Enable.
fun     Enables or disables the specified I2C peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStart(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables Generate a START condition.
Source comment
name    I2C_GenerateStart.
fun     Enable or disables Generate a START condition.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStop(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables Generate a STOP condition.
Source comment
name    I2C_GenerateStop.
fun     Enable  or disables Generate a STOP condition.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigAck(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables the acknowledgement.
Source comment
name    I2C_ConfigAck.
fun     Enable  or disables the acknowledgement.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SendAddr7bit(I2C_Module* I2Cx, uint8_t address, uint8_t mode)Transmits the address byte to select the slave device.
Source comment
name    I2C_SendAddr7bit.
fun     Transmits the address byte to select the slave device.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   address (The input parameters must be the following values)
        - 0 to 0xFF
param   mode (The input parameters must be the following values):
          - I2C_DIRECTION_SEND    Transmitter mode
          - I2C_DIRECTION_RECV    Receiver mode
return  none
void I2C_ConfigOwnAddr2(I2C_Module* I2Cx, uint8_t address)Set the specified I2C own address2.
Source comment
name    I2C_ConfigOwnAddr2.
fun     Set the specified I2C own address2.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   address(The input parameters must be the following values)
          - - 0 to 0xFF
return  none
void I2C_EnableDualAddr(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C dual addressing mode.
Source comment
name    I2C_EnableDualAddr.
fun     Enables or disables the specified I2C dual addressing mode.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
\*\param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableGeneralCall(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C general call feature.
Source comment
name    I2C_EnableGeneralCall.
fun     Enables or disables the specified I2C general call feature.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables the specified I2C interrupts.
Source comment
name    I2C_ConfigInt.
fun     Enables the specified I2C interrupts.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_IT (The input parameters must be the following values):
          - I2C_INT_BUF      Buffer interrupt mask
          - I2C_INT_EVENT    Event interrupt mask
          - I2C_INT_ERR      Error interrupt mask
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SendData(I2C_Module* I2Cx, uint8_t data)Sends a data byte through the I2Cx peripheral.
Source comment
name    I2C_SendData.
fun     Sends a data byte through the I2Cx peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   data: 0x00~0xFF
return  none
uint8_t I2C_RecvData(I2C_Module* I2Cx)Returns the most recent received data by the I2Cx peripheral.
Source comment
name    I2C_RecvData.
fun     Returns the most recent received data by the I2Cx peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
return  The value of the received data
void I2C_EnableDMA(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables I2C DMA transfer.
Source comment
name    I2C_EnableDMA.
fun     Enables or disables I2C DMA transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableDmaLastSend(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables I2C DMA last transfer.
Source comment
name    I2C_EnableDmaLastSend.
fun     Enables  or disables I2C DMA last transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    This bit is used in the master receiving mode, so that a NACK can be generated when
        data is received for the last time.
void I2C_EnableSoftwareReset(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C software reset.
Source comment
name    I2C_EnableSoftwareReset.
fun     Enables or disables the specified I2C software reset.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    This bit can be used when the I2C_STS2.BUSY bit is set to 1 and no stop condition is detected
        on the bus.
void I2C_ConfigNackLocation(I2C_Module* I2Cx, uint32_t position)Selects the specified I2C NACK position in master receiver mode. This function is useful in I2C Master Receiver mode when the number of data to be received is equal to 2. In this case, this function should be called (with parameter I2C_NACK_POS_NEXT) before data reception starts,as described in the 2-byte reception procedure recommended in User Manual in Section: Master receiver.
Source comment
name    I2C_ConfigNackLocation.
fun     Selects the specified I2C NACK position in master receiver mode.
        This function is useful in I2C Master Receiver mode when the number
        of data to be received is equal to 2. In this case, this function
        should be called (with parameter I2C_NACK_POS_NEXT) before data
        reception starts,as described in the 2-byte reception procedure
        recommended in User Manual in Section: Master receiver.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   position (The input parameters must be the following values):
          - I2C_NACK_POS_NEXT       Indicates that the next byte will be the last received byte.
          - I2C_NACK_POS_CURRENT    Indicates that the current byte is the last received byte.
return  none
note    ACKPOS bit can only be used in 2-byte receiving configuration and must be configured before
        receiving data.
        For the second byte of NACK, the I2C_CTRL1.ACKEN bit must be cleared after the
        I2C_STS1.ADDRF bit is cleared.
        To detect the PEC of the second byte, the I2C_CTRL1.PEC bit must be set after the ACKPOS bit is
        configured and when the ADDR event is extended.
void I2C_ConfigPecLocation(I2C_Module* I2Cx, uint32_t position)Selects the specified I2C PEC position.
Source comment
name    I2C_ConfigPecLocation.
fun     Selects the specified I2C PEC position.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   position (The input parameters must be the following values):
          - I2C_PEC_POS_NEXT       Indicates that the next byte will be the last received byte or the next byte is PEC.
          - I2C_PEC_POS_CURRENT    Indicates that the current byte is the last received byte or the current byte is PEC.
return  none
note   This function configures the same bit (ACKPOS) as I2C_ConfigNackLocation()
       but is intended to be used in SMBUS mode while I2C_ConfigNackLocation()
       is intended to used in I2C mode.
void I2C_ConfigSmbusAlert(I2C_Module* I2Cx, uint32_t polarity)Set the SMBus Alert pin high or low for the specified I2C.
Source comment
name    I2C_ConfigSmbusAlert.
fun     Set the SMBus Alert pin high or low for the specified I2C.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   polarity (The input parameters must be the following values):
          - I2C_SMBALERT_LOW     SMBAlert pin driven low
          - I2C_SMBALERT_HIGH    SMBAlert pin driven high
return  none
void I2C_SendPEC(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C PEC transfer.
Source comment
name    I2C_SendPEC.
fun     Enables or disables the specified I2C PEC transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    When arbitration is lost, the calculation of PEC is invalid.
void I2C_ComputePec(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the PEC value calculation of the transferred bytes.
Source comment
name    I2C_ComputePec.
fun     Enables or disables the PEC value calculation of the transferred bytes.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
uint8_t I2C_GetPec(I2C_Module* I2Cx)Get the PEC value for the specified I2C.
Source comment
name    I2C_GetPec.
fun     Get the PEC value for the specified I2C.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
return  The PEC value
void I2C_EnableArp(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C Address Resolution Protocol(ARP).
Source comment
name    I2C_EnableArp.
fun     Enables or disables the specified I2C Address Resolution Protocol(ARP).
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableExtendClk(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C clock extending.
Source comment
name    I2C_EnableExtendClk.
fun     Enables or disables the specified I2C clock extending.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SetSDADigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)SDA digital filter width selection.
Source comment
name    I2C_SetSDADigitalFilterWidth.
fun     SDA digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none
void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)SCL digital filter width selection.
Source comment
name    I2C_SetSCLDigitalFilterWidth.
fun     SCL digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none
void I2C_SetSDAAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)SDA analog filter width selection.
Source comment
name    I2C_SetSDAAnalogFilterWidth.
fun     SDA analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
        - I2C_ANALOG_FILTER_WIDTH_5NS
        - I2C_ANALOG_FILTER_WIDTH_15NS
        - I2C_ANALOG_FILTER_WIDTH_25NS
        - I2C_ANALOG_FILTER_WIDTH_35NS
return  none
void I2C_EnableSDAAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the SDA analog filter.
Source comment
name    I2C_EnableSDAAnalogFilter.
fun     Enables or disables the SDA analog filter.
param   I2Cx :
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SetSCLAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)SCL analog filter width selection.
Source comment
name    I2C_SetSCLAnalogFilterWidth.
fun     SCL analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
        - I2C_ANALOG_FILTER_WIDTH_5NS
        - I2C_ANALOG_FILTER_WIDTH_15NS
        - I2C_ANALOG_FILTER_WIDTH_25NS
        - I2C_ANALOG_FILTER_WIDTH_35NS
return  none
void I2C_EnableSCLAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the SCL analog filter.
Source comment
name    I2C_EnableSCLAnalogFilter.
fun     Enables or disables the SCL analog filter.
param   I2Cx :
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
ErrorStatus I2C_CheckEvent(I2C_Module* I2Cx, uint32_t I2C_event)Checks whether the last I2Cx Event is equal to the one passed as parameter.
Source comment
name    I2C_CheckEvent.
fun     Checks whether the last I2Cx Event is equal to the one passed as parameter.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_event :
          - I2C_EVT_SLAVE_SEND_ADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_RECV_ADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_SEND_ADDR2_MATCHED                    EV1
          - I2C_EVT_SLAVE_RECV_ADDR2_MATCHED                    EV1
          - I2C_EVT_SLAVE_GCALLADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_DATA_RECVD                            EV2
          - (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_DUALFLAG)      EV2
          - (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_GCALLADDR)     EV2
          - I2C_EVT_SLAVE_DATA_SENDED                           EV3
          - (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_DUALFLAG)     EV3
          - (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_GCALLADDR)    EV3
          - I2C_EVT_SLAVE_ACK_MISS                              EV3_2
          - I2C_EVT_SLAVE_STOP_RECVD                            EV4
          - I2C_EVT_MASTER_MODE_FLAG                            EV5
          - I2C_EVT_MASTER_TXMODE_FLAG                          EV6
          - I2C_EVT_MASTER_RXMODE_FLAG                          EV6
          - I2C_EVT_MASTER_DATA_RECVD_FLAG                      EV7
          - I2C_EVT_MASTER_DATA_SENDING                         EV8
          - I2C_EVT_MASTER_DATA_SENDED                          EV8_2
          - I2C_EVT_MASTER_MODE_ADDRESS10_FLAG                  EV9
return  SUCCESS or ERROR
note    If an error occurs (that is, an error flag is set in addition to the flags parameter entered), the function may still return SUCCESS.
        In this case, you can use error interrupts to handle error events.
uint32_t I2C_GetLastEvent(I2C_Module* I2Cx)Returns the last I2Cx Event.
Source comment
name    I2C_GetLastEvent.
fun     Returns the last I2Cx Event.
param   I2Cx :
          - I2C1
          - I2C2
return  The last event
note    Users can define their own events.
        If the user decides to check only the general communication flags (ignore error flags),
        when an error occurs, you can use error interrupts to handle error events.
FlagStatus I2C_GetFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Checks whether the specified I2C flag is set or not.
Source comment
name    I2C_GetFlag.
fun     Checks whether the specified I2C flag is set or not.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_flag :
          - I2C_FLAG_DUALFLAG     Dual flag (Slave mode)
          - I2C_FLAG_SMBHADDR     SMBus host header (Slave mode)
          - I2C_FLAG_SMBDADDR     SMBus default header (Slave mode)
          - I2C_FLAG_GCALLADDR    General call header flag (Slave mode)
          - I2C_FLAG_TRF          Transmitter/Receiver flag
          - I2C_FLAG_BUSY         Bus busy flag
          - I2C_FLAG_MSMODE       Master/Slave flag
          - I2C_FLAG_SMBALERT     SMBus Alert flag
          - I2C_FLAG_TIMEOUT      Timeout
          - I2C_FLAG_PECERR       PEC error in reception flag
          - I2C_FLAG_OVERRUN      Overrun/Underrun flag (Slave mode)
          - I2C_FLAG_ACKFAIL      Acknowledge failure flag
          - I2C_FLAG_ARLOST       Arbitration lost flag (Master mode)
          - I2C_FLAG_BUSERR       Bus error flag
          - I2C_FLAG_TXDATE       Data register empty flag (Transmitter)
          - I2C_FLAG_RXDATNE      Data register not empty (Receiver) flag
          - I2C_FLAG_STOPF        Stop detection flag (Slave mode)
          - I2C_FLAG_ADDR10F      10-bit header sent flag (Master mode)
          - I2C_FLAG_BYTEF        Byte transfer finished flag
          - I2C_FLAG_ADDRF        Address sent flag (Master mode)
                                  Address matched flag (Slave mode)
          - I2C_FLAG_STARTBF      Start bit flag (Master mode)
return  SET or RESET
note    When this function is called, the status register is accessed, so other flags may be cleared.
        You can call I2C_GetFlag() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Clears the I2Cx’s flags.
Source comment
name    I2C_ClrFlag.
fun     Clears the I2Cx's flags.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_flag :
          - I2C_FLAG_SMBALERT    SMBus Alert flag
          - I2C_FLAG_TIMEOUT      Timeout
          - I2C_FLAG_PECERR      PEC error in reception flag
          - I2C_FLAG_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_FLAG_ACKFAIL     Acknowledge failure flag
          - I2C_FLAG_ARLOST      Arbitration lost flag (Master mode)
          - I2C_FLAG_BUSERR      Bus error flag
note    Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
        The other flag bits are cleared in the following sequence:
        - STOPF (STOP detection) is cleared by software sequence: a read operation
          to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
          to re-enable the I2C peripheral).
        - ADDR10F (10-bit header sent) is cleared by software sequence: a read
          operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
        - BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
          operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
        - ADDRF (Address sent) is cleared by software sequence: a read operation to
          I2C_STS1 register followed by a read operation to I2C_STS2 register.
        - STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
          register followed by a write operation to I2C_DAT register.
return  none
INTStatus I2C_GetIntStatus(I2C_Module* I2Cx, uint32_t Interrupt)Checks whether the specified I2C interrupt has occurred or not.
Source comment
name    I2C_GetIntStatus.
fun     Checks whether the specified I2C interrupt has occurred or not.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Interrupt (The input parameters must be the following values):
          - I2C_INT_SMBALERT    SMBus Alert flag
          - I2C_INT_TIMEOUT     Timeout
          - I2C_INT_PECERR      PEC error in reception flag
          - I2C_INT_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_INT_ACKFAIL     Acknowledge failure flag
          - I2C_INT_ARLOST      Arbitration lost flag (Master mode)
          - I2C_INT_BUSERR      Bus error flag
          - I2C_INT_TXDATE      Data register empty flag (Transmitter)
          - I2C_INT_RXDATNE     Data register not empty (Receiver) flag
          - I2C_INT_STOPF       Stop detection flag (Slave mode)
          - I2C_INT_ADDR10F     10-bit header sent flag (Master mode)
          - I2C_INT_BYTEF       Byte transfer finished flag
          - I2C_INT_ADDRF       Address sent flag (Master mode)
                                Address matched flag (Slave mode)
          - I2C_INT_STARTBF     Start bit flag (Master mode)
return  SET or RESET
note    You can call I2C_GetIntStatus() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrIntPendingBit(I2C_Module* I2Cx, uint32_t Interrupt)Clears the I2Cx’s interrupt statu bits.
Source comment
name    I2C_ClrIntPendingBit.
fun     Clears the I2Cx's interrupt statu bits.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Interrupt (The input parameters must be the following values):
          - I2C_INT_SMBALERT    SMBus Alert flag
          - I2C_INT_TIMEOUT     Timeout
          - I2C_INT_PECERR      PEC error in reception flag
          - I2C_INT_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_INT_ACKFAIL     Acknowledge failure flag
          - I2C_INT_ARLOST      Arbitration lost flag (Master mode)
          - I2C_INT_BUSERR      Bus error flag
return  none
note    Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
        The other flag bits are cleared in the following sequence:
        - STOPF (STOP detection) is cleared by software sequence: a read operation
          to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
          to re-enable the I2C peripheral).
        - ADDR10F (10-bit header sent) is cleared by software sequence: a read
          operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
        - BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
          operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
        - ADDRF (Address sent) is cleared by software sequence: a read operation to
          I2C_STS1 register followed by a read operation to I2C_STS2 register.
        - STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
          register followed by a write operation to I2C_DAT register.
void I2C_SetTimeoutLowLimit(I2C_Module* I2Cx, uint32_t limit)timeout low threshold selection.
Source comment
name    I2C_SetTimeoutLowLimit.
fun     timeout low threshold selection.
param   I2Cx :
          - I2C1
          - I2C2
param   limit:
          - I2C_TIMEOUT_LOWLIMIT_25MS
          - I2C_TIMEOUT_LOWLIMIT_100MS
          - I2C_TIMEOUT_LOWLIMIT_1S
          - I2C_TIMEOUT_LOWLIMIT_4S
return  none
void I2C_SetTimeoutHighLimit(I2C_Module* I2Cx, uint32_t limit)timeout high threshold selection.
Source comment
name    I2C_SetTimeoutHighLimit.
fun     timeout high threshold selection.
param   I2Cx :
          - I2C1
          - I2C2
param   limit:
          - I2C_TIMEOUT_HIGHLIMIT_256US
          - I2C_TIMEOUT_HIGHLIMIT_512US
          - I2C_TIMEOUT_HIGHLIMIT_1MS
          - I2C_TIMEOUT_HIGHLIMIT_128US
return  none
void I2C_ConfigTimeoutInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables the specified I2C timeout interrupts.
Source comment
name    I2C_ConfigTimeoutInt.
fun     Enables the specified I2C timeout interrupts.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_IT (The input parameters must be the following values):
          - I2C_TIMEOUT_INT_SCLLOW     SCL low timeout error interrupt
          - I2C_TIMEOUT_INT_SCLHIGH    SCL high timeout error interrupt
          - I2C_TIMEOUT_INT_SDALOW     SDA low timeout error interrupt
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableTimeoutDetect(I2C_Module* I2Cx, uint32_t timeout_type, FunctionalState Cmd)Enables the specified I2C timeout detection function .
Source comment
name    I2C_EnableTimeout.
fun     Enables the specified I2C timeout detection function .
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   timeout_type (The input parameters must be the following values):
          - I2C_TIMEOUT_LOW     low timeout function enable
          - I2C_TIMEOUT_HIGH    high timeout function enable
param   Cmd:
          - ENABLE
          - DISABLE
note    This function can be disabled in I2C mode when an timeout error is not required.
return  none

Use note: The comment names the function I2C_EnableTimeout., which differs from the function name; line 529 of the header declares it as I2C_EnableTimeoutDetect.

FlagStatus I2C_GetTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Checks whether the specified I2C timeout flag is set or not.
Source comment
name    I2C_GetTimeoutFlag.
fun     Checks whether the specified I2C timeout flag is set or not.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_flag (The input parameters must be the following values):
          - I2C_TIMEOUT_FLAG_SCLLOW     SCL low timeout flag
          - I2C_TIMEOUT_FLAG_SCLHIGH    SCL high timeout flag
          - I2C_TIMEOUT_FLAG_SDALOW     SDA low timeout flag
return  SET or RESET
void I2C_ClrTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Clears the I2Cx’s pending flags.
Source comment
name    I2C_ClrTimeoutFlag.
fun     Clears the I2Cx's pending flags.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_flag (The input parameters must be the following values):
          - I2C_TIMEOUT_FLAG_SCLLOW     SCL low timeout flag
          - I2C_TIMEOUT_FLAG_SCLHIGH    SCL high timeout flag
          - I2C_TIMEOUT_FLAG_SDALOW     SDA low timeout flag
return  none

5.10.3 Demo descriptions

This section describes the 7 I2C examples: EEPROM reads and writes a serial EEPROM over I2C and compares the data, I2C_Master shows polled master transfers, I2C_Master_Int completes master transfers with event and error interrupts, I2C_Master_Dualaddr transfers with two slave addresses, I2C_Slave shows polled slave transfers, I2C_Slave_Int completes slave transfers with interrupts, and I2C_Slave_Dualaddr answers two own addresses.

5.10.3.1 EEPROM read and write of a serial EEPROM

Example directory: projects/n32g033_EVAL/examples/I2C/EEPROM/

Enable the GPIO and I2C clocks→Configure SCL and SDA as open-drain with internal pull-up→Initialize and enable the I2C master→Write an ascending block page by page→Read the data back and compare byte by byte

Item Source analysis
Function Write an ascending data block to the on-board serial EEPROM, read it back, compare it byte by byte, and store the test status.
Source files src/main.c runs the write, read-back, and compare flow;
src/i2c_eeprom.c implements EEPROM initialization, page write, and read-back;
inc/i2c_eeprom.h defines the slave address, page size, and timeout parameters;
src/n32g033_it.c holds the interrupt entry;
readme.txt gives the configuration and test steps.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain with internal pull-up;
Speed: 400 K;
Addressing: 7-bit, slave address 0xA0;
Page size: 8 bytes;
Timeout detection: low timeout disabled.
Program and data path main -> I2C_EE_Init sets up the pins and the I2C master -> I2C_EE_WriteBuffer writes page by page -> I2C_EE_ReadBuffer reads back byte by byte -> Buffercmp compares and prints the result.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_GenerateStop, I2C_EnableTimeoutDetect
Verification After compiling and resetting, a matching serial log means PASSED;
you can also watch the two buffers in debug mode.
Use note SCL is on PB6 and SDA on PB7, and external pull-ups are required;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example initializes the pins and the I2C master, then writes page by page, reads back byte by byte, and compares the two buffers.

Data path: main prepares the ascending data → I2C_EE_Init configures the pins and the I2C master → I2C_EE_WriteBuffer splits pages and sends them through I2C_EE_PageWrite → I2C_EE_WaitEepromStandbyState waits for the internal write to finish → I2C_EE_ReadBuffer fetches the data → Buffercmp compares

1. Configure SCL and SDA as open-drain with the internal pull-up, then load the I2C master parameters.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_Init
    I2Cx_scl_clk_en();
    I2Cx_sda_clk_en();
    GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin

    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    GPIO_InitStructure.GPIO_Mode  = GPIO_MODE_AF_OD;
    GPIO_InitStructure.GPIO_Alternate = GPIO_AF6;
    GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
    GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);

    /** I2C periphral configuration */
    I2C_DeInit(I2Cx);
    I2C_InitStruct(&I2C_InitStructure);
    I2C_InitStructure.BusMode     = I2C_BUSMODE_I2C;
    I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    I2C_InitStructure.OwnAddr1    = 0xff;
    I2C_InitStructure.AckEnable   = I2C_ACKEN;
    I2C_InitStructure.AddrMode    = I2C_ADDR_MODE_7BIT;
    I2C_InitStructure.ClkSpeed    = I2C_Speed;
    I2C_Init(I2Cx, &I2C_InitStructure);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

If you change the pins, update the pin macros and the alternate function in the header, and keep the external pull-ups on the bus.

2. Page write: send the start and the device address, then the page word address, then the data bytes, waiting for each transmission to finish.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_PageWrite
    /** Send START condition */
    I2C_GenerateStart(I2Cx, ENABLE);
    /** Test on EV5 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** Send EEPROM address for write */
    I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
    /** Test on EV6 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** Send the EEPROM's internal address to write to */
    I2C_SendData(I2Cx, WriteAddr);
    /** Test on EV8 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** While there is data to be written */
    while (NumByteToWrite--)
    {
        /** Send the current byte */
        I2C_SendData(I2Cx, *pBuffer);
        /** Point to the next byte to be written */
        pBuffer++;
        /** Test on EV8 and clear it */
        sEETimeout = sEE_LONG_TIMEOUT;
        while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
        {
            if ((sEETimeout--) == 0)
            {
                sEE_TIMEOUT_UserCallback();
                return;
            }
        }
    }
    /** Send STOP condition */
    I2C_GenerateStop(I2Cx, ENABLE);
    I2C_EE_WaitEepromStandbyState();
    I2C_EE_WriteOnePageCompleted();

The page size comes from the EEPROM type; the upper layer splits cross-page writes, so a new device only needs a page-size change.

3. Read-back: send the device address and the word address in the write direction, then restart and address the device in the read direction.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_ReadBuffer
    I2C_ConfigPecLocation(I2Cx, I2C_PEC_POS_CURRENT);
    I2C_ConfigAck(I2Cx, ENABLE);

    /** Send START condition */
    I2C_GenerateStart(I2Cx, ENABLE);

    /** Test on EV5 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** Send EEPROM address for write */
    I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
    /** Test on EV6 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** Clear EV6 by setting again the PE bit */
    I2C_Enable(I2Cx, ENABLE);
    /** Send the EEPROM's internal address to write to */
    I2C_SendData(I2Cx, ReadAddr);
    /** Test on EV8 and clear it */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }
    /** Send STRAT condition a second time */
    I2C_GenerateStart(I2Cx, ENABLE);

Keep the PEC position and acknowledge settings consistent with the tail-byte handling, and check them again when you change the length.

4. After a write, poll the internal status by re-addressing the device until the slave acknowledges again.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_WaitEepromStandbyState
    /** Keep looping till the slave acknowledge his address or maximum number
       of trials is reached (this number is defined by sEE_MAX_TRIALS_NUMBER) */
    while (1)
    {
        /** Send START condition */
        I2C_GenerateStart(I2Cx, ENABLE);

        /** Test on EV5 and clear it */
        sEETimeout = sEE_LONG_TIMEOUT;
        while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
        {
            if ((sEETimeout--) == 0)
            {
                sEE_TIMEOUT_UserCallback();
                return;
            }
        }

        /** Send EEPROM address for write */
        I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
        /** Wait for ADDR flag to be set (Slave acknowledged his address) */
        sEETimeout = sEE_LONG_TIMEOUT;
        do
        {
            /** Get the current value of the STS1 register */
            tmpSR1 = I2Cx->STS1;

            /** Update the timeout value and exit if it reach 0 */
            if ((sEETimeout--) == 0)
                sEE_TIMEOUT_UserCallback();
        }
        /** Keep looping till the Address is acknowledged or the AF flag is
           set (address not acknowledged at time) */
        while ((tmpSR1 & (I2C_STS1_ADDRF | I2C_STS1_ACKFAIL)) == 0);

        /** Check if the ADDR flag has been set */
        if (tmpSR1 & I2C_STS1_ADDRF)
        {
            /** Clear ADDR Flag by reading STS1 then STS2 registers (STS1 have already
               been read) */
            (void)I2Cx->STS2;

            /** STOP condition */
            I2C_GenerateStop(I2Cx, ENABLE);

            /** Exit the function */
            return;
        }
        else
        {
            /** Clear AF flag */
            I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
        }

        /** Check if the maximum allowed numbe of trials has bee reached */
        if (sEETrials++ == sEE_MAX_TRIALS_NUMBER)
        {
            /** If the maximum number of trials has been reached, exit the function */
            sEE_TIMEOUT_UserCallback();
            return;
        }
    }

The retry count is capped by a macro; raise it for slower devices so a valid write is not reported as a failure.

Corresponding source files: src/main.c, src/i2c_eeprom.c, inc/i2c_eeprom.h, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.2 I2C_Master polled master transfer

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master/

Enable the GPIO and I2C clocks→Configure SCL and SDA as open-drain→Initialize the master and set the slave address→Send a data block under polling→Receive under polling and compare

Item Source analysis
Function Act as a master in polled mode, send a data block to a slave, read it back, and compare byte by byte;
a timeout triggers the recovery callback.
Source files src/main.c holds the master initialization, send, receive, and timeout callback;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the test buffers and status;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, master address 0x30 and slave address 0x10;
Acknowledge: enabled for receive;
Recovery: software reset.
Program and data path main -> i2c_master_init sets up the pins and the master -> i2c_master_send sends under polling -> i2c_master_recv reads back under polling -> Buffercmp compares.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_ConfigAck, I2C_ConfigNackLocation, I2C_EnableSoftwareReset
Verification After compiling and resetting, a matching serial log means PASSED;
two boards or two I2C instances as master and slave also show the transfer.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example advances the event flags step by step under polling to complete master send, receive, and error recovery.

Data path: main prepares the data → i2c_master_init configures the pins and the master → i2c_master_send issues start, address, data, and stop → i2c_master_recv reads the bytes back → Buffercmp compares

1. Master initialization: enable the clocks, set the pins, then write the speed, address, and acknowledge options.

src/main.csrc/main.c · i2c_master_init
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
    GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin

    GPIO_InitStruct(&i2c1_gpio);
    /*PB10 -- SCL; PB11 -- SDA*/
    i2c1_gpio.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    i2c1_gpio.GPIO_Mode  = GPIO_MODE_AF_OD;
    i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
    i2c1_gpio.GPIO_Pull = GPIO_PULL_UP;
    GPIO_InitPeripheral(GPIOx, &i2c1_gpio);

    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2c1_master);
    i2c1_master.BusMode     = I2C_BUSMODE_I2C;
    i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2; //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
    i2c1_master.OwnAddr1    = I2C_MASTER_ADDR;
    i2c1_master.AckEnable   = I2C_ACKEN;
    i2c1_master.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_master.ClkSpeed    = 100000;

    I2C_Init(I2Cx, &i2c1_master);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);

Match the speed and duty cycle to the slave, and keep the duty-cycle setting within the manual requirement when you change the speed.

2. Send flow: wait on the matching event for the start, the device address, the data bytes, and the stop.

src/main.csrc/main.c · i2c_master_send
    I2CTimeout             = I2CT_LONG_TIMEOUT;
    while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_BUSY);
            return 1;
        }
    }

    if (Comm_Flag == C_READY)
    {
        Comm_Flag = C_START_BIT;
        I2C_GenerateStart(I2C1, ENABLE);
    }

    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_MODE);
            return 1;
        }
    }

    I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG)) // EV6
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_TXMODE);
            return 1;
        }
    }
    Comm_Flag = C_READY;

    // send data
    while (len-- > 0)
    {
        I2C_SendData(I2C1, *sendBufferPtr++);
        I2CTimeout = I2CT_LONG_TIMEOUT;
        while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)) // EV8
        {
            if ((I2CTimeout--) == 0)
            {
                CommTimeOut_CallBack(MASTER_SENDING);
                return 1;
            }
        }
    }

    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED)) // EV8-2
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_SENDED);
            return 1;
        }
    }

    if (Comm_Flag == C_READY)
    {
        Comm_Flag = C_STOP_BIT;
        I2C_GenerateStop(I2C1, ENABLE);
    }

Every step carries a timeout counter; raise the limit when you move to a slower bus.

3. Receive flow: branch on the length, and for the last byte set the NACK position and then disable the acknowledge.

src/main.csrc/main.c · i2c_master_recv
    if (len == 1)
    {
        I2C_ConfigAck(I2C1, DISABLE);
        (void)(I2C1->STS1); /// clear ADDR
        (void)(I2C1->STS2);
        if (Comm_Flag == C_READY)
        {
            Comm_Flag = C_STOP_BIT;
            I2C_GenerateStop(I2C1, ENABLE);
        }

        I2CTimeout = I2CT_LONG_TIMEOUT;
        while (!I2C_GetFlag(I2C1, I2C_FLAG_RXDATNE))
        {
            if ((I2CTimeout--) == 0)
            {
                CommTimeOut_CallBack(MASTER_RECVD);
                return 1;
            }
        }
        *recvBufferPtr++ = I2C_RecvData(I2C1);
        len--;
    }
    else if (len == 2)
    {
        I2C_ConfigNackLocation(I2C1, I2C_NACK_POS_NEXT);
        (void)(I2C1->STS1);
        (void)(I2C1->STS2);
        I2C_ConfigAck(I2C1, DISABLE);

Disable the acknowledge and prepare the stop before the final byte; reversing the order reads one extra byte.

4. When the bus hangs, recover it with a software reset and re-initialize.

src/main.csrc/main.c · IIC_SWReset
void IIC_SWReset(void)
{
    GPIO_InitType i2cx_gpio;

    GPIO_InitStruct(&i2cx_gpio);
    i2cx_gpio.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    i2cx_gpio.GPIO_Mode  = GPIO_MODE_INPUT;
    GPIO_InitPeripheral(GPIOx, &i2cx_gpio);

    I2CTimeout = I2CT_LONG_TIMEOUT;
    for (;;)
    {
        if ((I2Cx_SCL_PIN | I2Cx_SDA_PIN) == (GPIOx->PID & (I2Cx_SCL_PIN | I2Cx_SDA_PIN)))
        {
            I2C_EnableSoftwareReset(I2Cx,ENABLE);
            __NOP();
            __NOP();
            __NOP();
            __NOP();
            __NOP();
            I2C_EnableSoftwareReset(I2Cx,DISABLE);

            log_info("***** IIC module self reset! *****\r\n");
            break;
        }
        else
        {
            if ((I2CTimeout--) == 0)
            {
                IIC_RCCReset();
            }
        }
    }
}

Before a software reset, make sure the pins are released and the bus is idle, then rebuild the I2C configuration.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.3 I2C_Master_Int interrupt-driven master transfer

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master_Int/

Enable the GPIO and I2C clocks→Configure the pins, master parameters, and interrupt priority→Enable the event and error interrupts→Advance the transfer event by event in the interrupt→Wait for the finish flag in the main loop and compare

Item Source analysis
Function Act as an interrupt-driven master for send and receive, leaving the main loop to wait on a finish flag.
Source files src/main.c holds the initialization, the interrupt service routine, and the main loop;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the buffers, flags, and status;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, master address 0x30 and slave address 0x10;
Interrupts: event and error interrupts enabled, event priority 2 and error priority 3.
Program and data path main -> i2c_master_init configures the master and enables the interrupts -> I2C1_EV_IRQHandler advances the transfer event by event -> the main loop waits on the finish flag -> Buffercmp compares.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigInt, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ConfigAck, I2C_ClrFlag
Verification After compiling and resetting, a matching serial log means PASSED;
you can watch the event value inside the interrupt to confirm the ordered progress.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example moves the transfer into the event interrupt, leaving the main loop to start it and wait for the result.

Data path: main starts the condition → I2C1_EV_IRQHandler reads the latest event → it sends the address, sends data, or reads data per event → the end sets the finish flag → the main loop waits for the flag and calls Buffercmp

1. Master initialization: set the pins and master parameters, then enable the event, buffer, and error interrupts.

src/main.csrc/main.c · i2c_master_init
    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2c1_master);
    i2c1_master.BusMode     = I2C_BUSMODE_I2C;
    i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    i2c1_master.OwnAddr1    = I2C_MASTER_ADDR;
    i2c1_master.AckEnable   = I2C_ACKEN;
    i2c1_master.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_master.ClkSpeed    = 100000; // 100000 100K

    I2C_Init(I2Cx, &i2c1_master);
    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
    // int enable
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_ConfigurationMaster(1);
    I2C_Enable(I2Cx, ENABLE);
    return 0;

Enable the interrupts after the parameters are set, so the ISR does not run before the bus is configured.

2. Interrupt priority: the event and error lines are configured separately.

src/main.csrc/main.c · NVIC_ConfigurationMaster
void NVIC_ConfigurationMaster(uint8_t ch)
{
    NVIC_InitType NVIC_InitStructure;
    if (ch == 1)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    }
    if (ch == 2)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
    }
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    if (ch == 1)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    }
    if (ch == 2)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn;
    }
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

Order the two priorities against the other system interrupts so a high-priority interrupt does not hold the bus too long.

3. Event interrupt: read the latest event and branch to send the address, send data, or read data.

src/main.csrc/main.c · I2C1_EV_IRQHandler
void I2C1_EV_IRQHandler(void)
{
    uint32_t last_event = 0;

    last_event = I2C_GetLastEvent(I2C1);
    if ((last_event & I2C_ROLE_MASTER) == I2C_ROLE_MASTER) // master mode
    {
        switch (last_event)
        {
        case I2C_EVT_MASTER_MODE_FLAG: // EV5 Send addr

            if(flag_trans_direct)     // read
            {
                Memset(rx_buf, 0, TEST_BUFFER_SIZE); // clear recv buf, ready to recv data
                I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_RECV);
                rx_num = 0;
            }
            else // write
            {
                I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
                tx_num = 0;
            }
            break;

           // MasterTransmitter
        case I2C_EVT_MASTER_TXMODE_FLAG: // EV6 Send first data
            Comm_Flag = C_READY;
            I2C_SendData(I2C1, tx_buf[tx_num++]);
            break;

        case I2C_EVT_MASTER_DATA_SENDING: // EV8 Sending data

            if (tx_num < TEST_BUFFER_SIZE)
            {
                I2C_SendData(I2C1, tx_buf[tx_num++]);
            }
            break;

        case I2C_EVT_MASTER_DATA_SENDED: // EV8_2 Send finish

            if (tx_num == TEST_BUFFER_SIZE)
            {
                if (Comm_Flag == C_READY)
                {
                    Comm_Flag = C_STOP_BIT;
                    I2C_GenerateStop(I2C1, ENABLE);
                    flag_master_send_finish = 1;
                }
            }
            break;

            // MasterReceiver
        case I2C_EVT_MASTER_RXMODE_FLAG: // EV6
            Comm_Flag = C_READY;
            if (TEST_BUFFER_SIZE == 1)
            {
                I2C_ConfigAck(I2C1, DISABLE);
                if (Comm_Flag == C_READY)
                {
                    Comm_Flag = C_STOP_BIT;
                    I2C_GenerateStop(I2C1, ENABLE);
                }
            }
            else if (TEST_BUFFER_SIZE == 2)
            {
                I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
                I2C_ConfigAck(I2C1, DISABLE);
            }
            break;

        case I2C_EVT_MASTER_DATA_RECVD_FLAG: // EV7.one byte recved
        case I2C_EVT_MASTER_DATA_RECVD_BSF_FLAG: // EV7.When the I2C communication rate is too high, BSF = 1

            rx_buf[rx_num++] = I2C_RecvData(I2C1);
            if (rx_num == (TEST_BUFFER_SIZE - 1))
            {
                I2C_ConfigAck(I2C1, DISABLE);   // Disable I2C1 acknowledgement.
                if (Comm_Flag == C_READY)
                {
                    Comm_Flag = C_STOP_BIT;
                    I2C_GenerateStop(I2C1, ENABLE); // Send I2C1 STOP Condition.
                }
            }
            else if (rx_num == TEST_BUFFER_SIZE)
            {
                flag_master_recv_finish = 1;
            }
            break;
        case 0x00030201: // Arbitration lost
        case 0x00030101: // Acknowledge failure
        case 0x00030501: // Acknowledge failure and Bus error
            I2C_Enable(I2C1, DISABLE);
            I2C_Enable(I2C1, ENABLE);
            break;

        default:
            log_info("I2C error status:0x%x\r\n", last_event);
            break;
        }
    }
}

Cover both the normal and error branches; the error branch resets the peripheral before retrying.

4. Error interrupt: on an acknowledge failure, clear the flag and issue the stop condition.

src/main.csrc/main.c · I2C1_ER_IRQHandler
void I2C1_ER_IRQHandler(void)
{
   if(I2C_GetFlag(I2C1, I2C_FLAG_ACKFAIL))
   {
      I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
      I2C_GenerateStop(I2C1, ENABLE); // Send I2C1 STOP Condition.
   }
}

Keep only the minimal recovery in the error interrupt and leave the retry logic to the upper layer.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.4 I2C_Master_Dualaddr master with two slave addresses

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master_Dualaddr/

Enable the GPIO and I2C clocks→Configure the master parameters→Transfer and compare with the first address→Transfer and compare with the second address

Item Source analysis
Function Act as a master that transfers to two different slave addresses in turn and compares each result.
Source files src/main.c holds the master initialization, the address-parameterized send and receive, the main loop, and the recovery callback;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the two slave addresses, buffers, and status;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, master address 0x30 and slave addresses 0x10 and 0x20;
Acknowledge: enabled for receive.
Program and data path main -> i2c_master_init configures the master -> it first uses address 0x10 with i2c_master_send and i2c_master_recv -> then repeats with address 0x20 -> each pair is compared by Buffercmp.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_ConfigAck, I2C_ConfigNackLocation
Verification After compiling and resetting, both addresses print a pass when the transfers match;
you can watch the two compare results in debug mode.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example passes the slave address as a function parameter and reuses one transfer flow to reach two slaves in turn.

Data path: main fills the data → i2c_master_init configures the master → i2c_master_send(tx, len, 0x10) and i2c_master_recv(rx, len, 0x10) → Buffercmp compares → the buffer is cleared and the 0x20 cycle repeats

1. Master initialization matches the single-address example, with the same speed and addressing.

src/main.csrc/main.c · i2c_master_init
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
    GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin

    GPIO_InitStruct(&i2c1_gpio);
    /*PB10 -- SCL; PB11 -- SDA*/
    i2c1_gpio.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    i2c1_gpio.GPIO_Mode  = GPIO_MODE_AF_OD;
    i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
    i2c1_gpio.GPIO_Pull = GPIO_PULL_UP;
    GPIO_InitPeripheral(GPIOx, &i2c1_gpio);

    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2c1_master);
    i2c1_master.BusMode     = I2C_BUSMODE_I2C;
    i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2; //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
    i2c1_master.OwnAddr1    = I2C_MASTER_ADDR;
    i2c1_master.AckEnable   = I2C_ACKEN;
    i2c1_master.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_master.ClkSpeed    = 100000;

    I2C_Init(I2Cx, &i2c1_master);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);
    return 0;

To support different peripheral speeds, you can make the speed an initialization parameter too.

2. The send path writes the address into the device-address byte; the rest of the flow is unchanged.

src/main.csrc/main.c · i2c_master_send
    if (Comm_Flag == C_READY)
    {
        Comm_Flag = C_START_BIT;
        I2C_GenerateStart(I2C1, ENABLE);
    }

    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_MODE);
            return 1;
        }
    }

    I2C_SendAddr7bit(I2C1, addr, I2C_DIRECTION_SEND);
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG)) // EV6
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_TXMODE);
            return 1;
        }
    }
    Comm_Flag = C_READY;

The address is caller-supplied, so porting only needs the target address replaced at the call site.

3. Receive addresses the device with the passed-in value and keeps the generic tail-byte handling.

src/main.csrc/main.c · i2c_master_recv
    if (Comm_Flag == C_READY)
    {
        Comm_Flag = C_START_BIT;
        I2C_GenerateStart(I2C1, ENABLE);
    }

    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_MODE);
            return 1;
        }
    }
    // send addr
    I2C_SendAddr7bit(I2C1, addr, I2C_DIRECTION_RECV);
    I2CTimeout = I2CT_LONG_TIMEOUT;
    while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_RXMODE_FLAG)) // EV6
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_RXMODE);
            return 1;
        }
    }
    Comm_Flag = C_READY;

    if (len == 1)
    {
        I2C_ConfigAck(I2C1, DISABLE);
        (void)(I2C1->STS1); /// clear ADDR
        (void)(I2C1->STS2);

Between the two receives, clear the buffer and reset the index so the previous round does not affect the compare.

4. The main loop runs one send, receive, and compare cycle per address.

src/main.csrc/main.c · main
    i2c_master_init();

    /* Fill the buffer to send */
    for (i = 0; i < TEST_BUFFER_SIZE; i++)
    {
        tx_buf[i] = i;
    }

     /* First write in the memory followed by a read of the written data --------*/
    /* Write data*/
    i2c_master_send(tx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_1);

    /* Read data */
    i2c_master_recv(rx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_1);

    /* Check if the data written to the memory is read correctly */
    test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
    if (test_status == PASSED)
    {
        log_info("write and read data the same, i2c master addr1 test pass\r\n");

    }
    else
    {
        log_info("write and read data are different, i2c master addr1 test fail\r\n");
    }
    /* test_status = PASSED, if the write and read data are the same  */
    /* test_status = FAILED, if the write and read data are different */

    /* delay  */
    for (i = 0; i < 5000; i++);

    /* Empty the rx buffer  */
    for (i = 0; i < TEST_BUFFER_SIZE; i++)
    {
        rx_buf[i] = 0;
    }
    /* First write in the memory followed by a read of the written data --------*/
    /* Write data*/
    i2c_master_send(tx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_2);

    /* Read data */
    i2c_master_recv(rx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_2);

    /* Check if the data written to the memory is read correctly */
    test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);

Leave a delay between the two rounds so the slave has time to finish before the next round.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.5 I2C_Slave polled slave transfer

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave/

Enable the GPIO and I2C clocks→Configure the own address and slave parameters→Wait for the address match and receive under polling→Send the received bytes back after reception→Loop the transfer

Item Source analysis
Function Act as a slave that polls for the master, receives a data block, sends it back unchanged, and repeats.
Source files src/main.c holds the slave initialization, send, receive, main loop, and recovery callback;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the own address, buffers, and status;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, own address 0x10;
Acknowledge: enabled.
Program and data path main -> i2c_slave_init configures the slave -> i2c_slave_recv waits for the address match and receives -> i2c_slave_send sends the bytes back -> the cycle repeats.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_CheckEvent, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ClrFlag
Verification After compiling and resetting, the slave follows the master’s pace when paired with the master example;
you can watch in debug mode whether the received and sent data match.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example polls as a slave until its address matches, then sends the received data back unchanged.

Data path: main loops → i2c_slave_recv waits for the address match and receives byte by byte → i2c_slave_send sends the bytes back → the buffer is cleared for the next round

1. Slave initialization: write the own address into the parameters and enable the acknowledge.

src/main.csrc/main.c · i2c_slave_init
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
    GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
    GPIO_InitStruct(&i2c1_gpio);
    /*PB10 -- SCL; PB11 -- SDA*/
    i2c1_gpio.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    i2c1_gpio.GPIO_Mode  = GPIO_MODE_AF_OD;
    i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
    GPIO_InitPeripheral(GPIOx, &i2c1_gpio);

    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2c1_slave);
    i2c1_slave.BusMode     = I2C_BUSMODE_I2C;
    i2c1_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;   //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
    i2c1_slave.OwnAddr1    = I2C_SLAVE_ADDR;
    i2c1_slave.AckEnable   = I2C_ACKEN;
    i2c1_slave.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_slave.ClkSpeed    = 100000;

    I2C_Init(I2Cx, &i2c1_slave);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);

Keep the own address out of the reserved range and consistent with the master when you change it.

2. Send flow: after the address matches, send byte by byte and finish on a stop or a missing acknowledge.

src/main.csrc/main.c · i2c_slave_send
int i2c_slave_send(uint8_t* data, int len)
{
    uint32_t lastevent;
    uint8_t* sendBufferPtr = data;
    I2CTimeout = I2CT_LONG_TIMEOUT*10;

    while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR_MATCHED))/* send addr matched */
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(SLAVE_MODE);
            return 1;
        }
    }
    I2CTimeout = I2CT_LONG_TIMEOUT;


    I2C_Enable(I2C1, ENABLE);
    I2C_SendData(I2C1, *sendBufferPtr++);
    while (1)
    {
        lastevent = I2C_GetLastEvent(I2C1);
        if (lastevent == I2C_EVT_SLAVE_DATA_SENDED)
        {
            I2C_SendData(I2C1, *sendBufferPtr++);
            I2CTimeout = I2CT_LONG_TIMEOUT;
        }
        else if ((lastevent == I2C_EVT_SLAVE_ACK_MISS) || (lastevent == I2C_EVT_SLAVE_STOP_RECVD))
        {
            I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
            I2C_Enable(I2C1, ENABLE);
            break;
        }
        else if ((lastevent & 0x00000400) == 0x00000400)
        {
            CommTimeOut_CallBack(SLAVE_BUSERR);
            return 1;
        }
        else
        {
            if ((I2CTimeout--) == 0)
            {
                CommTimeOut_CallBack(SLAVE_UNKNOW);
                return 1;
            }
        }
    }

The slave cannot control when the stop comes, so the length must match what the master expects.

3. Receive flow: after the address matches, receive byte by byte and finish on the stop condition.

src/main.csrc/main.c · i2c_slave_recv
int i2c_slave_recv(uint8_t* data, uint32_t rcv_len)
{
    uint32_t lastevent;
    rxDataNum = 0;
    I2CTimeout = I2CT_LONG_TIMEOUT*10;

    while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_RECV_ADDR_MATCHED))/* receive addr matched */
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(SLAVE_MODE);
            return 1;
        }
    }
    I2CTimeout = I2CT_LONG_TIMEOUT;

    I2C_Enable(I2C1, ENABLE);
    while (1)
    {
        lastevent = I2C_GetLastEvent(I2C1);
        if (lastevent == I2C_EVT_SLAVE_DATA_RECVD)
        {
            *data++ = I2C_RecvData(I2C1);
            rxDataNum++;
            I2CTimeout = I2CT_LONG_TIMEOUT;
        }
        else if (lastevent == I2C_EVT_SLAVE_STOP_RECVD)
        {
            I2C_Enable(I2C1, ENABLE);
            break;
        }
        else if ((lastevent & 0x00000400) == 0x00000400)
        {
            CommTimeOut_CallBack(SLAVE_BUSERR);
            return 1;
        }
        else
        {
            if ((I2CTimeout--) == 0)
            {
                CommTimeOut_CallBack(SLAVE_UNKNOW);
                return 1;
            }
        }
    }

    return 0;

Match the receive count to the sender; change the length on both sides together.

4. The main loop repeats the receive and send, clearing the buffer after each round.

src/main.csrc/main.c · main
int main(void)
{
    uint16_t i = 0;

    log_init();
    log_info("this is a i2c slave test demo\r\n");
    /* Initialize the I2C EEPROM driver ----------------------------------------*/
    i2c_slave_init();
    while (1)
    {
        for(; ;)
        {
            /* Read data */
            if(i2c_slave_recv(data_buf, TEST_BUFFER_SIZE))
            {
                break;  /* receive fail, reaccept*/
            }

            /* Write data*/
            if(i2c_slave_send(data_buf, TEST_BUFFER_SIZE))
            {
                break;  /* send fail, reaccept*/
            }
            for (i = 0; i < TEST_BUFFER_SIZE; i++)
            {
                data_buf[i] = 0;
            }
        }
    }
}

The failure branch loops back to retry; add back-off and logging as needed when you port it.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.6 I2C_Slave_Int interrupt-driven slave transfer

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Int/

Enable the GPIO and I2C clocks→Configure the slave parameters, interrupts, and priority→Handle address and data events inside the event interrupt→Wait for the finish flag in the main loop

Item Source analysis
Function Act as an interrupt-driven slave for receive and send, leaving the main loop to wait on a finish flag.
Source files src/main.c holds the slave initialization, the interrupt service routine, and the main loop;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the own address, buffers, and flags;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, own address 0x10;
Interrupts: event and error interrupts enabled, event priority 2 and error priority 3.
Program and data path main -> i2c_slave_init configures the slave and enables the interrupts -> I2C1_EV_IRQHandler transfers per event and sets the finish flag -> the main loop waits on the flag.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigInt, I2C_GetLastEvent, I2C_RecvData, I2C_SendData, I2C_ClrFlag
Verification After compiling and resetting, the serial log prints the receive and send lengths when paired with the master example;
you can watch the event value inside the interrupt to confirm the ordered progress.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example moves the slave transfer into the event interrupt, leaving the main loop to wait on the receive and send flags.

Data path: I2C1_EV_IRQHandler reads the latest event → it prepares the transfer on an address match → it moves data through the data register on a data event → a stop event sets the finish flag → the main loop waits for the flag and prints the length

1. Slave interrupt initialization: after the parameters are set, enable the event, buffer, and error interrupts.

src/main.csrc/main.c · i2c_slave_init
int i2c_slave_init(void)
{
    I2C_InitType i2c1_slave;
    GPIO_InitType i2c1_gpio;
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
    RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
    GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin

    GPIO_InitStruct(&i2c1_gpio);
    /*PB10 -- SCL; PB11 -- SDA*/
    i2c1_gpio.Pin        = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
    i2c1_gpio.GPIO_Mode  = GPIO_MODE_AF_OD;
    i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
    GPIO_InitPeripheral(GPIOx, &i2c1_gpio);

    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2c1_slave);
    i2c1_slave.BusMode     = I2C_BUSMODE_I2C;
    i2c1_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    i2c1_slave.OwnAddr1    = I2C_SLAVE_ADDR;
    i2c1_slave.AckEnable   = I2C_ACKEN;
    i2c1_slave.AddrMode    = I2C_ADDR_MODE_7BIT;
    i2c1_slave.ClkSpeed    = 100000; // 100000 100K

    I2C_Init(I2Cx, &i2c1_slave);
    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
    // int enable
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_ConfigurationSlave((uint8_t)1);
    I2C_Enable(I2Cx, ENABLE);
    return 0;

Enable the interrupts after the parameters are set, so they do not fire before the bus is ready.

2. Interrupt priority: the event and error lines are configured separately.

src/main.csrc/main.c · NVIC_ConfigurationSlave
void NVIC_ConfigurationSlave(uint8_t ch)
{
    NVIC_InitType NVIC_InitStructure;
    if (ch == 1)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
    }
    if (ch == 2)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
    }
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 2;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    if (ch == 1)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
    }
    if (ch == 2)
    {
        NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn;
    }
    NVIC_InitStructure.NVIC_IRQChannelPriority           = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

Order the priorities against the other system interrupts so the preemption stays correct.

3. Event interrupt: distinguish the receive and send address matches, then move data on the data events.

src/main.csrc/main.c · I2C1_EV_IRQHandler
void I2C1_EV_IRQHandler(void)
{
    uint8_t timeout_flag = 0;
    uint32_t last_event = 0;

    last_event = I2C_GetLastEvent(I2C1);
    if ((last_event & I2C_ROLE_MASTER) != I2C_ROLE_MASTER) // MSMODE = 0:I2C slave mode
    {
        switch (last_event)
        {
        case I2C_EVT_SLAVE_RECV_ADDR_MATCHED: //EV1 Rx addr matched

            // clear flag,ready to receive data
            rxDataNum = 0;
            break;

        case I2C_EVT_SLAVE_SEND_ADDR_MATCHED: //EV1 Tx addr matched

            rxDataNum = 0;
            I2C1->DAT = data_buf[rxDataNum++];// Send first data
            break;

        // SlaveTransmitter
        case I2C_EVT_SLAVE_DATA_SENDING:  //EV3 Sending data

            break;

        case I2C_EVT_SLAVE_DATA_SENDED:
            I2C1->DAT = data_buf[rxDataNum++];
            break;

        // SlaveReceiver
        case I2C_EVT_SLAVE_DATA_RECVD: //EV2 one byte recved
            data_buf[rxDataNum++] = I2C1->DAT;
            break;


        case I2C_EVT_SLAVE_STOP_RECVD: // EV4
            I2C_Enable(I2C1, ENABLE);
            if(rxDataNum != 0)
            {
                flag_slave_recv_finish = 1; // The STOPF bit is not set after a NACK reception
            }
            break;

        default:
            I2C_Enable(I2C1, ENABLE);
            timeout_flag = 1;
            break;
        }
    }

    if (timeout_flag)
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(SLAVE_UNKNOW);
        }
    }
    else
    {
        I2CTimeout = I2CT_LONG_TIMEOUT;
    }

In slave mode the handler must check the role and process only the slave events.

4. Error interrupt: on a missing acknowledge, clear the flag and decide the finish flag by whether this was the last byte.

src/main.csrc/main.c · I2C1_ER_IRQHandler
void I2C1_ER_IRQHandler(void)
{
    uint32_t last_event;
    last_event = I2C_GetLastEvent(I2C1);
    if(last_event == I2C_EVT_SLAVE_ACK_MISS)
    {
        I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
        if(rxDataNum != 0)  //slave send the last data and recv NACK
        {
            flag_slave_send_finish = 1;
        }
        else //not the last data recv nack, send fail
        {
        }
    }
    else if(last_event == 0x601D0 || last_event == 0x601C0)
    {
        CommTimeOut_CallBack(SLAVE_UNKNOW);
    }
}

A missing acknowledge with a zero count means the send was not accepted and must be repeated.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.7 I2C_Slave_Dualaddr slave with two own addresses

Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Dualaddr/

Enable the GPIO and I2C clocks→Configure own address 1 and enable dual addressing→Set own address 2→Receive and send back under each of the two addresses

Item Source analysis
Function Act as a slave with two own addresses, each answering the master and transferring data.
Source files src/main.c holds the slave initialization, the address-parameterized send and receive, the main loop, and the recovery callback;
src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;
inc/main.h declares the two own addresses, buffers, and status;
readme.txt gives the configuration and test steps;
the project files are in MDK-ARM/ and EWARM/.
Key configuration Pins: SCL on PB6 and SDA on PB7, open-drain;
Speed: 100 K;
Addressing: 7-bit, own addresses 0x10 and 0x20;
Dual addressing: enabled.
Program and data path main -> i2c_slave_init configures own address 1 and enables dual addressing -> address 2 is written through I2C_ConfigOwnAddr2 -> i2c_slave_recv and i2c_slave_send run for address 1 and address 2 in turn.
Main APIs I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_CheckEvent, I2C_RecvData, I2C_SendData, I2C_ClrFlag
Verification After compiling and resetting, both addresses transfer when paired with the dual-address master example;
you can watch the two addresses in debug mode.
Use note SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6;
the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board;
the system clock is 64 MHz.
Core-code walkthrough and porting

The example gives the slave two own addresses and reuses one transfer flow to answer either master address.

Data path: i2c_slave_init writes own address 1 and enables address 2 and dual addressing → i2c_slave_recv waits for the address match and receives → i2c_slave_send sends back for that address → the main loop runs one round per address

1. Slave initialization: write own address 1, then write address 2 and enable dual addressing.

src/main.csrc/main.c · i2c_slave_init
    I2C_Init(I2Cx, &i2c1_slave);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
    I2C_ConfigOwnAddr2(I2C1,I2C_SLAVE_ADDR_2);
    I2C_EnableDualAddr(I2C1,ENABLE);

    I2C_Enable(I2Cx, ENABLE);

Address 1 goes through the init parameters while address 2 is written separately; neither may fall in the reserved range.

2. Send: pick the matching event from the passed-in address, then send byte by byte.

src/main.csrc/main.c · i2c_slave_send
    if(addr == 0)
    {
        while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR_MATCHED))/* send addr1 matched */
        {
        }
    }
    else
    {
        while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR2_MATCHED))/* send addr2 matched */
        {
        }
    }

The two addresses map to different event values; re-check the matching event after an address change.

3. Receive: wait for the match of the passed-in address, then receive byte by byte and wait for the stop.

src/main.csrc/main.c · i2c_slave_recv
    I2C_Enable(I2C1, ENABLE);
    while (Numbytetoread != 0)
    {
        I2CTimeout = I2CT_LONG_TIMEOUT;
        while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_DATA_RECVD))
        {
            if ((I2CTimeout--) == 0)
            {
                    CommTimeOut_CallBack(SLAVE_MODE);
                    return 1;
            }
        }
        *data++ = I2C_RecvData(I2C1);
        Numbytetoread--;
    }

Wait for the stop only after the count is used up, so no byte is dropped early.

4. The main loop runs one receive and send round for address 1 and address 2.

src/main.csrc/main.c · main
        /* Read data */
        i2c_slave_recv(data_buf, TEST_BUFFER_SIZE, 0);

        /* Write data*/
        i2c_slave_send(data_buf, TEST_BUFFER_SIZE, 0);


        /* address 2 */
        /* Read data */
        i2c_slave_recv(data_buf, TEST_BUFFER_SIZE, 1);

        /* Write data*/
        i2c_slave_send(data_buf, TEST_BUFFER_SIZE, 1);
    }

Keep the round order consistent with the master; making the address a parameter lets you reuse the flow.

Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.4 Notes and troubleshooting

Scenario Check & action Source
SDA and SCL pins The I2C chapter of the N32G033 user manual states that SDA and SCL are open-drain and need external pull-ups, with both lines high when the bus is idle;
pick the pull-up value from the bus speed and the capacitive load.
User manual
Clock stretching The I2C chapter of the N32G033 user manual states that you must check whether the slave supports clock stretching;
if NOEXTEND is cleared to allow stretching, a slave holding SCL low widens the bus clock;
keep NOEXTEND asserted for slaves that do not support stretching.
User manual
Software reset The I2C chapter of the N32G033 user manual states that before setting SWRESET you must confirm the I2C pins are released and the bus is idle, or the reset may interrupt an ongoing transfer. User manual
When the configuration registers are writable The I2C chapter of the N32G033 user manual states that the clock and filter configuration registers are writable only while the EN bit of I2C_CTRL1 is 0;
disable the I2C before changing a divider or a filter.
User manual
Analog filters The I2C chapter of the N32G033 user manual states that the SDA and SCL analog filters are enabled by default;
disable them only by an explicit register write, and re-check the bus waveform afterwards.
User manual
Last byte of a master receive The I2C chapter of the N32G033 user manual states that before receiving the last one or two bytes the master must set the NACK position, disable the acknowledge, and then issue the stop condition. User manual
STOP setup time in standard mode Errata sheet V1.0.0 (2025.9.25) lists this issue for revision A silicon: in master mode at 100 K, once slave clock stretching is triggered the STOP setup time falls below 4 us;
lower the speed to 50 K or below according to the slave timing requirement.
Errata sheet

Errata sheet V1.0.0 (2025.9.25) covers revision A silicon; its I2C interface section lists the STOP setup time limit in standard mode, as shown in the last row above.

Nations Technologies DevKit Docs

Datasheets, SDK, driver APIs and demo guides for Nations Technologies MCU development kits, organized by chip series.

© 2026 Nations Technologies Inc. All rights reserved.粤ICP备11069485号This site uses cookie-free anonymous analytics.