STM32+AT24C02实现易变参数存储

news2025/2/25 21:46:32

AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节存储单元,该器件通过IIC总线接口进行操作, AT24C02把存储空间分为 32 页,每页可存储8个字节的数据,具有硬件数据写保护功能,100万次擦写,数据保持100年。

当你的产品有存储一些关键且易变的参数时,内置的Flash频繁在程序运行时擦写会有一些风险,而片外挂接一些Flash数据量小又没有必要,所以片外挂一个E2PROM是一种常见的解决办法,本例介绍在STM32平台上利用其提供的I2C控制器来实现对E2PROM的读写操作。

文章目录

概念说明

实现原理

嵌入式程序


概念说明

  • 页:页(Page)、扇区(Sector)、块(Block)是存储器空间的单位,定义来源于早期的软盘、硬盘等存储器发展而来,单位大小:页>扇区>块。不同厂家的、不同类型存储器的页大小不同,在AT24C02中一页等于8字节数据。
  • IIC:IIC(Inter-Integrated Circuit)是一种通用的总线协议,总线上只有一个Master,通信全由Master发起。对于硬件设计人员来说,只需要2个管脚(时钟SCL与数据SDA),极少的连接线和面积,就可以实现芯片间的通讯,对于软件开发者来说,可以使用同一个I2C驱动库,来实现实现不同器件的驱动,大大减少了软件的开发时间。I2C协议包含四种信号:起始、停止、应答和非应答信号。
    • 起始:I2C协议规定,SCL处于高电平时,SDA由高到低变化,这种信号是起始信号。表示要开始传输数据。
    • 停止:I2C协议规定,SCL处于高电平,SDA由低到高变化,这种信号是停止信号。
    • ACK与NACK:应答信号出现在1个字节传输完成之后,即第9个SCL时钟周期内,此时Master需要释放SDA总线,把总线控制权交给Slave,由于上拉电阻的作用,此时总线为高电平,如果从机正确的收到了主机发来的数据,会把SDA拉低,表示应答响应(ACK)。如果没有拉低,则无应答(NACK),下图是I2C总线电平时序示意图:


实现原理

嵌入式程序跑在STM32平台,封装正确的初始化I2C控制器以及读写接口供上层按地址读写EEPROM即可。原理示意图如下:

SCL和SDK两条线都接了10K的上拉电阻,MCU需要把这两个引脚设置为开漏输出(低电平,高阻态)而不是推挽输出,这样才能实现Master与Slave的ACK握手机制。


嵌入式程序

嵌入式代码构成也比较简单:

  • I2C控制器初始化,包括IO口配置
  • E2PROM写函数
  • E2PROM读函数

首先是初始化部分代码:

//i2c IO口初始化
static void I2C_GPIO_Config(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
 
  /* 使能与 I2C1 有关的时钟 */
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
  /*  配置SCL SDA引脚速率输出方式 */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  // 开漏输出
  GPIO_Init(GPIOB, &GPIO_InitStructure);
}
//i2c 控制器配置
static void I2C_Mode_Configu(void)
{
  I2C_InitTypeDef I2C_InitStructure;
 
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;  /* I2C 配置 */
 
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;  /* 高电平数据稳定,低电平数据变化 SCL  时钟线的占空比 */
  I2C_InitStructure.I2C_OwnAddress1 = I2C1_OWN_ADDRESS7;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  /* I2C 的寻址模式 */
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;  /* 通信速率 */
 
  I2C_Init(I2C1, &I2C_InitStructure);  /* I2C1 初始化 */
  I2C_Cmd(I2C1, ENABLE);  /* 使能 I2C1 */
}

//AT24C02 驱动初始化
void I2C_EE_Init(void)
{
  I2C_GPIO_Config();
  I2C_Mode_Config();
 
  /* 根据头文件 i2c_ee.  14 h 中的定义来选择 EEPROM 要写入的地址 */
#ifdef EEPROM_Block0_ADDRESS /* 选择 EEPROM Block0 来写入 */
  EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;
#endif
#ifdef EEPROM_Block1_ADDRESS  /* 选择 EEPROM Block1 来写入 */
  EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;
#endif
#ifdef EEPROM_Block2_ADDRESS  /* 选择 EEPROM Block2 来写入 */
  EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;
#endif
#ifdef EEPROM_Block3_ADDRESS  /* 选择 EEPROM Block3 来写入 */
  EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;
#endif
}

