0 摘要
M24256 是一个256k的存储芯片,本文将主要讲解如何采用模拟IIC实现对M24256的读写驱动操作。并且使用Proteus进行仿真验证。处理器采用STM32F103,使用STM32CubeMX和Keil5进行联合开发。
1 所使用的软件
-
Proteus 8.9 SP2
链接:https://pan.baidu.com/s/1Mnc5M0A5rOIQ2xrPT96ugw
提取码:dfct
-
Keil5
链接:https://pan.baidu.com/s/1Aavx3TkvBSLrLxlFOZ7xPw
提取码:uagc
-
STM32CubeMX
https://www.st.com/en/development-tools/stm32cubemx.html
2 M24256介绍
M24256是ST公司的一款256k EEPROM存储芯片,其具有以下特点。
- M24256具有256kbit EEPROM内容,每页可写入的内容为64bytes(Page Size = 64 bytes)
- Single supply voltage and high speed
-
Write:
- – Byte Write within 5 ms
- – Page Write within 5 ms
- Random and sequential Read modes
- Write protect of the whole memory array 所有内存整列均可以进行写保护
- Enhanced ESD/Latch-Up protection
- More than 4 million Write cycles
-
More than 200-years data retention
M24256引脚接口如表2-1所示:
表2-1 芯片引脚定义
Signal name |
Function |
Direction |
E2、E1、E0 |
Chip Enable |
Input |
SDA |
Serial Data |
I/O |
SCL |
Serial Clock |
Input |
WC(低电平有效) |
Write Control |
Input |
VCC |
Supply voltage |
|
VSS |
Ground |
|
通过控制E2、E1、E0的电平关系来修改芯片的地址,也就是说每一个IIC总线上最多可以挂接8个M24256存储芯片。
Write Control引脚用于保护写操作,可以用来保护存储器的整个内容免受意外写操作。将写控制(WC)驱动为高电平时,将禁止对整个存储器阵列进行写操作。 当写控制(WC)被驱动为低电平或悬空时,使能写操作。 将写控制(WC)驱动为高电平时,确认器件选择和地址字节,不确认数据字节。
3 M24256设备地址
表3-1 芯片地址(设备选择代码)
通过硬件电路定义E2、E1、E0的电平来定义设备的地址,其中最低位为读写标志位,当bit0=1时处于Read模式,反之处于Write模式。而高八位为固定地址,通常使用的是Memory array情况,所以高八位为A
如果在设备选择代码上发生匹配,则相应的设备会在第9位时间内给出串行数据(SDA)的确认。 如果设备与设备选择代码不匹配,则会从总线上取消选择自身,并进入待机模式。
4 M24256写操作
当芯片处于非写保护状态,即WC为0时对应的时序图如图4-1所示。
表4-1 M24256写时序(WC为低电平)
由图可以看出,M24256支持两种形式的写操作,分别是按字节写入和按页写入。接下来将针对其时序图逐一进行分析。
4.1 Byte Write Mode
按字节写入的时序图如图4-2所示。
图4-2 Byte Write Mode 时序图
由图可知,该模式下,设备仅向指定地址中写入一个字节的数据。其包含的时序过程如下:
Start |
发送设备地址 1010 E2E1E0 0 |
ACK |
待写入的设备地址高8位 Byte addr |
ACK |
待写入的设备地址低8位 Byte addr |
ACK |
待写入的八位数据 Data in |
ACK |
Stop |
对应的写入代码如下:
uint8_t W24256_Byte_Write(uint8_t WriteData,uint16_t address) { uint16_t usAddr; usAddr = address; /* 第1步:发起I2C总线启动信号 */ IIC_Start(); /* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR | I2C_WR); /* 此处是写指令 */ /*第3步:等待ACK信号*/ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第4步:发送字节地址,发送地址直接高八位*/ IIC_Send_Byte((uint8_t)(usAddr>> 8)); /*第5步:等待ACK信号*/ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:发送字节地址,发送地址直接低八位*/ IIC_Send_Byte((uint8_t)(usAddr&0x00ff)); /*第7步:等待ACK信号*/ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第8步:开始写入数据 */ IIC_Send_Byte(WriteData); /*第7步:等待ACK信号*/ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } IIC_Stop(); return 1; cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */ /* 发送I2C总线停止信号 */ IIC_Stop(); return 0; } |
4.2 Page Write Mode
按页写入的时序图如图4-3所示。
图4-3 Page Write Mode
由图可知,该模式下,设备可以向指定页中写入指定字节的数据,可写入的字节数小于等于64byte。其包含的时序过程如下:
Start |
发送设备地址 1010 E2E1E0 0 |
ACK |
待写入的设备地址高8位 Byte addr |
ACK |
待写入的设备地址低8位 Byte addr |
ACK |
待写入的八位数据 Data in |
如果继续写入,则ACK 停止写入,则NACK |
STOP |
对应的按页写入代码如下:
uint8_t W24256_Page_Write(uint8_t *WriteBuf, uint16_t Page, uint16_t Size) { uint16_t i,m; uint16_t usAddr;
/* * 写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。 * 对于24xx02,page size = 8 * 简单的处理方法为:按字节写操作模式,没写1个字节,都发送地址 * 为了提高连续写的效率: 本函数采用page wirte操作。 */
usAddr = Page * 0x0040; for (i = 0; i < Size; i++) { /* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */ if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0) { // /* 第0步:发停止信号,启动内部写操作 */ // IIC_Stop();
/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms CLK频率为200KHz时,查询次数为30次左右 */ for (m = 0; m < 1000; m++) { /* 第1步:发起I2C总线启动信号 */ IIC_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR | I2C_WR); /* 此处是写指令 */
/* 第3步:发送一个时钟,判断器件是否正确应答 */ if (IIC_Wait_Ack() == 0) { break; } } if (m == 1000) { goto cmd_fail; /* EEPROM器件写超时 */ }
/* 第4步:发送字节地址,发送地址直接高八位*/ IIC_Send_Byte((uint8_t)(usAddr>> 8)); /*第五步:等待ACK信号*/ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:发送字节地址,发送地址直接低八位*/ IIC_Send_Byte((uint8_t)(usAddr&0x00ff)); /* 第5步:等待ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } }
/* 第6步:开始写入数据 */ IIC_Send_Byte(WriteBuf[i]);
/* 第7步:发送ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ }
usAddr++; /* 地址增1 */ }
/* 命令执行成功,发送I2C总线停止信号 */ IIC_Stop(); return 1;
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */ /* 发送I2C总线停止信号 */ IIC_Stop(); return 0; } |
注:M24256有256kbit空间,每一页的大小Page Size =64,所以计算可得页的取值范围为:256*1024/8/64 = 512 页。
5 M24256读操作
WC引脚的电平状态对读操作无效,所以其对应的时序图入图5-1所示。
图5-1 Read mode sequences
由图5-1可知,M24256共有三种模式的读操作,包括随机地址读取(Random Address Read)、当前地址(Current Address Read)、顺序读取(Sequential Read)
下面主要针对随机地址读取和顺序读取两种模式进行分析。其余分析类似。
5.1 Random Address Read
图5-2 Random Address Read 时序图
由图可知,该模式下,仅从设备的指定地址中读取一个字节的数据。其包括的时序如下:
Start |
设备地址和控制字 1010 E2E1E0 0 |
ACK |
待写入的设备地址高8位 Byte addr |
ACK |
待写入的设备地址低8位 Byte addr |
ACK |
Start 重启IIC总线 |
设备地址和控制字 1010 E2E1E0 1 |
ACK |
读出的数据 Data out |
NACK |
Stop |
对应的代码如下:
uint8_t W24256_Byte_Read( uint16_t Address) { uint16_t uAddress; uint8_t ReadData; uAddress = Address; /* 第1步:发起I2C总线启动信号 */ IIC_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR | I2C_WR); /* 此处是写指令 */
/* 第3步:等待ACK */ if (IIC_Wait_Ack() != 0) { IIC_Stop(); /* EEPROM器件无应答 */ }
/* 第4步:发送字节地址高8位 */ IIC_Send_Byte((uint8_t)(uAddress>>8));
/* 第5步:等待ACK */ if (IIC_Wait_Ack() != 0) { IIC_Stop(); /* EEPROM器件无应答 */ } /* 第6步:发送字节地址低八位 */ IIC_Send_Byte((uint8_t)uAddress&0xff);
/* 第7步:等待ACK */ if (IIC_Wait_Ack() != 0) { IIC_Stop(); /* EEPROM器件无应答 */ } /* 第8步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */ IIC_Start();
/* 第9步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR | I2C_RD); /* 此处是读指令 */
/* 第10步:发送ACK */ if (IIC_Wait_Ack() != 0) { IIC_Stop(); /* EEPROM器件无应答 */ }
/* 第11步:读取数据并保存*/ ReadData = IIC_Read_Byte(); /* 读1个字节 */ /*第12步:发送NACK命令*/ IIC_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
/*第13步:发送I2C总线停止信号 */ IIC_Stop(); return ReadData; /* 执行成功 */ } |
5.2 Sequention Random Read
图5-3 Sequention Random Read 时序图
由图可知,该模式下,可以向指定地址中读取指定数目的数据。其包括的时序如下:
Start |
设备地址和控制字 1010 E2E1E0 0 |
ACK |
待写入的设备地址高8位 Byte addr |
ACK |
待写入的设备地址低8位 Byte addr |
ACK |
Start 重启IIC总线 |
设备地址和控制字 1010 E2E1E0 1 |
ACK |
读出的数据 Data out |
如果到达要读取数据个数,发送NACK,反之发送ACK |
Stop |
其对应的代码如下:
/** * 函数功能: 从串行EEPROM指定地址处开始读取若干数据 * 输入参数: ReadBuf : 存放读到的数据的缓冲区指针 * Address : 起始地址 * Size : 数据长度,单位为字节 * 返 回 值: 0 表示失败,1表示成功 * 说 明:无 */ uint8_t W24256_Page_Read(uint8_t *ReadBuf, uint16_t Page, uint16_t Size) { uint16_t i; uint16_t uAddress; /* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */ uAddress = Page * 0x0040; /* 第1步:发起I2C总线启动信号 */ IIC_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR_Page | I2C_WR); /* 此处是写指令 */
/* 第3步:等待ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ }
/* 第4步:发送字节地址高8位 */ IIC_Send_Byte((uint8_t)(uAddress>>8));
/* 第5步:等待ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第6步:发送字节地址低八位 */ IIC_Send_Byte((uint8_t)uAddress&0xff);
/* 第7步:等待ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ } /* 第8步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */ IIC_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */ IIC_Send_Byte(EEPROM_DEV_ADDR_Page | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */ if (IIC_Wait_Ack() != 0) { goto cmd_fail; /* EEPROM器件无应答 */ }
/* 第9步:循环读取数据 */ for (i = 0; i < Size; i++) { ReadBuf[i] = IIC_Read_Byte(); /* 读1个字节 */
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */ if (i != Size - 1) { IIC_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */ } else { IIC_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */ } } /* 发送I2C总线停止信号 */ IIC_Stop(); return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */ /* 发送I2C总线停止信号 */ IIC_Stop(); return 0; } |
6 M24256读写测试
使用Proteus 和Stm32CubeMX keil 联合开发,对M24256的读写测试进行仿真分析。
图6-1 仿真搭建
6.1 按字节读写测试结果
代码如下
W24256_Byte_Write(0x01,0x0000); HAL_Delay(100); W24256_Byte_Read(0x0000); HAL_Delay(500); |
图6-2 仿真结果
第一行 |
S |
A0 |
A |
00 |
A |
00 |
A |
01 |
A |
P |
向1101 000x设备的0x0000地址写入0x01数据 |
第二行 |
S |
A0 |
A |
00 |
A |
00 |
A |
Sr |
A1 |
A |
01 |
N |
P |
从1101 000x设备的0x0000地址读出数据,读出的数据为0x01 |
6.2 按页读写测试结果
uint8_t Data[3]={0x01,0x05,0xff}; uint8_t DataRead[3]={0x00,0x00,0x00}; W24256_Page_Write(Data,10,3); HAL_Delay(100); W24256_Page_Read(DataRead,10,3); HAL_Delay(500); |
图6-3 仿真结果
第一行 |
S |
A0 |
A |
02 |
A |
80 |
A |
01 |
A |
05 |
A |
FF |
A |
P |
向1101 000x设备的第10页(0X0280)写入0x01、0X05、0XFF三个字节数据 |
第二行 |
S |
A0 |
A |
02 |
A |
80 |
A |
Sr |
A1 |
A |
01 |
A |
05 |
A |
FF |
N |
P |
从1101 000x 设备的第10页(0x0280)读取3个字节数据,读出的数据为 0x01、0x05、0xff |
6.3 按字节读写多个字节测试
uint8_t Data[3]={0x01,0x05,0xff}; uint8_t DataRead[3]={0x00,0x00,0x00}; W24256_Write(0x0000,Data,3); HAL_Delay(100); W24256_Read(0x0000,DataRead,3); HAL_Delay(500); |
图6-4 仿真结果
第一行 |
S |
A0 |
A |
00 |
A |
00 |
A |
01 |
A |
P |
向1010 000x设备的0x0000地址写入0x01数据 |
第二行 |
S |
A0 |
A |
00 |
A |
01 |
A |
05 |
A |
P |
向1010 000x设备的0x0001地址写入0x05数据 |
第三行 |
S |
A0 |
A |
00 |
A |
02 |
A |
FF |
A |
P |
向1010 000x设备的0x0002地址写入0xFF数据 |
第四行 |
S |
A0 |
A |
00 |
A |
00 |
A |
Sr |
A1 |
A |
01 |
N |
P |
从1010 000x设备的0x0000地址读取数据,结果为0x01 |
第五行 |
S |
A0 |
A |
00 |
A |
01 |
A |
Sr |
A1 |
A |
05 |
N |
P |
从1010 000x设备的0x0001地址读取数据,结果为0x05 |
第六行 |
S |
A0 |
A |
00 |
A |
02 |
A |
Sr |
A1 |
A |
FF |
N |
P |
从1010 000x设备的0x0002地址读取数据,结果为0xFF |
制作不易,如有错误或者不好理解的地方请留言
如果需要仿真源文件,请联系EMAIL:[email protected]
并添加QQ:975107705
请注明M24256 读写测试