STM32: Hardware IIC, realizes EEPROM page writing and continuous memory data reading, but data errors occur (top)

For realizing EEPROM page writing and continuous memory data reading, code can be implemented according to the "AT24C01" data manual. : For a detailed explanation of how to write a program from the diagram, you can see another article of mine " [STM32-I2C Learning Summary] STM32: Hardware-IIC Detailed Explanation, Firmware Library Programming, Hand-in-hand to teach you how to implement IIC "
"AT24C01" Data Manual After the data writing and reading icons, I directly uploaded the code, and finally analyzed the cause of the error. And give the solution measures
as shown below:
1. Single byte write
Insert picture description here

void I2C_ByteWrite(uint8_t *pBuffer, uint8_t WriteAddr)
{
    
    
	while(I2C_GetFlagStatus(DEBUG_I2Cx_Port, I2C_FLAG_BUSY));
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Transmitter);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);

	I2C_SendData(DEBUG_I2Cx_Port, WriteAddr);
	//check EV8_2
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
	
	I2C_SendData(DEBUG_I2Cx_Port, *pBuffer);
	//check EV8_2
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);

	I2C_GenerateSTOP(DEBUG_I2Cx_Port,ENABLE);

}

2. Page write
Insert picture description here

void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
    
    
  while(I2C_GetFlagStatus(DEBUG_I2Cx_Port, I2C_FLAG_BUSY));
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Transmitter);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);

	I2C_SendData(DEBUG_I2Cx_Port, WriteAddr);
	//check EV8_2
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
	
	/* 参考上图,页写入与单个写入的差别,仅仅是停止信号的发送 */
	/* 加入判断即可 */
	while(NumByteToWrite)
	{
    
    
		I2C_SendData(DEBUG_I2Cx_Port, *pBuffer);
		//check EV8_2
		while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);
		pBuffer++;
		NumByteToWrite--;
	}
	I2C_GenerateSTOP(DEBUG_I2Cx_Port,ENABLE);
 
}

3. Single byte read
Insert picture description here

void I2C_ByteRead(uint8_t *pBuffer, uint8_t ReadAddr)
{
    
     
	//这个函数我还没用过,只是参考上图写了出来,方便大家理解

	 while(I2C_GetFlagStatus(DEBUG_I2Cx_Port, I2C_FLAG_BUSY));
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Transmitter);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);
	I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
	I2C_GenerateSTOP(DEBUG_I2Cx_Port,ENABLE);
	//check EV7
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR);
	*pBuffer = I2C_ReceiveData(DEBUG_I2Cx_Port);
	
	I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
	
}

4. Read any single data
Insert picture description here

void I2C_ByteRead(uint8_t *pBuffer, uint8_t ReadAddr)
{
    
    
	 while(I2C_GetFlagStatus(DEBUG_I2Cx_Port, I2C_FLAG_BUSY));
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Transmitter);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);

	I2C_SendData(DEBUG_I2Cx_Port, ReadAddr);
	//check EV8_2
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);

	
	
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Receiver);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);

	I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
	I2C_GenerateSTOP(DEBUG_I2Cx_Port,ENABLE);
	//check EV7
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR);
	*pBuffer = I2C_ReceiveData(DEBUG_I2Cx_Port);
	
	I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
	
	
}

5. Read data continuously
Insert picture description here

void I2C_BufferRead(uint8_t* pBuffer, u8 ReadAddr, uint16_t NumByteToRead)
{
    
      
  I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Transmitter);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);

	I2C_SendData(DEBUG_I2Cx_Port, ReadAddr);
	//check EV8_2
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);

	
	
	I2C_GenerateSTART(DEBUG_I2Cx_Port,ENABLE);
	//check EV5
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
	
	I2C_Send7bitAddress(DEBUG_I2Cx_Port,DEBUG_EEPROM_Addr,I2C_Direction_Receiver);
	//check EV6
	while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR);
	
	while(NumByteToRead)
	{
    
    
		if(NumByteToRead == 1)
		{
    
    
			I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
			I2C_GenerateSTOP(DEBUG_I2Cx_Port,ENABLE);
		}
		//check EV7
		while(I2C_CheckEvent(DEBUG_I2Cx_Port,I2C_EVENT_MASTER_BYTE_RECEIVED) == ERROR);
		*pBuffer = I2C_ReceiveData(DEBUG_I2Cx_Port);
		pBuffer++;
		NumByteToRead--;
	}
	I2C_AcknowledgeConfig(DEBUG_I2Cx_Port,DISABLE);
}

Well, the main functions have been implemented, let’s show you how these functions are overturned according to the "AT24C01" data manual without other processing functions! The test code:

#include "stm32f10x.h"
#include "bsp_i2c.h"
#include "bsp_usart.h"

uint8_t I2C_Buf_Write[256];
uint8_t I2C_Buf_Read[256];
uint16_t i=0;
void delay(uint32_t count)
{
    
    
	while(count--);
}
int main(void)
{
    
    
	USART_Config();
	I2C_Config();
	for(i=0;i<256;i++)
	{
    
    
		I2C_Buf_Write[i] = i;
	}
	printf("\r\n 这是一个I2C外设(AT24C02)读写测试例程 \r\n");
	
	I2C_EE_PageWrite(&I2C_Buf_Write[2],0x18,8);
	delay(0xffff); //加一个延时 确保已经写入EEPROM  STM32工作速度可是远大
	             //   400k  而且我函数里没有处理,这不是关键
	I2C_BufferRead(&I2C_Buf_Read[2],0x18,8);

	for(i=0; i<8; i++)
	{
    
    
		printf("0x%x \t", I2C_Buf_Read[i+2]);
	}
	
	while(1);
}

Open serial port assistant

