历史上的今天
今天是:2025年08月12日(星期二)
2019年08月12日 | 初学24CXX系列EEPROM使用详解&STM32库函数I2C总线
2019-08-12 来源:eefocus
24CXX系列芯片属于EEPROM(Electrically Erasable Programmable read only memory)即电可擦可编程只读存储器,是一种掉电后数据不丢失(不挥发)存储芯片。
24CXX系列芯片数据说明:见下表(只做参考,只表明常用24CXX器件的常用数据,具体使用请查阅器件数据手册):
如24C02BN容量=2KB,但写缓冲区(页容量)=8K
1、“型号”:24Cxx系列型号的标称C后面的参数代表其存储容量大小,单位为KB,这里的1KB=1024bit;
2、“存储容量”:24Cxx系列存储数据为8位(bit)格式,根据其容量可以计算出各型号具体存储数据的字节(Byte)数;
3、“页数”和“写缓冲器容量”:为减少器件的功耗,每个24CXX器件都有一个写缓冲器,器件收到总线发来的数据时,先写入缓冲器并进行应答,直到器件收到总线上发出的STOP信号后,器件将收到的数据写入其内部存储器中;
收到总线STOP信号后,器件将数据从写缓冲器写入存储器中,这个过程需要5ms,如果后面需要进行立即读取或再次写入操作,必须延时>5ms。
“写缓冲器容量”就代表器件可以一次性写入数据的大小,也就是“页”大小。24CXX系列“写缓冲器”(页容量)大小不同,再根据器件存储器大小,“页数”也不相同。
页写:每次对器件的写入(写缓冲器),可以进行多字节数据连续写(只要总线不发出STOP信号),写缓冲器地址位自动+1,如果写入的字节数超过写缓冲器容量,地址计数器自动翻转,从写缓冲器0x00地址重新计数,并覆盖原先写入的数据。
如果在对器件的写操作是从数据地址的0x00地址位开始,在写缓冲器也是从0x00位开始,写满一页需要写入字节数也就等于写缓冲器容量大小;如果设定的写数据地址不是0x00,对于写入写缓冲器的位置地址就是“写数据初始地址%写缓冲器容量(求余)”,这时写满第1页的字节数不一定等于写缓冲器容量大小。
4、“存储数据地址位数”:(数据存储地址和器件地址不是一个概念),24CXX器件内部存储的每个字节(Byte)都可以进行单独的读写操作(单字节读写),“存储数据地址位数”就代表了其用于寻址的地址位长度。根据容量不同,24C01/02使用8位数据地址位,24C04/08/16虽然也使用了8位的数据地址位,但8位数据地址最大0xff,表示最高可寻址256位(0~255),而24C04/08/16的存储容量有512/1024/2048Byte,明显不够,这时就看图中黄色标注部分,也就是下面说到的A2、A1和A0的其他功能;
5、“A2”、“A1”、“A0”引脚功能:24CXX系列不同型号器件的这3个引脚接法不同,部分型号可以悬空,但统一接VSS代表0,接VCC代表1。不同型号器件的这3个引脚功能也不同,具体如下:
功能1:图中引脚功能为A2、A1或A0时,说明这3个引脚可以配置为器件的总线地址(通过分别接VSS或VCC),也表示了同一I2C总线上可以连接的同一型号的器件数量。如24C01/02可以接8个,而24C16在同一个I2C总线上只能接1个器件;
功能2:引脚功能位P8、P9和P10时(黄色标注部分),其代表的是数据帧地址。刚才已经说了,24C04存储容量有512Byte,使用8位地址数据寻址只能寻址256位,这里其A0引脚的功能就是数据地址位的第9位,这样其实际寻址功能为2^9=512,24C08和24C16也同理。所以在操作24C04/08/16器件时,如果要使用全部的存储空间,相应P8、P9或P10引脚应单独使用IO控制。
遇到的问题:在实际操作中使用STM32的I2C总线读写24CXX系列EEPROM时,遇到很多问题,陆陆续续学习了好几天,才一一解决。主要的问题有:
1、 开始使用的是板载24C02,在以起始地址0x00进行操作,单字节读写正常,使用多页写时要注意,根据器件的数据手册,写数据首先是写入写缓冲器中,页写就是指向写缓冲器连续写入多个字节,只有在写满写缓冲器(页)或数据字节已经写完,当器件收到总线发出STOP信号后,器件将写缓冲器中数据转写入存储器中,这个事件需要5ms的时间,而且在此期间器件不会对总线做任何反应的。所以在进行页写、总线有多个发送STOP程序之间,或者写完立即读的时候,必须在每个STOP信号后延时5ms。
2、 在使用板载的24C02时,调试页写程序,按照查找的资料,包括程序中定义的24C02参数,应该是写缓冲器为16字节(页容量),也就是每次连续写最多16位,但实际情况是写入和读出的数据不符。最后仔细查看板载的芯片型号,详细的型号为24C02BN,再次查看数据手册,才发现这个型号的24C02,写缓冲器(页容量)是8位,而不是24C02的16位,修改程序,调用程序中24C01定义的参数,读写相符。
3、 使用的是板载24C02,在以起始地址不等于0x00进行操作时,例如起始地址0x05,写入看不出来异常,但是读取时从第0x08位置数据错误,最后确认数据在写入写缓冲器时,并不是始终都是从写缓冲器的头部开始写入,而是根据页(写缓冲器)对应(映射)的存储器地址来写入的。例如起始地址0x05,则在写入写缓冲器时也是从第6位开始写入,写到第8位就算写满本页,需要发送STOP使器件将写缓冲器的内容转写到存储器对应地址中。
4、 I2C总线可以挂在多个器件,正好手上有24C32芯片,刚开始调试单字节读写程序时就发现读写24C02正常,读写24C32不符的问题,仔细查看数据手册,发现24C32因为存储容量较大,数据的地址位使用8位已经不能够定义,24C32使用了16位数据地址长度,程序中也需要对各个芯片的数据地址位长度进行定义。同时也知道了24C04/08/16芯片的A0、A1和A2引脚的不同功能,虽然这三个型号也是8位数据地址位,但同样引用了A0/A1/A2的引脚硬件控制作为存储器数据地址的第9/10/11位;
/*****************************************************************
* 课例名称:学习STM32固件库I2C总线
* 内容说明:IIC总线上EEPROM存储器按页写操作及读的例程
* 平台芯片:STM32F103ZE
* 端口说明:I2C1_SCK=PB6;I2C1_SDA=PB7
* USART1_Tx=PA9;USART1_Rx=PA10;
* 其他说明:24CXX系列的型号只定义了容量大小,但尾标不同,其页容量和
* 数据地址字节长度也不同,具体以数据手册为准
*****************************************************************/
#include "stm32f10x.h"
#include "stdio.h"
/*********************定义I2C初始化数据**********************/
#define I2C_Speed 400000 //I2C1设置数据传输速度
#define I2C1_OWN_ADDRESS7 0X0a //主设备的从地址
/********************定义I2C中线器件参数*********************/
#define EE_24C32_ADDRESS 0xae //定义器件型号和器件地址
/*根据型号,定义PAGE_SIZE写存储器(页)大小和数据地址位长度*/
#ifdef EE_24C01_ADDRESS
#define PAGE_SIZE 8
#define WORD_ADDR_SIZE 8
#define EEPROM_ADDRESS EE_24C01_ADDRESS
#endif
#ifdef EE_24C02_ADDRESS
#define PAGE_SIZE 16
#define WORD_ADDR_SIZE 8
#define EEPROM_ADDRESS EE_24C02_ADDRESS
#endif
#ifdef EE_24C04_ADDRESS
#define PAGE_SIZE 16
#define WORD_ADDR_SIZE 8
#define EEPROM_ADDRESS EE_24C04_ADDRESS
#endif
#ifdef EE_24C08_ADDRESS
#define PAGE_SIZE 16
#define WORD_ADDR_SIZE 8
#define EEPROM_ADDRESS EE_24C08_ADDRESS
#endif
#ifdef EE_24C16_ADDRESS
#define PAGE_SIZE 16
#define WORD_ADDR_SIZE 8
#define EEPROM_ADDRESS EE_24C16_ADDRESS
#endif
#ifdef EE_24C32_ADDRESS
#define PAGE_SIZE 32
#define WORD_ADDR_SIZE 16
#define EEPROM_ADDRESS EE_24C32_ADDRESS
#endif
#ifdef EE_24C64_ADDRESS
#define PAGE_SIZE 32
#define WORD_ADDR_SIZE 16
#define EEPROM_ADDRESS EE_24C64_ADDRESS
#endif
#ifdef EE_24C128_ADDRESS
#define PAGE_SIZE 64
#define WORD_ADDR_SIZE 16
#define EEPROM_ADDRESS EE_24C128_ADDRESS
#endif
u8 ReadData; //第二次测试读取数据变量
int fputc(int ch,FILE *f);
void Delay_MS(u16 nms);
void USART1_GPIO_Config(void);
void I2C1_GPIO_Config(void);
void I2C_WriteData(u8 addr,u8* dat,u8 size);
void I2C_PageWrite(u8 WriteAddr,u8* WriteDat,u16 WriteSize);
void I2C_ReadData(u8 ReadAddr,u8* ReadDat,u16 ReadSize);
/*****************************************************************
* 名称:Pritf重定向子函数
*****************************************************************/
int fputc(int ch,FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
USART_SendData(USART1,(unsigned char)ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
return (ch);
}
/*****************************************************************
* 名称:SysTick_MS延时子函数
*****************************************************************/
void Delay_MS(u16 nms)
{
u32 temp;
SysTick->LOAD = 9000*nms;
SysTick->VAL=0X00;//清空计数器
SysTick->CTRL=0X01;//使能,减到零是无动作,采用外部时钟源
do
{
temp=SysTick->CTRL;//读取当前倒计数值
}
while((temp&0x01)&&(!(temp&(1<<16))));//等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
/*****************************************************************
* 名称:USART1|GPIOA初始化子函数
*****************************************************************/
static void USART1_GPIO_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin=(GPIO_Pin_9|GPIO_Pin_10);
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置USART1_Tx=PA9;USART1_Rx=PA10;端口为复用推挽输出模式
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate=9600;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_InitStruct.USART_StopBits=USART_StopBits_1;
USART_InitStruct.USART_Parity=USART_Parity_No;
USART_InitStruct.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_Init(USART1,&USART_InitStruct);
USART_Cmd(USART1,ENABLE);
}
/*****************************************************************
* 名称:I2C1|GPIOB初始化子函数
*****************************************************************/
static void I2C1_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能与 I2C1 有关的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
/* PB6-I2C1_SCL、PB7-I2C1_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_InitTypeDef I2C_InitStructure;
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* 高电平数据稳定,低电平数据变化 SCL 时钟线的占空比 */
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = I2C1_OWN_ADDRESS7;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
/* I2C的寻址模式 */
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* 通信速率 */
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
/* I2C1 初始化 */
I2C_Init(I2C1, &I2C_InitStructure);
/* 使能 I2C1 */
I2C_Cmd(I2C1, ENABLE);
}
/*****************************************************************
* 名称:I2C总线等待EEPROM状态检测子函数
*****************************************************************/
//void I2C_EE_WaitEepromStandbyState(void)
//{
// vu16 SR1_Tmp = 0;
// do
// {
// /* Send START condition */
// I2C_GenerateSTART(I2C1, ENABLE);
// /* Read I2C1 SR1 register */
// SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);
// /* Send EEPROM address for write */
// I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
// }
// while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));
// /* Clear AF flag */
// I2C_ClearFlag(I2C1, I2C_FLAG_AF);
// /* STOP condition */
// I2C_GenerateSTOP(I2C1, ENABLE);
//}
/*****************************************************************
* 函数名称:I2C总线向EEPROM写数据子函数
* 函数说明:使I2C向EEPROM器件发送数据前的准备工作
* 输入参数:addr - 写入的数据起始地址
* dat - 写入的数据
* size - 写入的数据长度
* 输出参数:无
*****************************************************************/
void I2C_WriteData(u8 addr,u8* dat,u8 size)
{
// I2C_EE_WaitEepromStandbyState();
/* [1]Send STRAT condition向I2C总线发送START信号 */
I2C_GenerateSTART(I2C1, ENABLE);
/* [2]Test on EV5 and clear it验证EV5事件 */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
/* [3]Send EEPROM address for write发送从设备地址 */
史海拾趣
|
目录: 第一章 8051单片机的构成及硬件特性 第二章 MCS-51指令系统 第三章 INTEL公司MCS-51产品 第四章 PHILIPS和其他公司的8051系列单片机 第五章 附录 详细信息: 书名:8051系列单片机应用手册 作者:苏伟斌编著 出版社:科学 ...… 查看全部问答> |
|
请问一下那位大哥或大姐知道51单片机应用系统典型木块开发大全这本书哪有下载 请告诉我啊 我觉得这本书现在很适合我 现在想找这本书 小弟刚学单片机 强大家多多照顾啊 谢谢了… 查看全部问答> |
|
我今天在修改eboot的开机logo的时候发现我的bootloader中居然是用了压缩算法 我人为是压缩算法 下面是源程序,这个图像的压缩是怎么实现的? #define RLE_MARK 0x1234 static void rle_expres ...… 查看全部问答> |
|
如何实现在手机IE地址栏输入http://127.0.0.1,访问本手机SIM卡中的内容 在使用windows mobile系统的手机中,如何实现在手机IE地址栏输入http://127.0.0.1,访问本手机SIM卡中的内容,并通过浏览器显示出来。 要求不能采用服务器技术(httpd)。 ps:采用插件的形式可以吗,例如ACTIVEX?谢谢!… 查看全部问答> |
|
在window 下安装,双击install.exe 提示要用软驱:A。 但是我没有软驱,请教各位你们是怎么安装的?谢谢 我下的BC3.1压缩文件。 坛子里有安装过程文件,看到了, 版主英明。谢谢 [ 本帖最后由 chaoyuliang 于 2011-8-10 08:51 编 ...… 查看全部问答> |
|
在钓鱼岛起争端及美国防部长访华之际,中国再次发射北斗卫星,这次用一箭双星的发射方法,将使北斗卫星导航快速组网效率大大提高。预计再过8年,中国就可完成这4000亿的产业规模。这是军事带动经济发展的典型,中国导航产业的4000亿,就是战争带来 ...… 查看全部问答> |
|
【COS 操作系统的视频来了】中科院15日发布,中国拥有了自己的操作系统COS。COS主打安全,有超过十万的应用程序,类似App Store。不过它可比苹果系统炫酷多了,用户可在操作界面同时浏览四段视频,连发短信都不耽误! $(\'swf_F2W\').innerHTML=AC ...… 查看全部问答> |




