目录

参考调试MPU6050与EEPROM的经验,整合了目标内存/寄存器地址是否为16位的情况,合并了单字节与多字节间的操作,添加了返回值与读写超时功能:硬件IIC的7位从机地址查询方式读写参考代码 - JayWell - 博客园 (cnblogs.com)

根据手上这片EEPROM的型号,24LC515,自行搜索了手册,发现其读写时序和之前调的MPU6050使用的并无很大区别,只是对于MPU6050来说,读写的内容是8位寄存器地址中的值,而对于手里的这片EEPROM来说,读写的内容体现在代码上是16位存储内存地址中的值(第15位最高位是无效位,实际使用15位)。24XX515,结合A0A1A2三根线上的信号,8片组合起来能存储512K数据。一片EEPROM内有两块,每个块内寻址需要15位二进制数据。(IIC的重映射会有点问题,建议先使用PB12、13的硬件IIC)

手册参考网页:24LC515 datasheet(1/22 Pages) MICROCHIP | 512K I2C CMOS Serial EEPROM (alldatasheetcn.com)

进行了如下改写↓

//从从机的某寄存器,读取一个字节的数据
void IIC_16b_read_byte(uint8_t addr, uint16_t mem, uint8_t *des)
{
//主机通知从机要读取它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送器件地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData((uint8_t)mem); //发送内存地址的低8位
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags //直接产生一个重起始信号即可开始读的过程
I2C_GenerateSTART(ENABLE); //重起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Receiver); //发送地址+最低位1表示为“读”
while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //判断BUSY, MSL and ADDR flags I2C_GenerateSTOP(DISABLE);
  //关闭停止信号使能(某些情况下可能会在此清除PE,这一行可以去掉) I2C_AcknowledgeConfig(DISABLE);
  //关闭ACK使能,接收一个字节数据后,主机就回NACK表示不再接收数据
  //(某些情况下可能会在此清除PE,这一行可以去掉,但逻辑分析仪抓时序,会多接收一个字节数据)
                        
while(!I2C_GetFlagStatus(I2C_FLAG_RXNE)); //获取RxEN的状态,等待收到数据
*des = I2C_ReceiveData(); //获得从机的寄存器中的数据
I2C_GenerateSTOP(ENABLE); //停止信号
I2C_AcknowledgeConfig(ENABLE); //传输完毕,再次打开ACK使能
} //从从机的某寄存器起始,连续读取n个字节的数据
void IIC_16b_read_nBytes(uint8_t addr, uint16_t mem, uint8_t *des, uint8_t len)
{
//主机通知从机要读取它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送器件地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData((uint8_t)mem); //发送内存地址的低8位
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags //直接产生一个重起始信号即可开始读的过程
I2C_GenerateSTART(ENABLE); //重起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Receiver); //发送地址+最低位1表示为“读”
while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //判断BUSY, MSL and ADDR flags I2C_GenerateSTOP(DISABLE);
  //关闭停止信号使能(某些情况下可能会在此清除PE,这一行可以去掉) for(uint8_t i=0; i<len; i++)
{
if(i == len-1)
I2C_AcknowledgeConfig(DISABLE);   
    //清除ACK位(某些情况下可能会在此清除PE,这一行可以去掉,但逻辑分析仪抓时序,会多接收一字节数据)
    //主机为了能在收到最后一个字节后产生一个NACK信号,必须在读取倒数第二个字节之后(倒数第二个RxNE 事件之后)清除ACK位(ACK=0)
while(!I2C_GetFlagStatus(I2C_FLAG_RXNE)); //获取RxEN的状态,等待收到数据
*(des+i) = I2C_ReceiveData(); //获得从机的寄存器中的数据
} I2C_GenerateSTOP(ENABLE); //使能停止信号
I2C_AcknowledgeConfig(ENABLE); //传输完毕,再次打开ACK使能
} //向从机的某内存地址写入1个字节的数据
void IIC_16b_write_byte(uint8_t addr, uint16_t mem, uint8_t data)
{
//主机通知从机要写它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData((uint8_t)mem); //发送内存地址的低8位 //ACK之后直接写入数据
while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData(data); //发送数据
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags
I2C_GenerateSTOP(ENABLE); //停止信号
} //向从机某寄内存地址起始,连续写入n个字节的数据
void IIC_16b_write_nBytes(uint8_t addr, uint16_t mem, uint8_t *src, uint8_t len)
{
//主机通知从机要写它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData((uint8_t)(mem>>8)); //发送内存地址的高8位 while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData((uint8_t)mem); //发送内存地址的低8位 //ACK之后直接写入数据
for(uint8_t i=0; i<len; i++)
{
while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData(*(src+i)); //发送数据
} while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags
I2C_GenerateSTOP(ENABLE); //停止信号
}

