IC读写EEPROM问题总结
- 格式:docx
- 大小:49.73 KB
- 文档页数:10
2017年6月30日星期五目(de):利用TMS320F2801芯片上外设I2C(2线串口)读写EEPROM数据(24LC128)关键点1:24LC时钟频率400KHz,寄存器设置如下:I2caRegs.I2CPSC.all = 9; // Prescaler - need 7-12 Mhz on module clkI2caRegs.I2CCLKL = 10; // NOTE: must be non zeroI2caRegs.I2CCLKH = 5; // NOTE: must be non zero时钟频率也可设为200KHz,三个参数分别为9、20、20(CPU时钟频率为100MHz)(未测试)关键点2:波形分析问题:I2C模块是不是只有I2CCNT 减到0才会发出停止信号I2C模块是硬件(de),当检测到发送完了就会发结束自动发信号,不需要人为干预问题1:字节写操作正常,但是字节读函数出错原因:写EEPROM是在七位器件地址后添加写标志,而读EEPROM需要在七位器件地址后添加写标志.关键点:读EEPROM数据需要发送两次命令.第一次为写地址(此地址会被赋值给EEPROM内(de)地址指针),因此需要添加写标志;第二次为读数据,将写标志改为读标志.问题2:主机接收时,SDA数据线上有数据传输,且I2CDRR接收数据寄存器有数据更新,但寄存器显示不可读,即CPU认为一直没接收到数据,一直停在下面语句while关键点:初始化设置时采用(de)是FIFO接收方式,因此无效,应查询FIFO 接收中断位while方式查询位.此位只有在非FIFO中断接收方式时才有效.问题3:断续单字节读写正常,但是采用连续(de)单字节读写出错.原因:EEPROM写过程(de)结束并不是I2C总线写结束就结束,实际上I2C 总线(de)写入数据先被保存到了EEPROM内部(de)缓冲区,当遇到I2C结束条件后,EEPROM才启动内部写过程,这个过程才是保存数据(de)过程.非常悲哀(de)是这个过程比较长,官方文档标注为5ms.如果在这5ms以内对EEPROM芯片访问将被忽略.关键点:读写EEPROM应延时至少5ms,软件延时10msdo{}while(EEPROM_Timer <= 10); //10ms问题4:查询EEPROM写过程是否结束造成死机,只能查询EEPROM读过程.官方文档说EEPROM内部写周期最长为5ms,在很多情况下是远远低于5ms(de),为了节约时间,官方给出一个解决办法.当写周期完毕后就开始进行应答查询,来确定EEPROM写周期何时结束.所谓应答查询官方解释为:就是向EEPROM发送一个I2C起始条件后发送器件地址和一个读写标志位,当EEPROM完成内部写周期会回应一个ACK,这时MCU就可以进行正常(de)其他读写过程了.官方原文如下:杯具就是始于我画红线(de)那句话,它说可以在器件地址后任意填写读写标志.我就填了读标志,事实证明在EEPROM写入过程采用读查询将导致系统死机,I2C总线不能被正常拉高,可能是EEPROM内部已经把总线拉到了地不管怎样,反正就是死机了.经过多次尝试最终发现应答查询只能采用写应答查询可以正常确定EEPROM内部写周期(de)结束.——by数据传输过程:从机发送数据 SDA总线 I2CRSR缓冲寄存器I2CDRR接收数据寄存器中间变量内存.问题5:CPU以字(双字节)为单位读写EEPROM数据有误;原因:CPU中(de)内存单元以字(word-16bit)为单位,而EEPROM中(de)内存单元是字节(byte-8bit)为单位,因此将CPU内存地址转化为EEPROM 指针地址时,因乘以2(左移一位)问题6:使用读数据函数式,收到(de)数据都是1.原因:EEPROM初次读取未写过(de)内存单元时,默认为高电平,即收到(de)字节为0xFF.如果已经写过内存单元,则代表数据未成功写入;写入与读数据(de)内存地址不一样.问题7:写入EEPROM(de)数据与随后读出来(de)数据不一致,但读出来(de)数据又没有规律性.可能原因:数据未成功写入;数据读写字节数超过EEPROM(de)页内字节数(跨页);读写地址不一致;读写EEPROM之间应有一定(de)延时时间.解决办法:若连续读多字节数据,则读取数据之间应加延时,因为数据从I2CRSR数据接收缓冲寄存器(多字节)复制到I2CCDRR数据接收寄存器(一字节)需要时间.单字节延时25us,双字节(字)延时50us——测试通过问题8:使用示波器观测SDA数据线上(de)波形时,发现每次应答信号之前都有一个毛刺(尖峰),是什么原因导致(de)(不影响数据(de)正常读写)类似问题:使用F28335模拟I2C时序读取惯导器件(de)数据时,发现在更改SDA(de)传输方向时,Gpio中数据寄存器会发生变化,导致SDA上有毛刺产生.问题9: I2C在跟EEPROM通讯(de)时候,第一次写入数据,一个一个读取(de)话,能知道写入EEPROM(de)值是没有错(de),但是在连续读取数据(de)时候,就会出现,上电第一次读取数据串(de)时候,是全部读取正确,然后再读取一遍数据串(de)时候,只有第一个读取(de)数据是正确(de),后面(de)数据会全部变成FF FF 这是怎么回事办法1:大家都说STM32(de)IIC有点bug,所以很少人用其自带(de)IIC,一般都是用IO口模拟IIC,模拟很简单而且不会出错.逻辑分析仪抓取I2C总线数据, 改为转接板抓取数据,即I2C转USB通讯.问题10:I2C给EEPROM写数据时,两字节地址需不需要算进去吗答案:需要,且地址字节数与EEPROM(de)型号(容量)相关,有些为1字节地址,有些为2字节地址.24LC128需要两字节地址来区分内存单元,其内存最小单元为1字节,地址从0x00开始,一页64字节,因此地址指针范围为:0x00~0x3F.I2caRegs.I2CCNT = (n<<1)+2;问题11:一次读写数据字节数最好不超过16字节.原因:其一I2C深度寄存器范围限定,其二,读写数据字节太多会导致I2C 总线出错(de)概率加大.ST_5bit (0x0000~0x10000)I2caRegs.I2CFFTX.bit.TXFFIL_5bitI2caRegs.I2CFFRX.bit.RXFFST_5bit(0x0000~0x10000)I2caRegs.I2CFFRX.bit.RXFFIL_5bitI2caRegs.I2CCNT_32bit问题12:总是提示总线繁忙= 1总线繁忙,BB = 0 总线空闲关键:总线繁忙这个位只读.猜想解决办法:如果是上电第一次读写就出现总线繁忙,就对I2C模块进行复位;如果不是第一次,且I2C总线上只有一主,则等待一定时间(5ms);如果是多主,则返回,等待总线空闲吧.问题13:CPU写数据给EEPROM时,如果设置断点,就能成功写入数据,但没有断点,数据写不成功.什么原因呀原因:写保护WP引脚(de)电平应在接收到停止信号后,应保持低电平一段时间才使能写保护,即加延时语句.———已测试通过//所有字节(地址字节+数据字节)都是添加写标志,即低电平,2n+2 <=64////写EEPROM地址从0开始//void EEPROM_Write_call(Uint16 address,Uint16 n,Uint16 s){Uint16 i,data_temp;do{}while(EEPROM_Timer <= 20); //20ms// Check if bus busyif (I2caRegs.I2CSTR.bit.BB = 0)//总线忙位,不能手动清除{return;}if(WP = 0) WP = 0; //清除从机EEPROM写保护模式// Setup slave address-7bitI2caRegs.I2CSAR = 0x50;I2caRegs.I2CCNT = (n<<1)+2; // Setup number of bytes to send //set up write modeI2caRegs.I2CMDR.all= 0x6E20;// Send start as master transmitter //发起始信号//主机发送停止信号//TX modeif(I2caRegs.I2CSTR.bit.NACK = 0) return;//存储器首地址——2字节(14bit)while(I2caRegs.I2CSTR.bit.XRDY = 1);I2caRegs.I2CDXR = (address>>8);if(I2caRegs.I2CSTR.bit.NACK = 0) return;while(I2caRegs.I2CSTR.bit.XRDY = 1);I2caRegs.I2CDXR = (address&0x00FF);if(I2caRegs.I2CSTR.bit.NACK = 0) return;// Setup data to sendfor(i=0; i<n; i++){data_temp = MK_Data[s+i];while(I2caRegs.I2CSTR.bit.XRDY = 1);I2caRegs.I2CDXR = (data_temp&0x00FF);if(I2caRegs.I2CSTR.bit.NACK = 0) return;while(I2caRegs.I2CSTR.bit.XRDY = 1);I2caRegs.I2CDXR = data_temp >> 8;if(I2caRegs.I2CSTR.bit.NACK = 0) return;}do{}while(I2caRegs.I2CSTR.bit.SCD = 1); //是否有停止信号WP = 1; //保护EEPROM,使其只读EEPROM_Timer = 0;}问题14:波形错误——用I2C接口,SCK 和 SDA都接有4.7K(de)上拉电阻,用示波器抓SCK和SDA(de)波形,发现SCK时序正常,SDA异常,见附图(黄为SCK,紫为SDA),请问这个锯齿波形大概是什么原因造成(de)呢——by猜测原因:可能是I2C模块时钟频率设置不合理;——未验证IIC中断作用:IIC中断和UART中断一样,你可以立刻得到数据,而不需要总是查询.IIC接收数据只是存到指定(de)寄存器中,如果你不取走,下次再接收数据就直接冲掉了,所以IIC接收到数据之后给CPU中断,去处理这些收到(de)数据查找中断源是一种保险(de)做法,要是由于其他(de)哪几种原因产生了中断,但是此时数据并没有接收完,中断服务子程序去处理数据了,结果就不对了如果你自己敢保证不会出现哪几种情况就可以完全不用写——by//I2C (接收)interrupt void i2c_int1a_isr(void) // I2C-A{Uint16 IntSource; // Read interrupt sourceswitch(IntSource){case I2C_NO_ISRC: break; // =0case I2C_ARB_ISRC: break; // =1case I2C_NACK_ISRC:break; // =2case I2C_ARDY_ISRC:break; // =3case I2C_RX_ISRC: // =4InData[I2cIndex++] = I2caRegs.I2CDRR; break;case I2C_TX_ISRC: break; // =5case I2C_SCD_ISRC: break; // =6case I2C_AAS_ISRC: break; // =7default: //asm(" ESTOP0"); // Halt on invalid number. asm(" RPT 5 ||NOP ");} // Enable future I2C (PIE Group 8) interrupts}。
eeprom读写程序详解EEPROM(Electrically Erasable Programmable Read-Only Memory) 是一种可编程只读存储器,可以在电信号的作用下进行擦写和改写。
它通常用于存储单片机或其他嵌入式系统中的数据、设置参数、配置文件等。
对于 EEPROM 的读写程序,需要考虑以下几个方面:1. 读操作:读操作通常包括以下步骤:- 等待上次读操作完成。
- 获取要读取的数据的单元地址。
- 将 EEPGD 位和 CFGS 位清零。
- 启动读操作控制位 RD。
- 等待本次读操作完成。
- 将该单元地址中对应的数据返回。
在读取 EEPROM 数据时,为了避免芯片在一瞬间无法获取到数据,需要给芯片一定的时间来稳定获取数据。
因此,在读取操作中需要加入等待步骤。
2. 写操作:写操作通常包括以下步骤:- 等待上次写操作完成。
- 获取要写的数据的单元地址。
- 将要写的数据写入 EEPROM 的单元中。
- 将 EEPGD 位和 CFGS 位清零。
- 启动写操作控制位 WP。
- 等待写操作完成。
在写操作中,为了确保数据的可靠性,需要将要写的数据写入EEPROM 的单元中,并等待写操作完成。
同时,在写操作过程中,需要注意避免对无关的单元进行写操作,以免损坏 EEPROM 芯片。
3. 中断处理:在 EEPROM 的读写操作中,通常需要加入中断处理机制,以便在读写过程中及时响应和处理异常情况。
例如,在读取 EEPROM 数据时,如果 EEPROM 芯片出现故障,可能会导致读取失败。
为了避免这种情况,可以在读取操作中加入中断处理机制,在读取失败时及时报警或采取相应的应对措施。
总之,EEPROM 读写程序的实现需要考虑多个方面的因素,包括读操作、写操作、中断处理等。
同时,需要考虑 EEPROM 芯片的特性和限制,以便实现高效、稳定、可靠的 EEPROM 读写操作。
dzjk2010-11-14 09:58貌似使用STC单片机内部EEPROM有一定的局限wieke982010-11-14 10:41 要分块的,如果两个字节是同时写入的可以放在一个块里,如果不是要放在两个块里!fang32010-11-14 13:11MOV DPTR,#2000H;指针指向第一扇区EEPROMW:;写MOV ISP_CONTR,#1;设置等等待时间ORL ISP_CONTR,#80h;允许ISP/IAP操作MOV ISP_CMD,#02H ;送写命令MOV ISP_ADDRH,DPH ;送高地址MOV ISP_ADDRL,DPL ;送低地址MOV ISP_DATA,A ;A是要写入的数据ACALL ISPXX ;触发RET上面的只是写一个字节A而已wieke982010-11-14 14:33EEPROMW:;写MOV R1,#30H;30h和31H是要写的数据MOV R3,#2;要写的字节数FGRW:MOV ISP_CONTR,#1;设置等等待时间ORL ISP_CONTR,#80h;允许ISP/IAP操作MOV ISP_CMD,#02H ;送写命令MOV ISP_ADDRH,DPH ;送高地址MOV ISP_ADDRL,DPL ;送低地址MOV A,@R1;将要写的数据传入累加器MOV ISP_DATA,A ;A是要写入的数据ACALL ISPXX ;触发INC DPTRINC R1DJNZ R3,FGRW;是否写完,没写继续fang32010-11-14 21:18FGRW:MOV ISP_CONTR,#1;设置等等待时间ORL ISP_CONTR,#80h;允许ISP/IAP操作MOV ISP_CMD,#02H ;送写命令MOV ISP_ADDRH,DPH ;送高地址MOV ISP_ADDRL,DPL ;送低地址MOV A,@R1;将要写的数据传入累加器MOV ISP_DATA,A ;A是要写入的数据ACALL ISPXX ;触发因为说扇区是空的才能写还是搞不明白,如果写完一个字节,那么这个扇区已经不是空的了,还能写第二个字节吗wieke982010-11-15 10:30所谓空就是是0FFH,在擦除之后就是0FFH都可以写入了!fang32010-11-15 22:35 哦,有点明白了,,,就是说,在一个扇区里,只要是空的单元就可以写入,谢谢哪么,你说的要分块的,如果两个字节是同时写入的可以放在一个块里,如果不是要放在两个块里!怎么解释?wieke982010-11-16 08:19因为是整块擦除的,每一块中的每一个字节只能写入一次第二次要写入就必须重新擦除,一擦除整块的数据都会被擦除,所以不是同时修改的数据要放在不同的块中。
/*----------------------------------------------------------------------------------------------------------IIC总线读写EEPROM(串行扩展eeprom,24c02)(STC12C系列单片机自带eeprom,且有另外的eeprom操作方式)作者:Allen.H(帮同学修改的一个程序)时间:2010.11.5----------------------------------------------------------------------------------------------------------*/#include <reg52.h>#include <intrins.h>//是用括号还是双引号看情况,本地头文件用双引号,系统头文件用括号//这里使用了_nop_()函数,所以调用此头文件#define TRUE 1/*define宏定义一般用大写,宏定义并不会减少最终代码空间define多行语句时,每一行末尾写上\,最后一行可以不写,有时比较短的语句写成一个子函数会牺牲更多的时间,因为函数调用耗时比较多,这个时候用一个define语句更好*/#define FALSE 0typedef unsigned char uchar;//良好的程序风格,不应该用#define//#define uchar unsigned charsbit sda=P2^0; //---------你把sda和scl引脚可能定反了,我换过来了-------------------------------sbit scl=P2^1;//等号对其,变量名长短不一时,注意,且测试等于号"=="或者其他双目关系运算符两边都空一格//-----------------------------------------------------------------void delay(uchar z)//带参数很好{//大括号所在行不要写代码uchar i,j;//局部变量中用来自加自减可以用i,j之类的定义,计数建议不要用i,j//局部变量不占内存,函数调用时生成堆栈,不应该定义局部变量时作初始化//----局部变量命名后空一格,写正式代码for(i=z;i>0;i--)for(j=100;j>0;j--);//注明多少时间,在调试模式下,看窗口左边的SEC值}//函数与函数之间空一格void delay_7nop()//子程序命名最好顾名思义,比如delay_1ms(),这里考虑都是使用7nop,不带参数{/*程序代码每进一层逻辑就缩进一格TAB键,TAB设置为3,4格,在keil的view->options里面设置,不要使用几个空格来缩进,统一使用TAB键*/_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();//这里0-1000多个_nop_都可以}//delay函数都放在一起,函数顺序不要乱放,相关的放一起,//--------------------------------------------------------------------void init(){sda=1;delay_7nop();scl=1;delay_7nop();}//---SCL线为高电平期间,SDA线由高电平向低电平的变化表示起始信号;//SCL线为高电平期间,SDA线由低电平向高电平的变化表示终止信号。
EEPROM 数据被破坏的主要原因有:1、电源异常使EEPROM的数据彻底丢失;2、复位不好和软件跑飞可能会使EEPROM的数据被改写。
要防止EEPROM数据被破坏,主要在以下几方面做工作:1、选用比MCU的电源范围宽并有WP引脚的EEPROM芯片;2、做好电源滤波,而且要等电源开机稳定后才去读写EEPROM;3、做好复位电路;4、做好软件跑飞的处理;5、SDA和SCK的上拉最好用I/O口控制,既可省电,也可在一定情况下保护EEPROM;6、WP接MCU的RESET;如WP做软件保护,将写不进数据;接I/O,上电时WP的状态可能不稳定。
7、EEPROM空间富余时考虑双备份或多备份数据,每份数据都有校验和.选用比MCU的电源范围宽并有WP引脚的EEPROM芯片的原因:1、EEPROM的芯片本身有一定的保护时序;2、电源低于MCU工作电源高于EEPROM芯片的最低工作电源时,EEPROM芯片会处于稳定状态,不会丢失数据.3、当电源较长时间低于EEPROM芯片的最低工作电压时非常容易丢失全部数据.否则MCU还能工作,但EEPROM芯片已不能工作时,EEPROM中的数据会全部丢失。
4、用I/O口线给EEPROM供电,只在读写EEPROM时才给器件供电,不仅能提高可靠性,而且能省电。
但有两点要注意:一是一些单片机复位时所有I/O都是高电平,会使EEPROM芯片进入工作;二是EEPROM芯片给电后需要有大于写周期的延时才能读写.EEPROM数据丢失的原因与对策1、环境因素★原因:高温、高湿、辐射、静电、强电磁场均可能使EEPROM存储单元造成数据丢失或数据保存时间缩短。
●对策:①不要在高温、高湿、辐射、静电、强电磁场环境中存放EEPROM器,如果法避免,应采取适当的防护措施。
②在高温环境中使用EEPROM器件,须确认存储内容的更新时间和器件使用期限③工作环境湿度较大时可考虑线路板灌胶防潮,防水胶要选用吸水率低的④在辐射、静电、强电磁场环境中工作要做好屏蔽。
STM32HAL库-针对芯⽚内部EEprom读写操作介绍⽬录概述本篇⽂章介绍如何使⽤STM32HAL库,操作芯⽚内部EEprom读写数据,类似操作Flash,可实现掉电保存数据功能。
(注:有些型号才有内部EEprom,没有的话,只能使⽤内部FLASH模拟EEprom,或者外挂EEprom芯⽚)硬件:STM32L051C8T6最⼩系统板软件:Keil 5.29 + STM32CubeMX6.2.1⼀、使⽤⽅法通过参阅《STM32数据⼿册》得知,通过⽬录找到芯⽚中的内部eeprom章节,如下所⽰:在《STM32中⽂参考⼿册》pdf⽂档中找到,第3.3⼩节:嵌⼊式闪存,对应的页数57。
这⾥我使⽤的是STM32L051C8T6的eeprom是512byte,通过⼿册得知是属于⼩容量,所以只需看地址分配图与每⼀页对应的⼤⼩(字节)即可。
想更详细的了解,请阅读《STM32数据⼿册》。
⼆、STM32CubeMx配置三、Examples打开STM32CubeMx⽣成的keil⼯程,新建Bsp⽂件夹,同时分别新建bsp_eeprom.c与bsp_eeprom.h⽂件,并把这两个⽂件,添加keil ⼯程中来即可。
添加头⽂件路径1、bsp_eeprom.h⽂件/*-------------------------------------------------*//* *//* 实现内部eeprom功能的头⽂件 *//* *//*-------------------------------------------------*/#ifndef __EEPROM_H#define __EEPROM_H#include "stm32l0xx.h" //包含需要的头⽂件#define DATA_EEPROM_START_ADDR 0x08080000 //起始地址#define USER_DATA_EEPROM_ADDR DATA_EEPROM_START_ADDR + 0x00000000 //⽤户地址#define DATA_EEPROM_BYTE_SIZE 0x01FF //空间#define DATA_EEPROM_END_ADDR DATA_EEPROM_START_ADDR + DATA_EEPROM_BYTE_SIZE //结束地址#define iEEPROM_CHECK_NUM 2HAL_StatusTypeDef EEPROM_WriteData(uint32_t Address, uint32_t *wData, uint32_t len);HAL_StatusTypeDef EEPROM_ReadData(uint32_t Address, uint32_t *rData, uint32_t len);HAL_StatusTypeDef EEPROM_WRITE_Verify_CHECK(uint32_t Address, uint32_t *wData, uint32_t len);HAL_StatusTypeDef EEPROM_Read_Verify_CHECK(uint32_t Address, uint32_t *rData, uint32_t len);#endif2、bsp_eeprom.c⽂件/*-------------------------------------------------*//* *//* 实现内部eeprom功能的源⽂件 *//* *//*-------------------------------------------------*/#include "bsp_eeprom.h" //包含需要的头⽂件#include <string.h>#include "usart.h" //包含需要的头⽂件#include "stdio.h"/*-------------------------------------------------*//*函数名:内部eeprom擦除功能 *//*参数:Address:擦除地址 *//*参数:wData:擦除数据缓冲区 *//*参数:len:擦除数据总长 *//*返回值:⽆ *//*-------------------------------------------------*/HAL_StatusTypeDef EEPROM_EraseData(uint32_t start, uint32_t NumberSectors){uint32_t i;uint32_t NbrOfPages = 0;uint32_t Address = start;HAL_StatusTypeDef status = HAL_OK;printf("EEPROM_EraseData len:%d\r\n", NumberSectors);NbrOfPages = (DATA_EEPROM_END_ADDR - Address)/FLASH_PAGE_SIZE;if(NumberSectors > NbrOfPages) return (HAL_ERROR);for(i=0; i<NumberSectors; i++){ //for循环,需要写⼊多少数据,就循环⼏次status = HAL_FLASHEx_DATAEEPROM_Erase(Address + i * 4);}if (status != HAL_OK){printf("Erase Fail\r\n"); //串⼝提⽰写⼊错误return (HAL_ERROR);}printf("Erase Success.\r\n\r\n");return HAL_OK;}/*-------------------------------------------------*//*函数名:内部eeprom写功能 *//*参数:Address:写⼊地址 *//*参数:wData:写⼊数据缓冲区 *//*参数:len:写⼊数据总长 *//*返回值:⽆ *//*-------------------------------------------------*/HAL_StatusTypeDef EEPROM_WriteData(uint32_t Address, uint32_t *wData, uint32_t len){uint32_t i;//uint32_t *Data = 0;HAL_FLASHEx_DATAEEPROM_Unlock(); //解锁EEPROM_EraseData(Address, len);printf("Write Address:%d\r\n", Address);printf("Write Data:\r\n");for(i=0; i<len; i++) //for循环,需要写⼊多少数据,就循环⼏次{printf("wData[%d]=%08x\r\n",i,wData[i]);#if 0HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address + i * 4, *(wData+i)); //字节:FLASH_TYPEPROGRAM_WORD#elseif(HAL_FLASHEx_DATAEEPROM_Program(FLASH_TYPEPROGRAMDATA_WORD, Address, *(uint32_t*)(wData+i)) == HAL_OK) //调⽤写数据函数,如果返回的 {if (*(uint32_t*)Address != *(uint32_t*)(wData+i)){printf("Write Error\r\n"); //串⼝提⽰写⼊错误HAL_FLASHEx_DATAEEPROM_Lock();/* FLASHEx_DATAEEPROM content doesn't match SRAM content */return(HAL_ERROR);}Address += 4; //地址递增4,因为⼀次写⼀个字,是4个字节}#endif}printf("Write Success.\r\n\r\n");HAL_FLASHEx_DATAEEPROM_Lock(); //上锁return HAL_OK;}/*-------------------------------------------------*//*函数名:内部eeprom读功能 *//*参数:Address:读取地址 *//*参数:rData:保存数据缓冲区 *//*参数:len:读取数据总长 *//*返回值:⽆ *//*-------------------------------------------------*/HAL_StatusTypeDef EEPROM_ReadData(uint32_t Address, uint32_t *rData, uint32_t len){uint32_t i;uint32_t *wAddr = 0;wAddr = (uint32_t *)(Address); //转换地址,地址从0x08080000开始printf("Read Address:%d\r\n", Address);//printf("Read Data:\r\n");for(i=0;i<len;i++){ //for循环,需要读取多少数据,就循环⼏次*rData++ = *wAddr++; //每次读取的数据保存到rData缓冲区//printf("rData[%d]=%08x\r\n",i,rData[i]);}printf("Read Complete.\r\n");return HAL_OK;}/*-------------------------------------------------*//*函数名:带有校验操作的内部eeprom写功能 *//*参数:Address:写⼊地址 *//*参数:wData:写⼊数据缓冲区 *//*参数:len:写⼊数据总长 *//*返回值:⽆ *//*-------------------------------------------------*/HAL_StatusTypeDef EEPROM_WRITE_Verify_CHECK(uint32_t Address, uint32_t *wData, uint32_t len) {uint32_t buff[len];uint32_t i;for (i=0; i < iEEPROM_CHECK_NUM; i++){EEPROM_WriteData(Address, wData, len);EEPROM_ReadData(Address, buff, len);if (memcmp(wData, buff, len)==0){printf("\r\nWRITE_Verify Completing Comparative\r\n\r\n");return HAL_OK;}}return HAL_ERROR;}/*-------------------------------------------------*//*函数名:带有校验操作的内部eeprom读功能 *//*参数:Address:读取地址 *//*参数:rData:保存数据缓冲区 *//*参数:len:读取数据总长 *//*返回值:⽆ *//*-------------------------------------------------*/HAL_StatusTypeDef EEPROM_Read_Verify_CHECK(uint32_t Address, uint32_t *rData, uint32_t len) {uint32_t buff0[len];uint32_t buff1[len];uint8_t i,j;// printf("len:%d\r\n", len);// printf("Address:%d\r\n", Address);for (i=0; i<iEEPROM_CHECK_NUM; i++){printf("First read Verify\r\n");EEPROM_ReadData(Address, buff0, len);printf("Second read Verify\r\n");EEPROM_ReadData(Address, buff1, len);if (memcmp(buff0, buff1, len)==0){printf("Read_Verify Completing Comparative\r\n");for (j=0; j<len; j++)*rData++ = buff0[j];}return HAL_OK;}}return HAL_ERROR;}3、mian.c⽂件/* USER CODE BEGIN Header *//********************************************************************************* @file : main.c* @brief : Main program body******************************************************************************* @attention** <h2><center>© Copyright (c) 2021 STMicroelectronics.* All rights reserved.</center></h2>** This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the* License. You may obtain a copy of the License at:* /licenses/BSD-3-Clause********************************************************************************//* USER CODE END Header *//* Includes ------------------------------------------------------------------*/#include "main.h"#include "usart.h"#include "gpio.h"/* Private includes ----------------------------------------------------------*//* USER CODE BEGIN Includes */#include "bsp_eeprom.h"#include "stdio.h"#include "string.h"/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*//* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*//* USER CODE BEGIN PM *///uint8_t wData[10] = "0x5a5a5a5a"; //需要写⼊的数据//uint8_t rData[1]; //⽤于保存读取数据的缓冲区typedef __PACKED_STRUCT{uint32_t Device_id; // 设备号uint32_t Hardware_Version; // 硬件版本信息uint32_t Application_Version; // APP软件版本}DEVInfoTypeDef;/* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/void SystemClock_Config(void);/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */void test_eeprom(void){DEVInfoTypeDef DevInfo_read = {0};DEVInfoTypeDef DevInfo_default = {.Device_id = 0x10000111,.Hardware_Version = 0x10000111,.Application_Version = 0x10000111};DEVInfoTypeDef DevInfo_update= {.Device_id = 0x20000222,.Hardware_Version = 0x20000222,.Application_Version = 0x20000222};uint32_t page = sizeof(DevInfo_default) / 4;printf("*******************Internal EEPROM test***************\r\n\r\n"); //串⼝提⽰信息printf("****************No verification function**************\r\n\r\n");//printf("page:%d\r\n", page);memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容EEPROM_WriteData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_default, page); //内部EEprom写⼊数据 HAL_Delay(200); //延时EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据 printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",DevInfo_read.Device_id, DevInfo_read.Hardware_Version,DevInfo_read.Application_Version);memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容EEPROM_WriteData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_update, page); //内部EEprom写⼊数据 HAL_Delay(200); //延时EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",DevInfo_read.Device_id, DevInfo_read.Hardware_Version,DevInfo_read.Application_Version);// EEPROM_ReadData(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据// printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",// DevInfo_read.Device_id, DevInfo_read.Hardware_Version,// DevInfo_read.Application_Version);printf("\r\n****************Add validation function**************\r\n\r\n");memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容EEPROM_WRITE_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_default, page); //内部EEprom写⼊数据 HAL_Delay(200); //延时EEPROM_Read_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据 printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",DevInfo_read.Device_id, DevInfo_read.Hardware_Version,DevInfo_read.Application_Version);memset(&DevInfo_read, 0, sizeof(DevInfo_read)); //清空结构体内容EEPROM_WRITE_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_update, page); //内部EEprom写⼊数据 HAL_Delay(200); //延时EEPROM_Read_Verify_CHECK(USER_DATA_EEPROM_ADDR, (uint32_t *)&DevInfo_read, page); //内部EEprom读取数据printf("Device_id:0x%08lX, Hardware_Version:0x%08lX, Application_Version:0x%08lX \r\n",DevInfo_read.Device_id, DevInfo_read.Hardware_Version,DevInfo_read.Application_Version);}/* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/int main(void){/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */test_eeprom();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){HAL_Delay(1000);HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 *//*** @brief System Clock Configuration* @retval None*/void SystemClock_Config(void){RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};/** Configure the main internal regulator output voltage*/__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;RCC_OscInitStruct.HSEState = RCC_HSE_ON;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;RCC_OscInitStruct.PLL.PLLMUL = RCC_PLLMUL_8;RCC_OscInitStruct.PLL.PLLDIV = RCC_PLLDIV_2;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK){Error_Handler();}PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;art1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK){Error_Handler();}}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief This function is executed in case of error occurrence.* @retval None*/void Error_Handler(void){/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */}}#ifdef USE_FULL_ASSERT/*** @brief Reports the name of the source file and the source line number* where the assert_param error has occurred.* @param file: pointer to the source file name* @param line: assert_param error line source number* @retval None*/void assert_failed(uint8_t *file, uint32_t line){/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */}#endif /* USE_FULL_ASSERT *//************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/四、运⾏结果1、⽆校验运算2、校验运算运⾏结果传送门->五、总结好了,就介绍到此,通过该案例,可以在⼀些产品上做掉电保存功能,和⼀些数据保存等功能。
详解AVR单片机防eeprom掉数据的办法应用A VR芯片内部EEPROM写入(或写入后读出)出错问题,下面有A VR的芯片手册有相关介绍(来自mega8中文翻译文档):防止EEPROM 数据丢失若电源电压过低,CPU 和EEPROM 有可能工作不正常,造成EEPROM 数据的毁坏( 丢失)。
这种情况在使用独立的EEPROM 器件时也会遇到。
因而需要使用相同的保护方案。
由于电压过低造成EEPROM 数据损坏有两种可能:一是电压低于EEPROM 写操作所需要的最低电压;二是CPU 本身已经无法正常工作。
EEPROM 数据损坏的问题可以通过以下方法解决:当电压过低时保持A VR RESET 信号为低。
这可以通过使能芯片的掉电检测电路BOD 来实现。
如果BOD 电平无法满足要求则可以使用外部复位电路。
若写操作过程当中发生了复位,只要电压足够高,写操作仍将正常结束。
以上官方文档介绍主要提示的是电压过低发生的异常而造成读写错误,并未涉及到其它问题。
个人总结,在实际应用过程中有以下问题造成数据读写错误:1.程序受到干扰(或程序存在BUG)造成写入EEPROM的数据本身就是错误的;2.EEPROM写入次数过多(这个问题在频繁写入时会遇到),造成无法写入的;3.再提电压问题:由于电压过低,造成写入的数据实际未写入或写入错误;EEPROM写入错误问题是不可避免的,因此就应有相关的归避措施和恢复措施:从硬件方面来说:加入BOD措施是必要的,同时芯片的电源滤波也有较高要求,芯片的复位电路、晶振(及芯片晶振设置位,指单片机的工作频率,这对EEPROM读写有影响)也应仔细处理,以提高抗干扰;当然,一个设计优良的线路板对抗干扰有很大帮助;从软件方面来说:可以有以下方式进行控制:1.在写入EEPROM前,需对写入的EEPROM数据进行验证措施,若不正常则不写入;2.EEPROM写入后再读出(即较验),写前数据比较,应一致,否则可能为EEPROM无法再写入,这时可能要更换存储区地址;3.楼主的解决方案有比较好的效果,但是我14楼提出的问题:太占EERPOM存储空间了,可以精简一下会更好;因为A VR内部的EEROM区有限,若存在大量存储数据情况下,则有可能选用高阶的芯片而造成成本上升;4.数据读出时有验证,并存在恢复措施,以使数据错误降到最低。
EEPROM读写测试实验z意义与作用EEPROM是一种电可擦可编程只读存储器,掉电后数据不丢失。
是单片机应用系统中经常会用到的存储器。
EEPROM掉电后数据不会丢失,而且可以用电信号直接清除存储数据和再编程,正是由于它的这一特性,EEPROM在嵌入式设备中应用广泛,用于产品出厂数据的保存,产品运行过程中一些数据量不大的重要数据保存等。
在本章节,我们以最常见的I2C接口的24CXX芯片为例进行学习研究。
它采用PHILIPS 公司开发的两线式串行总线(I2C总线),读写访问简单。
通过本章节实验,我们将对I2C总线有一个深入的了解,进而掌握如何读写访问24CXX这一系列的I2C接口EEPROM。
z实验原理24CXX系列EEPROM采用的访问接口是I2C接口。
I2C(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。
在CPU与被控IC之间、IC与IC之间进行双向传送,高速I2C总线一般可达400kbps以上。
I2C总线在传送数据过程中共有三种类型信号,它们分别是:开始信号、结束信号和应答信号。
开始信号:SCL为高电平时,SDA由高电平向低电平跳变,开始传送数据。
结束信号:SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。
应答信号:接收数据的IC在接收到8bit数据后,向发送数据的IC发出特定的低电平脉冲,表示已收到数据。
CPU向受控单元发出一个信号后,等待受控单元发出一个应答信号,CPU接收到应答信号后,根据实际情况作出是否继续传递信号的判断。
若未收到应答信号,由判断为受控单元出现故障。
I2C总线时序图如下:神舟IV号开发板板载的EEPROM芯片型号为24C02,该芯片的总容量是256个字节。
本节实验的基本原理:神舟IV号通过STM32F107VCT6处理器本身自带的硬件I2C接口与24C02相连,我们首先往EEPROM中写入一连串的有规律的数据,然后顺序读出,通过串口打印读出的数据,判断读出的数据是否正确,从而得知EERPOM是否可以正常访问。
EEPROM读写学习笔记与I2C总线(⼆)⽆论任何电⼦产品都会涉及到数据的产⽣与数据的保存,这个数据可能并不是⽤来长久保存,只是在运⾏程序才会⽤到,有些数据体量较⼤对于获取时效性并不太强,各种各样的数据也就有不同的存储载体,这次在EEPROM读写中,顺道把看到的关于存储的⼀些东西整理⼀下,有些话来⾃于⽹友,所以还是那句话,看到的⼈要带着⾃⼰的思考去看,记住尽信书不如⽆书,fighting⼀、基本概念最熟悉的两个词语应该是RAM与ROM,RAM(Random Access Memory)的全名为随机存取记忆体,它相当于PC机上的移动存储,⽤来存储和保存数据的。
它在任何时候都可以读写,RAM通常是作为操作系统或其他正在运⾏程序的临时存储介质,它的⼀切都是最好的,唯⼀缺点断电⼀切东西都没有了。
⼀般情况下,现在移动设备也多了,我们叫它内存,更通常的叫运⾏内存。
还有⼀个熟悉的词DDR2或DDR3,后⾯还会学习到的。
ROM(Read Only Memory)的全名为唯读记忆体,它相当于PC机上的硬盘,⽤来存储和保存数据。
ROM数据不能随意更新,但是在任何时候都可以读取。
即使是断电,ROM也能够保留数据。
但是资料⼀但写⼊后只能⽤特殊⽅法或根本⽆法更改,但这么久了ROM已经有了很⼤的发展,不再是最初的摸样了。
rom最初不能编程,出⼚什么内容就永远什么内容,不灵活。
后来出现了prom,可以⾃⼰写⼊⼀次,要是写错了,只能换⼀⽚,⾃认倒霉。
⼈类⽂明不断进步,终于出现了可多次擦除写⼊的EPROM,每次擦除要把芯⽚拿到紫外线上照⼀下,想⼀下你往单⽚机上下了⼀个程序之后发现有个地⽅需要加⼀句话,为此你要把单⽚机放紫外灯下照半⼩时,然后才能再下⼀次,这么折腾⼀天也改不了⼏次。
历史的车轮不断前进,伟⼤的EEPROM出现了,拯救了⼀⼤批程序员,终于可以随意的修改rom中的内容了,这⼀段话就说出了ROM的发展历程。
狭义的EEPROM:这种rom的特点是可以随机访问和修改任何⼀个字节,可以往每个bit中写⼊0或者1。
eeprom读写作者:onme提交日期:2007-9-7 10:31:00所看过的对24系列I2C读写时序描述最准确最容易理解的资料,尤其是关于主从器件的应答描述和页写描述,看完后明白了很多。
关于页写的描述,网络上绝大部分范程都没提到页写时的数据地址必须是每页的首地址才能准确写入,而且如果写入超过一页的数据会循环覆盖当前页的数据。
关于IIC总线I2C总线:i2c总线是 Philips 公司首先推出的一种两线制串行传输总线。
它由一根数据线(SDA)和一根时钟线(SDL)组成。
i2c总线的数据传输过程如图3所示,基本过程为:1、主机发出开始信号。
2、主机接着送出1字节的从机地址信息,其中最低位为读写控制码(1为读、0为写),高7位为从机器件地址代码。
3、从机发出认可信号。
4、主机开始发送信息,每发完一字节后,从机发出认可信号给主机。
5、主机发出停止信号。
I2C总线上各信号的具体说明:开始信号:在时钟线(SCL)为高电平其间,数据线(SDA)由高变低,将产生一个开始信号。
停止信号:在时钟线(SCL)为高电平其间,数据线(SDA)由低变高,将产生一个停止信号。
应答信号:既认可信号,主机写从机时每写完一字节,如果正确从机将在下一个时钟周期将数据线(SDA)拉低,以告诉主机操作有效。
在主机读从机时正确读完一字节后,主机在下一个时钟周期同样也要将数据线(SDA)拉低,发出认可信号,告诉从机所发数据已经收妥。
(注:读从机时主机在最后1字节数据接收完以后不发应答,直接发停止信号)。
注意:在I2C通信过程中,所有的数据改变都必须在时钟线SCL为低电平时改变,在时钟线SCL为高电平时必须保持数据SDA信号的稳定,任何在时钟线为高电平时数据线上的电平改变都被认为是起始或停止信号。
作为一种非易失性存储器(NVM),24系列EEPROM使用的很普遍,一般作为数据量不太大的数据存储器。
下面总结一下其应用的一些要点。
从命名上看,24CXX中XX的单位是kbit,如24C08,其存储容量为8k bit,即1k Byte=1024 Byte。
关于EEPROM的应用总结EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,与传统的ROM相比,EEPROM具有可擦写操作的能力,允许从存储器中擦除已有的信息,并在需要时重新编程新的数据。
EEPROM的应用非常广泛,主要集中在以下几个方面:1.嵌入式系统EEPROM在嵌入式系统中发挥了重要作用。
嵌入式系统通常需要存储一些参数、控制字或调整值。
EEPROM提供了一种可编程的存储解决方案,可以存储和更新这些信息。
例如,智能手机、数码相机、家电设备等嵌入式系统中的参数设置、用户配置和设备状态可以使用EEPROM存储。
2.电子设备校准和校验在许多电子设备中,EEPROM用于存储校准参数,以确保设备的精准性和可靠性。
例如,传感器、计量设备和测试设备经常要求进行校正以提高准确性。
EEPROM可以存储这些校准系数值,以便在需要时进行校准和修正。
3.网络和通信设备EEPROM在网络设备中广泛应用。
例如,以太网交换机和路由器中经常使用EEPROM存储引导配置、MAC地址和其他网络参数。
这些参数可以在电源断电后进行保存,以便在设备重新启动后恢复。
4.汽车电子EEPROM在汽车电子中扮演着至关重要的角色。
汽车的各种控制单元需要保存与车辆性能和操作相关的参数和配置。
例如,发动机控制单元(ECU)存储着发动机性能和排放控制相关的校准参数。
其他控制单元,如仪表盘、音频和通信系统,也使用EEPROM存储信息。
5.消费电子产品EEPROM广泛应用于各种消费电子产品中。
例如,电视机、音响系统、游戏机、智能手表等常常使用EEPROM存储用户首选项、设置参数和设备状态信息。
这使得用户在断电或设备重启后可以保留他们的个人配置和偏好。
6.传感器和测量设备传感器和测量设备通常需要对数据进行校准和校验。
EEPROM被广泛用于存储这些校准参数。
例如,温度传感器、湿度传感器、压力传感器等常常使用EEPROM来存储校准系数,以确保测量结果的准确性。
2017 年6 月3 0 日星期五目的:利用TMS320F2801芯片上外设I2C (2线串口)读写EEPROM数据(24LC128)关键点1 : 24LC时钟频率400KHZ,寄存器设置如下://Prescaler- need7-12Mhzo nm oduleclk=10; 〃NOTE:mustbe non zero=5; 〃NOTE:mustbe non zero时钟频率也可设为200KHz,三个参数分别为9、20、20(CPU时钟频率为100MHz)(未测试)关键点2 :波形分析问题:I2C模块是不是只有I2CCNT减到0才会发出停止信号I2C模块是硬件的,当检测到发送完了就会发结束自动发信号,不需要人为干预问题1:字节写操作正常,但是字节读函数出错原因:写EEPROM是在七位器件地址后添加写标志,而读EEPROM需要在七位器件地址后添加写标志。
关键点:读EEPROM数据需要发送两次命令。
第一次为写地址(此地址会被赋值给EEPROM 内的地址指针),因此需要添加写标志;第二次为读数据,将写标志改为读标志。
问题2:主机接收时,SDA数据线上有数据传输,且I2CDRR接收数据寄存器有数据更新,但寄存器显示不可读,即CPU认为一直没接收到数据,一直停在下面语句while!=1);关键点:初始化设置时采用的是FIF O接收方式,因此无效,应查询FIF O接收中断位while!=1);//FIFORX 方式查询位。
此位只有在非FIFO中断接收方式时才有效。
问题3:断续单字节读写正常,但是采用连续的单字节读写出错。
原因:EEPRO M写过程的结束并不是I2C总线写结束就结束,实际上I2C总线的写入数据先被保存到了EEPRO M内部的缓冲区,当遇到I2C结束条件后,EEPROM才启动内部写过程,这个过程才是保存数据的过程。
非常悲哀的是这个过程比较长,官方文档标注为5ms。
如果在这5ms以内对EEPROM芯片访问将被忽略。
STM32 系统学习——I2C (读写EEPROM)
I2C 通讯协议(Inter-Integrated Circuit)引脚少,硬件实现简单,可扩展性强,不需要USART、CAN 等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。
在计算机科学里,大部分复杂的问题都可以通过分层来简化。
如芯片被分为
内核层和片上外设;STM32 标准库则是在寄存器与用户代码之间的软件层。
对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协
议层。
物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物
理媒体的传输。
协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
简单来说物理层规定我们用嘴巴还是用肢体来交流,
协议层则规定我们用中文还是英文来交流。
一、I2C 物理层
它的物理层有如下特点:
(1) 它是一个支持设备的总线。
“总线”指多个设备共用的信号线。
在一个I2C 通讯总线中,可连接多个I2C 通讯设备,支持多个通讯主机及多个通讯从机。
(2) 一个I2C 总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线(SCL)。
数据线即用来表示数据,时钟线用于数据收发同步。
(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。
(4) 总线通过上拉电阻接到电源。
当I2C 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
I C读写E E P R O M问题总结文件管理序列号:[K8UY-K9IO69-O6M243-OL889-F88688]2017年6月30日星期五目的:利用TMS320F2801芯片上外设I2C(2线串口)读写EEPROM数据(24LC128)关键点1:24LC时钟频率400KHz,寄存器设置如下:I2caRegs.I2CPSC.all = 9; // Prescaler - need 7-12 Mhz on module clkI2caRegs.I2CCLKL = 10; // NOTE: must be non zero I2caRegs.I2CCLKH = 5; // NOTE: must be non zero 时钟频率也可设为200KHz,三个参数分别为9、20、20(CPU时钟频率为100MHz)(未测试?)关键点2:波形分析问题:I2C模块是不是只有I2CCNT 减到0才会发出停止信号I2C模块是硬件的,当检测到发送完了就会发结束自动发信号,不需要人为干预问题1:字节写操作正常,但是字节读函数出错原因:写EEPROM是在七位器件地址后添加写标志,而读EEPROM需要在七位器件地址后添加写标志。
关键点:读EEPROM数据需要发送两次命令。
第一次为写地址(此地址会被赋值给EEPROM内的地址指针),因此需要添加写标志;第二次为读数据,将写标志改为读标志。
问题2:主机接收时,SDA数据线上有数据传输,且I2CDRR接收数据寄存器有数据更新,但寄存器显示不可读,即CPU认为一直没接收到数据,一直停在下面语句while关键点:初始化设置时采用的是FIFO接收方式,因此无效,应查询FIFO 接收中断位while方式查询位。
此位只有在非FIFO中断接收方式时才有效。
问题3:断续单字节读写正常,但是采用连续的单字节读写出错。
原因:EEPROM写过程的结束并不是I2C总线写结束就结束,实际上I2C 总线的写入数据先被保存到了EEPROM内部的缓冲区,当遇到I2C结束条件后,EEPROM才启动内部写过程,这个过程才是保存数据的过程。
非常悲哀的是这个过程比较长,官方文档标注为5ms。
如果在这5ms以内对EEPROM芯片访问将被忽略。
关键点:读写EEPROM应延时至少5ms,软件延时10msdo{}while(EEPROM_Timer <= 10); //10ms问题4:查询EEPROM写过程是否结束造成死机,只能查询EEPROM读过程。
官方文档说EEPROM内部写周期最长为5ms,在很多情况下是远远低于5ms的,为了节约时间,官方给出一个解决办法。
当写周期完毕后就开始进行应答查询,来确定EEPROM写周期何时结束。
所谓应答查询官方解释为:就是向EEPROM发送一个I2C起始条件后发送器件地址和一个读写标志位,当EEPROM完成内部写周期会回应一个ACK,这时MCU就可以进行正常的其他读写过程了。
官方原文如下:杯具就是始于我画红线的那句话,它说可以在器件地址后任意填写读写标志。
我就填了读标志,事实证明在EEPROM写入过程采用读查询将导致系统死机,I2C总线不能被正常拉高,可能是EEPROM内部已经把总线拉到了地!不管怎样,反正就是死机了。
经过多次尝试最终发现应答查询只能采用写应答查询可以正常确定EEPROM内部写周期的结束。
——by数据传输过程:从机发送数据?SDA总线?I2CRSR缓冲寄存器?I2CDRR接收数据寄存器?中间变量?内存。
问题5:CPU以字(双字节)为单位读写EEPROM数据有误;原因:CPU中的内存单元以字(word-16bit)为单位,而EEPROM中的内存单元是字节(byte-8bit)为单位,因此将CPU内存地址转化为EEPROM 指针地址时,因乘以2(左移一位)问题6:使用读数据函数式,收到的数据都是1。
原因:EEPROM初次读取未写过的内存单元时,默认为高电平,即收到的字节为0xFF。
如果已经写过内存单元,则代表数据未成功写入;写入与读数据的内存地址不一样。
问题7:写入EEPROM的数据与随后读出来的数据不一致,但读出来的数据又没有规律性。
可能原因:数据未成功写入;数据读写字节数超过EEPROM的页内字节数(跨页);读写地址不一致;读写EEPROM之间应有一定的延时时间。
解决办法:若连续读多字节数据,则读取数据之间应加延时,因为数据从I2CRSR数据接收缓冲寄存器(多字节)复制到I2CCDRR数据接收寄存器(一字节)需要时间。
单字节延时25us,双字节(字)延时50us——测试通过问题8:使用示波器观测SDA数据线上的波形时,发现每次应答信号之前都有一个毛刺(尖峰),是什么原因导致的(不影响数据的正常读写)类似问题:使用F28335模拟I2C时序读取惯导器件的数据时,发现在更改SDA的传输方向时,Gpio中数据寄存器会发生变化,导致SDA上有毛刺产生。
问题9: I2C在跟EEPROM通讯的时候,第一次写入数据,一个一个读取的话,能知道写入EEPROM的值是没有错的,但是在连续读取数据的时候,就会出现,上电第一次读取数据串的时候,是全部读取正确,然后再读取一遍数据串的时候,只有第一个读取的数据是正确的,后面的数据会全部变成FF FF 这是怎么回事办法1:大家都说STM32的IIC有点bug,所以很少人用其自带的IIC,一般都是用IO口模拟IIC,模拟很简单而且不会出错。
逻辑分析仪抓取I2C总线数据,?改为转接板抓取数据,即I2C转USB通讯。
问题10:I2C给EEPROM写数据时,两字节地址需不需要算进去吗答案:需要,且地址字节数与EEPROM的型号(容量)相关,有些为1字节地址,有些为2字节地址。
24LC128需要两字节地址来区分内存单元,其内存最小单元为1字节,地址从0x00开始,一页64字节,因此地址指针范围为:0x00~0x3F。
I2caRegs.I2CCNT = (n<<1)+2;问题11:一次读写数据字节数最好不超过16字节。
原因:其一I2C深度寄存器范围限定,其二,读写数据字节太多会导致I2C总线出错的概率加大。
ST_5bit (0x0000~0x10000)I2caRegs.I2CFFTX.bit.TXFFIL_5bitI2caRegs.I2CFFRX.bit.RXFFST_5bit(0x0000~0x10000)I2caRegs.I2CFFRX.bit.RXFFIL_5bitI2caRegs.I2CCNT_32bit问题12:总是提示总线繁忙= 1总线繁忙,BB = 0 总线空闲关键:总线繁忙这个位只读。
猜想解决办法:如果是上电第一次读写就出现总线繁忙,就对I2C模块进行复位;如果不是第一次,且I2C总线上只有一主,则等待一定时间(5ms);如果是多主,则返回,等待总线空闲吧。
问题13:CPU写数据给EEPROM时,如果设置断点,就能成功写入数据,但没有断点,数据写不成功。
什么原因呀原因:写保护WP引脚的电平应在接收到停止信号后,应保持低电平一段时间才使能写保护,即加延时语句。
———已测试通过//所有字节(地址字节+数据字节)都是添加写标志,即低电平,2n+2 <= 64////写EEPROM地址从0开始//void EEPROM_Write_call(Uint16 address,Uint16 n,Uint16 s){Uint16 i,data_temp;do{}while(EEPROM_Timer <= 20); //20ms// Check if bus busyif (I2caRegs.I2CSTR.bit.BB != 0)//总线忙位,不能手动清除{return;}if(WP != 0) WP = 0; //清除从机EEPROM写保护模式// Setup slave address-7bitI2caRegs.I2CSAR = 0x50;I2caRegs.I2CCNT = (n<<1)+2; // Setup number of bytes to send//set up write modeI2caRegs.I2CMDR.all = 0x6E20;// Send start as master transmitter//发起始信号//主机发送停止信号//TX modeif(I2caRegs.I2CSTR.bit.NACK != 0) return;//存储器首地址——2字节(14bit)while(I2caRegs.I2CSTR.bit.XRDY != 1);I2caRegs.I2CDXR = (address>>8);if(I2caRegs.I2CSTR.bit.NACK != 0) return;while(I2caRegs.I2CSTR.bit.XRDY != 1);I2caRegs.I2CDXR = (address&0x00FF);if(I2caRegs.I2CSTR.bit.NACK != 0) return;// Setup data to sendfor(i=0; i<n; i++){data_temp = MK_Data[s+i];while(I2caRegs.I2CSTR.bit.XRDY != 1);I2caRegs.I2CDXR = (data_temp&0x00FF);if(I2caRegs.I2CSTR.bit.NACK != 0) return;while(I2caRegs.I2CSTR.bit.XRDY != 1);I2caRegs.I2CDXR = data_temp >> 8;if(I2caRegs.I2CSTR.bit.NACK != 0) return; }do{}while(I2caRegs.I2CSTR.bit.SCD != 1); //是否有停止信号WP = 1; //保护EEPROM,使其只读EEPROM_Timer = 0;}问题14:波形错误——用I2C接口,SCK 和 SDA都接有4.7K的上拉电阻,用示波器抓SCK和SDA的波形,发现SCK时序正常,SDA异常,见附图(黄为SCK,紫为SDA),请问这个锯齿波形大概是什么原因造成的呢——by猜测原因:可能是I2C模块时钟频率设置不合理;——未验证IIC中断作用:IIC中断和UART中断一样,你可以立刻得到数据,而不需要总是查询。
IIC接收数据只是存到指定的寄存器中,如果你不取走,下次再接收数据就直接冲掉了,所以IIC接收到数据之后给CPU中断,去处理这些收到的数据!查找中断源是一种保险的做法,要是由于其他的哪几种原因产生了中断,但是此时数据并没有接收完,中断服务子程序去处理数据了,结果就不对了!如果你自己敢保证不会出现哪几种情况就可以完全不用写!——by//I2C (接收)interrupt void i2c_int1a_isr(void) // I2C-A{Uint16 IntSource; // Read interrupt sourceswitch(IntSource){case I2C_NO_ISRC: break; // =0case I2C_ARB_ISRC: break; // =1case I2C_NACK_ISRC:break; // =2case I2C_ARDY_ISRC:break; // =3case I2C_RX_ISRC: // =4InData[I2cIndex++] = I2caRegs.I2CDRR; break;case I2C_TX_ISRC: break; // =5case I2C_SCD_ISRC: break; // =6case I2C_AAS_ISRC: break; // =7default: //asm(" ESTOP0"); // Halt on invalid number. asm(" RPT #5 ||NOP ");} // Enable future I2C (PIE Group 8) interrupts}。