0%

11-2W25Q64简介&软件SPI读写W25Q64

W25Q64简介

•W25Qxx系列是一种低成本、小型化、使用简单的非易失性存储器(数据掉电不丢失),常应用于数据存储、字库存储、固件程序存储等场景
•存储介质:Nor Flash(闪存)
•时钟频率:80MHz / 160MHz (Dual SPI双重SPI) / 320MHz (Quad SPI四重SPI)
•存储容量(24位地址):
  W25Q40:    4Mbit / 512KByte
  W25Q80:    8Mbit / 1MByte
  W25Q16:    16Mbit / 2MByte
  W25Q32:    32Mbit / 4MByte
  W25Q64:    64Mbit / 8MByte
  W25Q128:  128Mbit / 16MByte
  W25Q256:  256Mbit / 32MByte

硬件电路

硬件电路

硬件电路

引脚功能
VCC、GND电源(2.7~3.6V)
CS(SS)SPI片选
CLK(SCK)SPI时钟
DI(MOSI)SPI主机输出从机输入
DO(MISO)SPI主机输入从机输出
WP写保护
HOLD数据保持

W25Q64框图

W25Q64框图
右上角为存储器的规划示意图,以64KB为基本单元,分为若干个块。

Flash操作注意事项

写入操作时:

•写入操作前,必须先进行写使能(使用SPI发送写使能的指令)
•每个数据位只能由1改写为0,不能由0改写为1
•写入数据前必须先擦除,擦除后,所有数据位变为1
•擦除必须按最小擦除单元进行
•连续写入多字节时,最多写入一页的数据,超过页尾位置的数据,会回到页首覆盖写入
•写入操作结束后,芯片进入忙状态,不响应新的读写操作

读取操作时:

•直接调用读取时序,无需使能,无需额外操作,没有页的限制,读取操作结束后不会进入忙状态,但不能在忙状态时读取

软件SPI读写W25Q64

MySPI.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "stm32f10x.h"                  // Device header

void MySPI_W_CS(uint8_t BitValue)//写CS引脚
{
GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}

void MySPI_W_SCK(uint8_t BitValue)//写SCK引脚
{
GPIO_WriteBit(GPIOA,GPIO_Pin_5,(BitAction)BitValue);
}

void MySPI_W_MOSI(uint8_t BitValue)//写MOSI引脚
{
GPIO_WriteBit(GPIOA,GPIO_Pin_7,(BitAction)BitValue);
}

uint8_t MySPI_R_MISO(void)//读MISO引脚
{
return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
}

void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//初始化DI、CLK、CS为推挽输出
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_7);
//初始化DO为上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
MySPI_W_CS(1);
MySPI_W_SCK(0);
}

void MySPI_Start(void)//起始信号,只需要把CS置低电平即可
{
MySPI_W_CS(0);
}

void MySPI_Stop(void)//终止信号,只需要把CS置高电平即可
{
MySPI_W_CS(1);
}
uint8_t MySPI_SwapByte(uint8_t ByteSend)//交换字节
{
// uint8_t i,ByteReceive = 0x00;//接收到的字节
// for(i = 0; i < 8 ;i ++)
// {
// MySPI_W_MOSI(ByteSend & (0x80 >> i));
// MySPI_W_SCK(1);
// if (MySPI_R_MISO() == 1 )
// {
// ByteReceive |= (0x80 >> i);
// }
// MySPI_W_SCK(0);
// }
uint8_t i;
for(i = 0; i < 8 ;i ++)
{
MySPI_W_MOSI(ByteSend & 0x80);
ByteSend <<= 1;
MySPI_W_SCK(1);
if (MySPI_R_MISO() == 1 )
{
ByteSend |= 0x01;
}
MySPI_W_SCK(0);
}

return ByteSend;
}

W25Q64.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "stm32f10x.h"                  // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"

void W25Q64_Init(void)//初始化W25Q64
{
MySPI_Init();
}

void W25Q64_ReadID(uint8_t *MID,uint16_t *DID)//读取ID
{
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_JEDEC_ID);//发送0x9F,交换到厂商号
*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收厂商号,发送的0xFF是为了交换到返回的厂商号
*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收返回值的高8位
*DID <<= 8;//左移8位
*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE);//接收返回值的低8位
MySPI_Stop();
}

void W25Q64_WriteEnable(void)//写使能
{
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_WRITE_ENABLE);//发送指令码,写使能
MySPI_Stop();//发送停止信号结束
}

void W25Q64_WaitBusy(void)
{
uint32_t Timeout;
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);//发送指令码,读状态寄存器1
Timeout = 100000;
while((MySPI_SwapByte(W25Q64_DUMMY_BYTE) & 0x01) == 0x01)
{
Timeout -- ;
if(Timeout == 0)
{
break;
}
}
MySPI_Stop();
}

void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{
W25Q64_WriteEnable();
uint16_t i;
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_PAGE_PROGRAM);//发送指令码
MySPI_SwapByte(Address >> 16);//发送地址最高位
MySPI_SwapByte(Address >> 8);//发送地址中间位
MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了

for(i = 0; i < Count; i ++)
{
MySPI_SwapByte(DataArray[i]);
}

MySPI_Stop();
W25Q64_WaitBusy();
}

void W25Q64_SectorErase(uint32_t Address)
{
W25Q64_WriteEnable();
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);//发送指令码,扇区擦除
MySPI_SwapByte(Address >> 16);//发送地址最高位
MySPI_SwapByte(Address >> 8);//发送地址中间位
MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了
MySPI_Stop();
W25Q64_WaitBusy();//事后等待
}

void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{
uint32_t i;
MySPI_Start();//先发送起始信号
MySPI_SwapByte(W25Q64_READ_DATA);//发送指令码,读数据
MySPI_SwapByte(Address >> 16);//发送地址最高位
MySPI_SwapByte(Address >> 8);//发送地址中间位
MySPI_SwapByte(Address);//发送地址最低位,高位被舍弃了

for(i = 0; i < Count; i ++)
{
DataArray[i] = MySPI_SwapByte(W25Q64_DUMMY_BYTE);//发送0xFF,换回有用的数据
}
MySPI_Stop();
}

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "W25Q64.h"

uint8_t MID;
uint16_t DID;

uint8_t ArrayWrite[] = {0x55,0x66,0x77,0x88};
uint8_t ArrayRead[4];

int main(void)
{
OLED_Init();
W25Q64_Init();

OLED_ShowString(1,1,"MID: DID:");
OLED_ShowString(2,1,"W:");
OLED_ShowString(3,1,"R:");
W25Q64_ReadID(&MID,&DID);
OLED_ShowHexNum(1,5,MID,2);
OLED_ShowHexNum(1,12,DID,4);

// W25Q64_SectorErase(0x000000);
W25Q64_PageProgram(0x000000,ArrayWrite,4);
W25Q64_ReadData(0x000000,ArrayRead,4);

OLED_ShowHexNum(2,3,ArrayWrite[0],2);
OLED_ShowHexNum(2,6,ArrayWrite[1],2);
OLED_ShowHexNum(2,9,ArrayWrite[2],2);
OLED_ShowHexNum(2,12,ArrayWrite[3],2);

OLED_ShowHexNum(3,3,ArrayRead[0],2);
OLED_ShowHexNum(3,6,ArrayRead[1],2);
OLED_ShowHexNum(3,9,ArrayRead[2],2);
OLED_ShowHexNum(3,12,ArrayRead[3],2);
}