然后是写EEPROM实现代码:

//AT24C02页写操作
void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)
{
  while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
 
  I2C_GenerateSTART(I2C1, ENABLE);  /* Send START condition */
  while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));  /* Test on EV5 and clear it */
 
 
  I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);  /* Send EEPROM address for write */
  while (!I2C_CheckEvent(I2C1,  I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));  /* Test on EV6 and clear it */
 
 
  I2C_SendData(I2C1, WriteAddr);  /* Send the EEPROM's internal address to write to */
  while (! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));  /* Test on EV8 and clear it */
 
  while (NumByteToWrite--)  /* While there is data to be written */
  {
    I2C_SendData(I2C1, *pBuffer);    /* Send the current byte */
    pBuffer++;    /* Point to the next byte to be written */
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) );    /* Test on EV8 and clear it */
  }
 
  I2C_GenerateSTOP(I2C1, ENABLE);  /* Send STOP condition */
}
//AT24C02等待页写完毕
void I2C_EE_WaitEepromStandbyState(void)
{
  vu16 SR1_Tmp = 0;
  do
  {
    I2C_GenerateSTART(I2C1, ENABLE);    /* Send START condition */
    SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);    /* Read I2C1 SR1 register */
    I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS,  I2C_Direction_Transmitter);    /* Send EEPROM address for write */
  }
  while (!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));
 
  I2C_ClearFlag(I2C1, I2C_FLAG_AF);  /* Clear AF flag */
  I2C_GenerateSTOP(I2C1, ENABLE);  /* STOP condition */
}