然后就出了BUG,连续写可以往里写,连续读数据时,故意将地址往后偏移了几个单位,读的值却和没偏移一样,往后偏移64个单位也一样,似乎只能“按扇区读写”乃至“按块读写”。连续写后单字节读也是类似的现象,地址往后稍微偏移,也只能读出最近一次写入的第一个字节的数据。逻辑分析仪抓包,与串口打印数据一致,ACK/NACK都正常。

试用8位内存地址寻址,不用16位寻址,结果居然一切正常……可是8位地址+块选择控制位,怎么也不能达到1片EEPROM的64K的存储量呀……手册中的读写时序也都是16位内存地址……

现象就是这么个现象,可能是笔者忽略了一些东西,又或者买到了假EEPROM……总之,代码直接贴出,该篇博客均是在从机设备地址为7bit的情况下编写的代码,可在CH582上用该代码交互其他IIC从机设备。

//从从机的某寄存器,读取一个字节的数据
void IIC_8b_read_byte(uint8_t addr, uint8_t mem, uint8_t *des)
{
//主机通知从机要读取它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送器件地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData(mem); //发送内存地址
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags //直接产生一个重起始信号即可开始读的过程
I2C_GenerateSTART(ENABLE); //重起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Receiver); //发送地址+最低位1表示为“读”
while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //判断BUSY, MSL and ADDR flags I2C_GenerateSTOP(DISABLE);
  //关闭停止信号使能(某些情况下可能会在此清除PE,这一行可以去掉) I2C_AcknowledgeConfig(DISABLE);
  //关闭ACK使能,接收一个字节数据后,主机就回NACK表示不再接收数据
  //(某些情况下可能会在此清除PE,这一行可以去掉,但逻辑分析仪抓时序,会多接收一个字节数据) while(!I2C_GetFlagStatus(I2C_FLAG_RXNE)); //获取RxEN的状态,等待收到数据
*des = I2C_ReceiveData(); //获得从机的寄存器中的数据
I2C_GenerateSTOP(ENABLE); //停止信号
I2C_AcknowledgeConfig(ENABLE); //传输完毕,再次打开ACK使能
} //从从机的某寄存器起始,连续读取n个字节的数据
void IIC_8b_read_nBytes(uint8_t addr, uint8_t mem, uint8_t *des, uint8_t len)
{
//主机通知从机要读取它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送器件地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData(mem); //发送内存地址
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags //直接产生一个重起始信号即可开始读的过程
I2C_GenerateSTART(ENABLE); //重起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Receiver); //发送地址+最低位1表示为“读”
while(!I2C_CheckEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); //判断BUSY, MSL and ADDR flags I2C_GenerateSTOP(DISABLE);
  //关闭停止信号使能(某些情况下可能会在此清除PE,这一行可以去掉) for(uint8_t i=0; i<len; i++)
{
if(i == len-1)
I2C_AcknowledgeConfig(DISABLE);
  //清除ACK位(某些情况下可能会在此清除PE,这一行可以去掉,但是逻辑分析仪抓时序,会多接收一个字节数据)
  //主设备为了能在收到最后一个字节后产生一个NACK信号,必须在读取倒数第二个字节之后(倒数第二个RxNE 事件之后)清除ACK位(ACK=0)
while(!I2C_GetFlagStatus(I2C_FLAG_RXNE)); //获取RxEN的状态,等待收到数据
*(des+i) = I2C_ReceiveData(); //获得从机的寄存器中的数据
} I2C_GenerateSTOP(ENABLE); //使能停止信号
I2C_AcknowledgeConfig(ENABLE); //传输完毕,再次打开ACK使能
} //向从机的某内存地址写入1个字节的数据
void IIC_8b_write_byte(uint8_t addr, uint8_t mem, uint8_t data)
{
//主机通知从机要写它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData(mem); //发送内存地址 //ACK之后直接写入数据
while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData(data); //发送数据
while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags
I2C_GenerateSTOP(ENABLE); //停止信号
} //向从机某寄内存地址起始,连续写入n个字节的数据
void IIC_8b_write_nBytes(uint8_t addr, uint8_t mem, uint8_t *src, uint8_t len)
{
//主机通知从机要写它的哪块内存
while(I2C_GetFlagStatus(I2C_FLAG_BUSY)); //IIC主机判忙
I2C_GenerateSTART(ENABLE); //起始信号
while(!I2C_CheckEvent(I2C_EVENT_MASTER_MODE_SELECT)); //判断BUSY, MSL and SB flags
I2C_Send7bitAddress(addr, I2C_Direction_Transmitter); //发送地址+最低位0表示为“写” while(!I2C_CheckEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //判断BUSY, MSL, ADDR, TXE and TRA flags
I2C_SendData(mem); //发送内存地址 //ACK之后直接写入数据
for(uint8_t i=0; i<len; i++)
{
while(!I2C_GetFlagStatus(I2C_FLAG_TXE)); //获取TxE的状态 数据寄存器为空标志位,可以向其中写数据
I2C_SendData(*(src+i)); //发送数据
} while(!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //判断TRA, BUSY, MSL, TXE and BTF flags
I2C_GenerateSTOP(ENABLE); //停止信号
}

以EEPROM为例的硬件IIC的使用的更多相关文章

  1. STM32 IIC双机通信—— HAL库硬件IIC版

    参考传送门 关于IIC的原理这里我就不多说了,网上有很多很好的解析,如果要看我个人对IIC的理解的话,可以点击查看,这里主要讲一下怎样利用STM32CubeMx实现IIC的通讯,经过个人实践,感觉HA ...

  2. STM32读取bq33100数据——硬件IIC

    背景:拟采用bq33100超级电容管理芯片,实现自动的超级电容组的均压任务.需监控芯片的工作情况,以及电容组的均压情况. 平台: 硬件:STM32F103C8T6 通信:SMBus(低速IIC) 目标 ...

  3. STM32硬件IIC操作

    Stm32具有IIC接口,接口有以下主要特性 多主机功能:该模块既可做主设备也可做从设备 主设备功能 C地址检测 位/10位地址和广播呼叫 支持不同的通讯速度 状态标志: 发送器/接收器模式标志 字节 ...

  4. STM32硬件IIC (转)

    源: STM32硬件IIC

  5. STM32硬件IIC驱动设计(转)

    源: STM32硬件IIC驱动设计 参考: STM32—硬件IIC主机通信 STM32’s I2C 硬件BUG引发的血案(qzm) 解决STM32 I2C接口死锁在BUSY状态的方法讨论

  6. STM32硬件IIC

    /** * @brief 写一个字节到I2C设备中 * @param * @arg pBuffer:缓冲区指针 * @arg WriteAddr:写地址 * @retval 正常返回1,异常返回0 * ...

  7. LPC2478的硬件IIC使用

    LPC2478的IIC使用 LPC2478带有三个IIC接口,每个IIC都可以工作在主机或者从机模式下,LPC的IIC的架构是一种状态机的形式,在不同的的时间做不同的工作之后有不同的状态来表示, 简单 ...

  8. (转)stm32硬件IIC

    cube与I2C:https://www.cnblogs.com/121792730applllo/p/5044920.html I2C官网:https://www.i2c-bus.org/stand ...

  9. 硬件IIC驱动原理

    1.IIC物理层 IIC通信属于同步半双工通信,IIC总线由两根信号线组成.一根是数据线SDA,一根是时钟线SCL,时钟线只能由主机发送给从机,数据线可以双向进行通信,总线上可挂载多个设备,挂载数量受 ...

  10. S3C2440硬件IIC详解

    S3C2440A RISC微处理器可以支持一个多主控IIC 总线串行接口.一条专用串行数据线(SDA)和一条专用串行时钟线(SCL)传递连接到IIC总线的总线主控和外设之间的信息.SDA和SCL线都为 ...

随机推荐

  1. 均有商业公司支持!2023再看数据湖 hudi iceberg delta2 社区发展现状!

    开源数据湖三剑客 Apache hudi.Apache iceberg .Databricks delta 近年来大动作不断. 2021年8月,Apache Iceberg 的创始人 Ryan Blu ...

  2. 踩坑纪实----tomcat部署前端服务器不能访问中文文件夹或中文文件名问题

    修改tomcat的server.xml文件(解决含有中文的文件.图片的不能下载.显示的问题): 找到下列配置信息在xml文件中的位置,添加黑体字部分的参数即可(disableUploadTimeout ...

  3. 1月3日内容总结——bbs项目登陆页面和主页、个人站点页的搭建

    目录 一.登陆功能完善 验证码功能实现 单机验证码实现验证码刷新(局部刷新) 点击登陆提交数据进行校验 二.主页搭建 html代码 views.py代码 主页内容部分 后台添加数据 分页器 前端获取头 ...

  4. C#DataTable(转List /JSON/字典 互转)

    1 #region 数据表DataTable 转键值对集合 List 2 /// <summary> 3 /// 数据表DataTable 转键值对集合 List 4 /// 把DataT ...

  5. Nginx10 Lua入门 + openresty

    1 Idea中创建Lua项目 lua官网:https://www.lua.org/ 1.1 添加插件,重启idea 1.2 创建项目 file-New Project 1.3 创建lua文件 1.4 ...

  6. Snipaste下载安装(使用教程)

    ## Snipaste下载安装(使用教程)**一 简单介绍** Snipaste 是一个免费简单但强大的截图工具,也可以让你将截图贴回到屏幕上!下载并打开 Snipaste,按下 F1 来开始截图,再 ...

  7. python爬虫(含scrapy)

    python爬虫 1.解析HTML: lxml 是一种python编写的库,可以迅速.灵活的处理xml和html 使用:根据版本的不同,有如下两种: 形式1: from lxml import etr ...

  8. 关于vux-ui框架的scroller组件所踩的坑

    这是我在做一个demo的一个上垃加载下拉刷新功能时所遇到的问题,由于伤了好一会脑筋,所以留下这篇笔记以供后续查询: 在上代码前建议在开发项目时不要优先选择vux这个框架,因为有一些常用的功能组件官方已 ...

  9. Protocol Buffers 3 学习

    一.定义消息 1.首先看一个简单的例子: 1 syntax = "proto3"; 2 3 message SearchRequest { 4 string query = 1; ...

  10. slate源码解析(一)- 序言

    笔者从大学时期就开始接触的前端,在刚去实习的时候就被导师安排去做内网的一个小富文本工具.之后从毕业后干的第一份工作游戏客户端,到现在做着可视化相关的前端工作,都有在做富文本相关的内容.可以说是和富文本 ...