百度文库一个很详细的介绍、、、、https://wenku.baidu.com/view/7db1401e1a37f111f0855b81.html?from=search
(。。。。)

先来简单认识一下这个芯片 W25Q16

其实就是以SPI作为通信时序要求的一款储存芯片,升级版的EEPROM,比EEPROM读取速度快,价格还差不多。

W25X16分为8192页,每页256字节,用“页编程指令”每次就可以编程256字节,用“扇区擦除指令”每次可擦除16页,用“块擦除指令”每次可擦除256页,用“整片擦除指令”可一次擦除整个芯片,W25X16有512个可擦除扇区或32个可擦除块。

对于W25X16,1页=256字节,归纳一下,

1页=256字节
1扇区=16页=16*256字节=4096字节 (W25X16有512个扇区)。
1块=256页=256*256字节=65536字节 (W25X16有32块)。

W25X16引脚排列如下图

注意有一点比较坑的,,,,
关于IO口控制这个存储芯片。。。。。。。。

RP2的作用的限流,而RP5的作用就是上拉!还是外部上拉!!!
所以单片机要想IO控制这个存储芯片的电平,必须是开漏模式!
所以在下面的程序 main.c中可以看到这么几行

    P1M0 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P1M1 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P0M0 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P0M1 = 0xFF;

就是讲P1口以及P0设置成开漏模式

引脚重点说明:

DIO脚 :在普通方式下,这个引脚是串行输入引脚(DI),数据、地址和命令通过此引脚送到芯片内部,在CLK引脚的上升沿捕获。

当使用了“快读双输出指令”时,这个引脚就变成了DO引脚,这种情况下,芯片就有了2个DO引脚,所以叫做双输出,这时芯片的通信速度相当于翻了一倍,所以传输速度更快。

/HOLD脚: 保持引脚,当/CS片选为低电平,且HOLD为低电平时,DO引脚处于高阻态,而且会忽略DIO和CLK引脚上的信号,把HOLD拉高,器件恢复正常工作当芯片与多个其它芯片共享单片机上的同一个SPI接口时,此引脚就显得非常有用,通常此引脚接高电平保证芯片正常工作。

W25Q16内部状态寄存器


(上电复位时,各位都被清零)

BUSY忙位:

只读位,在芯片执行“页编程”,“扇区擦除” 、“块擦除”、 “芯片擦除”、
“写状态寄存器”指令时,该位自动置1,此时除了“读状态寄存器”指令,其它指令都无效,当编程、擦除和写状态寄存器指令执行完毕后,该为自动变0,表示芯片可以接收其它指令了。

WEL写保护位:

只读位,写操作允许标志位,当执行完写使能指令后,该位为0表示允许写操作,为1表示禁止写,当芯片掉电后或执行写禁止、页编程、扇区擦除、块擦除、芯片擦除和写状态寄存器命令后自动进入写保护状态,即置1。

BP2、BP1、BP0块保护位:

可读写位,用于块区保护,可用写状态寄存器命令修改这几位,为这3位为0时,块区无保护,当SPR位为1或/WP脚为低时,这3位无法更改。

TB 底部顶部块保护位:

可读写位,用于底部顶部块保护,可用写状态寄存器命令修改这1位,当这1位为0时,底部顶部块区无保护,当SPR位为1或/WP脚为低时,这1位无法更改。

SPR状态寄存器保护位:
可读写位,意义如下表

重点来了::::

W25X16包括15个基本指令,通过这15个基本指令与SPI总线就完全可以控制芯片,指令在/CS拉低后开始传送,DIO引脚上数据的第一个字节就是指令码在CLK引脚的上升沿采集DIO数据,高位在前
指令的长度从1个字节到多个字节,有时还会跟随地址字节、数据字节、伪字节,有时候还会是它们的组合,在/CS引脚的上升沿完成指令的传输,所有的读指令都可以在任意时钟位完成,而所有的写、编程和擦除指令在一个字节的边界后才能完成,否则,指令将不起作用,这个特征可以保护芯片不被意外写入,当芯片正在被编程、擦除或写状态寄存器的时候,除“读状态寄存器”指令,其它所有指令都将被忽略直到擦写周期结束。

说明:数据高位在前,带括号的数据表示数据从DO引脚读出。

指令说明学习:

写使能06H:

写使能指令将会使状态寄存器WEL位置位,在执行每个“页编程”、“扇区擦除”、“块擦除”、“芯片擦除”和“写状态寄存器”命令之前,都要先置位WEL,*再把/CS脚先拉低之后*,“写使能”指令码06H从DIO引脚输入,在CLK上升沿采集,然后再拉高/CS引脚

写禁止04H:

时序与写使能相同,执行完“页编程”、“扇区擦除”、“块擦除”、“芯片擦除”和
“写状态寄存器”命令之后WEL位会自动变0,即自动进入写禁止状态。

读状态寄存器05H

当/CS拉低之后,开始把05H从DIO引脚送入芯片,在CLK的上升沿数据被芯片采集,当芯片认出采集到的数据是05H时,芯片就会把“状态寄存器”的值从DO引脚输出,数据在CLK的下降沿输出,高位在前。

读状态寄存器指令在任何时候都可以用,甚至在编程、擦除、写状态寄存器的过程中也可以用,这样就可从状态寄存器的BUSY位判断编程、擦除、写状态寄存器周期是否结束,从而让我们知道芯片是否可以接收下一指令,如果/CS不被拉高,状态寄存器的值将一直从DO脚输出,当/CS拉高后,该指令结束

