5.10 I2C 使用说明

本章说明 N32G033 标准外设库中 I2C 驱动的文件组成、43 个公开函数,以及 EEPROM、I2C_Master、I2C_Master_Int、I2C_Master_Dualaddr、I2C_Slave、I2C_Slave_Int、I2C_Slave_Dualaddr 7 个示例。总线时序、时钟延展与数字模拟滤波请配合用户手册的“I2C”章节阅读;芯片已知问题以勘误手册为准。

5.10.1 相关文件

相对路径 作用
firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h 定义 I2C_InitType 结构体与工作模式、寻址、应答、时钟延展、数字与模拟滤波、PEC、超时检测等宏,并按复位与初始化、启停与从机地址、中断与数据收发等区段声明 43 个公开函数
firmware/n32g033_std_periph_driver/src/n32g033_i2c.c 实现上述 43 个函数:I2C 复位与初始化、起始停止与地址收发、SMBus 与 PEC、时钟延展与数字模拟滤波、超时检测、事件标志与中断处理
projects/n32g033_EVAL/examples/I2C/ I2C 示例根目录,含 EEPROM、I2C_Master、I2C_Master_Int、I2C_Master_Dualaddr、I2C_Slave、I2C_Slave_Int、I2C_Slave_Dualaddr 7 个示例
projects/n32g033_EVAL/examples/I2C/EEPROM/ 该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt
projects/n32g033_EVAL/bsp/ 示例共用的板级支持文件:delay.h 的 SysTick 延时与 log.h 的串口日志接口

5.10.2 API 参考

搜索 API共 43 个函数

以下 43 个公开函数按 n32g033_i2c.h 的声明顺序分为 8 组;每个条目给出功能摘要与 n32g033_i2c.c 中该函数正上方的注释原文。

复位与初始化

I2C_DeInit、I2C_Init、I2C_InitStruct、I2C_Enable

起始停止、应答与从机地址

I2C_GenerateStart、I2C_GenerateStop、I2C_ConfigAck、I2C_SendAddr7bit、I2C_ConfigOwnAddr2、I2C_EnableDualAddr、I2C_EnableGeneralCall

中断、数据收发与 DMA

I2C_ConfigInt、I2C_SendData、I2C_RecvData、I2C_EnableDMA、I2C_EnableDmaLastSend

软件复位与 NACK、PEC 位置

I2C_EnableSoftwareReset、I2C_ConfigNackLocation、I2C_ConfigPecLocation

SMBus、PEC 与 ARP

I2C_ConfigSmbusAlert、I2C_SendPEC、I2C_ComputePec、I2C_GetPec、I2C_EnableArp

时钟延展与数字、模拟滤波

I2C_EnableExtendClk、I2C_SetSDADigitalFilterWidth、I2C_SetSCLDigitalFilterWidth、I2C_SetSDAAnalogFilterWidth、I2C_EnableSDAAnalogFilter、I2C_SetSCLAnalogFilterWidth、I2C_EnableSCLAnalogFilter

事件、标志与中断状态

I2C_CheckEvent、I2C_GetLastEvent、I2C_GetFlag、I2C_ClrFlag、I2C_GetIntStatus、I2C_ClrIntPendingBit

超时检测

I2C_SetTimeoutLowLimit、I2C_SetTimeoutHighLimit、I2C_ConfigTimeoutInt、I2C_EnableTimeoutDetect、I2C_GetTimeoutFlag、I2C_ClrTimeoutFlag

void I2C_DeInit(I2C_Module* I2Cx)复位 I2Cx 外设寄存器到默认值。
源码注释
name    I2C_DeInit.
fun     Deinitializes the I2Cx peripheral registers to their default reset values.
param   I2Cx :
          - I2C1
          - I2C2
return  none
void I2C_Init(I2C_Module* I2Cx, I2C_InitType* I2C_InitParam)按 I2C_InitStruct 中的参数初始化 I2Cx 外设。
源码注释
name    I2C_Init.
fun     Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_InitParam :
          - ClkSpeed(common speed:100000(100KHz) - 400000(400KHz) - 1000000(1MHz)):
            - between 1 to 1000000
          - BusMode:
            - I2C_BUSMODE_I2C
            - I2C_BUSMODE_SMBDEVICE
            - I2C_BUSMODE_SMBHOST
          - FmDutyCycle(If the ClkSpeed does not exceed 100KHz,duty is fixed at 1:1,
                      otherwise it can be configured as 2:1 or 16:9):
            - I2C_FMDUTYCYCLE_16_9
            - I2C_FMDUTYCYCLE_2
          - OwnAddr1:
            - between 0 to 0x3FF
          - AckEnable:
            - I2C_ACKEN
            - I2C_ACKDIS
          - AddrMode:
            - I2C_ADDR_MODE_7BIT
            - I2C_ADDR_MODE_10BIT
return  none
void I2C_InitStruct(I2C_InitType* I2C_InitParam)用默认值填充 I2C_InitParam 的各个成员。
源码注释
name    I2C_Initializes_Structure.
fun     Fills each I2C_InitParam member with its default value.
param   (The input parameters must be the following values):
          - ClkSpeed
          - BusMode
          - FmDutyCycle
          - OwnAddr1
          - AckEnable
          - AddrMode
return  none

使用提示: 注释 name 写作 I2C_Initializes_Structure.,与函数名不一致;头文件第 491 行声明为 I2C_InitStruct。

