OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 461|回复: 1

W25QXX全功能测试,灵活的保护功能

[复制链接]

3

主题

47

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
448
金钱
448
注册时间
2016-7-29
在线时间
103 小时
发表于 2026-7-20 20:31:49 | 显示全部楼层 |阅读模式
本帖最后由 小迷哥 于 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
ScreenShot_2026-07-21_075723_323.png

2)status reg2
ScreenShot_2026-07-21_075921_313.png

3)status reg3
ScreenShot_2026-07-21_075946_871.png

以上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




Planets, no limit!
回复

使用道具 举报

3

主题

47

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
448
金钱
448
注册时间
2016-7-29
在线时间
103 小时
 楼主| 发表于 3 天前 | 显示全部楼层
cool                                       
Planets, no limit!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

如发现本坛存在违规或侵权内容, 请点击这里发送邮件举报 (或致电020-38271790)。请提供侵权说明和联系方式。我们将及时审核依法处理,感谢配合。

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-8-4 23:21

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表