文章目录[隐藏]
国产芯片
FLASH芯片 学习笔记
国微的SM25QH128M
百度上搜索的资料太少了。目前为止,百度只能搜到2条相关的文章。
该国产芯片与进口芯片W25Q128JV 很相似。可以参考W25Q128JV。
1. 简述
- SM25QH128M是一种Nor FLash芯片。
- 存储空间大小为128Mbit。
- 工作环境满足国军标N1级要求。
- 工作电压为2.7V到3.6V。
- 支持SPI,Dual SPI,Quad SPI。SPI可以工作在mode0或者mode3模式下。
- 正常读取时钟为83MHz,快读频率最大支持104MHz。
- 支持扇区擦除,块擦除,芯片擦除。支持页page编程写入数据。
2. 地址空间划分
- 128Mbit空间,也就是16MByte。空间大小可以满足绝大多数应用场景。
- 一个扇区有4KByte(4096字节)。共有4096个扇区。
- 可以将8个扇区分为一个块,或者将16个扇区分成一个块。这点在进行块擦除时,值得注意。
3. 状态寄存器
与进口芯片不同,SM25QH128M官方手册表明,只有一个状态寄存器。如下图所示:
- SRP位 为 状态寄存器保护位。
- EBL位 为 使能锁定位。
4. 指令列表
根据上表,进行宏定义:
/*
2021年11月19日
根据官方手册,进行的用户宏定义。By Kshine。
*/
//配置,状态,信息
#define SM25QH128M_RESET_ENABLE 0x66 //复位使能 (第一字节写入)
#define SM25QH128M_RESET_DEVICE 0x99 //复位器件 (第一字节写入)
#define SM25QH128M_QPI_ENTER 0x38 //进入QPI模式 (第一字节写入)
#define SM25QH128M_QPI_EXIT 0xFF //退出QPI模式 (第一字节写入)
#define SM25QH128M_WRITE_ENABLE 0x06 //写入使能 (第一字节写入)
#define SM25QH128M_WRITE_DISABLE 0x04 //写入禁止 (第一字节写入)
#define SM25QH128M_STATUS_READ 0x05 //读状态寄存器 (第一字节写入)(第二字节读出状态)
#define SM25QH128M_STATUS_WRITE 0x01 //写状态寄存器 (第一字节写入)(第二字节写入状态)
#define SM25QH128M_MANUFACTURER 0x90 //读取制造商ID,设备ID (第一字节写入)(第2字节冗余)(第3字节冗余)(第4字节写入0x00或者0x01)(第5字节读出前数据)(第6字节读出后数据)
#define SM25QH128M_ID 0x9F //读取ID (第一字节写入)(第二字节制造商号)(第3字节ID高8位)(第4字节ID低8位)
//读取指令
#define SM25QH128M_NORMAL_READ 0x03 //正常读取 (第一字节写入指令)(再写入3个字节的地址)(读出数据)
#define SM25QH128M_FAST_READ 0x0B //快速读取
#define SM25QH128M_DUAL_OUTPUT_FAST_READ 0x3B
#define SM25QH128M_DUAL_IO_FAST_READ 0xBB
#define SM25QH128M_QUAD_IO_FAST_READ 0xEB
#define SM25QH128M_QUAD_OUTPUT_FAST_READ 0x6B
//写指令
#define SM25QH128M_PAGE_PROGRAM 0x02 //页编程 (第一字节写入指令)(再写入3个字节的地址)(写入数据)
#define SM25QH128M_QUAD_PAGE_PROGRAM 0x32 //Quad 输入页编程
//擦除
#define SM25QH128M_SECTOR_ERASE 0x20 //扇区擦除
#define SM25QH128M_BLOCK32KB_ERASE 0x52 //块擦除 32KB
#define SM25QH128M_BLOCK64KB_ERASE 0xD8 //块擦除 64KB
#define SM25QH128M_CHIP_ERASE 0xC7 //或者 0x60 //芯片擦除
5. SPI常用操作
(1)写入使能
在执行任何写入操作之前,需要先将WEL位置1。当操作完成之后,WEL自动复位到0状态。
void SM25QH128_WriteEnable(void)
{
_CS = 0;
WriteByte(SM25QH128M_WRITE_ENABLE);//0x06
_CS = 1;
}
void SM25QH128_WriteDisable(void)
{
_CS = 0;
WriteByte(SM25QH128M_WRITE_DISABLE );//0x04
_CS = 1;
}
(2)读取制造商MID,设备ID
- FLASH存储器有两个标准,CFI和JEDEC。本存储芯片很明显支持的是JEDEC。Joint Electron Device Engineering Council 即电子元件工业联合会。JEDEC是由生产厂商们制定的国际性协议,主要为计算机内存制定。工业标准的内存通常指的是符合JEDEC标准的一组内存。
- jedec_id 信息,包括制造商ID,存储类型ID,容量ID。
这里我们使用的指令0x9F。
#define MANUFACTURER_ID 0x20
#define MEMMORY_TYPE_ID 0x7018
#define CAPACITY_ID 0x17
#define FLASH9FID 0x00207017
uint32_t SM25QH128_ReadID(void)
{
uint32_t ID = 0;
_CS = 0;
WriteByte(SM25QH128M_ID);//0x9F
ID |= readWriteByte(0xFF) << 16;
ID |= readWriteByte(0xFF) << 8;
ID |= readWriteByte(0xFF);
_CS = 1;
return ID;
}
(3)对芯片进行复位
芯片的复位操作需要 复位使能0x66 和 复位操作0x99 一起执行,才能实现复位操作。
void SM25QH128_ResetEnable(void)
{
_CS = 0;
WriteByte(SM25QH128M_RESET_ENABLE );//0x66
_CS = 1;
}
void SM25QH128_Reset(void)
{
_CS = 0;
WriteByte(SM25QH128M_RESET_DEVICE);//0x99
_CS = 1;
}
void SM25QH128_init(void)
{
uint8_t i=0;
SM25QH128_WriteEnable(); //写使能
SM25QH128_ResetEnable(); //复位使能
SM25QH128_Reset(); //复位
//SM25QH128_WriteDisable();//写禁止 (自动关闭)
rt_thread_delay(50);
//读取ID信息
for(i = 0;i < 100;i++) // 等待访问Flash OK
{
if(FLASH9FID == SM25QH128_ReadID())
{
i=0;
break;
}
rt_thread_delay(50);
}
if(i != 0) return 0; //失败
return 1; //成功
}
(3)读状态寄存器
在执行编程,擦除,写入状态位时,通过读取状态寄存器0x05 ,WIP位,检测芯片的状态。WIP位可以看成 busy 指示位。
由下面的时序图,可以看到 有2个字节的状态寄存器值。(与上述定义的1字节 状态寄存器 不太相符,官方文档还需要更严谨才行)实际使用按照一个字节去读。
uint8_t SM25QH128_ReadStatus(void)
{
uint8_t status = 0;
_CS = 0;
WriteByte(SM25QH128M_STATUS_READ);
status = readWriteByte(0xFF);
_CS = 1;
return status;
}
(4)正常读取操作
正常读取操作频率为83MHz。
uint32_t SM25QH128_Read(uint32_t addr, uint8_t *buff, uint32_t len)
{
int i=0;
uint8_t status = 0;
uint8_t cmd[4] = {0};
cmd[0] = SM25QH128M_NORMAL_READ;
cmd[1] = (addr >> 16 ) & 0xFF;
cmd[2] = (addr >> 8 ) & 0xFF;
cmd[3] = (addr >> 0 ) & 0xFF;
while(1)
{
state = SM25QH128_ReadStatus();
if(state&0x01) continue;
else break;
}
_CS = 0;
for(i = 0; i < 4; i++) WriteByte(cmd[i]);
for(i = 0; i < len; i++) buff[i] = readWriteByte(0xff);
_CS = 1;
return len;
}
(5)快速读取数据
最高频率为104MHz。由时序图可以看到,发送完地址后,有一字节的无效数据。
uint32_t SM25QH128_FastRead(uint32_t addr, uint8_t *buff, uint32_t len)
{
int i=0;
uint8_t status = 0;
uint8_t cmd[5] = {0};
cmd[0] = SM25QH128M_FAST_READ ;
cmd[1] = (addr >> 16 ) & 0xFF;
cmd[2] = (addr >> 8 ) & 0xFF;
cmd[3] = (addr >> 0 ) & 0xFF;
cmd[4] = 0xFF;
while(1)
{
state = SM25QH128_ReadStatus();
if(state&0x01) continue;
else break;
}
_CS = 0;
for(i = 0; i < 5; i++) WriteByte(cmd[i]);
for(i = 0; i < len; i++) buff[i] = readWriteByte(0xff);
_CS = 1;
return len;
}
(6)写入数据,页编程
- Page页编程 0x02 操作,一次可以向芯片写入256字节的数据。
- 在页编写之前,需要发送**写使能指令0x06 **,使得状态寄存器的WEL位z置1。完成操作以后,WEL 自动复位0。
- 如果超过 256 bytes 数据被写入到芯片内,则芯片只会接收后面的 256 bytes 数据写入 Page 内部。
- 如果小于 256 bytes 数据被写入到芯片内,则芯片会将对应地址的数据写入,而其他未涉及到的数据则保持不变。
//一次可以向芯片写入256字节的数据
void SM25QH128_WirtePage(uint32_t addr, uint8_t *data, uint16_t len)
{
uint8_t state = 0;
uint16_t i = 0;
uint8_t cmd [4] = {0};
cmd[0] = SM25QH128M_PAGE_PROGRAM;
cmd[1] = (addr >> 16 ) & 0xFF;
cmd[2] = (addr >> 8 ) & 0xFF;
cmd[3] = (addr >> 0 ) & 0xFF;
while(1)
{
state = SM25QH128_ReadStatus();
if(state&0x01) continue;
else break;
}
SM25QH128_WriteEnable();
_CS = 0;
for(i = 0; i < 4; i++) WriteByte(cmd[i]);
for(i = 0; i < len; i++) WriteByte(data[i]);
_CS = 1;
//SM25QH128_WriteDisable();//写禁止 (自动关闭)
}
int SM25QH128_Wirte(uint32_t addr, uint8_t *data, int len)
{
int i = 0;
int page_num = len / PAGE_SIZE;
int last_len = len % PAGE_SIZE;
for(i = 0; i < page_num; i++)
{
SM25QH128_WirtePage((addr + (i * PAGE_SIZE)), data + (i * PAGE_SIZE), PAGE_SIZE);
}
if(last_len)
{
SM25QH128_WirtePage((addr + (i * PAGE_SIZE)), data + (i * PAGE_SIZE), last_len);
}
return len;
}
(7)扇区擦除
- 在执行扇区擦除操作之前,WEL 必须通过 **写使能指令0x06 **设置为 1,完成扇区擦除操作以后,WEL 自动复位。
- 扇区擦除指令 0x20。
int SM25QH128_EraseSector(uint32_t addr)
{
uint8_t state = 0;
uint16_t i = 0;
uint8_t cmd [4] = {0};
cmd[0] = SM25QH128M_SECTOR_ERASE;
cmd[1] = (addr >> 16 ) & 0xFF;
cmd[2] = (addr >> 8 ) & 0xFF;
cmd[3] = (addr >> 0 ) & 0xFF;
while(1)
{
state = SM25QH128_ReadStatus();
if(state&0x01) continue;
else break;
}
SM25QH128_WriteEnable();
_CS = 0;
for(i = 0; i < 4; i++) WriteByte(cmd[i]);
_CS = 1;
//SM25QH128_WriteDisable();//写禁止 (自动关闭)
}
#define PAGE_SIZE 0x100 //一页 256字节
#define SECTOR_SIZE 0x1000 //一个扇区有4KByte(4096字节)。共有4096个扇区
#define SECTOR_TO_ADDR(n) ((n)*0x1000)
void Flash_erase(uint32_t addr, uint32_t len)
{
uint32_t i = 0;
uint32_t start_sector = addr / SECTOR_SIZE;
uint32_t sector_num = len / SECTOR_SIZE;
if( addr % SECTOR_SIZE)
{
start_sector = start_sector -1;
sector_num++;
}
if(len % SECTOR_SIZE)
{
sector_num++;
}
for(i = 0; i <= sector_num; i++)
{
SM25QH128_EraseSector(SECTOR_TO_ADDR(start_sector+i));
}
}
(8)块擦除
- 在执行扇区擦除操作之前,WEL 必须通过 **写使能指令0x06 **设置为 1,完成扇区擦除操作以后,WEL 自动复位。
- 块擦除指令 32KB的0x52 、64KB的0xD8。
(9)芯片擦除
- 在执行扇区擦除操作之前,WEL 必须通过 **写使能指令0x06 **设置为 1,完成扇区擦除操作以后,WEL 自动复位。
- 芯片擦除指令0xC7 、0x60。
6. 软件初始化与应用
7. 应用电路
版权声明:本文为CSDN博主「Kshine2017」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Kshine2017/article/details/121414791
暂无评论