5.16 I2C Guide

The I2C driver of the N32H49x standard peripheral library supports 7-bit and 10-bit addressing, general call, SMBus/PMBus, PEC, analog and digital filtering, FIFO, and timeout detection. This chapter covers the files of the driver, its 53 public functions, and the 12 examples EEPROM_Polling, EEPROM_Int, EEPROM_DMA, I2C_Master, I2C_Slave, I2C_Master_Int, I2C_Slave_Int, I2C_Master_FIFO, I2C_Slave_FIFO, I2C_10bit, PMBUS_Master, and PMBUS_Slave. For bus timing, clock synchronization and arbitration, error conditions, and register bit definitions, read it together with the “I2C” chapter of the user manual.

5.16.1 Related files

Relative path Purpose
firmware/n32h49x_std_periph_driver/inc/n32h49x_i2c.h Defines the I2C_InitType structure and the macros for bus mode, duty cycle, addressing mode, acknowledge, PEC, interrupts, DMA, FIFO, filtering, and timeout;
declares the 53 public functions grouped as reset and initialization, start/stop conditions and addressing, interrupts and DMA, data transfer, PEC and filtering, FIFO, and timeout and status query
firmware/n32h49x_std_periph_driver/src/n32h49x_i2c.c Implements the 53 functions: register reset and initialization, start/stop conditions and address transmission, interrupt and DMA enabling, data and FIFO transfer, PEC and filter configuration, and timeout thresholds and status query
projects/n32h49x_EVAL/examples/I2C/ I2C example root, containing the 12 examples EEPROM_Polling, EEPROM_Int, EEPROM_DMA, I2C_Master, I2C_Slave, I2C_Master_Int, I2C_Slave_Int, I2C_Master_FIFO, I2C_Slave_FIFO, I2C_10bit, PMBUS_Master, and PMBUS_Slave
projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/ Project layout of that example: inc/, src/, readme.txt, MDK-ARM/, EWARM/
projects/n32h49x_EVAL/bsp/ Board support files shared by the examples: evaluation-board LED and key definitions, a millisecond delay function, and the log print interface

5.16.2 API reference

Search API53 functions

The 53 public functions below follow the declaration order of n32h49x_i2c.h and fall into 15 groups; each entry gives a summary and the original comment directly above the function in n32h49x_i2c.c.

Initialization

I2C_DeInit, I2C_Init, I2C_InitStruct, I2C_Enable

Start/stop and acknowledge

I2C_GenerateStart, I2C_GenerateStop, I2C_ConfigAck, I2C_SendAddr7bit

Own address and general call

I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_EnableGeneralCall

Interrupts, DMA, and software reset

I2C_ConfigInt, I2C_EnableDMA, I2C_EnableDmaLastSend, I2C_EnableSoftwareReset

Data transfer

I2C_SendData, I2C_RecvData

NACK/PEC position and SMBus alert

I2C_ConfigNackLocation, I2C_ConfigPecLocation, I2C_ConfigSmbusAlert

PEC and ARP

I2C_SendPEC, I2C_ComputePec, I2C_GetPec, I2C_EnableArp

Clock stretching and fast mode

I2C_EnableExtendClk, I2C_ConfigFastModeDutyCycle

Digital and analog filtering

I2C_SetSDADigitalFilterWidth, I2C_SetSCLDigitalFilterWidth, I2C_SetSDAAnalogFilterWidth, I2C_EnableSDAAnalogFilter, I2C_SetSCLAnalogFilterWidth, I2C_EnableSCLAnalogFilter

Flag and interrupt clearing

I2C_ClrFlag, I2C_ClrIntPendingBit

FIFO management

I2C_ClrFIFO, I2C_EnableFIFO, I2C_EnableDMAFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold

Byte count control

I2C_EnableByteNum, I2C_SetMasterReceivedDataBytesNum, I2C_SetByteNumLastStartStop

FIFO data transfer

I2C_SendFIFOData, I2C_RecvFIFOData

Timeout detection

I2C_SetTimeoutLowLimit, I2C_SetTimeoutHighLimit, I2C_EnableTimeoutDetect

Status query

I2C_GetRegister, I2C_GetIntStatus, I2C_CheckFifoEvent, I2C_CheckEvent, I2C_GetLastEvent, I2C_GetFlag

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
          - I2C3
          - I2C4
return  none
void I2C_Init(I2C_Module* I2Cx,I2C_InitType* I2C_InitStruct)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
          - I2C3
          - I2C4
param   I2C_InitStruct :
          - 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_InitStruct)Fills each I2C_InitStruct member with its default value.
Source comment
name    I2C_InitStruct.
fun     Fills each I2C_InitStruct member with its default value.
param   I2C_InitStruct :
          - ClkSpeed
          - BusMode
          - FmDutyCycle
          - OwnAddr1
          - AckEnable
          - AddrMode
