中级会员
 
- 积分
- 448
- 金钱
- 448
- 注册时间
- 2016-7-29
- 在线时间
- 103 小时
|
本帖最后由 小迷哥 于 2026-8-1 16:51 编辑
主要以W25Q80/16/32/64/128展开,使用标准SPI 4线通信,支持mode0(0,0)和mode3(1,1)原子程序中使用的mode3使用USMART进行测试。。。。。。
一:基础知识
换算关系如下:
1页=256B=2^8B
1扇区=4KB=2^12B=16页
1块=64KB=2^16B=16扇区=256页
1)W25Q80,1MB闪存,地址用20位表示,寻址空间0x000000~0x0FFFFF;
使用块/扇区/页分别表示为:共16个块,共256个扇区,共4096页,
2)W25Q16,2MB闪存,地址用21位表示,寻址空间0x000000~0x1FFFFF;
使用块/扇区/页分别表示为:共32个块,共512个扇区,共8192页,
3)W25Q32,4MB闪存,地址用22位表示,寻址空间0x000000~0x3FFFFF;
使用块/扇区/页分别表示为:共64个块,共1024个扇区,共16384页,
4)W25Q64,8MB闪存,地址用23位表示,寻址空间0x000000~0x7FFFFF;
使用块/扇区/页分别表示为:共128个块,共2048个扇区,共32768页,
5)W25Q128,16MB闪存,地址用24位表示,寻址空间0x000000~0xFFFFFF;
使用块/扇区/页分别表示为:共256个块,共4096个扇区,共65536页,
可以看得出来相邻2个型号,容量相差1倍
指令里面的地址,均使用的3字节共24位进行表示;
二:实际使用,可以参考原子的战舰板 SPI程序
1)扇区内计算
sec=add/4096;//计算1个地址,所在的扇区sec
sec_off=add%4096;//计算1个地址,在扇区内字节的偏移
sec_remain=4096-add%4096;//计算1个地址,在扇区内还剩多少空间到达扇区边界
2)页内计算
page=add/256;//计算1个地址,所在的页号
page_off=add%256;//计算1个地址,在页内字节的偏移
page_remain=256-add%256;//计算1个地址,在当前页内还剩多少空间到达页边界
注意:这个页内计算,在AT24CXX中也有使用,将数据拆分后,按页写入
页计算和扇区计算在数据写入时是非常有用的;
当然这里面还可以用半字偏移来计算,计算扇区/页内 半字的偏移,以及还剩多少个半字到扇区/页边界
三:W25QXX系列最强大的功能主要是灵活的存储空间保护,主要通过控制和状态寄存器实现
我们以W25Q128为例说明,其他型号,参考对应手册
1)status reg1
2)status reg2
3)status reg3
以上3个状态寄存器位,标注为volatile/non_volatile表示数据位设置后掉电可保存
2种保护:上电/复位后,先用命令0x35读取寄存器3,WPS位数据
- WPS=0,全局保护[使用CMP, SEC, TB, BP0~2]
- WPS=1, 独立保护[独立块或扇区保护,一共254块+首尾32扇区=286个保护位,可以使用命令0x36,0x39,0x3d,0x7e,0x78]
- 当我们想擦除/写入数据都某个扇区时,如果对应地址处于保护状态,那么是无法执行的,只有解除保护后,才能擦除/写入数据
数据存储区,分3个部分
- 主闪存区,就是闪存本身寻址空间,比如W25Q128,存储大小为16MB,寻址空间0x000000~0xFFFFFF
- 参数表区SFDR,一共256B
- 安全寄存器区,有3个,每个256B
存储保护,上电/复位后,读取status reg3,可以得到WPS位数据,WPS=1时,独立扇区保护模式,这个上电就是默认闪存空间全保护的,我们可以通过
0x36 对某个独立扇区/块 保护
0x39 对某个独立扇区/块 解除保护
0x7E 对所有独立扇区/块 保护
0x98 对所有独立扇区/块 解除保护
0x3D 对某个独立扇区/块 读取保护状态,1-保护,0-解除保护
WPS=0时,读取status reg1, reg2,可以获得CMP, SEC, TB, BP0~2数据位
分CMP=0,正常保护和CMP=1反转保护2种类,通过7.1.15和7.1.16两张表看出,在同一行的2者保护区域相加就是整个闪存空间
反转保护的意思,就是原来受保护的区域现在不保护了;原来不保护的区域现在保护了
四:程序设计//定义保护结构体数据,使用位域方式
typedef struct
{
vu8 s1;
vu8 s2;
vu8 s3;
vu8 sec: 1;//0-64KB, 1-4KB
vu8 tb: 1;//0-顶部块, 1-底部块
vu8 bp2_0: 3;//块保护范围
vu8 srp1_0: 2;//
vu8 cmp: 1;//反转保护,0-正常,1-反转
vu8 lb3_1: 3;//安全寄存器保护位,共3个,每个位表示1个安全寄存器256B
vu8 hold_rst:1;//0-hold, 1-rset
vu8 drv1_0: 2;//SPI驱动强度,4个等级
vu8 wps: 1;//保护逻辑,0-全局保护,1-独立块保护
}W25QXX_STATUS_STU;
W25QXX_STATUS_STU w25qxx_status_typedef;
那么我们可以根据这两个表,输入任意1个地址,看下下是否处于保护状态,我们设计了1个函数u8 myW25QXX_ProtectAnaly(u32 add),用于分析保护情况//存储阵列保护分析
//判断给定的任意1个地址,0~(1<<24 -1)是否处于保护状态
//返回值:0无保护,其他值保护
//以W25Q128为例,测试
//S1:bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
// SPR0 SEC TB BP2 BP1 BP0 WEL BUSY
//S2:bit15 bit14 bit13 bit12 bit11 bit10 bit9 bit8
// SUS CMP LB3 LB2 LB1 NC QE SPR1
//S3:bit23 bit22 bit21 bit20 bit19 bit18 bit17 bit16
// H/RST DRV1 DRV0 NC NC WPS NC NC
u8 myW25QXX_ProtectAnaly(u32 add)
{
u8 res=0;
u32 add_start=0, add_end=0; //计算地址
u32 protect_size=0; //保护大小
if(add>=(1<<24)) return 0;//数据非法
//读状态寄存器并处理
w25qxx_status_typedef.s1=myW25QXX_ReadSR(W25X_ReadStatusReg);
w25qxx_status_typedef.s2=myW25QXX_ReadSR(W25X_ReadStatusReg2);
w25qxx_status_typedef.s3=myW25QXX_ReadSR(W25X_ReadStatusReg3);
//数据处理
w25qxx_status_typedef.sec=(w25qxx_status_typedef.s1&SEC_POS)>>SEC_SHIFT;
w25qxx_status_typedef.tb=(w25qxx_status_typedef.s1&TB_POS)>>TB_SHIFT;
w25qxx_status_typedef.bp2_0=(w25qxx_status_typedef.s1&BP_POS)>>BP_SHIFT;
w25qxx_status_typedef.srp1_0=((w25qxx_status_typedef.s1&SPR0_POS)>>SPR0_SHIFT)|((w25qxx_status_typedef.s2&SPR1_POS)<<1);
w25qxx_status_typedef.cmp=(w25qxx_status_typedef.s2&CMP_POS)>>CMP_SHIFT;
w25qxx_status_typedef.lb3_1=(w25qxx_status_typedef.s2&LB_POS)>>LB_SHIFT;
w25qxx_status_typedef.hold_rst=(w25qxx_status_typedef.s3&HRST_POS)>>HRST_SHIFT;
w25qxx_status_typedef.drv1_0=(w25qxx_status_typedef.s3&DRV_POS)>>DRV_SHIFT;
w25qxx_status_typedef.wps=(w25qxx_status_typedef.s3&WPS_POS)>>WPS_SHIFT;
//打印当前的数据
printf("\r\nS1=%02X, S2=%02X, S3=%02X\r\nWPS=%d, CMP=%d\r\nSEC=%d, TB=%d, BP2_1=%d\r\nLB3_1=%d, DRV0_1=%d, HOLD/RST=%d\r\n",
w25qxx_status_typedef.s1, w25qxx_status_typedef.s2, w25qxx_status_typedef.s3,\
w25qxx_status_typedef.wps, w25qxx_status_typedef.cmp,\
w25qxx_status_typedef.sec, w25qxx_status_typedef.tb, w25qxx_status_typedef.bp2_0,\
w25qxx_status_typedef.lb3_1, w25qxx_status_typedef.drv1_0, w25qxx_status_typedef.hold_rst);
//分析过程
if(!w25qxx_status_typedef.wps)//全局保护方式,使用CMP, TB, SEC, BP2~0
{
if(!w25qxx_status_typedef.cmp)//普通保护
{
if(!w25qxx_status_typedef.bp2_0) {printf("\r\nNone protect!");return 1;}
if(w25qxx_status_typedef.bp2_0==7) {printf("\r\nAll protect!, add_start=0, add_end=0xFFFFFF");return 0;}
if(!w25qxx_status_typedef.sec && !w25qxx_status_typedef.tb)//SEC-0, TB-0
{
//计算保护区域
protect_size=BLOCK_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0+1);
add_start=ALL_SIZE-protect_size;
add_end=ALL_SIZE-1;
}else if(!w25qxx_status_typedef.sec && w25qxx_status_typedef.tb)//SEC-0, TB-1
{
//计算保护区域
protect_size=BLOCK_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0+1);
add_start=0;
add_end=protect_size-1;
}else if(w25qxx_status_typedef.sec && !w25qxx_status_typedef.tb)//SEC-1, TB-0
{
//计算保护区域
if(w25qxx_status_typedef.bp2_0>4) w25qxx_status_typedef.bp2_0=4;//限制保护范围
protect_size=SECTOR_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0-1);
add_start=ALL_SIZE-protect_size;
add_end=ALL_SIZE-1;
}else if(w25qxx_status_typedef.sec && w25qxx_status_typedef.tb)//SEC-1, TB-1
{
//计算保护区域
if(w25qxx_status_typedef.bp2_0>4) w25qxx_status_typedef.bp2_0=4;//限制保护范围
protect_size=SECTOR_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0-1);
add_start=0;
add_end=protect_size-1;
}
//16MB对应的大小为16384KB
if(!w25qxx_status_typedef.sec)//64KB
printf("\r\n%s, %d/256, %d thru %d\r\n", (!w25qxx_status_typedef.tb)?"U":"L", protect_size/BLOCK_SIZE, add_start/BLOCK_SIZE, add_end/BLOCK_SIZE);
else//4KB
printf("\r\n%s, %d/4096, %d thru %d\r\n", (!w25qxx_status_typedef.tb)?"U":"L", protect_size/SECTOR_SIZE, add_start/BLOCK_SIZE, add_end/BLOCK_SIZE);
printf("\r\nadd_start=%06X, add_end=%06X, protect_size=%dKB\r\n", add_start, add_end, protect_size/1024);
}else//反转保护
{
if(w25qxx_status_typedef.bp2_0==7) {printf("\r\nNone protect!");return 1;}
if(!w25qxx_status_typedef.bp2_0) {printf("\r\nAll protect 16MB!, add_start=0, add_end=0xFFFFFF");return 0;}
if(!w25qxx_status_typedef.sec && !w25qxx_status_typedef.tb)//SEC-0, TB-0
{
//计算保护区域
protect_size=BLOCK_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0+1);
add_start=0;
add_end=ALL_SIZE-protect_size-1;
}else if(!w25qxx_status_typedef.sec && w25qxx_status_typedef.tb)//SEC-0, TB-1
{
//计算保护区域
protect_size=BLOCK_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0+1);
add_start=protect_size;
add_end=ALL_SIZE-1;
}else if(w25qxx_status_typedef.sec && !w25qxx_status_typedef.tb)//SEC-1, TB-0
{
//计算保护区域
if(w25qxx_status_typedef.bp2_0>4) w25qxx_status_typedef.bp2_0=4;//限制保护范围
protect_size=SECTOR_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0-1);
add_start=0;
add_end=ALL_SIZE-protect_size-1;
}else if(w25qxx_status_typedef.sec && w25qxx_status_typedef.tb)//SEC-1, TB-1
{
//计算保护区域
if(w25qxx_status_typedef.bp2_0>4) w25qxx_status_typedef.bp2_0=4;//限制保护范围
protect_size=SECTOR_SIZE*myw25qxx_pow(2, w25qxx_status_typedef.bp2_0-1);
add_start=protect_size;
add_end=ALL_SIZE-1;
}
//16MB对应的大小为16384KB
if(!w25qxx_status_typedef.sec)//64KB
printf("\r\n%s, %d/256, %d thru %d\r\n", (!w25qxx_status_typedef.tb)?"L":"U", 256-protect_size/BLOCK_SIZE, add_start/BLOCK_SIZE, add_end/BLOCK_SIZE);
else//4KB
printf("\r\n%s, %d/4096, %d thru %d\r\n", (!w25qxx_status_typedef.tb)?"L":"U", 4096-protect_size/SECTOR_SIZE, add_start/BLOCK_SIZE, add_end/BLOCK_SIZE);
printf("\r\nadd_start=%06X, add_end=%06X, protect_size=%dKB\r\n", add_start, add_end, 16384-protect_size/1024);
}
if(add>=add_start && add<=add_end)
{
res=1;
}else res=0;
}else//独立扇区保护方式,使用独立块锁方式
{
res=myW25QXX_block_lock_sta_read(add, 0);
myW25QXX_block_lock_sta_read(add, 1);//读出所有块锁状态
}
if(res) printf("\r\nCurrent add is protected!\r\n");
else printf("\r\nCurrent add is no protected!");
return res;
}
五:SFDP参数寄存器读,安全寄存器读/写
//读出SFDP, SECURITY1/2/3数据
//type:0-SFDP, 1-SECURITY1, 2-SECURITY1, 3-SECURITY1,
void my25qxx_read_sfr(u8 type)
{
u16 i;
u32 add;
if(type>3) return;
W25QXX_CS=0; //使能器件
if(type==0) //SFDP:参数表读取
{
add=W25QX_SFR_Start_ADD;
SPI2_ReadWriteByte(W25X_READ_SFDP);
SPI2_ReadWriteByte(add>>16);
SPI2_ReadWriteByte(add>>8);
SPI2_ReadWriteByte(add);
SPI2_ReadWriteByte(0xff); //dummy
for(i=0;i<256;i++)
W25QXX_BUFFER=SPI2_ReadWriteByte(0xff);
}else //安全寄存器读取
{
add=W25QX_Security_Start_ADD+(type-1)*SECTOR_SIZE;
SPI2_ReadWriteByte(W25X_Read_Security);
SPI2_ReadWriteByte(add>>16);
SPI2_ReadWriteByte(add>>8);
SPI2_ReadWriteByte(add);
SPI2_ReadWriteByte(0xff); //dummy
for(i=0;i<256;i++)
W25QXX_BUFFER=SPI2_ReadWriteByte(0xff);
}
W25QXX_CS=1; //取消片选
//数据打印
printf("\r\nRead %s, Add=%06X", (type==0)?"SFDP":"Security", add);
for(i=0;i<256;i++)
{
if(i%16==0) printf("\r\n");
printf("%02X ",W25QXX_BUFFER);
}
}
六:头文件的其他定义
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
//#define W25X_ReadStatusReg 0x05
//#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
//26/1/20测试保护功能,新增的定义
//状态寄存器1/2/3的读和写
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadStatusReg2 0x35
#define W25X_WriteStatusReg2 0x31
#define W25X_ReadStatusReg3 0x15
#define W25X_WriteStatusReg3 0x11
//独立的块/扇区锁设置
#define W25X_Individual_Block_lock 0x36//参数为24位地址,使用块地址对齐/扇区地址对齐
#define W25X_Individual_Block_unlock 0x39//参数为24位地址,使用块地址对齐/扇区地址对齐
#define W25X_Read_Block_lock 0x3d//参数为24位地址,使用块地址对齐/扇区地址对齐
#define W25X_GLOBAL_Block_lock 0x7E
#define W25X_GLOBAL_Block_unlock 0x98
#define W25X_32KBErase 0x52
#define W25X_64KBErase 0xD8
#define W25X_RESET_ENABLE 0x66//复位使能+复位
#define W25X_RESET 0x99
#define W25X_READ_SFDP 0x5A//读取参数表
#define W25X_Erase_Security 0x44//安全寄存器操作
#define W25X_Program_Security 0x42
#define W25X_Read_Security 0x48
#define W25X_Erase_Program_Suspend 0X75//擦除/编程挂起
#define W25X_Erase_Program_Resume 0x7A//擦除/编程恢复
#define W25QX_SFR_Start_ADD 0
#define W25QX_Security_Start_ADD 0x1000//安全寄存器1开始地址
#define W25QX_REG_SIZE 256//参数表和安全寄存器大小256B
////////////////////////////////////////////////////////////////////////////
//S1
#define SPR0_POS (1<<7)
#define SEC_POS (1<<6)
#define TB_POS (1<<5)
#define BP_POS (7<<2)
//S2
#define CMP_POS (1<<6)
#define LB_POS (1<<3)
#define SPR1_POS (1<<0)
//S3
#define HRST_POS (1<<7)
#define DRV_POS (1<<5)
#define WPS_POS (1<<2)
//S1
#define SPR0_SHIFT (7)
#define SEC_SHIFT (6)
#define TB_SHIFT (5)
#define BP_SHIFT (2)
//S2
#define CMP_SHIFT (6)
#define LB_SHIFT (3)
//S3
#define HRST_SHIFT (7)
#define DRV_SHIFT (5)
#define WPS_SHIFT (2)
#define SECTOR_SIZE 0x1000 //4KB
#define BLOCK_SIZE 0x10000 //64KB
#define ALL_SIZE (1<<24) //16MB
|
|