1.概述
国民技术N32A455系列MCU搭载了多达4个独立的I2C总线接口,它提供多主机功能,控制所有I2C总线特定的时序、协议、仲裁和定时。支持多种通信速率模式(最高支持1MHz),支持DMA操作,同时与SMBus 2.0兼容。 I2C模块有多种用途,包括CRC码的生成和校验、 SMBus(系统管理总线—System Management Bus)和PMBus(电源管理总线—PowerManagement Bus)。
2.N32A455开发板EEPROM原理图
开发板选用EEROM芯片(CAT24C08YI-GT3),分别连接到硬件I2C1的PB8和PB9这两个引脚上,EEPROM的A0~A2地址选择引脚都连接了GND,如下所示:
3.I2C示例程序
通过I2C模块与外部EEPRON的通信,对EEPROM进行读取操作,熟悉I2C的编程和应用方法:
int main(void)
{
uint16_t i = 0;
log_init();
log_info("this is a I2C EEPROM demo\r\n");
/* Initialize the I2C EEPROM driver ----------------------------------------*/
I2C_EE_Init();
/* Fill the buffer to send */
for (i = 0; i < TEST_EEPROM_SIZE; i++)
{
tx_buf[i] = i;
}
log_info("\r\nWrite to I2C EEPROM...");
/* First write in the memory followed by a read of the written data --------*/
/* Write to I2C EEPROM from TEST_EEPROM_ADDR */
I2C_EE_WriteBuffer(tx_buf, TEST_EEPROM_ADDR, TEST_EEPROM_SIZE);
log_info("\r\nRead from I2C EEPROM :");
/* Read from I2C EEPROM from sEE_READ_ADDRESS1 */
I2C_EE_ReadBuffer(rx_buf, TEST_EEPROM_ADDR, TEST_EEPROM_SIZE);
for (i = 0; i < TEST_EEPROM_SIZE; i++)
{
if ((i % 16) == 0)
{
printf("\r\n");
}
printf("0x%02X ", rx_buf[i]);
}
printf("\r\n");
/* Check if the data written to the memory is read correctly */
test_status = Buffercmp(tx_buf, rx_buf, TEST_EEPROM_SIZE);
if (test_status == PASSED)
{
log_info("\r\nthe write and read data are the same,I2C EEPROM test pass\r\n");
}
else
{
log_info("\r\nthe write and read data are different,I2C EEPROM test fail\r\n");
}
while (1)
{
}
}
EEPROM写PAGE操作:
写PAGE操作实现了查访和DMA两种方式,操作流程都相同,都是先产生START信号、发送器件地址、发送写入地址、写入数据、最后发送STOP停止位;每一个步骤都对应一个事件检测,判断是否操作完成:
void I2C_EE_PageWrite(u8* pBuffer, u16 WriteAddr, u16 NumByteToWrite)
{
#if PROCESS_MODE == 0 /* polling */
sEETimeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, WriteAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** While there is data to be written */
while (NumByteToWrite--)
{
/** Send the current byte */
I2C_SendData(I2Cx, *pBuffer);
/** Point to the next byte to be written */
pBuffer++;
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
}
/** Send STOP condition */
I2C_GenerateStop(I2Cx, ENABLE);
I2C_EE_WaitEepromStandbyState();
I2C_EE_WriteOnePageCompleted();
#elif PROCESS_MODE == 1 /* interrupt */
/** initialize static parameter */
MasterDirection = Transmitter;
i2c_comm_state = COMM_PRE;
/** initialize static parameter according to input parameter */
SlaveADDR = EEPROM_ADDRESS; /// this byte shoule be send by F/W (in loop or INTSTS way)
DeviceOffset = WriteAddr; /// this byte can be send by both F/W and DMA
OffsetDone = FALSE;
memcpy(SendBuf, pBuffer, NumByteToWrite);
BufCount = 0;
Int_NumByteToWrite = NumByteToWrite;
I2C_ConfigAck(I2C1, ENABLE);
I2C_ConfigInt(I2C1, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
/** Send START condition */
sEETimeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
I2C_GenerateStart(I2C1, ENABLE);
I2C_EE_WaitOperationIsCompleted();
I2C_EE_WriteOnePageCompleted();
#elif PROCESS_MODE == 2 /* DMA */
DMA_InitType DMA_InitStructure;
/** DMA initialization */
DMA_DeInit(DMA1_CH6);
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.DMA_MemoryInc = DMA_MEM_INC_ENABLE; /// fixed
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE; /// fixed
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_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);
sEETimeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, WriteAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDING))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
DMA_EnableChannel(DMA1_CH6, ENABLE);
I2C_EnableDMA(I2C1, ENABLE);
sEETimeout = sEE_LONG_TIMEOUT;
while(I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send STOP condition */
I2C_GenerateStop(I2Cx, ENABLE);
I2C_EnableDMA(I2Cx, DISABLE);
DMA_EnableChannel(DMA1_CH6, DISABLE);
I2C_EE_WaitEepromStandbyState();
I2C_EE_WriteOnePageCompleted();
#endif
}
EEPROM读取数据:
EEPROM数据读取的函数,依然使用了查询和DMA两种实现方式,EEPROM的读取大小没有写入PAGE字节数的限制了;具体的操作流程:产生START信号、发送器件地址、发送读取地址、产生RESTART信号、发送读取命令、读取数据:
void I2C_EE_ReadBuffer(u8* pBuffer, u16 ReadAddr, u16 NumByteToRead)
{
#if PROCESS_MODE == 0 /* polling */
sEETimeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
I2C_ConfigNackLocation(I2Cx, I2C_NACK_POS_CURRENT); // clear ACKPOS
I2C_ConfigAck(I2Cx, ENABLE);
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Clear EV6 by setting again the PE bit */
I2C_Enable(I2Cx, ENABLE);
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, ReadAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send STRAT condition a second time */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for read */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_RECV);
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_ADDRF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** While there is data to be read */
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)
{
I2C_ConfigNackLocation(I2Cx, 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);
}
while (NumByteToRead)
{
if (NumByteToRead <= 3)
{
/** One byte */
if (NumByteToRead == 1)
{
/** Wait until RXNE flag is set */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Read data from DAT */
/** Read a byte from the EEPROM */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
}
/** Two bytes */
else if (NumByteToRead == 2)
{
/** Wait until BTF flag is set */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send STOP Condition */
I2C_GenerateStop(I2Cx, ENABLE);
/** Read data from DAT */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
/** Read data from DAT */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
}
/** 3 Last bytes */
else
{
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Disable Acknowledgement */
I2C_ConfigAck(I2Cx, DISABLE);
/** Read data from DAT */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
/** Wait until BTF flag is set */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send STOP Condition */
I2C_GenerateStop(I2Cx, ENABLE);
/** Read data from DAT */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
/** Read data from DAT */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
}
}
else
{
/** Test on EV7 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_RECVD_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Read a byte from the EEPROM */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
if (I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
/** Read a byte from the EEPROM */
*pBuffer = I2C_RecvData(I2Cx);
/** Point to the next location where the byte read will be saved */
pBuffer++;
/** Decrement the read bytes counter */
NumByteToRead--;
}
}
}
#elif PROCESS_MODE == 1 /* interrupt */
I2C_pBuffer = pBuffer;
MasterDirection = Receiver;
/** initialize static parameter according to input parameter*/
SlaveADDR = EEPROM_ADDRESS;
DeviceOffset = ReadAddr;
OffsetDone = FALSE;
i2c_comm_state = COMM_PRE;
I2C_ConfigNackLocation(I2Cx, I2C_NACK_POS_CURRENT); // clear ACKPOS
I2C_ConfigAck(I2C1, ENABLE);
I2C_ConfigInt(I2C1, I2C_INT_EVENT | I2C_INT_BUF | I2C_INT_ERR, ENABLE);
Int_NumByteToRead = NumByteToRead;
sEETimeout = sEE_LONG_TIMEOUT;
while (I2C_GetFlag(I2Cx, I2C_FLAG_BYTEF))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
I2C_GenerateStart(I2C1, ENABLE);
I2C_EE_WaitOperationIsCompleted();
#elif PROCESS_MODE == 2 /* DMA */
/** DMA initialization */
if(NumByteToRead > 1)
{
DMA_InitType DMA_InitStructure;
/** DMA initialization */
DMA_DeInit(DMA1_CH7);
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.DMA_MemoryInc = DMA_MEM_INC_ENABLE; /// fixed
DMA_InitStructure.PeriphDataSize = DMA_PERIPH_DATA_SIZE_BYTE; /// fixed
DMA_InitStructure.MemDataSize = DMA_MemoryDataSize_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);
}
while (I2C_GetFlag(I2Cx, I2C_FLAG_BUSY))
{
if ((sEETimeout--) == 0)
{
sEE_TIMEOUT_UserCallback();
}
}
/** Send START condition */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for write */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_SEND);
/** Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_TXMODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Clear EV6 by setting again the PE bit */
I2C_Enable(I2Cx, ENABLE);
/** Send the EEPROM's internal address to write to */
I2C_SendData(I2Cx, ReadAddr);
/** Test on EV8 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_DATA_SENDED))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send STRAT condition a second time */
I2C_GenerateStart(I2Cx, ENABLE);
/** Test on EV5 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_CheckEvent(I2Cx, I2C_EVT_MASTER_MODE_FLAG))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Send EEPROM address for read */
I2C_SendAddr7bit(I2Cx, EEPROM_ADDRESS, I2C_DIRECTION_RECV);
/* Test on EV6 and clear it */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_ADDRF)) //EV6
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** While there is data to be read */
if (NumByteToRead == 1)
{
/** Disable Acknowledgement */
I2C_ConfigAck(I2Cx, DISABLE);
(void)(I2Cx->STS1); /// clear ADDR
(void)(I2Cx->STS2);
I2C_GenerateStop(I2Cx, ENABLE);
/** Wait until RXNE flag is set */
sEETimeout = sEE_LONG_TIMEOUT;
while (!I2C_GetFlag(I2Cx, I2C_FLAG_RXDATNE))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
/** Read data from DAT */
/** Read a byte from the EEPROM */
*pBuffer = I2C_RecvData(I2Cx);
}
else
{
I2C_ConfigAck(I2Cx, ENABLE);
(void)(I2Cx->STS1);
(void)(I2Cx->STS2);
I2C_EnableDmaLastSend(I2Cx, ENABLE);
DMA_EnableChannel(DMA1_CH7, ENABLE);
I2C_EnableDMA(I2Cx, ENABLE);
sEETimeout = sEE_LONG_TIMEOUT;
while(!(DMA_GetFlagStatus(DMA1_FLAG_TC7,DMA1)))
{
if ((sEETimeout--) == 0)
sEE_TIMEOUT_UserCallback();
}
I2C_EnableDMA(I2Cx, DISABLE);
DMA_EnableChannel(DMA1_CH7, DISABLE);
I2C_EnableDmaLastSend(I2Cx, DISABLE);
I2C_GenerateStop(I2Cx, ENABLE);
}
I2C_EE_WaitOperationIsCompleted();
#endif
}
4.运行结果
5.工程源码
DMA的方式比普通的读取在快好多倍呀?
引用: lugl4313820 发表于 2024-3-16 06:59 DMA的方式比普通的读取在快好多倍呀?
读写的速度是一样的呀,只是DMA可以释放MCU资源去做其它事情……