void I2C_Enable(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 外设。
源码注释
name    I2C_Enable.
fun     Enables or disables the specified I2C peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStart(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭产生起始条件。
源码注释
name    I2C_GenerateStart.
fun     Enable or disables Generate a START condition.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_GenerateStop(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭产生停止条件。
源码注释
name    I2C_GenerateStop.
fun     Enable  or disables Generate a STOP condition.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigAck(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭应答。
源码注释
name    I2C_ConfigAck.
fun     Enable  or disables the acknowledgement.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SendAddr7bit(I2C_Module* I2Cx, uint8_t address, uint8_t mode)发送地址字节以选中从设备。
源码注释
name    I2C_SendAddr7bit.
fun     Transmits the address byte to select the slave device.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   address (The input parameters must be the following values)
        - 0 to 0xFF
param   mode (The input parameters must be the following values):
          - I2C_DIRECTION_SEND    Transmitter mode
          - I2C_DIRECTION_RECV    Receiver mode
return  none
void I2C_ConfigOwnAddr2(I2C_Module* I2Cx, uint8_t address)设置指定的 I2C 自有地址 2。
源码注释
name    I2C_ConfigOwnAddr2.
fun     Set the specified I2C own address2.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   address(The input parameters must be the following values)
          - - 0 to 0xFF
return  none
void I2C_EnableDualAddr(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 双地址模式。
源码注释
name    I2C_EnableDualAddr.
fun     Enables or disables the specified I2C dual addressing mode.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
\*\param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableGeneralCall(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 广播呼叫功能。
源码注释
name    I2C_EnableGeneralCall.
fun     Enables or disables the specified I2C general call feature.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_ConfigInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)使能指定的 I2C 中断。
源码注释
name    I2C_ConfigInt.
fun     Enables the specified I2C interrupts.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_IT (The input parameters must be the following values):
          - I2C_INT_BUF      Buffer interrupt mask
          - I2C_INT_EVENT    Event interrupt mask
          - I2C_INT_ERR      Error interrupt mask
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SendData(I2C_Module* I2Cx, uint8_t data)通过 I2Cx 外设发送一字节数据。
源码注释
name    I2C_SendData.
fun     Sends a data byte through the I2Cx peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   data: 0x00~0xFF
return  none
uint8_t I2C_RecvData(I2C_Module* I2Cx)返回 I2Cx 外设最近收到的数据。
源码注释
name    I2C_RecvData.
fun     Returns the most recent received data by the I2Cx peripheral.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
return  The value of the received data
void I2C_EnableDMA(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭 I2C 的 DMA 传输。
源码注释
name    I2C_EnableDMA.
fun     Enables or disables I2C DMA transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableDmaLastSend(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭 I2C 的 DMA 最后一次传输。
源码注释
name    I2C_EnableDmaLastSend.
fun     Enables  or disables I2C DMA last transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    This bit is used in the master receiving mode, so that a NACK can be generated when
        data is received for the last time.
void I2C_EnableSoftwareReset(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 软件复位。
源码注释
name    I2C_EnableSoftwareReset.
fun     Enables or disables the specified I2C software reset.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    This bit can be used when the I2C_STS2.BUSY bit is set to 1 and no stop condition is detected
        on the bus.
void I2C_ConfigNackLocation(I2C_Module* I2Cx, uint32_t position)在主机接收模式下选择 NACK 位置。
源码注释
name    I2C_ConfigNackLocation.
fun     Selects the specified I2C NACK position in master receiver mode.
        This function is useful in I2C Master Receiver mode when the number
        of data to be received is equal to 2. In this case, this function
        should be called (with parameter I2C_NACK_POS_NEXT) before data
        reception starts,as described in the 2-byte reception procedure
        recommended in User Manual in Section: Master receiver.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   position (The input parameters must be the following values):
          - I2C_NACK_POS_NEXT       Indicates that the next byte will be the last received byte.
          - I2C_NACK_POS_CURRENT    Indicates that the current byte is the last received byte.
return  none
note    ACKPOS bit can only be used in 2-byte receiving configuration and must be configured before
        receiving data.
        For the second byte of NACK, the I2C_CTRL1.ACKEN bit must be cleared after the
        I2C_STS1.ADDRF bit is cleared.
        To detect the PEC of the second byte, the I2C_CTRL1.PEC bit must be set after the ACKPOS bit is
        configured and when the ADDR event is extended.
void I2C_ConfigPecLocation(I2C_Module* I2Cx, uint32_t position)选择指定的 I2C PEC 位置。
源码注释
name    I2C_ConfigPecLocation.
fun     Selects the specified I2C PEC position.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   position (The input parameters must be the following values):
          - I2C_PEC_POS_NEXT       Indicates that the next byte will be the last received byte or the next byte is PEC.
          - I2C_PEC_POS_CURRENT    Indicates that the current byte is the last received byte or the current byte is PEC.
return  none
note   This function configures the same bit (ACKPOS) as I2C_ConfigNackLocation()
       but is intended to be used in SMBUS mode while I2C_ConfigNackLocation()
       is intended to used in I2C mode.
void I2C_ConfigSmbusAlert(I2C_Module* I2Cx, uint32_t polarity)设置指定 I2C 的 SMBus Alert 引脚为高电平或低电平。
源码注释
name    I2C_ConfigSmbusAlert.
fun     Set the SMBus Alert pin high or low for the specified I2C.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   polarity (The input parameters must be the following values):
          - I2C_SMBALERT_LOW     SMBAlert pin driven low
          - I2C_SMBALERT_HIGH    SMBAlert pin driven high
return  none
void I2C_SendPEC(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C PEC 传输。
源码注释
name    I2C_SendPEC.
fun     Enables or disables the specified I2C PEC transfer.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
note    When arbitration is lost, the calculation of PEC is invalid.
void I2C_ComputePec(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭已传输字节的 PEC 值计算。
源码注释
name    I2C_ComputePec.
fun     Enables or disables the PEC value calculation of the transferred bytes.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
uint8_t I2C_GetPec(I2C_Module* I2Cx)获取指定 I2C 的 PEC 值。
源码注释
name    I2C_GetPec.
fun     Get the PEC value for the specified I2C.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
return  The PEC value
void I2C_EnableArp(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 地址解析协议(ARP)。
源码注释
name    I2C_EnableArp.
fun     Enables or disables the specified I2C Address Resolution Protocol(ARP).
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableExtendClk(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭指定的 I2C 时钟延展。
源码注释
name    I2C_EnableExtendClk.
fun     Enables or disables the specified I2C clock extending.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SetSDADigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)选择 SDA 数字滤波器宽度。
源码注释
name    I2C_SetSDADigitalFilterWidth.
fun     SDA digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none
void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)选择 SCL 数字滤波器宽度。
源码注释
name    I2C_SetSCLDigitalFilterWidth.
fun     SCL digital filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
          - 0x00            Disable the digital filter.
          - 0x01 ~ 0x0F     The numbers of pclk cycles.
return  none
void I2C_SetSDAAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)选择 SDA 模拟滤波器宽度。
源码注释
name    I2C_SetSDAAnalogFilterWidth.
fun     SDA analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
        - I2C_ANALOG_FILTER_WIDTH_5NS
        - I2C_ANALOG_FILTER_WIDTH_15NS
        - I2C_ANALOG_FILTER_WIDTH_25NS
        - I2C_ANALOG_FILTER_WIDTH_35NS
return  none
void I2C_EnableSDAAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭 SDA 模拟滤波器。
源码注释
name    I2C_EnableSDAAnalogFilter.
fun     Enables or disables the SDA analog filter.
param   I2Cx :
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_SetSCLAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)选择 SCL 模拟滤波器宽度。
源码注释
name    I2C_SetSCLAnalogFilterWidth.
fun     SCL analog filter width selection.
param   I2Cx :
          - I2C1
          - I2C2
param   width :
        - I2C_ANALOG_FILTER_WIDTH_5NS
        - I2C_ANALOG_FILTER_WIDTH_15NS
        - I2C_ANALOG_FILTER_WIDTH_25NS
        - I2C_ANALOG_FILTER_WIDTH_35NS
return  none
void I2C_EnableSCLAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)使能或关闭 SCL 模拟滤波器。
源码注释
name    I2C_EnableSCLAnalogFilter.
fun     Enables or disables the SCL analog filter.
param   I2Cx :
          - I2C1
          - I2C2
param   Cmd:
          - ENABLE
          - DISABLE
return  none
ErrorStatus I2C_CheckEvent(I2C_Module* I2Cx, uint32_t I2C_event)检查 I2Cx 最近一次事件是否等于传入的事件。
源码注释
name    I2C_CheckEvent.
fun     Checks whether the last I2Cx Event is equal to the one passed as parameter.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_event :
          - I2C_EVT_SLAVE_SEND_ADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_RECV_ADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_SEND_ADDR2_MATCHED                    EV1
          - I2C_EVT_SLAVE_RECV_ADDR2_MATCHED                    EV1
          - I2C_EVT_SLAVE_GCALLADDR_MATCHED                     EV1
          - I2C_EVT_SLAVE_DATA_RECVD                            EV2
          - (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_DUALFLAG)      EV2
          - (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_GCALLADDR)     EV2
          - I2C_EVT_SLAVE_DATA_SENDED                           EV3
          - (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_DUALFLAG)     EV3
          - (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_GCALLADDR)    EV3
          - I2C_EVT_SLAVE_ACK_MISS                              EV3_2
          - I2C_EVT_SLAVE_STOP_RECVD                            EV4
          - I2C_EVT_MASTER_MODE_FLAG                            EV5
          - I2C_EVT_MASTER_TXMODE_FLAG                          EV6
          - I2C_EVT_MASTER_RXMODE_FLAG                          EV6
          - I2C_EVT_MASTER_DATA_RECVD_FLAG                      EV7
          - I2C_EVT_MASTER_DATA_SENDING                         EV8
          - I2C_EVT_MASTER_DATA_SENDED                          EV8_2
          - I2C_EVT_MASTER_MODE_ADDRESS10_FLAG                  EV9
return  SUCCESS or ERROR
note    If an error occurs (that is, an error flag is set in addition to the flags parameter entered), the function may still return SUCCESS.
        In this case, you can use error interrupts to handle error events.
uint32_t I2C_GetLastEvent(I2C_Module* I2Cx)返回 I2Cx 最近一次事件。
源码注释
name    I2C_GetLastEvent.
fun     Returns the last I2Cx Event.
param   I2Cx :
          - I2C1
          - I2C2
return  The last event
note    Users can define their own events.
        If the user decides to check only the general communication flags (ignore error flags),
        when an error occurs, you can use error interrupts to handle error events.
FlagStatus I2C_GetFlag(I2C_Module* I2Cx, uint32_t I2C_flag)检查指定的 I2C 标志是否置位。
源码注释
name    I2C_GetFlag.
fun     Checks whether the specified I2C flag is set or not.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_flag :
          - I2C_FLAG_DUALFLAG     Dual flag (Slave mode)
          - I2C_FLAG_SMBHADDR     SMBus host header (Slave mode)
          - I2C_FLAG_SMBDADDR     SMBus default header (Slave mode)
          - I2C_FLAG_GCALLADDR    General call header flag (Slave mode)
          - I2C_FLAG_TRF          Transmitter/Receiver flag
          - I2C_FLAG_BUSY         Bus busy flag
          - I2C_FLAG_MSMODE       Master/Slave flag
          - I2C_FLAG_SMBALERT     SMBus Alert flag
          - I2C_FLAG_TIMEOUT      Timeout
          - I2C_FLAG_PECERR       PEC error in reception flag
          - I2C_FLAG_OVERRUN      Overrun/Underrun flag (Slave mode)
          - I2C_FLAG_ACKFAIL      Acknowledge failure flag
          - I2C_FLAG_ARLOST       Arbitration lost flag (Master mode)
          - I2C_FLAG_BUSERR       Bus error flag
          - I2C_FLAG_TXDATE       Data register empty flag (Transmitter)
          - I2C_FLAG_RXDATNE      Data register not empty (Receiver) flag
          - I2C_FLAG_STOPF        Stop detection flag (Slave mode)
          - I2C_FLAG_ADDR10F      10-bit header sent flag (Master mode)
          - I2C_FLAG_BYTEF        Byte transfer finished flag
          - I2C_FLAG_ADDRF        Address sent flag (Master mode)
                                  Address matched flag (Slave mode)
          - I2C_FLAG_STARTBF      Start bit flag (Master mode)
return  SET or RESET
note    When this function is called, the status register is accessed, so other flags may be cleared.
        You can call I2C_GetFlag() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrFlag(I2C_Module* I2Cx, uint32_t I2C_flag)清除 I2Cx 的标志。
源码注释
name    I2C_ClrFlag.
fun     Clears the I2Cx's flags.
param   I2Cx :
          - I2C1
          - I2C2
param   I2C_flag :
          - I2C_FLAG_SMBALERT    SMBus Alert flag
          - I2C_FLAG_TIMEOUT      Timeout
          - I2C_FLAG_PECERR      PEC error in reception flag
          - I2C_FLAG_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_FLAG_ACKFAIL     Acknowledge failure flag
          - I2C_FLAG_ARLOST      Arbitration lost flag (Master mode)
          - I2C_FLAG_BUSERR      Bus error flag
note    Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
        The other flag bits are cleared in the following sequence:
        - STOPF (STOP detection) is cleared by software sequence: a read operation
          to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
          to re-enable the I2C peripheral).
        - ADDR10F (10-bit header sent) is cleared by software sequence: a read
          operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
        - BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
          operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
        - ADDRF (Address sent) is cleared by software sequence: a read operation to
          I2C_STS1 register followed by a read operation to I2C_STS2 register.
        - STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
          register followed by a write operation to I2C_DAT register.
return  none
INTStatus I2C_GetIntStatus(I2C_Module* I2Cx, uint32_t Interrupt)检查指定的 I2C 中断是否发生。
源码注释
name    I2C_GetIntStatus.
fun     Checks whether the specified I2C interrupt has occurred or not.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Interrupt (The input parameters must be the following values):
          - I2C_INT_SMBALERT    SMBus Alert flag
          - I2C_INT_TIMEOUT     Timeout
          - I2C_INT_PECERR      PEC error in reception flag
          - I2C_INT_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_INT_ACKFAIL     Acknowledge failure flag
          - I2C_INT_ARLOST      Arbitration lost flag (Master mode)
          - I2C_INT_BUSERR      Bus error flag
          - I2C_INT_TXDATE      Data register empty flag (Transmitter)
          - I2C_INT_RXDATNE     Data register not empty (Receiver) flag
          - I2C_INT_STOPF       Stop detection flag (Slave mode)
          - I2C_INT_ADDR10F     10-bit header sent flag (Master mode)
          - I2C_INT_BYTEF       Byte transfer finished flag
          - I2C_INT_ADDRF       Address sent flag (Master mode)
                                Address matched flag (Slave mode)
          - I2C_INT_STARTBF     Start bit flag (Master mode)
return  SET or RESET
note    You can call I2C_GetIntStatus() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrIntPendingBit(I2C_Module* I2Cx, uint32_t Interrupt)清除 I2Cx 的中断状态位。
源码注释
name    I2C_ClrIntPendingBit.
fun     Clears the I2Cx's interrupt statu bits.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   Interrupt (The input parameters must be the following values):
          - I2C_INT_SMBALERT    SMBus Alert flag
          - I2C_INT_TIMEOUT     Timeout
          - I2C_INT_PECERR      PEC error in reception flag
          - I2C_INT_OVERRUN     Overrun/Underrun flag (Slave mode)
          - I2C_INT_ACKFAIL     Acknowledge failure flag
          - I2C_INT_ARLOST      Arbitration lost flag (Master mode)
          - I2C_INT_BUSERR      Bus error flag
return  none
note    Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
        The other flag bits are cleared in the following sequence:
        - STOPF (STOP detection) is cleared by software sequence: a read operation
          to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
          to re-enable the I2C peripheral).
        - ADDR10F (10-bit header sent) is cleared by software sequence: a read
          operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
        - BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
          operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
        - ADDRF (Address sent) is cleared by software sequence: a read operation to
          I2C_STS1 register followed by a read operation to I2C_STS2 register.
        - STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
          register followed by a write operation to I2C_DAT register.
void I2C_SetTimeoutLowLimit(I2C_Module* I2Cx, uint32_t limit)选择超时低阈值。
源码注释
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)选择超时高阈值。
源码注释
name    I2C_SetTimeoutHighLimit.
fun     timeout high threshold selection.
param   I2Cx :
          - I2C1
          - I2C2
param   limit:
          - I2C_TIMEOUT_HIGHLIMIT_256US
          - I2C_TIMEOUT_HIGHLIMIT_512US
          - I2C_TIMEOUT_HIGHLIMIT_1MS
          - I2C_TIMEOUT_HIGHLIMIT_128US
return  none
void I2C_ConfigTimeoutInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)使能指定的 I2C 超时中断。
源码注释
name    I2C_ConfigTimeoutInt.
fun     Enables the specified I2C timeout interrupts.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_IT (The input parameters must be the following values):
          - I2C_TIMEOUT_INT_SCLLOW     SCL low timeout error interrupt
          - I2C_TIMEOUT_INT_SCLHIGH    SCL high timeout error interrupt
          - I2C_TIMEOUT_INT_SDALOW     SDA low timeout error interrupt
param   Cmd:
          - ENABLE
          - DISABLE
return  none
void I2C_EnableTimeoutDetect(I2C_Module* I2Cx, uint32_t timeout_type, FunctionalState Cmd)使能指定的 I2C 超时检测功能。
源码注释
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

使用提示: 注释 name 写作 I2C_EnableTimeout.,与函数名不一致;头文件第 529 行声明为 I2C_EnableTimeoutDetect。

FlagStatus I2C_GetTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)检查指定的 I2C 超时标志是否置位。
源码注释
name    I2C_GetTimeoutFlag.
fun     Checks whether the specified I2C timeout flag is set or not.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_flag (The input parameters must be the following values):
          - I2C_TIMEOUT_FLAG_SCLLOW     SCL low timeout flag
          - I2C_TIMEOUT_FLAG_SCLHIGH    SCL high timeout flag
          - I2C_TIMEOUT_FLAG_SDALOW     SDA low timeout flag
return  SET or RESET
void I2C_ClrTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)清除 I2Cx 的挂起标志。
源码注释
name    I2C_ClrTimeoutFlag.
fun     Clears the I2Cx's pending flags.
param   I2Cx (The input parameters must be the following values):
          - I2C1
          - I2C2
param   I2C_flag (The input parameters must be the following values):
          - I2C_TIMEOUT_FLAG_SCLLOW     SCL low timeout flag
          - I2C_TIMEOUT_FLAG_SCLHIGH    SCL high timeout flag
          - I2C_TIMEOUT_FLAG_SDALOW     SDA low timeout flag
return  none

5.10.3 Demo 示例说明

本节说明 I2C 的 7 个示例:EEPROM 通过 I2C 读写串行 EEPROM 并比对数据,I2C_Master 演示主机轮询收发,I2C_Master_Int 用事件与错误中断完成主机收发,I2C_Master_Dualaddr 用两个从机地址收发,I2C_Slave 演示从机轮询收发,I2C_Slave_Int 用中断完成从机收发,I2C_Slave_Dualaddr 用两个自有地址收发。

5.10.3.1 EEPROM 读写串行 EEPROM

工程目录: projects/n32g033_EVAL/examples/I2C/EEPROM/

使能 GPIO 与 I2C 时钟→配置 SCL、SDA 为复用开漏并开启内部上拉→初始化 I2C 主机并使能→按页写入一段递增数据→读回数据并逐字节比对

项目 源码解读
功能 对板载串行 EEPROM 写入一段递增数据,再读回并逐字节比对,结果写入测试状态。
源码组成 src/main.c 组织写入、读回与比对流程;
src/i2c_eeprom.c 实现 EEPROM 的初始化、页写与读回;
inc/i2c_eeprom.h 定义从机地址、页大小与超时参数;
src/n32g033_it.c 提供中断入口;
readme.txt 给出配置与测试步骤。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏并开启内部上拉;
速率:400 K;
寻址:7 位,从机地址 0xA0;
页大小:8 字节;
超时检测:关闭低压超时。
程序与数据路径 main → I2C_EE_Init 初始化引脚与 I2C 主机 → I2C_EE_WriteBuffer 按页写入 → I2C_EE_ReadBuffer 逐字节读回 → Buffercmp 比对并打印结果。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_GenerateStart、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_CheckEvent、I2C_GenerateStop、I2C_EnableTimeoutDetect
验证方法 编译下载后复位运行,串口打印读写一致即可判定 PASSED;
也可在调试模式下观察读写缓冲区是否相同。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例先完成引脚与 I2C 主机初始化,再按页写入、逐字节读回并比对。

数据路径: main 准备递增数据 → I2C_EE_Init 配置引脚与 I2C 主机 → I2C_EE_WriteBuffer 拆页经 I2C_EE_PageWrite 发出 → I2C_EE_WaitEepromStandbyState 等待内部写完成 → I2C_EE_ReadBuffer 取回 → Buffercmp 比对

1. 把 SCL、SDA 配成复用开漏并开启内部上拉,再灌入 I2C 主机参数。

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

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

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

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

换成其他引脚时同步修改头文件里的引脚宏与复用号,并确认总线上有外部上拉。

2. 按页写入:先发起始与器件地址,再写页内地址,最后逐字节发出并等待发送结束。

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

页大小取自 EEPROM 型号,跨页写入由上层函数拆页,改动型号时只改页大小宏。

3. 读回流程:先以写方向发器件地址与读地址,再重发起始后以读方向取回。

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

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

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

PEC 位置与应答设置要与后续末字节处理保持一致,改读长度时一并核对。

4. 写完后轮询器件内部状态,直到从机再次应答其地址才算写完成。

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

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

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

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

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

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

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

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

轮询次数上限用宏限制,移到更慢的器件时同步放宽,避免过早判定失败。

对应源码: src/main.c、src/i2c_eeprom.c、inc/i2c_eeprom.h、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.2 I2C_Master 主机轮询收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Master/

使能 GPIO 与 I2C 时钟→配置 SCL、SDA 为复用开漏→初始化主机并设置从机地址→轮询发送一段数据→轮询接收并比对

项目 源码解读
功能 以主机身份轮询方式向从机发送一段数据,再读回并逐字节比对;
通信超时会触发恢复回调。
源码组成 src/main.c 含主机初始化、发送、接收与超时回调;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明测试缓冲区与状态;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,主机地址 0x30、从机地址 0x10;
应答:接收时使能;
恢复方式:软件复位。
程序与数据路径 main → i2c_master_init 配置引脚与主机 → i2c_master_send 轮询发出数据 → i2c_master_recv 轮询读回 → Buffercmp 比对。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_GenerateStart、I2C_GenerateStop、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_CheckEvent、I2C_ConfigAck、I2C_ConfigNackLocation、I2C_EnableSoftwareReset
验证方法 编译下载后复位运行,串口打印收发一致即可判定 PASSED;
两块板或两个 I2C 实例分别作主从时也能观察收发过程。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例以轮询方式一步步推进事件标志,完成主机发送、接收与异常恢复。

数据路径: main 准备数据 → i2c_master_init 配置引脚与主机 → i2c_master_send 依次发起始、地址、数据、停止 → i2c_master_recv 按时长读回 → Buffercmp 比对

1. 主机初始化:开时钟、配引脚,再写入速率、地址与应答方式。

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

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

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

    I2C_Init(I2Cx, &i2c1_master);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);

