STM32F1与STM32CubeIDE编程实例-W25Q-SPI-Flash驱动

W25Q-SPI-Flash驱动

Winbond 的 W25X 和 W25Q SpiFlash® 多 I/O 存储器具有流行的串行外设接口 (SPI)、从 512K 位到 512M 位的密度、小型可擦除扇区和业界最高的性能。

W25X 系列支持双 SPI,有效地将标准 SPI 时钟速率提高一倍。 W25Q 系列是 25X 系列的“超集”,具有双 I/O 和四 I/O SPI,性能更高。使用 Quad-SPI 时,高达 104MHz 的时钟速率相当于 416MHz(50M-Byte/S 传输速率)。这是普通串行闪存(50MHz)性能的四倍多,甚至超过了异步并行闪存,同时使用更少的引脚和更少的空间。

本文将介绍如何简单驱动W25Q SPI内存。

1、W25Q配置

开发环境搭建、系统时钟配置、调试配置及串口配置,请参考:

本次配置如下:

在这里插入图片描述

2、驱动实现

1)驱动基本定义

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/\*
\* w25qxx.h
\*
\* Created on: Jul 12, 2022
\* Author: jenson
\*/

#ifndef \_\_W25QXX\_H\_\_
#define \_\_W25QXX\_H\_\_

#include <stm32f1xx\_hal.h>
#include <stdbool.h>

// W25Q控制命令
#define W25X\_WriteEnable 0x06
#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
#define W25X\_ManufactDeviceID 0x90
#define W25X\_JedecDeviceID 0x9F
#define W25X\_Unique\_ID 0x4B
#define W25QXX\_Read\_SFDP\_Register 0x5A /\*\*< 读取 SFDP 寄存器 \*/
#define W25QXX\_DUMMY\_BYTE 0xA5
#define W25QXX\_Enable\_Reset 0x66
#define W25QXX\_Reset 0x99
#define W25QXX\_Sector\_Erase\_4KB 0x20
#define W25QXX\_Block\_Erase\_32KB 0x52
#define W25QXX\_Block\_Erase\_64KB 0xD8

typedef enum {
W25Q_Unknow = 0,
W25Q10 = 0xEF11,
W25Q20 = 0xEF12,
W25Q40 = 0xEF13,
W25Q80 = 0xEF14,
W25Q16 = 0xEF15,
W25Q32 = 0xEF16,
W25Q64 = 0xEF17,
W25Q128 = 0xEF18,
W25Q256 = 0xEF19,
W25Q512 = 0XEF20,
} w25qxx\_type\_t;

typedef struct {
uint16\_t id;
uint32\_t device_id; // 设备ID
uint16\_t device_type; // 设备类型
uint8\_t unique_id[8]; // 设备序列号
uint16\_t page_size; // 页面大小
uint32\_t page_count; // 页面数量
uint32\_t sector_size; // 扇区大小
uint32\_t sector_count; // 扇区数量
uint32\_t block_size; // 块大小
uint32\_t block_count; // 块数量
uint32\_t capacity_in_kb; // 容量
SPI_HandleTypeDef \*SPI_Handle; // SPI句柄
GPIO_TypeDef \*SPI_CS_Port; // SPI CS端口
uint16\_t SPI_CS_Pin; // SPI CS引脚
} w25qxx\_t;

/\*\*
\*初始化
\*/
bool w25qxx\_init(w25qxx\_t \*w25qxx);
/\*\*
\* 指定地址读取指定长度数据
\*/
void w25qxx\_read(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer, uint32\_t ReadAddr,
uint32\_t NumByteToRead);
/\*\*
\* 指定地址读取一字节数据
\*/
uint8\_t w25qxx\_read\_byte(w25qxx\_t \*w25qxx, uint32\_t ReadAddr);
/\*\*
\* 读取块
\*/
void w25qxx\_read\_block(w25qxx\_t \*w25qxx, uint32\_t addr, uint8\_t \*data_ptr,
uint32\_t read_len);

/\*\*
\* 读扇区
\*/
void w25qxx\_read\_sector(w25qxx\_t \*w25qxx, uint32\_t sec_addr, uint8\_t \*ptr_buff,
uint32\_t sec_cnt);

/\*\*
\*定地址写入指定长度数据
\*/
void w25qxx\_write(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer, uint32\_t WriteAddr,
uint32\_t NumByteToWrite);

/\*\*
\* 写页(指定长度)
\*/
void w25qxx\_write\_page(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer, uint32\_t WriteAddr,
uint32\_t NumByteToWrite);
/\*\*
\* 写页(长度为一个页大小)
\*/
void w25qxx\_page\_program(w25qxx\_t \*w25qxx, uint32\_t page_addr,
const uint8\_t \*ptr_buff) ;

