5.16 I2C 使用说明
N32H49x 标准外设库的 I2C 驱动支持 7 位与 10 位地址、广播呼叫、SMBus/PMBus、PEC 校验、模拟与数字滤波、FIFO 与超时检测。本章说明该驱动的文件组成、53 个公开函数,以及 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、PMBUS_Slave 12 个示例。总线时序、时钟同步与仲裁、错误条件与寄存器位定义请配合用户手册的“内部集成电路总线(I2C)”章节阅读。
5.16.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_i2c.h |
定义 I2C_InitType 结构体与总线模式、占空比、寻址模式、应答、PEC、中断、DMA、FIFO、滤波与超时等宏,并按复位与初始化、起止条件与地址、中断与 DMA、数据收发、PEC 与滤波、FIFO、超时与状态查询的顺序声明 53 个公开函数 |
firmware/n32h49x_std_periph_driver/src/n32h49x_i2c.c |
实现上述 53 个函数:寄存器复位与初始化、起止条件与地址发送、中断与 DMA 使能、数据与 FIFO 收发、PEC 与滤波器配置、超时阈值与状态查询 |
projects/n32h49x_EVAL/examples/I2C/ |
I2C 示例根目录,含 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、PMBUS_Slave 12 个示例 |
projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/ |
该示例的工程目录组成:inc/、src/、readme.txt、MDK-ARM/、EWARM/ |
projects/n32h49x_EVAL/bsp/ |
各示例共用的板级支持文件:评估板 LED 与按键定义、毫秒延时函数、日志打印接口 |
5.16.2 API 参考
以下 53 个公开函数按 n32h49x_i2c.h 的声明顺序分为 15 组;每个条目给出功能摘要与 n32h49x_i2c.c 中该函数正上方的注释原文。
复位与初始化
I2C_DeInit、I2C_Init、I2C_InitStruct、I2C_Enable
起止条件与应答
I2C_GenerateStart、I2C_GenerateStop、I2C_ConfigAck、I2C_SendAddr7bit
从机地址与广播
I2C_ConfigOwnAddr2、I2C_EnableDualAddr、I2C_EnableGeneralCall
中断、DMA 与软复位
I2C_ConfigInt、I2C_EnableDMA、I2C_EnableDmaLastSend、I2C_EnableSoftwareReset
数据收发
I2C_SendData、I2C_RecvData
NACK/PEC 位置与 SMBus 提醒
I2C_ConfigNackLocation、I2C_ConfigPecLocation、I2C_ConfigSmbusAlert
PEC 与 ARP
I2C_SendPEC、I2C_ComputePec、I2C_GetPec、I2C_EnableArp
时钟延展与快速模式
I2C_EnableExtendClk、I2C_ConfigFastModeDutyCycle
数字与模拟滤波
I2C_SetSDADigitalFilterWidth、I2C_SetSCLDigitalFilterWidth、I2C_SetSDAAnalogFilterWidth、I2C_EnableSDAAnalogFilter、I2C_SetSCLAnalogFilterWidth、I2C_EnableSCLAnalogFilter
标志与中断清除
I2C_ClrFlag、I2C_ClrIntPendingBit
FIFO 管理
I2C_ClrFIFO、I2C_EnableFIFO、I2C_EnableDMAFIFO、I2C_SetTxFifoThreshold、I2C_SetRxFifoThreshold
字节数控制
I2C_EnableByteNum、I2C_SetMasterReceivedDataBytesNum、I2C_SetByteNumLastStartStop
FIFO 数据收发
I2C_SendFIFOData、I2C_RecvFIFOData
超时检测
I2C_SetTimeoutLowLimit、I2C_SetTimeoutHighLimit、I2C_EnableTimeoutDetect
状态查询
I2C_GetRegister、I2C_GetIntStatus、I2C_CheckFifoEvent、I2C_CheckEvent、I2C_GetLastEvent、I2C_GetFlag
void I2C_DeInit(I2C_Module* I2Cx)将 I2Cx 外设寄存器复位为默认复位值。
name I2C_DeInit.
fun Deinitializes the I2Cx peripheral registers to their default reset values.
param I2Cx :
- I2C1
- I2C2
- I2C3
- I2C4
return none
void I2C_Init(I2C_Module* I2Cx,I2C_InitType* I2C_InitStruct)按 I2C_InitStruct 中的参数初始化 I2Cx 外设。
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)为 I2C_InitStruct 的每个成员赋默认值。
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)使能或关闭指定的 I2C 外设。
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)产生 I2Cx 通信的 START 条件。
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)产生 I2Cx 通信的 STOP 条件。
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)使能或关闭指定的 I2C 应答功能。
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)发送地址字节以选中从设备。
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)配置指定的 I2C 自身地址 2。
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)使能或关闭指定的 I2C 双地址模式。
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)使能或关闭指定的 I2C 广播呼叫功能。
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)使能或关闭指定的 I2C 中断。
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)通过 I2Cx 外设发送一个数据字节。
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)返回 I2Cx 外设最近一次接收到的数据。
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)使能或关闭指定的 I2C DMA 请求。
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)指定下一次 DMA 传输是否为最后一次。
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)使能或关闭指定的 I2C 软件复位。
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)在主接收模式下选择指定的 I2C NACK 位置。
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)选择指定的 I2C PEC 位置。
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)为指定的 I2C 驱动 SMBusAlert 引脚为高电平或低电平。
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)使能或关闭指定的 I2C PEC 传输。
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)使能或关闭传输字节的 PEC 值计算。
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)使能或关闭传输字节的 PEC 值计算。
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)使能或关闭指定的 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)使能或关闭指定的 I2C 时钟延展。
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)选择指定的 I2C 快速模式占空比。
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 数字滤波宽度选择。
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
使用提示: 源码注释的 name 字段写作 I2C_Digital_Filter_Width_Set.,与函数名 I2C_SetSDADigitalFilterWidth 不一致,按函数名调用即可。
void I2C_SetSCLDigitalFilterWidth(I2C_Module* I2Cx, uint32_t width)SCL 数字滤波宽度选择。
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 模拟滤波宽度选择。
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)使能或关闭 SDA 模拟滤波器。
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 模拟滤波宽度选择。
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)使能或关闭 SCL 模拟滤波器。
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)清除 I2Cx 的挂起标志。
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)清除 I2Cx 的中断挂起位。
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)清空 I2C 的 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)使能或关闭 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)使能或关闭 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)设置 I2C 的发送 FIFO 阈值。
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)设置 I2C 的接收 FIFO 阈值。
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)使能或关闭指定的 I2C 外设字节数功能。
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)设置 I2C 外设主接收将要接收的数据字节数。
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)配置主机发送完所需数据字节后的最终状态。
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)设置 FIFO 数据量。
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)返回 FIFO 数据。
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)超时低阈值选择。
name I2C_SetTimeoutLowLimit.
fun timeout low threshold selection.
param I2Cx :
- I2C1
- I2C2
param limit:
- I2C_TIMEOUT_LOWLIMIT_25MS
- I2C_TIMEOUT_LOWLIMIT_100MS
- I2C_TIMEOUT_LOWLIMIT_1S
- I2C_TIMEOUT_LOWLIMIT_4S
return none
void I2C_SetTimeoutHighLimit(I2C_Module* I2Cx, uint32_t limit)超时高阈值选择。
name I2C_SetTimeoutHighLimit.
fun timeout high threshold selection.
param I2Cx :
- I2C1
- I2C2
param limit:
- I2C_TIMEOUT_HIGHLIMIT_256US
- I2C_TIMEOUT_HIGHLIMIT_512US
- I2C_TIMEOUT_HIGHLIMIT_1MS
- I2C_TIMEOUT_HIGHLIMIT_128US
return none
void I2C_EnableTimeoutDetect(I2C_Module* I2Cx, uint32_t timeout_type, FunctionalState Cmd)使能指定的 I2C 超时检测功能。
name I2C_EnableTimeout.
fun Enables the specified I2C timeout detection function .
param I2Cx (The input parameters must be the following values):
- I2C1
- I2C2
param timeout_type (The input parameters must be the following values):
- I2C_TIMEOUT_LOW low timeout function enable
- I2C_TIMEOUT_HIGH high timeout function enable
param Cmd:
- ENABLE
- DISABLE
note This function can be disabled in I2C mode when an timeout error is not required.
return none
使用提示: 源码注释的 name 字段写作 I2C_EnableTimeout.,与函数名 I2C_EnableTimeoutDetect 不一致,按函数名调用即可。
uint32_t I2C_GetRegister(I2C_Module* I2Cx, uint8_t I2C_Register)读取指定的 I2C 寄存器并返回其值。
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)检查指定的 I2C 中断是否发生。
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)检查 I2Cx 最后一次 FIFO 事件是否等于传入的参数。
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)检查 I2Cx 最后一次事件是否等于传入的参数。
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)返回 I2Cx 的最后一次事件。
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)检查指定的 I2C 标志是否置位。
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 示例说明
本节说明 I2C 的 12 个示例:EEPROM_Polling、EEPROM_Int、EEPROM_DMA 用 AT24C02 演示主机的轮询、中断与 DMA 读写;I2C_Master、I2C_Master_Int、I2C_Master_FIFO、I2C_Slave、I2C_Slave_Int、I2C_Slave_FIFO 演示 7 位地址下单主机单从机的收发;I2C_10bit 演示 10 位地址;PMBUS_Master、PMBUS_Slave 演示 PMBus 的 PEC 通信。
5.16.3.1 EEPROM_Polling 轮询读写 AT24C02
工程目录: projects/n32h49x_EVAL/examples/I2C/EEPROM_Polling/
初始化 I2C1 与 PB6、PB7 引脚→主机轮询发送器件地址与页内地址→按页写入测试数据并等待器件就绪→重新发送起始条件与读地址→读回数据并与写入缓冲比较→串口打印测试结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 I2C1 作主机,以轮询方式读写板外 AT24C02 EEPROM; 先写入 256 字节数据,再读回并与写入缓冲逐字节比较。 |
| 源码组成 | src/main.c:主流程与缓冲比较;src/i2c_eeprom.c、inc/i2c_eeprom.h:EEPROM 读写驱动、配置宏与超时回调;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,器件地址 EEPROM_ADDRESS 为 0xA0,ClkSpeed 为 100 kHz;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并上拉;页大小 I2C_PageSize 为 8 字节,测试长度 TEST_EEPROM_SIZE 为 256 字节。系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 调用 I2C_EE_Init 完成引脚与外设初始化,并把 tx_buf 填成 0 到 255;I2C_EE_WriteBuffer 把数据按页拆给 I2C_EE_WriteOnePage 与 I2C_EE_PageWrite;I2C_EE_ReadBuffer 用两次起始条件完成“写页内地址 + 读数据”;Buffercmp 比较 tx_buf 与 rx_buf 后打印结果。 |
| 主要 API | I2C_InitStruct、I2C_Init、I2C_GenerateStart、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_CheckEvent、I2C_GenerateStop |
| 验证方法 | 连接好 EEPROM 后编译下载并复位运行; 串口依次打印初始化、写入与读取提示,最后输出测试通过或失败。 |
| 使用提示 | 板外器件为 AT24C02,共 32 页、每页 8 字节; 读写长度超过一页时器件内部地址会自动回卷。 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用轮询方式读写板外 EEPROM:主机先按页写入 256 字节,再读回并比较。
数据路径: main → I2C_EE_Init 配置 I2C1 与 PB6、PB7 → I2C_EE_WriteBuffer 拆页写入 → I2C_EE_PageWrite 发送起始、器件地址、页内地址与数据 → I2C_EE_ReadBuffer 用两次起始条件读回 → Buffercmp 比较 → 串口打印
1. 初始化外设与引脚。 I2C_EE_Init 打开 I2C1 与 GPIOB 时钟,把 PB6、PB7 配为复用开漏并上拉,再按 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);
这份配置被轮询、中断与 DMA 三个示例共用,区别只在数据搬运方式。
2. 按页写入数据。 I2C_EE_PageWrite 在 EV8 事件后进入字节循环,每写一字节都等待一次数据发送完成事件。
/** 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))
{
AT24C02 每页 8 字节,跨页拆分由上层完成,这里不再判断页边界。
3. 按长度读回数据。 I2C_EE_ReadBuffer 依据读取长度设置应答与 ACKPOS:读 1 字节时不发应答,读 2 字节时把 ACKPOS 置位。
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);
}
起始条件与页内地址的发送在分支之前完成,此处只处理数据阶段的应答与时序。
对应源码: src/i2c_eeprom.c
5.16.3.2 EEPROM_Int 中断读写 AT24C02
工程目录: projects/n32h49x_EVAL/examples/I2C/EEPROM_Int/
初始化 I2C1 与外设中断→主机发出起始与器件地址→中断按事件推进写入→器件内部写周期内重试起始→中断按事件推进读取→比较收发缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 I2C1 作主机,以中断方式读写板外 AT24C02 EEPROM; 写入 256 字节后再读回比较,收发过程由中断服务函数推进。 |
| 源码组成 | src/main.c:主流程与缓冲比较;src/i2c_eeprom.c、inc/i2c_eeprom.h:中断读写驱动、事件与错误处理;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,器件地址 EEPROM_ADDRESS 为 0xA0,ClkSpeed 为 100 kHz;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并上拉;使能 I2C_INT_EVENT、I2C_INT_BUF 与 I2C_INT_ERR,事件中断抢占优先级 0、子优先级 1,错误中断抢占优先级 0、子优先级 0。系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 调用 I2C_EE_Init 后进入 I2C_EE_WriteBuffer 与 I2C_EE_ReadBuffer;事件中断按 EV5、EV6、EV7、EV8 逐个推进状态机, i2c_comm_state 变为完成或退出后主流程继续;Buffercmp 比较 tx_buf 与 rx_buf 并打印结果。 |
| 主要 API | I2C_ConfigInt、I2C_GetLastEvent、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_ConfigAck、I2C_GenerateStart、I2C_GenerateStop |
| 验证方法 | 连接好 EEPROM 后编译下载并复位运行; 串口输出写入与读取提示,最后给出测试通过或失败。 |
| 使用提示 | 板外器件为 AT24C02,共 32 页、每页 8 字节; 读写长度超过一页时器件内部地址会自动回卷。 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把 EEPROM 读写改为中断方式:事件与错误各有一个中断服务函数,主流程只负责发起与收尾。
数据路径: main → I2C_EE_Init 使能事件与错误中断 → I2C1_EV_IRQHandler 按 EV5、EV6、EV7、EV8 推进收发 → I2C1_ER_IRQHandler 处理应答失败与总线错误 → i2c_comm_state 上报完成 → Buffercmp 比较缓冲
1. 初始化并打开中断。 I2C_EE_Init 配好外设后设置 NVIC 优先级分组,并分别使能 I2C1 的事件与错误中断。
/** 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;
事件与错误中断使用同一优先级分组,抢占优先级都为 0,靠子优先级区分先后。
2. 接收方向用两个中断完成“写地址 + 读数据”。 收到 EV6 后,I2C1_EV_IRQHandler 依读取长度决定是否关闭应答或置位 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;
读 1 字节时先关应答再发停止;读 2 字节时置 ACKPOS 并关应答,保证应答失败落在倒数第二个字节之后。
3. 发送方向的字节搬运。 EV8 时若本帧只发一个内部地址就发停止并结束,否则继续送 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 用来区分“本次起始是为探测器件是否忙”还是真正的数据传输,两种情况下停止条件的处理不同。
4. 错误中断收尾。 I2C1_ER_IRQHandler 在读操作失败或总线错误时发停止并把状态置为退出。
void I2C1_ER_IRQHandler(void)
{
if (I2C_GetFlag(I2C1, I2C_FLAG_ACKFAIL))
{
if (check_begin) /// EEPROM write busy
{
I2C_GenerateStart(I2C1, ENABLE);
}
else if (I2C1->STS2 & 0x01) /// real fail
{
I2C_GenerateStop(I2C1, ENABLE);
i2c_comm_state = COMM_EXIT;
}
I2C_ClrFlag(I2C1, I2C_FLAG_ACKFAIL);
}
if (I2C_GetFlag(I2C1, I2C_FLAG_BUSERR))
{
if (I2C1->STS2 & 0x01)
{
I2C_GenerateStop(I2C1, ENABLE);
i2c_comm_state = COMM_EXIT;
}
I2C_ClrFlag(I2C1, I2C_FLAG_BUSERR);
}
}
应答失败若发生在 EEPROM 内部写周期内,这里重新发起始等待器件就绪,而不是直接判为通信失败。
对应源码: src/i2c_eeprom.c
5.16.3.3 EEPROM_DMA DMA 读写 AT24C02
工程目录: projects/n32h49x_EVAL/examples/I2C/EEPROM_DMA/
初始化 I2C1、GPIO 与 DMA 时钟→主机发送起始、器件地址与页内地址→配置发送 DMA 通道并启动搬运→等待字节传输完成标志后停止并等待器件就绪→配置接收 DMA 通道读回数据→比较收发缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 用 I2C1 作主机,以 DMA 方式读写板外 AT24C02 EEPROM; 写入 256 字节后再读回比较,数据搬运由 DMA 通道完成。 |
| 源码组成 | src/main.c:主流程与缓冲比较;src/i2c_eeprom.c、inc/i2c_eeprom.h:DMA 读写驱动、通道重映射宏与超时回调;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,器件地址 EEPROM_ADDRESS 为 0xA0,ClkSpeed 为 100 kHz;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并上拉;发送用 DMA1_CH6、重映射为 I2C_DMA_TX_Remap,接收用 DMA1_CH7、重映射为 I2C_DMA_RX_Remap;页大小 8 字节,测试长度 256 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 调用 I2C_EE_Init 后进入 I2C_EE_WriteBuffer 与 I2C_EE_ReadBuffer;发送时 I2C_EE_PageWrite 配置 DMA1_CH6 并置 I2C_EnableDMA,以 I2C_FLAG_BSF 判定完成;接收时 I2C_EE_ReadBuffer 配置 DMA1_CH7,由通道把 DAT 搬到用户缓冲;Buffercmp 比较 tx_buf 与 rx_buf 并打印结果。 |
| 主要 API | I2C_EnableDMA、I2C_GenerateStart、I2C_SendAddr7bit、I2C_SendData、I2C_CheckEvent、I2C_GenerateStop、RCC_EnableAHBPeriphClk |
| 验证方法 | 连接好 EEPROM 后编译下载并复位运行; 串口打印初始化、写入与读取提示,最后输出测试通过或失败。 |
| 使用提示 | 板外器件为 AT24C02,共 32 页、每页 8 字节; 读写长度超过一页时器件内部地址会自动回卷; 使用 DMA 收发时不要打开缓冲区中断。 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例把 EEPROM 的字节搬运交给 DMA:主机只负责协议打包,数据由通道在数据寄存器与内存之间搬移。
数据路径: main → I2C_EE_Init 打开 DMA1 时钟 → I2C_EE_PageWrite 配置 DMA1_CH6 并启动 → I2C_FLAG_BSF 判定完成 → I2C_EE_ReadBuffer 配置 DMA1_CH7 → Buffercmp 比较
1. 打开 DMA 时钟。 I2C_EE_Init 在配好 I2C1 之后补上 DMA1 时钟,为后续通道使用做准备。
/** 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);
发送与接收分别使用 DMA1_CH6 与 DMA1_CH7。
2. 配置发送通道。 I2C_EE_PageWrite 用 DMA_StructInit 取默认值,逐项指定外设地址、内存地址、传输方向与数据宽度,再申请通道重映射。
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);
外设地址固定为 I2Cx->DAT,传输方向为存储器到外设,缓冲长度取自入参。
3. 启动搬运并等待完成。 页内地址发完后打开 I2C 的 DMA 请求与通道,以字节传输完成标志判定结束,再关闭 DMA 并等待器件就绪。
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();
停止条件发出后立即关闭 DMA 请求与通道,避免残留请求影响下一次传输。
4. 配置接收通道。 I2C_EE_ReadBuffer 在读取长度大于 1 时配置 DMA1_CH7,方向改为外设到存储器。
/** 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);
长度为 1 时仍走查询路径,不启用 DMA。
对应源码: src/i2c_eeprom.c
5.16.3.4 I2C_Master 主机收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Master/
初始化 I2C1 与 PB6、PB7 引脚→主机发送起始与从机地址→按 200 字节长度连续写入数据→重新发起起始并发送读地址→按长度分支接收数据并生成停止→比较收发缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 在 I2C1 上实现时序完整的主机收发函数; 向地址 0x10 的从机写入 200 字节后读回,比较一致则打印测试通过。 |
| 源码组成 | src/main.c:主机初始化、收发函数、主流程与缓冲比较,并附总线时钟恢复、外设复位与超时回调;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,自身地址 I2C_MASTER_ADDR 为 0x30,目标从机地址 I2C_SLAVE_ADDR 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,占空比 I2C_FMDUTYCYCLE_2;测试缓冲 200 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 先把 tx_buf 填成 0 到 199;i2c_master_send 依次等待总线空闲、发起始、发地址,再逐字节发送并等待 I2C_FLAG_BSF;i2c_master_recv 按 1 字节、2 字节与多字节三种长度分支处理,末字节前关闭应答;Buffercmp 比较后打印结果,清空 rx_buf 再延时重试。 |
| 主要 API | I2C_GenerateStart、I2C_SendAddr7bit、I2C_SendData、I2C_CheckEvent、I2C_RecvData、I2C_ConfigAck、I2C_GenerateStop |
| 验证方法 | 把本示例与 I2C_Slave 示例分别烧录到两块开发板,用杜邦线对接 SCL、SDA 并共地;复位运行后串口打印 this is a i2c master demo 与测试结果。 |
| 使用提示 | 这是纯主机示例,总线上必须存在地址为 0x10 的从机应答;接收按长度区分分支,最后两个字节要在停止条件发出前读完; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
主机侧完全靠标志查询推进时序:每一步都带超时计数,超时即调用统一回调并返回错误码。
数据路径: main 填充 tx_buf → i2c_master_send → i2c_master_recv → Buffercmp → 打印
1. 初始化主机。 i2c_master_init 打开 I2C1 与 GPIOB 时钟,把 PB6、PB7 配成开漏复用,再填入主机参数。
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 取 I2C_MASTER_ADDR,ClkSpeed 设为 100 kHz,最后使能外设。
2. 发起起始条件。 Comm_Flag 标记一次事务所处阶段,起始条件只在空闲状态下发出,避免重复。
if (Comm_Flag == C_READY)
{
Comm_Flag = C_START_BIT;
I2C_GenerateStart(I2Cx, ENABLE);
}
紧随其后用 I2C_SendAddr7bit 送出从机地址,并等待 I2C_EVT_MASTER_TXMODE_FLAG。
3. 接收末字节。 长度为 1 时先关应答、读走 STS1 与 STS2 释放 ADDR,再提前发出停止条件。
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);
}
清 STS1、STS2 是释放 ADDR 标志的固定写法。
4. 主流程。 main 把发送缓冲填成递增序列,之后在同一总线上先写后读,比较一致才算通过。
while (1)
{
for(; ;)
{
/* First write in the memory followed by a read of the written data --------*/
/* Write data*/
if(i2c_master_send(tx_buf, TEST_BUFFER_SIZE))
{
break; /* send fail, resend*/
}
/* Read data */
if(i2c_master_recv(rx_buf, TEST_BUFFER_SIZE))
{
break; /* receive fail, resend*/
}
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_BUFFER_SIZE);
if (test_status == PASSED)
{
log_info("write and read data are same, i2c master test pass\r\n");
}
else
{
log_info("write and read data are different, i2c master test fail\r\n");
}
for (i = 0; i < TEST_BUFFER_SIZE; i++)
{
rx_buf[i] = 0;
}
systick_delay_ms(50);
}
}
任一方向返回非零就跳出本轮重发;通过后清空 rx_buf 并延时 50 ms 再测一次。
对应源码: src/main.c
5.16.3.5 I2C_Master_Int 主机中断收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Master_Int/
初始化 I2C1、GPIO 与 NVIC→打开事件、缓冲与错误中断→主流程发起始并等待发送完成标志→事件中断按 EV5 到 EV8 推进发送→中断中重发起始并按 EV7 逐字节接收→比较收发缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 用中断而非查询推进 I2C1 主机时序; 发送与接收都在 I2Cx_EV_IRQHandler 内按事件码分支完成,主流程只等待完成标志。 |
| 源码组成 | src/main.c:初始化、中断服务函数、主流程与缓冲比较,并含 Memset 等辅助函数;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,自身地址 I2C_MASTER_ADDR 为 0x30,目标从机地址 I2C_SLAVE_ADDR 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,事件用 I2C1_EV_IRQn 抢占优先级 4,错误用 I2C1_ER_IRQn 抢占优先级 3;测试缓冲 100 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 先把 tx_buf 填成递增序列并发起始,随后阻塞等待 flag_master_send_finish;发送完成后重发起始,等待 flag_master_recv_finish;事件中断里 EV5 按方向发送地址,EV6 至 EV8 推进发送,EV7 逐字节接收并在倒数第二个字节关应答、发停止; Buffercmp 比较后打印结果。 |
| 主要 API | I2C_ConfigInt、NVIC_Init、I2C_GenerateStart、I2C_SendAddr7bit、I2C_SendData、I2C_RecvData、I2C_GetLastEvent |
| 验证方法 | 把本示例与 I2C_Slave 示例分别烧录到两块开发板并对接总线;复位运行后串口打印 This is a i2c master int demo! 与测试结果。 |
| 使用提示 | 主机侧时序都在中断里推进,主流程用完成标志同步; 错误中断只处理应答失败,清 I2C_FLAG_ACKFAIL 后发停止条件;开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
中断版把事件码当作状态机输入:所有收发动作集中在事件中断里,主流程只负责发起与等待。
数据路径: main 发起始 → 事件中断 EV5 发地址 → EV6 与 EV8 推进发送 → EV7 逐字节接收 → 置完成标志 → Buffercmp
1. 配置中断优先级。 事件中断抢占优先级为 4,错误中断为 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);
两个通道都使能,子优先级都取 1。
2. 使能外设中断。 初始化末尾一次性打开事件、缓冲与错误三类中断,再使能 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、I2C_INT_ERR 分别对应事件、缓冲与错误请求。
3. 主流程同步。 主流程发出起始后阻塞等待完成标志,超时则调用统一回调。
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);
}
}
发送与接收各有一个完成标志,中断置位后主流程继续向下走。
4. 中断内寻址。 事件中断先读 I2C_GetLastEvent,EV5 时按当前传输方向发送读地址或写地址。
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 区分本轮是写还是读,读方向还会先清空接收缓冲并复位计数。
5. 中断内接收。 EV7 每来一次读走一个字节,读到倒数第二个字节时关应答并发出停止条件。
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;
最后一个字节读完后置起接收完成标志,交由主流程比较数据。
对应源码: src/main.c
5.16.3.6 I2C_Master_FIFO FIFO 主机收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Master_FIFO/
初始化 I2C1 与 PB6、PB7 引脚→使能 FIFO 并设置收发阈值→按字节数配置主机接收→以 FIFO 事件推进发送与接收→比较收发缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 在 I2C1 上启用收发 FIFO 完成主机发送与接收; 收发函数以 FIFO 事件而非单字节事件推进,向地址 0x10 的从机收发 10 字节。 |
| 源码组成 | src/main.c:初始化、FIFO 收发函数、主流程与时钟恢复等容错代码;inc/main.h:从机地址、缓冲长度与 FIFO 阈值等宏;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,目标从机地址 I2Cx_SLAVE_ADDRESS7 为 0x10,自身地址为 0x30;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,发送阈值 FIFO_HALF_EMPTY_THR 与接收阈值 FIFO_HALF_FULL_THR 都取 4;测试缓冲 10 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 初始化后把 tx_buf 填成递增序列,调用 i2c_master_fifo_send 发送,延时 2 ms 再调用 i2c_master_fifo_recv 接收;接收先按 I2C_SetMasterReceivedDataBytesNum 设定字节数,再以 FIFO 事件推进搬运;Buffercmp 比较后打印结果。 |
| 主要 API | I2C_EnableFIFO、I2C_SetTxFifoThreshold、I2C_SetRxFifoThreshold、I2C_CheckFifoEvent、I2C_RecvFIFOData、I2C_SetMasterReceivedDataBytesNum、I2C_ClrFIFO |
| 验证方法 | 把本示例与从机示例分别烧录到两块开发板并对接 SCL、SDA 与共地; 复位运行后串口打印 this is a i2c master fifo demo 与测试结果。 |
| 使用提示 | FIFO 收发依赖阈值事件,接收前要先设定本次字节数; 超时回调按 COMM_RECOVER_MODE 选择软件复位或 RCC 复位模块;开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
FIFO 版把单字节事件升级为阈值事件:一次事件可搬多个字节,长传输时能减少等待次数。
数据路径: main → i2c_master_fifo_send → i2c_master_fifo_recv → Buffercmp → 打印
1. 打开 FIFO 并设置阈值。 初始化在使能 I2C1 之后开启收发 FIFO,并分别设置发送与接收阈值。
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);
发送阈值取 FIFO_HALF_EMPTY_THR,接收阈值取 FIFO_HALF_FULL_THR,本示例都为 4。
2. 主流程。 main 填好发送缓冲后先发后收,中间留 2 ms 让从机就绪。
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)
{
}
}
发送与接收各由一个函数完成,比较结果一致就打印通过信息。
3. 超时恢复。 任何一步等待超时都会进入统一回调,按 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
}
默认配置走模块级 RCC 复位,复位后重新初始化 I2C 并打印提示。
对应源码: src/main.c
5.16.3.7 I2C_Slave 从机收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Slave/
初始化 I2C1 并配置从机地址→等待主机发来的匹配地址→接收数据写入缓冲直到停止条件→主机转为读方向后按字节发送缓冲→清除缓冲并等待下一轮→循环打印测试结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 把 I2C1 配置成地址 0x10 的从机;先接收主机写入的 256 字节到缓冲,再在方向反转后把缓冲回送给主机。 |
| 源码组成 | src/main.c:从机初始化、收发函数、主流程与超时回调;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 从机、7 位地址,本机地址 I2C_SLAVE_ADDR 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,占空比 I2C_FMDUTYCYCLE_2;收发缓冲 256 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 初始化后进入循环,先用 i2c_slave_recv 收齐一整帧到 data_buf;i2c_slave_recv 等到 I2C_EVT_SLAVE_DATA_RECVD 就取走数据并计数,收到 I2C_EVT_SLAVE_STOP_RECVD 才返回;i2c_slave_send 等到 I2C_EVT_SLAVE_SEND_ADDR_MATCHED 后逐字节发送,遇停止或应答缺失即结束;每轮结束清空缓冲并打印通过信息。 |
| 主要 API | I2C_CheckEvent、I2C_GetLastEvent、I2C_SendData、I2C_RecvData、I2C_ClrFlag、I2C_Enable、I2C_Init |
| 验证方法 | 把本示例烧录到从机板,主机端运行 I2C_Master 等示例并与之对接;从机串口打印 this is a i2c slave test demo,每完成一轮收发打印通过信息。 |
| 使用提示 | 从机地址必须与主机寻址的地址一致,本示例为 0x10;收发都以停止条件或地址匹配作为帧边界,中间无需额外握手; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
从机不能主动发起时序,只能等待主机寻址;代码用事件码轮询把一次完整事务拆成若干状态。
数据路径: main → i2c_slave_recv 收齐一帧 → i2c_slave_send 回送 → 清缓冲 → 循环
1. 配置为从机。 初始化把本机地址写入 OwnAddr1,其余总线参数与主机侧一致。
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 取 7 位,AckEnable 打开自动应答,ClkSpeed 为 100 kHz。
2. 从机发送。 发送函数先等地址匹配,再在循环里按事件逐字节送出缓冲。
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;
}
收到停止条件或应答缺失时清除应答失败标志并退出,等待下一帧。
3. 从机接收。 接收函数每来一个数据事件就取走一个字节,直到主机发出停止条件。
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 记录本轮收到的字节数,供主流程判断是否完整。
对应源码: src/main.c
5.16.3.8 I2C_Slave_Int 从机中断收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Slave_Int/
初始化 I2C1、GPIO 与 NVIC→打开事件、缓冲与错误中断→主流程等待接收完成标志→事件中断按从机事件码存取数据→停止条件到来后置完成标志→主流程再等待发送完成并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 用中断处理 I2C1 从机事务; 接收与发送都在 I2Cx_EV_IRQHandler 内按事件码完成,主流程只等待两个完成标志。 |
| 源码组成 | src/main.c:初始化、中断服务函数、主流程与超时回调;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 从机、7 位地址,本机地址 I2C_SLAVE_ADDR 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,事件用 I2C1_EV_IRQn 抢占优先级 4,错误用 I2C1_ER_IRQn 抢占优先级 3;收发缓冲 100 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 初始化后阻塞等待 flag_slave_recv_finish,中断收满一帧后置位;随后等待 flag_slave_send_finish,中断发完后置位;中断里按 I2C_EVT_SLAVE_RECV_ADDR_MATCHED、I2C_EVT_SLAVE_DATA_RECVD 等事件存取 data_buf;两段等待结束后分别打印长度信息。 |
| 主要 API | I2C_ConfigInt、NVIC_Init、I2C_GetLastEvent、I2C_Enable、I2C_Init、I2C_CheckEvent、I2C_ClrFlag |
| 验证方法 | 把本示例烧录到从机板,主机端运行 I2C_Master_Int 等示例并与之对接;从机串口打印 this is a i2c slave int demo 与收发长度。 |
| 使用提示 | 从机中断在停止条件到来且已收到数据时置接收完成标志; 错误中断单独处理应答失败,主流程不参与错误分支; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
从机中断把每一类事件映射成一次数据搬运或一次状态记录,主流程只关心两个完成标志。
数据路径: main 等待接收标志 → 事件中断按 I2C_EVT_SLAVE_* 存取 data_buf → 置完成标志 → main 打印
1. 配置中断优先级。 从机与主机示例一样,事件中断抢占优先级为 4,错误中断为 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);
}
错误中断优先级更高,保证总线异常能被优先处理。
2. 使能外设中断。 初始化末尾打开事件、缓冲与错误三类中断,再使能 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 一次性配置三类请求,随后调用 NVIC_Configuration。
3. 主流程同步。 主流程依次等待接收与发送完成标志,超时由回调统一接管。
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)
{;}
两个标志由中断置位,主流程读完长度后清零并再次等待。
4. 中断内搬运。 从机模式下按事件码取数或送数,收到停止条件才置接收完成标志。
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 既是接收计数,也是发送时的读指针。
对应源码: src/main.c
5.16.3.9 I2C_Slave_FIFO FIFO 从机收发
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_Slave_FIFO/
初始化 I2C1 并配置从机地址→使能收发 FIFO 与阈值→从机等待地址匹配→以 FIFO 事件接收数据到缓冲→停止条件后方向反转并回送缓冲→完成后进入空转等待复位
| 项目 | 源码解读 |
|---|---|
| 功能 | 把 I2C1 配置成地址 0x10 的从机并启用收发 FIFO;先以 FIFO 事件接收主机写入的 10 字节,方向反转后再把缓冲回送给主机。 |
| 源码组成 | src/main.c:初始化、FIFO 收发函数、主流程与复位回调;inc/main.h:从机地址、缓冲长度与 FIFO 阈值等宏;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 从机、7 位地址,本机地址 I2Cx_SLAVE_ADDRESS7 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,发送阈值 FIFO_HALF_EMPTY_THR 与接收阈值 FIFO_HALF_FULL_THR 都取 4;收发缓冲 10 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 初始化后先调用 i2c_slave_fifo_recv 收齐一帧到 data_buf;接收先按 I2C_SetMasterReceivedDataBytesNum 设定字节数,等地址匹配后按 I2C_EVENT_SLAVE_FIFO_RECEIVING 成批搬运;发送等到 I2C_EVENT_SLAVE_FIFO_TRANSMITTER_ADDRESS_MATCHED,先填满发送 FIFO 再按事件续填;结束后进入空转。 |
| 主要 API | I2C_EnableFIFO、I2C_SetTxFifoThreshold、I2C_SetRxFifoThreshold、I2C_CheckFifoEvent、I2C_SendFIFOData、I2C_RecvFIFOData、I2C_ClrFIFO |
| 验证方法 | 把本示例烧录到从机板,主机端运行 I2C_Master_FIFO 等示例并与之对接;从机串口打印 This is the i2c slave fifo mode.。 |
| 使用提示 | FIFO 从机收发都以地址匹配事件作为帧起点; 发送时先一次填满 FIFO,再按半空事件续填; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
FIFO 从机把整帧数据拆成若干次阈值事件:接收按半满搬走,发送按半空续填。
数据路径: main → i2c_slave_fifo_recv 收齐一帧 → i2c_slave_fifo_send 回送 → 空转
1. 打开 FIFO 并设置阈值。 初始化在从机参数写好后开启收发 FIFO,并分别设置发送与接收阈值。
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;
}
发送阈值取 FIFO_HALF_EMPTY_THR,接收阈值取 FIFO_HALF_FULL_THR,本示例都为 4。
2. 主流程。 主流程只有一收一发:先收满一帧,方向反转后立刻回送,随后进入空转。
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);
收发都以 10 字节为单位,长度由 TEST_BUFFER_SIZE 决定。
3. 超时恢复。 等待 FIFO 事件超时会进入统一回调,按 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
}
默认配置走模块级 RCC 复位,复位后重新初始化并打印提示。
对应源码: src/main.c
5.16.3.10 I2C_10bit 十位地址主从
工程目录: projects/n32h49x_EVAL/examples/I2C/I2C_10bit/
初始化 I2C1 主机与 I2C2 从机→两块外设都配置为 10 位地址→主机写入并把数据送到从机→比较主机发送与从机接收缓冲→方向反转,从机写主机读→再次比较缓冲并打印结果
| 项目 | 源码解读 |
|---|---|
| 功能 | 在同一块板上用 I2C1 作主机、I2C2 作从机演示 10 位地址收发; 主机先写从机读,再把方向反转成从机写主机读,两轮都做缓冲比较。 |
| 源码组成 | src/main.c:双外设初始化、主流程、两个事件中断与错误中断,以及复位恢复代码;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | 主机 I2C1 的 SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,自身地址 I2C_MASTER_ADDR 为 0x230;从机 I2C2 的 SCL 为 PB10、SDA 为 PB11,复用 GPIO_AF7,自身地址 I2C_SLAVE_ADDR 为 0x2A0;两侧 AddrMode 都是 I2C_ADDR_MODE_10BIT,ClkSpeed 为 100 kHz;首字节用 I2C_FRAME_HEAD_TX 与 I2C_FRAME_HEAD_RX 携带地址高位;测试缓冲 100 字节。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 分别初始化主机与从机,填好 master_tx_buf 后发起始并同时等待 flag_master_complete 与 flag_slave_complete;第一轮比较 master_tx_buf 与 slave_rx_buf,第二轮把方向切换为 RECEIVE 并比较 master_rx_buf 与 slave_tx_buf;两个事件中断分别在 I2C1 与 I2C2 上按事件码推进收发。 |
| 主要 API | I2C_ConfigInt、NVIC_Configuration、I2C_GenerateStart、I2C_SendData、I2C_RecvData、I2C_GetLastEvent、I2C_ConfigAck |
| 验证方法 | 本示例用一块开发板即可,I2C1 与 I2C2 的引脚需要按板上的连接对接在一起; 复位运行后串口打印 this is a i2c(10bit) master/slave demo 与两轮测试结果。 |
| 使用提示 | 两端都必须使用 10 位地址模式,地址高两位随首字节一起发送; 每轮传输要等主机与从机两个完成标志都置位再比较; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
10 位地址事务多一步寻址:起始后先发帧头与地址高位,再补发地址低位,之后才进入数据阶段。
数据路径: main 发起始 → I2C1 中断发帧头与地址 → I2C2 中断应答并存数 → 两个完成标志置位 → Buffercmp → 反向再测一次
1. 主机用 10 位地址初始化。 i2c_master_init 把主机地址模式设为 10 位,并一次打开事件、缓冲与错误中断。
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);
自身地址取 I2C_MASTER_ADDR,即 0x230。
2. 从机初始化。 从机侧在 I2C2 上重复同样的步骤,地址模式同样是 10 位,引脚换成 PB10 与 PB11。
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);
两个外设各配一套中断,互不干扰。
3. 主流程反向传输。 第二轮把方向切换为 RECEIVE,先等总线空闲,再清 ACKPOS 并发起始。
/*----------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);
}
每次传输都要等主机与从机两个完成标志同时置位才继续。
4. 中断内发 10 位地址。 主机中断在 EV5 先发帧头与地址高位,随后在 I2C_EVT_MASTER_MODE_ADDRESS10_FLAG 事件里补发地址低位。
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;
帧头用 I2C_FRAME_HEAD_TX 或 I2C_FRAME_HEAD_RX 区分收发方向。
对应源码: src/main.c
5.16.3.11 PMBUS_Master PMBus 主机
工程目录: projects/n32h49x_EVAL/examples/I2C/PMBUS_Master/
初始化控制线与告警线引脚→初始化 I2C1 主机→依次读取温度、工作模式、状态字与输出电压→写回操作码与输出电压并回读比对→汇总七个测试状态→打印通过或失败
| 项目 | 源码解读 |
|---|---|
| 功能 | 按 PMBus 协议在 I2C1 上做主设备; 先读取温度、工作模式、状态字与输出电压命令,再以写后回读方式验证写操作,最后统一打印结果。 |
| 源码组成 | src/main.c:测试流程与结果判定;src/PMBusMaster.c、inc/PMBusMaster.h:命令索引表与 PMBusMaster 事务函数;inc/I2CMaster.h:底层主机接口声明;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 主机、7 位地址,自身地址为 0x30;PMBus 从设备地址 slave_address 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并上拉;控制线为 PA7、告警线为 PA8,配成复用推挽; 命令索引表覆盖 120 项,按区间区分读字节、读字、写字节等分组。 系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 调用 PMBusMaster_Init 后按命令依次调用 PMBusMaster;PMBusMaster 用命令索引查 PMBus_Commands 表得到命令码,再按索引区间决定读写分组;读回的字节在 main 里拼成 16 位字,写入值用 OPERATION 与 VOUT_COMMAND 回读比对;七个状态全部非 FAILED 才打印通过。 |
| 主要 API | PMBusMaster_Init、PMBusMaster、I2C_ConfigInt、I2C_GenerateStart、I2C_SendData、I2C_RecvData、I2C_GenerateStop |
| 验证方法 | 把本示例与 PMBUS_Slave 示例分别烧录到两块开发板并对接总线;复位运行后串口依次打印各命令的读取结果与最终通过或失败信息。 |
| 使用提示 | 命令索引不是命令码,要先经 PMBus_Commands 表转换;读字时低字节先到,需按高字节左移八位再相或的方式组合; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
PMBus 把命令组织成一张索引表:事务函数先查表得到命令码,再按索引区间决定这一次是读还是写、读几个字节。
数据路径: main → PMBusMaster_Init → 逐条 PMBusMaster(命令索引, 方向, 数据, 缓冲) → 查 PMBus_Commands → 分组执行 → 回读校验
1. 主流程入口。 main 打开日志后先初始化 PMBus 主机,随后按命令逐条发起读测试。
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");
每条命令都用 PMBusMaster(命令索引, 方向, 数据, 缓冲) 的统一形式调用。
2. 命令索引表。 PMBus_Commands 把命令索引映射成实际命令码,事务函数据此填发送缓冲的首字节。
/* 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
};
表内第 0 项是占位字节,有效命令从下标 1 开始。
3. 事务入口。 PMBusMaster 为一次事务准备发送与接收缓冲,并把命令码与 16 位数据拆进发送缓冲。
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;
数据低位字节先入缓冲,高位字节暂存在临时变量里。
4. 写后校验。 main 用同一命令读回刚写入的值,比对不一致就打印失败,最后汇总七个测试状态。
if(Vout_Command != 0xCCDD)
{
log_info("pmbus write VOUT_COMMAND cmd fail\r\n");
}
if(test_status1 == FAILED || test_status2 == FAILED || test_status3 == FAILED
|| test_status4 == FAILED || test_status5 == FAILED || test_status6 == FAILED
|| test_status7 == FAILED)
{
log_info("pmbus master int test fail!\r\n");
}
else
{
log_info("pmbus master int test pass!\r\n");
}
while (1)
{
}
}
只有全部命令都返回非 FAILED 时才打印通过。
对应源码: src/main.c、src/PMBusMaster.c
5.16.3.12 PMBUS_Slave PMBus 从机
工程目录: projects/n32h49x_EVAL/examples/I2C/PMBUS_Slave/
初始化控制线与告警线引脚→初始化 I2C1 从机并打开中断→等待主机寻址→按收到的命令字节反查索引→依命令分组准备或写入寄存器数据→事件中断推进收发并置完成标志
| 项目 | 源码解读 |
|---|---|
| 功能 | 把开发板配置成地址 0x10 的 PMBus 从设备;主机发来的命令字节经索引反查后分组处理,读命令把寄存值送入发送缓冲,写命令更新本地寄存器。 |
| 源码组成 | src/main.c:主循环;src/PMBusSlave.c、inc/PMBusSlave.h:从机初始化、命令译码与事件中断服务;inc/PMBus.h:命令索引与寄存器结构定义;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | I2C1 从机、7 位地址,本机地址 I2C_SLAVE_ADDR 为 0x10;SCL 为 PB6、SDA 为 PB7,复用 GPIO_AF2,开漏输出并用 POD 置上拉;ClkSpeed 为 100 kHz,事件中断抢占优先级 4,错误中断为 3;控制线为 PA7、告警线为 PA8,配成复用推挽; 初始寄存器值为温度 0x12、工作模式 0x34、状态字 0x5678、输出电压命令 0x90AB。系统时钟 240 MHz,打印串口 PA9、波特率 115200。 |
| 程序与数据路径 | main 调用 PMBusSlave_Init 后进入 PMBusSlave 主循环;PMBusSlave_Init 配好控制与告警引脚后调用 i2c_slave_init;PMBusSlave_DecodeCommand 用命令字节在 PMBus_Commands 表里反查索引,再按区间分组;事件中断按分组读写 PMBusSlave_ReceiveBuffer 与 PMBusSlave_TransmitBuffer,并置收发完成标志。 |
| 主要 API | PMBusSlave_Init、PMBusSlave、PMBusSlave_DecodeCommand、I2C_ConfigInt、I2C_GetLastEvent、I2C_SendData、I2C_RecvData |
| 验证方法 | 把本示例与 PMBUS_Master 示例分别烧录到两块开发板并对接总线;从机串口打印 this is a pmbus slave int demo,主机读到的值应与从机初始寄存器一致。 |
| 使用提示 | 命令字节要在 PMBus_Commands 表里反查索引,索引 0 表示主机在探测从机是否存在;写命令只更新本示例支持的寄存器,未实现的命令会被忽略; 开发环境为 KEIL MDK-ARM 5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
PMBus 从机把收到的命令字节当作查表键:先反查索引,再按索引区间决定这一帧要读几个字节或写入哪个寄存器。
数据路径: main → PMBusSlave_Init → i2c_slave_init → 事件中断收命令 → PMBusSlave_DecodeCommand 分组 → 组织收发缓冲
1. 初始化 PMBus 引脚。 PMBusSlave_Init 先清状态寄存器,再把控制线与告警线配成复用推挽,最后初始化 I2C 从机。
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);
控制线与告警线都在 GPIOA,编号 7 与 8。
2. 配置为 I2C 从机。 i2c_slave_init 把本机地址写入 OwnAddr1,并一次打开事件、缓冲与错误中断。
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;
地址模式为 7 位,时钟 100 kHz,自动应答打开。
3. 命令译码。 PMBusSlave_DecodeCommand 用命令字节在命令表里从下标 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;
查找结果下标为 0 说明主机在发探测命令,需要回复从机存在。
4. 主循环。 main 初始化后进入无限循环,由 PMBusSlave 反复处理事件与命令。
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();
}
}
从机不主动发起总线动作,循环里只等待主机寻址。
对应源码: src/main.c、src/PMBusSlave.c
5.16.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| FIFO 使能与中断、DMA 配置 | FIFOEN 打开时必须关闭 BUFINTEN; 使用 DMA 时需要使能 DMAFIFOEN 并关闭 DMAEN。 |
用户手册 |
| DMA 传输与缓冲器中断 | 使用 DMA 进行发送或接收时,不要设置 I2C_CTRL2 的 BUFINTEN 位。 | 用户手册 |
| 7 位地址的主机发送 | 主机发送且为 7 位地址模式时,从机地址不能配置成 0xF0、0xF2、0xF4 或 0xF6。 | 用户手册 |
| FIFO 接收数据的读取 | 关闭外设使能之前需要把接收的数据从 FIFO 中读取处理; PECEN 置位时接收的最后一个字节为校验数据。 |
用户手册 |
| 软件复位与总线释放 | SWRESET 置位前要确认 I2C 引脚已释放、总线为空; 该位可用于 BUSY 位为 1 且总线上没有检测到停止条件时。 |
用户手册 |
| 控制寄存器写入时序 | 设置了 STOPGEN、STARTGEN 或 PEC 位后,在硬件清除该位之前不要对 I2C_CTRL1 执行任何写操作。 | 用户手册 |
| 寄存器访问宽度 | I2C 外设寄存器可以用半字(16 位)或字(32 位)方式操作。 | 用户手册 |