写状态寄存器01H

在执行写状态寄存器指令以前,需要先按“写使能时序”执行完“写使能”指令,然后再次将/CS拉低,把01H从DIO引脚送入芯片,然后再把需要的状态寄存器的值送入芯片,拉高/CS,指令结束,如果此时没把/CS脚拉高,或者是拉得晚了,值将不会被写入,指令无效。

读数据03H

读数据指令允许读取一个或多个字节,先将/CS拉低,把03H从DIO引脚送入芯片,然后再把24位地址送入芯片,这些数据在时钟的上升沿被芯片采集,芯片收到24位在CLK引脚的下降沿从DO引脚输出,高位在前。当读完这个地址的数据后,地址自动增加,然后通过DO引脚把下一个地址的数据输出,也就是说,只要CLK在工作,通过一条指令就可把整个芯片储存区的数据全部读出来,把/CS脚拉高,“读数据”指令结束,当芯片在执行编程、擦除和读状态寄存器指令的周期内,“读数据”指令无效。

页编程02H

执行页编程指令以前,需要先擦除整个待写入区域,保证待写入区域全为1,然后按“写使能时序”执行完“写使能”指令,再把/CS拉低,将02H从DIO引脚送入芯片,然后再把24位地址送入芯片,然后接着送要写入的字节到芯片,在写完数据后,把/CS拉高写完一页后必须把地址改为0,不然的话,如果时钟还在继续,地址将自动变为页的开始地址,如果写入的字节不足256个字节的话,其它写入的字节都是无意义的,如果写入的字节大于256字节,多余的字节加上无用的字节覆盖刚刚写入的256字节,所以需要保证写入的字节小于或等于256字节。如果写入的地址处于写保护状态,“页编程”指令无效。

扇区擦除20H

扇区擦除指令将一个扇区(4096字节)擦除,擦除后扇区字节都为FFH,在执行扇区擦除指令以前,需要先按“写使能时序”执行完“写使能”指令,然后再次将/CS拉低,把20H从DIO引脚送入芯片,然后再把24位扇区地址送入芯片,然后拉高/CS,指令结束,如果此时没及时把/CS脚拉高,指令将不起作用。如果擦除的地址处于写保护状态,“扇区擦除”指令无效。

块擦除D8H

块擦除指令将一个块(65536字节)擦除,擦除后扇区字节都为FFH,在执行块擦除指令以前,需要先按“写使能时序”执行完“写使能”指令,然后再次将/CS拉低,把D8H从DIO引脚送入芯片,然后再把24位扇区地址送入芯片,然后拉高/CS,指令结束,如果此时没及时把/CS脚拉高,指令将不起作用。如果擦除的地址处于写保护状态,“块擦除”指令无效。

芯片擦除C7H

芯片擦除指令将整个芯片储存区擦除,擦除后整个芯片储存区字节都为FFH,在执行芯片擦除指令以前,需要先按“写使能时序”执行完“写使能”指令,然后再次将/CS拉低,把C7H从DIO引脚送入芯片,然后拉高/CS,指令结束,如果此时没及时把/CS脚拉高,指令将不起作用。任何一个块处于写保护状态,“块擦除”指令无效。

软件模拟SPI

emmmmm,,,,,,,来一个实例,实战一下效果更佳。。。
下面实验 晶振12M 波特率115200

嗯,先来说一下程序实现的功能。。。

程序如果正常执行,上电后先打印出一个’OK’
0x01 —> 读器件ID
0x02 —> 读制造+器件ID
0x03 —> 读JEDEC ID
0x04 —> 读指定地址的1个字节数据
0x05 —> 向指定地址写入1个字节的数据
0x06 —> 读寄存器状态
0x07 —> 写使能
0x08 —> 向指定地址写入指定个数的字节数据
0x09 —> 擦除整个chip(芯片)的数据
0x0a —> 擦除指定扇区的数据
0x0b —> 测试数据页面
0x0c —> 读测试数据页面