速率与占空比要与从机能力匹配,改速率时保持占空比设置符合手册要求。

2. 发送流程:起始、器件地址、数据、停止逐步等待对应事件。

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

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

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

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

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

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

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

每一步都带超时计数,移植到更慢的总线时同步放宽计数上限。

3. 接收流程:按长度区分末字节处理,先配置 NACK 位置再关闭应答。

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

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

读到最后一个字节前必须关闭应答并准备停止,顺序颠倒会多收一个字节。

4. 通信卡死时用软件复位恢复总线,随后重新初始化。

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

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

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

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

软件复位前先确认引脚已释放、总线为空,复位后再重建 I2C 配置。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.3 I2C_Master_Int 主机中断收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Master_Int/

使能 GPIO 与 I2C 时钟→配置引脚、主机参数与中断优先级→使能事件与错误中断→中断中按事件推进收发→主循环等待完成标志并比对

项目 源码解读
功能 以主机身份用事件中断完成发送与接收,主循环只等待完成标志。
源码组成 src/main.c 含初始化、中断服务函数与主循环;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明缓冲区、标志与状态;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,主机地址 0x30、从机地址 0x10;
中断:事件与错误中断均使能,事件优先级 2、错误优先级 3。
程序与数据路径 main → i2c_master_init 配置主机并使能中断 → 启动后由 I2C1_EV_IRQHandler 按事件逐步收发 → 主循环等待完成标志 → Buffercmp 比对。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_ConfigInt、I2C_GenerateStart、I2C_GenerateStop、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_GetLastEvent、I2C_ConfigAck、I2C_ClrFlag
验证方法 编译下载后复位运行,串口打印收发一致即可判定 PASSED;
可在中断内观察事件值,确认收发按顺序推进。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例把收发过程搬进事件中断,主循环只负责启动与等待结果。

