百度文库一个很详细的介绍、、、、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;
}
}
这个必须上一下实验的结果图了。。。。。