void w25qxx\_write\_sector(w25qxx\_t \*w25qxx, const uint8\_t \*buff,
uint32\_t sector, uint32\_t count);
/\*\*
\* 擦除芯片
\*/
void w25qxx\_erase\_chip(w25qxx\_t \*w25qxx);
/\*\*
\* 擦除扇区
\*/
void w25qxx\_sector\_erase(w25qxx\_t \*w25qxx,uint32\_t sector_addr);
void w25qxx\_sector\_erase\_4kb(w25qxx\_t \*w25qxx,uint32\_t sector_addr);
/\*\*
\* 擦除块
\*/
void w25qxx\_block\_erase\_32kb(w25qxx\_t \*w25qxx,uint32\_t block_addr);
void w25qxx\_block\_erase\_64kb(w25qxx\_t \*w25qxx,uint32\_t block_addr);

/\*\*
\* 显示信息
\*/
void w25qxx\_display\_info(w25qxx\_t \*w25qxx);
/\*\*
\* 使能重置
\*/
void w25qxx\_enable\_reset(w25qxx\_t \*w25qxx);
/\*\*
\* 重置
\*/
void w25qxx\_reset(w25qxx\_t \*w25qxx);

#endif /\* W25QXX\_W25QXX\_H\_ \*/


2)驱动辅助函数

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// w25qxx.c

#include "w25qxx.h"

// 每个扇区有16页
#define PAGES\_PER\_SECTOR 16

static uint8\_t \_\_w25qxx\_spi\_readwrite\_byte(w25qxx\_t \*w25qxx, uint8\_t TxData) {
uint8\_t RxData = 0x00;

while (HAL\_SPI\_TransmitReceive(w25qxx->SPI_Handle, &TxData, &RxData, 1, 100)
!= HAL_OK) {
printf("\_\_w25qxx\_spi\_readwrite\_byte:read failed\r\n");
RxData = 0xFF;
}

return RxData;
}

static void \_\_w25qxx\_cs\_low(w25qxx\_t \*w25qxx) {
HAL\_GPIO\_WritePin(w25qxx->SPI_CS_Port, w25qxx->SPI_CS_Pin, GPIO_PIN_RESET);
}

static void \_\_w25qxx\_cs\_high(w25qxx\_t \*w25qxx) {
HAL\_GPIO\_WritePin(w25qxx->SPI_CS_Port, w25qxx->SPI_CS_Pin, GPIO_PIN_SET);
}

static void \_\_w25qxx\_read\_device\_type(w25qxx\_t \*w25qxx) {
uint16\_t temp = 0, temp1 = 0, temp2 = 0;
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ManufactDeviceID);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
temp1 = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
temp2 = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
\_\_w25qxx\_cs\_high(w25qxx);
temp = temp2 << 8 | temp1;
w25qxx->device_type = temp;
}

static void \_\_w25qxx\_read\_device\_id(w25qxx\_t \*w25qxx) {
uint32\_t temp = 0, temp0 = 0, temp1 = 0, temp2 = 0;
\_\_w25qxx\_cs\_low(w25qxx);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_JedecDeviceID);

temp0 = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
temp1 = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
temp2 = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);

\_\_w25qxx\_cs\_high(w25qxx);

temp = (temp0 << 16) | (temp1 << 8) | temp2;

w25qxx->device_id = temp;
}

static void \_\_w25qxx\_read\_uniq\_id(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_cs\_low(w25qxx);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_Unique_ID);

for (uint8\_t i = 0; i < 4; i++) {
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
}
for (uint8\_t i = 0; i < 8; i++) {
w25qxx->unique_id[i] = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx,
W25QXX_DUMMY_BYTE);
}

\_\_w25qxx\_cs\_high(w25qxx);
}

static uint8\_t \_\_w25qxx\_read\_status\_register(w25qxx\_t \*w25qxx) {
uint8\_t byte = 0;
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ReadStatusReg);
byte = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
\_\_w25qxx\_cs\_high(w25qxx);
return byte;
}

static void \_\_w25qxx\_write\_status\_register(w25qxx\_t \*w25qxx, uint8\_t sr) {
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_WriteStatusReg);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, sr);
\_\_w25qxx\_cs\_high(w25qxx);
}

void \_\_w25qxx\_write\_enable(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_WriteEnable);
\_\_w25qxx\_cs\_high(w25qxx);
}

void \_\_w25qxx\_write\_disable(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_WriteDisable);
\_\_w25qxx\_cs\_high(w25qxx);
}