数据路径: main 启动起始条件 → I2C1_EV_IRQHandler 读最近事件 → 按事件值发地址、发数据或读数据 → 结束置完成标志 → 主循环等到标志后 Buffercmp 比对

1. 主机初始化:配好引脚与主机参数后使能事件、缓冲与错误中断。

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

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

中断使能放在参数配置之后,避免在总线未配置时提前进入中断。

2. 中断优先级:事件与错误两路分开配置。

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

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

两路优先级要与系统其他中断统一排布,避免高优先级中断长时间占用总线。

3. 事件中断:读出最近事件,再按事件值决定发地址、发数据或读数据。

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

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

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

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

        case I2C_EVT_MASTER_DATA_SENDING: // EV8 Sending data

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

        case I2C_EVT_MASTER_DATA_SENDED: // EV8_2 Send finish

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

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

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

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

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

事件匹配要覆盖正常与异常两路,异常分支先复位外设再重试。

4. 错误中断:收到应答失败时清标志并补发停止条件。

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

错误中断里只做最小恢复动作,重试逻辑交给上层。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.4 I2C_Master_Dualaddr 主机双地址收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Master_Dualaddr/

使能 GPIO 与 I2C 时钟→配置主机参数→先用第一个地址收发并比对→再用第二个地址收发并比对

