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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
|
#include "quadspi.h"
#include "w25qxx.h"
// 4K bytes为一个Sector, 16个Sector为1个Block
uint16_t W25Q_TYPE = W25Q256;
uint8_t W25Q_QPI_MODE = 0; // QSPI模式标志:0 SPI模式;1 QPI模式
void w25q_qspi_enable(void);
uint8_t w25q_read_status_reg(uint8_t command);
void w25q_write_status_reg(uint8_t reg, uint8_t data);
void w25q_write_enable(void);
void w25q_write_disable(void);
void w25q_wait_busy(void);
uint16_t w25q_read_id(void);
void w25q_write_flash_page(uint8_t *buf, uint32_t address, uint16_t length);
void w25q_erase_chip(void);
void w25q_erase_sector(uint32_t address);
void w25q_init(void) {
w25q_qspi_enable();
W25Q_TYPE = w25q_read_id();
if(W25Q_TYPE == W25Q256) {
uint8_t status_reg3 = w25q_read_status_reg(3);
if(status_reg3 & 0x01 == 0x00) { // 不是4字节地址模式,需进入
w25q_write_enable();
qspi_send_cmd(W25Q_Enable4ByteAddr, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
}
w25q_write_enable();
qspi_send_cmd(W25Q_SetReadParam, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);
uint8_t temp = 0x30;
qspi_transmit(&temp, 1);
}
}
void w25q_qspi_enable(void) { // 使能QSPI模式
uint8_t status_reg2 = w25q_read_status_reg(2);
if((status_reg2 & 0x02) == 0x00) {
w25q_write_enable();
status_reg2 |= 0x02;
w25q_write_status_reg(2, status_reg2);
}
qspi_send_cmd(W25Q_EnterQPIMode, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
W25Q_QPI_MODE = 1;
}
/* 状态寄存器说明
* 状态寄存器1(默认0x00):
* 7 6 5 4 3 2 1 0
* SPR RV TB BP2 BP1 BP0 WEL BUSY
* SPR 默认0,状态寄存器保护位,配合WP使用
* TB、BP2、BP1、BP0 FLASH区域写保护设置
* WEL 写使能锁定
* BUSY 忙标记位(1忙,0空闲)
* 状态寄存器2:
* 7 6 5 4 3 2 1 0
* SUS CMP LB3 LB2 LB1 (R) QE SPR1
* 状态寄存器3:
* 7 6 5 4 3 2 1 0
* HOLD/RST BRV1 DRV0 (R) (R) WPS ADP ADS
*/
uint8_t w25q_read_status_reg(uint8_t reg) { // 读状态寄存器
uint8_t byte, command;
switch(reg) {
case 1:
command = W25Q_ReadStatusReg1;
break;
case 2:
command = W25Q_ReadStatusReg2;
break;
case 3:
command = W25Q_ReadStatusReg3;
break;
default:
command = W25Q_ReadStatusReg1;
break;
}
if(W25Q_QPI_MODE) qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);
else qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);
qspi_receive(&byte, 1);
return byte;
}
void w25q_write_status_reg(uint8_t reg, uint8_t data) { // 写状态寄存器
uint8_t command;
switch(reg) {
case 1:
command = W25Q_WriteStatusReg1;
break;
case 2:
command = W25Q_WriteStatusReg2;
break;
case 3:
command = W25Q_WriteStatusReg3;
break;
default:
command = W25Q_WriteStatusReg1;
break;
}
if(W25Q_QPI_MODE) qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_4_LINES);
else qspi_send_cmd(command, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_1_LINE);
qspi_transmit(&data, 1);
}
void w25q_write_enable(void) { // W25Q写使能:将S1寄存器的WEL置位
if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_WriteEnable, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
else qspi_send_cmd(W25Q_WriteEnable, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
}
void w25q_write_disable(void) { // W25Q写禁止:将S1寄存器的WEL清零
if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_WriteDisable, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
else qspi_send_cmd(W25Q_WriteDisable, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
}
void w25q_wait_busy(void) { // 等待空闲
while((w25q_read_status_reg(1) & 0x01) == 0x01);
}
uint16_t w25q_read_id(void) {
uint8_t temp[2];
if(W25Q_QPI_MODE) qspi_send_cmd(W25Q_ManufactDeviceID, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_24_BITS, QSPI_DATA_4_LINES);
else qspi_send_cmd(W25Q_ManufactDeviceID, 0, 0, QSPI_INSTRUCTION_1_LINE, QSPI_ADDRESS_1_LINE, QSPI_ADDRESS_24_BITS, QSPI_DATA_1_LINE);
qspi_receive(temp, 2);
return (temp[0] << 8) | temp[1];
}
/* 读取FLASH,仅支持QPI模式
* buf 数据存储区
* address 开始读取的地址,最大32bit
* length 读取的字节长度,最大65535
*/
void w25q_read_flash(uint8_t *buf, uint32_t address, uint16_t length) {
qspi_send_cmd(W25Q_FastReadData, address, 8, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES);
qspi_receive(buf, length);
}
/* 在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256个字节的数据
* buf 数据存储区
* address 开始读取的地址,最大32bit
* length 读取的字节长度,最大256,不应该超过该页剩余的字节数
*/
void w25q_write_flash_page(uint8_t *buf, uint32_t address, uint16_t length) {
w25q_write_enable();
qspi_send_cmd(W25Q_PageProgram, address, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_4_LINES);
qspi_transmit(buf, length);
w25q_wait_busy();
}
/* 在指定地址开始写入指定长度的数据,但要确保地址不越界
* 无检验写FLASH,必须确保所写地址范围内的数据全部为0xFF,否则在0xFF处写入的数据将失败,具有自动换页功能
*/
void w25q_write_flash_no_check(uint8_t *buf, uint32_t address, uint16_t length) {
uint16_t pageremain = 256 - address % 256; // 单页剩余字节数
if(length <= pageremain) { // 不大于256个字节
pageremain = length;
}
while(1) {
w25q_write_flash_page(buf, address, length);
if(length == pageremain) {
break;
}
else {
buf += pageremain;
address += pageremain;
length -= pageremain; // 减去已经写入了的字节数
if(length > 256) { // 一次可以写入256个字节
pageremain = 256;
}
else { // 不够256个字节
pageremain = length;
}
}
}
}
/* 在指定地址开始写入指定长度的数据,但要确保地址不越界
* 该函数带有自动擦除功能
*/
uint8_t W25Q_BUFFER[4096];
void w25q_write_flash(uint8_t *buf, uint32_t address, uint16_t length) {
uint32_t secpos;
uint16_t secoff, secremain, i;
uint8_t *w25q_buf;
w25q_buf = W25Q_BUFFER;
secpos = address / 4096; // 扇区地址
secoff = address % 4096; // 在扇区内的偏移
secremain = 4096 - secoff; // 扇区剩余空间大小
if(length <= secremain) { // 不大于4096个字节
secremain = length;
}
while(1) {
w25q_read_flash(w25q_buf, secpos * 4096, 4096); // 读出整个扇区的数据
for(i = 0; i < secremain; i++) { // 校验数据
if(w25q_buf[secoff + i] != 0xFF) break;
}
if(i < secremain) { // 需要擦除
w25q_erase_sector(secpos); // 擦除这个扇区
for(i = 0; i < secremain; i++) {
w25q_buf[i + secoff] = buf[i];
}
w25q_write_flash_no_check(w25q_buf, secpos * 4096, 4096); // 写入整个扇区
}
else {
w25q_write_flash_no_check(buf, address, secremain);
}
if(length == secremain) { // 写入完成
break;
}
else {
secpos++;
secoff = 0;
buf += secremain; // 指针偏移
address += secremain; // 写地址偏移
length -= secremain;
if(length > 4096) secremain = 4096;
else secremain = length;
}
}
}
void w25q_erase_chip(void) { // 擦除整个芯片,等待时间超长
w25q_write_enable();
w25q_wait_busy();
qspi_send_cmd(W25Q_ChipErase, 0, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_NONE, QSPI_ADDRESS_8_BITS, QSPI_DATA_NONE);
w25q_wait_busy();
}
void w25q_erase_sector(uint32_t address) { // 擦除一个扇区,至少150ms
address *= 4096;
w25q_write_enable();
w25q_wait_busy();
qspi_send_cmd(W25Q_SectorErase, address, 0, QSPI_INSTRUCTION_4_LINES, QSPI_ADDRESS_4_LINES, QSPI_ADDRESS_32_BITS, QSPI_DATA_NONE);
w25q_wait_busy();
}
|