void \_\_w25qxx\_wait\_busy(w25qxx\_t \*w25qxx) {
while ((\_\_w25qxx\_read\_status\_register(w25qxx) & 0x01) == 0x01)
;
}

3)驱动定义实现-W25Q初始化

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
92
bool w25qxx\_init(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, 0XFF);
\_\_w25qxx\_cs\_high(w25qxx);
printf("w25qxx\_read:start to read id\r\n");
\_\_w25qxx\_read\_device\_type(w25qxx);
\_\_w25qxx\_read\_device\_id(w25qxx);
\_\_w25qxx\_read\_uniq\_id(w25qxx);

printf("device type = 0x%x\r\n", w25qxx->device_type);
printf("device id = 0x%x\r\n", w25qxx->device_id);
switch (w25qxx->device_type) {
case W25Q512: //w25q512
w25qxx->block_count = 1024;
break;
case W25Q256: //w25q256
w25qxx->block_count = 512;
break;
case W25Q128: //w25q128
w25qxx->block_count = 256;
break;
case W25Q64: //w25q64
w25qxx->block_count = 128;
break;
case W25Q32: //w25q32
w25qxx->block_count = 64;
break;
case W25Q16: //w25q16
w25qxx->block_count = 32;
break;
case W25Q80: //w25q80
w25qxx->block_count = 16;
break;
case W25Q40: //w25q40
w25qxx->block_count = 8;
case W25Q20: //w25q20
w25qxx->block_count = 4;
break;
case W25Q10: //w25q10
w25qxx->block_count = 2;
break;
default:
printf("unknow w25qxx device\r\n");
w25qxx->device_id = 0;
w25qxx->device_type = W25Q_Unknow;
w25qxx->block_count = 0;
return false;
}

// 容量相关计算
w25qxx->page_size = 256;
w25qxx->sector_size = 0x1000;
w25qxx->sector_count = w25qxx->block_count \* 16;
w25qxx->page_count = (w25qxx->sector_count \* w25qxx->sector_size)
/ w25qxx->page_size;
w25qxx->block_size = w25qxx->sector_size \* 16;
w25qxx->capacity_in_kb = (w25qxx->sector_count \* w25qxx->sector_size)
/ 1024;

return true;
}

void w25qxx\_display\_info(w25qxx\_t \*w25qxx) {
printf("w25qxx type:0x%x\r\n", w25qxx->device_type);
printf("w25qxx device id: 0x%x\r\n", w25qxx->device_id);
printf("w25qxx unique id: %d%d-%d%d-%d%d-%d%d\r\n", w25qxx->unique_id[0],
w25qxx->unique_id[1], w25qxx->unique_id[2], w25qxx->unique_id[3],
w25qxx->unique_id[4], w25qxx->unique_id[5], w25qxx->unique_id[6],
w25qxx->unique_id[7]);
printf("w25qxx capacity(kb) = 0x%x\r\n", w25qxx->capacity_in_kb);
printf("w25qxx page size = %d\r\n", w25qxx->page_size);
printf("w25qxx page count = %d\r\n", w25qxx->page_count);
printf("w25qxx sector size = %d\r\n", w25qxx->sector_size);
printf("w25qxx sector count = %d\r\n", w25qxx->sector_count);
printf("w25qxx block size = %d\r\n", w25qxx->block_size);
printf("w25qxx block count = %d\r\n", w25qxx->block_count);
}

void w25qxx\_enable\_reset(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_Enable_Reset);
\_\_w25qxx\_cs\_high(w25qxx);
}

void w25qxx\_reset(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_Reset);
\_\_w25qxx\_cs\_high(w25qxx);
}

4)驱动定义实现-读数据

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
void w25qxx\_read(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer, uint32\_t ReadAddr,
uint32\_t NumByteToRead) {
uint16\_t i;
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ReadData);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, ((ReadAddr) >> 16));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, ((ReadAddr) >> 8));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) ReadAddr);
for (i = 0; i < NumByteToRead; i++) {
pBuffer[i] = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_DUMMY_BYTE);
}
\_\_w25qxx\_cs\_high(w25qxx);
}

uint8\_t w25qxx\_read\_byte(w25qxx\_t \*w25qxx, uint32\_t ReadAddr) {
uint8\_t ret = 0;

\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_cs\_low(w25qxx);

uint8\_t add_hi = (uint8\_t) ((ReadAddr & 0x00FF0000) >> 16);
uint8\_t add_mid = (uint8\_t) ((ReadAddr & 0x0000FF00) >> 8);
uint8\_t add_lo = (uint8\_t) (ReadAddr);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ReadData);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_hi);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_mid);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_lo);

