草庐IT

STM32之I2C对EEPROM 页读写、连续读写操作

wiyoo0 2023-10-14 原文

页写入

原理

stm32的EEPROM(24C02)的一页只有8个字节  组织架构是256 * 8bits = 2k(bits)

 步骤:

1、起始信号

2、检查总线是否忙碌

3、发送EEPROM设备地址和发送方向

4、发送要写入的单元格地址

5、开始发送数据

6、发送完响应noack 停止发送   

7、结束信号

注意:以上的每一步都需要响应EV事件

代码

//-----------------------对EEPROM进行页写入----------------------------------
// buffer : 要写入的缓冲区数据
// addr   :要写入的单元格地址
// num	 :要写入的数据数量  不能超过页大小 8位
uint16_t EEPROM_Page_write(u8 *buffer, u8 addr, uint8_t num)
{
	//查询总线是否忙碌
	TimeOut_count = I2C_time_out;
	while (I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(4);
	}

	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);

	//等待EV5事件响应
	TimeOut_count = I2C_time_out;
	while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(5);
	}

	//发送要写入的EEPROM的地址和读写方向
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);

	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(6);
	}

	//发送要写入EEPROM的单元地方
	I2C_SendData(EEPROM_I2C, addr);

	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(7);
	}

	while (num--)
	{
		//发送要写入的数据
		I2C_SendData(EEPROM_I2C, *buffer);

		//要写入的数据指针递增
		buffer++;

		//等待EV事件响应
		TimeOut_count = I2C_time_out;
		while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
		{
			if ((TimeOut_count--) == 0)				return I2C_timeout_callback(8);
		}
	}

	//发送停止信号
	I2C_GenerateSTOP(I2C1, ENABLE);

	//写完马上读会出错  在写入时是不能读的
	Wait_for_StandBy();

	return 0;
}

连续写

原理

 这里介绍了 AT24C02的内存内部架构是 32页的8byte组成的  32*8byte = 256 byte

所以stm32这I2C对EEPROM最多写入256个byte

关于连续写入会遇到的几个问题:

1、写入的单元格地址不一定是每一页的起始位,导致后面连续页写入位置对不齐,导致数据写入失败

2、 将连续的写入最后凑不满一页的几个字节单独写入,有助于提高效率

 关于解决的方法就是:算出对不齐的位数,从要写入的数据中选出可以凑满一页的数据先行讲那一页进行凑满,后续再按正常对齐的方式进行写入。

代码

//buffer : 要写入的数据
//addr   : 要写入的单元格地址
//num    : 要写入的数据数量
uint16_t I2c_Buffer_write(u8 *buffer, u8 addr, u16 num)
{
	u16 Num_Page, Num_Single, count, aaddr;	

	//算出是否对齐位置  
	count = addr % EEPROM_PAGE_SIZE;		
	//没对齐的话  aaddr代表需要单独写入的位数  为了凑齐一页的数量
	aaddr = EEPROM_PAGE_SIZE - count;			

	//如果对齐了
	if (count == 0)
	{
		//算出需要插入的页数  和凑不满一页的单独数
		Num_Page = num / EEPROM_PAGE_SIZE;
		Num_Single = num % EEPROM_PAGE_SIZE;

		//若凑不满一页  则直接写入
		if (Num_Page == 0)
		{
			EEPROM_Page_write(buffer, addr, Num_Single);
			Wait_for_StandBy();
		}
		else
		{
			//先写入需要的页数
			while (Num_Page--)
			{
				EEPROM_Page_write(buffer, addr, EEPROM_PAGE_SIZE);
				Wait_for_StandBy();

				//每写一页都需要将  数据指针和单元格地址指针进行位移
				buffer += EEPROM_PAGE_SIZE;
				addr += EEPROM_PAGE_SIZE;
			}
			//如果有凑不满一页的位数
			if (Num_Single)
			{
				EEPROM_Page_write(buffer, addr, Num_Single);
				Wait_for_StandBy();
			}
		}
	}
	//若没有对齐
	else
	{
		//算出凑满第一页后还需要写入的位数
		num = num - aaddr;
		//算出凑满后还需要写入的页数
		Num_Page = num / EEPROM_PAGE_SIZE;
		//算出多余的凑不满一页的位数
		Num_Single = num % EEPROM_PAGE_SIZE;
		
		//若凑满一页后 不满一页的的话 直接写入
		if (Num_Page == 0)
		{
			EEPROM_Page_write(buffer, addr, aaddr);
			Wait_for_StandBy();
		}
		else
		{
			//若有凑不满一页的
			if(count)
			{
				//先写入凑满一页的位数
				EEPROM_Page_write(buffer, addr, aaddr);
				Wait_for_StandBy();

				//对数据指针和地址指针进行位移
				buffer += aaddr;
				addr += aaddr;
			}

			//接下来的操作跟对齐的操作一样
			while (Num_Page--)
			{
				EEPROM_Page_write(buffer, addr, EEPROM_PAGE_SIZE);
				Wait_for_StandBy();

				buffer += EEPROM_PAGE_SIZE;
				addr += EEPROM_PAGE_SIZE;
			}

			if (Num_Single)
			{
				EEPROM_Page_write(buffer, addr, Num_Single);
				Wait_for_StandBy();
			}
		}
	}
	return 1;
}

