1.w25q64概述

1. 空间

  1. 首先w25q64有8mb的空间,属于nor flash 然后分128个块,每块64kb, 每一个块分为16个扇区,每个扇区4kb,每个扇区分128页,每页的空间为256b,如下图

2. 指令集

  1. busy ,每次对w25q64操作都必须当它busy为0才行

  1. 每次对w25q64进行写操作时,都要进行写使能,即发送0x06

  1. 这是下面常用的指令 0x03 ,0x02 ,0xD8

2. SPI通信协议

这里不再多说了,需要注意的是

SPI只有主模式和从模式之分,没有读和写的说法,外设的写操作和读操作是同步完成的。如果只进行写操作,主机只需忽略接收到的字节;反之,若主机要读取从机的一个字节,就必须发送一个空字节来引发从机的传输。也就是说,你发一个数据必然会收到一个数据;你要收一个数据必须也要先发一个数据。

spi通信的模式,一般选择模式0,即空闲态时,SCLK处于低电平,数据采样是在第1个边沿

3.代码

1. spi.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
#include "gd32f10x.h"
#include "spi.h"

void SPI0_Init(void)
{
rcu_periph_clock_enable(RCU_SPI0);
rcu_periph_clock_enable(RCU_GPIOA);

gpio_init(GPIOA,GPIO_MODE_AF_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_5|GPIO_PIN_7);
gpio_init(GPIOA,GPIO_MODE_IN_FLOATING,GPIO_OSPEED_50MHZ,GPIO_PIN_6);
spi_parameter_struct spi_config_t;
spi_i2s_deinit(SPI0);

spi_config_t.clock_polarity_phase = SPI_CK_PL_LOW_PH_1EDGE;
spi_config_t.device_mode = SPI_MASTER ;
spi_config_t.endian = SPI_ENDIAN_MSB ;
spi_config_t.frame_size = SPI_FRAMESIZE_8BIT;
spi_config_t.nss = SPI_NSS_SOFT;
spi_config_t.prescale =SPI_PSC_2 ;
spi_config_t.trans_mode = SPI_TRANSMODE_FULLDUPLEX;

spi_init(SPI0,&spi_config_t);
spi_enable(SPI0);
}

uint8_t SPI0_ReadWriteByte(uint8_t txd)
{
while(spi_i2s_flag_get(SPI0,SPI_FLAG_TBE) != 1);
spi_i2s_data_transmit(SPI0,txd);
while(spi_i2s_flag_get(SPI0,I2S_FLAG_RBNE) != 1);
return spi_i2s_data_receive(SPI0);
}

void SPI0_SendData(uint8_t *txd,uint16_t len)
{
uint16_t i;
for( i = 0; i < len; i++)
{
SPI0_ReadWriteByte(txd[i]);//不需要考虑接收值
}
}

void SPI0_ReceData(uint8_t *rxd,uint16_t len)
{
uint16_t i;
for( i = 0; i < len; i++)
{
rxd[i] = SPI0_ReadWriteByte(0xff);//发什么并不重要,重要的是怎么接收数据
}
}

2. w25q64

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
#include "gd32f10x.h"
#include "spi.h"
#include "w25q64.h"

void W25Q64_Init(void)
{
rcu_periph_clock_enable(RCU_GPIOA);
gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_50MHZ,GPIO_PIN_4);
W25Q64_CS_DISENABLE;
SPI0_Init();
}

void W25Q64_WaitBusy(void)
{
uint8_t res;
do{
W25Q64_CS_ENABLE;
SPI0_ReadWriteByte(0x05);
res = SPI0_ReadWriteByte(0xff);
W25Q64_CS_DISENABLE;
}while((res & 0x01) == 0x01);
}

void W25Q64_Enable(void)
{
W25Q64_WaitBusy();
W25Q64_CS_ENABLE;
SPI0_ReadWriteByte(0x06);
W25Q64_CS_DISENABLE;
}

void W25Q64_Erase64K(uint8_t blockNB)
{
uint8_t wdata[4];
wdata[0] = 0xD8;
wdata[1] = (blockNB*64*1024) >> 16;
wdata[2] = (blockNB*64*1024) >> 8;
wdata[3] = (blockNB*64*1024) >> 0;
W25Q64_WaitBusy();
W25Q64_Enable();
W25Q64_CS_ENABLE;
SPI0_SendData(wdata,4);
W25Q64_CS_DISENABLE;
W25Q64_WaitBusy();
}

void W25Q64_Write_Page(uint8_t *wbuff, uint16_t pageNB)
{
uint8_t wdata[4];
wdata[0] = 0x02;
wdata[1] = (pageNB*256) >> 16;
wdata[2] = (pageNB*256) >> 8;
wdata[3] = (pageNB*256) >> 0;
W25Q64_WaitBusy();
W25Q64_Enable();
W25Q64_CS_ENABLE;
SPI0_SendData(wdata,4);
SPI0_SendData(wbuff,256);
W25Q64_CS_DISENABLE;
}

void W25Q64_Read(uint32_t addr,uint8_t *rbuff, uint32_t len)
{
uint8_t wdata[4];
wdata[0] = 0x03;
wdata[1] = (addr) >> 16;
wdata[2] = (addr) >> 8;
wdata[3] = (addr) >> 0;
W25Q64_WaitBusy();
W25Q64_CS_ENABLE;
SPI0_SendData(wdata,4);
SPI0_ReceData(rbuff,len);
W25Q64_CS_DISENABLE;
}
  1. 主程序验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main(void){

uint16_t i,j;
Delay_Init();
Usart0_Init(921600);
W25Q64_Init();
W25Q64_Erase64K(0);

for(i=0;i<256;i++){
for(j=0;j<256;j++)
wdata[j] = i;
W25Q64_Write_Page(wdata,i);
}
Delay_ms(50);

for(i=0;i<256;i++){
W25Q64_Read(i*256,rdata,256);
for(j=0;j<256;j++)
u0_printf("%d=%x\r\n",i*256+j,rdata[j]);
}
while(1){
}
}