ret = \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, 0x00);

\_\_w25qxx\_cs\_high(w25qxx);

return ret;
}

void w25qxx\_read\_sector(w25qxx\_t \*w25qxx, uint32\_t sec_addr, uint8\_t \*ptr_buff,
uint32\_t sec_cnt) {
uint32\_t byte_addr_start, byte_cnt;

byte_addr_start = sec_addr \* PAGES_PER_SECTOR \* w25qxx->page_size;
byte_cnt = sec_cnt \* PAGES_PER_SECTOR \* w25qxx->page_size;

w25qxx\_read\_block(w25qxx, byte_addr_start, ptr_buff, byte_cnt);
}

void w25qxx\_read\_block(w25qxx\_t \*w25qxx, uint32\_t addr, uint8\_t \*data_ptr,
uint32\_t read_len) {

\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_cs\_low(w25qxx);

uint8\_t add_hi = (uint8\_t) ((addr & 0x00FF0000) >> 16);
uint8\_t add_mid = (uint8\_t) ((addr & 0x0000FF00) >> 8);
uint8\_t add_lo = (uint8\_t) (addr);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ReadData);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_hi);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_mid);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_lo);

for (uint32\_t i = 0; i < read_len; i++) {
data_ptr[i] = (uint8\_t) \_\_w25qxx\_spi\_readwrite\_byte(w25qxx, 0x00);
}

\_\_w25qxx\_cs\_high(w25qxx);
}

5)驱动定义实现-写数据

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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
void w25qxx\_write(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer, uint32\_t WriteAddr,
uint32\_t NumByteToWrite) {

\_\_w25qxx\_wait\_busy(w25qxx);

uint8\_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0;

Addr = WriteAddr % w25qxx->page_size;

count = w25qxx->page_size - Addr;

NumOfPage = NumByteToWrite / w25qxx->page_size;

NumOfSingle = NumByteToWrite % w25qxx->page_size;

if (Addr == 0) {
if (NumOfPage == 0) {
w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, NumByteToWrite);
} else {

while (NumOfPage--) {
w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr,
w25qxx->page_size);
WriteAddr += w25qxx->page_size;
pBuffer += w25qxx->page_size;
}

w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, NumOfSingle);
}
}

else {
if (NumOfPage == 0) {

if (NumOfSingle > count) {
temp = NumOfSingle - count;

w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, temp);
} else {
w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, NumByteToWrite);
}
} else {
NumByteToWrite -= count;
NumOfPage = NumByteToWrite / w25qxx->page_size;
NumOfSingle = NumByteToWrite % w25qxx->page_size;

w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

while (NumOfPage--) {
w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr,
w25qxx->page_size);
WriteAddr += w25qxx->page_size;
pBuffer += w25qxx->page_size;
}
if (NumOfSingle != 0) {
w25qxx\_write\_page(w25qxx, pBuffer, WriteAddr, NumOfSingle);
}
}
}

\_\_w25qxx\_wait\_busy(w25qxx);
}

void w25qxx\_write\_page(w25qxx\_t \*w25qxx, uint8\_t \*p_puffer, uint32\_t write_addr,
uint32\_t bytes_to_write) {
uint16\_t i;
\_\_w25qxx\_write\_enable(w25qxx);
\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_PageProgram);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) ((write_addr) >> 16));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) ((write_addr) >> 8));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) write_addr);
for (i = 0; i < bytes_to_write; i++)
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, p_puffer[i]);
\_\_w25qxx\_cs\_high(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);
}

void w25qxx\_page\_program(w25qxx\_t \*w25qxx, uint32\_t page_addr,
const uint8\_t \*ptr_buff) {
\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_write\_enable(w25qxx);

\_\_w25qxx\_cs\_low(w25qxx);

uint32\_t byte_addr = page_addr \* w25qxx->page_size;

uint8\_t add_hi = (uint8\_t) ((byte_addr & 0x00FF0000) >> 16);
uint8\_t add_mid = (uint8\_t) ((byte_addr & 0x0000FF00) >> 8);
uint8\_t add_lo = (uint8\_t) (byte_addr);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_PageProgram);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_hi);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_mid);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_lo);

for (uint32\_t i = 0; i < w25qxx->page_size; i++) {
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, ptr_buff[i]);
}

\_\_w25qxx\_cs\_high(w25qxx);
}