Insert picture description here
Of course I am here to explain to you why something went wrong. First, observe the test function. I want the unit address of the EEPROM. What is written is 8 consecutive addresses starting from 0x18. If I shift this initial address to the right, what will happen?

I2C_EE_PageWrite(&I2C_Buf_Write[2],0x19,8);//仅平移一个
	delay(0xffff);
	I2C_BufferRead(&I2C_Buf_Read[2],0x19,8);

Insert picture description here

Of course, if I shift two addresses, it is conceivable that there will be two wrong addresses. What if I shift 10 addresses?

	I2C_EE_PageWrite(&I2C_Buf_Write[2],0x28,8);//平移10个地址
	delay(0xffff);  
	I2C_BufferRead(&I2C_Buf_Read[2],0x28,8);

Insert picture description here

Therefore, it can be observed from here that you continuously write and read data, whether it is correct or not, depends on the location of the memory unit address you write, and it has a certain pattern. Therefore, it must be processed, and the cross-page operation , similar to the flash operation, cannot write two sectors continuously . Will cause errors. The correctness of the data can be guaranteed by continuously writing the data on a separate page. Therefore, a data address alignment operation must be added . The following is the official program of Wildfire, which contains the data address to operate it. I have just learned this. There are still some bugs in the program. After I deal with it, I will attach my program:

 /**
 * @brief 将缓冲区中的数据写到 I2C EEPROM 中
 * @param
 * @arg pBuffer:缓冲区指针
 * @arg WriteAddr:写地址
 * @arg NumByteToWrite:写的字节数
 * @retval 无
 */
 void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr,
						u16 NumByteToWrite)
 {
    
    
	u8 NumOfPage=0,NumOfSingle=0,Addr =0,count=0,temp =0;
 
	/*mod 运算求余,若 writeAddr 是 I2C_PageSize 整数倍,
	运算结果 Addr 值为 0*/
	Addr = WriteAddr % I2C_PageSize;
 
	/*差 count 个数据值,刚好可以对齐到页地址*/
	count = I2C_PageSize - Addr;
 
	/*计算出要写多少整数页*/
	NumOfPage = NumByteToWrite / I2C_PageSize;
 
	/*mod 运算求余,计算出剩余不满一页的字节数*/
	NumOfSingle = NumByteToWrite % I2C_PageSize;
	
	// Addr=0,则 WriteAddr 刚好按页对齐 aligned
	// 这样就很简单了,直接写就可以,写完整页后
	// 把剩下的不满一页的写完即可
	if (Addr == 0) {
    
    
		/* 如果 NumByteToWrite < I2C_PageSize */
		if (NumOfPage == 0) {
    
    
			I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
			I2C_EE_WaitEepromStandbyState();
		}
		/* 如果 NumByteToWrite > I2C_PageSize */
		else {
    
    
			/*先把整数页都写了*/
			while (NumOfPage--) {
    
    
				I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
				I2C_EE_WaitEepromStandbyState();
				WriteAddr += I2C_PageSize;
				pBuffer += I2C_PageSize;
			}
			/*若有多余的不满一页的数据,把它写完*/
			if (NumOfSingle!=0) {
    
    
			I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
			I2C_EE_WaitEepromStandbyState();
			}
		}
	}
	// 如果 WriteAddr 不是按 I2C_PageSize 对齐
	// 那就算出对齐到页地址还需要多少个数据,然后
	// 先把这几个数据写完,剩下开始的地址就已经对齐
	// 到页地址了,代码重复上面的即可
	else {
    
    
		/* 如果 NumByteToWrite < I2C_PageSize */
		if (NumOfPage== 0) {
    
    
			/*若 NumOfSingle>count,当前面写不完,要写到下一页*/
				if (NumOfSingle > count) {
    
    
					// temp 的数据要写到写一页
					temp = NumOfSingle - count;
					I2C_EE_PageWrite(pBuffer, WriteAddr, count);
					I2C_EE_WaitEepromStandbyState();
					WriteAddr += count;
					pBuffer += count;
					
					I2C_EE_PageWrite(pBuffer, WriteAddr, temp);
					I2C_EE_WaitEepromStandbyState();
				} 
				else {
    
     /*若 count 比 NumOfSingle 大*/
						I2C_EE_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
					I2C_EE_WaitEepromStandbyState();
				}
		}
		/* 如果 NumByteToWrite > I2C_PageSize */
		else {
    
    
			/*地址不对齐多出的 count 分开处理,不加入这个运算*/
			NumByteToWrite -= count;
			NumOfPage = NumByteToWrite / I2C_PageSize;
			NumOfSingle = NumByteToWrite % I2C_PageSize;
			
			/*先把 WriteAddr 所在页的剩余字节写了*/
			if (count != 0) {
    
    
				I2C_EE_PageWrite(pBuffer, WriteAddr, count);
				I2C_EE_WaitEepromStandbyState();
				
				/*WriteAddr 加上 count 后,地址就对齐到页了*/
				WriteAddr += count;
				pBuffer += count;
			}
			/*把整数页都写了*/
			while (NumOfPage--) {
    
    
				I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
				I2C_EE_WaitEepromStandbyState();
				WriteAddr += I2C_PageSize;
				pBuffer += I2C_PageSize;
			}
			/*若有多余的不满一页的数据,把它写完*/
			if (NumOfSingle != 0) {
    
    
				I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
				I2C_EE_WaitEepromStandbyState();
			}
		}
	}
}

I think there is a sentence in the "AT24C01" data manual that limits the length of data that can be written at one time, which should be to avoid the problem of cross-page operations.
Insert picture description here

I will explain to you the principle of address alignment in the next article

Guess you like

Origin blog.csdn.net/qq_45689790/article/details/113729858