5.10 I2C Guide
This chapter describes the file layout, the 43 public functions, and the 7 examples (EEPROM, I2C_Master, I2C_Master_Int, I2C_Master_Dualaddr, I2C_Slave, I2C_Slave_Int, and I2C_Slave_Dualaddr) of the I2C driver in the N32G033 standard peripheral library. Read the “I2C” chapter of the user manual for the bus timing, clock stretching, and digital/analog filters, and the errata sheet for known chip issues.
5.10.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h |
Defines the I2C_InitType structure and the macros for bus mode, addressing, acknowledge, clock stretching, digital and analog filters, PEC, and timeout detection, and declares 43 public functions in sections such as reset and initialization, start/stop and slave address, and interrupt and data transfer |
firmware/n32g033_std_periph_driver/src/n32g033_i2c.c |
Implements those 43 functions: I2C reset and initialization, start/stop and address transfer, SMBus and PEC, clock stretching and digital/analog filters, timeout detection, and event flag and interrupt handling |
projects/n32g033_EVAL/examples/I2C/ |
I2C example root directory, with 7 examples: EEPROM, I2C_Master, I2C_Master_Int, I2C_Master_Dualaddr, I2C_Slave, I2C_Slave_Int, and I2C_Slave_Dualaddr |
projects/n32g033_EVAL/examples/I2C/EEPROM/ |
Project layout of this example: MDK-ARM/, EWARM/, inc/, src/, and readme.txt |
projects/n32g033_EVAL/bsp/ |
Board support files shared by the examples: the SysTick delay in delay.h and the UART log interface in log.h |
5.10.2 API reference
The following 43 public functions are listed in the declaration order of n32g033_i2c.h in 8 groups; each entry gives a summary and the original comment above the function in n32g033_i2c.c.
Reset and initialization
I2C_DeInit, I2C_Init, I2C_InitStruct, I2C_Enable
Start/stop, acknowledge, and slave address
I2C_GenerateStart, I2C_GenerateStop, I2C_ConfigAck, I2C_SendAddr7bit, I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_EnableGeneralCall
Interrupts, data transfer, and DMA
I2C_ConfigInt, I2C_SendData, I2C_RecvData, I2C_EnableDMA, I2C_EnableDmaLastSend
Software reset and NACK/PEC position
I2C_EnableSoftwareReset, I2C_ConfigNackLocation, I2C_ConfigPecLocation
SMBus, PEC, and ARP
I2C_ConfigSmbusAlert, I2C_SendPEC, I2C_ComputePec, I2C_GetPec, I2C_EnableArp
Clock stretching, digital and analog filters
I2C_EnableExtendClk, I2C_SetSDADigitalFilterWidth, I2C_SetSCLDigitalFilterWidth, I2C_SetSDAAnalogFilterWidth, I2C_EnableSDAAnalogFilter, I2C_SetSCLAnalogFilterWidth, I2C_EnableSCLAnalogFilter
Events, flags, and interrupt status
I2C_CheckEvent, I2C_GetLastEvent, I2C_GetFlag, I2C_ClrFlag, I2C_GetIntStatus, I2C_ClrIntPendingBit
Timeout detection
I2C_SetTimeoutLowLimit, I2C_SetTimeoutHighLimit, I2C_ConfigTimeoutInt, I2C_EnableTimeoutDetect, I2C_GetTimeoutFlag, I2C_ClrTimeoutFlag
void I2C_DeInit(I2C_Module* I2Cx)Deinitializes the I2Cx peripheral registers to their default reset values.
name I2C_DeInit.
fun Deinitializes the I2Cx peripheral registers to their default reset values.
param I2Cx :
- I2C1
- I2C2
return none
void I2C_Init(I2C_Module* I2Cx, I2C_InitType* I2C_InitParam)Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
name I2C_Init.
fun Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
param I2Cx :
- I2C1
- I2C2
param I2C_InitParam :
- ClkSpeed(common speed:100000(100KHz) - 400000(400KHz) - 1000000(1MHz)):
- between 1 to 1000000
- BusMode:
- I2C_BUSMODE_I2C
- I2C_BUSMODE_SMBDEVICE
- I2C_BUSMODE_SMBHOST
- FmDutyCycle(If the ClkSpeed does not exceed 100KHz,duty is fixed at 1:1,
otherwise it can be configured as 2:1 or 16:9):
- I2C_FMDUTYCYCLE_16_9
- I2C_FMDUTYCYCLE_2
- OwnAddr1:
- between 0 to 0x3FF
- AckEnable:
- I2C_ACKEN
- I2C_ACKDIS
- AddrMode:
- I2C_ADDR_MODE_7BIT
- I2C_ADDR_MODE_10BIT
return none
void I2C_InitStruct(I2C_InitType* I2C_InitParam)Fills each I2C_InitParam member with its default value.
name I2C_Initializes_Structure.
fun Fills each I2C_InitParam member with its default value.
param (The input parameters must be the following values):
- ClkSpeed
- BusMode
- FmDutyCycle
- OwnAddr1
- AckEnable
- AddrMode
return none
Use note: The comment names the function I2C_Initializes_Structure., which differs from the function name; line 491 of the header declares it as I2C_InitStruct.
void I2C_Enable(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C peripheral.
name I2C_Enable.
fun Enables or disables the specified I2C peripheral.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_GenerateStart(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables Generate a START condition.
name I2C_GenerateStart.
fun Enable or disables Generate a START condition.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_GenerateStop(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables Generate a STOP condition.
name I2C_GenerateStop.
fun Enable or disables Generate a STOP condition.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_ConfigAck(I2C_Module* I2Cx, FunctionalState Cmd)Enable or disables the acknowledgement.
name I2C_ConfigAck.
fun Enable or disables the acknowledgement.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_SendAddr7bit(I2C_Module* I2Cx, uint8_t address, uint8_t mode)Transmits the address byte to select the slave device.
name I2C_SendAddr7bit.
fun Transmits the address byte to select the slave device.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param address (The input parameters must be the following values)
- 0 to 0xFF
param mode (The input parameters must be the following values):
- I2C_DIRECTION_SEND Transmitter mode
- I2C_DIRECTION_RECV Receiver mode
return none
void I2C_ConfigOwnAddr2(I2C_Module* I2Cx, uint8_t address)Set the specified I2C own address2.
name I2C_ConfigOwnAddr2.
fun Set the specified I2C own address2.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param address(The input parameters must be the following values)
- - 0 to 0xFF
return none
void I2C_EnableDualAddr(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C dual addressing mode.
name I2C_EnableDualAddr.
fun Enables or disables the specified I2C dual addressing mode.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
\*\param Cmd:
- ENABLE
- DISABLE
return none
void I2C_EnableGeneralCall(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C general call feature.
name I2C_EnableGeneralCall.
fun Enables or disables the specified I2C general call feature.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_ConfigInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables the specified I2C interrupts.
name I2C_ConfigInt.
fun Enables the specified I2C interrupts.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param I2C_IT (The input parameters must be the following values):
- I2C_INT_BUF Buffer interrupt mask
- I2C_INT_EVENT Event interrupt mask
- I2C_INT_ERR Error interrupt mask
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_SendData(I2C_Module* I2Cx, uint8_t data)Sends a data byte through the I2Cx peripheral.
name I2C_SendData.
fun Sends a data byte through the I2Cx peripheral.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param data: 0x00~0xFF
return none
uint8_t I2C_RecvData(I2C_Module* I2Cx)Returns the most recent received data by the I2Cx peripheral.
name I2C_RecvData.
fun Returns the most recent received data by the I2Cx peripheral.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
return The value of the received data
void I2C_EnableDMA(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables I2C DMA transfer.
name I2C_EnableDMA.
fun Enables or disables I2C DMA transfer.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_EnableDmaLastSend(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables I2C DMA last transfer.
name I2C_EnableDmaLastSend.
fun Enables or disables I2C DMA last transfer.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
note This bit is used in the master receiving mode, so that a NACK can be generated when
data is received for the last time.
void I2C_EnableSoftwareReset(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C software reset.
name I2C_EnableSoftwareReset.
fun Enables or disables the specified I2C software reset.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
note This bit can be used when the I2C_STS2.BUSY bit is set to 1 and no stop condition is detected
on the bus.
void I2C_ConfigNackLocation(I2C_Module* I2Cx, uint32_t position)Selects the specified I2C NACK position in master receiver mode. This function is useful in I2C Master Receiver mode when the number of data to be received is equal to 2. In this case, this function should be called (with parameter I2C_NACK_POS_NEXT) before data reception starts,as described in the 2-byte reception procedure recommended in User Manual in Section: Master receiver.
name I2C_ConfigNackLocation.
fun Selects the specified I2C NACK position in master receiver mode.
This function is useful in I2C Master Receiver mode when the number
of data to be received is equal to 2. In this case, this function
should be called (with parameter I2C_NACK_POS_NEXT) before data
reception starts,as described in the 2-byte reception procedure
recommended in User Manual in Section: Master receiver.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param position (The input parameters must be the following values):
- I2C_NACK_POS_NEXT Indicates that the next byte will be the last received byte.
- I2C_NACK_POS_CURRENT Indicates that the current byte is the last received byte.
return none
note ACKPOS bit can only be used in 2-byte receiving configuration and must be configured before
receiving data.
For the second byte of NACK, the I2C_CTRL1.ACKEN bit must be cleared after the
I2C_STS1.ADDRF bit is cleared.
To detect the PEC of the second byte, the I2C_CTRL1.PEC bit must be set after the ACKPOS bit is
configured and when the ADDR event is extended.
void I2C_ConfigPecLocation(I2C_Module* I2Cx, uint32_t position)Selects the specified I2C PEC position.
name I2C_ConfigPecLocation.
fun Selects the specified I2C PEC position.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param position (The input parameters must be the following values):
- I2C_PEC_POS_NEXT Indicates that the next byte will be the last received byte or the next byte is PEC.
- I2C_PEC_POS_CURRENT Indicates that the current byte is the last received byte or the current byte is PEC.
return none
note This function configures the same bit (ACKPOS) as I2C_ConfigNackLocation()
but is intended to be used in SMBUS mode while I2C_ConfigNackLocation()
is intended to used in I2C mode.
void I2C_ConfigSmbusAlert(I2C_Module* I2Cx, uint32_t polarity)Set the SMBus Alert pin high or low for the specified I2C.
name I2C_ConfigSmbusAlert.
fun Set the SMBus Alert pin high or low for the specified I2C.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param polarity (The input parameters must be the following values):
- I2C_SMBALERT_LOW SMBAlert pin driven low
- I2C_SMBALERT_HIGH SMBAlert pin driven high
return none
void I2C_SendPEC(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C PEC transfer.
name I2C_SendPEC.
fun Enables or disables the specified I2C PEC transfer.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
note When arbitration is lost, the calculation of PEC is invalid.
void I2C_ComputePec(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the PEC value calculation of the transferred bytes.
name I2C_ComputePec.
fun Enables or disables the PEC value calculation of the transferred bytes.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
uint8_t I2C_GetPec(I2C_Module* I2Cx)Get the PEC value for the specified I2C.
name I2C_GetPec.
fun Get the PEC value for the specified I2C.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
return The PEC value
void I2C_EnableArp(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C Address Resolution Protocol(ARP).
name I2C_EnableArp.
fun Enables or disables the specified I2C Address Resolution Protocol(ARP).
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_EnableExtendClk(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C clock extending.
name I2C_EnableExtendClk.
fun Enables or disables the specified I2C clock extending.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_SetSDADigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)SDA digital filter width selection.
name I2C_SetSDADigitalFilterWidth.
fun SDA digital filter width selection.
param I2Cx :
- I2C1
- I2C2
param width :
- 0x00 Disable the digital filter.
- 0x01 ~ 0x0F The numbers of pclk cycles.
return none
void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint8_t width)SCL digital filter width selection.
name I2C_SetSCLDigitalFilterWidth.
fun SCL digital filter width selection.
param I2Cx :
- I2C1
- I2C2
param width :
- 0x00 Disable the digital filter.
- 0x01 ~ 0x0F The numbers of pclk cycles.
return none
void I2C_SetSDAAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)SDA analog filter width selection.
name I2C_SetSDAAnalogFilterWidth.
fun SDA analog filter width selection.
param I2Cx :
- I2C1
- I2C2
param width :
- I2C_ANALOG_FILTER_WIDTH_5NS
- I2C_ANALOG_FILTER_WIDTH_15NS
- I2C_ANALOG_FILTER_WIDTH_25NS
- I2C_ANALOG_FILTER_WIDTH_35NS
return none
void I2C_EnableSDAAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the SDA analog filter.
name I2C_EnableSDAAnalogFilter.
fun Enables or disables the SDA analog filter.
param I2Cx :
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_SetSCLAnalogFilterWidth(I2C_Module* I2Cx, uint16_t width)SCL analog filter width selection.
name I2C_SetSCLAnalogFilterWidth.
fun SCL analog filter width selection.
param I2Cx :
- I2C1
- I2C2
param width :
- I2C_ANALOG_FILTER_WIDTH_5NS
- I2C_ANALOG_FILTER_WIDTH_15NS
- I2C_ANALOG_FILTER_WIDTH_25NS
- I2C_ANALOG_FILTER_WIDTH_35NS
return none
void I2C_EnableSCLAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the SCL analog filter.
name I2C_EnableSCLAnalogFilter.
fun Enables or disables the SCL analog filter.
param I2Cx :
- I2C1
- I2C2
param Cmd:
- ENABLE
- DISABLE
return none
ErrorStatus I2C_CheckEvent(I2C_Module* I2Cx, uint32_t I2C_event)Checks whether the last I2Cx Event is equal to the one passed as parameter.
name I2C_CheckEvent.
fun Checks whether the last I2Cx Event is equal to the one passed as parameter.
param I2Cx :
- I2C1
- I2C2
param I2C_event :
- I2C_EVT_SLAVE_SEND_ADDR_MATCHED EV1
- I2C_EVT_SLAVE_RECV_ADDR_MATCHED EV1
- I2C_EVT_SLAVE_SEND_ADDR2_MATCHED EV1
- I2C_EVT_SLAVE_RECV_ADDR2_MATCHED EV1
- I2C_EVT_SLAVE_GCALLADDR_MATCHED EV1
- I2C_EVT_SLAVE_DATA_RECVD EV2
- (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_DUALFLAG) EV2
- (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_GCALLADDR) EV2
- I2C_EVT_SLAVE_DATA_SENDED EV3
- (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_DUALFLAG) EV3
- (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_GCALLADDR) EV3
- I2C_EVT_SLAVE_ACK_MISS EV3_2
- I2C_EVT_SLAVE_STOP_RECVD EV4
- I2C_EVT_MASTER_MODE_FLAG EV5
- I2C_EVT_MASTER_TXMODE_FLAG EV6
- I2C_EVT_MASTER_RXMODE_FLAG EV6
- I2C_EVT_MASTER_DATA_RECVD_FLAG EV7
- I2C_EVT_MASTER_DATA_SENDING EV8
- I2C_EVT_MASTER_DATA_SENDED EV8_2
- I2C_EVT_MASTER_MODE_ADDRESS10_FLAG EV9
return SUCCESS or ERROR
note If an error occurs (that is, an error flag is set in addition to the flags parameter entered), the function may still return SUCCESS.
In this case, you can use error interrupts to handle error events.
uint32_t I2C_GetLastEvent(I2C_Module* I2Cx)Returns the last I2Cx Event.
name I2C_GetLastEvent.
fun Returns the last I2Cx Event.
param I2Cx :
- I2C1
- I2C2
return The last event
note Users can define their own events.
If the user decides to check only the general communication flags (ignore error flags),
when an error occurs, you can use error interrupts to handle error events.
FlagStatus I2C_GetFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Checks whether the specified I2C flag is set or not.
name I2C_GetFlag.
fun Checks whether the specified I2C flag is set or not.
param I2Cx :
- I2C1
- I2C2
param I2C_flag :
- I2C_FLAG_DUALFLAG Dual flag (Slave mode)
- I2C_FLAG_SMBHADDR SMBus host header (Slave mode)
- I2C_FLAG_SMBDADDR SMBus default header (Slave mode)
- I2C_FLAG_GCALLADDR General call header flag (Slave mode)
- I2C_FLAG_TRF Transmitter/Receiver flag
- I2C_FLAG_BUSY Bus busy flag
- I2C_FLAG_MSMODE Master/Slave flag
- I2C_FLAG_SMBALERT SMBus Alert flag
- I2C_FLAG_TIMEOUT Timeout
- I2C_FLAG_PECERR PEC error in reception flag
- I2C_FLAG_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_FLAG_ACKFAIL Acknowledge failure flag
- I2C_FLAG_ARLOST Arbitration lost flag (Master mode)
- I2C_FLAG_BUSERR Bus error flag
- I2C_FLAG_TXDATE Data register empty flag (Transmitter)
- I2C_FLAG_RXDATNE Data register not empty (Receiver) flag
- I2C_FLAG_STOPF Stop detection flag (Slave mode)
- I2C_FLAG_ADDR10F 10-bit header sent flag (Master mode)
- I2C_FLAG_BYTEF Byte transfer finished flag
- I2C_FLAG_ADDRF Address sent flag (Master mode)
Address matched flag (Slave mode)
- I2C_FLAG_STARTBF Start bit flag (Master mode)
return SET or RESET
note When this function is called, the status register is accessed, so other flags may be cleared.
You can call I2C_GetFlag() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Clears the I2Cx’s flags.
name I2C_ClrFlag.
fun Clears the I2Cx's flags.
param I2Cx :
- I2C1
- I2C2
param I2C_flag :
- I2C_FLAG_SMBALERT SMBus Alert flag
- I2C_FLAG_TIMEOUT Timeout
- I2C_FLAG_PECERR PEC error in reception flag
- I2C_FLAG_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_FLAG_ACKFAIL Acknowledge failure flag
- I2C_FLAG_ARLOST Arbitration lost flag (Master mode)
- I2C_FLAG_BUSERR Bus error flag
note Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
The other flag bits are cleared in the following sequence:
- STOPF (STOP detection) is cleared by software sequence: a read operation
to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
to re-enable the I2C peripheral).
- ADDR10F (10-bit header sent) is cleared by software sequence: a read
operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
- BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
- ADDRF (Address sent) is cleared by software sequence: a read operation to
I2C_STS1 register followed by a read operation to I2C_STS2 register.
- STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
register followed by a write operation to I2C_DAT register.
return none
INTStatus I2C_GetIntStatus(I2C_Module* I2Cx, uint32_t Interrupt)Checks whether the specified I2C interrupt has occurred or not.
name I2C_GetIntStatus.
fun Checks whether the specified I2C interrupt has occurred or not.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Interrupt (The input parameters must be the following values):
- I2C_INT_SMBALERT SMBus Alert flag
- I2C_INT_TIMEOUT Timeout
- I2C_INT_PECERR PEC error in reception flag
- I2C_INT_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_INT_ACKFAIL Acknowledge failure flag
- I2C_INT_ARLOST Arbitration lost flag (Master mode)
- I2C_INT_BUSERR Bus error flag
- I2C_INT_TXDATE Data register empty flag (Transmitter)
- I2C_INT_RXDATNE Data register not empty (Receiver) flag
- I2C_INT_STOPF Stop detection flag (Slave mode)
- I2C_INT_ADDR10F 10-bit header sent flag (Master mode)
- I2C_INT_BYTEF Byte transfer finished flag
- I2C_INT_ADDRF Address sent flag (Master mode)
Address matched flag (Slave mode)
- I2C_INT_STARTBF Start bit flag (Master mode)
return SET or RESET
note You can call I2C_GetIntStatus() in I2Cx_ER_IRQHandler() to determine which error occurred.
void I2C_ClrIntPendingBit(I2C_Module* I2Cx, uint32_t Interrupt)Clears the I2Cx’s interrupt statu bits.
name I2C_ClrIntPendingBit.
fun Clears the I2Cx's interrupt statu bits.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param Interrupt (The input parameters must be the following values):
- I2C_INT_SMBALERT SMBus Alert flag
- I2C_INT_TIMEOUT Timeout
- I2C_INT_PECERR PEC error in reception flag
- I2C_INT_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_INT_ACKFAIL Acknowledge failure flag
- I2C_INT_ARLOST Arbitration lost flag (Master mode)
- I2C_INT_BUSERR Bus error flag
return none
note Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
The other flag bits are cleared in the following sequence:
- STOPF (STOP detection) is cleared by software sequence: a read operation
to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
to re-enable the I2C peripheral).
- ADDR10F (10-bit header sent) is cleared by software sequence: a read
operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
- BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
- ADDRF (Address sent) is cleared by software sequence: a read operation to
I2C_STS1 register followed by a read operation to I2C_STS2 register.
- STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
register followed by a write operation to I2C_DAT register.
void I2C_SetTimeoutLowLimit(I2C_Module* I2Cx, uint32_t limit)timeout low threshold selection.
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.
name I2C_SetTimeoutHighLimit.
fun timeout high threshold selection.
param I2Cx :
- I2C1
- I2C2
param limit:
- I2C_TIMEOUT_HIGHLIMIT_256US
- I2C_TIMEOUT_HIGHLIMIT_512US
- I2C_TIMEOUT_HIGHLIMIT_1MS
- I2C_TIMEOUT_HIGHLIMIT_128US
return none
void I2C_ConfigTimeoutInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables the specified I2C timeout interrupts.
name I2C_ConfigTimeoutInt.
fun Enables the specified I2C timeout interrupts.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param I2C_IT (The input parameters must be the following values):
- I2C_TIMEOUT_INT_SCLLOW SCL low timeout error interrupt
- I2C_TIMEOUT_INT_SCLHIGH SCL high timeout error interrupt
- I2C_TIMEOUT_INT_SDALOW SDA low timeout error interrupt
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_EnableTimeoutDetect(I2C_Module* I2Cx, uint32_t timeout_type, FunctionalState Cmd)Enables the specified I2C timeout detection function .
name I2C_EnableTimeout.
fun Enables the specified I2C timeout detection function .
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param timeout_type (The input parameters must be the following values):
- I2C_TIMEOUT_LOW low timeout function enable
- I2C_TIMEOUT_HIGH high timeout function enable
param Cmd:
- ENABLE
- DISABLE
note This function can be disabled in I2C mode when an timeout error is not required.
return none
Use note: The comment names the function I2C_EnableTimeout., which differs from the function name; line 529 of the header declares it as I2C_EnableTimeoutDetect.
FlagStatus I2C_GetTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Checks whether the specified I2C timeout flag is set or not.
name I2C_GetTimeoutFlag.
fun Checks whether the specified I2C timeout flag is set or not.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param I2C_flag (The input parameters must be the following values):
- I2C_TIMEOUT_FLAG_SCLLOW SCL low timeout flag
- I2C_TIMEOUT_FLAG_SCLHIGH SCL high timeout flag
- I2C_TIMEOUT_FLAG_SDALOW SDA low timeout flag
return SET or RESET
void I2C_ClrTimeoutFlag(I2C_Module* I2Cx, uint32_t I2C_flag)Clears the I2Cx’s pending flags.
name I2C_ClrTimeoutFlag.
fun Clears the I2Cx's pending flags.
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param I2C_flag (The input parameters must be the following values):
- I2C_TIMEOUT_FLAG_SCLLOW SCL low timeout flag
- I2C_TIMEOUT_FLAG_SCLHIGH SCL high timeout flag
- I2C_TIMEOUT_FLAG_SDALOW SDA low timeout flag
return none
5.10.3 Demo descriptions
This section describes the 7 I2C examples: EEPROM reads and writes a serial EEPROM over I2C and compares the data, I2C_Master shows polled master transfers, I2C_Master_Int completes master transfers with event and error interrupts, I2C_Master_Dualaddr transfers with two slave addresses, I2C_Slave shows polled slave transfers, I2C_Slave_Int completes slave transfers with interrupts, and I2C_Slave_Dualaddr answers two own addresses.
5.10.3.1 EEPROM read and write of a serial EEPROM
Example directory: projects/n32g033_EVAL/examples/I2C/EEPROM/
Enable the GPIO and I2C clocks→Configure SCL and SDA as open-drain with internal pull-up→Initialize and enable the I2C master→Write an ascending block page by page→Read the data back and compare byte by byte
| Item | Source analysis |
|---|---|
| Function | Write an ascending data block to the on-board serial EEPROM, read it back, compare it byte by byte, and store the test status. |
| Source files | src/main.c runs the write, read-back, and compare flow;src/i2c_eeprom.c implements EEPROM initialization, page write, and read-back;inc/i2c_eeprom.h defines the slave address, page size, and timeout parameters;src/n32g033_it.c holds the interrupt entry;readme.txt gives the configuration and test steps. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain with internal pull-up; Speed: 400 K; Addressing: 7-bit, slave address 0xA0; Page size: 8 bytes; Timeout detection: low timeout disabled. |
| Program and data path | main -> I2C_EE_Init sets up the pins and the I2C master -> I2C_EE_WriteBuffer writes page by page -> I2C_EE_ReadBuffer reads back byte by byte -> Buffercmp compares and prints the result. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_GenerateStop, I2C_EnableTimeoutDetect |
| Verification | After compiling and resetting, a matching serial log means PASSED; you can also watch the two buffers in debug mode. |
| Use note | SCL is on PB6 and SDA on PB7, and external pull-ups are required; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example initializes the pins and the I2C master, then writes page by page, reads back byte by byte, and compares the two buffers.
Data path: main prepares the ascending data → I2C_EE_Init configures the pins and the I2C master → I2C_EE_WriteBuffer splits pages and sends them through I2C_EE_PageWrite → I2C_EE_WaitEepromStandbyState waits for the internal write to finish → I2C_EE_ReadBuffer fetches the data → Buffercmp compares
1. Configure SCL and SDA as open-drain with the internal pull-up, then load the I2C master parameters.
I2Cx_scl_clk_en();
I2Cx_sda_clk_en();
GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
GPIO_InitStruct(&GPIO_InitStructure);
GPIO_InitStructure.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_OD;
GPIO_InitStructure.GPIO_Alternate = GPIO_AF6;
GPIO_InitStructure.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitPeripheral(GPIOx, &GPIO_InitStructure);
/** I2C periphral configuration */
I2C_DeInit(I2Cx);
I2C_InitStruct(&I2C_InitStructure);
I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
I2C_InitStructure.OwnAddr1 = 0xff;
I2C_InitStructure.AckEnable = I2C_ACKEN;
I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
I2C_InitStructure.ClkSpeed = I2C_Speed;
I2C_Init(I2Cx, &I2C_InitStructure);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
If you change the pins, update the pin macros and the alternate function in the header, and keep the external pull-ups on the bus.
2. Page write: send the start and the device address, then the page word address, then the data bytes, waiting for each transmission to finish.
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, WriteAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** While there is data to be written */
while (NumByteToWrite--)
{
/** Send the current byte */
I2C_SendData(I2Cx, *pBuffer);
/** Point to the next byte to be written */
pBuffer++;
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
}
/** Send STOP condition */
I2C_GenerateStop(I2Cx, ENABLE);
I2C_EE_WaitEepromStandbyState();
I2C_EE_WriteOnePageCompleted();
The page size comes from the EEPROM type; the upper layer splits cross-page writes, so a new device only needs a page-size change.
3. Read-back: send the device address and the word address in the write direction, then restart and address the device in the read direction.
I2C_ConfigPecLocation(I2Cx, I2C_PEC_POS_CURRENT);
I2C_ConfigAck(I2Cx, ENABLE);
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Clear EV6 by setting again the PE bit */
I2C_Enable(I2Cx, ENABLE);
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, ReadAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Send STRAT condition a second time */
I2C_GenerateStart(I2Cx, ENABLE);
Keep the PEC position and acknowledge settings consistent with the tail-byte handling, and check them again when you change the length.
4. After a write, poll the internal status by re-addressing the device until the slave acknowledges again.
/** Keep looping till the slave acknowledge his address or maximum number
of trials is reached (this number is defined by sEE_MAX_TRIALS_NUMBER) */
while (1)
{
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
return;
}
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Wait for ADDR flag to be set (Slave acknowledged his address) */
sEETimeout = sEE_LONG_TIMEOUT;
do
{
/** Get the current value of the STS1 register */
tmpSR1 = I2Cx->STS1;
/** Update the timeout value and exit if it reach 0 */
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Keep looping till the Address is acknowledged or the AF flag is
set (address not acknowledged at time) */
while ((tmpSR1 & (I2C_STS1_ADDRF | I2C_STS1_ACKFAIL)) == 0);
/** Check if the ADDR flag has been set */
if (tmpSR1 & I2C_STS1_ADDRF)
{
/** Clear ADDR Flag by reading STS1 then STS2 registers (STS1 have already
been read) */
(void)I2Cx->STS2;
/** STOP condition */
I2C_GenerateStop(I2Cx, ENABLE);
/** Exit the function */
return;
}
else
{
/** Clear AF flag */
I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
}
/** Check if the maximum allowed numbe of trials has bee reached */
if (sEETrials++ == sEE_MAX_TRIALS_NUMBER)
{
/** If the maximum number of trials has been reached, exit the function */
sEE_TIMEOUT_UserCallback();
return;
}
}
The retry count is capped by a macro; raise it for slower devices so a valid write is not reported as a failure.
Corresponding source files: src/main.c, src/i2c_eeprom.c, inc/i2c_eeprom.h, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.2 I2C_Master polled master transfer
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master/
Enable the GPIO and I2C clocks→Configure SCL and SDA as open-drain→Initialize the master and set the slave address→Send a data block under polling→Receive under polling and compare
| Item | Source analysis |
|---|---|
| Function | Act as a master in polled mode, send a data block to a slave, read it back, and compare byte by byte; a timeout triggers the recovery callback. |
| Source files | src/main.c holds the master initialization, send, receive, and timeout callback;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the test buffers and status;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, master address 0x30 and slave address 0x10; Acknowledge: enabled for receive; Recovery: software reset. |
| Program and data path | main -> i2c_master_init sets up the pins and the master -> i2c_master_send sends under polling -> i2c_master_recv reads back under polling -> Buffercmp compares. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_ConfigAck, I2C_ConfigNackLocation, I2C_EnableSoftwareReset |
| Verification | After compiling and resetting, a matching serial log means PASSED; two boards or two I2C instances as master and slave also show the transfer. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example advances the event flags step by step under polling to complete master send, receive, and error recovery.
Data path: main prepares the data → i2c_master_init configures the pins and the master → i2c_master_send issues start, address, data, and stop → i2c_master_recv reads the bytes back → Buffercmp compares
1. Master initialization: enable the clocks, set the pins, then write the speed, address, and acknowledge options.
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
GPIO_InitStruct(&i2c1_gpio);
/*PB10 -- SCL; PB11 -- SDA*/
i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2c1_gpio.GPIO_Mode = GPIO_MODE_AF_OD;
i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
i2c1_gpio.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitPeripheral(GPIOx, &i2c1_gpio);
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2c1_master);
i2c1_master.BusMode = I2C_BUSMODE_I2C;
i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2; //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
i2c1_master.OwnAddr1 = I2C_MASTER_ADDR;
i2c1_master.AckEnable = I2C_ACKEN;
i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_master.ClkSpeed = 100000;
I2C_Init(I2Cx, &i2c1_master);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
I2C_Enable(I2Cx, ENABLE);
Match the speed and duty cycle to the slave, and keep the duty-cycle setting within the manual requirement when you change the speed.
2. Send flow: wait on the matching event for the start, the device address, the data bytes, and the stop.
I2CTimeout = I2CT_LONG_TIMEOUT;
while (I2C_GetFlag(I2C1, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
return 1;
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_MODE);
return 1;
}
}
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG)) // EV6
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_TXMODE);
return 1;
}
}
Comm_Flag = C_READY;
// send data
while (len-- > 0)
{
I2C_SendData(I2C1, *sendBufferPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDING)) // EV8
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDING);
return 1;
}
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_DATA_SENDED)) // EV8-2
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_SENDED);
return 1;
}
}
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
Every step carries a timeout counter; raise the limit when you move to a slower bus.
3. Receive flow: branch on the length, and for the last byte set the NACK position and then disable the acknowledge.
if (len == 1)
{
I2C_ConfigAck(I2C1, DISABLE);
(void)(I2C1->STS1); /// clear ADDR
(void)(I2C1->STS2);
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_GetFlag(I2C1, I2C_FLAG_RXDATNE))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_RECVD);
return 1;
}
}
*recvBufferPtr++ = I2C_RecvData(I2C1);
len--;
}
else if (len == 2)
{
I2C_ConfigNackLocation(I2C1, I2C_NACK_POS_NEXT);
(void)(I2C1->STS1);
(void)(I2C1->STS2);
I2C_ConfigAck(I2C1, DISABLE);
Disable the acknowledge and prepare the stop before the final byte; reversing the order reads one extra byte.
4. When the bus hangs, recover it with a software reset and re-initialize.
void IIC_SWReset(void)
{
GPIO_InitType i2cx_gpio;
GPIO_InitStruct(&i2cx_gpio);
i2cx_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2cx_gpio.GPIO_Mode = GPIO_MODE_INPUT;
GPIO_InitPeripheral(GPIOx, &i2cx_gpio);
I2CTimeout = I2CT_LONG_TIMEOUT;
for (;;)
{
if ((I2Cx_SCL_PIN | I2Cx_SDA_PIN) == (GPIOx->PID & (I2Cx_SCL_PIN | I2Cx_SDA_PIN)))
{
I2C_EnableSoftwareReset(I2Cx,ENABLE);
__NOP();
__NOP();
__NOP();
__NOP();
__NOP();
I2C_EnableSoftwareReset(I2Cx,DISABLE);
log_info("***** IIC module self reset! *****\r\n");
break;
}
else
{
if ((I2CTimeout--) == 0)
{
IIC_RCCReset();
}
}
}
}
Before a software reset, make sure the pins are released and the bus is idle, then rebuild the I2C configuration.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.3 I2C_Master_Int interrupt-driven master transfer
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master_Int/
Enable the GPIO and I2C clocks→Configure the pins, master parameters, and interrupt priority→Enable the event and error interrupts→Advance the transfer event by event in the interrupt→Wait for the finish flag in the main loop and compare
| Item | Source analysis |
|---|---|
| Function | Act as an interrupt-driven master for send and receive, leaving the main loop to wait on a finish flag. |
| Source files | src/main.c holds the initialization, the interrupt service routine, and the main loop;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the buffers, flags, and status;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, master address 0x30 and slave address 0x10; Interrupts: event and error interrupts enabled, event priority 2 and error priority 3. |
| Program and data path | main -> i2c_master_init configures the master and enables the interrupts -> I2C1_EV_IRQHandler advances the transfer event by event -> the main loop waits on the finish flag -> Buffercmp compares. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigInt, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ConfigAck, I2C_ClrFlag |
| Verification | After compiling and resetting, a matching serial log means PASSED; you can watch the event value inside the interrupt to confirm the ordered progress. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example moves the transfer into the event interrupt, leaving the main loop to start it and wait for the result.
Data path: main starts the condition → I2C1_EV_IRQHandler reads the latest event → it sends the address, sends data, or reads data per event → the end sets the finish flag → the main loop waits for the flag and calls Buffercmp
1. Master initialization: set the pins and master parameters, then enable the event, buffer, and error interrupts.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2c1_master);
i2c1_master.BusMode = I2C_BUSMODE_I2C;
i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2c1_master.OwnAddr1 = I2C_MASTER_ADDR;
i2c1_master.AckEnable = I2C_ACKEN;
i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_master.ClkSpeed = 100000; // 100000 100K
I2C_Init(I2Cx, &i2c1_master);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
// int enable
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_ConfigurationMaster(1);
I2C_Enable(I2Cx, ENABLE);
return 0;
Enable the interrupts after the parameters are set, so the ISR does not run before the bus is configured.
2. Interrupt priority: the event and error lines are configured separately.
void NVIC_ConfigurationMaster(uint8_t ch)
{
NVIC_InitType NVIC_InitStructure;
if (ch == 1)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
}
if (ch == 2)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
}
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
if (ch == 1)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
}
if (ch == 2)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn;
}
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
Order the two priorities against the other system interrupts so a high-priority interrupt does not hold the bus too long.
3. Event interrupt: read the latest event and branch to send the address, send data, or read data.
void I2C1_EV_IRQHandler(void)
{
uint32_t last_event = 0;
last_event = I2C_GetLastEvent(I2C1);
if ((last_event & I2C_ROLE_MASTER) == I2C_ROLE_MASTER) // master mode
{
switch (last_event)
{
case I2C_EVT_MASTER_MODE_FLAG: // EV5 Send addr
if(flag_trans_direct) // read
{
Memset(rx_buf, 0, TEST_BUFFER_SIZE); // clear recv buf, ready to recv data
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_RECV);
rx_num = 0;
}
else // write
{
I2C_SendAddr7bit(I2C1, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
tx_num = 0;
}
break;
// MasterTransmitter
case I2C_EVT_MASTER_TXMODE_FLAG: // EV6 Send first data
Comm_Flag = C_READY;
I2C_SendData(I2C1, tx_buf[tx_num++]);
break;
case I2C_EVT_MASTER_DATA_SENDING: // EV8 Sending data
if (tx_num < TEST_BUFFER_SIZE)
{
I2C_SendData(I2C1, tx_buf[tx_num++]);
}
break;
case I2C_EVT_MASTER_DATA_SENDED: // EV8_2 Send finish
if (tx_num == TEST_BUFFER_SIZE)
{
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
flag_master_send_finish = 1;
}
}
break;
// MasterReceiver
case I2C_EVT_MASTER_RXMODE_FLAG: // EV6
Comm_Flag = C_READY;
if (TEST_BUFFER_SIZE == 1)
{
I2C_ConfigAck(I2C1, DISABLE);
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE);
}
}
else if (TEST_BUFFER_SIZE == 2)
{
I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
I2C_ConfigAck(I2C1, DISABLE);
}
break;
case I2C_EVT_MASTER_DATA_RECVD_FLAG: // EV7.one byte recved
case I2C_EVT_MASTER_DATA_RECVD_BSF_FLAG: // EV7.When the I2C communication rate is too high, BSF = 1
rx_buf[rx_num++] = I2C_RecvData(I2C1);
if (rx_num == (TEST_BUFFER_SIZE - 1))
{
I2C_ConfigAck(I2C1, DISABLE); // Disable I2C1 acknowledgement.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2C1, ENABLE); // Send I2C1 STOP Condition.
}
}
else if (rx_num == TEST_BUFFER_SIZE)
{
flag_master_recv_finish = 1;
}
break;
case 0x00030201: // Arbitration lost
case 0x00030101: // Acknowledge failure
case 0x00030501: // Acknowledge failure and Bus error
I2C_Enable(I2C1, DISABLE);
I2C_Enable(I2C1, ENABLE);
break;
default:
log_info("I2C error status:0x%x\r\n", last_event);
break;
}
}
}
Cover both the normal and error branches; the error branch resets the peripheral before retrying.
4. Error interrupt: on an acknowledge failure, clear the flag and issue the stop condition.
void I2C1_ER_IRQHandler(void)
{
if(I2C_GetFlag(I2C1, I2C_FLAG_ACKFAIL))
{
I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
I2C_GenerateStop(I2C1, ENABLE); // Send I2C1 STOP Condition.
}
}
Keep only the minimal recovery in the error interrupt and leave the retry logic to the upper layer.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.4 I2C_Master_Dualaddr master with two slave addresses
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Master_Dualaddr/
Enable the GPIO and I2C clocks→Configure the master parameters→Transfer and compare with the first address→Transfer and compare with the second address
| Item | Source analysis |
|---|---|
| Function | Act as a master that transfers to two different slave addresses in turn and compares each result. |
| Source files | src/main.c holds the master initialization, the address-parameterized send and receive, the main loop, and the recovery callback;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the two slave addresses, buffers, and status;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, master address 0x30 and slave addresses 0x10 and 0x20; Acknowledge: enabled for receive. |
| Program and data path | main -> i2c_master_init configures the master -> it first uses address 0x10 with i2c_master_send and i2c_master_recv -> then repeats with address 0x20 -> each pair is compared by Buffercmp. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_GenerateStart, I2C_GenerateStop, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_ConfigAck, I2C_ConfigNackLocation |
| Verification | After compiling and resetting, both addresses print a pass when the transfers match; you can watch the two compare results in debug mode. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example passes the slave address as a function parameter and reuses one transfer flow to reach two slaves in turn.
Data path: main fills the data → i2c_master_init configures the master → i2c_master_send(tx, len, 0x10) and i2c_master_recv(rx, len, 0x10) → Buffercmp compares → the buffer is cleared and the 0x20 cycle repeats
1. Master initialization matches the single-address example, with the same speed and addressing.
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
GPIO_InitStruct(&i2c1_gpio);
/*PB10 -- SCL; PB11 -- SDA*/
i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2c1_gpio.GPIO_Mode = GPIO_MODE_AF_OD;
i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
i2c1_gpio.GPIO_Pull = GPIO_PULL_UP;
GPIO_InitPeripheral(GPIOx, &i2c1_gpio);
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2c1_master);
i2c1_master.BusMode = I2C_BUSMODE_I2C;
i2c1_master.FmDutyCycle = I2C_FMDUTYCYCLE_2; //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
i2c1_master.OwnAddr1 = I2C_MASTER_ADDR;
i2c1_master.AckEnable = I2C_ACKEN;
i2c1_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_master.ClkSpeed = 100000;
I2C_Init(I2Cx, &i2c1_master);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
I2C_Enable(I2Cx, ENABLE);
return 0;
To support different peripheral speeds, you can make the speed an initialization parameter too.
2. The send path writes the address into the device-address byte; the rest of the flow is unchanged.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_MODE);
return 1;
}
}
I2C_SendAddr7bit(I2C1, addr, I2C_DIRECTION_SEND);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_TXMODE_FLAG)) // EV6
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_TXMODE);
return 1;
}
}
Comm_Flag = C_READY;
The address is caller-supplied, so porting only needs the target address replaced at the call site.
3. Receive addresses the device with the passed-in value and keeps the generic tail-byte handling.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2C1, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_MODE_FLAG)) // EV5
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_MODE);
return 1;
}
}
// send addr
I2C_SendAddr7bit(I2C1, addr, I2C_DIRECTION_RECV);
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_MASTER_RXMODE_FLAG)) // EV6
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_RXMODE);
return 1;
}
}
Comm_Flag = C_READY;
if (len == 1)
{
I2C_ConfigAck(I2C1, DISABLE);
(void)(I2C1->STS1); /// clear ADDR
(void)(I2C1->STS2);
Between the two receives, clear the buffer and reset the index so the previous round does not affect the compare.
4. The main loop runs one send, receive, and compare cycle per address.
i2c_master_init();
/* Fill the buffer to send */
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
tx_buf[i] = i;
}
/* First write in the memory followed by a read of the written data --------*/
/* Write data*/
i2c_master_send(tx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_1);
/* Read data */
i2c_master_recv(rx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_1);
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
if (test_status == PASSED)
{
log_info("write and read data the same, i2c master addr1 test pass\r\n");
}
else
{
log_info("write and read data are different, i2c master addr1 test fail\r\n");
}
/* test_status = PASSED, if the write and read data are the same */
/* test_status = FAILED, if the write and read data are different */
/* delay */
for (i = 0; i < 5000; i++);
/* Empty the rx buffer */
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
rx_buf[i] = 0;
}
/* First write in the memory followed by a read of the written data --------*/
/* Write data*/
i2c_master_send(tx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_2);
/* Read data */
i2c_master_recv(rx_buf, TEST_BUFFER_SIZE, I2C_SLAVE_ADDR_2);
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
Leave a delay between the two rounds so the slave has time to finish before the next round.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.5 I2C_Slave polled slave transfer
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave/
Enable the GPIO and I2C clocks→Configure the own address and slave parameters→Wait for the address match and receive under polling→Send the received bytes back after reception→Loop the transfer
| Item | Source analysis |
|---|---|
| Function | Act as a slave that polls for the master, receives a data block, sends it back unchanged, and repeats. |
| Source files | src/main.c holds the slave initialization, send, receive, main loop, and recovery callback;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the own address, buffers, and status;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, own address 0x10; Acknowledge: enabled. |
| Program and data path | main -> i2c_slave_init configures the slave -> i2c_slave_recv waits for the address match and receives -> i2c_slave_send sends the bytes back -> the cycle repeats. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_CheckEvent, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ClrFlag |
| Verification | After compiling and resetting, the slave follows the master’s pace when paired with the master example; you can watch in debug mode whether the received and sent data match. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example polls as a slave until its address matches, then sends the received data back unchanged.
Data path: main loops → i2c_slave_recv waits for the address match and receives byte by byte → i2c_slave_send sends the bytes back → the buffer is cleared for the next round
1. Slave initialization: write the own address into the parameters and enable the acknowledge.
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
GPIO_InitStruct(&i2c1_gpio);
/*PB10 -- SCL; PB11 -- SDA*/
i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2c1_gpio.GPIO_Mode = GPIO_MODE_AF_OD;
i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
GPIO_InitPeripheral(GPIOx, &i2c1_gpio);
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2c1_slave);
i2c1_slave.BusMode = I2C_BUSMODE_I2C;
i2c1_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2; //if the spped greater than 400KHz, the FmDutyCycle mast be configured to I2C_FMDUTYCYCLE_2
i2c1_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2c1_slave.AckEnable = I2C_ACKEN;
i2c1_slave.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_slave.ClkSpeed = 100000;
I2C_Init(I2Cx, &i2c1_slave);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
I2C_Enable(I2Cx, ENABLE);
Keep the own address out of the reserved range and consistent with the master when you change it.
2. Send flow: after the address matches, send byte by byte and finish on a stop or a missing acknowledge.
int i2c_slave_send(uint8_t* data, int len)
{
uint32_t lastevent;
uint8_t* sendBufferPtr = data;
I2CTimeout = I2CT_LONG_TIMEOUT*10;
while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR_MATCHED))/* send addr matched */
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_MODE);
return 1;
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
I2C_Enable(I2C1, ENABLE);
I2C_SendData(I2C1, *sendBufferPtr++);
while (1)
{
lastevent = I2C_GetLastEvent(I2C1);
if (lastevent == I2C_EVT_SLAVE_DATA_SENDED)
{
I2C_SendData(I2C1, *sendBufferPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
}
else if ((lastevent == I2C_EVT_SLAVE_ACK_MISS) || (lastevent == I2C_EVT_SLAVE_STOP_RECVD))
{
I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
I2C_Enable(I2C1, ENABLE);
break;
}
else if ((lastevent & 0x00000400) == 0x00000400)
{
CommTimeOut_CallBack(SLAVE_BUSERR);
return 1;
}
else
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_UNKNOW);
return 1;
}
}
}
The slave cannot control when the stop comes, so the length must match what the master expects.
3. Receive flow: after the address matches, receive byte by byte and finish on the stop condition.
int i2c_slave_recv(uint8_t* data, uint32_t rcv_len)
{
uint32_t lastevent;
rxDataNum = 0;
I2CTimeout = I2CT_LONG_TIMEOUT*10;
while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_RECV_ADDR_MATCHED))/* receive addr matched */
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_MODE);
return 1;
}
}
I2CTimeout = I2CT_LONG_TIMEOUT;
I2C_Enable(I2C1, ENABLE);
while (1)
{
lastevent = I2C_GetLastEvent(I2C1);
if (lastevent == I2C_EVT_SLAVE_DATA_RECVD)
{
*data++ = I2C_RecvData(I2C1);
rxDataNum++;
I2CTimeout = I2CT_LONG_TIMEOUT;
}
else if (lastevent == I2C_EVT_SLAVE_STOP_RECVD)
{
I2C_Enable(I2C1, ENABLE);
break;
}
else if ((lastevent & 0x00000400) == 0x00000400)
{
CommTimeOut_CallBack(SLAVE_BUSERR);
return 1;
}
else
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_UNKNOW);
return 1;
}
}
}
return 0;
Match the receive count to the sender; change the length on both sides together.
4. The main loop repeats the receive and send, clearing the buffer after each round.
int main(void)
{
uint16_t i = 0;
log_init();
log_info("this is a i2c slave test demo\r\n");
/* Initialize the I2C EEPROM driver ----------------------------------------*/
i2c_slave_init();
while (1)
{
for(; ;)
{
/* Read data */
if(i2c_slave_recv(data_buf, TEST_BUFFER_SIZE))
{
break; /* receive fail, reaccept*/
}
/* Write data*/
if(i2c_slave_send(data_buf, TEST_BUFFER_SIZE))
{
break; /* send fail, reaccept*/
}
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
data_buf[i] = 0;
}
}
}
}
The failure branch loops back to retry; add back-off and logging as needed when you port it.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.6 I2C_Slave_Int interrupt-driven slave transfer
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Int/
Enable the GPIO and I2C clocks→Configure the slave parameters, interrupts, and priority→Handle address and data events inside the event interrupt→Wait for the finish flag in the main loop
| Item | Source analysis |
|---|---|
| Function | Act as an interrupt-driven slave for receive and send, leaving the main loop to wait on a finish flag. |
| Source files | src/main.c holds the slave initialization, the interrupt service routine, and the main loop;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the own address, buffers, and flags;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, own address 0x10; Interrupts: event and error interrupts enabled, event priority 2 and error priority 3. |
| Program and data path | main -> i2c_slave_init configures the slave and enables the interrupts -> I2C1_EV_IRQHandler transfers per event and sets the finish flag -> the main loop waits on the flag. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigInt, I2C_GetLastEvent, I2C_RecvData, I2C_SendData, I2C_ClrFlag |
| Verification | After compiling and resetting, the serial log prints the receive and send lengths when paired with the master example; you can watch the event value inside the interrupt to confirm the ordered progress. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example moves the slave transfer into the event interrupt, leaving the main loop to wait on the receive and send flags.
Data path: I2C1_EV_IRQHandler reads the latest event → it prepares the transfer on an address match → it moves data through the data register on a data event → a stop event sets the finish flag → the main loop waits for the flag and prints the length
1. Slave interrupt initialization: after the parameters are set, enable the event, buffer, and error interrupts.
int i2c_slave_init(void)
{
I2C_InitType i2c1_slave;
GPIO_InitType i2c1_gpio;
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_I2C1, ENABLE);
RCC_EnableAPBPeriphClk(RCC_APB_PERIPH_GPIO, ENABLE);
GPIOx->POD |= (I2Cx_SCL_PIN | I2Cx_SDA_PIN);//pull up pin
GPIO_InitStruct(&i2c1_gpio);
/*PB10 -- SCL; PB11 -- SDA*/
i2c1_gpio.Pin = I2Cx_SCL_PIN | I2Cx_SDA_PIN;
i2c1_gpio.GPIO_Mode = GPIO_MODE_AF_OD;
i2c1_gpio.GPIO_Alternate = GPIO_AF_I2C;
GPIO_InitPeripheral(GPIOx, &i2c1_gpio);
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2c1_slave);
i2c1_slave.BusMode = I2C_BUSMODE_I2C;
i2c1_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2c1_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2c1_slave.AckEnable = I2C_ACKEN;
i2c1_slave.AddrMode = I2C_ADDR_MODE_7BIT;
i2c1_slave.ClkSpeed = 100000; // 100000 100K
I2C_Init(I2Cx, &i2c1_slave);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
// int enable
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_ConfigurationSlave((uint8_t)1);
I2C_Enable(I2Cx, ENABLE);
return 0;
Enable the interrupts after the parameters are set, so they do not fire before the bus is ready.
2. Interrupt priority: the event and error lines are configured separately.
void NVIC_ConfigurationSlave(uint8_t ch)
{
NVIC_InitType NVIC_InitStructure;
if (ch == 1)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
}
if (ch == 2)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
}
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
if (ch == 1)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
}
if (ch == 2)
{
NVIC_InitStructure.NVIC_IRQChannel = I2C2_ER_IRQn;
}
NVIC_InitStructure.NVIC_IRQChannelPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
Order the priorities against the other system interrupts so the preemption stays correct.
3. Event interrupt: distinguish the receive and send address matches, then move data on the data events.
void I2C1_EV_IRQHandler(void)
{
uint8_t timeout_flag = 0;
uint32_t last_event = 0;
last_event = I2C_GetLastEvent(I2C1);
if ((last_event & I2C_ROLE_MASTER) != I2C_ROLE_MASTER) // MSMODE = 0:I2C slave mode
{
switch (last_event)
{
case I2C_EVT_SLAVE_RECV_ADDR_MATCHED: //EV1 Rx addr matched
// clear flag,ready to receive data
rxDataNum = 0;
break;
case I2C_EVT_SLAVE_SEND_ADDR_MATCHED: //EV1 Tx addr matched
rxDataNum = 0;
I2C1->DAT = data_buf[rxDataNum++];// Send first data
break;
// SlaveTransmitter
case I2C_EVT_SLAVE_DATA_SENDING: //EV3 Sending data
break;
case I2C_EVT_SLAVE_DATA_SENDED:
I2C1->DAT = data_buf[rxDataNum++];
break;
// SlaveReceiver
case I2C_EVT_SLAVE_DATA_RECVD: //EV2 one byte recved
data_buf[rxDataNum++] = I2C1->DAT;
break;
case I2C_EVT_SLAVE_STOP_RECVD: // EV4
I2C_Enable(I2C1, ENABLE);
if(rxDataNum != 0)
{
flag_slave_recv_finish = 1; // The STOPF bit is not set after a NACK reception
}
break;
default:
I2C_Enable(I2C1, ENABLE);
timeout_flag = 1;
break;
}
}
if (timeout_flag)
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_UNKNOW);
}
}
else
{
I2CTimeout = I2CT_LONG_TIMEOUT;
}
In slave mode the handler must check the role and process only the slave events.
4. Error interrupt: on a missing acknowledge, clear the flag and decide the finish flag by whether this was the last byte.
void I2C1_ER_IRQHandler(void)
{
uint32_t last_event;
last_event = I2C_GetLastEvent(I2C1);
if(last_event == I2C_EVT_SLAVE_ACK_MISS)
{
I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
if(rxDataNum != 0) //slave send the last data and recv NACK
{
flag_slave_send_finish = 1;
}
else //not the last data recv nack, send fail
{
}
}
else if(last_event == 0x601D0 || last_event == 0x601C0)
{
CommTimeOut_CallBack(SLAVE_UNKNOW);
}
}
A missing acknowledge with a zero count means the send was not accepted and must be repeated.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.3.7 I2C_Slave_Dualaddr slave with two own addresses
Example directory: projects/n32g033_EVAL/examples/I2C/I2C_Slave_Dualaddr/
Enable the GPIO and I2C clocks→Configure own address 1 and enable dual addressing→Set own address 2→Receive and send back under each of the two addresses
| Item | Source analysis |
|---|---|
| Function | Act as a slave with two own addresses, each answering the master and transferring data. |
| Source files | src/main.c holds the slave initialization, the address-parameterized send and receive, the main loop, and the recovery callback;src/n32g033_it.c and inc/n32g033_it.h provide the interrupt entry;inc/main.h declares the two own addresses, buffers, and status;readme.txt gives the configuration and test steps;the project files are in MDK-ARM/ and EWARM/. |
| Key configuration | Pins: SCL on PB6 and SDA on PB7, open-drain; Speed: 100 K; Addressing: 7-bit, own addresses 0x10 and 0x20; Dual addressing: enabled. |
| Program and data path | main -> i2c_slave_init configures own address 1 and enables dual addressing -> address 2 is written through I2C_ConfigOwnAddr2 -> i2c_slave_recv and i2c_slave_send run for address 1 and address 2 in turn. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_Enable, I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_CheckEvent, I2C_RecvData, I2C_SendData, I2C_ClrFlag |
| Verification | After compiling and resetting, both addresses transfer when paired with the dual-address master example; you can watch the two addresses in debug mode. |
| Use note | SCL is on PB6 and SDA on PB7, external pull-ups are required, and the slave address must avoid 0xF0, 0xF2, 0xF4, and 0xF6; the development environment is KEIL MDK-ARM V5.34.0.0 or IAR EWARM 8.50.1 on the N32G033K8Q7-1_STB V1.0 evaluation board; the system clock is 64 MHz. |
Core-code walkthrough and porting
The example gives the slave two own addresses and reuses one transfer flow to answer either master address.
Data path: i2c_slave_init writes own address 1 and enables address 2 and dual addressing → i2c_slave_recv waits for the address match and receives → i2c_slave_send sends back for that address → the main loop runs one round per address
1. Slave initialization: write own address 1, then write address 2 and enable dual addressing.
I2C_Init(I2Cx, &i2c1_slave);
I2C_EnableTimeoutDetect(I2Cx, I2C_TIMEOUT_LOW, DISABLE);
I2C_ConfigOwnAddr2(I2C1,I2C_SLAVE_ADDR_2);
I2C_EnableDualAddr(I2C1,ENABLE);
I2C_Enable(I2Cx, ENABLE);
Address 1 goes through the init parameters while address 2 is written separately; neither may fall in the reserved range.
2. Send: pick the matching event from the passed-in address, then send byte by byte.
if(addr == 0)
{
while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR_MATCHED))/* send addr1 matched */
{
}
}
else
{
while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_SEND_ADDR2_MATCHED))/* send addr2 matched */
{
}
}
The two addresses map to different event values; re-check the matching event after an address change.
3. Receive: wait for the match of the passed-in address, then receive byte by byte and wait for the stop.
I2C_Enable(I2C1, ENABLE);
while (Numbytetoread != 0)
{
I2CTimeout = I2CT_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2C1, I2C_EVT_SLAVE_DATA_RECVD))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(SLAVE_MODE);
return 1;
}
}
*data++ = I2C_RecvData(I2C1);
Numbytetoread--;
}
Wait for the stop only after the count is used up, so no byte is dropped early.
4. The main loop runs one receive and send round for address 1 and address 2.
/* Read data */
i2c_slave_recv(data_buf, TEST_BUFFER_SIZE, 0);
/* Write data*/
i2c_slave_send(data_buf, TEST_BUFFER_SIZE, 0);
/* address 2 */
/* Read data */
i2c_slave_recv(data_buf, TEST_BUFFER_SIZE, 1);
/* Write data*/
i2c_slave_send(data_buf, TEST_BUFFER_SIZE, 1);
}
Keep the round order consistent with the master; making the address a parameter lets you reuse the flow.
Corresponding source files: src/main.c, src/n32g033_it.c, firmware/n32g033_std_periph_driver/inc/n32g033_i2c.h
5.10.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| SDA and SCL pins | The I2C chapter of the N32G033 user manual states that SDA and SCL are open-drain and need external pull-ups, with both lines high when the bus is idle; pick the pull-up value from the bus speed and the capacitive load. |
User manual |
| Clock stretching | The I2C chapter of the N32G033 user manual states that you must check whether the slave supports clock stretching; if NOEXTEND is cleared to allow stretching, a slave holding SCL low widens the bus clock; keep NOEXTEND asserted for slaves that do not support stretching. |
User manual |
| Software reset | The I2C chapter of the N32G033 user manual states that before setting SWRESET you must confirm the I2C pins are released and the bus is idle, or the reset may interrupt an ongoing transfer. | User manual |
| When the configuration registers are writable | The I2C chapter of the N32G033 user manual states that the clock and filter configuration registers are writable only while the EN bit of I2C_CTRL1 is 0;disable the I2C before changing a divider or a filter. |
User manual |
| Analog filters | The I2C chapter of the N32G033 user manual states that the SDA and SCL analog filters are enabled by default; disable them only by an explicit register write, and re-check the bus waveform afterwards. |
User manual |
| Last byte of a master receive | The I2C chapter of the N32G033 user manual states that before receiving the last one or two bytes the master must set the NACK position, disable the acknowledge, and then issue the stop condition. | User manual |
| STOP setup time in standard mode | Errata sheet V1.0.0 (2025.9.25) lists this issue for revision A silicon: in master mode at 100 K, once slave clock stretching is triggered the STOP setup time falls below 4 us; lower the speed to 50 K or below according to the slave timing requirement. |
Errata sheet |
Errata sheet V1.0.0 (2025.9.25) covers revision A silicon; its I2C interface section lists the STOP setup time limit in standard mode, as shown in the last row above.