项目 源码解读
功能 以主机身份向两个不同从机地址先后发送并读回,各自比对结果。
源码组成 src/main.c 含主机初始化、带地址参数的发送与接收、主循环与恢复回调;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明两个从机地址、缓冲区与状态;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,主机地址 0x30,从机地址 0x10 与 0x20;
应答:接收时使能。
程序与数据路径 main → i2c_master_init 配置主机 → 先用地址 0x10 调 i2c_master_send 与 i2c_master_recv → 再用地址 0x20 重复 → 各自 Buffercmp 比对。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_GenerateStart、I2C_GenerateStop、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_CheckEvent、I2C_ConfigAck、I2C_ConfigNackLocation
验证方法 编译下载后复位运行,两个地址的收发都一致时串口分别打印通过;
可在调试模式观察两次比对状态。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例把从机地址作为函数参数传入,用同一套收发逻辑轮流访问两个从机。

数据路径: main 填充数据 → i2c_master_init 配置主机 → i2c_master_send(tx, len, 0x10) 与 i2c_master_recv(rx, len, 0x10) → Buffercmp 比对 → 清空缓冲区后对 0x20 重复

1. 主机初始化与单地址示例一致,速率与寻址方式相同。

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

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

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

    I2C_Init(I2Cx, &i2c1_master);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);
    return 0;

