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
|
// Quad SPI for the 64 MBit flash on Disco-F723
namespace qspi {
using namespace jeeh;
// see ST's ref man: RM0431 rev 3, section 13, p.347 (QUADSPI)
enum { CR=0x00, DCR=0x04, SR=0x08, DLR=0x10, CCR=0x14, AR=0x18, DR=0x20,
PSMKR=0x24, PSMAR=0x28, PIR=0x2C, LPTR=0x30 };
const auto addr = (uint32_t const*) 0x9000'0000;
constexpr auto fsize = 26; // flash has 64 MB, 2^26 bytes
constexpr auto dummy = 6; // number of cycles between cmd and data
void waitBusy () {
while (QUADSPI[SR](5)) {} // wait until not busy
}
void mmapEnable () {
waitBusy();
QUADSPI[DLR] = 0;
QUADSPI[PIR] = 0x10;
QUADSPI[PSMKR] = 1<<0;
QUADSPI[PSMAR] = 0;
// mem-mapped: FMODE DMODE, IMODE INS
QUADSPI[CCR] = (2<<26) | (3<<24) | (3<<8) | (0x05<<0); // poll status
waitBusy();
// mem-mapped: DDRM DHHC FMODE DMODE DCYC, ADSIZE ADMODE IMODE INS
QUADSPI[CCR] = (1<<31) | (1<<30) | (3<<26) | (3<<24) | (dummy<<18) |
(3<<12) | (3<<10) | (3<<8) | (0xEE<<0);
}
void init () {
RCC(EN_QSPI, 1) = 1;
Pin::config("B6:PV10,B2:PV9,C9,C10,D13,E2");
QUADSPI[DCR] = ((fsize-1)<<16); // FSIZE
QUADSPI[LPTR] = (1<<10); // raise nsel after 1k idle cycles
QUADSPI[CR] = (2<<24) | (1<<22) | (1<<3) | (1<<0); // PRESCALER APMS TCEN EN
waitBusy();
// indirect: IMODE INS
QUADSPI[CCR] = (1<<8) | (0x35<<0); // enter QPI mode for INS+ADDR+DATA
mmapEnable();
}
void deinit () {
waitBusy();
QUADSPI[CCR] = (3<<8) | (0xF5<<0); // reset QPI mode
RCC(EN_QSPI, 1) = 0;
}
// mass erase, this takes 2..3 mins
void wipe () {
waitBusy();
// indirect: IMODE INS
QUADSPI[CCR] = (3<<8) | (0x06<<0); // write enable
waitBusy();
QUADSPI[CCR] = (3<<8) | (0xC7<<0); // chip erase
mmapEnable();
}
void read (uint32_t addr, uint32_t* ptr, int num) {
waitBusy();
// indirect: DDRM DHHC FMODE DMODE DCYC, ADSIZE ADMODE IMODE INS
QUADSPI[CCR] = (1<<31) | (1<<30) | (1<<26) | (3<<24) | (dummy<<18) |
(3<<12) | (3<<10) | (3<<8) | (0xEE<<0); // read bytes
QUADSPI[AR] = addr;
QUADSPI[DLR] = 4*num-1;
for (int i = 0; i < num; ++i)
ptr[i] = QUADSPI[DR];
mmapEnable();
}
// erase one 4 kB sector
void erase (uint32_t addr) {
waitBusy();
// indirect: IMODE INS
QUADSPI[CCR] = (3<<8) | (0x06<<0); // write enable
waitBusy();
// indirect: ADSIZE ADMODE IMODE INS // program bytes
QUADSPI[CCR] = (3<<12) | (3<<10) | (3<<8) | (0x21<<0); // page erase
QUADSPI[AR] = addr;
mmapEnable();
}
// can write at most 64 words (256 bytes)
void write (uint32_t addr, uint32_t const* ptr, int num) {
waitBusy();
// indirect: IMODE INS
QUADSPI[CCR] = (3<<8) | (0x06<<0); // write enable
waitBusy();
// indirect: DMODE ADSIZE ADMODE IMODE INS
QUADSPI[CCR] = (3<<24) | (3<<12) | (3<<10) | (3<<8) | (0x12<<0); // program
QUADSPI[AR] = addr;
QUADSPI[DLR] = 4*num-1;
waitBusy();
for (int i = 0; i < num; ++i)
QUADSPI[DR] = ptr[i];
mmapEnable();
}
} // namespace qspi
|