return  none
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStart(I2C_Module* I2Cx, FunctionalState Cmd)Generates I2Cx communication START condition.
Source comment
name    I2C_GenerateStart.
fun     Generates I2Cx communication START condition.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStop(I2C_Module* I2Cx, FunctionalState Cmd)Generates I2Cx communication STOP condition.
Source comment
name    I2C_GenerateStop.
fun     Generates I2Cx communication STOP condition.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigAck(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C acknowledge feature.
Source comment
name    I2C_ConfigAck.
fun     Enables or disables the specified I2C acknowledge feature.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_SendAddr7bit(I2C_Module* I2Cx, uint8_t Address, uint8_t I2C_Direction)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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Address
        - 0 to 0xFF
param   I2C_Direction :
          - I2C_DIRECTION_SEND Transmitter mode
          - I2C_DIRECTION_RECV Receiver mode
return  none
void I2C_ConfigOwnAddr2(I2C_Module* I2Cx, uint8_t Address)Configures the specified I2C own address2.
Source comment
name    I2C_ConfigOwnAddr2.
fun     Configures the specified I2C own address2.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Address
          - 0 to 0xFF (7bit I2C own address2)
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables or disables the specified I2C interrupts.
Source comment
name    I2C_ConfigInt.
fun     Enables or disables the specified I2C interrupts.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_IT :
          - I2C_INT_BUF            buffer interrupt enable
          - I2C_INT_EVENT          event interrupt enable
          - I2C_INT_ERR            error full interrupt enable
          - I2C_INT_FIFOFEN        FIFO full interrupt enable
          - I2C_INT_FIFOEEN        FIFO empty interrupt enable
          - I2C_INT_FIFOHFEN       FIFO half full interrupt enable
          - I2C_INT_FIFOHEEN       FIFOHE half empty interrupt enable
          - I2C_INT_FIFOUREN       FIFO under-run interrupt enable
          - I2C_INT_FIFOOREN       FIFO over-run interrupt enable
          - I2C_INT_DMAETOERREN    DMA ETO error interrupt enable
          - I2C_INT_SDALTOEN       SDA low timeout interrupt enable
          - I2C_INT_SCLHTOEN       SCL high timeout interrupt enable
          - I2C_INT_SCLLTOEN       SCL low timeout interrupt enable
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
return  The value of the received data
void I2C_EnableDMA(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C DMA requests.
Source comment
name    I2C_EnableDMA.
fun     Enables or disables the specified I2C DMA requests.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_EnableDmaLastSend(I2C_Module* I2Cx, FunctionalState Cmd)Specifies if the next DMA transfer will be the last one.
Source comment
name    I2C_EnableDmaLastSend.
fun     Specifies if the next DMA transfer will be the last one.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigNackLocation(I2C_Module* I2Cx, uint32_t I2C_NACKPosition)Selects the specified I2C NACK position in master receiver mode.
Source comment
name    I2C_ConfigNackLocation.
fun     Selects the specified I2C NACK position in master receiver mode.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_NACKPosition :
          - I2C_NACK_POS_NEXT
          - I2C_NACK_POS_CURRENT
return  none
void I2C_ConfigPecLocation(I2C_Module* I2Cx, uint32_t I2C_PECPosition)Selects the specified I2C PEC position.
Source comment
name    I2C_ConfigPecLocation.
fun     Selects the specified I2C PEC position.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_PECPosition :
          - I2C_PEC_POS_NEXT
          - I2C_PEC_POS_CURRENT
return  none
void I2C_ConfigSmbusAlert(I2C_Module* I2Cx, uint32_t I2C_SMBusAlert)Drives the SMBusAlert pin high or low for the specified I2C.
Source comment
name    I2C_ConfigSmbusAlert.
fun     Drives the SMBusAlert pin high or low for the specified I2C.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_SMBusAlert :
          - I2C_SMBALERT_LOW
          - I2C_SMBALERT_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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
uint8_t I2C_GetPec(I2C_Module* I2Cx)Enables or disables the PEC value calculation of the transferred bytes.
Source comment
name    I2C_GetPec.
fun     Enables or disables the PEC value calculation of the transferred bytes.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
return  none
void I2C_EnableArp(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C ARP.
Source comment
name    I2C_EnableArp.
fun     Enables or disables the specified I2C ARP.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_EnableExtendClk(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C Clock stretching.
Source comment
name    I2C_EnableExtendClk.
fun     Enables or disables the specified I2C Clock stretching.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigFastModeDutyCycle(I2C_Module* I2Cx, uint32_t DutyCycle)Selects the specified I2C fast mode duty cycle.
Source comment
name    I2C_ConfigFastModeDutyCycle.
fun     Selects the specified I2C fast mode duty cycle.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   DutyCycle :
          - I2C_FMDUTYCYCLE_16_9
          - I2C_FMDUTYCYCLE_2
return  none
void I2C_SetSDADigitalFilterWidth(I2C_Module* I2Cx, uint32_t width)SDA digital filter width selection.
Source comment
name    I2C_Digital_Filter_Width_Set.
fun     SDA digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none

Use note: The name field in the source comment is written as I2C_Digital_Filter_Width_Set. and differs from the function name I2C_SetSDADigitalFilterWidth; call it by the function name.

void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint32_t width)SCL digital filter width selection.
Source comment
name    I2C_SetSCLDigitalFilterWidth.
fun     SCL digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none
void I2C_SetSDAAnalogFilterWidth(I2C_Module* I2Cx, uint32_t width)SDA analog filter width selection.
Source comment
name    I2C_SetSDAAnalogFilterWidth.
fun     SDA analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
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 SDA Analog Filter.
Source comment
name    I2C_EnableSDAAnalogFilter.
fun     Enables or disables SDA Analog Filter.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_SetSCLAnalogFilterWidth(I2C_Module* I2Cx, uint32_t width)SCL analog filter width selection.
Source comment
name    I2C_SetSCLAnalogFilterWidth.
fun     SCL analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
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 SCL Analog Filter.
Source comment
name    I2C_EnableSCLAnalogFilter.
fun     Enables or disables SCL Analog Filter.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_ClrFlag(I2C_Module* I2Cx, uint32_t I2C_FLAG)Clears the I2Cx’s pending flags.
Source comment
name    I2C_ClrFlag.
fun     Clears the I2Cx's pending flags.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_FLAG :
          - I2C_FLAG_SMBALERT           SMBus Alert flag
          - 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_SCLLTO                 SCL timeout low error flag
          - I2C_FLAG_SCLHTO                     SCL timeout high error flag
          - I2C_FLAG_SDALTO                 SDA timeout low error flag
          - I2C_FLAG_DMAETOERR              DMA ETO error in reception flag
          - I2C_FLAG_FIFOOR             Write data when FIFO is full
          - I2C_FLAG_FIFOUR             Read data when FIFO is empty
          - I2C_FLAG_FIFOHE                     FIFO half empty flag
          - I2C_FLAG_FIFOHF                     FIFO half full flag
          - I2C_FLAG_FIFOE                      FIFO empty flag
          - I2C_FLAG_FIFOF                      FIFO full 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
void I2C_ClrIntPendingBit(I2C_Module* I2Cx, uint32_t I2C_IT)Clears the I2Cx’s interrupt pending bits.
Source comment
name    I2C_ClrIntPendingBit.
fun     Clears the I2Cx's interrupt pending bits.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Interrupt :
          - I2C_INT_SCLLTO          SCL timeout low error flag
          - I2C_INT_SCLHTO          SCL timeout high error flag
          - I2C_INT_SDALTO          SDA timeout low error flag
          - I2C_INT_FIFOUR  Read data when FIFO is empty
          - I2C_INT_FIFOOR  Write data when FIFO is full
          - I2C_INT_DMAETOERR   Prove that the DMA transmission is complete but no ETO signal is given
          - I2C_INT_SMBALERT    SMBus Alert flag
          - 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
note    - STOPF (STOP detection) is cleared by software sequence: a read operation
            to I2C_STS1 register (I2C_GetIntStatus()) followed by a write operation to
            I2C_CTRL1 register (I2C_Enable() to re-enable the I2C peripheral).
            - ADD10F (10-bit header sent) is cleared by software sequence: a read
            operation to I2C_STS1 (I2C_GetIntStatus()) followed by writing the second
            byte of the address in I2C_DAT register.
            - BSF (Byte Transfer Finished) is cleared by software sequence: a read
            operation to I2C_STS1 register (I2C_GetIntStatus()) followed by a
            read/write to I2C_DAT register (I2C_SendData()).
            - ADDRF (Address sent) is cleared by software sequence: a read operation to
            I2C_STS1 register (I2C_GetIntStatus()) followed by a read operation to
            I2C_STS2 register ((void)(I2Cx->STS2)).
            - STARTBF (Start Bit) is cleared by software sequence: a read operation to
            I2C_STS1 register (I2C_GetIntStatus()) followed by a write operation to
            I2C_DAT register (I2C_SendData()).
return  none
void I2C_ClrFIFO(I2C_Module* I2Cx)Clear the I2C’s FIFO.
Source comment
name    I2C_ClrFIFO.
fun     Clear the I2C's FIFO.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
return  none
void I2C_EnableFIFO(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the FIFO.
Source comment
name    I2C_EnableFIFO.
fun     Enables or disables the FIFO.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_EnableDMAFIFO(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the DMA FIFO.
Source comment
name    I2C_EnableDMAFIFO.
fun     Enables or disables the DMA FIFO.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd :
          - ENABLE
          - DISABLE
return  none
void I2C_SetTxFifoThreshold( I2C_Module* I2Cx, uint8_t TxFifoThreshold)Sets the Tx FIFO threshold of I2C.
Source comment
name    I2C_SetTxFifoThreshold.
fun     Sets the Tx FIFO threshold of I2C.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   TxFifoThreshold:
        - 0x0~0x7
return  none
void I2C_SetRxFifoThreshold( I2C_Module* I2Cx, uint8_t RxFifoThreshold)Sets the Rx FIFO threshold of I2C.
Source comment
name    I2C_SetRxFifoThreshold.
fun     Sets the Rx FIFO threshold of I2C.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   RxFifoThreshold:
        - 0x0~0x7
return  none
void I2C_EnableByteNum(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C peripheral ByteNum function.
Source comment
name    I2C_EnableByteNum.
fun     Enables or disables the specified I2C peripheral ByteNum function.
param   I2Cx :
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SetMasterReceivedDataBytesNum(I2C_Module* I2Cx, uint16_t Number_Of_bytes)Set the number of data bytes that will be received by master receiver of I2C peripheral.
Source comment
name    I2C_SetMasterReceivedDataBytesNum.
fun     Set the number of data bytes that will be received by master receiver of I2C peripheral.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Number_Of_bytes(the number of data bytes) :
          - 0x0~0x3FFF
return  none
void I2C_SetByteNumLastStartStop(I2C_Module* I2Cx, uint32_t LastStatus)Configure the last status after master have finished sending the required data bytes.
Source comment
name    I2C_SetByteNumLastStartStop.
fun     Configure the last status after master have finished sending the required data bytes.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   LastStatus :
          - I2C_BYTENUM_LAST_STOP
          - I2C_BYTENUM_LAST_START
return  none
void I2C_SendFIFOData(I2C_Module* I2Cx, uint8_t Data)Sets FIFO data volume.
Source comment
name    I2C_SendFIFOData.
fun     Sets FIFO data volume.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   Data
          - 0x00~0xFF
return  none
uint8_t I2C_RecvFIFOData(I2C_Module* I2Cx)Returns FIFO data.
Source comment
name    I2C_RecvFIFOData.
fun     Returns FIFO data.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
return  FIFO data.
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_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 name field in the source comment is written as I2C_EnableTimeout. and differs from the function name I2C_EnableTimeoutDetect; call it by the function name.

uint32_t I2C_GetRegister(I2C_Module* I2Cx, uint8_t I2C_Register)Reads the specified I2C register and returns its value.
Source comment
name    I2C_GetRegister.
fun     Reads the specified I2C register and returns its value.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_Register :
          - I2C_REG_CTRL1 CTRL1 register
          - I2C_REG_CTRL2 CTRL2 register
          - I2C_REG_OADDR1 OADDR1 register
          - I2C_REG_OADDR2 OADDR2 register
          - I2C_REG_DAT DAT register
          - I2C_REG_STS1 STS1 register
          - I2C_REG_STS2 STS2 register
          - I2C_REG_CLKCTRL CLKCTRL register
          - I2C_REG_TMRISE TMRISE register
          - I2C_REG_BYTENUM
          - I2C_REG_GFLTRCTRL
return  none
INTStatus I2C_GetIntStatus(I2C_Module* I2Cx, uint32_t I2C_IT)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 :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_IT :
          - I2C_INT_STARTBF                 Start Bit (Master mode)
          - I2C_INT_ADDRF                   Address sent (master mode)/matched (slave mode)
          - I2C_INT_BSF                     Byte Transfer Finished
          - I2C_INT_STOPF                   Stop detection (Slave mode)
          - I2C_INT_RXDATNE                 Data Register not Empty (receivers)
          - I2C_INT_TXDATE                  Data Register Empty (transmitters)
          - I2C_INT_ADDR10F                 10-bit header sent (Master mode)
          - I2C_INT_ACKFAIL                 Acknowledge Failure
          - I2C_INT_ARLOST                  Arbitration Lost (master mode)
          - I2C_INT_BUSERR                  Bus Error
          - I2C_INT_OVERRUN                 Overrun/Underrun
          - I2C_INT_PECERR                  PEC Error in reception
          - I2C_INT_SMBALERT                SMBus Alert
          - I2C_INT_FIFOF                   FIFO full
          - I2C_INT_FIFOE                   FIFO empty
          - I2C_INT_FIFOHF                  FIFO half full
          - I2C_INT_FIFOHE                  FIFO half empty
          - I2C_INT_FIFOUR          Read data when FIFO is empty
          - I2C_INT_FIFOOR          Write data when FIFO is full
          - I2C_INT_DMAETOERR           Prove that the DMA transmission is complete but no ETO signal is given
          - I2C_INT_SDALTO              SDA timeout low error
          - I2C_INT_SCLHTO              SCL timeout high error
          - I2C_INT_SCLLTO              SCL timeout low error
return  SET or RESET
ErrorStatus I2C_CheckFifoEvent(I2C_Module* I2Cx, uint32_t I2C_FIFO_EVENT)Checks whether the last I2Cx FIFO Event is equal to the one passed as parameter.
Source comment
name    I2C_CheckFifoEvent.
fun     Checks whether the last I2Cx FIFO Event is equal to the one passed as parameter.
param   I2Cx :
          - I2C1
          - I2C2
          - I2C3
          - I2C4
param   I2C_FIFO_EVENT :
          - I2C_EVENT_MASTER_FIFO_TRANSMITTER_MODE_SELECTED
          - I2C_EVENT_MASTER_FIFO_RECEIVER_MODE_SELECTED
          - I2C_EVENT_MASTER_FIFO_MODE_ADDRESS10
          - I2C_EVENT_MASTER_FIFO_TRANSMITTING
          - I2C_EVENT_MASTER_FIFO_TRANSMITTED
          - I2C_EVENT_MASTER_FIFO_STOP_DETECTED
          - I2C_EVENT_MASTER_FIFO_RECEIVING
          - I2C_EVENT_MASTER_FIFO_RECEIVED
          - I2C_EVENT_SLAVE_FIFO_RECEIVER_ADDRESS_MATCHED
          - I2C_EVENT_SLAVE_FIFO_TRANSMITTER_ADDRESS_MATCHED
          - I2C_EVENT_SLAVE_FIFO_RECEIVER_SECONDADDRESS_MATCHED
          - I2C_EVENT_SLAVE_FIFO_TRANSMITTER_SECONDADDRESS_MATCHED
          - I2C_EVENT_SLAVE_FIFO_GENERALCALLADDRESS_MATCHED
          - I2C_EVENT_SLAVE_FIFO_STOP_DETECTED
          - I2C_EVENT_SLAVE_FIFO_RECEIVING
          - I2C_EVENT_SLAVE_FIFO_RECEIVED
          - I2C_EVENT_SLAVE_FIFO_TRANSMITTING
          - I2C_EVENT_SLAVE_FIFO_ACK_FAILURE
note    For detailed description of Events, please refer to section
        I2C_Events in n32h49x_i2c.h file.
return  SUCCESS or ERROR
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
          - I2C3
          - I2C4
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
note    For detailed description of Events, please refer to section
        I2C_Events in n32h49x_i2c.h file.
return  SUCCESS or ERROR
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
          - I2C3
          - I2C4
note    For detailed description of Events, please refer to section
        I2C_Events in n32h49x_i2c.h file.
return  The last event
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
          - I2C3
          - I2C4
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_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_BSF                    Byte transfer finished flag
          - I2C_FLAG_ADDRF              Address sent flag (Master mode) "ADSL"
                                        Address matched flag (Slave mode)"ENDA"
          - I2C_FLAG_STARTBF            Start bit flag (Master mode)
          - I2C_FLAG_SCLLTO                 SCL timeout low error flag
          - I2C_FLAG_SCLHTO                     SCL timeout high error flag
          - I2C_FLAG_SDALTO                 SDA timeout low error flag
          - I2C_FLAG_DMAETOERR              DMA ETO error in reception flag
          - I2C_FLAG_FIFOOR             Write data when FIFO is full
          - I2C_FLAG_FIFOUR             Read data when FIFO is empty
          - I2C_FLAG_FIFOHE                     FIFO half empty flag
          - I2C_FLAG_FIFOHF                     FIFO half full flag
          - I2C_FLAG_FIFOE                      FIFO empty flag
          - I2C_FLAG_FIFOF                      FIFO full flag
return  SET or RESET

5.16.3 Demo descriptions

This section covers the 12 I2C examples: EEPROM_Polling, EEPROM_Int, and EEPROM_DMA use an AT24C02 to demonstrate polling, interrupt, and DMA access; I2C_Master, I2C_Master_Int, I2C_Master_FIFO, I2C_Slave, I2C_Slave_Int, and I2C_Slave_FIFO demonstrate single-master single-slave transfer with 7-bit addressing; I2C_10bit demonstrates 10-bit addressing; PMBUS_Master and PMBUS_Slave demonstrate PMBus communication with PEC.

5.16.3.1 EEPROM_Polling Polling access to an AT24C02

Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/

Initialize I2C1 and the PB6 and PB7 pins→The master polls the device address and the page address→Write the test data page by page and wait for the device to be ready→Send a repeated start and the read address→Read the data back and compare it with the transmit buffer→Print the test result on the UART

Item Source analysis
Function Use I2C1 as a master to access an external AT24C02 EEPROM in polling mode;
write 256 bytes and read them back for a byte-by-byte comparison with the transmit buffer.
Source files src/main.c: main flow and buffer comparison;
src/i2c_eeprom.c and inc/i2c_eeprom.h: the EEPROM read/write driver, the configuration macros, and the timeout callback;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;
the page size I2C_PageSize is 8 bytes and the test length TEST_EEPROM_SIZE is 256 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main calls I2C_EE_Init to configure the pins and the peripheral and fills tx_buf with 0 to 255;
I2C_EE_WriteBuffer splits the data page by page for I2C_EE_WriteOnePage and I2C_EE_PageWrite;
I2C_EE_ReadBuffer uses two start conditions to send the page address and then read the data;
Buffercmp compares tx_buf with rx_buf and prints the result.
Main APIs I2C_InitStruct, I2C_Init, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_GenerateStop
Verification Connect the EEPROM, build, download, and reset the board;
the UART prints initialization, write, and read messages in turn and finally reports whether the test passed.
Use note The external device is an AT24C02 with 32 pages of 8 bytes each;
when the transfer length exceeds one page the internal address wraps around automatically.
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example accesses an external EEPROM in polling mode: the master writes 256 bytes page by page and then reads them back for comparison.

Data path: main → I2C_EE_Init configures I2C1 and PB6 and PB7 → I2C_EE_WriteBuffer splits the data into pages → I2C_EE_PageWrite sends the start, the device address, the page address, and the data → I2C_EE_ReadBuffer reads back with two start conditions → Buffercmp compares → the UART prints

1. Configure the peripheral and pins. I2C_EE_Init enables the I2C1 and GPIOB clocks, sets PB6 and PB7 as alternate-function open-drain with pull-up, and fills the initialization structure for 100 kHz.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_Init
    /** 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);

This configuration is shared by the polling, interrupt, and DMA examples; only the way the data is moved differs.

2. Write the data page by page. I2C_EE_PageWrite enters the byte loop after the EV8 event and waits for a data-send-finished event after each byte.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_PageWrite
    /** 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))
        {

An AT24C02 page holds 8 bytes, and the upper layer performs the page split, so the page boundary is not checked here.

3. Read back by length. I2C_EE_ReadBuffer sets the acknowledge and ACKPOS from the read length: it does not acknowledge for one byte and sets ACKPOS for two bytes.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_ReadBuffer
    if (NumByteToRead == 1)
    {
        /** Disable Acknowledgement */
        I2C_ConfigAck(I2Cx, DISABLE);
        (void)(I2Cx->STS1); /// clear ADDR
        (void)(I2Cx->STS2);
        I2C_GenerateStop(I2Cx, ENABLE);
    }
    else if (NumByteToRead == 2)
    {
        I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
        (void)(I2Cx->STS1);
        (void)(I2Cx->STS2);
        I2C_ConfigAck(I2Cx, DISABLE);
    }
    else
    {
        I2C_ConfigAck(I2Cx, ENABLE);
        (void)(I2Cx->STS1);
        (void)(I2Cx->STS2);
    }

The start condition and the page address are sent before the branches, so only the data-phase acknowledge and timing are handled here.

Corresponding source files: src/i2c_eeprom.c

5.16.3.2 EEPROM_Int Interrupt access to an AT24C02

Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_Int/

Initialize I2C1 and the peripheral interrupts→The master sends the start and the device address→The interrupt advances the write by event→The start is retried during the device internal write cycle→The interrupt advances the read by event→Compare the buffers and print the result

Item Source analysis
Function Use I2C1 as a master to access an external AT24C02 EEPROM with interrupts;
write 256 bytes and read them back for comparison, with the transfer driven by the interrupt handlers.
Source files src/main.c: main flow and buffer comparison;
src/i2c_eeprom.c and inc/i2c_eeprom.h: the interrupt read/write driver and the event and error handling;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;
I2C_INT_EVENT, I2C_INT_BUF, and I2C_INT_ERR are enabled, the event interrupt pre-emption priority is 0 with sub-priority 1, and the error interrupt pre-emption priority is 0 with sub-priority 0.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main calls I2C_EE_Init and then enters I2C_EE_WriteBuffer and I2C_EE_ReadBuffer;
the event interrupt advances the state machine through EV5, EV6, EV7, and EV8, and the main flow continues once i2c_comm_state reports complete or exit;
Buffercmp compares tx_buf with rx_buf and prints the result.
Main APIs I2C_ConfigInt, I2C_GetLastEvent, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_ConfigAck, I2C_GenerateStart, I2C_GenerateStop
Verification Connect the EEPROM, build, download, and reset the board;
the UART prints write and read messages and finally reports whether the test passed.
Use note The external device is an AT24C02 with 32 pages of 8 bytes each;
when the transfer length exceeds one page the internal address wraps around automatically.
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example moves EEPROM access to interrupts: the event and error paths each have a handler, and the main flow only starts and finishes the transfer.

Data path: main → I2C_EE_Init enables the event and error interrupts → I2C1_EV_IRQHandler advances the transfer through EV5, EV6, EV7, and EV8 → I2C1_ER_IRQHandler handles acknowledge failure and bus error → i2c_comm_state reports completion → Buffercmp compares the buffers

1. Initialize and enable the interrupts. I2C_EE_Init configures the peripheral, sets the NVIC priority grouping, and enables the I2C1 event and error interrupts separately.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_Init
    /** I2C NVIC configuration */
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel                   = I2C1_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    NVIC_InitStructure.NVIC_IRQChannel                   = I2C1_ER_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;

The event and error interrupts share one priority grouping; both pre-emption priorities are 0 and the sub-priority decides the order.

2. The receive direction uses two interrupts to send the address and read the data. After EV6, I2C1_EV_IRQHandler decides from the read length whether to disable the acknowledge or set ACKPOS.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C1_EV_IRQHandler
    case I2C_EVT_MASTER_RXMODE_FLAG: /// EV6
            if (Int_NumByteToRead == 1)
            {
                I2C_ConfigAck(I2C1, DISABLE);
                I2C_GenerateStop(I2C1, ENABLE);
            }
            else if (Int_NumByteToRead == 2)
            {
                I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
                I2C_ConfigAck(I2C1, DISABLE);
            }
        break;

For one byte it disables the acknowledge and then sends the stop; for two bytes it sets ACKPOS and disables the acknowledge so that the acknowledge failure falls after the second-to-last byte.

3. Moving bytes in the send direction. On EV8, if the frame sends only one internal address it sends the stop and finishes, otherwise it keeps sending the bytes in SendBuf.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C1_EV_IRQHandler
    /** Master Transmitter events */
    case I2C_EVT_MASTER_TXMODE_FLAG: /// EV8 just after EV6
        if (check_begin)
        {
            check_begin = FALSE;
            I2C_ConfigInt(I2C1, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, DISABLE);
            I2C_GenerateStop(I2C1, ENABLE);
            i2c_comm_state = COMM_DONE;
            break;
        }
        I2C_SendData(I2C1, DeviceOffset);
        OffsetDone = TRUE;
        break;
    case I2C_EVT_MASTER_DATA_SENDING: ///  EV8 I2C_EVENT_MASTER_DATA_TRANSMITTING
        if (MasterDirection == Receiver)
        {
            I2C_GenerateStart(I2C1, ENABLE);
        }
        break;

check_begin distinguishes whether the current start probes whether the device is busy or performs a real data transfer; the stop condition is handled differently in the two cases.

4. Wrapping up in the error interrupt. I2C1_ER_IRQHandler sends the stop and sets the state to exit when a read fails or a bus error occurs.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C1_ER_IRQHandler
void I2C1_ER_IRQHandler(void)
{
    if (I2C_GetFlag(I2C1, I2C_FLAG_ACKFAIL))
    {
        if (check_begin) /// EEPROM write busy
        {
            I2C_GenerateStart(I2C1, ENABLE);
        }
        else if (I2C1->STS2 & 0x01) /// real fail
        {
            I2C_GenerateStop(I2C1, ENABLE);
            i2c_comm_state = COMM_EXIT;
        }
        I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
    }

    if (I2C_GetFlag(I2C1, I2C_FLAG_BUSERR))
    {
        if (I2C1->STS2 & 0x01)
        {
            I2C_GenerateStop(I2C1, ENABLE);
            i2c_comm_state = COMM_EXIT;
        }
        I2C_ClrFlag(I2C1, I2C_FLAG_BUSERR);
    }
}

When the acknowledge failure falls inside the EEPROM internal write cycle, the start is issued again to wait for the device to be ready instead of reporting a communication failure.

Corresponding source files: src/i2c_eeprom.c

5.16.3.3 EEPROM_DMA DMA access to an AT24C02

Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_DMA/

Initialize I2C1, the GPIO, and the DMA clock→The master sends the start, the device address, and the page address→Configure the transmit DMA channel and start the transfer→Wait for the byte-transfer-finished flag, then stop and wait for the device to be ready→Configure the receive DMA channel and read the data back→Compare the buffers and print the result

Item Source analysis
Function Use I2C1 as a master to access an external AT24C02 EEPROM with DMA;
write 256 bytes and read them back for comparison, with the data moved by the DMA channels.
Source files src/main.c: main flow and buffer comparison;
src/i2c_eeprom.c and inc/i2c_eeprom.h: the DMA read/write driver, the channel remap macros, and the timeout callback;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;
transmit uses DMA1_CH6 remapped as I2C_DMA_TX_Remap and receive uses DMA1_CH7 remapped as I2C_DMA_RX_Remap;
the page size is 8 bytes and the test length is 256 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main calls I2C_EE_Init and then enters I2C_EE_WriteBuffer and I2C_EE_ReadBuffer;
for transmit, I2C_EE_PageWrite configures DMA1_CH6 and sets I2C_EnableDMA, using I2C_FLAG_BSF to detect completion;
for receive, I2C_EE_ReadBuffer configures DMA1_CH7 and the channel copies DAT into the user buffer;
Buffercmp compares tx_buf with rx_buf and prints the result.
Main APIs I2C_EnableDMA, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_CheckEvent, I2C_GenerateStop, RCC_EnableAHBPeriphClk
Verification Connect the EEPROM, build, download, and reset the board;
the UART prints initialization, write, and read messages and finally reports whether the test passed.
Use note The external device is an AT24C02 with 32 pages of 8 bytes each;
when the transfer length exceeds one page the internal address wraps around automatically;
do not enable the buffer interrupt when DMA is used to transmit or receive.
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The example hands the EEPROM byte transfer to DMA: the master only builds the protocol, and the channels move the data between the data register and memory.

Data path: main → I2C_EE_Init enables the DMA1 clock → I2C_EE_PageWrite configures DMA1_CH6 and starts → I2C_FLAG_BSF detects completion → I2C_EE_ReadBuffer configures DMA1_CH7 → Buffercmp compares

1. Enable the DMA clock. After configuring I2C1, I2C_EE_Init adds the DMA1 clock for the channels used later.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_Init
    /** 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);
    RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_DMA1, ENABLE);

Transmit and receive use DMA1_CH6 and DMA1_CH7 respectively.

2. Configure the transmit channel. I2C_EE_PageWrite takes the defaults from DMA_StructInit, sets the peripheral address, the memory address, the transfer direction, and the data width, and then requests the channel remap.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_PageWrite
    DMA_DeInit(DMA1_CH6);
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.PeriphAddr              = (u32)&I2Cx->DAT;            /// (u32)I2C1_DR_Address;
    DMA_InitStructure.MemAddr                 = (u32)pBuffer;              /// from function input parameter
    DMA_InitStructure.Direction               = DMA_DIR_PERIPH_DST;     /// fixed for send function
    DMA_InitStructure.BufSize                 = NumByteToWrite;            /// from function input parameter
    DMA_InitStructure.PeriphInc               = DMA_PERIPH_INC_DISABLE; // fixed
    DMA_InitStructure.MemoryInc               = DMA_MEM_INC_ENABLE;      /// fixed
    DMA_InitStructure.PeriphDataSize          = DMA_PERIPH_DATA_WIDTH_BYTE;  /// fixed
    DMA_InitStructure.MemDataSize             = DMA_MEM_DATA_WIDTH_BYTE;  /// fixed
    DMA_InitStructure.CircularMode            = DMA_MODE_NORMAL;           /// fixed
    DMA_InitStructure.Priority                = DMA_PRIORITY_VERY_HIGH;     /// up to user
    DMA_InitStructure.Mem2Mem                 = DMA_M2M_DISABLE;      /// fixed
    DMA_Init(DMA1_CH6, &DMA_InitStructure);
    DMA_RequestRemap(I2C_DMA_TX_Remap,DMA1_CH6,ENABLE);

The peripheral address is fixed to I2Cx->DAT, the direction is memory to peripheral, and the buffer length comes from the argument.

3. Start the transfer and wait for completion. After the page address is sent it enables the I2C DMA request and the channel, detects the end with the byte-transfer-finished flag, then disables DMA and waits for the device to be ready.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_PageWrite
    I2C_EnableDMA(I2Cx, ENABLE);
    DMA_EnableChannel(DMA1_CH6, ENABLE);
    /* Wait for transfer finished */
    sEETimeout = sEE_LONG_TIMEOUT;
    while (!I2C_GetFlag(I2Cx, I2C_FLAG_BSF))
    {
        if ((sEETimeout--) == 0)
        {
            sEE_TIMEOUT_UserCallback();

The DMA request and the channel are disabled right after the stop condition is sent, so no pending request disturbs the next transfer.

4. Configure the receive channel. I2C_EE_ReadBuffer configures DMA1_CH7 when the read length is greater than 1, with the direction changed to peripheral to memory.

src/i2c_eeprom.csrc/i2c_eeprom.c · I2C_EE_ReadBuffer
        /** DMA initialization */
        DMA_DeInit(DMA1_CH7);
        DMA_StructInit(&DMA_InitStructure);
        DMA_InitStructure.PeriphAddr     = (u32)&I2Cx->DAT;            /// (u32)I2C1_DR_Address;
        DMA_InitStructure.MemAddr        = (u32)pBuffer;               /// from function input parameter
        DMA_InitStructure.Direction      = DMA_DIR_PERIPH_SRC;         /// fixed for read function
        DMA_InitStructure.BufSize        = NumByteToRead;              /// from function input parameter
        DMA_InitStructure.PeriphInc      = DMA_PERIPH_INC_DISABLE;     /// fixed
        DMA_InitStructure.MemoryInc      = DMA_MEM_INC_ENABLE;         /// fixed
        DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;//  fixed
        DMA_InitStructure.MemDataSize    = DMA_MEM_DATA_WIDTH_BYTE;// DMA_MEM_DATA_WIDTH_BYTE;    /// fixed
        DMA_InitStructure.CircularMode   = DMA_MODE_NORMAL;            /// fixed
        DMA_InitStructure.Priority       = DMA_PRIORITY_VERY_HIGH;     /// up to user
        DMA_InitStructure.Mem2Mem        = DMA_M2M_DISABLE;            /// fixed
        DMA_Init(DMA1_CH7, &DMA_InitStructure);
        DMA_RequestRemap(I2C_DMA_RX_Remap,DMA1_CH7,ENABLE);

A length of 1 still goes through the polling path and does not use DMA.

Corresponding source files: src/i2c_eeprom.c

5.16.3.4 I2C_Master Master transmit and receive

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

Initialize I2C1 and the PB6 and PB7 pins→The master sends the start and the slave address→Write 200 bytes continuously→Send a repeated start and the read address→Receive the data by length branch and generate the stop→Compare the buffers and print the result

Item Source analysis
Function Implements full master transmit and receive functions on I2C1;
writes 200 bytes to the slave at address 0x10 and reads them back, printing a pass when they match.
Source files src/main.c: master initialization, the transmit and receive functions, the main flow, and buffer comparison, plus bus clock recovery, peripheral reset, and the timeout callback;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, own address I2C_MASTER_ADDR is 0x30 and target slave address I2C_SLAVE_ADDR is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz with duty cycle I2C_FMDUTYCYCLE_2;
the test buffer is 200 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main fills tx_buf with 0 to 199;
i2c_master_send waits for the bus to be idle, sends the start and the address, then sends each byte and waits for I2C_FLAG_BSF;
i2c_master_recv handles the 1-byte, 2-byte, and multi-byte lengths in separate branches and disables the acknowledge before the last byte;
Buffercmp prints the result, clears rx_buf, and delays before retrying.
Main APIs I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_CheckEvent, I2C_RecvData, I2C_ConfigAck, I2C_GenerateStop
Verification Flash this example and the I2C_Slave example to two boards and wire SCL and SDA together with a common ground;
after reset the UART prints this is a i2c master demo and the test result.
Use note This is a pure master example, so a slave at address 0x10 must respond on the bus;
the receive path branches by length, and the last two bytes must be read before the stop condition is sent;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The master advances the timing purely by polling flags: every step carries a timeout counter, and an expiry calls the shared callback and returns an error code.

Data path: main fills tx_buf → i2c_master_send → i2c_master_recv → Buffercmp → print

1. Initialize the master. i2c_master_init enables the I2C1 and GPIOB clocks, sets PB6 and PB7 as open-drain alternate function, and fills the master parameters.

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

    I2C_Init(I2Cx, &i2cx_master);
    I2C_Enable(I2Cx, ENABLE);

OwnAddr1 takes I2C_MASTER_ADDR and ClkSpeed is set to 100 kHz; the peripheral is enabled last.

2. Send the start condition. Comm_Flag marks the phase of one transaction, and the start condition is sent only when idle so that it is not repeated.

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

It then sends the slave address with I2C_SendAddr7bit and waits for I2C_EVT_MASTER_TXMODE_FLAG.

3. Receive the last byte. For a length of 1 it disables the acknowledge, reads STS1 and STS2 to release ADDR, and sends the stop ahead of time.

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

Reading STS1 and STS2 is the fixed way to release the ADDR flag.

4. Main flow. main fills the transmit buffer with an incrementing sequence and then writes and reads on the same bus, counting a pass only when they match.

src/main.csrc/main.c · main
    while (1)
    {
        for(; ;)
        {
            /* First write in the memory followed by a read of the written data --------*/
            /* Write data*/
            if(i2c_master_send(tx_buf, TEST_BUFFER_SIZE))
            {
                break;  /* send fail, resend*/
            }

            /* Read data */
            if(i2c_master_recv(rx_buf, TEST_BUFFER_SIZE))
            {
                break; /* receive fail, resend*/
            }

            /* 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 are same, i2c master test pass\r\n");
            }
            else
            {
                log_info("write and read data are different, i2c master test fail\r\n");
            }
            for (i = 0; i < TEST_BUFFER_SIZE; i++)
            {
                rx_buf[i] = 0;
            }
            systick_delay_ms(50);
        }
    }

A non-zero return from either direction breaks out and retries; a pass clears rx_buf and delays 50 ms before the next round.

Corresponding source files: src/main.c

5.16.3.5 I2C_Master_Int Master interrupt transfer

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

Initialize I2C1, the GPIO, and the NVIC→Enable the event, buffer, and error interrupts→The main flow sends the start and waits for the send-complete flag→The event interrupt advances the send from EV5 to EV8→The interrupt re-issues the start and receives byte by byte on EV7→Compare the buffers and print the result

Item Source analysis
Function Advances the I2C1 master timing with interrupts instead of polling;
both transmit and receive are completed inside I2Cx_EV_IRQHandler by event code, and the main flow only waits for the completion flags.
Source files src/main.c: initialization, the interrupt handlers, the main flow, and buffer comparison, including helpers such as Memset;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, own address I2C_MASTER_ADDR is 0x30 and target slave address I2C_SLAVE_ADDR is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz, the event interrupt uses I2C1_EV_IRQn with pre-emption priority 4, and the error interrupt uses I2C1_ER_IRQn with pre-emption priority 3;
the test buffer is 100 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main fills tx_buf with an incrementing sequence and sends the start, then blocks waiting for flag_master_send_finish;
after the send it re-issues the start and waits for flag_master_recv_finish;
in the event interrupt EV5 sends the address by direction, EV6 to EV8 advance the send, and EV7 receives byte by byte, disabling the acknowledge and sending the stop at the second-to-last byte;
Buffercmp prints the result.
Main APIs I2C_ConfigInt, NVIC_Init, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_GetLastEvent
Verification Flash this example and the I2C_Slave example to two boards and connect the bus;
after reset the UART prints This is a i2c master int demo! and the test result.
Use note The master timing runs in the interrupt and the main flow synchronizes with the completion flags;
the error interrupt only handles acknowledge failure and sends the stop after clearing I2C_FLAG_ACKFAIL;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The interrupt version treats the event code as a state-machine input: all transfer actions are centralized in the event interrupt, and the main flow only starts and waits.

Data path: main sends the start → the event interrupt sends the address on EV5 → EV6 and EV8 advance the send → EV7 receives byte by byte → the completion flag is set → Buffercmp

1. Configure the interrupt priority. The event interrupt pre-emption priority is 4 and the error interrupt is 3, so error handling can pre-empt ordinary events.

src/main.csrc/main.c · NVIC_Configuration
void NVIC_Configuration(void)
{
    NVIC_InitType NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = I2Cx_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2Cx_ER_IRQn; // test err
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

Both channels are enabled and both sub-priorities are 1.

2. Enable the peripheral interrupts. At the end of initialization it enables the event, buffer, and error interrupts at once and then enables I2C1.

src/main.csrc/main.c · i2c_master_init
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_Configuration();
    I2C_Enable(I2Cx, ENABLE);

I2C_INT_EVENT, I2C_INT_BUF, and I2C_INT_ERR correspond to the event, buffer, and error requests.

3. Synchronize the main flow. After sending the start the main flow blocks on the completion flag and calls the shared callback on timeout.

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

    I2CTimeout = I2CT_LONG_TIMEOUT * 1000;
    while (flag_master_send_finish == 0)
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_UNKNOW);
        }
    }

Transmit and receive each have a completion flag, and the main flow continues once the interrupt sets one.

4. Addressing in the interrupt. The event interrupt reads I2C_GetLastEvent first, and on EV5 it sends the read or write address depending on the current direction.

src/main.csrc/main.c · I2Cx_EV_IRQHandler
        case I2C_EVT_MASTER_MODE_FLAG: // EV5 start
            if (flag_trans_direct)     // read
            {
                Memset(rx_buf, 0, TEST_BUFFER_SIZE); // clear recv buf, ready to recv data
                I2C_SendAddr7bit(I2Cx, I2C_SLAVE_ADDR, I2C_DIRECTION_RECV);
                rx_num = 0;
            }
            else // write
            {
                I2C_SendAddr7bit(I2Cx, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
                tx_num = 0;
            }
            break;

flag_trans_direct distinguishes a write round from a read round; the read direction also clears the receive buffer and resets the counter first.

5. Receiving in the interrupt. Each EV7 reads one byte, and at the second-to-last byte it disables the acknowledge and sends the stop condition.

src/main.csrc/main.c · I2Cx_EV_IRQHandler
        case I2C_EVT_MASTER_DATA_RECVD_FLAG: // one byte recved :EV7.//BUSY,MSL and RXDATNE flags.
//        case I2C_EVT_MASTER_SFT_DATA_RECVD_FLAG: // BUSY, MSMODE(Master) and Data register not empty, BSF(Byte transfer finished)flags.
            rx_buf[rx_num++] = I2C_RecvData(I2Cx);

            if (rx_num == (TEST_BUFFER_SIZE - 1))
            {
                I2C_ConfigAck(I2Cx, DISABLE);   // Disable I2Cx acknowledgement.
                if (Comm_Flag == C_READY)
                {
                    Comm_Flag = C_STOP_BIT;
                    I2C_GenerateStop(I2Cx, ENABLE); // Send I2Cx STOP Condition.
                }
            }
            else if (rx_num == TEST_BUFFER_SIZE)
            {
                flag_master_recv_finish = 1;
            }
            break;

After the last byte is read the receive-complete flag is set for the main flow to compare the data.

Corresponding source files: src/main.c

5.16.3.6 I2C_Master_FIFO FIFO master transfer

Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Master_FIFO/

Initialize I2C1 and the PB6 and PB7 pins→Enable the FIFO and set the transmit and receive thresholds→Configure the master receive by byte count→Advance the transmit and receive by FIFO events→Compare the buffers and print the result

Item Source analysis
Function Enables the transmit and receive FIFO on I2C1 to complete a master transfer;
the transfer functions advance by FIFO events instead of single-byte events, sending and receiving 10 bytes to and from the slave at address 0x10.
Source files src/main.c: initialization, the FIFO transfer functions, the main flow, and clock-recovery fault handling;
inc/main.h: the slave address, buffer length, and FIFO threshold macros;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, target slave address I2Cx_SLAVE_ADDRESS7 is 0x10 and the own address is 0x30;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz, the transmit threshold FIFO_HALF_EMPTY_THR and the receive threshold FIFO_HALF_FULL_THR are both 4;
the test buffer is 10 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path After initialization main fills tx_buf with an incrementing sequence, calls i2c_master_fifo_send, delays 2 ms, and calls i2c_master_fifo_recv;
the receive first sets the byte count with I2C_SetMasterReceivedDataBytesNum and then advances the transfer by FIFO events;
Buffercmp prints the result.
Main APIs I2C_EnableFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold, I2C_CheckFifoEvent, I2C_RecvFIFOData, I2C_SetMasterReceivedDataBytesNum, I2C_ClrFIFO
Verification Flash this example and a slave example to two boards and wire SCL, SDA, and a common ground;
after reset the UART prints this is a i2c master fifo demo and the test result.
Use note FIFO transfer relies on threshold events, so the byte count must be set before receiving;
the timeout callback selects a software reset or an RCC module reset by COMM_RECOVER_MODE;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The FIFO version upgrades the single-byte event to a threshold event: one event can move several bytes, which reduces the number of waits in a long transfer.

Data path: main → i2c_master_fifo_send → i2c_master_fifo_recv → Buffercmp → print

1. Enable the FIFO and set the thresholds. After enabling I2C1, initialization turns on the transmit and receive FIFO and sets the two thresholds.

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

    I2C_Init(I2Cx, &i2cx_master);
    I2C_Enable(I2Cx, ENABLE);
    I2C_EnableFIFO(I2Cx, ENABLE);

    I2C_SetTxFifoThreshold(I2Cx, FIFO_HALF_EMPTY_THR);
    I2C_SetRxFifoThreshold(I2Cx, FIFO_HALF_FULL_THR);

The transmit threshold is FIFO_HALF_EMPTY_THR and the receive threshold is FIFO_HALF_FULL_THR; both are 4 in this example.

2. Main flow. After filling the transmit buffer, main sends first and then receives, with a 2 ms gap for the slave to get ready.

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

    log_init();
    log_info("\nthis is a i2c master fifo demo\r\n");
    /* Initialize the I2C EEPROM driver ----------------------------------------*/
    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_fifo_send(tx_buf, TEST_BUFFER_SIZE);

    systick_delay_ms(2);

    /* Read data */
    i2c_master_fifo_recv(rx_buf, TEST_BUFFER_SIZE);

    /* 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("i2c master fifo test pass!\r\n");
    }
    else
    {
        log_info("i2c master fifo test fail!\r\n");
    }
    while (1)
    {
    }
}

Transmit and receive are each handled by one function, and a consistent comparison prints the pass message.

3. Timeout recovery. A timeout in any wait enters the shared callback, which selects the recovery method from COMM_RECOVER_MODE.

src/main.csrc/main.c · CommTimeOut_CallBack
void CommTimeOut_CallBack(ErrCode_t errcode)
{
    log_info("...ErrCode:%d\r\n", errcode);

#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
    IIC_SWReset();
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
    IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
    SystemNVICReset();
#endif
}

The default configuration uses a module-level RCC reset, after which the I2C peripheral is re-initialized and a message is printed.

Corresponding source files: src/main.c

5.16.3.7 I2C_Slave Slave transmit and receive

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

Initialize I2C1 and set the slave address→Wait for the matching address from the master→Receive the data into the buffer until the stop condition→After the master switches to read, send the buffer byte by byte→Clear the buffer and wait for the next round→Loop and print the result

Item Source analysis
Function Configures I2C1 as a slave at address 0x10;
it first receives the 256 bytes written by the master into the buffer and then, after the direction is reversed, sends the buffer back to the master.
Source files src/main.c: slave initialization, the transmit and receive functions, the main flow, and the timeout callback;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz with duty cycle I2C_FMDUTYCYCLE_2;
the transfer buffer is 256 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path After initialization main enters a loop and first receives a whole frame into data_buf with i2c_slave_recv;
i2c_slave_recv takes the data and counts it on I2C_EVT_SLAVE_DATA_RECVD and returns only on I2C_EVT_SLAVE_STOP_RECVD;
i2c_slave_send waits for I2C_EVT_SLAVE_SEND_ADDR_MATCHED and then sends byte by byte, ending on a stop or a missing acknowledge;
each round clears the buffer and prints the pass message.
Main APIs I2C_CheckEvent, I2C_GetLastEvent, I2C_SendData, I2C_RecvData, I2C_ClrFlag, I2C_Enable, I2C_Init
Verification Flash this example to the slave board and run I2C_Master or another master example against it;
the slave UART prints this is a i2c slave test demo and prints a pass message after each completed round.
Use note The slave address must match the address the master uses, which is 0x10 in this example;
both directions use the stop condition or an address match as the frame boundary, so no extra handshake is needed;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

A slave cannot start the timing on its own and can only wait to be addressed; the code polls event codes to split one transaction into several states.

Data path: main → i2c_slave_recv receives a whole frame → i2c_slave_send sends it back → clear the buffer → loop

1. Configure as a slave. Initialization writes the own address into OwnAddr1, and the other bus parameters match the master side.

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

    I2C_Init(I2Cx, &i2cx_slave);
    I2C_Enable(I2Cx, ENABLE);

AddrMode is 7-bit, AckEnable turns on automatic acknowledge, and ClkSpeed is 100 kHz.

2. Slave transmit. The transmit function waits for the address match and then sends the buffer byte by byte on events in a loop.

src/main.csrc/main.c · i2c_slave_send
    I2CTimeout = I2CT_LONG_TIMEOUT;

    I2C_Enable(I2Cx, ENABLE);
    I2C_SendData(I2Cx, *sendBufferPtr++);
    while (1)
    {
        lastevent = I2C_GetLastEvent(I2Cx);
        if (lastevent == I2C_EVT_SLAVE_DATA_SENDED)
        {
            I2C_SendData(I2Cx, *sendBufferPtr++);
            I2CTimeout = I2CT_LONG_TIMEOUT;
        }
        else if ((lastevent == I2C_EVT_SLAVE_ACK_MISS) || (lastevent == I2C_EVT_SLAVE_STOP_RECVD))
        {
            I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
            I2C_Enable(I2Cx, ENABLE);
            break;
        }

On a stop condition or a missing acknowledge it clears the acknowledge-failure flag and exits, waiting for the next frame.

3. Slave receive. The receive function takes one byte on each data event until the master sends the stop condition.

src/main.csrc/main.c · i2c_slave_recv
    I2C_Enable(I2Cx, ENABLE);
    while (1)
    {
        lastevent = I2C_GetLastEvent(I2Cx);
        if (lastevent == I2C_EVT_SLAVE_DATA_RECVD)
        {
            *data++ = I2C_RecvData(I2Cx);
            rxDataNum++;
            I2CTimeout = I2CT_LONG_TIMEOUT;
        }
        else if (lastevent == I2C_EVT_SLAVE_STOP_RECVD)
        {
            I2C_Enable(I2Cx, ENABLE);
            break;
        }

rxDataNum records the number of bytes received in this round for the main flow to check whether the frame is complete.

Corresponding source files: src/main.c

5.16.3.8 I2C_Slave_Int Slave interrupt transfer

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

Initialize I2C1, the GPIO, and the NVIC→Enable the event, buffer, and error interrupts→The main flow waits for the receive-complete flag→The event interrupt stores or loads data by slave event code→The completion flag is set when the stop condition arrives→The main flow waits for the send to finish and prints the result

Item Source analysis
Function Handles I2C1 slave transactions with interrupts;
both receive and transmit are completed inside I2Cx_EV_IRQHandler by event code, and the main flow only waits for two completion flags.
Source files src/main.c: initialization, the interrupt handlers, the main flow, and the timeout callback;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz, the event interrupt uses I2C1_EV_IRQn with pre-emption priority 4, and the error interrupt uses I2C1_ER_IRQn with pre-emption priority 3;
the transfer buffer is 100 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path After initialization main blocks waiting for flag_slave_recv_finish, which the interrupt sets after a whole frame;
it then waits for flag_slave_send_finish, which the interrupt sets after the send;
the interrupt accesses data_buf by events such as I2C_EVT_SLAVE_RECV_ADDR_MATCHED and I2C_EVT_SLAVE_DATA_RECVD;
each wait prints the length when it ends.
Main APIs I2C_ConfigInt, NVIC_Init, I2C_GetLastEvent, I2C_Enable, I2C_Init, I2C_CheckEvent, I2C_ClrFlag
Verification Flash this example to the slave board and run I2C_Master_Int or another master example against it;
the slave UART prints this is a i2c slave int demo and the transfer lengths.
Use note The slave interrupt sets the receive-complete flag when the stop condition arrives and data has been received;
the error interrupt handles acknowledge failure separately, and the main flow does not take part in error branches;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The slave interrupt maps each event class to one data move or one state record, and the main flow only cares about two completion flags.

Data path: main waits for the receive flag → the event interrupt accesses data_buf by I2C_EVT_SLAVE_* → the completion flag is set → main prints

1. Configure the interrupt priority. As in the master example, the event interrupt pre-emption priority is 4 and the error interrupt is 3.

src/main.csrc/main.c · NVIC_Configuration
void NVIC_Configuration(void)
{
    NVIC_InitType NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = I2Cx_EV_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = I2Cx_ER_IRQn; // test err
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

The error interrupt has the higher priority, so bus faults are handled first.

2. Enable the peripheral interrupts. At the end of initialization it enables the event, buffer, and error interrupts and then enables I2C1.

src/main.csrc/main.c · i2c_slave_init
    // int enable
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_Configuration();
    I2C_Enable(I2Cx, ENABLE);
    return 0;

I2C_ConfigInt configures the three request classes at once and then NVIC_Configuration is called.

3. Synchronize the main flow. The main flow waits for the receive and then the send completion flag, and a timeout is taken over by the shared callback.

src/main.csrc/main.c · main
    while (flag_slave_recv_finish == 0) // wait for recv data finish
    {
        if ((I2CTimeout--) == 0)
            CommTimeOut_CallBack(SLAVE_UNKNOW);
    }

    log_info("recv finish,recv len = %d\r\n", rxDataNum);
    flag_slave_recv_finish = 0;

    I2CTimeout = I2CT_LONG_TIMEOUT * 1000;
    // send data
    while (flag_slave_send_finish == 0) // wait for send data finish
    {
        if ((I2CTimeout--) == 0)
            CommTimeOut_CallBack(SLAVE_UNKNOW);
    }

    log_info("tx finish,tx len = %d\r\n", rxDataNum-1);
    flag_slave_send_finish = 0;

    while (1)
    {;}

The interrupt sets both flags; the main flow reads the length, clears the flag, and waits again.

4. Moving data in the interrupt. In slave mode it takes or sends data by event code and sets the receive-complete flag only on the stop condition.

src/main.csrc/main.c · I2Cx_EV_IRQHandler
    if ((last_event & I2C_ROLE_MASTER) != I2C_ROLE_MASTER) // MSMODE = 0:I2C slave mode
    {
        switch (last_event)
        {
        case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:
            // clear flag,ready to receive data
            rxDataNum = 0;
            break;
        case I2C_EVT_SLAVE_SEND_ADDR_MATCHED:
            rxDataNum = 0;
            break;
        case I2C_EVT_SLAVE_DATA_SENDING:
            I2Cx->DAT = data_buf[rxDataNum++];
            break;
        case I2C_EVT_SLAVE_DATA_SENDED:
            I2Cx->DAT = data_buf[rxDataNum++];
            break;
        case I2C_EVT_SLAVE_DATA_RECVD:
            data_buf[rxDataNum++] = I2Cx->DAT;
            break;
        case I2C_EVT_SLAVE_STOP_RECVD:
            I2C_Enable(I2Cx, ENABLE);
            if(rxDataNum != 0)
            {
                flag_slave_recv_finish = 1; // The STOPF bit is not set after a NACK reception
            }
            break;
        default:
            I2C_Enable(I2Cx, ENABLE);
            timeout_flag = 1;
            break;
        }
    }

rxDataNum is both the receive count and the read pointer used for the send.

Corresponding source files: src/main.c

5.16.3.9 I2C_Slave_FIFO FIFO slave transfer

Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Slave_FIFO/

Initialize I2C1 and set the slave address→Enable the transmit and receive FIFO and the thresholds→The slave waits for the address match→Advance the receive into the buffer by FIFO events→After the stop condition the direction reverses and the buffer is sent back→When done it idles until reset

Item Source analysis
Function Configures I2C1 as a slave at address 0x10 and enables the transfer FIFO;
it first receives the 10 bytes written by the master by FIFO events and, after the direction is reversed, sends the buffer back.
Source files src/main.c: initialization, the FIFO transfer functions, the main flow, and the reset callback;
inc/main.h: the slave address, buffer length, and FIFO threshold macros;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 slave, 7-bit addressing, own address I2Cx_SLAVE_ADDRESS7 is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz, the transmit threshold FIFO_HALF_EMPTY_THR and the receive threshold FIFO_HALF_FULL_THR are both 4;
the transfer buffer is 10 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path After initialization main first receives a whole frame into data_buf with i2c_slave_fifo_recv;
the receive sets the byte count with I2C_SetMasterReceivedDataBytesNum and then moves data in batches on I2C_EVENT_SLAVE_FIFO_RECEIVING after the address match;
the send waits for I2C_EVENT_SLAVE_FIFO_TRANSMITTER_ADDRESS_MATCHED, fills the transmit FIFO once, and then tops it up on events;
it then idles.
Main APIs I2C_EnableFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold, I2C_CheckFifoEvent, I2C_SendFIFOData, I2C_RecvFIFOData, I2C_ClrFIFO
Verification Flash this example to the slave board and run I2C_Master_FIFO or another master example against it;
the slave UART prints This is the i2c slave fifo mode..
Use note FIFO slave transfer uses the address-match event as the frame start in both directions;
the send fills the FIFO once and then tops it up on the half-empty event;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The FIFO slave splits a whole frame into several threshold events: the receive moves data on half-full and the send tops up on half-empty.

Data path: main → i2c_slave_fifo_recv receives a whole frame → i2c_slave_fifo_send sends it back → idle

1. Enable the FIFO and set the thresholds. After the slave parameters are written, initialization turns on the transmit and receive FIFO and sets the two thresholds.

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

    I2C_Init(I2Cx, &i2cx_slave);
    I2C_Enable(I2Cx, ENABLE);
    I2C_EnableFIFO(I2Cx, ENABLE);

    I2C_SetTxFifoThreshold(I2Cx, FIFO_HALF_EMPTY_THR);
    I2C_SetRxFifoThreshold(I2Cx, FIFO_HALF_FULL_THR);

    return 0;
}

The transmit threshold is FIFO_HALF_EMPTY_THR and the receive threshold is FIFO_HALF_FULL_THR; both are 4 in this example.

2. Main flow. The main flow is a single receive and send: it collects one frame, sends it back as soon as the direction reverses, and then idles.

src/main.csrc/main.c · main
int main(void)
{
    log_init();
    log_info("\nThis is the i2c slave fifo mode.\r\n");

    /* Initialize the I2C driver ----------------------------------------*/
    i2c_slave_init();

    /* Read data */
    i2c_slave_fifo_recv(data_buf, TEST_BUFFER_SIZE);

Both directions work in units of 10 bytes, set by TEST_BUFFER_SIZE.

3. Timeout recovery. A timeout while waiting for a FIFO event enters the shared callback, which selects the recovery method from COMM_RECOVER_MODE.

src/main.csrc/main.c · CommTimeOut_CallBack
void CommTimeOut_CallBack(ErrCode_t errcode)
{
    log_info("...ErrCode:%d\r\n", errcode);

#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
    IIC_SWReset();
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
    IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
    SystemNVICReset();
#endif
}

The default configuration uses a module-level RCC reset, after which the peripheral is re-initialized and a message is printed.

Corresponding source files: src/main.c

5.16.3.10 I2C_10bit 10-bit addressing master and slave

Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_10bit/

Initialize I2C1 as master and I2C2 as slave→Configure both peripherals for 10-bit addressing→The master writes and sends the data to the slave→Compare the master transmit and slave receive buffers→Reverse the direction so the slave writes and the master reads→Compare the buffers again and print the result

Item Source analysis
Function Uses I2C1 as master and I2C2 as slave on the same board to demonstrate 10-bit addressing;
the master writes while the slave reads, then the direction is reversed to slave-write and master-read, with a buffer comparison in both rounds.
Source files src/main.c: initialization of both peripherals, the main flow, two event interrupts and the error interrupt, plus reset-recovery code;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration On master I2C1, SCL is PB6 and SDA is PB7 with alternate function GPIO_AF2, and the own address I2C_MASTER_ADDR is 0x230;
on slave I2C2, SCL is PB10 and SDA is PB11 with alternate function GPIO_AF7, and the own address I2C_SLAVE_ADDR is 0x2A0;
AddrMode is I2C_ADDR_MODE_10BIT on both sides and ClkSpeed is 100 kHz;
the first byte uses I2C_FRAME_HEAD_TX or I2C_FRAME_HEAD_RX to carry the high address bits;
the test buffer is 100 bytes.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main initializes the master and the slave, fills master_tx_buf, sends the start, and waits for both flag_master_complete and flag_slave_complete;
the first round compares master_tx_buf with slave_rx_buf, and the second round switches the direction to RECEIVE and compares master_rx_buf with slave_tx_buf;
the two event interrupts advance the transfer by event code on I2C1 and I2C2.
Main APIs I2C_ConfigInt, NVIC_Configuration, I2C_GenerateStart, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ConfigAck
Verification A single board is enough;
the I2C1 and I2C2 pins must be wired together as on the board;
after reset the UART prints this is a i2c(10bit) master/slave demo and the results of both rounds.
Use note Both ends must use 10-bit addressing, and the top two address bits are sent together with the first byte;
each round must wait for both the master and the slave completion flags before comparing;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

A 10-bit address transaction adds one addressing step: after the start the frame head and the high address bits are sent, then the low address byte, and only then does the data phase begin.

Data path: main sends the start → the I2C1 interrupt sends the frame head and the address → the I2C2 interrupt acknowledges and stores the data → both completion flags are set → Buffercmp → the reverse direction is tested once more

1. Initialize the master for 10-bit addressing. i2c_master_init sets the master address mode to 10-bit and enables the event, buffer, and error interrupts at once.

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

    I2C_Init(I2Cx, &i2cx_master);
    // int enable
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_Configuration(1);
    I2C_Enable(I2Cx, ENABLE);

The own address is I2C_MASTER_ADDR, that is 0x230.

2. Initialize the slave. The slave side repeats the same steps on I2C2 with the same 10-bit address mode, but with PB10 and PB11 as the pins.

src/main.csrc/main.c · i2c_slave_init
    I2C_DeInit(Slave_I2Cx);
    I2C_InitStruct(&i2c2_slave);
    i2c2_slave.BusMode     = I2C_BUSMODE_I2C;
    i2c2_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
    i2c2_slave.OwnAddr1    = I2C_SLAVE_ADDR;
    i2c2_slave.AckEnable   = I2C_ACKEN;
    i2c2_slave.AddrMode    = I2C_ADDR_MODE_10BIT;
    i2c2_slave.ClkSpeed    = 100000; //100K

    I2C_Init(Slave_I2Cx, &i2c2_slave);
    // int enable
    I2C_ConfigInt(Slave_I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_Configuration(2);
    I2C_Enable(Slave_I2Cx, ENABLE);

Each peripheral has its own interrupt set, so they do not interfere with each other.

3. Reverse transfer in the main flow. The second round switches the direction to RECEIVE, waits for the bus to be idle, clears ACKPOS, and sends the start.

src/main.csrc/main.c · main
    /*----------I2C1 Master Read and I2C2 Slave Write data---------*/
    for (i = 0; i < TEST_BUFFER_SIZE; i++)
    {
        slave_tx_buf[i] = (0x80 + i);
    }

    flag_master_complete = 0;
    flag_slave_complete = 0;
    master_TR = RECEIVE;

    I2CTimeout = I2CT_LONG_TIMEOUT;

    /*Wait until no busy*/
    while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
    {
        if ((I2CTimeout--) == 0)
        {
            CommTimeOut_CallBack(MASTER_BUSY);
            return 1;
        }
    }


    I2C_ConfigAck(I2Cx, ENABLE);

    /* I2C1 Read data and I2C2 Write data */
    if (Comm_Flag == C_READY)
    {
        Comm_Flag = C_START_BIT;
        I2Cx->CTRL1 &= I2C_NACK_POS_CURRENT; // reset ACKPOS  //==========
        I2C_GenerateStart(I2Cx, ENABLE);
    }

Every transfer continues only after both the master and the slave completion flags are set.

4. Send the 10-bit address in the interrupt. On EV5 the master interrupt sends the frame head and the high address bits, and then sends the low address byte on the I2C_EVT_MASTER_MODE_ADDRESS10_FLAG event.

src/main.csrc/main.c · I2C1_EV_IRQHandler
            case I2C_EVT_MASTER_MODE_FLAG: // EV5 start
                if (flag_master_dir == TRANSMIT)
                {
                    Memset(slave_rx_buf, 0, TEST_BUFFER_SIZE);
                    I2C_SendData(I2Cx, (I2C_FRAME_HEAD_TX | ((I2C_SLAVE_ADDR & 0x300) >> 7)));
                    tx_num = 0;
                }
                else if (flag_master_dir == RECEIVE)
                {
                    Memset(master_rx_buf, 0, TEST_BUFFER_SIZE);
                    I2C_SendData(I2Cx, (I2C_FRAME_HEAD_RX | ((I2C_SLAVE_ADDR & 0x300) >> 7)));
                    rx_num = 0;
                    flag_master_dir = TRANSMIT;
                }
                break;

            case I2C_EVT_MASTER_MODE_ADDRESS10_FLAG:
                I2C_SendData(I2Cx, (I2C_SLAVE_ADDR & 0xFF));
                break;

The frame head I2C_FRAME_HEAD_TX or I2C_FRAME_HEAD_RX distinguishes the transfer direction.

Corresponding source files: src/main.c

5.16.3.11 PMBUS_Master PMBus master

Example directory: projects/n32h49x_EVAL/examples/I2C/PMBUS_Master/

Initialize the control and alert pin lines→Initialize the I2C1 master→Read the temperature, operation, status word, and output voltage in turn→Write back the operation code and the output voltage and read them back for comparison→Summarize the seven test states→Print pass or fail

Item Source analysis
Function Acts as a PMBus master on I2C1;
it reads the temperature, operation, status word, and output voltage commands, verifies the write operations by reading them back, and finally prints the result.
Source files src/main.c: test flow and result evaluation;
src/PMBusMaster.c and inc/PMBusMaster.h: the command index table and the PMBusMaster transaction function;
inc/I2CMaster.h: low-level master interface declarations;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 master, 7-bit addressing, own address 0x30;
the PMBus target address slave_address is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;
the control line is PA7 and the alert line is PA8, configured as alternate-function push-pull;
the command index table covers 120 entries and groups them by range into read byte, read word, write byte, and so on.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main calls PMBusMaster_Init and then calls PMBusMaster once per command;
PMBusMaster looks the command code up in PMBus_Commands and then decides the read/write group from the index range;
the bytes read back are packed into a 16-bit word in main, and the written values are checked by reading back OPERATION and VOUT_COMMAND;
a pass is printed only when none of the seven states is FAILED.
Main APIs PMBusMaster_Init, PMBusMaster, I2C_ConfigInt, I2C_GenerateStart, I2C_SendData, I2C_RecvData, I2C_GenerateStop
Verification Flash this example and the PMBUS_Slave example to two boards and connect the bus;
after reset the UART prints the value read for each command and the final pass or fail message.
Use note The command index is not the command code and must be converted through the PMBus_Commands table;
when reading a word the low byte arrives first, so combine it as the high byte shifted left by eight bits then OR-ed with the low byte;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

PMBus organizes the commands into an index table: the transaction function first looks up the command code and then decides from the index range whether this call reads or writes and how many bytes it reads.

Data path: main → PMBusMaster_Init → PMBusMaster(command index, direction, data, buffer) for each command → look up PMBus_Commands → execute by group → read back and verify

1. Main-flow entry. After opening the log, main initializes the PMBus master and then issues the read tests command by command.

src/main.csrc/main.c · main
int main(void)
{
    log_init();
    log_info("\nThis is a pmbus master int demo!\r\n");
    /* Initialize the I2C driver  */
    PMBusMaster_Init();

    //reads
    log_info("pmbus read test\r\n");
    log_info("pmbus master read temperature test!\r\n");

Every command uses the same PMBusMaster(command index, direction, data, buffer) call form.

2. Command index table. PMBus_Commands maps a command index to the actual command code, which the transaction function puts into the first byte of the transmit buffer.

src/PMBusMaster.csrc/PMBusMaster.c
/* This is the PMBus command look-up table. Do not modify---------------------*/
const unsigned char PMBus_Commands[120] =
{
    0x00, // dummy byte
    0x19,0x78,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x98,
    0x79,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,
    0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,
    0xA7,0xA8,0xA9,0x13,0x14,0x17,0x18,0x03,0x11,0x12,0x15,0x16,
    0x00,0x01,0x02,0x04,0x10,0x20,0x3A,0x3D,0x41,0x45,0x47,0x49,
    0x4C,0x50,0x54,0x56,0x5A,0x5C,0x63,0x69,0x21,0x22,0x23,0x24,
    0x25,0x26,0x27,0x28,0x29,0x2A,0x31,0x32,0x33,0x35,0x36,0x37,
    0x38,0x39,0x3B,0x3C,0x3E,0x3F,0x40,0x42,0x43,0x44,0x46,0x48,
    0x4A,0x4B,0x4F,0x51,0x52,0x53,0x55,0x57,0x58,0x59,0x5B,0x5D,
    0x5E,0x5F,0x60,0x61,0x62,0x64,0x65,0x66,0x68,0x6A,0x6B
};

Entry 0 of the table is a reserved byte; valid commands start at index 1.

3. Transaction entry. PMBusMaster prepares the transmit and receive buffers for one transaction and splits the command code and the 16-bit data into the transmit buffer.

src/PMBusMaster.csrc/PMBusMaster.c · PMBusMaster
uint8_t PMBusMaster(uint8_t PMBusMaster_CommandByte, uint8_t PMBusMaster_RWFlag, uint16_t PMBusMaster_Message,uint8_t *PMBusMaster_ReceivedValue)
{
    uint8_t  PMBusMaster_TransmitBuffer[4];
    uint8_t  PMBusMaster_ReceiveBuffer[5];
    uint8_t  PMBusMaster_RxCount = 0;
    uint16_t PMBusMaster_Temp = 0;
    uint8_t  PMBusMaster_Index;

#if PEC  //PEC variables
    uint8_t PMBusMaster_CrcMsgSize = 0;
    uint8_t PMBusMaster_CrcMsg[5];
    uint8_t PMBusMaster_CrcMasterGenerated = 0;
    uint8_t PMBusMaster_CrcSlaveGenerated = 0;
    uint8_t PMBusMaster_Result = 0;

The low data byte goes into the buffer first and the high byte is held in a temporary variable.

4. Read-back verification. main reads the just-written value back with the same command, prints a failure when they differ, and finally summarizes the seven test states.

src/main.csrc/main.c · main
    if(Vout_Command != 0xCCDD)
    {
        log_info("pmbus write VOUT_COMMAND cmd fail\r\n");
    }


    if(test_status1 == FAILED || test_status2 == FAILED || test_status3 == FAILED
            || test_status4 == FAILED || test_status5 == FAILED || test_status6 == FAILED
            || test_status7 == FAILED)
    {
        log_info("pmbus master int test fail!\r\n");
    }
    else
    {
        log_info("pmbus master int test pass!\r\n");
    }

    while (1)
    {

    }
}

A pass is printed only when every command returns other than FAILED.

Corresponding source files: src/main.c, src/PMBusMaster.c

5.16.3.12 PMBUS_Slave PMBus slave

Example directory: projects/n32h49x_EVAL/examples/I2C/PMBUS_Slave/

Initialize the control and alert pin lines→Initialize the I2C1 slave and enable the interrupts→Wait to be addressed by the master→Look up the index of the received command byte→Prepare or write register data by command group→The event interrupt advances the transfer and sets the completion flags

Item Source analysis
Function Configures the board as a PMBus slave at address 0x10;
the command byte from the master is grouped after an index look-up, so read commands load register values into the transmit buffer and write commands update the local registers.
Source files src/main.c: main loop;
src/PMBusSlave.c and inc/PMBusSlave.h: slave initialization, command decoding, and the event interrupt handler;
inc/PMBus.h: command index and register structure definitions;
readme.txt: function, environment, and usage;
MDK-ARM/ and EWARM/: Keil and IAR projects.
Key configuration I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;
SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;
ClkSpeed is 100 kHz, the event interrupt pre-emption priority is 4 and the error interrupt is 3;
the control line is PA7 and the alert line is PA8, configured as alternate-function push-pull;
the initial register values are temperature 0x12, operation 0x34, status word 0x5678, and output voltage command 0x90AB.
The system clock is 240 MHz and the print UART is PA9 at 115200 baud.
Program and data path main calls PMBusSlave_Init and then enters the PMBusSlave main loop;
PMBusSlave_Init configures the control and alert pins and then calls i2c_slave_init;
PMBusSlave_DecodeCommand looks the command byte up in the PMBus_Commands table and groups it by range;
the event interrupt reads and writes PMBusSlave_ReceiveBuffer and PMBusSlave_TransmitBuffer by group and sets the transfer completion flags.
Main APIs PMBusSlave_Init, PMBusSlave, PMBusSlave_DecodeCommand, I2C_ConfigInt, I2C_GetLastEvent, I2C_SendData, I2C_RecvData
Verification Flash this example and the PMBUS_Master example to two boards and connect the bus;
the slave UART prints this is a pmbus slave int demo and the values the master reads should match the slave’s initial registers.
Use note The command byte must be looked up in the PMBus_Commands table, and index 0 means the master is probing whether a slave is present;
write commands update only the registers this example supports, and unimplemented commands are ignored;
Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1.
Core-code walkthrough and porting

The PMBus slave treats the received command byte as a table key: it first looks up the index and then decides from the index range how many bytes this frame reads or which register it writes.

Data path: main → PMBusSlave_Init → i2c_slave_init → the event interrupt receives the command → PMBusSlave_DecodeCommand groups it → the transfer buffers are prepared

1. Initialize the PMBus pins. PMBusSlave_Init clears the status registers, sets the control and alert lines as alternate-function push-pull, and finally initializes the I2C slave.

src/PMBusSlave.csrc/PMBusSlave.c · PMBusSlave_Init
    RCC_EnableAHB1PeriphClk(pmbus_gpio_clk, ENABLE);
    // Control Line functionality GPIO0
    GPIO_InitStruct(&pmbus_gpio);
    pmbus_gpio.Pin            = PMBUS_CONTRL_PIN ;
    pmbus_gpio.GPIO_Pull      = GPIO_PULL_UP;
    pmbus_gpio.GPIO_Alternate = GPIO_NO_AF;
    pmbus_gpio.GPIO_Mode      = GPIO_MODE_AF_PP;
    pmbus_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
    GPIO_InitPeripheral(PMBUS_CONTRL_GPIO, &pmbus_gpio);

    pmbus_gpio.Pin            = PMBUS_ALERT_PIN;
    pmbus_gpio.GPIO_Pull      = GPIO_PULL_UP;
    pmbus_gpio.GPIO_Alternate = GPIO_NO_AF;
    pmbus_gpio.GPIO_Mode      = GPIO_MODE_AF_PP;
    pmbus_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
    GPIO_InitPeripheral(PMBUS_ALERT_GPIO, &pmbus_gpio);

The control and alert lines are both on GPIOA, at numbers 7 and 8.

2. Configure as an I2C slave. i2c_slave_init writes the own address into OwnAddr1 and enables the event, buffer, and error interrupts at once.

src/PMBusSlave.csrc/PMBusSlave.c · i2c_slave_init
    I2C_DeInit(I2Cx);
    I2C_InitStruct(&i2cx_slave);
    i2cx_slave.BusMode       = I2C_BUSMODE_I2C;
    i2cx_slave.FmDutyCycle   = I2C_FMDUTYCYCLE_2;
    i2cx_slave.OwnAddr1      = I2C_SLAVE_ADDR;
    i2cx_slave.AckEnable     = I2C_ACKEN;
    i2cx_slave.AddrMode      = I2C_ADDR_MODE_7BIT;
    i2cx_slave.ClkSpeed      = 100000; // 100000 100K

    I2C_Init(I2Cx, &i2cx_slave);

    // int enable
    I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
    NVIC_Configuration();
    I2C_Enable(I2Cx, ENABLE);
    return 0;

The address mode is 7-bit, the clock is 100 kHz, and automatic acknowledge is on.

3. Command decoding. PMBusSlave_DecodeCommand searches the command table for a match with the command byte, starting at index 1.

src/PMBusSlave.csrc/PMBusSlave.c · PMBusSlave_DecodeCommand
uint8_t PMBusSlave_DecodeCommand(uint8_t PMBusSlave_RxCommand)
{
    uint8_t PMBusSlave_CommandGroup;

    for(PMBusSlave_Index = 0; PMBusSlave_Index < 120; PMBusSlave_Index++)
    {
        if(PMBus_Commands[PMBusSlave_Index] == PMBusSlave_RxCommand)
            break;
    }

    if(PMBusSlave_Index == 0)//dummy byte to check if slave is present
        PMBusSlave_DummyCommand = 1;

    if(PMBusSlave_Index > 0  && PMBusSlave_Index < 13)    //read byte from slave device
        PMBusSlave_CommandGroup = READBYTE;

A result index of 0 means the master is sending a probe command, which requires the slave to report its presence.

4. Main loop. After initialization main enters an endless loop in which PMBusSlave processes events and commands repeatedly.

src/main.csrc/main.c · main
int main(void)
{
    log_init();
    log_info("\nthis is a pmbus slave int demo\r\n");

    /* Initialize the I2C slave driver */
    PMBusSlave_Init();

    // Application loop
    while(1)
    {
       PMBusSlave();
    }
}

A slave never starts a bus action by itself, so the loop only waits to be addressed.

Corresponding source files: src/main.c, src/PMBusSlave.c

5.16.4 Notes and troubleshooting

Scenario Check & action Source
FIFO enabling and interrupt and DMA configuration When FIFOEN is set, BUFINTEN must be disabled;
when DMA is used, DMAFIFOEN must be enabled and DMAEN must be disabled.
User manual
DMA transfer and buffer interrupt Do not set the BUFINTEN bit of I2C_CTRL2 when DMA is used to transmit or receive. User manual
Master transmit with 7-bit addressing In 7-bit addressing master transmit mode, the slave address must not be configured as 0xF0, 0xF2, 0xF4, or 0xF6. User manual
Reading FIFO receive data The received data must be read out of the FIFO before the peripheral is disabled;
when PECEN is set, the last byte received is the check data.
User manual
Software reset and bus release Before SWRESET is set, make sure the I2C pins are released and the bus is free;
this bit is useful when the BUSY bit is 1 and no stop condition has been detected on the bus.
User manual
Control register write timing After STOPGEN, STARTGEN, or the PEC bit is set, do not write to I2C_CTRL1 until the hardware clears the bit. User manual
Register access width The I2C peripheral registers can be accessed as half-words (16 bits) or words (32 bits). User manual
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.