5.16 I2C Guide
The I2C driver of the N32H49x standard peripheral library supports 7-bit and 10-bit addressing, general call, SMBus/PMBus, PEC, analog and digital filtering, FIFO, and timeout detection. This chapter covers the files of the driver, its 53 public functions, and the 12 examples EEPROM_Polling, EEPROM_Int, EEPROM_DMA, I2C_Master, I2C_Slave, I2C_Master_Int, I2C_Slave_Int, I2C_Master_FIFO, I2C_Slave_FIFO, I2C_10bit, PMBUS_Master, and PMBUS_Slave. For bus timing, clock synchronization and arbitration, error conditions, and register bit definitions, read it together with the “I2C” chapter of the user manual.
5.16.1 Related files
| Relative path | Purpose |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_i2c.h |
Defines the I2C_InitType structure and the macros for bus mode, duty cycle, addressing mode, acknowledge, PEC, interrupts, DMA, FIFO, filtering, and timeout;declares the 53 public functions grouped as reset and initialization, start/stop conditions and addressing, interrupts and DMA, data transfer, PEC and filtering, FIFO, and timeout and status query |
firmware/n32h49x_std_periph_driver/src/n32h49x_i2c.c |
Implements the 53 functions: register reset and initialization, start/stop conditions and address transmission, interrupt and DMA enabling, data and FIFO transfer, PEC and filter configuration, and timeout thresholds and status query |
projects/n32h49x_EVAL/examples/I2C/ |
I2C example root, containing the 12 examples EEPROM_Polling, EEPROM_Int, EEPROM_DMA, I2C_Master, I2C_Slave, I2C_Master_Int, I2C_Slave_Int, I2C_Master_FIFO, I2C_Slave_FIFO, I2C_10bit, PMBUS_Master, and PMBUS_Slave |
projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/ |
Project layout of that example: inc/, src/, readme.txt, MDK-ARM/, EWARM/ |
projects/n32h49x_EVAL/bsp/ |
Board support files shared by the examples: evaluation-board LED and key definitions, a millisecond delay function, and the log print interface |
5.16.2 API reference
The 53 public functions below follow the declaration order of n32h49x_i2c.h and fall into 15 groups; each entry gives a summary and the original comment directly above the function in n32h49x_i2c.c.
Initialization
I2C_DeInit, I2C_Init, I2C_InitStruct, I2C_Enable
Start/stop and acknowledge
I2C_GenerateStart, I2C_GenerateStop, I2C_ConfigAck, I2C_SendAddr7bit
Own address and general call
I2C_ConfigOwnAddr2, I2C_EnableDualAddr, I2C_EnableGeneralCall
Interrupts, DMA, and software reset
I2C_ConfigInt, I2C_EnableDMA, I2C_EnableDmaLastSend, I2C_EnableSoftwareReset
Data transfer
I2C_SendData, I2C_RecvData
NACK/PEC position and SMBus alert
I2C_ConfigNackLocation, I2C_ConfigPecLocation, I2C_ConfigSmbusAlert
PEC and ARP
I2C_SendPEC, I2C_ComputePec, I2C_GetPec, I2C_EnableArp
Clock stretching and fast mode
I2C_EnableExtendClk, I2C_ConfigFastModeDutyCycle
Digital and analog filtering
I2C_SetSDADigitalFilterWidth, I2C_SetSCLDigitalFilterWidth, I2C_SetSDAAnalogFilterWidth, I2C_EnableSDAAnalogFilter, I2C_SetSCLAnalogFilterWidth, I2C_EnableSCLAnalogFilter
Flag and interrupt clearing
I2C_ClrFlag, I2C_ClrIntPendingBit
FIFO management
I2C_ClrFIFO, I2C_EnableFIFO, I2C_EnableDMAFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold
Byte count control
I2C_EnableByteNum, I2C_SetMasterReceivedDataBytesNum, I2C_SetByteNumLastStartStop
FIFO data transfer
I2C_SendFIFOData, I2C_RecvFIFOData
Timeout detection
I2C_SetTimeoutLowLimit, I2C_SetTimeoutHighLimit, I2C_EnableTimeoutDetect
Status query
I2C_GetRegister, I2C_GetIntStatus, I2C_CheckFifoEvent, I2C_CheckEvent, I2C_GetLastEvent, I2C_GetFlag
void I2C_DeInit(I2C_Module* I2Cx)Deinitializes the I2Cx peripheral registers to their default reset values.
name I2C_DeInit.
fun Deinitializes the I2Cx peripheral registers to their default reset values.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return none
void I2C_Init(I2C_Module* I2Cx,I2C_InitType* I2C_InitStruct)Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
name I2C_Init.
fun Initializes the I2Cx peripheral according to the specified parameters in the I2C_InitStruct.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_InitStruct :
- ClkSpeed(common speed:100000(100KHz) - 400000(400KHz) - 1000000(1MHz)):
- between 1 to 1000000
- BusMode:
- I2C_BUSMODE_I2C
- I2C_BUSMODE_SMBDEVICE
- I2C_BUSMODE_SMBHOST
- FmDutyCycle(If the ClkSpeed does not exceed 100KHz,duty is fixed at 1:1,
otherwise it can be configured as 2:1 or 16:9):
- I2C_FMDUTYCYCLE_16_9
- I2C_FMDUTYCYCLE_2
- OwnAddr1:
- between 0 to 0x3FF
- AckEnable:
- I2C_ACKEN
- I2C_ACKDIS
- AddrMode:
- I2C_ADDR_MODE_7BIT
- I2C_ADDR_MODE_10BIT
return none
void I2C_InitStruct(I2C_InitType* I2C_InitStruct)Fills each I2C_InitStruct member with its default value.
name I2C_InitStruct.
fun Fills each I2C_InitStruct member with its default value.
param I2C_InitStruct :
- ClkSpeed
- BusMode
- FmDutyCycle
- OwnAddr1
- AckEnable
- AddrMode
return none
void I2C_Enable(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C peripheral.
name I2C_Enable.
fun Enables or disables the specified I2C peripheral.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_GenerateStart(I2C_Module* I2Cx, FunctionalState Cmd)Generates I2Cx communication START condition.
name I2C_GenerateStart.
fun Generates I2Cx communication START condition.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_GenerateStop(I2C_Module* I2Cx, FunctionalState Cmd)Generates I2Cx communication STOP condition.
name I2C_GenerateStop.
fun Generates I2Cx communication STOP condition.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ConfigAck(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C acknowledge feature.
name I2C_ConfigAck.
fun Enables or disables the specified I2C acknowledge feature.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_SendAddr7bit(I2C_Module* I2Cx, uint8_t Address, uint8_t I2C_Direction)Transmits the address byte to select the slave device.
name I2C_SendAddr7bit.
fun Transmits the address byte to select the slave device.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Address
- 0 to 0xFF
param I2C_Direction :
- I2C_DIRECTION_SEND Transmitter mode
- I2C_DIRECTION_RECV Receiver mode
return none
void I2C_ConfigOwnAddr2(I2C_Module* I2Cx, uint8_t Address)Configures the specified I2C own address2.
name I2C_ConfigOwnAddr2.
fun Configures the specified I2C own address2.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Address
- 0 to 0xFF (7bit I2C own address2)
return none
void I2C_EnableDualAddr(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C dual addressing mode.
name I2C_EnableDualAddr.
fun Enables or disables the specified I2C dual addressing mode.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_EnableGeneralCall(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C general call feature.
name I2C_EnableGeneralCall.
fun Enables or disables the specified I2C general call feature.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ConfigInt(I2C_Module* I2Cx, uint32_t I2C_IT, FunctionalState Cmd)Enables or disables the specified I2C interrupts.
name I2C_ConfigInt.
fun Enables or disables the specified I2C interrupts.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_IT :
- I2C_INT_BUF buffer interrupt enable
- I2C_INT_EVENT event interrupt enable
- I2C_INT_ERR error full interrupt enable
- I2C_INT_FIFOFEN FIFO full interrupt enable
- I2C_INT_FIFOEEN FIFO empty interrupt enable
- I2C_INT_FIFOHFEN FIFO half full interrupt enable
- I2C_INT_FIFOHEEN FIFOHE half empty interrupt enable
- I2C_INT_FIFOUREN FIFO under-run interrupt enable
- I2C_INT_FIFOOREN FIFO over-run interrupt enable
- I2C_INT_DMAETOERREN DMA ETO error interrupt enable
- I2C_INT_SDALTOEN SDA low timeout interrupt enable
- I2C_INT_SCLHTOEN SCL high timeout interrupt enable
- I2C_INT_SCLLTOEN SCL low timeout interrupt enable
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_SendData(I2C_Module* I2Cx, uint8_t Data)Sends a data byte through the I2Cx peripheral.
name I2C_SendData.
fun Sends a data byte through the I2Cx peripheral.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Data: 0x00~0xFF
return none
uint8_t I2C_RecvData(I2C_Module* I2Cx)Returns the most recent received data by the I2Cx peripheral.
name I2C_RecvData.
fun Returns the most recent received data by the I2Cx peripheral.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return The value of the received data
void I2C_EnableDMA(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C DMA requests.
name I2C_EnableDMA.
fun Enables or disables the specified I2C DMA requests.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_EnableDmaLastSend(I2C_Module* I2Cx, FunctionalState Cmd)Specifies if the next DMA transfer will be the last one.
name I2C_EnableDmaLastSend.
fun Specifies if the next DMA transfer will be the last one.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_EnableSoftwareReset(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C software reset.
name I2C_EnableSoftwareReset.
fun Enables or disables the specified I2C software reset.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ConfigNackLocation(I2C_Module* I2Cx, uint32_t I2C_NACKPosition)Selects the specified I2C NACK position in master receiver mode.
name I2C_ConfigNackLocation.
fun Selects the specified I2C NACK position in master receiver mode.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_NACKPosition :
- I2C_NACK_POS_NEXT
- I2C_NACK_POS_CURRENT
return none
void I2C_ConfigPecLocation(I2C_Module* I2Cx, uint32_t I2C_PECPosition)Selects the specified I2C PEC position.
name I2C_ConfigPecLocation.
fun Selects the specified I2C PEC position.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_PECPosition :
- I2C_PEC_POS_NEXT
- I2C_PEC_POS_CURRENT
return none
void I2C_ConfigSmbusAlert(I2C_Module* I2Cx, uint32_t I2C_SMBusAlert)Drives the SMBusAlert pin high or low for the specified I2C.
name I2C_ConfigSmbusAlert.
fun Drives the SMBusAlert pin high or low for the specified I2C.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_SMBusAlert :
- I2C_SMBALERT_LOW
- I2C_SMBALERT_HIGH
return none
void I2C_SendPEC(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C PEC transfer.
name I2C_SendPEC.
fun Enables or disables the specified I2C PEC transfer.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ComputePec(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the PEC value calculation of the transferred bytes.
name I2C_ComputePec.
fun Enables or disables the PEC value calculation of the transferred bytes.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
uint8_t I2C_GetPec(I2C_Module* I2Cx)Enables or disables the PEC value calculation of the transferred bytes.
name I2C_GetPec.
fun Enables or disables the PEC value calculation of the transferred bytes.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return none
void I2C_EnableArp(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C ARP.
name I2C_EnableArp.
fun Enables or disables the specified I2C ARP.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_EnableExtendClk(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C Clock stretching.
name I2C_EnableExtendClk.
fun Enables or disables the specified I2C Clock stretching.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ConfigFastModeDutyCycle(I2C_Module* I2Cx, uint32_t DutyCycle)Selects the specified I2C fast mode duty cycle.
name I2C_ConfigFastModeDutyCycle.
fun Selects the specified I2C fast mode duty cycle.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param DutyCycle :
- I2C_FMDUTYCYCLE_16_9
- I2C_FMDUTYCYCLE_2
return none
void I2C_SetSDADigitalFilterWidth(I2C_Module* I2Cx, uint32_t width)SDA digital filter width selection.
name I2C_Digital_Filter_Width_Set.
fun SDA digital filter width selection.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param width :
- 0x00 Disable the digital filter.
- 0x01 ~ 0x0F The numbers of pclk cycles.
return none
Use note: The name field in the source comment is written as I2C_Digital_Filter_Width_Set. and differs from the function name I2C_SetSDADigitalFilterWidth; call it by the function name.
void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint32_t width)SCL digital filter width selection.
name I2C_SetSCLDigitalFilterWidth.
fun SCL digital filter width selection.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param width :
- 0x00 Disable the digital filter.
- 0x01 ~ 0x0F The numbers of pclk cycles.
return none
void I2C_SetSDAAnalogFilterWidth(I2C_Module* I2Cx, uint32_t width)SDA analog filter width selection.
name I2C_SetSDAAnalogFilterWidth.
fun SDA analog filter width selection.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param width :
- I2C_ANALOG_FILTER_WIDTH_5NS
- I2C_ANALOG_FILTER_WIDTH_15NS
- I2C_ANALOG_FILTER_WIDTH_25NS
- I2C_ANALOG_FILTER_WIDTH_35NS
return none
void I2C_EnableSDAAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables SDA Analog Filter.
name I2C_EnableSDAAnalogFilter.
fun Enables or disables SDA Analog Filter.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_SetSCLAnalogFilterWidth(I2C_Module* I2Cx, uint32_t width)SCL analog filter width selection.
name I2C_SetSCLAnalogFilterWidth.
fun SCL analog filter width selection.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param width :
- I2C_ANALOG_FILTER_WIDTH_5NS
- I2C_ANALOG_FILTER_WIDTH_15NS
- I2C_ANALOG_FILTER_WIDTH_25NS
- I2C_ANALOG_FILTER_WIDTH_35NS
return none
void I2C_EnableSCLAnalogFilter(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables SCL Analog Filter.
name I2C_EnableSCLAnalogFilter.
fun Enables or disables SCL Analog Filter.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_ClrFlag(I2C_Module* I2Cx, uint32_t I2C_FLAG)Clears the I2Cx’s pending flags.
name I2C_ClrFlag.
fun Clears the I2Cx's pending flags.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_FLAG :
- I2C_FLAG_SMBALERT SMBus Alert flag
- I2C_FLAG_PECERR PEC error in reception flag
- I2C_FLAG_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_FLAG_ACKFAIL Acknowledge failure flag
- I2C_FLAG_ARLOST Arbitration lost flag (Master mode)
- I2C_FLAG_BUSERR Bus error flag
- I2C_FLAG_SCLLTO SCL timeout low error flag
- I2C_FLAG_SCLHTO SCL timeout high error flag
- I2C_FLAG_SDALTO SDA timeout low error flag
- I2C_FLAG_DMAETOERR DMA ETO error in reception flag
- I2C_FLAG_FIFOOR Write data when FIFO is full
- I2C_FLAG_FIFOUR Read data when FIFO is empty
- I2C_FLAG_FIFOHE FIFO half empty flag
- I2C_FLAG_FIFOHF FIFO half full flag
- I2C_FLAG_FIFOE FIFO empty flag
- I2C_FLAG_FIFOF FIFO full flag
note Software can read this bit or clear it by writing' 0', and writing' 1' has no effect on this bit.
The other flag bits are cleared in the following sequence:
- STOPF (STOP detection) is cleared by software sequence: a read operation
to I2C_STS1 register followed by a write operation to I2C_CTRL1 register (I2C_ON()
to re-enable the I2C peripheral).
- ADDR10F (10-bit header sent) is cleared by software sequence: a read
operation to I2C_STS1 followed by writing the second byte of the address in DAT register.
- BYTEF (Byte Transfer Finished) is cleared by software sequence: a read
operation to I2C_STS1 register followed by a read/write to I2C_DAT register.
- ADDRF (Address sent) is cleared by software sequence: a read operation to
I2C_STS1 register followed by a read operation to I2C_STS2 register.
- STARTBF (Start Bit) is cleared software sequence: a read operation to I2C_STS1
register followed by a write operation to I2C_DAT register.
return none
void I2C_ClrIntPendingBit(I2C_Module* I2Cx, uint32_t I2C_IT)Clears the I2Cx’s interrupt pending bits.
name I2C_ClrIntPendingBit.
fun Clears the I2Cx's interrupt pending bits.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Interrupt :
- I2C_INT_SCLLTO SCL timeout low error flag
- I2C_INT_SCLHTO SCL timeout high error flag
- I2C_INT_SDALTO SDA timeout low error flag
- I2C_INT_FIFOUR Read data when FIFO is empty
- I2C_INT_FIFOOR Write data when FIFO is full
- I2C_INT_DMAETOERR Prove that the DMA transmission is complete but no ETO signal is given
- I2C_INT_SMBALERT SMBus Alert flag
- I2C_INT_PECERR PEC error in reception flag
- I2C_INT_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_INT_ACKFAIL Acknowledge failure flag
- I2C_INT_ARLOST Arbitration lost flag (Master mode)
- I2C_INT_BUSERR Bus error flag
note - STOPF (STOP detection) is cleared by software sequence: a read operation
to I2C_STS1 register (I2C_GetIntStatus()) followed by a write operation to
I2C_CTRL1 register (I2C_Enable() to re-enable the I2C peripheral).
- ADD10F (10-bit header sent) is cleared by software sequence: a read
operation to I2C_STS1 (I2C_GetIntStatus()) followed by writing the second
byte of the address in I2C_DAT register.
- BSF (Byte Transfer Finished) is cleared by software sequence: a read
operation to I2C_STS1 register (I2C_GetIntStatus()) followed by a
read/write to I2C_DAT register (I2C_SendData()).
- ADDRF (Address sent) is cleared by software sequence: a read operation to
I2C_STS1 register (I2C_GetIntStatus()) followed by a read operation to
I2C_STS2 register ((void)(I2Cx->STS2)).
- STARTBF (Start Bit) is cleared by software sequence: a read operation to
I2C_STS1 register (I2C_GetIntStatus()) followed by a write operation to
I2C_DAT register (I2C_SendData()).
return none
void I2C_ClrFIFO(I2C_Module* I2Cx)Clear the I2C’s FIFO.
name I2C_ClrFIFO.
fun Clear the I2C's FIFO.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return none
void I2C_EnableFIFO(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the FIFO.
name I2C_EnableFIFO.
fun Enables or disables the FIFO.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_EnableDMAFIFO(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the DMA FIFO.
name I2C_EnableDMAFIFO.
fun Enables or disables the DMA FIFO.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd :
- ENABLE
- DISABLE
return none
void I2C_SetTxFifoThreshold( I2C_Module* I2Cx, uint8_t TxFifoThreshold)Sets the Tx FIFO threshold of I2C.
name I2C_SetTxFifoThreshold.
fun Sets the Tx FIFO threshold of I2C.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param TxFifoThreshold:
- 0x0~0x7
return none
void I2C_SetRxFifoThreshold( I2C_Module* I2Cx, uint8_t RxFifoThreshold)Sets the Rx FIFO threshold of I2C.
name I2C_SetRxFifoThreshold.
fun Sets the Rx FIFO threshold of I2C.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param RxFifoThreshold:
- 0x0~0x7
return none
void I2C_EnableByteNum(I2C_Module* I2Cx, FunctionalState Cmd)Enables or disables the specified I2C peripheral ByteNum function.
name I2C_EnableByteNum.
fun Enables or disables the specified I2C peripheral ByteNum function.
param I2Cx :
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Cmd:
- ENABLE
- DISABLE
return none
void I2C_SetMasterReceivedDataBytesNum(I2C_Module* I2Cx, uint16_t Number_Of_bytes)Set the number of data bytes that will be received by master receiver of I2C peripheral.
name I2C_SetMasterReceivedDataBytesNum.
fun Set the number of data bytes that will be received by master receiver of I2C peripheral.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Number_Of_bytes(the number of data bytes) :
- 0x0~0x3FFF
return none
void I2C_SetByteNumLastStartStop(I2C_Module* I2Cx, uint32_t LastStatus)Configure the last status after master have finished sending the required data bytes.
name I2C_SetByteNumLastStartStop.
fun Configure the last status after master have finished sending the required data bytes.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param LastStatus :
- I2C_BYTENUM_LAST_STOP
- I2C_BYTENUM_LAST_START
return none
void I2C_SendFIFOData(I2C_Module* I2Cx, uint8_t Data)Sets FIFO data volume.
name I2C_SendFIFOData.
fun Sets FIFO data volume.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param Data
- 0x00~0xFF
return none
uint8_t I2C_RecvFIFOData(I2C_Module* I2Cx)Returns FIFO data.
name I2C_RecvFIFOData.
fun Returns FIFO data.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return FIFO data.
void I2C_SetTimeoutLowLimit(I2C_Module* I2Cx, uint32_t limit)timeout low threshold selection.
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_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 name field in the source comment is written as I2C_EnableTimeout. and differs from the function name I2C_EnableTimeoutDetect; call it by the function name.
uint32_t I2C_GetRegister(I2C_Module* I2Cx, uint8_t I2C_Register)Reads the specified I2C register and returns its value.
name I2C_GetRegister.
fun Reads the specified I2C register and returns its value.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_Register :
- I2C_REG_CTRL1 CTRL1 register
- I2C_REG_CTRL2 CTRL2 register
- I2C_REG_OADDR1 OADDR1 register
- I2C_REG_OADDR2 OADDR2 register
- I2C_REG_DAT DAT register
- I2C_REG_STS1 STS1 register
- I2C_REG_STS2 STS2 register
- I2C_REG_CLKCTRL CLKCTRL register
- I2C_REG_TMRISE TMRISE register
- I2C_REG_BYTENUM
- I2C_REG_GFLTRCTRL
return none
INTStatus I2C_GetIntStatus(I2C_Module* I2Cx, uint32_t I2C_IT)Checks whether the specified I2C interrupt has occurred or not.
name I2C_GetIntStatus.
fun Checks whether the specified I2C interrupt has occurred or not.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_IT :
- I2C_INT_STARTBF Start Bit (Master mode)
- I2C_INT_ADDRF Address sent (master mode)/matched (slave mode)
- I2C_INT_BSF Byte Transfer Finished
- I2C_INT_STOPF Stop detection (Slave mode)
- I2C_INT_RXDATNE Data Register not Empty (receivers)
- I2C_INT_TXDATE Data Register Empty (transmitters)
- I2C_INT_ADDR10F 10-bit header sent (Master mode)
- I2C_INT_ACKFAIL Acknowledge Failure
- I2C_INT_ARLOST Arbitration Lost (master mode)
- I2C_INT_BUSERR Bus Error
- I2C_INT_OVERRUN Overrun/Underrun
- I2C_INT_PECERR PEC Error in reception
- I2C_INT_SMBALERT SMBus Alert
- I2C_INT_FIFOF FIFO full
- I2C_INT_FIFOE FIFO empty
- I2C_INT_FIFOHF FIFO half full
- I2C_INT_FIFOHE FIFO half empty
- I2C_INT_FIFOUR Read data when FIFO is empty
- I2C_INT_FIFOOR Write data when FIFO is full
- I2C_INT_DMAETOERR Prove that the DMA transmission is complete but no ETO signal is given
- I2C_INT_SDALTO SDA timeout low error
- I2C_INT_SCLHTO SCL timeout high error
- I2C_INT_SCLLTO SCL timeout low error
return SET or RESET
ErrorStatus I2C_CheckFifoEvent(I2C_Module* I2Cx, uint32_t I2C_FIFO_EVENT)Checks whether the last I2Cx FIFO Event is equal to the one passed as parameter.
name I2C_CheckFifoEvent.
fun Checks whether the last I2Cx FIFO Event is equal to the one passed as parameter.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_FIFO_EVENT :
- I2C_EVENT_MASTER_FIFO_TRANSMITTER_MODE_SELECTED
- I2C_EVENT_MASTER_FIFO_RECEIVER_MODE_SELECTED
- I2C_EVENT_MASTER_FIFO_MODE_ADDRESS10
- I2C_EVENT_MASTER_FIFO_TRANSMITTING
- I2C_EVENT_MASTER_FIFO_TRANSMITTED
- I2C_EVENT_MASTER_FIFO_STOP_DETECTED
- I2C_EVENT_MASTER_FIFO_RECEIVING
- I2C_EVENT_MASTER_FIFO_RECEIVED
- I2C_EVENT_SLAVE_FIFO_RECEIVER_ADDRESS_MATCHED
- I2C_EVENT_SLAVE_FIFO_TRANSMITTER_ADDRESS_MATCHED
- I2C_EVENT_SLAVE_FIFO_RECEIVER_SECONDADDRESS_MATCHED
- I2C_EVENT_SLAVE_FIFO_TRANSMITTER_SECONDADDRESS_MATCHED
- I2C_EVENT_SLAVE_FIFO_GENERALCALLADDRESS_MATCHED
- I2C_EVENT_SLAVE_FIFO_STOP_DETECTED
- I2C_EVENT_SLAVE_FIFO_RECEIVING
- I2C_EVENT_SLAVE_FIFO_RECEIVED
- I2C_EVENT_SLAVE_FIFO_TRANSMITTING
- I2C_EVENT_SLAVE_FIFO_ACK_FAILURE
note For detailed description of Events, please refer to section
I2C_Events in n32h49x_i2c.h file.
return SUCCESS or ERROR
ErrorStatus I2C_CheckEvent(I2C_Module* I2Cx, uint32_t I2C_EVENT)Checks whether the last I2Cx Event is equal to the one passed as parameter.
name I2C_CheckEvent.
fun Checks whether the last I2Cx Event is equal to the one passed as parameter.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_EVENT :
- I2C_EVT_SLAVE_SEND_ADDR_MATCHED EV1
- I2C_EVT_SLAVE_RECV_ADDR_MATCHED EV1
- I2C_EVT_SLAVE_SEND_ADDR2_MATCHED EV1
- I2C_EVT_SLAVE_RECV_ADDR2_MATCHED EV1
- I2C_EVT_SLAVE_GCALLADDR_MATCHED EV1
- I2C_EVT_SLAVE_DATA_RECVD EV2
- (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_DUALFLAG) EV2
- (I2C_EVT_SLAVE_DATA_RECVD | I2C_FLAG_GCALLADDR) EV2
- I2C_EVT_SLAVE_DATA_SENDED EV3
- (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_DUALFLAG) EV3
- (I2C_EVT_SLAVE_DATA_SENDED | I2C_FLAG_GCALLADDR) EV3
- I2C_EVT_SLAVE_ACK_MISS EV3_2
- I2C_EVT_SLAVE_STOP_RECVD EV4
- I2C_EVT_MASTER_MODE_FLAG EV5
- I2C_EVT_MASTER_TXMODE_FLAG EV6
- I2C_EVT_MASTER_RXMODE_FLAG EV6
- I2C_EVT_MASTER_DATA_RECVD_FLAG EV7
- I2C_EVT_MASTER_DATA_SENDING EV8
- I2C_EVT_MASTER_DATA_SENDED EV8_2
- I2C_EVT_MASTER_MODE_ADDRESS10_FLAG EV9
note For detailed description of Events, please refer to section
I2C_Events in n32h49x_i2c.h file.
return SUCCESS or ERROR
uint32_t I2C_GetLastEvent(I2C_Module* I2Cx)Returns the last I2Cx Event.
name I2C_GetLastEvent.
fun Returns the last I2Cx Event.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
note For detailed description of Events, please refer to section
I2C_Events in n32h49x_i2c.h file.
return The last event
FlagStatus I2C_GetFlag(I2C_Module* I2Cx, uint32_t I2C_FLAG)Checks whether the specified I2C flag is set or not.
name I2C_GetFlag.
fun Checks whether the specified I2C flag is set or not.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
param I2C_FLAG :
- I2C_FLAG_DUALFLAG Dual flag (Slave mode)
- I2C_FLAG_SMBHADDR SMBus host header (Slave mode)
- I2C_FLAG_SMBDADDR SMBus default header (Slave mode)
- I2C_FLAG_GCALLADDR General call header flag (Slave mode)
- I2C_FLAG_TRF Transmitter/Receiver flag
- I2C_FLAG_BUSY Bus busy flag
- I2C_FLAG_MSMODE Master/Slave flag
- I2C_FLAG_SMBALERT SMBus Alert flag
- I2C_FLAG_PECERR PEC error in reception flag
- I2C_FLAG_OVERRUN Overrun/Underrun flag (Slave mode)
- I2C_FLAG_ACKFAIL Acknowledge failure flag
- I2C_FLAG_ARLOST Arbitration lost flag (Master mode)
- I2C_FLAG_BUSERR Bus error flag
- I2C_FLAG_TXDATE Data register empty flag (Transmitter)
- I2C_FLAG_RXDATNE Data register not empty (Receiver) flag
- I2C_FLAG_STOPF Stop detection flag (Slave mode)
- I2C_FLAG_ADDR10F 10-bit header sent flag (Master mode)
- I2C_FLAG_BSF Byte transfer finished flag
- I2C_FLAG_ADDRF Address sent flag (Master mode) "ADSL"
Address matched flag (Slave mode)"ENDA"
- I2C_FLAG_STARTBF Start bit flag (Master mode)
- I2C_FLAG_SCLLTO SCL timeout low error flag
- I2C_FLAG_SCLHTO SCL timeout high error flag
- I2C_FLAG_SDALTO SDA timeout low error flag
- I2C_FLAG_DMAETOERR DMA ETO error in reception flag
- I2C_FLAG_FIFOOR Write data when FIFO is full
- I2C_FLAG_FIFOUR Read data when FIFO is empty
- I2C_FLAG_FIFOHE FIFO half empty flag
- I2C_FLAG_FIFOHF FIFO half full flag
- I2C_FLAG_FIFOE FIFO empty flag
- I2C_FLAG_FIFOF FIFO full flag
return SET or RESET
5.16.3 Demo descriptions
This section covers the 12 I2C examples: EEPROM_Polling, EEPROM_Int, and EEPROM_DMA use an AT24C02 to demonstrate polling, interrupt, and DMA access; I2C_Master, I2C_Master_Int, I2C_Master_FIFO, I2C_Slave, I2C_Slave_Int, and I2C_Slave_FIFO demonstrate single-master single-slave transfer with 7-bit addressing; I2C_10bit demonstrates 10-bit addressing; PMBUS_Master and PMBUS_Slave demonstrate PMBus communication with PEC.
5.16.3.1 EEPROM_Polling Polling access to an AT24C02
Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/
Initialize I2C1 and the PB6 and PB7 pins→The master polls the device address and the page address→Write the test data page by page and wait for the device to be ready→Send a repeated start and the read address→Read the data back and compare it with the transmit buffer→Print the test result on the UART
| Item | Source analysis |
|---|---|
| Function | Use I2C1 as a master to access an external AT24C02 EEPROM in polling mode; write 256 bytes and read them back for a byte-by-byte comparison with the transmit buffer. |
| Source files | src/main.c: main flow and buffer comparison;src/i2c_eeprom.c and inc/i2c_eeprom.h: the EEPROM read/write driver, the configuration macros, and the timeout callback;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;the page size I2C_PageSize is 8 bytes and the test length TEST_EEPROM_SIZE is 256 bytes.The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main calls I2C_EE_Init to configure the pins and the peripheral and fills tx_buf with 0 to 255;I2C_EE_WriteBuffer splits the data page by page for I2C_EE_WriteOnePage and I2C_EE_PageWrite;I2C_EE_ReadBuffer uses two start conditions to send the page address and then read the data;Buffercmp compares tx_buf with rx_buf and prints the result. |
| Main APIs | I2C_InitStruct, I2C_Init, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_CheckEvent, I2C_GenerateStop |
| Verification | Connect the EEPROM, build, download, and reset the board; the UART prints initialization, write, and read messages in turn and finally reports whether the test passed. |
| Use note | The external device is an AT24C02 with 32 pages of 8 bytes each; when the transfer length exceeds one page the internal address wraps around automatically. Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example accesses an external EEPROM in polling mode: the master writes 256 bytes page by page and then reads them back for comparison.
Data path: main → I2C_EE_Init configures I2C1 and PB6 and PB7 → I2C_EE_WriteBuffer splits the data into pages → I2C_EE_PageWrite sends the start, the device address, the page address, and the data → I2C_EE_ReadBuffer reads back with two start conditions → Buffercmp compares → the UART prints
1. Configure the peripheral and pins. I2C_EE_Init enables the I2C1 and GPIOB clocks, sets PB6 and PB7 as alternate-function open-drain with pull-up, and fills the initialization structure for 100 kHz.
/** I2C periphral configuration */
I2C_DeInit(I2Cx);
I2C_InitStruct(&I2C_InitStructure);
I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
I2C_InitStructure.OwnAddr1 = 0xff;
I2C_InitStructure.AckEnable = I2C_ACKEN;
I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
I2C_InitStructure.ClkSpeed = I2C_Speed;
I2C_Init(I2Cx, &I2C_InitStructure);
This configuration is shared by the polling, interrupt, and DMA examples; only the way the data is moved differs.
2. Write the data page by page. I2C_EE_PageWrite enters the byte loop after the EV8 event and waits for a data-send-finished event after each byte.
/** While there is data to be written */
while (NumByteToWrite--)
{
/** Send the current byte */
I2C_SendData(I2Cx, *pBuffer);
/** Point to the next byte to be written */
pBuffer++;
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
An AT24C02 page holds 8 bytes, and the upper layer performs the page split, so the page boundary is not checked here.
3. Read back by length. I2C_EE_ReadBuffer sets the acknowledge and ACKPOS from the read length: it does not acknowledge for one byte and sets ACKPOS for two bytes.
if (NumByteToRead == 1)
{
/** Disable Acknowledgement */
I2C_ConfigAck(I2Cx, DISABLE);
(void)(I2Cx->STS1); /// clear ADDR
(void)(I2Cx->STS2);
I2C_GenerateStop(I2Cx, ENABLE);
}
else if (NumByteToRead == 2)
{
I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
(void)(I2Cx->STS1);
(void)(I2Cx->STS2);
I2C_ConfigAck(I2Cx, DISABLE);
}
else
{
I2C_ConfigAck(I2Cx, ENABLE);
(void)(I2Cx->STS1);
(void)(I2Cx->STS2);
}
The start condition and the page address are sent before the branches, so only the data-phase acknowledge and timing are handled here.
Corresponding source files: src/i2c_eeprom.c
5.16.3.2 EEPROM_Int Interrupt access to an AT24C02
Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_Int/
Initialize I2C1 and the peripheral interrupts→The master sends the start and the device address→The interrupt advances the write by event→The start is retried during the device internal write cycle→The interrupt advances the read by event→Compare the buffers and print the result
| Item | Source analysis |
|---|---|
| Function | Use I2C1 as a master to access an external AT24C02 EEPROM with interrupts; write 256 bytes and read them back for comparison, with the transfer driven by the interrupt handlers. |
| Source files | src/main.c: main flow and buffer comparison;src/i2c_eeprom.c and inc/i2c_eeprom.h: the interrupt read/write driver and the event and error handling;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;I2C_INT_EVENT, I2C_INT_BUF, and I2C_INT_ERR are enabled, the event interrupt pre-emption priority is 0 with sub-priority 1, and the error interrupt pre-emption priority is 0 with sub-priority 0.The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main calls I2C_EE_Init and then enters I2C_EE_WriteBuffer and I2C_EE_ReadBuffer;the event interrupt advances the state machine through EV5, EV6, EV7, and EV8, and the main flow continues once i2c_comm_state reports complete or exit;Buffercmp compares tx_buf with rx_buf and prints the result. |
| Main APIs | I2C_ConfigInt, I2C_GetLastEvent, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_ConfigAck, I2C_GenerateStart, I2C_GenerateStop |
| Verification | Connect the EEPROM, build, download, and reset the board; the UART prints write and read messages and finally reports whether the test passed. |
| Use note | The external device is an AT24C02 with 32 pages of 8 bytes each; when the transfer length exceeds one page the internal address wraps around automatically. Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example moves EEPROM access to interrupts: the event and error paths each have a handler, and the main flow only starts and finishes the transfer.
Data path: main → I2C_EE_Init enables the event and error interrupts → I2C1_EV_IRQHandler advances the transfer through EV5, EV6, EV7, and EV8 → I2C1_ER_IRQHandler handles acknowledge failure and bus error → i2c_comm_state reports completion → Buffercmp compares the buffers
1. Initialize and enable the interrupts. I2C_EE_Init configures the peripheral, sets the NVIC priority grouping, and enables the I2C1 event and error interrupts separately.
/** I2C NVIC configuration */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = I2C1_ER_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
The event and error interrupts share one priority grouping; both pre-emption priorities are 0 and the sub-priority decides the order.
2. The receive direction uses two interrupts to send the address and read the data. After EV6, I2C1_EV_IRQHandler decides from the read length whether to disable the acknowledge or set ACKPOS.
case I2C_EVT_MASTER_RXMODE_FLAG: /// EV6
if (Int_NumByteToRead == 1)
{
I2C_ConfigAck(I2C1, DISABLE);
I2C_GenerateStop(I2C1, ENABLE);
}
else if (Int_NumByteToRead == 2)
{
I2C1->CTRL1 |= I2C_NACK_POS_NEXT; /// set ACKPOS
I2C_ConfigAck(I2C1, DISABLE);
}
break;
For one byte it disables the acknowledge and then sends the stop; for two bytes it sets ACKPOS and disables the acknowledge so that the acknowledge failure falls after the second-to-last byte.
3. Moving bytes in the send direction. On EV8, if the frame sends only one internal address it sends the stop and finishes, otherwise it keeps sending the bytes in SendBuf.
/** Master Transmitter events */
case I2C_EVT_MASTER_TXMODE_FLAG: /// EV8 just after EV6
if (check_begin)
{
check_begin = FALSE;
I2C_ConfigInt(I2C1, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, DISABLE);
I2C_GenerateStop(I2C1, ENABLE);
i2c_comm_state = COMM_DONE;
break;
}
I2C_SendData(I2C1, DeviceOffset);
OffsetDone = TRUE;
break;
case I2C_EVT_MASTER_DATA_SENDING: /// EV8 I2C_EVENT_MASTER_DATA_TRANSMITTING
if (MasterDirection == Receiver)
{
I2C_GenerateStart(I2C1, ENABLE);
}
break;
check_begin distinguishes whether the current start probes whether the device is busy or performs a real data transfer; the stop condition is handled differently in the two cases.
4. Wrapping up in the error interrupt. I2C1_ER_IRQHandler sends the stop and sets the state to exit when a read fails or a bus error occurs.
void I2C1_ER_IRQHandler(void)
{
if (I2C_GetFlag(I2C1, I2C_FLAG_ACKFAIL))
{
if (check_begin) /// EEPROM write busy
{
I2C_GenerateStart(I2C1, ENABLE);
}
else if (I2C1->STS2 & 0x01) /// real fail
{
I2C_GenerateStop(I2C1, ENABLE);
i2c_comm_state = COMM_EXIT;
}
I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
}
if (I2C_GetFlag(I2C1, I2C_FLAG_BUSERR))
{
if (I2C1->STS2 & 0x01)
{
I2C_GenerateStop(I2C1, ENABLE);
i2c_comm_state = COMM_EXIT;
}
I2C_ClrFlag(I2C1, I2C_FLAG_BUSERR);
}
}
When the acknowledge failure falls inside the EEPROM internal write cycle, the start is issued again to wait for the device to be ready instead of reporting a communication failure.
Corresponding source files: src/i2c_eeprom.c
5.16.3.3 EEPROM_DMA DMA access to an AT24C02
Example directory: projects/n32h49x_EVAL/examples/I2C/EEPROM_DMA/
Initialize I2C1, the GPIO, and the DMA clock→The master sends the start, the device address, and the page address→Configure the transmit DMA channel and start the transfer→Wait for the byte-transfer-finished flag, then stop and wait for the device to be ready→Configure the receive DMA channel and read the data back→Compare the buffers and print the result
| Item | Source analysis |
|---|---|
| Function | Use I2C1 as a master to access an external AT24C02 EEPROM with DMA; write 256 bytes and read them back for comparison, with the data moved by the DMA channels. |
| Source files | src/main.c: main flow and buffer comparison;src/i2c_eeprom.c and inc/i2c_eeprom.h: the DMA read/write driver, the channel remap macros, and the timeout callback;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, device address EEPROM_ADDRESS is 0xA0, ClkSpeed is 100 kHz;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;transmit uses DMA1_CH6 remapped as I2C_DMA_TX_Remap and receive uses DMA1_CH7 remapped as I2C_DMA_RX_Remap;the page size is 8 bytes and the test length is 256 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main calls I2C_EE_Init and then enters I2C_EE_WriteBuffer and I2C_EE_ReadBuffer;for transmit, I2C_EE_PageWrite configures DMA1_CH6 and sets I2C_EnableDMA, using I2C_FLAG_BSF to detect completion;for receive, I2C_EE_ReadBuffer configures DMA1_CH7 and the channel copies DAT into the user buffer;Buffercmp compares tx_buf with rx_buf and prints the result. |
| Main APIs | I2C_EnableDMA, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_CheckEvent, I2C_GenerateStop, RCC_EnableAHBPeriphClk |
| Verification | Connect the EEPROM, build, download, and reset the board; the UART prints initialization, write, and read messages and finally reports whether the test passed. |
| Use note | The external device is an AT24C02 with 32 pages of 8 bytes each; when the transfer length exceeds one page the internal address wraps around automatically; do not enable the buffer interrupt when DMA is used to transmit or receive. Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The example hands the EEPROM byte transfer to DMA: the master only builds the protocol, and the channels move the data between the data register and memory.
Data path: main → I2C_EE_Init enables the DMA1 clock → I2C_EE_PageWrite configures DMA1_CH6 and starts → I2C_FLAG_BSF detects completion → I2C_EE_ReadBuffer configures DMA1_CH7 → Buffercmp compares
1. Enable the DMA clock. After configuring I2C1, I2C_EE_Init adds the DMA1 clock for the channels used later.
/** I2C periphral configuration */
I2C_DeInit(I2Cx);
I2C_InitStruct(&I2C_InitStructure);
I2C_InitStructure.BusMode = I2C_BUSMODE_I2C;
I2C_InitStructure.FmDutyCycle = I2C_FMDUTYCYCLE_2;
I2C_InitStructure.OwnAddr1 = 0xff;
I2C_InitStructure.AckEnable = I2C_ACKEN;
I2C_InitStructure.AddrMode = I2C_ADDR_MODE_7BIT;
I2C_InitStructure.ClkSpeed = I2C_Speed;
I2C_Init(I2Cx, &I2C_InitStructure);
RCC_EnableAHBPeriphClk(RCC_AHB_PERIPHEN_DMA1, ENABLE);
Transmit and receive use DMA1_CH6 and DMA1_CH7 respectively.
2. Configure the transmit channel. I2C_EE_PageWrite takes the defaults from DMA_StructInit, sets the peripheral address, the memory address, the transfer direction, and the data width, and then requests the channel remap.
DMA_DeInit(DMA1_CH6);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (u32)&I2Cx->DAT; /// (u32)I2C1_DR_Address;
DMA_InitStructure.MemAddr = (u32)pBuffer; /// from function input parameter
DMA_InitStructure.Direction = DMA_DIR_PERIPH_DST; /// fixed for send function
DMA_InitStructure.BufSize = NumByteToWrite; /// from function input parameter
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE; // fixed
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE; /// fixed
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE; /// fixed
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE; /// fixed
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL; /// fixed
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH; /// up to user
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; /// fixed
DMA_Init(DMA1_CH6, &DMA_InitStructure);
DMA_RequestRemap(I2C_DMA_TX_Remap,DMA1_CH6,ENABLE);
The peripheral address is fixed to I2Cx->DAT, the direction is memory to peripheral, and the buffer length comes from the argument.
3. Start the transfer and wait for completion. After the page address is sent it enables the I2C DMA request and the channel, detects the end with the byte-transfer-finished flag, then disables DMA and waits for the device to be ready.
I2C_EnableDMA(I2Cx, ENABLE);
DMA_EnableChannel(DMA1_CH6, ENABLE);
/* Wait for transfer finished */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_BSF))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
The DMA request and the channel are disabled right after the stop condition is sent, so no pending request disturbs the next transfer.
4. Configure the receive channel. I2C_EE_ReadBuffer configures DMA1_CH7 when the read length is greater than 1, with the direction changed to peripheral to memory.
/** DMA initialization */
DMA_DeInit(DMA1_CH7);
DMA_StructInit(&DMA_InitStructure);
DMA_InitStructure.PeriphAddr = (u32)&I2Cx->DAT; /// (u32)I2C1_DR_Address;
DMA_InitStructure.MemAddr = (u32)pBuffer; /// from function input parameter
DMA_InitStructure.Direction = DMA_DIR_PERIPH_SRC; /// fixed for read function
DMA_InitStructure.BufSize = NumByteToRead; /// from function input parameter
DMA_InitStructure.PeriphInc = DMA_PERIPH_INC_DISABLE; /// fixed
DMA_InitStructure.MemoryInc = DMA_MEM_INC_ENABLE; /// fixed
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_WIDTH_BYTE;// fixed
DMA_InitStructure.MemDataSize = DMA_MEM_DATA_WIDTH_BYTE;// DMA_MEM_DATA_WIDTH_BYTE; /// fixed
DMA_InitStructure.CircularMode = DMA_MODE_NORMAL; /// fixed
DMA_InitStructure.Priority = DMA_PRIORITY_VERY_HIGH; /// up to user
DMA_InitStructure.Mem2Mem = DMA_M2M_DISABLE; /// fixed
DMA_Init(DMA1_CH7, &DMA_InitStructure);
DMA_RequestRemap(I2C_DMA_RX_Remap,DMA1_CH7,ENABLE);
A length of 1 still goes through the polling path and does not use DMA.
Corresponding source files: src/i2c_eeprom.c
5.16.3.4 I2C_Master Master transmit and receive
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Master/
Initialize I2C1 and the PB6 and PB7 pins→The master sends the start and the slave address→Write 200 bytes continuously→Send a repeated start and the read address→Receive the data by length branch and generate the stop→Compare the buffers and print the result
| Item | Source analysis |
|---|---|
| Function | Implements full master transmit and receive functions on I2C1; writes 200 bytes to the slave at address 0x10 and reads them back, printing a pass when they match. |
| Source files | src/main.c: master initialization, the transmit and receive functions, the main flow, and buffer comparison, plus bus clock recovery, peripheral reset, and the timeout callback;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, own address I2C_MASTER_ADDR is 0x30 and target slave address I2C_SLAVE_ADDR is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz with duty cycle I2C_FMDUTYCYCLE_2;the test buffer is 200 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main fills tx_buf with 0 to 199;i2c_master_send waits for the bus to be idle, sends the start and the address, then sends each byte and waits for I2C_FLAG_BSF;i2c_master_recv handles the 1-byte, 2-byte, and multi-byte lengths in separate branches and disables the acknowledge before the last byte;Buffercmp prints the result, clears rx_buf, and delays before retrying. |
| Main APIs | I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_CheckEvent, I2C_RecvData, I2C_ConfigAck, I2C_GenerateStop |
| Verification | Flash this example and the I2C_Slave example to two boards and wire SCL and SDA together with a common ground;after reset the UART prints this is a i2c master demo and the test result. |
| Use note | This is a pure master example, so a slave at address 0x10 must respond on the bus;the receive path branches by length, and the last two bytes must be read before the stop condition is sent; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The master advances the timing purely by polling flags: every step carries a timeout counter, and an expiry calls the shared callback and returns an error code.
Data path: main fills tx_buf → i2c_master_send → i2c_master_recv → Buffercmp → print
1. Initialize the master. i2c_master_init enables the I2C1 and GPIOB clocks, sets PB6 and PB7 as open-drain alternate function, and fills the master parameters.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_master);
i2cx_master.BusMode = I2C_BUSMODE_I2C;
i2cx_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_master.OwnAddr1 = I2C_MASTER_ADDR;
i2cx_master.AckEnable = I2C_ACKEN;
i2cx_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2cx_master.ClkSpeed = 100000; // 100K
I2C_Init(I2Cx, &i2cx_master);
I2C_Enable(I2Cx, ENABLE);
OwnAddr1 takes I2C_MASTER_ADDR and ClkSpeed is set to 100 kHz; the peripheral is enabled last.
2. Send the start condition. Comm_Flag marks the phase of one transaction, and the start condition is sent only when idle so that it is not repeated.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2Cx, ENABLE);
}
It then sends the slave address with I2C_SendAddr7bit and waits for I2C_EVT_MASTER_TXMODE_FLAG.
3. Receive the last byte. For a length of 1 it disables the acknowledge, reads STS1 and STS2 to release ADDR, and sends the stop ahead of time.
if (len == 1)
{
I2C_ConfigAck(I2Cx, DISABLE);
(void)(I2Cx->STS1); /// clear ADDR
(void)(I2Cx->STS2);
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2Cx, ENABLE);
}
Reading STS1 and STS2 is the fixed way to release the ADDR flag.
4. Main flow. main fills the transmit buffer with an incrementing sequence and then writes and reads on the same bus, counting a pass only when they match.
while (1)
{
for(; ;)
{
/* First write in the memory followed by a read of the written data --------*/
/* Write data*/
if(i2c_master_send(tx_buf, TEST_BUFFER_SIZE))
{
break; /* send fail, resend*/
}
/* Read data */
if(i2c_master_recv(rx_buf, TEST_BUFFER_SIZE))
{
break; /* receive fail, resend*/
}
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
if (test_status == PASSED)
{
log_info("write and read data are same, i2c master test pass\r\n");
}
else
{
log_info("write and read data are different, i2c master test fail\r\n");
}
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
rx_buf[i] = 0;
}
systick_delay_ms(50);
}
}
A non-zero return from either direction breaks out and retries; a pass clears rx_buf and delays 50 ms before the next round.
Corresponding source files: src/main.c
5.16.3.5 I2C_Master_Int Master interrupt transfer
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Master_Int/
Initialize I2C1, the GPIO, and the NVIC→Enable the event, buffer, and error interrupts→The main flow sends the start and waits for the send-complete flag→The event interrupt advances the send from EV5 to EV8→The interrupt re-issues the start and receives byte by byte on EV7→Compare the buffers and print the result
| Item | Source analysis |
|---|---|
| Function | Advances the I2C1 master timing with interrupts instead of polling; both transmit and receive are completed inside I2Cx_EV_IRQHandler by event code, and the main flow only waits for the completion flags. |
| Source files | src/main.c: initialization, the interrupt handlers, the main flow, and buffer comparison, including helpers such as Memset;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, own address I2C_MASTER_ADDR is 0x30 and target slave address I2C_SLAVE_ADDR is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz, the event interrupt uses I2C1_EV_IRQn with pre-emption priority 4, and the error interrupt uses I2C1_ER_IRQn with pre-emption priority 3;the test buffer is 100 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main fills tx_buf with an incrementing sequence and sends the start, then blocks waiting for flag_master_send_finish;after the send it re-issues the start and waits for flag_master_recv_finish;in the event interrupt EV5 sends the address by direction, EV6 to EV8 advance the send, and EV7 receives byte by byte, disabling the acknowledge and sending the stop at the second-to-last byte; Buffercmp prints the result. |
| Main APIs | I2C_ConfigInt, NVIC_Init, I2C_GenerateStart, I2C_SendAddr7bit, I2C_SendData, I2C_RecvData, I2C_GetLastEvent |
| Verification | Flash this example and the I2C_Slave example to two boards and connect the bus;after reset the UART prints This is a i2c master int demo! and the test result. |
| Use note | The master timing runs in the interrupt and the main flow synchronizes with the completion flags; the error interrupt only handles acknowledge failure and sends the stop after clearing I2C_FLAG_ACKFAIL;Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The interrupt version treats the event code as a state-machine input: all transfer actions are centralized in the event interrupt, and the main flow only starts and waits.
Data path: main sends the start → the event interrupt sends the address on EV5 → EV6 and EV8 advance the send → EV7 receives byte by byte → the completion flag is set → Buffercmp
1. Configure the interrupt priority. The event interrupt pre-emption priority is 4 and the error interrupt is 3, so error handling can pre-empt ordinary events.
void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = I2Cx_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = I2Cx_ER_IRQn; // test err
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
Both channels are enabled and both sub-priorities are 1.
2. Enable the peripheral interrupts. At the end of initialization it enables the event, buffer, and error interrupts at once and then enables I2C1.
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_Configuration();
I2C_Enable(I2Cx, ENABLE);
I2C_INT_EVENT, I2C_INT_BUF, and I2C_INT_ERR correspond to the event, buffer, and error requests.
3. Synchronize the main flow. After sending the start the main flow blocks on the completion flag and calls the shared callback on timeout.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2Cx, ENABLE);
}
I2CTimeout = I2CT_LONG_TIMEOUT * 1000;
while (flag_master_send_finish == 0)
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_UNKNOW);
}
}
Transmit and receive each have a completion flag, and the main flow continues once the interrupt sets one.
4. Addressing in the interrupt. The event interrupt reads I2C_GetLastEvent first, and on EV5 it sends the read or write address depending on the current direction.
case I2C_EVT_MASTER_MODE_FLAG: // EV5 start
if (flag_trans_direct) // read
{
Memset(rx_buf, 0, TEST_BUFFER_SIZE); // clear recv buf, ready to recv data
I2C_SendAddr7bit(I2Cx, I2C_SLAVE_ADDR, I2C_DIRECTION_RECV);
rx_num = 0;
}
else // write
{
I2C_SendAddr7bit(I2Cx, I2C_SLAVE_ADDR, I2C_DIRECTION_SEND);
tx_num = 0;
}
break;
flag_trans_direct distinguishes a write round from a read round; the read direction also clears the receive buffer and resets the counter first.
5. Receiving in the interrupt. Each EV7 reads one byte, and at the second-to-last byte it disables the acknowledge and sends the stop condition.
case I2C_EVT_MASTER_DATA_RECVD_FLAG: // one byte recved :EV7.//BUSY,MSL and RXDATNE flags.
// case I2C_EVT_MASTER_SFT_DATA_RECVD_FLAG: // BUSY, MSMODE(Master) and Data register not empty, BSF(Byte transfer finished)flags.
rx_buf[rx_num++] = I2C_RecvData(I2Cx);
if (rx_num == (TEST_BUFFER_SIZE - 1))
{
I2C_ConfigAck(I2Cx, DISABLE); // Disable I2Cx acknowledgement.
if (Comm_Flag == C_READY)
{
Comm_Flag = C_STOP_BIT;
I2C_GenerateStop(I2Cx, ENABLE); // Send I2Cx STOP Condition.
}
}
else if (rx_num == TEST_BUFFER_SIZE)
{
flag_master_recv_finish = 1;
}
break;
After the last byte is read the receive-complete flag is set for the main flow to compare the data.
Corresponding source files: src/main.c
5.16.3.6 I2C_Master_FIFO FIFO master transfer
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Master_FIFO/
Initialize I2C1 and the PB6 and PB7 pins→Enable the FIFO and set the transmit and receive thresholds→Configure the master receive by byte count→Advance the transmit and receive by FIFO events→Compare the buffers and print the result
| Item | Source analysis |
|---|---|
| Function | Enables the transmit and receive FIFO on I2C1 to complete a master transfer; the transfer functions advance by FIFO events instead of single-byte events, sending and receiving 10 bytes to and from the slave at address 0x10. |
| Source files | src/main.c: initialization, the FIFO transfer functions, the main flow, and clock-recovery fault handling;inc/main.h: the slave address, buffer length, and FIFO threshold macros;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, target slave address I2Cx_SLAVE_ADDRESS7 is 0x10 and the own address is 0x30;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz, the transmit threshold FIFO_HALF_EMPTY_THR and the receive threshold FIFO_HALF_FULL_THR are both 4;the test buffer is 10 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | After initialization main fills tx_buf with an incrementing sequence, calls i2c_master_fifo_send, delays 2 ms, and calls i2c_master_fifo_recv;the receive first sets the byte count with I2C_SetMasterReceivedDataBytesNum and then advances the transfer by FIFO events;Buffercmp prints the result. |
| Main APIs | I2C_EnableFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold, I2C_CheckFifoEvent, I2C_RecvFIFOData, I2C_SetMasterReceivedDataBytesNum, I2C_ClrFIFO |
| Verification | Flash this example and a slave example to two boards and wire SCL, SDA, and a common ground; after reset the UART prints this is a i2c master fifo demo and the test result. |
| Use note | FIFO transfer relies on threshold events, so the byte count must be set before receiving; the timeout callback selects a software reset or an RCC module reset by COMM_RECOVER_MODE;Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The FIFO version upgrades the single-byte event to a threshold event: one event can move several bytes, which reduces the number of waits in a long transfer.
Data path: main → i2c_master_fifo_send → i2c_master_fifo_recv → Buffercmp → print
1. Enable the FIFO and set the thresholds. After enabling I2C1, initialization turns on the transmit and receive FIFO and sets the two thresholds.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_master);
i2cx_master.BusMode = I2C_BUSMODE_I2C;
i2cx_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_master.OwnAddr1 = I2C_MASTER_ADDR;
i2cx_master.AckEnable = I2C_ACKEN;
i2cx_master.AddrMode = I2C_ADDR_MODE_7BIT;
i2cx_master.ClkSpeed = 100000; // 100K
I2C_Init(I2Cx, &i2cx_master);
I2C_Enable(I2Cx, ENABLE);
I2C_EnableFIFO(I2Cx, ENABLE);
I2C_SetTxFifoThreshold(I2Cx, FIFO_HALF_EMPTY_THR);
I2C_SetRxFifoThreshold(I2Cx, FIFO_HALF_FULL_THR);
The transmit threshold is FIFO_HALF_EMPTY_THR and the receive threshold is FIFO_HALF_FULL_THR; both are 4 in this example.
2. Main flow. After filling the transmit buffer, main sends first and then receives, with a 2 ms gap for the slave to get ready.
int main(void)
{
uint16_t i = 0;
log_init();
log_info("\nthis is a i2c master fifo demo\r\n");
/* Initialize the I2C EEPROM driver ----------------------------------------*/
i2c_master_init();
/* Fill the buffer to send */
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
tx_buf[i] = i;
}
/* First write in the memory followed by a read of the written data --------*/
/* Write data*/
i2c_master_fifo_send(tx_buf, TEST_BUFFER_SIZE);
systick_delay_ms(2);
/* Read data */
i2c_master_fifo_recv(rx_buf, TEST_BUFFER_SIZE);
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
if (test_status == PASSED)
{
log_info("i2c master fifo test pass!\r\n");
}
else
{
log_info("i2c master fifo test fail!\r\n");
}
while (1)
{
}
}
Transmit and receive are each handled by one function, and a consistent comparison prints the pass message.
3. Timeout recovery. A timeout in any wait enters the shared callback, which selects the recovery method from COMM_RECOVER_MODE.
void CommTimeOut_CallBack(ErrCode_t errcode)
{
log_info("...ErrCode:%d\r\n", errcode);
#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
IIC_SWReset();
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
SystemNVICReset();
#endif
}
The default configuration uses a module-level RCC reset, after which the I2C peripheral is re-initialized and a message is printed.
Corresponding source files: src/main.c
5.16.3.7 I2C_Slave Slave transmit and receive
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Slave/
Initialize I2C1 and set the slave address→Wait for the matching address from the master→Receive the data into the buffer until the stop condition→After the master switches to read, send the buffer byte by byte→Clear the buffer and wait for the next round→Loop and print the result
| Item | Source analysis |
|---|---|
| Function | Configures I2C1 as a slave at address 0x10;it first receives the 256 bytes written by the master into the buffer and then, after the direction is reversed, sends the buffer back to the master. |
| Source files | src/main.c: slave initialization, the transmit and receive functions, the main flow, and the timeout callback;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz with duty cycle I2C_FMDUTYCYCLE_2;the transfer buffer is 256 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | After initialization main enters a loop and first receives a whole frame into data_buf with i2c_slave_recv;i2c_slave_recv takes the data and counts it on I2C_EVT_SLAVE_DATA_RECVD and returns only on I2C_EVT_SLAVE_STOP_RECVD;i2c_slave_send waits for I2C_EVT_SLAVE_SEND_ADDR_MATCHED and then sends byte by byte, ending on a stop or a missing acknowledge;each round clears the buffer and prints the pass message. |
| Main APIs | I2C_CheckEvent, I2C_GetLastEvent, I2C_SendData, I2C_RecvData, I2C_ClrFlag, I2C_Enable, I2C_Init |
| Verification | Flash this example to the slave board and run I2C_Master or another master example against it;the slave UART prints this is a i2c slave test demo and prints a pass message after each completed round. |
| Use note | The slave address must match the address the master uses, which is 0x10 in this example;both directions use the stop condition or an address match as the frame boundary, so no extra handshake is needed; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
A slave cannot start the timing on its own and can only wait to be addressed; the code polls event codes to split one transaction into several states.
Data path: main → i2c_slave_recv receives a whole frame → i2c_slave_send sends it back → clear the buffer → loop
1. Configure as a slave. Initialization writes the own address into OwnAddr1, and the other bus parameters match the master side.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_slave);
i2cx_slave.BusMode = I2C_BUSMODE_I2C;
i2cx_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2cx_slave.AckEnable = I2C_ACKEN;
i2cx_slave.AddrMode = I2C_ADDR_MODE_7BIT;
i2cx_slave.ClkSpeed = 100000; // 100000 100K
I2C_Init(I2Cx, &i2cx_slave);
I2C_Enable(I2Cx, ENABLE);
AddrMode is 7-bit, AckEnable turns on automatic acknowledge, and ClkSpeed is 100 kHz.
2. Slave transmit. The transmit function waits for the address match and then sends the buffer byte by byte on events in a loop.
I2CTimeout = I2CT_LONG_TIMEOUT;
I2C_Enable(I2Cx, ENABLE);
I2C_SendData(I2Cx, *sendBufferPtr++);
while (1)
{
lastevent = I2C_GetLastEvent(I2Cx);
if (lastevent == I2C_EVT_SLAVE_DATA_SENDED)
{
I2C_SendData(I2Cx, *sendBufferPtr++);
I2CTimeout = I2CT_LONG_TIMEOUT;
}
else if ((lastevent == I2C_EVT_SLAVE_ACK_MISS) || (lastevent == I2C_EVT_SLAVE_STOP_RECVD))
{
I2C_ClrFlag(I2Cx, I2C_FLAG_ACKFAIL);
I2C_Enable(I2Cx, ENABLE);
break;
}
On a stop condition or a missing acknowledge it clears the acknowledge-failure flag and exits, waiting for the next frame.
3. Slave receive. The receive function takes one byte on each data event until the master sends the stop condition.
I2C_Enable(I2Cx, ENABLE);
while (1)
{
lastevent = I2C_GetLastEvent(I2Cx);
if (lastevent == I2C_EVT_SLAVE_DATA_RECVD)
{
*data++ = I2C_RecvData(I2Cx);
rxDataNum++;
I2CTimeout = I2CT_LONG_TIMEOUT;
}
else if (lastevent == I2C_EVT_SLAVE_STOP_RECVD)
{
I2C_Enable(I2Cx, ENABLE);
break;
}
rxDataNum records the number of bytes received in this round for the main flow to check whether the frame is complete.
Corresponding source files: src/main.c
5.16.3.8 I2C_Slave_Int Slave interrupt transfer
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Slave_Int/
Initialize I2C1, the GPIO, and the NVIC→Enable the event, buffer, and error interrupts→The main flow waits for the receive-complete flag→The event interrupt stores or loads data by slave event code→The completion flag is set when the stop condition arrives→The main flow waits for the send to finish and prints the result
| Item | Source analysis |
|---|---|
| Function | Handles I2C1 slave transactions with interrupts; both receive and transmit are completed inside I2Cx_EV_IRQHandler by event code, and the main flow only waits for two completion flags. |
| Source files | src/main.c: initialization, the interrupt handlers, the main flow, and the timeout callback;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz, the event interrupt uses I2C1_EV_IRQn with pre-emption priority 4, and the error interrupt uses I2C1_ER_IRQn with pre-emption priority 3;the transfer buffer is 100 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | After initialization main blocks waiting for flag_slave_recv_finish, which the interrupt sets after a whole frame;it then waits for flag_slave_send_finish, which the interrupt sets after the send;the interrupt accesses data_buf by events such as I2C_EVT_SLAVE_RECV_ADDR_MATCHED and I2C_EVT_SLAVE_DATA_RECVD;each wait prints the length when it ends. |
| Main APIs | I2C_ConfigInt, NVIC_Init, I2C_GetLastEvent, I2C_Enable, I2C_Init, I2C_CheckEvent, I2C_ClrFlag |
| Verification | Flash this example to the slave board and run I2C_Master_Int or another master example against it;the slave UART prints this is a i2c slave int demo and the transfer lengths. |
| Use note | The slave interrupt sets the receive-complete flag when the stop condition arrives and data has been received; the error interrupt handles acknowledge failure separately, and the main flow does not take part in error branches; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The slave interrupt maps each event class to one data move or one state record, and the main flow only cares about two completion flags.
Data path: main waits for the receive flag → the event interrupt accesses data_buf by I2C_EVT_SLAVE_* → the completion flag is set → main prints
1. Configure the interrupt priority. As in the master example, the event interrupt pre-emption priority is 4 and the error interrupt is 3.
void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = I2Cx_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = I2Cx_ER_IRQn; // test err
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
The error interrupt has the higher priority, so bus faults are handled first.
2. Enable the peripheral interrupts. At the end of initialization it enables the event, buffer, and error interrupts and then enables I2C1.
// int enable
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_Configuration();
I2C_Enable(I2Cx, ENABLE);
return 0;
I2C_ConfigInt configures the three request classes at once and then NVIC_Configuration is called.
3. Synchronize the main flow. The main flow waits for the receive and then the send completion flag, and a timeout is taken over by the shared callback.
while (flag_slave_recv_finish == 0) // wait for recv data finish
{
if ((I2CTimeout--) == 0)
CommTimeOut_CallBack(SLAVE_UNKNOW);
}
log_info("recv finish,recv len = %d\r\n", rxDataNum);
flag_slave_recv_finish = 0;
I2CTimeout = I2CT_LONG_TIMEOUT * 1000;
// send data
while (flag_slave_send_finish == 0) // wait for send data finish
{
if ((I2CTimeout--) == 0)
CommTimeOut_CallBack(SLAVE_UNKNOW);
}
log_info("tx finish,tx len = %d\r\n", rxDataNum-1);
flag_slave_send_finish = 0;
while (1)
{;}
The interrupt sets both flags; the main flow reads the length, clears the flag, and waits again.
4. Moving data in the interrupt. In slave mode it takes or sends data by event code and sets the receive-complete flag only on the stop condition.
if ((last_event & I2C_ROLE_MASTER) != I2C_ROLE_MASTER) // MSMODE = 0:I2C slave mode
{
switch (last_event)
{
case I2C_EVT_SLAVE_RECV_ADDR_MATCHED:
// clear flag,ready to receive data
rxDataNum = 0;
break;
case I2C_EVT_SLAVE_SEND_ADDR_MATCHED:
rxDataNum = 0;
break;
case I2C_EVT_SLAVE_DATA_SENDING:
I2Cx->DAT = data_buf[rxDataNum++];
break;
case I2C_EVT_SLAVE_DATA_SENDED:
I2Cx->DAT = data_buf[rxDataNum++];
break;
case I2C_EVT_SLAVE_DATA_RECVD:
data_buf[rxDataNum++] = I2Cx->DAT;
break;
case I2C_EVT_SLAVE_STOP_RECVD:
I2C_Enable(I2Cx, ENABLE);
if(rxDataNum != 0)
{
flag_slave_recv_finish = 1; // The STOPF bit is not set after a NACK reception
}
break;
default:
I2C_Enable(I2Cx, ENABLE);
timeout_flag = 1;
break;
}
}
rxDataNum is both the receive count and the read pointer used for the send.
Corresponding source files: src/main.c
5.16.3.9 I2C_Slave_FIFO FIFO slave transfer
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_Slave_FIFO/
Initialize I2C1 and set the slave address→Enable the transmit and receive FIFO and the thresholds→The slave waits for the address match→Advance the receive into the buffer by FIFO events→After the stop condition the direction reverses and the buffer is sent back→When done it idles until reset
| Item | Source analysis |
|---|---|
| Function | Configures I2C1 as a slave at address 0x10 and enables the transfer FIFO;it first receives the 10 bytes written by the master by FIFO events and, after the direction is reversed, sends the buffer back. |
| Source files | src/main.c: initialization, the FIFO transfer functions, the main flow, and the reset callback;inc/main.h: the slave address, buffer length, and FIFO threshold macros;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 slave, 7-bit addressing, own address I2Cx_SLAVE_ADDRESS7 is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz, the transmit threshold FIFO_HALF_EMPTY_THR and the receive threshold FIFO_HALF_FULL_THR are both 4;the transfer buffer is 10 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | After initialization main first receives a whole frame into data_buf with i2c_slave_fifo_recv;the receive sets the byte count with I2C_SetMasterReceivedDataBytesNum and then moves data in batches on I2C_EVENT_SLAVE_FIFO_RECEIVING after the address match;the send waits for I2C_EVENT_SLAVE_FIFO_TRANSMITTER_ADDRESS_MATCHED, fills the transmit FIFO once, and then tops it up on events;it then idles. |
| Main APIs | I2C_EnableFIFO, I2C_SetTxFifoThreshold, I2C_SetRxFifoThreshold, I2C_CheckFifoEvent, I2C_SendFIFOData, I2C_RecvFIFOData, I2C_ClrFIFO |
| Verification | Flash this example to the slave board and run I2C_Master_FIFO or another master example against it;the slave UART prints This is the i2c slave fifo mode.. |
| Use note | FIFO slave transfer uses the address-match event as the frame start in both directions; the send fills the FIFO once and then tops it up on the half-empty event; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The FIFO slave splits a whole frame into several threshold events: the receive moves data on half-full and the send tops up on half-empty.
Data path: main → i2c_slave_fifo_recv receives a whole frame → i2c_slave_fifo_send sends it back → idle
1. Enable the FIFO and set the thresholds. After the slave parameters are written, initialization turns on the transmit and receive FIFO and sets the two thresholds.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_slave);
i2cx_slave.BusMode = I2C_BUSMODE_I2C;
i2cx_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2cx_slave.AckEnable = I2C_ACKEN;
i2cx_slave.AddrMode = I2C_ADDR_MODE_7BIT;
i2cx_slave.ClkSpeed = 100000; // 100000 100K
I2C_Init(I2Cx, &i2cx_slave);
I2C_Enable(I2Cx, ENABLE);
I2C_EnableFIFO(I2Cx, ENABLE);
I2C_SetTxFifoThreshold(I2Cx, FIFO_HALF_EMPTY_THR);
I2C_SetRxFifoThreshold(I2Cx, FIFO_HALF_FULL_THR);
return 0;
}
The transmit threshold is FIFO_HALF_EMPTY_THR and the receive threshold is FIFO_HALF_FULL_THR; both are 4 in this example.
2. Main flow. The main flow is a single receive and send: it collects one frame, sends it back as soon as the direction reverses, and then idles.
int main(void)
{
log_init();
log_info("\nThis is the i2c slave fifo mode.\r\n");
/* Initialize the I2C driver ----------------------------------------*/
i2c_slave_init();
/* Read data */
i2c_slave_fifo_recv(data_buf, TEST_BUFFER_SIZE);
Both directions work in units of 10 bytes, set by TEST_BUFFER_SIZE.
3. Timeout recovery. A timeout while waiting for a FIFO event enters the shared callback, which selects the recovery method from COMM_RECOVER_MODE.
void CommTimeOut_CallBack(ErrCode_t errcode)
{
log_info("...ErrCode:%d\r\n", errcode);
#if (COMM_RECOVER_MODE == MODULE_SELF_RESET)
IIC_SWReset();
#elif (COMM_RECOVER_MODE == MODULE_RCC_RESET)
IIC_RCCReset();
#elif (COMM_RECOVER_MODE == SYSTEM_NVIC_RESET)
SystemNVICReset();
#endif
}
The default configuration uses a module-level RCC reset, after which the peripheral is re-initialized and a message is printed.
Corresponding source files: src/main.c
5.16.3.10 I2C_10bit 10-bit addressing master and slave
Example directory: projects/n32h49x_EVAL/examples/I2C/I2C_10bit/
Initialize I2C1 as master and I2C2 as slave→Configure both peripherals for 10-bit addressing→The master writes and sends the data to the slave→Compare the master transmit and slave receive buffers→Reverse the direction so the slave writes and the master reads→Compare the buffers again and print the result
| Item | Source analysis |
|---|---|
| Function | Uses I2C1 as master and I2C2 as slave on the same board to demonstrate 10-bit addressing; the master writes while the slave reads, then the direction is reversed to slave-write and master-read, with a buffer comparison in both rounds. |
| Source files | src/main.c: initialization of both peripherals, the main flow, two event interrupts and the error interrupt, plus reset-recovery code;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | On master I2C1, SCL is PB6 and SDA is PB7 with alternate function GPIO_AF2, and the own address I2C_MASTER_ADDR is 0x230;on slave I2C2, SCL is PB10 and SDA is PB11 with alternate function GPIO_AF7, and the own address I2C_SLAVE_ADDR is 0x2A0;AddrMode is I2C_ADDR_MODE_10BIT on both sides and ClkSpeed is 100 kHz;the first byte uses I2C_FRAME_HEAD_TX or I2C_FRAME_HEAD_RX to carry the high address bits;the test buffer is 100 bytes. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main initializes the master and the slave, fills master_tx_buf, sends the start, and waits for both flag_master_complete and flag_slave_complete;the first round compares master_tx_buf with slave_rx_buf, and the second round switches the direction to RECEIVE and compares master_rx_buf with slave_tx_buf;the two event interrupts advance the transfer by event code on I2C1 and I2C2. |
| Main APIs | I2C_ConfigInt, NVIC_Configuration, I2C_GenerateStart, I2C_SendData, I2C_RecvData, I2C_GetLastEvent, I2C_ConfigAck |
| Verification | A single board is enough; the I2C1 and I2C2 pins must be wired together as on the board; after reset the UART prints this is a i2c(10bit) master/slave demo and the results of both rounds. |
| Use note | Both ends must use 10-bit addressing, and the top two address bits are sent together with the first byte; each round must wait for both the master and the slave completion flags before comparing; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
A 10-bit address transaction adds one addressing step: after the start the frame head and the high address bits are sent, then the low address byte, and only then does the data phase begin.
Data path: main sends the start → the I2C1 interrupt sends the frame head and the address → the I2C2 interrupt acknowledges and stores the data → both completion flags are set → Buffercmp → the reverse direction is tested once more
1. Initialize the master for 10-bit addressing. i2c_master_init sets the master address mode to 10-bit and enables the event, buffer, and error interrupts at once.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_master);
i2cx_master.BusMode = I2C_BUSMODE_I2C;
i2cx_master.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_master.OwnAddr1 = I2C_MASTER_ADDR;
i2cx_master.AckEnable = I2C_ACKEN;
i2cx_master.AddrMode = I2C_ADDR_MODE_10BIT;
i2cx_master.ClkSpeed = 100000; //100K
I2C_Init(I2Cx, &i2cx_master);
// int enable
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_Configuration(1);
I2C_Enable(I2Cx, ENABLE);
The own address is I2C_MASTER_ADDR, that is 0x230.
2. Initialize the slave. The slave side repeats the same steps on I2C2 with the same 10-bit address mode, but with PB10 and PB11 as the pins.
I2C_DeInit(Slave_I2Cx);
I2C_InitStruct(&i2c2_slave);
i2c2_slave.BusMode = I2C_BUSMODE_I2C;
i2c2_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2c2_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2c2_slave.AckEnable = I2C_ACKEN;
i2c2_slave.AddrMode = I2C_ADDR_MODE_10BIT;
i2c2_slave.ClkSpeed = 100000; //100K
I2C_Init(Slave_I2Cx, &i2c2_slave);
// int enable
I2C_ConfigInt(Slave_I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_Configuration(2);
I2C_Enable(Slave_I2Cx, ENABLE);
Each peripheral has its own interrupt set, so they do not interfere with each other.
3. Reverse transfer in the main flow. The second round switches the direction to RECEIVE, waits for the bus to be idle, clears ACKPOS, and sends the start.
/*----------I2C1 Master Read and I2C2 Slave Write data---------*/
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
slave_tx_buf[i] = (0x80 + i);
}
flag_master_complete = 0;
flag_slave_complete = 0;
master_TR = RECEIVE;
I2CTimeout = I2CT_LONG_TIMEOUT;
/*Wait until no busy*/
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
{
if ((I2CTimeout--) == 0)
{
CommTimeOut_CallBack(MASTER_BUSY);
return 1;
}
}
I2C_ConfigAck(I2Cx, ENABLE);
/* I2C1 Read data and I2C2 Write data */
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2Cx->CTRL1 &= I2C_NACK_POS_CURRENT; // reset ACKPOS //==========
I2C_GenerateStart(I2Cx, ENABLE);
}
Every transfer continues only after both the master and the slave completion flags are set.
4. Send the 10-bit address in the interrupt. On EV5 the master interrupt sends the frame head and the high address bits, and then sends the low address byte on the I2C_EVT_MASTER_MODE_ADDRESS10_FLAG event.
case I2C_EVT_MASTER_MODE_FLAG: // EV5 start
if (flag_master_dir == TRANSMIT)
{
Memset(slave_rx_buf, 0, TEST_BUFFER_SIZE);
I2C_SendData(I2Cx, (I2C_FRAME_HEAD_TX | ((I2C_SLAVE_ADDR & 0x300) >> 7)));
tx_num = 0;
}
else if (flag_master_dir == RECEIVE)
{
Memset(master_rx_buf, 0, TEST_BUFFER_SIZE);
I2C_SendData(I2Cx, (I2C_FRAME_HEAD_RX | ((I2C_SLAVE_ADDR & 0x300) >> 7)));
rx_num = 0;
flag_master_dir = TRANSMIT;
}
break;
case I2C_EVT_MASTER_MODE_ADDRESS10_FLAG:
I2C_SendData(I2Cx, (I2C_SLAVE_ADDR & 0xFF));
break;
The frame head I2C_FRAME_HEAD_TX or I2C_FRAME_HEAD_RX distinguishes the transfer direction.
Corresponding source files: src/main.c
5.16.3.11 PMBUS_Master PMBus master
Example directory: projects/n32h49x_EVAL/examples/I2C/PMBUS_Master/
Initialize the control and alert pin lines→Initialize the I2C1 master→Read the temperature, operation, status word, and output voltage in turn→Write back the operation code and the output voltage and read them back for comparison→Summarize the seven test states→Print pass or fail
| Item | Source analysis |
|---|---|
| Function | Acts as a PMBus master on I2C1; it reads the temperature, operation, status word, and output voltage commands, verifies the write operations by reading them back, and finally prints the result. |
| Source files | src/main.c: test flow and result evaluation;src/PMBusMaster.c and inc/PMBusMaster.h: the command index table and the PMBusMaster transaction function;inc/I2CMaster.h: low-level master interface declarations;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 master, 7-bit addressing, own address 0x30;the PMBus target address slave_address is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up;the control line is PA7 and the alert line is PA8, configured as alternate-function push-pull; the command index table covers 120 entries and groups them by range into read byte, read word, write byte, and so on. The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main calls PMBusMaster_Init and then calls PMBusMaster once per command;PMBusMaster looks the command code up in PMBus_Commands and then decides the read/write group from the index range;the bytes read back are packed into a 16-bit word in main, and the written values are checked by reading back OPERATION and VOUT_COMMAND;a pass is printed only when none of the seven states is FAILED. |
| Main APIs | PMBusMaster_Init, PMBusMaster, I2C_ConfigInt, I2C_GenerateStart, I2C_SendData, I2C_RecvData, I2C_GenerateStop |
| Verification | Flash this example and the PMBUS_Slave example to two boards and connect the bus;after reset the UART prints the value read for each command and the final pass or fail message. |
| Use note | The command index is not the command code and must be converted through the PMBus_Commands table;when reading a word the low byte arrives first, so combine it as the high byte shifted left by eight bits then OR-ed with the low byte; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
PMBus organizes the commands into an index table: the transaction function first looks up the command code and then decides from the index range whether this call reads or writes and how many bytes it reads.
Data path: main → PMBusMaster_Init → PMBusMaster(command index, direction, data, buffer) for each command → look up PMBus_Commands → execute by group → read back and verify
1. Main-flow entry. After opening the log, main initializes the PMBus master and then issues the read tests command by command.
int main(void)
{
log_init();
log_info("\nThis is a pmbus master int demo!\r\n");
/* Initialize the I2C driver */
PMBusMaster_Init();
//reads
log_info("pmbus read test\r\n");
log_info("pmbus master read temperature test!\r\n");
Every command uses the same PMBusMaster(command index, direction, data, buffer) call form.
2. Command index table. PMBus_Commands maps a command index to the actual command code, which the transaction function puts into the first byte of the transmit buffer.
/* This is the PMBus command look-up table. Do not modify---------------------*/
const unsigned char PMBus_Commands[120] =
{
0x00, // dummy byte
0x19,0x78,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,0x80,0x81,0x82,0x98,
0x79,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,
0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,
0xA7,0xA8,0xA9,0x13,0x14,0x17,0x18,0x03,0x11,0x12,0x15,0x16,
0x00,0x01,0x02,0x04,0x10,0x20,0x3A,0x3D,0x41,0x45,0x47,0x49,
0x4C,0x50,0x54,0x56,0x5A,0x5C,0x63,0x69,0x21,0x22,0x23,0x24,
0x25,0x26,0x27,0x28,0x29,0x2A,0x31,0x32,0x33,0x35,0x36,0x37,
0x38,0x39,0x3B,0x3C,0x3E,0x3F,0x40,0x42,0x43,0x44,0x46,0x48,
0x4A,0x4B,0x4F,0x51,0x52,0x53,0x55,0x57,0x58,0x59,0x5B,0x5D,
0x5E,0x5F,0x60,0x61,0x62,0x64,0x65,0x66,0x68,0x6A,0x6B
};
Entry 0 of the table is a reserved byte; valid commands start at index 1.
3. Transaction entry. PMBusMaster prepares the transmit and receive buffers for one transaction and splits the command code and the 16-bit data into the transmit buffer.
uint8_t PMBusMaster(uint8_t PMBusMaster_CommandByte, uint8_t PMBusMaster_RWFlag, uint16_t PMBusMaster_Message,uint8_t *PMBusMaster_ReceivedValue)
{
uint8_t PMBusMaster_TransmitBuffer[4];
uint8_t PMBusMaster_ReceiveBuffer[5];
uint8_t PMBusMaster_RxCount = 0;
uint16_t PMBusMaster_Temp = 0;
uint8_t PMBusMaster_Index;
#if PEC //PEC variables
uint8_t PMBusMaster_CrcMsgSize = 0;
uint8_t PMBusMaster_CrcMsg[5];
uint8_t PMBusMaster_CrcMasterGenerated = 0;
uint8_t PMBusMaster_CrcSlaveGenerated = 0;
uint8_t PMBusMaster_Result = 0;
The low data byte goes into the buffer first and the high byte is held in a temporary variable.
4. Read-back verification. main reads the just-written value back with the same command, prints a failure when they differ, and finally summarizes the seven test states.
if(Vout_Command != 0xCCDD)
{
log_info("pmbus write VOUT_COMMAND cmd fail\r\n");
}
if(test_status1 == FAILED || test_status2 == FAILED || test_status3 == FAILED
|| test_status4 == FAILED || test_status5 == FAILED || test_status6 == FAILED
|| test_status7 == FAILED)
{
log_info("pmbus master int test fail!\r\n");
}
else
{
log_info("pmbus master int test pass!\r\n");
}
while (1)
{
}
}
A pass is printed only when every command returns other than FAILED.
Corresponding source files: src/main.c, src/PMBusMaster.c
5.16.3.12 PMBUS_Slave PMBus slave
Example directory: projects/n32h49x_EVAL/examples/I2C/PMBUS_Slave/
Initialize the control and alert pin lines→Initialize the I2C1 slave and enable the interrupts→Wait to be addressed by the master→Look up the index of the received command byte→Prepare or write register data by command group→The event interrupt advances the transfer and sets the completion flags
| Item | Source analysis |
|---|---|
| Function | Configures the board as a PMBus slave at address 0x10;the command byte from the master is grouped after an index look-up, so read commands load register values into the transmit buffer and write commands update the local registers. |
| Source files | src/main.c: main loop;src/PMBusSlave.c and inc/PMBusSlave.h: slave initialization, command decoding, and the event interrupt handler;inc/PMBus.h: command index and register structure definitions;readme.txt: function, environment, and usage;MDK-ARM/ and EWARM/: Keil and IAR projects. |
| Key configuration | I2C1 slave, 7-bit addressing, own address I2C_SLAVE_ADDR is 0x10;SCL is PB6 and SDA is PB7, alternate function GPIO_AF2, open-drain output with pull-up set through POD;ClkSpeed is 100 kHz, the event interrupt pre-emption priority is 4 and the error interrupt is 3;the control line is PA7 and the alert line is PA8, configured as alternate-function push-pull; the initial register values are temperature 0x12, operation 0x34, status word 0x5678, and output voltage command 0x90AB.The system clock is 240 MHz and the print UART is PA9 at 115200 baud. |
| Program and data path | main calls PMBusSlave_Init and then enters the PMBusSlave main loop;PMBusSlave_Init configures the control and alert pins and then calls i2c_slave_init;PMBusSlave_DecodeCommand looks the command byte up in the PMBus_Commands table and groups it by range;the event interrupt reads and writes PMBusSlave_ReceiveBuffer and PMBusSlave_TransmitBuffer by group and sets the transfer completion flags. |
| Main APIs | PMBusSlave_Init, PMBusSlave, PMBusSlave_DecodeCommand, I2C_ConfigInt, I2C_GetLastEvent, I2C_SendData, I2C_RecvData |
| Verification | Flash this example and the PMBUS_Master example to two boards and connect the bus;the slave UART prints this is a pmbus slave int demo and the values the master reads should match the slave’s initial registers. |
| Use note | The command byte must be looked up in the PMBus_Commands table, and index 0 means the master is probing whether a slave is present;write commands update only the registers this example supports, and unimplemented commands are ignored; Toolchains are KEIL MDK-ARM 5.34 and IAR EWARM 8.50.1. |
Core-code walkthrough and porting
The PMBus slave treats the received command byte as a table key: it first looks up the index and then decides from the index range how many bytes this frame reads or which register it writes.
Data path: main → PMBusSlave_Init → i2c_slave_init → the event interrupt receives the command → PMBusSlave_DecodeCommand groups it → the transfer buffers are prepared
1. Initialize the PMBus pins. PMBusSlave_Init clears the status registers, sets the control and alert lines as alternate-function push-pull, and finally initializes the I2C slave.
RCC_EnableAHB1PeriphClk(pmbus_gpio_clk, ENABLE);
// Control Line functionality GPIO0
GPIO_InitStruct(&pmbus_gpio);
pmbus_gpio.Pin = PMBUS_CONTRL_PIN ;
pmbus_gpio.GPIO_Pull = GPIO_PULL_UP;
pmbus_gpio.GPIO_Alternate = GPIO_NO_AF;
pmbus_gpio.GPIO_Mode = GPIO_MODE_AF_PP;
pmbus_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
GPIO_InitPeripheral(PMBUS_CONTRL_GPIO, &pmbus_gpio);
pmbus_gpio.Pin = PMBUS_ALERT_PIN;
pmbus_gpio.GPIO_Pull = GPIO_PULL_UP;
pmbus_gpio.GPIO_Alternate = GPIO_NO_AF;
pmbus_gpio.GPIO_Mode = GPIO_MODE_AF_PP;
pmbus_gpio.GPIO_Slew_Rate = GPIO_SLEW_RATE_SLOW;
GPIO_InitPeripheral(PMBUS_ALERT_GPIO, &pmbus_gpio);
The control and alert lines are both on GPIOA, at numbers 7 and 8.
2. Configure as an I2C slave. i2c_slave_init writes the own address into OwnAddr1 and enables the event, buffer, and error interrupts at once.
I2C_DeInit(I2Cx);
I2C_InitStruct(&i2cx_slave);
i2cx_slave.BusMode = I2C_BUSMODE_I2C;
i2cx_slave.FmDutyCycle = I2C_FMDUTYCYCLE_2;
i2cx_slave.OwnAddr1 = I2C_SLAVE_ADDR;
i2cx_slave.AckEnable = I2C_ACKEN;
i2cx_slave.AddrMode = I2C_ADDR_MODE_7BIT;
i2cx_slave.ClkSpeed = 100000; // 100000 100K
I2C_Init(I2Cx, &i2cx_slave);
// int enable
I2C_ConfigInt(I2Cx, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
NVIC_Configuration();
I2C_Enable(I2Cx, ENABLE);
return 0;
The address mode is 7-bit, the clock is 100 kHz, and automatic acknowledge is on.
3. Command decoding. PMBusSlave_DecodeCommand searches the command table for a match with the command byte, starting at index 1.
uint8_t PMBusSlave_DecodeCommand(uint8_t PMBusSlave_RxCommand)
{
uint8_t PMBusSlave_CommandGroup;
for(PMBusSlave_Index = 0; PMBusSlave_Index < 120; PMBusSlave_Index++)
{
if(PMBus_Commands[PMBusSlave_Index] == PMBusSlave_RxCommand)
break;
}
if(PMBusSlave_Index == 0)//dummy byte to check if slave is present
PMBusSlave_DummyCommand = 1;
if(PMBusSlave_Index > 0 && PMBusSlave_Index < 13) //read byte from slave device
PMBusSlave_CommandGroup = READBYTE;
A result index of 0 means the master is sending a probe command, which requires the slave to report its presence.
4. Main loop. After initialization main enters an endless loop in which PMBusSlave processes events and commands repeatedly.
int main(void)
{
log_init();
log_info("\nthis is a pmbus slave int demo\r\n");
/* Initialize the I2C slave driver */
PMBusSlave_Init();
// Application loop
while(1)
{
PMBusSlave();
}
}
A slave never starts a bus action by itself, so the loop only waits to be addressed.
Corresponding source files: src/main.c, src/PMBusSlave.c
5.16.4 Notes and troubleshooting
| Scenario | Check & action | Source |
|---|---|---|
| FIFO enabling and interrupt and DMA configuration | When FIFOEN is set, BUFINTEN must be disabled; when DMA is used, DMAFIFOEN must be enabled and DMAEN must be disabled. |
User manual |
| DMA transfer and buffer interrupt | Do not set the BUFINTEN bit of I2C_CTRL2 when DMA is used to transmit or receive. | User manual |
| Master transmit with 7-bit addressing | In 7-bit addressing master transmit mode, the slave address must not be configured as 0xF0, 0xF2, 0xF4, or 0xF6. | User manual |
| Reading FIFO receive data | The received data must be read out of the FIFO before the peripheral is disabled; when PECEN is set, the last byte received is the check data. |
User manual |
| Software reset and bus release | Before SWRESET is set, make sure the I2C pins are released and the bus is free; this bit is useful when the BUSY bit is 1 and no stop condition has been detected on the bus. |
User manual |
| Control register write timing | After STOPGEN, STARTGEN, or the PEC bit is set, do not write to I2C_CTRL1 until the hardware clears the bit. | User manual |
| Register access width | The I2C peripheral registers can be accessed as half-words (16 bits) or words (32 bits). | User manual |