若要区分不同速率的外设,可把速率也做成初始化函数的参数。

2. 发送时把地址写进器件地址字节,其余流程不变。

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

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

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

地址参数由调用者提供,移植时只需在调用处替换目标地址。

3. 接收同样按传入地址寻址,末字节处理沿用通用逻辑。

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

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

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

两次接收之间要清空并复位序号,避免上一轮残留影响比对。

4. 主循环对两个地址各做一轮发送、接收与比对。

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

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

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

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

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

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

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

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

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

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

两轮之间留出延时,给从机内部处理留出时间再发起下一轮。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.5 I2C_Slave 从机轮询收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Slave/

使能 GPIO 与 I2C 时钟→配置自有地址与从机参数→轮询等待地址匹配并接收→接收完成后原样发回→循环收发

项目 源码解读
功能 以从机身份轮询等待主机寻址,先接收一段数据再原样发回,循环往复。
源码组成 src/main.c 含从机初始化、发送、接收、主循环与恢复回调;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明自有地址、缓冲区与状态;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,自有地址 0x10;
应答:使能。
程序与数据路径 main → i2c_slave_init 配置从机 → i2c_slave_recv 等地址匹配后接收 → i2c_slave_send 原样发回 → 循环重复。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_CheckEvent、I2C_SendData、I2C_RecvData、I2C_GetLastEvent、I2C_ClrFlag
验证方法 编译下载后复位运行,与主机示例联调时从机按主机节奏收发;
可在调试模式观察接收与发送的数据是否一致。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例以从机身份轮询等待地址匹配,接收后把数据原样发回。

