百度上搜索的资料太少了。目前为止,百度只能搜到2条相关的文章。
该国产芯片与进口芯片W25Q128JV 很相似。可以参考W25Q128JV。
与进口芯片不同,SM25QH128M官方手册表明,只有一个状态寄存器。如下图所示:
/*
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 //芯片擦除
在执行任何写入操作之前,需要先将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;
}
#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;
}
芯片的复位操作需要 复位使能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; //成功
}
在执行编程,擦除,写入状态位时,通过读取状态寄存器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;
}
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;
}
最高频率为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;
}
//一次可以向芯片写入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;
}
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));
}
}