#include <W25.H>
#include "stc15f2k60s2.h"
#include <intrins.h>extern    uint8 upper_128[16];
extern    uint8 tx_buff[16];void    delay_nms(uchar i)
{    uchar  j;i=i*2;for(;i>0;i--)    {   j = 246;    while(--j);    }
}
void    delay(uchar tt)
{    while(tt--);}
//=================================================================================================
//SPI_Read_StatusReg        Reads the status register of the serial flash
//SPI_Write_StatusReg        Performs a write to the status register
//SPI_Write_Enable            Write enables the serial flash
//SPI_Write_Disable            Write disables the serial flash
//SPI_Read_ID1                Reads the device ID using the instruction 0xAB
//SPI_Read_ID2                Reads the manufacturer ID and device ID with 0x90
//SPI_Read_ID3()            Reads the JedecDevice ID
//SPI_Read_Byte                Reads one byte from the serial flash and returns byte(max of 20 MHz CLK frequency)
//SPI_Read_nBytes            Reads multiple bytes(max of 20 MHz CLK frequency)
//SPI_FastRead_Byte            Reads one byte from the serial flash and returns byte(max of 33 MHz CLK frequency)
//SPI_FastRead_nBytes        Reads multiple bytes(max of 33 MHz CLK frequency)
//SPI_Write_Byte            Program one byte to the serial flash
//SPI_Write_nBytes            Program n bytes to the serial flash, n<=256
//SPI_Erase_Chip            Erases entire serial flash
//SPI_Erase_Sector            Erases one sector (64 KB) of the serial flash
//SPI_Wait_Busy                Polls status register until busy bit is low
//=================================================================================================
uchar    SPI_Read_StatusReg()            //读状态寄存器  备注:Xcc
{    uchar byte = 0;W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_ReadStatusReg);        //    send Read Status Register commandbyte = SPI_Get_Byte();                    //    receive byteW25X_CS = 1;                            //    disable device    return byte;
}
void    SPI_Write_StatusReg(byte)         //写状态寄存器
{    W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_WriteStatusReg);        //    select write to status registerSPI_Send_Byte(byte);                    //    data that will change the status(only bits 2,3,7 can be written)W25X_CS = 1;                            //    disable the device
}
void    SPI_Write_Enable()                   //写使能
{    W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_WriteEnable);        //    send W25X_Write_Enable commandW25X_CS = 1;                            //    disable device
}
void    SPI_Write_Disable()                   //写禁能
{    W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_WriteDisable);        //    send W25X_WriteW25X_DIsable commandW25X_CS = 1;                            //    disable device
}
uchar    SPI_Read_ID1()                        //释放掉电/器件ID
{    uchar byte;W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_DeviceID);            //    send read device ID command (ABh)SPI_Send_Byte(0);                        //    send addressSPI_Send_Byte(0);                        //    send addressSPI_Send_Byte(0);                        //    send 3_Dummy addressbyte = SPI_Get_Byte();                    //    receive Device ID byte    W25X_CS  = 1;                            //    disable devicedelay(4);                                //    remain CS high for tRES2 = 1.8uSreturn byte;
}
uint    SPI_Read_ID2(uchar ID_Addr)             //制造/器件ID
{    uint IData16;W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_ManufactDeviceID);    //    send read ID command (90h)SPI_Send_Byte(0x00);                    //    send addressSPI_Send_Byte(0x00);                    //    send addressSPI_Send_Byte(ID_Addr);                    //    send W25Pxx selectable ID address 00H or 01HIData16 = SPI_Get_Byte()<<8;            //    receive Manufature or Device ID byteIData16 |= SPI_Get_Byte();                //    receive Device or Manufacture ID byteW25X_CS = 1;                            //    disable device    return IData16;
}
uint    SPI_Read_ID3()                           //读JEDEC ID
{    uint IData16;W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_JedecDeviceID);        //    send read ID command (9Fh)IData16 = SPI_Get_Byte()<<8;            //    receive Manufature or Device ID byteIData16 |= SPI_Get_Byte();                //    receive Device or Manufacture ID bytetx_buff[2] = SPI_Get_Byte();    W25X_CS = 1;                            //    disable device    return IData16;
}
uchar    SPI_Read_Byte(uint32 Dst_Addr)                //读某地址 数据
{    uchar byte = 0;    W25X_CS = 0;                                        //    enable deviceSPI_Send_Byte(W25X_ReadData);                        //    read commandSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));//    send 3 address bytesSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte((uchar)(Dst_Addr & 0xFF));byte = SPI_Get_Byte();W25X_CS = 1;                                        //    disable device    return byte;                                        //    return one byte read
}
void    SPI_Read_nBytes(uint32 Dst_Addr, uchar nBytes_128)    //读某地址起nBytes_128字节以内内容
{    uint32 i = 0;    W25X_CS = 0;                                        //    enable deviceSPI_Send_Byte(W25X_ReadData);                        //    read commandSPI_Send_Byte(((Dst_Addr & 0xFFFFFF) >> 16));        //    send 3 address bytesSPI_Send_Byte(((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte(Dst_Addr & 0xFF);for (i = 0; i < nBytes_128; i++)                    //    read until no_bytes is reachedupper_128[i] = SPI_Get_Byte();                    //    receive byte and store at address 80H - FFHW25X_CS = 1;                                        //    disable device
}
uchar    SPI_FastRead_Byte(uint32 Dst_Addr)                  //快读 某地址 数据
{    uchar byte = 0;W25X_CS = 0;                                        //    enable deviceSPI_Send_Byte(W25X_FastReadData);                    //    fast read commandSPI_Send_Byte(((Dst_Addr & 0xFFFFFF) >> 16));        //    send 3 address bytesSPI_Send_Byte(((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte(Dst_Addr & 0xFF);SPI_Send_Byte(0xFF);                                //    dummy bytebyte = SPI_Get_Byte();W25X_CS = 1;                                        //    disable device    return byte;                                        //    return one byte read
}
void    SPI_FastRead_nBytes(uint32 Dst_Addr, uchar nBytes_128)      //快读 某地址 nBytes_128 个字节数据
{    uchar i = 0;    W25X_CS = 0;                                        //    enable deviceSPI_Send_Byte(W25X_FastReadData);                    //    read commandSPI_Send_Byte(((Dst_Addr & 0xFFFFFF) >> 16));        //    send 3 address bytesSPI_Send_Byte(((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte(Dst_Addr & 0xFF);SPI_Send_Byte(0xFF);                                //    dummy bytefor (i = 0; i < nBytes_128; i++)                    //    read until no_bytes is reachedupper_128[i] = SPI_Get_Byte();                    //    receive byte and store at address 80H - FFHW25X_CS = 1;                                        //    disable device
}
void    SPI_Write_Byte(uint32 Dst_Addr, uchar byte)          //但字节写入
{    W25X_CS = 0;                                    //    enable deviceSPI_Write_Enable();                                //    set WELSPI_Wait_Busy();    W25X_CS = 0;    SPI_Send_Byte(W25X_PageProgram);                //    send Byte Program commandSPI_Send_Byte(((Dst_Addr & 0xFFFFFF) >> 16));    //    send 3 address bytesSPI_Send_Byte(((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte(Dst_Addr & 0xFF);SPI_Send_Byte(byte);                            //    send byte to be programmedW25X_CS = 1;                                    //    disable device
}
void    SPI_Write_nBytes(uint32 Dst_Addr, uchar nBytes_128)         //页编程 128个字节
{    uchar i, byte;    W25X_CS = 0;                    /* enable device */SPI_Write_Enable();                /* set WEL */W25X_CS = 0;SPI_Send_Byte(W25X_PageProgram);         /* send Byte Program command */SPI_Send_Byte(((Dst_Addr & 0xFFFFFF) >> 16));    /* send 3 address bytes */SPI_Send_Byte(((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte(Dst_Addr & 0xFF);for (i = 0; i < nBytes_128; i++){byte = upper_128[i];SPI_Send_Byte(byte);        /* send byte to be programmed */}    W25X_CS = 1;                /* disable device */
}
void    SPI_Erase_Chip()                     //擦除芯片
{W25X_CS = 0;                                        //    enable deviceSPI_Write_Enable();                                    //    set WELW25X_CS = 0;SPI_Wait_Busy();W25X_CS = 0;SPI_Send_Byte(W25X_ChipErase);                        //    send Chip Erase commandW25X_CS = 1;                                        //    disable device
}
void    SPI_Erase_Sector(uint32 Dst_Addr)                //扇区擦除
{    W25X_CS = 0;                                        //    enable deviceSPI_Write_Enable();                                    //    set WELW25X_CS = 0;SPI_Send_Byte(W25X_SectorErase);                    //    send Sector Erase commandSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));//    send 3 address bytesSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte((uchar)Dst_Addr & 0xFF);W25X_CS = 1;                                        //    disable device
}
void    SPI_Wait_Busy()                                 //等待忙结束
{    while (SPI_Read_StatusReg() == 0x03)SPI_Read_StatusReg();                //    waste time until not busy WEL & Busy bit all be 1 (0x03)
}
void    SPI_PowerDown()
{    W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_PowerDown);            //    send W25X_PowerDown command 0xB9W25X_CS = 1;                            //    disable devicedelay(6);                                //    remain CS high for tPD = 3uS
}
void    SPI_ReleasePowerDown()
{    W25X_CS = 0;                            //    enable deviceSPI_Send_Byte(W25X_ReleasePowerDown);    //    send W25X_PowerDown command 0xABW25X_CS = 1;                            //    disable devicedelay(6);                                //    remain CS high for tRES1 = 3uS
}#ifdef    SST_SPI
void    SPI_init()
{    P1 = 0xFF; SPCR = 0x50;
}
void    SPI_Send_Byte(uchar out)
{    unsigned char temp; SPDR = out;do    {    temp = SPSR & 0x80;    }    while (temp != 0x80); SPSR = SPSR & 0x7F;
}
uchar    SPI_Get_Byte()
{    unsigned char temp; SPDR = 0x00;do    {    temp = SPSR & 0x80;    }    while (temp != 0x80); SPSR = SPSR & 0x7F;return SPDR;
}
#endif#ifndef    SST_SPI
void    SPI_init()
{    W25X_CLK = 0;                            //    set clock to low initial state for SPI operation mode 0
//    W25X_CLK = 1;                            //    set clock to High initial state for SPI operation mode 3
//    _hold = 1;
//    W25X_WP = 1;W25X_CS = 1;    SPI_Write_Disable();
}
void    SPI_Send_Byte(uchar out)
{    uchar i = 0;    for (i = 0; i < 8; i++){    if ((out & 0x80) == 0x80)            //    check if MSB is highW25X_DI = 1;elseW25X_DI = 0;                    //    if not, set to lowW25X_CLK = 1;                        //    toggle clock highout = (out << 1);                    //    shift 1 place for next bitnop();nop();nop();nop();W25X_CLK = 0;                        //    toggle clock low}
}
uchar    SPI_Get_Byte()
{    uchar i = 0, in = 0, temp = 0;    for (i = 0; i < 8; i++){    in = (in << 1);                        //    shift 1 place to the left or shift in 0temp = W25X_DO;                        //    save inputW25X_CLK = 1;                        //    toggle clock highif (temp == 1)                        //    check to see if bit is highin |= 0x01;                        //    if high, make bit highW25X_CLK = 0;                        //    toggle clock low}    return in;
}
#endif

#include "stc15f2k60s2.h"       // 单片机STC15F2K60S2头文件,可以不再加入reg51.h
#include <intrins.h>                // 加入此头文件后,可使用_nop_库函数
#include "delay.h"              // 延时函数头文件
#include "W25.H"#define    uint8    unsigned char
#define    uint16    unsigned int
#define    uchar    unsigned char
#define    uint    unsigned int
#define    uint32    unsigned longvoid    init_cpu(void);
void    delay(uchar tt);
void    trace(uchar *str,uchar len);
void    test_page(uchar addr);
void    read_page(uchar addr);
void    Verify(uchar byte, uchar cor_byte);uint8    Rxtemp;
bit      MYTI;
uint8    tx_buff[16];
uint8    upper_128[16];
bit      rx_ok;void main(void)
{uint i;P1M0 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P1M1 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P0M0 = 0xFF;    //将SPI CS引脚P3.5设为开漏输出, 使用外置3.3V上拉.P0M1 = 0xFF;//    W25X_HOLD= 1;init_cpu();    SPI_init();tx_buff[0]='O';tx_buff[1]='K';trace(tx_buff,2);for(;;){    if(rx_ok == 1){    rx_ok = 0;switch(Rxtemp){    case 0x01:Rxtemp = 0;tx_buff[0] = SPI_Read_ID1();trace(tx_buff,1);break;case 0x02:i = SPI_Read_ID2(0x00);tx_buff[1] = (uchar)i;tx_buff[0] = (uchar)(i>>8);trace(tx_buff,2);break;case 0x03:i = SPI_Read_ID3();tx_buff[1] = (uchar)i;tx_buff[0] = (uchar)(i>>8);trace(tx_buff,3);break;case 0x04:tx_buff[0] = SPI_Read_Byte(0x00000000);trace(tx_buff,1);break;case 0x05:tx_buff[0] = 0x55;SPI_Write_Byte(0x00000000,0xa5);          trace(tx_buff,1);break;case 0x06:tx_buff[0] = SPI_Read_StatusReg();trace(tx_buff,1);break;case 0x07:SPI_Write_Enable();    break;case 0x08:upper_128[0]=0x01;upper_128[1]=0x02;upper_128[2]=0x03;upper_128[3]=0x04;SPI_Write_nBytes(0x00000000,4);break;case 0x09:SPI_Erase_Chip();break;case 0x0a:SPI_Erase_Sector(0x000ff000);while(1){    tx_buff[0] = SPI_Read_StatusReg();if(tx_buff[0] == 0){    trace(tx_buff,1);break;}}break;case 0x0b:test_page(0x00);break;case 0x0c:read_page(0x00);break;default:break;}            }            }
}void init_cpu(void)
{
//  SCON = 0x50;        //8位数据,可变波特率 18.432M
//  AUXR |= 0x40;       //定时器1时钟为Fosc,即1T
//  AUXR &= 0xFE;       //串口1选择定时器1为波特率发生器
//  TMOD &= 0x0F;       //设定定时器1为16位自动重装方式
//  TL1 = 0x20;         //设定定时初值
//  TH1 = 0xFE;         //设定定时初值
//  ET1 = 0;            //禁止定时器1中断
//  TR1 = 1;            //启动定时器1 SCON = 0x50;        //8位数据,可变波特率  12M AUXR |= 0x40;       //定时器1时钟为Fosc,即1T AUXR &= 0xFE;       //串口1选择定时器1为波特率发生器 TMOD &= 0x0F;       //设定定时器1为16位自动重装方式TL1 = 0xC7;     //设定定时初值TH1 = 0xFE;     //设定定时初值ET1 = 0;        //禁止定时器1中断TR1 = 1;        //启动定时器1 ES    = 1;                //uart interrupt enable     EA = 1;                    //all interrupt enable
}//串口中断程序
void UART_isr(void) interrupt 4
{ if(RI){ RI = 0;Rxtemp = SBUF;   //接收//SBUF = Rxtemp;      //发送rx_ok = 1;return;}if(TI){ TI = 0; MYTI = 1;        }
}
void test_page(uchar addr)
{    uint i; uchar byte;uint32 Dst_Addr;    W25X_CS = 0;                                            //    enable deviceSPI_Write_Enable();                                        //    set WELW25X_CS = 0;Dst_Addr = (uint32)addr*256;Dst_Addr = 0x0ff000;//(uint32)addr*256;SPI_Send_Byte(W25X_PageProgram);                        //    send Byte Program commandSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));    //    send 3 address bytesSPI_Send_Byte((uchar)((Dst_Addr & 0xFFFF) >> 8));SPI_Send_Byte((uchar)(Dst_Addr & 0xFF));for (i = 0; i < 256; i++)                                //    send byte to be programmedSPI_Send_Byte(i);W25X_CS = 1;    delay_nms(5);W25X_CS = 0;while(1){    tx_buff[0] = SPI_Read_StatusReg();trace(tx_buff,1);if(tx_buff[0] == 0)    break;}Dst_Addr = 0x0ff000;for (i = 0; i < 256; i++){    byte = SPI_Read_Byte(Dst_Addr+i); ES = 0; SBUF = byte;while (TI == 0); TI = 0; ES = 1;}W25X_CS = 1;
}
//=================================================================================================
void read_page(uchar addr)
{    uint i;uchar byte;uint32 Dst_Addr;Dst_Addr = addr*256;Dst_Addr = 0x0ff000;W25X_CS = 0;for (i = 0; i < 256; i++){    byte = SPI_Read_Byte(Dst_Addr+i); ES = 0; SBUF = byte;while (TI == 0); TI = 0; ES = 1;}W25X_CS = 1;
}
//=================================================================================================
void Verify(uchar byte, uchar cor_byte)
{    if (byte != cor_byte){    while(1);//LED_Error = 0; /* display to view error on LED. */            }
}
//=================================================================================================
void myputchar(uchar c)
{     ES = 0; SBUF = c;while (TI == 0); TI = 0; ES = 1;
}
//=================================================================================================
void trace(uchar *str,uchar len)
{    uint i;for(i=0;i<len;i++)    {    myputchar(*str);    str++;    }
}

#ifndef   _W25_H_#define  _W25_H_#include "stc15f2k60s2.h"#define    uint8    unsigned char
#define    uint16    unsigned int
#define    uchar    unsigned char
#define    uint    unsigned int
#define    uint32    unsigned long//sbit    W25X_HOLD  = P1^2;
//sbit    W25X_WP    = P1^1;
sbit    W25X_CS    = P0^5;
sbit    W25X_DI    = P1^3;
sbit    W25X_DO    = P1^4;
sbit    W25X_CLK   = P1^5;#define nop() _nop_()#define W25X_WriteEnable        0x06    //写使能  备注:xcc
#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      //器件ID
#define W25X_ManufactDeviceID    0x90      //制造/器件ID
#define W25X_JedecDeviceID        0x9F      //JEDEC IDuchar    SPI_Read_StatusReg();
void    SPI_Write_StatusReg(byte);
void    SPI_Write_Enable();
void    SPI_Write_Disable();
uchar    SPI_Read_ID1();
uint    SPI_Read_ID2(uchar ID_Addr);
uint    SPI_Read_ID3();
uchar    SPI_Read_Byte(uint32 Dst_Addr);
void    SPI_Read_nBytes(uint32 Dst_Addr, uchar nBytes_128);
uchar    SPI_FastRead_Byte(uint32 Dst_Addr);
void    SPI_FastRead_nBytes(uint32 Dst_Addr, uchar nBytes_128);
void    SPI_Write_Byte(uint32 Dst_Addr, uchar byte);
void    SPI_Write_nBytes(uint32 Dst_Addr, uchar nBytes_128);
void    SPI_Erase_Chip();
void    SPI_Erase_Sector(uint32 Dst_Addr);
void    SPI_Wait_Busy();
void    SPI_PowerDown();
void    SPI_ReleasePowerDown();
void    SPI_init();
void    SPI_Send_Byte(uchar out);
uchar    SPI_Get_Byte();
void    delay_nms(uchar i);
void    delay(uchar tt);#endif

上述结果就不在一一上图试验了,,,躬行且体会即可。

硬件SPI实现 ##

基于官方例程修改
下面实验 晶振11.0592M 波特率9600

手记:SPIF和WCOL较为特殊必须写1才能清0

关于程序实现的功能:

从0x00000000开始的256个字节赋值为 0~0xFF,写入到flash,清空数据数组,在从0x00000000开始的256个字节读出数据存入数据数组,打印到串口。

#include "reg51.h"typedef bit BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;#define FOSC            11059200L
#define BAUD            (65536 - FOSC / 4 / 115200)#define NULL            0
#define FALSE           0
#define TRUE            1sfr P0M1 = 0x93;
sfr P0M0 = 0x94;
sfr P1M1 = 0x91;
sfr P1M0 = 0x92;
sfr P2M1 = 0x95;
sfr P2M0 = 0x96;
sfr P3M1 = 0xb1;
sfr P3M0 = 0xb2;
sfr P4M1 = 0xb3;
sfr P4M0 = 0xb4;
sfr P5M1 = 0xC9;
sfr P5M0 = 0xCA;
sfr P6M1 = 0xCB;
sfr P6M0 = 0xCC;
sfr P7M1 = 0xE1;
sfr P7M0 = 0xE2;sfr  AUXR           =   0x8e;                   //辅助寄存器
sfr P_SW1           =   0xa2;                   //外设功能切换寄存器1
#define SPI_S0          0x04
#define SPI_S1          0x08sfr SPSTAT          =   0xcd;                   //SPI状态寄存器
#define SPIF            0x80                    //SPSTAT.7
#define WCOL            0x40                    //SPSTAT.6
sfr SPCTL           =   0xce;                   //SPI控制寄存器
#define SSIG            0x80                    //SPCTL.7
#define SPEN            0x40                    //SPCTL.6
#define DORD            0x20                    //SPCTL.5
#define MSTR            0x10                    //SPCTL.4
#define CPOL            0x08                    //SPCTL.3
#define CPHA            0x04                    //SPCTL.2
#define SPDHH           0x00                    //CPU_CLK/4
#define SPDH            0x01                    //CPU_CLK/16
#define SPDL            0x02                    //CPU_CLK/64
#define SPDLL           0x03                    //CPU_CLK/128
sfr SPDAT           =   0xcf;                   //SPI数据寄存器sbit SS             =   P0^5;                   //SPI的SS脚,连接到Flash的CE#define SFC_WREN        0x06                    //串行Flash命令集
#define SFC_WRDI        0x04
#define SFC_RDSR        0x05
#define SFC_WRSR        0x01
#define SFC_READ        0x03
#define SFC_FASTREAD    0x0B
#define SFC_RDID        0xAB
#define SFC_PAGEPROG    0x02
#define SFC_RDCR        0xA1
#define SFC_WRCR        0xF1
#define SFC_SECTORER    0xD7
#define SFC_BLOCKER     0xD8
#define SFC_CHIPER      0xC7void InitUart();
void SendUart(BYTE dat);
void InitSpi();
BYTE SpiShift(BYTE dat);
BOOL FlashCheckID();
BOOL IsFlashBusy();
void FlashWriteEnable();
void FlashErase();
void FlashRead(DWORD addr, DWORD size, BYTE *buffer);
void FlashWrite(DWORD addr, DWORD size, BYTE *buffer);#define BUFFER_SIZE     1024                    //缓冲区大小
#define TEST_ADDR       0                       //Flash测试地址BYTE xdata g_Buffer[BUFFER_SIZE];               //Flash读写缓冲区
BOOL g_fFlashOK;                                //Flash状态void main()
{
    int i;    P0M0 = 0xFF;
    P0M1 = 0xFF;
    P1M0 = 0xFF;
    P1M1 = 0xFF;    //初始化Flash状态
    g_fFlashOK = FALSE;    //初始化串口和SPI
    InitUart();
    InitSpi();    //检测Flash状态
    FlashCheckID();    //TEST
    FlashErase();
    for(i=0; i<256; i++)
        g_Buffer[i] = i;
    FlashWrite(TEST_ADDR, 256, g_Buffer);
    for(i=0; i<256; i++)
        g_Buffer[i] = 0;
    FlashRead(TEST_ADDR, 256, g_Buffer);
    for(i=0; i<256; i++)
        SendUart(g_Buffer[i]);    while (1);
}/************************************************
串口初始化
入口参数: 无
出口参数: 无
************************************************/
void InitUart()
{
    AUXR = 0x40;                                //设置定时器1为1T模式
    TMOD = 0x00;                                //定时器1为16位重载模式
    TH1 = BAUD >> 8;                            //设置波特率
    TL1 = BAUD;
    TR1 = 1;
    SCON = 0x5a;                                //设置串口为8位数据位,波特率可变模式
}/************************************************
发送数据到串口
入口参数:
    dat : 准备发送的数据
出口参数: 无
************************************************/
void SendUart(BYTE dat)
{
    while (!TI);                                //等待上一个数据发送完成
    TI = 0;                                     //清除发送完成标志
    SBUF = dat;                                 //触发本次的数据发送
}/************************************************
SPI初始化
入口参数: 无
出口参数: 无
************************************************/
void InitSpi()
{
    SPSTAT = SPIF | WCOL;                       //清除SPI状态   1100 0000   SPIF和WCOL较为特殊必须写1才能清0
    SS = 1;
    SPCTL = SSIG | SPEN | MSTR;                 //设置SPI为主模式
    // 1101 0000
}/************************************************
使用SPI方式与Flash进行数据交换
入口参数:
    dat : 准备写入的数据
出口参数:
    从Flash中读出的数据
************************************************/
BYTE SpiShift(BYTE dat)
{
    SPDAT = dat;                                //触发SPI发送
    while (!(SPSTAT & SPIF));                   //等待SPI数据传输完成 SPSTAT的第7位SPIF在不断更新,不断和SPIF是1相与,当发送完毕后两者相与为1,取非为0,退出while
    SPSTAT = SPIF | WCOL;                       //清除SPI状态
    //写1清除
    return SPDAT;
}/************************************************
检测Flash是否准备就绪
入口参数: 无
出口参数:
    0 : 没有检测到正确的Flash
    1 : Flash准备就绪
************************************************/
BOOL FlashCheckID()
{
    BYTE dat;    SS = 0;
    SpiShift(SFC_RDID);                         //发送读取ID命令
    SpiShift(0x00);                             //发送三个地址字节
    SpiShift(0x00);
    SpiShift(0x00);
    dat = SpiShift(0x00);                      //读取制造商ID1(释放掉电/器件ID)    SS = 1;
    //SendUart(dat);
    if(dat == 0x14)                            //检测是否为25QX系列系列的Flash
        g_fFlashOK = 1;    return g_fFlashOK;
}/************************************************
检测Flash的忙状态
入口参数: 无
出口参数:
    0 : Flash处于空闲状态
    1 : Flash处于忙状态
************************************************/
BOOL IsFlashBusy()
{
    BYTE dat;    SS = 0;
    SpiShift(SFC_RDSR);                         //发送读取状态命令
    dat = SpiShift(0);                          //读取状态
    SS = 1;    return (dat & 0x01);                        //状态值的Bit0即为忙标志
}/************************************************
使能Flash写命令
入口参数: 无
出口参数: 无
************************************************/
void FlashWriteEnable()
{
    while (IsFlashBusy());                      //Flash忙检测
    SS = 0;
    SpiShift(SFC_WREN);                         //发送写使能命令
    SS = 1;
}/************************************************
擦除整片Flash
入口参数: 无
出口参数: 无
************************************************/
void FlashErase()
{
    if (g_fFlashOK)
    {        FlashWriteEnable();                     //使能Flash写命令
        SS = 0;
        SpiShift(SFC_CHIPER);                   //发送片擦除命令
        SS = 1;
    }
}/************************************************
从Flash中读取数据
入口参数:
    addr   : 地址参数
    size   : 数据块大小
    buffer : 缓冲从Flash中读取的数据
出口参数:
    无
************************************************/
void FlashRead(DWORD addr, DWORD size, BYTE *buffer)
{
    if (g_fFlashOK)
    {        while (IsFlashBusy());                  //Flash忙检测
        SS = 0;
        SpiShift(SFC_FASTREAD);                 //使用快速读取命令
        SpiShift(((BYTE *)&addr)[1]);           //设置起始地址
        SpiShift(((BYTE *)&addr)[2]);
        SpiShift(((BYTE *)&addr)[3]);
        SpiShift(0);                            //需要空读一个字节
        while (size)
        {            *buffer = SpiShift(0);              //自动连续读取并保存
            addr++;
            buffer++;
            size--;
        }
        SS = 1;
    }
}/************************************************
写数据到Flash中
入口参数:
    addr   : 地址参数
    size   : 数据块大小
    buffer : 缓冲需要写入Flash的数据
出口参数: 无
************************************************/
void FlashWrite(DWORD addr, DWORD size, BYTE *buffer)
{
    if (g_fFlashOK)
    while (size)
    {        FlashWriteEnable();                     //使能Flash写命令
        SS = 0;
        SpiShift(SFC_PAGEPROG);                 //发送页编程命令
        SpiShift(((BYTE *)&addr)[1]);           //设置起始地址
        SpiShift(((BYTE *)&addr)[2]);
        SpiShift(((BYTE *)&addr)[3]);
        while (size)
        {            SpiShift(*buffer);                  //连续页内写
            addr++;
            buffer++;
            size--;
            if ((addr & 0xff) == 0) break;
        }
        SS = 1;
    }
}

这个必须上一下实验的结果图了。。。。。

SPI Flash Rom W25Q16 ----基于STC15相关推荐

  1. 2M字节Flash Rom存储器W25Q16/W25X16 认识Flash

    原地址:http://www.51hei.com/bbs/dpj-41029-1.html 认识Flash Rom FlashRom 是快速擦写只读存储器,也就是我们常说的"闪存" ...

  2. Flash Rom存储器W25Q16/W25X16

    认识Flash Rom FlashRom 是快速擦写只读存储器,也就是我们常说的"闪存",单片机程序存储器就是Flash Rom,所谓"闪存",就是一种非易失性 ...

  3. 基于FPGA的SPI FLASH控制器设计

    1.SPI FLASH的基本特征 本文实现用FPGA来设计SPI FLASH,FLASH型号为W25Q128BV.支持3种通信方式,SPI.Dual SPI和Quad SPI.FLASH的存储单元无法 ...

  4. 基于spi FLASH的嵌入式文件系统 littlefs(转)

    基于spi FLASH的嵌入式文件系统 littlefs 转载于:https://www.cnblogs.com/LittleTiger/p/10737640.html

  5. 上海航芯技术分享 | 基于SPI Flash的U盘程序,从STM32F103到ACM32F403

    前言 本项目是以SPI Flash(如W25Q128等)存储元件作为存储单元,MCU主控完成USB接口通信并根据SCSI协议实现U盘功能.其结构如下图所示: SPI Flash部分移植 SPI功能部分 ...

  6. 基于SPI flash的 Multiboot远程更新

    基于SPI flash的 Multiboot远程更新 前言 Xilixn FPGA提供了一种在线升级的方式(本文使用的是A7和K7系列fpga,以下所讲述和涉及的都是与此2系列fpga相关)可以通过I ...

  7. Winbond W25Qxx SPI FLASH 使用示例(基于沁恒CH32V307单片机)

    文章目录 目的 基础说明 使用示例 总结 目的 Winbond(华邦)的 W25Qxx 系列 SPI FLASH 是比较常用的芯片,这篇文章将演示单片机中通过SPI使用该芯片的操作过程. 本文使用沁恒 ...

  8. FPGA配置 - 基于SPI FLASH的FPGA多重配置(Xilinx)

    原文地址:http://www.elecfans.com/emb/fpga/20140124334884_2.html IPROG指令的作用是对FPGA芯片进行复位操作,该复位操作对FPGA内部的应用 ...

  9. SPI Flash芯片W25Q32英文版数据手册解读(三)---------程序编写,电路应用

    一.序言 序言对这篇文章进行一个总体的说明: 1.这部分是根据手册写程序,因此采用手册截图+程序截图的形式,对图片不进行标号,而且对重点部分进行颜色标注. 2.考虑到很多读者(包括我),使用手机看文章 ...

最新文章

  1. C#从SQL server数据库中读取l图片和存入图片
  2. abap 调用外部web service 使用小结
  3. VTK:PolyData之GradientFilter
  4. python如何创建模块教程_Python创建模块及模块导入的方法
  5. 使用NW.js封装微信公众号菜单编辑器为桌面应用
  6. Java变量声明在循环体内还是循环体外,你用哪一个?
  7. [AIR] 获取U盘,打开U盘
  8. psc格式文件 的数据库导入问题
  9. 模拟鼠标键盘操作,含硬件模拟技术。[转]
  10. ABM410-ASEMI贴片整流桥ABM410
  11. 7-2 jmu-Java-03面向对象-06-继承覆盖综合练习-Person、Student、Employee、Company (15分)
  12. 关于技术博客--找工作--这些年--陆续的
  13. Kotlin 基础语法(《第一行代码(第三版)》第二章读书笔记)
  14. 真王服务器文件,3方位对比谁是2015级真王 唐斯vs波神各有优势
  15. 如何使用正规seo技术做网站排名?
  16. 近期企业财报 | 蔡司、埃森哲、尚德教育、卡塔尔国民银行发布业绩
  17. 【时事摘抄】上调电价的主意太馊了
  18. SPEED TREE技术分析
  19. 【sdx62】从板子导出fdt文件转化为dtsi文件方法
  20. Bluebird中promisify的用法

热门文章

  1. numpy 学习汇总33 - 索引切片( 初步学习 tcy)
  2. BlackICE(黑冰)防火墙蓝屏解决方法
  3. 男孩和女孩-爱情需要理解
  4. 备份utu信息丢失_关于数据丢失和家庭备份策略
  5. 做小红书营销,如何注意发布低腰产品的图文笔记?
  6. 机器学习--分类算法中单标签与多标签的问题
  7. constraints
  8. 支付宝启动新域名zhifubao.com 已通过备案
  9. 维瓦尔第_维瓦尔第:歌剧的精神继任者
  10. 使用canvas给图片添加水印, canvas转换base64,,canvas,图片,base64等转换成二进制文档流的方法,并将合成的图片上传到服务器,...