SPI Flash Rom W25Q16 ----基于STC15

百度文库一个很详细的介绍、、、、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 device
    SPI_Send_Byte(W25X_ReadStatusReg);        //    send Read Status Register command
    byte = SPI_Get_Byte();                    //    receive byte
    W25X_CS = 1;                            //    disable device    
    return byte;
}
void    SPI_Write_StatusReg(byte)         //写状态寄存器
{    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_WriteStatusReg);        //    select write to status register
    SPI_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 device
    SPI_Send_Byte(W25X_WriteEnable);        //    send W25X_Write_Enable command
    W25X_CS = 1;                            //    disable device
}
void    SPI_Write_Disable()                   //写禁能
{    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_WriteDisable);        //    send W25X_WriteW25X_DIsable command
    W25X_CS = 1;                            //    disable device
}
uchar    SPI_Read_ID1()                        //释放掉电/器件ID
{    uchar byte;
    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_DeviceID);            //    send read device ID command (ABh)
    SPI_Send_Byte(0);                        //    send address
    SPI_Send_Byte(0);                        //    send address
    SPI_Send_Byte(0);                        //    send 3_Dummy address
    byte = SPI_Get_Byte();                    //    receive Device ID byte    
    W25X_CS  = 1;                            //    disable device
    delay(4);                                //    remain CS high for tRES2 = 1.8uS
    return byte;
}
uint    SPI_Read_ID2(uchar ID_Addr)             //制造/器件ID
{    uint IData16;
    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_ManufactDeviceID);    //    send read ID command (90h)
    SPI_Send_Byte(0x00);                    //    send address
    SPI_Send_Byte(0x00);                    //    send address
    SPI_Send_Byte(ID_Addr);                    //    send W25Pxx selectable ID address 00H or 01H
    IData16 = SPI_Get_Byte()<<8;            //    receive Manufature or Device ID byte
    IData16 |= SPI_Get_Byte();                //    receive Device or Manufacture ID byte
    W25X_CS = 1;                            //    disable device    
    return IData16;
}
uint    SPI_Read_ID3()                           //读JEDEC ID
{    uint IData16;
    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_JedecDeviceID);        //    send read ID command (9Fh)
    IData16 = SPI_Get_Byte()<<8;            //    receive Manufature or Device ID byte
    IData16 |= SPI_Get_Byte();                //    receive Device or Manufacture ID byte
    tx_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 device
    SPI_Send_Byte(W25X_ReadData);                        //    read command
    SPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));//    send 3 address bytes
    SPI_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 device
    SPI_Send_Byte(W25X_ReadData);                        //    read 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++)                    //    read until no_bytes is reached
        upper_128[i] = SPI_Get_Byte();                    //    receive byte and store at address 80H - FFH
    W25X_CS = 1;                                        //    disable device
}
uchar    SPI_FastRead_Byte(uint32 Dst_Addr)                  //快读 某地址 数据
{    uchar byte = 0;
    W25X_CS = 0;                                        //    enable device
    SPI_Send_Byte(W25X_FastReadData);                    //    fast read 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);
    SPI_Send_Byte(0xFF);                                //    dummy byte
    byte = 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 device
    SPI_Send_Byte(W25X_FastReadData);                    //    read 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);
    SPI_Send_Byte(0xFF);                                //    dummy byte
    for (i = 0; i < nBytes_128; i++)                    //    read until no_bytes is reached
        upper_128[i] = SPI_Get_Byte();                    //    receive byte and store at address 80H - FFH
    W25X_CS = 1;                                        //    disable device
}
void    SPI_Write_Byte(uint32 Dst_Addr, uchar byte)          //但字节写入
{    W25X_CS = 0;                                    //    enable device
    SPI_Write_Enable();                                //    set WEL
    SPI_Wait_Busy();    
    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);
    SPI_Send_Byte(byte);                            //    send byte to be programmed
    W25X_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 device
    SPI_Write_Enable();                                    //    set WEL
    W25X_CS = 0;
    SPI_Wait_Busy();
    W25X_CS = 0;
    SPI_Send_Byte(W25X_ChipErase);                        //    send Chip Erase command
    W25X_CS = 1;                                        //    disable device
}
void    SPI_Erase_Sector(uint32 Dst_Addr)                //扇区擦除
{    W25X_CS = 0;                                        //    enable device
    SPI_Write_Enable();                                    //    set WEL
    W25X_CS = 0;
    SPI_Send_Byte(W25X_SectorErase);                    //    send Sector Erase command
    SPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));//    send 3 address bytes
    SPI_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 device
    SPI_Send_Byte(W25X_PowerDown);            //    send W25X_PowerDown command 0xB9
    W25X_CS = 1;                            //    disable device
    delay(6);                                //    remain CS high for tPD = 3uS
}
void    SPI_ReleasePowerDown()
{    W25X_CS = 0;                            //    enable device
    SPI_Send_Byte(W25X_ReleasePowerDown);    //    send W25X_PowerDown command 0xAB
    W25X_CS = 1;                            //    disable device
    delay(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 high
            W25X_DI = 1;
        else
            W25X_DI = 0;                    //    if not, set to low
        W25X_CLK = 1;                        //    toggle clock high
        out = (out << 1);                    //    shift 1 place for next bit
        nop();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 0
        temp = W25X_DO;                        //    save input
        W25X_CLK = 1;                        //    toggle clock high
        if (temp == 1)                        //    check to see if bit is high
            in |= 0x01;                        //    if high, make bit high
        W25X_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 long


void    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 device
    SPI_Write_Enable();                                        //    set WEL
    W25X_CS = 0;
    Dst_Addr = (uint32)addr*256;
    Dst_Addr = 0x0ff000;//(uint32)addr*256;
    SPI_Send_Byte(W25X_PageProgram);                        //    send Byte Program command
    SPI_Send_Byte((uchar)((Dst_Addr & 0xFFFFFF) >> 16));    //    send 3 address bytes
    SPI_Send_Byte((uchar)((Dst_Addr & 0xFFFF) >> 8));
    SPI_Send_Byte((uchar)(Dst_Addr & 0xFF));

    for (i = 0; i < 256; i++)                                //    send byte to be programmed
        SPI_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 ID



uchar    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            1

sfr 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          0x08

sfr 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      0xC7

void 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, 特别适用于主板BIOS的DIY,无需购买昂贵的专业的编程器.本人刚刚用它恢复了我的华硕本本的BIOS,感觉比较方便,特此分享.只需稍微懂一些电路知识,不要把引脚弄错,任何人都可以DIY,再也不用担心主板不启动.该编程器只需连接四只200-400欧姆的电阻(图中为150ohm,但是不是很稳定,我是用四只270ohm的电阻成功的),一个并口连接公头,电源直接用普通干电池,3.3V的Flash系列用两节1.5V的干电池串联即可省去原理图中的1000µF电容,5V系列3节电池即可,连接前测一下,保证电压没有超过datasheet中的允许值.连接时最好能够使用转DIP的卡座,这样会省很多时间,而且也容易接错引脚.终端程序使用打印机接口模拟SPI,可以识别并支持下列SPI芯片: Atmel: AT26DF041 (512kB) AT26DF081 (1MB) AT26DF081A (1MB) AT26DF161 (2MB) AT26DF161A (2MB) AT26DF321 (4MB) Intel: QB25F016S33B8 (2MB) QB25F032S33B8 (4MB) QB25F064S33B8 (8MB) Macronix: MX25L1005 (128kB) MX25L2005 (256kB) MX25L4005 (512kB) MX25L8005 (1MB) MX25L1605 (2MB) MX25L3205 (4MB) MX25L6405 (8MB) Spansion: S25FL004A (512kB) S25FL008A (1MB) S25FL016A (2MB) S25FL032A (4MB) S25FL064A (8MB) S25FL128P (16MB) SST: SST25VF010 (128kB) SST25VF020 (256kB) SST25VF040 (512kB) SST25VF040B (512kB) SST25VF080A (1MB) SST25VF080B (1MB) SST25VF016 (2MB) SST25VF032 (4MB) SST25VF064 (8MB) SST25VF128 (16MB) SST26VF016 (2MB) SST26VF032 (4MB) SST26VF064 (8MB) ST Microelectronic: M25P10 (128kB) M25P20 (256kB) M25P40 (512kB) M25P80 (1MB) M25P16 (2MB) M25P32 (4MB) M25P64 (8MB) M25P128 (16MB) Winbond: W25X10 (128kB) W25X20 (256kB) W25X40 (512kB) W25X80 (1MB) W25X16 (2MB) W25X32 (4MB) W25X64 (8MB) 但是终端程序运行于DOS模式或者W2K, XP, Vista下的模拟DOS环境. 几个重要的命令行参数说明(方便不懂e文的)> 1. *** /i (如果芯片连接正确就会显示芯片的型号等信息,验证正确与否) 2. *** /d amibios.bin (自动备份SPI芯片中的内容到当前文件夹中的amibios.bin文件中,以备不时之需) 3. *** /e (清空芯片内容) 4. *** /p ami.bios (把名为ami.bios的文件写入SPI芯片中) 顺便罗嗦一句,操作时别忘了防静电,小心永久损坏芯片.
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReCclay

如果觉得不错,不妨请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值