//指定起始地址写入数据
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)
{
  u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
 
  Addr = WriteAddr % I2C_PageSize;
  count = I2C_PageSize - Addr;
  NumOfPage = NumByteToWrite / I2C_PageSize;
  NumOfSingle = NumByteToWrite % I2C_PageSize;
 
  /* If WriteAddr is I2C_PageSize aligned */
  if (Addr == 0)
  {
    /* If NumByteToWrite < I2C_PageSize */
    if (NumOfPage == 0)
    {
      I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }
    /* If 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();
      }
    }
  }
  /* If WriteAddr is not I2C_PageSize aligned */
  else
  {
    /* If NumByteToWrite < I2C_PageSize */
    if (NumOfPage == 0)
    {
      I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
      I2C_EE_WaitEepromStandbyState();
    }
    /* If NumByteToWrite > I2C_PageSize */
    else
    {
      NumByteToWrite -= count;
      NumOfPage = NumByteToWrite / I2C_PageSize;
      NumOfSingle = NumByteToWrite % I2C_PageSize;
 
      if (count != 0)
      {
        I2C_EE_PageWrite(pBuffer, WriteAddr, count);
        I2C_EE_WaitEepromStandbyState();
        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();
      }
    }
  }
}

最后是读EEPROM的实现:

//EEprom读取操作
void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{
  //*((u8 *)0x4001080c) |=0x80;
  while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // Added by Najoua
 
  /* Send START condition */
  I2C_GenerateSTART(I2C1, ENABLE);
  //*((u8 *)0x4001080c) &=~0x80;
 
  /* Test on EV5 and clear it */
  while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
 
  /* Send EEPROM address for write */
  I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
 
  /* Test on EV6 and clear it */
  while (!I2C_CheckEvent(I2C1,
                         I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
 
  /* Clear EV6 by setting again the PE bit */
  I2C_Cmd(I2C1, ENABLE);
 
  /* Send the EEPROM's internal address to write to */
  I2C_SendData(I2C1, ReadAddr);
 
  /* Test on EV8 and clear it */
  while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
 
  /* Send STRAT condition a second time */
  I2C_GenerateSTART(I2C1, ENABLE);
 
  /* Test on EV5 and clear it */
  while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
 
  /* Send EEPROM address for read */
  I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);
 
  /* Test on EV6 and clear it */
  while (!I2C_CheckEvent(I2C1,
                         I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
 
  /* While there is data to be read */
  while (NumByteToRead)
  {
    if (NumByteToRead == 1)
    {
      /* Disable Acknowledgement */
      I2C_AcknowledgeConfig(I2C1, DISABLE);
 
      /* Send STOP Condition */
      I2C_GenerateSTOP(I2C1, ENABLE);
    }
 
    /* Test on EV7 and clear it */
    if (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
    {
      /* Read a byte from the EEPROM */
      *pBuffer = I2C_ReceiveData(I2C1);
 
      /* Point to the next location where the byte read will be
      saved */
      pBuffer++;
 
      /* Decrement the read bytes counter */
      NumByteToRead--;
    }
  }
 
  /* Enable Acknowledgement to be ready for another reception */
  I2C_AcknowledgeConfig(I2C1, ENABLE);
}

十六宿舍 原创作品,转载必须标注原文链接。

©2023 Yang Li. All rights reserved.

欢迎关注 『十六宿舍』,大家喜欢的话,给个👍,更多关于嵌入式相关技术的内容持续更新中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/413167.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker理论总结

目录 容器技术的演变 物理机时代 虚拟化时代 容器化时代 容器化技术的应用场景 Docker介绍与基本概念 标准化的应用打包 Docker的发展 Docker是容器化平台 Docker体系架构 容器与镜像 Docker执行流程 容器内部 Tomcat容器内部结构 在容器中执行命令 容器生命周…

( “树” 之 DFS) 104. 二叉树的最大深度 ——【Leetcode每日一题】

104. 二叉树的最大深度 给定一个二叉树&#xff0c;找出其最大深度。 二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 说明: 叶子节点是指没有子节点的节点。 示例&#xff1a; 给定二叉树 [3,9,20,null,null,15,7]&#xff0c; 返回它的最大深度 3 。 思路&am…

深度学习TensorFlow

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

redis整理之-持久化

redis整理之-持久化5. 持久化5.1 持久化简介5.1.1 场景-意外断电5.1.2 什么是持久化5.2 RDB5.2.1 save指令5.2.2 bgsave指令5.2.3 save配置自动执行5.2.4 RDB三种启动方式对比5.3 AOF5.3.1 AOF概念5.3.2 AOF执行策略5.3.3 AOF重写5.3.4 AOF工作流程及重写流程5.4 RDB与AOF区别5…

python开发环境的安装,PyCharm+python解释器

文章目录注意&#xff1a;软件打包下载&#xff1a;PyCharm的下载与安装下载安装python解释器的下载与安装下载安装PyCharm软件设置注意&#xff1a; 以下是Windows下的安装&#xff0c;Linux以及其他操作系统下的安装请类比参照&#xff01; 软件打包下载&#xff1a; PyCh…

主干网络backbone讲解—— Conv4与Resnet12

1 Conv4 在一些论文中&#xff0c;也称为Conv-64F。其中“64F”表示网络中使用了64个滤波器&#xff08;filters&#xff09; 它包含 4 个重复的卷积块&#xff0c;在每个块中包含&#xff1a; 一个 kernel3&#xff0c;stride1&#xff0c;padding1的卷积层&#xff1b;一个…

jquery中的ajax方法怎样通过JSONP进行远程调用

关于JSONP的概念以及为什么要使用JSONP您可以参考JSONP教程&#xff0c;本文的重点在于演示下在JQUERY中的Ajax方法怎样通过JSONP进行远程调用。 $.ajax的参数 首先介绍下$.ajax的参数&#xff1a; type&#xff1a;请求方式 GET/POST url:请求地址 async:布尔类型&#xff…

第十四届蓝桥杯大赛软件赛省赛 C/C++ 大学 A 组题解+个人总结

提示&#xff1a;此题解为本人自己解决&#xff0c;如有差错请大家多多指正。 文章目录题解总结一、幸运数1.试题2.解法3.代码二、[有奖问答](https://blog.csdn.net/A2105153335/article/details/130038980?spm1001.2014.3001.5501)三、[平方差](https://blog.csdn.net/A2105…

spring 随笔 ioc/di 4-Bean生命周期钩子

0. RUN&#xff0c;或8RUN&#xff0c;是每年都会思考的问题 Spring框架后置处理器PostProcessor详解 BeanDefinitionRegistryPostProcessor 扩展接口 动态注册bean AOP的高级特性targetSource AnnotationAwareAspectJAutoProxyCreator#postProcessBeforeInstantiation源码解析…

第五十九章 线段树(二)

第五十九章 线段树&#xff08;二&#xff09;一、懒标记&#xff08;lazy_tag&#xff09;1、作用2、思路二、结构体定义三、带有懒标记的函数操作1、pushup函数2、build函数3、modify函数4、pushdown函数5、query函数四、代码一、懒标记&#xff08;lazy_tag&#xff09; 1、…

redis五大基本数据类型之(源码分析)

redis五大数据结构StringHashsetListZset总结String String 是最基本的 key-value 结构&#xff0c;key 是唯一标识&#xff0c;value 是具体的值&#xff0c;value其实不仅是字符串&#xff0c; 也可以是数字&#xff08;整数或浮点数&#xff09;&#xff0c;value 最多可以…

远程桌面连接是什么?远程桌面连接使用教程

有时候电脑出现各类网络连接、网址访问出错问题&#xff0c;自己无法解决的情况下&#xff0c;常常会求助其他擅长IT的同事或朋友&#xff0c;要么自己通过社交工具在线沟通、要么抱着电脑找人家解决。然而&#xff0c;通过远程桌面完全可以让朋友同事远程帮自己查看电脑问题&a…

永远加载不满的进度条

前言 各位开发大佬&#xff0c;平时肯定见到过这种进度条吧&#xff0c;一直在加载&#xff0c;但等了好久都是在99% 如下所示&#xff1a;有没有好奇这个玩意儿咋做的呢&#xff1f;细听分说 &#xff08;需要看使用&#xff1a;直接看实践即可&#xff09; fake-progress …

亚马逊云科技Serverless Data:数字经济下的创新动能

Serverless时代已经到来&#xff01;企业的技术架构&#xff0c;总是伴随着不断增长的数据与日趋复杂的业务持续演进。如何通过构建更易用的技术架构来聚焦在业务本身&#xff0c;而不必在底层基础设施的管理上投入过多的精力&#xff0c;是数据驱动型企业需要思考的重要议题。…

实操| 前端新人无敲代码开发APP

作为一种大型的基于GPT-3. 5结构的语言模型&#xff0c;ChatGPT由OpenAI训练&#xff0c;采用深度学习技术&#xff0c;通过大量的文本数据学习&#xff0c;可以生成类似于人类自然语言的文字。ChatGPT是一种非常强大的对话引擎&#xff0c;能进行对话、回答问题和完成任务。Ch…

数据库锁原理

数据库锁原理锁的定义InnoDB中的锁模式共享锁独占锁共享意向锁和独占意向锁LOCK_AUTO_INC自增锁INNODB_AUTOINC_LOCK_MODEInnoDB中的锁类型表锁行锁行子类型LOCK_REC_NOT_GAP精准行锁LOCK_GAP行GAP锁LOCK_ORDINARY行NEXT-KEY锁LOCK_INSERT_INTENTION插入意向锁锁的定义 为了体…

数据分析之Matplotlib 基础入门

目录 第一章 什么是Matplotlib 常见图表及其分类 Matplotlib 第一个绘图程序 第二章 Matplotlib 基础 Matplotlib 图表常用设置 颜色设置 线条样式和标记样式 画布设置 设置坐标轴标题 设置坐标轴刻度 设置坐标轴范围 设置网格线 设置文本标签和标题 添加图例 添…

Image Deconvolution with the Half-quadratic Splitting Method

Image Deconvolution with the Half-quadratic Splitting Method 在处理图像重建或者逆问题的时候&#xff0c;我们经常会看到一种称为 Half-quadratic Splitting&#xff08;HQS&#xff09;的方法&#xff0c;这是在优化领域里非常经典的一种方法&#xff0c;之前也断断续续…

【Cesium 编程第一篇】概述、环境搭建、界面介绍

年前年后一直在面试&#xff0c;发现一个奇怪的现象&#xff1a;很多互联网公司经受住三年的疫情冲击&#xff0c;反而在疫情放开的那一刻撑不住了&#xff0c;很多大厂都在批量的裁员&#xff1a;美国硅谷、北京字节、迪士尼中国等等。在北京的朋友也是年后到现在一直没有找到…

AI是一场革命,我真不是在跟风

AI是场革命&#xff0c;好像现在很多人都开始这么说&#xff0c;那么我说我不是在跟风&#xff0c;为什么&#xff1f;不好意思&#xff0c;又要翻翻旧贴 -> AI是一场革命&#xff0c;不要笑&#xff0c;我是认真的。2016年我就这样讲了&#xff0c;就如我常说的&#xff0c…