数据路径: main 循环调用 → i2c_slave_recv 等地址匹配并逐字节接收 → i2c_slave_send 逐字节发回 → 清空缓冲区进入下一轮

1. 从机初始化:把自有地址写入参数并使能应答。

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

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

    I2C_Init(I2Cx, &i2c1_slave);

    I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);

    I2C_Enable(I2Cx, ENABLE);

自有地址不能落在保留区间,改地址时要与主机寻址保持一致。

2. 发送流程:等地址匹配后逐字节发出,遇到停止或应答缺失即结束。

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

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


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

从机不能控制停止时机,发送长度要与主机预期一致。

3. 接收流程:等地址匹配后逐字节接收,收到停止条件结束。

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

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

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

    return 0;

接收计数要与发送侧对应,改长度时两侧同步修改。

4. 主循环反复执行接收与发送,每轮结束后清空缓冲区。

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

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

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

失败分支直接回到循环开头重试,移植时可按需要加入退避与日志。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.6 I2C_Slave_Int 从机中断收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Int/

使能 GPIO 与 I2C 时钟→配置从机参数、中断与优先级→在事件中断中按地址与数据事件收发→主循环等待完成标志

项目 源码解读
功能 以从机身份用事件中断完成接收与发送,主循环只等待完成标志。
源码组成 src/main.c 含从机初始化、中断服务函数与主循环;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明自有地址、缓冲区与标志;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,自有地址 0x10;
中断:事件与错误中断均使能,事件优先级 2、错误优先级 3。
程序与数据路径 main → i2c_slave_init 配置从机并使能中断 → I2C1_EV_IRQHandler 按事件收发并置完成标志 → 主循环等待标志。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_ConfigInt、I2C_GetLastEvent、I2C_RecvData、I2C_SendData、I2C_ClrFlag
验证方法 编译下载后复位运行,与主机示例联调时串口打印接收与发送长度;
可在中断内观察事件值,确认收发按顺序推进。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例把从机收发搬进事件中断,主循环只等待接收与发送完成的标志。

数据路径: I2C1_EV_IRQHandler 读最近事件 → 地址匹配时准备收发 → 数据事件时经数据寄存器搬运 → 停止事件置完成标志 → 主循环等到标志后打印长度

1. 从机中断初始化:写完参数后使能事件、缓冲与错误中断。

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

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

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

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

中断使能要排在参数配置之后,避免总线未就绪时提前触发。

2. 中断优先级:事件与错误两路分开配置。

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

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

优先级要与系统其他中断统一排布,避免打断关系出错。

3. 事件中断:区分接收与发送地址匹配,再按数据事件搬运。

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

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

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

        case I2C_EVT_SLAVE_SEND_ADDR_MATCHED: //EV1 Tx addr matched

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

        // SlaveTransmitter
        case I2C_EVT_SLAVE_DATA_SENDING:  //EV3 Sending data

            break;

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

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


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

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

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