void w25qxx\_write\_sector(w25qxx\_t \*w25qxx, uint8\_t \*pBuffer,
uint32\_t WriteAddr, uint32\_t count) {
\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_write\_enable(w25qxx);

uint32\_t page_cnt;
uint32\_t page_addr;

for (int i = 0; i < count; i++) {
w25qxx\_sector\_erase\_4kb(w25qxx,
(sector + i) \* w25qxx->page_size \* PAGES_PER_SECTOR);
}

page_addr = sector \* PAGES_PER_SECTOR;
page_cnt = count \* PAGES_PER_SECTOR;

\_\_w25qxx\_cs\_low(w25qxx);
for (uint32\_t cnt = 0; cnt < page_cnt; cnt++) {
\_\_w25qxx\_wait\_busy(w25qxx);
w25qxx\_page\_program(w25qxx, page_addr, buff + w25qxx->page_size \* cnt);
page_addr += 1;
}
\_\_w25qxx\_cs\_high(w25qxx);
}

6)驱动定义实现-擦除

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
void w25qxx\_erase\_chip(w25qxx\_t \*w25qxx) {
\_\_w25qxx\_write\_enable(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);

\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_ChipErase);
\_\_w25qxx\_cs\_high(w25qxx);

\_\_w25qxx\_wait\_busy(w25qxx);
}

void w25qxx\_sector\_erase(w25qxx\_t \*w25qxx, uint32\_t sector_addr) {

\_\_w25qxx\_wait\_busy(w25qxx);

\_\_w25qxx\_write\_enable(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);

uint32\_t addr = sector_addr \* w25qxx->sector_size;

uint8\_t add_hi = (uint8\_t) ((addr & 0x00FF0000) >> 16);
uint8\_t add_mid = (uint8\_t) ((addr & 0x0000FF00) >> 8);
uint8\_t add_lo = (uint8\_t) (addr);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_SectorErase);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_hi);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_mid);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_lo);

\_\_w25qxx\_cs\_high(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);
}

void w25qxx\_sector\_erase\_4kb(w25qxx\_t \*w25qxx, uint32\_t addr_sector) {
\_\_w25qxx\_wait\_busy(w25qxx);
\_\_w25qxx\_write\_enable(w25qxx);

\_\_w25qxx\_cs\_low(w25qxx);

uint8\_t add_hi = (uint8\_t) ((addr_sector & 0x00FF0000) >> 16);
uint8\_t add_mid = (uint8\_t) ((addr_sector & 0x0000FF00) >> 8);
uint8\_t add_lo = (uint8\_t) (addr_sector);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25QXX_Sector_Erase_4KB);

\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_hi);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_mid);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, add_lo);

\_\_w25qxx\_cs\_high(w25qxx);
}

void w25qxx\_erase\_block(w25qxx\_t \*w25qxx, uint32\_t block_addr) {

block_addr = block_addr \* w25qxx->sector_size \* 16;
\_\_w25qxx\_wait\_busy(w25qxx);

\_\_w25qxx\_write\_enable(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);

\_\_w25qxx\_cs\_low(w25qxx);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, W25X_BlockErase);
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) ((block_addr) >> 16));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) ((block_addr) >> 8));
\_\_w25qxx\_spi\_readwrite\_byte(w25qxx, (uint8\_t) block_addr);

\_\_w25qxx\_cs\_high(w25qxx);
\_\_w25qxx\_wait\_busy(w25qxx);
}

3、驱动测试

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
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
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include<stdio.h>
#include "w25qxx/w25qxx.h"
/\* USER CODE END Includes \*/

/\* USER CODE BEGIN PV \*/
w25qxx\_t w25qxx;
uint8\_t rx_buffer[32];
/\* USER CODE END PV \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_USART1\_UART\_Init();
MX\_SPI1\_Init();
/\* USER CODE BEGIN 2 \*/

printf("W25QXX Demo\r\n");
w25qxx.SPI_CS_Port = FLASH_CS_GPIO_Port;
w25qxx.SPI_CS_Pin = FLASH_CS_Pin;
w25qxx.SPI_Handle = &hspi1;
// w25qxx\_erase\_chip(&w25qxx);
printf("start to init w25qxx ...\r\n");
bool res = w25qxx\_init(&w25qxx);
if (res) {
printf("w25qxx inited\r\n");
w25qxx\_display\_info(&w25qxx);
} else {
printf("w25qxx init failed\r\n");
while (1)
;
}
printf("write data @0x10\r\n");
w25qxx\_write(&w25qxx, "0123456789ABCDEF", 0x10, 16);
w25qxx\_read(&w25qxx, rx_buffer, 0x10, 16);
printf("read @0x10 = %s\r\n", rx_buffer);

/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

文章来源: https://iotsmart.blog.csdn.net/article/details/125822885