目录

参考调试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. angular打包出现JavaScript堆内存不足、启动也会内存溢出解决办法\increase-memory-limit' 不是内部或外部命令,

    ## 打包出现JavaScript堆内存不足 最近打包遇到这种报错 Ineffective mark-compacts near heap limit Allocation failed - Java ...

  2. Java入门与进阶 P-2.1+P-2.2

    比较运算符 关系运算符(relational operators)也可以称为"比较运算符",用于用来比较判断两个变量或常量的大小.关系运算符是二元运算符,运算结果是 boolean ...

  3. Node.js+Koa2+TypeScript技术概览

    最近几年一直使用Node.js作为后端服务平台,通过Koa2框架中间件快速搭建Web服务,但是使用JavaScript开发大型后端服务时会使程序变得难以维护,继而使用TypeScript语言开发,使编 ...

  4. you-get下载B站视频

    you-get下载B站视频 库存 you-get介绍 https://github.com/soimort/you-get you-get是一个命令行工具,可以下载知名网站的视频 支持的网站有  ht ...

  5. javaWeb03-请求转发和请求重定向【包含浏览器与响应编码格式不一致的解决方法】

    本文主要讲述javaWeb的请求转发和请求重定向的区别 一. 请求转发 1. 图解 2. 代码示例 Servlet1的代码示例如下 public class Servlet1 extends Http ...

  6. Vue21 组件

    1 模块及组件简介 组件(component)是vue.js最强大的功能之一.组件的作用就是封装可重用的代码,通常一个组件就是一个功能体,便于在多个地方都能够调用这个功能体. 每个组件都是Vue的实例 ...

  7. 电商网站Web自动化测试实战( 编写京东搜索脚本python+selenium框架)

    电商网站Web自动化测试实战( 编写京东搜索脚本) 1,打开京东页 京东首页地址:https://www.jd.com/,故进入京东首页如下: 2,打开浏览器开发者模式 定位元素前需先打开浏览器开发者 ...

  8. 【亲妈教学】配置Gzip压缩,含前后端步骤

    前言 在使用 vite 进行项目打包时,默认已经帮我们做了一些优化工作,比如代码的压缩,分包等等. 除此之外,我们还有一些可选的优化策略,比如使用 CDN ,开启 Gzip 压缩等.本文会介绍在 vi ...

  9. 2020.11.30【NOIP提高A组】模拟

    总结与反思 很不幸,估分 \(170\),可惜 \(T2\) 暴力 \(50pts\) 全掉了 \(T1\) 结论题,如果想到了,\(O(n)\) 过,只有十几行代码 感觉不好想,不过还是 \(A\) ...

  10. python破解zip或者rar压缩文件

    转载博客园python大师:https://www.cnblogs.com/daniumiqi/p/12167764.html