从机模式要在事件里判断主从角色,只有从机事件才处理。

4. 错误中断:应答缺失时清标志,并按是否末字节决定完成标志。

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

接收计数为零时的应答缺失说明发送未被接受,需要重发。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.3.7 I2C_Slave_Dualaddr 从机双地址收发

工程目录: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Dualaddr/

使能 GPIO 与 I2C 时钟→配置自有地址 1 并使能双地址→设置自有地址 2→分别按两个地址接收与发回

项目 源码解读
功能 以从机身份配置两个自有地址,分别响应主机的寻址并收发数据。
源码组成 src/main.c 含从机初始化、带地址参数的发送与接收、主循环与恢复回调;
src/n32g033_it.c 与 inc/n32g033_it.h 提供中断入口;
inc/main.h 声明两个自有地址、缓冲区与状态;
readme.txt 给出配置与测试步骤;
工程文件在 MDK-ARM/ 与 EWARM/。
关键配置 引脚:SCL 接 PB6、SDA 接 PB7,复用开漏;
速率:100 K;
寻址:7 位,自有地址 0x10 与 0x20;
双地址模式:使能。
程序与数据路径 main → i2c_slave_init 配置自有地址 1 并使能双地址 → 地址 2 经 I2C_ConfigOwnAddr2 写入 → 按地址 1 与地址 2 分别调 i2c_slave_recv 与 i2c_slave_send。
主要 API I2C_InitStruct、I2C_Init、I2C_Enable、I2C_ConfigOwnAddr2、I2C_EnableDualAddr、I2C_CheckEvent、I2C_RecvData、I2C_SendData、I2C_ClrFlag
验证方法 编译下载后复位运行,与双地址主机示例联调时两个地址都能收发;
可在调试模式观察两个地址的收发情况。
使用提示 SCL 接 PB6、SDA 接 PB7,需外接上拉电阻,从机地址不要使用 0xF0、0xF2、0xF4、0xF6;
开发环境为 KEIL MDK-ARM V5.34.0.0 或 IAR EWARM 8.50.1,硬件基于评估板 N32G033K8Q7-1_STB V1.0;
系统时钟 64 MHz。
核心代码解读与移植

示例为从机配置两个自有地址,用同一套收发逻辑响应主机的不同寻址。

数据路径: i2c_slave_init 写自有地址 1 并经地址 2 与双地址使能 → i2c_slave_recv 按地址等待匹配并接收 → i2c_slave_send 按地址发回 → 主循环对两个地址各走一轮

1. 从机初始化:写入自有地址 1,再写入地址 2 并使能双地址模式。

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

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

    I2C_Enable(I2Cx, ENABLE);

地址 1 走初始化参数,地址 2 需单独写入;两个地址都不能落在保留区间。

2. 发送:按传入的地址选择匹配事件,再逐字节发出。

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

两种地址对应不同的事件值,改地址后要同步核对匹配事件。

3. 接收:按传入的地址等待匹配,再逐字节接收并等停止条件。

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

接收计数用完后再等停止条件,避免提前结束丢字节。

4. 主循环对地址 1 与地址 2 各做一轮接收与发送。

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

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


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

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

两轮顺序与主机侧保持一致,移植时把地址做成参数即可复用。

对应源码: src/main.c、src/n32g033_it.c、firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h

5.10.4 注意与排障

场景 检查与建议 来源
SDA、SCL 引脚 用户手册 N32G033 的 I2C 章写明 SDA 与 SCL 为开漏输出,必须外接上拉电阻,总线空闲时两线均为高电平;
上拉阻值按总线速率与容性负载选取。
用户手册
时钟延展 用户手册 N32G033 的 I2C 章写明确认从机是否支持时钟延展,若清除 NOEXTEND 使能延展,从机拉低 SCL 时会展宽总线时钟;
不支持延展的从机应保持 NOEXTEND 有效。
用户手册
软件复位 用户手册 N32G033 的 I2C 章写明置位 SWRESET 前要确认 I2C 引脚已释放、总线为空,否则复位可能中断正在进行的传输。 用户手册
配置寄存器的写入时机 用户手册 N32G033 的 I2C 章写明 I2C_CTRL1 的 EN 位为 0 时才能写时钟与滤波等配置寄存器;
修改分频或滤波参数前先关闭 I2C。
用户手册
模拟滤波器 用户手册 N32G033 的 I2C 章写明 SDA、SCL 模拟滤波器默认开启;
若需关闭须显式写寄存器,改动后重新核对总线波形。
用户手册
主机接收末字节 用户手册 N32G033 的 I2C 章写明主机接收最后 1~2 个字节前要配置 NACK 位置并关闭应答,再产生停止条件。 用户手册
标准模式下的停止建立时间 勘误手册 V1.0.0(2025.9.25)针对 A 版芯片列出该问题:主机模式通讯速率 100 K 时,触发从机时钟延展后 STOP 建立时间会小于 4 µs;
建议按从机外设时序要求把速率降到 50 K 及以下。
勘误手册

勘误手册 V1.0.0(2025.9.25)针对 A 版芯片,其 I2C 接口章节列出标准模式下 STOP 建立时间的限制,见上表最后一行。

国民技术开发套件文档中心

按芯片系列在线查阅国民技术 MCU 开发套件的芯片资料、SDK、驱动 API 与 Demo 示例说明。

© 2026 国民技术股份有限公司 版权所有粤ICP备11069485号本站使用不含 Cookie 的匿名访问统计