连续读

原理

 从上面第一张步骤:

1、发送起始信号

2、发送要接收的EEPROM地址和发送方向

3、发送要接收的单元格地址

4、发送要写入的EEPROM地址和接收方向

5、开始接收数据

6、接收完毕发送noack应答结束传输

7、发送结束信号

注意:以上的每一步都需要响应EV事件

代码

// EPPROM_Random_Read  :  连续读取
// addr : 要读取的单元格地址
// DATA :读取的内容存放区
// num  :要读取的数据数量
uint32_t EPPROM_Random_Read(u8 addr, u8 *Data, u16 num)
{
	//*((u8 *)0x4001080c) |=0x80;
	while (I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(99);
	}
	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);

	//等待EV5事件响应
	TimeOut_count = I2C_time_out;
	while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(21);
	}

	//发送要写入的EEPROM的地址和读写方向   先选择写方向写入单元格地址
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Transmitter);

	//等待EV6事件响应
	TimeOut_count = I2C_time_out;
	while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(22);
	}

	//发送要读取EEPROM的单元地方
	I2C_SendData(EEPROM_I2C, addr);

	//等待EV8_2事件响应
	TimeOut_count = I2C_time_out;
	while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(23);
	}

	//---------------------------------------------------------------------------------------------------------
	//-----------------------产生第二次起始信号---------------------------------------------------------

	//发送起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);

	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(24);
	}

	//*********************************读方向************************************************
	//发送要写入的EEPROM的地址和读方向
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDRESS, I2C_Direction_Receiver);

	//等待EV事件响应
	TimeOut_count = I2C_time_out;
	while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
	{
		if ((TimeOut_count--) == 0)
			return I2C_timeout_callback(25);
	}

	while (num--)
	{
		if (num == 0)
		{
			I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE);
		}
		else
		{
			I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
		}

		//等待EV事件响应
		TimeOut_count = I2C_time_out;
		while (I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
		{
			if ((TimeOut_count--) == 0)
				return I2C_timeout_callback(26);
		}

		*Data = I2C_ReceiveData(EEPROM_I2C);

		// printf(" data = %d \r\n",*Data);
		Data++;
	}
	
	return 1;
}

主函数


#define  Test_num   256

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
		u16 i;
    u8 buff[Test_num];
    u8 data[Test_num];
	
	//初始化要写入的数据
	for(i = 0 ; i < Test_num ; i++)
	{
		buff[i] = i;
	}
	
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  Debug_USART_Config();
		
	//初始化I2C
	I2C_EE_init();
 
	/*调用printf函数,因为重定向了fputc,printf的内容会输出到串口*/
	printf("\r\n IIC实验 \r\n");	

   I2c_Buffer_write(buff , 0x45 , Test_num);
		
	 EPPROM_Random_Read(0x45 , data, Test_num);
	
	printf(" \r\n EEPROM读写实验结束 data =  \n");

  for ( i = 0; i < Test_num; i++)
  {
    printf("%d ",data[i]);
  }


  while(1)
	{	
		
	}	

}

结果截图

 

有关STM32之I2C对EEPROM 页读写、连续读写操作的更多相关文章

  1. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  2. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  3. ruby - 如何使用 Selenium Webdriver 根据 div 的内容执行操作? - 2

    我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption

  4. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  5. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  6. ruby - 在 Ruby 中是否有一种惯用的方法来操作 2 个数组? - 2

    a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],

  7. ruby-on-rails - 如何让 Rails View 返回其关联的操作名称? - 2

    我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam

  8. ruby - 如何用 Nokogiri 解析连续的标签? - 2

    我有这样的HTML代码:Label1Value1Label2Value2...我的代码不起作用。doc.css("first").eachdo|item|label=item.css("dt")value=item.css("dd")end显示所有首先标记,然后标记标签,我需要“标签:值” 最佳答案 首先,您的HTML应该有和中的元素:Label1Value1Label2Value2...但这不会改变您解析它的方式。你想找到s并遍历它们,然后在每个你可以使用next_element得到;像这样:doc=Nokogiri::HTML(

  9. ruby - 我可以从 Ruby 中的系统调用中获得连续输出吗? - 2

    当您在Ruby脚本中使用系统调用时,您可以像这样获得该命令的输出:output=`ls`putsoutput这就是thisquestion是关于。但是有没有办法显示系统调用的连续输出?例如,如果您运行此安全复制命令,以通过SSH从服务器获取文件:scpuser@someserver:remoteFile/some/local/folder/...它显示随着下载进度的连续输出。但是这个:output=`scpuser@someserver:remoteFile/some/local/folder/`putsoutput...不捕获该输出。如何从我的Ruby脚本中显示正在进行的下载进度?

  10. ruby - 获取数组中值的最大连续出现次数 - 2

    下面有没有更优雅的方法来实现这个:输入:array=[1,1,1,0,0,1,1,1,1,0]输出:4我的算法:streak=0max_streak=0arr.eachdo|n|ifn==1streak+=1elsemax_streak=streakifstreak>max_streakstreak=0endendputsmax_streak 最佳答案 类似于w0lf'sanswer,但通过从chunk返回nil来跳过元素:array.chunk{|x|x==1||nil}.map{|_,x|x.size}.max

随机推荐