├── 1_Flash_library
└── README.md
├── 2_ExternalLoader
└── README.md
├── 3_CubeIDE_mapping
└── README.md
├── 4_TouchGFX_mapping
├── README.md
├── Senzanome.png
├── designer1.png
└── designer2.png
├── Dev_Inf.c
├── Dev_Inf.h
├── EL_linker.ld
├── LICENSE
├── Loader_Src.c
├── Post-Build command.txt
├── Program_linker_include.txt
├── README.md
├── z_flash_W25QXXX.c
└── z_flash_W25QXXX.h
/1_Flash_library/README.md:
--------------------------------------------------------------------------------
1 | **Piu' sotto, al termine della lingua inglese trovi il testo in italiano. **_
2 | _**
Below the English text you'll find the Italian version**_
3 |
4 |
5 |
6 |
7 | # 1) Using the W25QXXXX Winbond SPI Flash chip library
8 | Here you'll find documentation about handling a flash memory chip connected to a uC SPI port
9 |
10 | - (1) in CubeMX:
11 | creating your STM32 project assign an SPI port to the communication with the Flash memory.
12 | SPI must be set this way:
13 |
14 | |Mode|value|
15 | |---|---|
16 | |mode|Full-Duplex Master|
17 | |NSS|Disable|
18 |
19 |
20 | |Parameter|value|
21 | |---|---|
22 | |Frame format|Motorola|
23 | |Data size|8 bit|
24 | |First bit|MSB first|
25 | |CPOL|low|
26 | |CPHA|1 Edge|
27 | |CRC calculation|disabled|
28 | |NSS type|Output Sw|
29 |
30 | SPI pins must have these names:
31 |
32 |
33 | FLASH_SCK
34 | FLASH_MISO
35 | FLASH_MOSI
36 |
37 |
38 |
39 |
40 | Setup a pin as GPIO_Output:
41 |
42 | |pinname to assign|output level|mode|pull-up/down|
43 | |---|---|---|---|
44 | |FLASH_CS|high|Output push pull|No pull-up/down|
45 |
46 | Pin speed affects SPI port: rise (step by step from LOW to VERY HIGH) pins speed if you see uC cannot handle the defined communication speed (more information on your uC datasheet).
47 |
48 |
49 |
50 | -
51 | (2) in CubeIDE:
52 | after saving CubeMX and after C code generation:
53 |
54 | copy into the "include" folder (core\inc) z_flash_W25QXXX.h file
55 | copy into the "source" folder (core\src) z_flash_W25QXXX.c file
56 |
57 |
58 |
59 | -
60 | (3) in main.h:
61 | open file main.h
62 | and edit /* USER CODE BEGIN Includes */ segment, this way:
63 |
64 |
65 | ```sh
66 | (main.h)
67 | ...
68 | /* Private includes ----------------------------------------------------------*/
69 | /* USER CODE BEGIN Includes */
70 | #include "z_flash_W25QXXX.h"
71 | /* USER CODE END Includes */
72 | ...
73 | ```
74 |
75 |
76 | -
77 | (4) in z_flash_W25QXXX.h:
78 | edit file z_flash_W25QXXX.h this way:
79 |
80 | -
81 | into segment STEP1:
82 | register SPI used, updating the two lines (hspi1/SPI1, hspi2/SPI2, ...)
83 | -
84 | into segment STEP2:
85 | no changes: currently available only "Fast Mode"
86 | -
87 | into segment STEP3:
88 | no changes: currently available only "Polling Mode"
89 | -
90 | into segment STEP4:
91 | update all parameters with information about memory chip to handle.
92 | Currently file shows, inside comments, configuration parameters for:
93 |
94 | W25Q80DV/DL
95 | W25Q64JV
96 | W25Q128JV
97 |
98 | if you are using a chip of these, you just need to copy the related commented parameters to the uncommented area STEP4
99 | if you are using a different memory chip, just use this data as example of what is needed for a correct library setup.
100 |
101 |
102 |
103 |
104 | Having done what above shown, you can use all functions of the library handling flash.
105 | Each function has its own description and help, inside z_flash_W25QXXX.c file
106 |
107 |
108 |
109 |
110 | [Back to the home page](../.)
111 |
112 |
113 |
114 |
115 |
116 | ## GitHub Folders
117 |
118 | This guide is divided in 4 chapters following the above list.
119 |
120 | - [handling an external W25Q flash with this library](../1_Flash_library)
121 | - [creating and using an External Loader for the SPI flash](../2_ExternalLoader)
122 | - [mapping an external SPI flash on CubeIDE projects](../3_CubeIDE_mapping)
123 | - [using an external SPI flash in TouchGFX projects](../4_TouchGFX_mapping)
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 | ---
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 | # Gestire un chip SPI Flash W25Q di Winbond con questa libreria
140 | Qui trovi le informazioni su come gestire una memoria flash connessa alla porta SPI del uC
141 |
142 | - (1) in CubeMX:
143 | durante la creazione del progetto, assegnare una porta SPI per la comunicazione con la memoria Flash.
144 | la porta SPI deve essere configurata così:
145 |
146 | |Mode|value|
147 | |---|---|
148 | |mode|Full-Duplex Master|
149 | |NSS|Disable|
150 |
151 |
152 | |Parameter|value|
153 | |---|---|
154 | |Frame format|Motorola|
155 | |Data size|8 bit|
156 | |First bit|MSB first|
157 | |CPOL|low|
158 | |CPHA|1 Edge|
159 | |CRC calculation|disabled|
160 | |NSS type|Output Sw|
161 |
162 | Ai pin della porta SPI assegnare i nomi:
163 |
164 | FLASH_SCK
165 | FLASH_MISO
166 | FLASH_MOSI
167 |
168 |
169 |
170 | Configura un altro pin come GPIO_Output:
171 |
172 | |pinname to assign|output level|mode|pull-up/down|
173 | |---|---|---|---|
174 | |FLASH_CS|high|Output push pull|No pull-up/down|
175 |
176 | La velocità del pin ha effetti sulla velocità della porta SPI: aumenta (per gradi da LOW to VERY HIGH) la velocità dei pin se vedi che il uC non riesce a gestire la velocità di comunicazione assegnata (maggiori informazioni sul datasheet del tuo uC).
177 |
178 |
179 |
180 | -
181 | (2) in CubeIDE:
182 | dopo aver salvato la configurazione CubeMX e generato il codice C:
183 |
184 | copiare nella cartella "include" (core\inc) il file z_flash_W25QXXX.h
185 | copiare nella cartella "source" (core\src) il file z_flash_W25QXXX.c
186 |
187 |
188 |
189 | -
190 | (3) in main.h:
191 | aprire il file main.h
192 | e modifica il segmento /* USER CODE BEGIN Includes */ in questo modo:
193 |
194 |
195 | ```sh
196 | (main.h)
197 | ...
198 | /* Private includes ----------------------------------------------------------*/
199 | /* USER CODE BEGIN Includes */
200 | #include "z_flash_W25QXXX.h"
201 | /* USER CODE END Includes */
202 | ...
203 | ```
204 |
205 |
206 | -
207 |
208 | (4) in z_flash_W25QXXX.h:
209 | aprire il file z_flash_W25QXXX.h e modificare in questo modo:
210 |
211 | -
212 | nel segmento STEP1:
213 | indicare la porta SPI utilizzata aggiornando le due voci (hspi1/SPI1, hspi2/SPI2, ...)
214 | -
215 | nel segmento STEP2:
216 | non modificare: attualmente disponibile solo la modalità "Fast Mode"
217 | -
218 | nel segmento STEP3:
219 | non modificare: attualmente disponibile solo la modalità "Polling Mode"
220 | -
221 | nel segmento STEP4:
222 | aggiornare tutti i parametri con le informazioni del chip di memoria utilizzato.
223 | Attualmente il file contiene nei commenti le configurazioni relative a:
224 |
225 | W25Q80DV/DL
226 | W25Q64JV
227 | W25Q128JV
228 |
229 | se si utilizza uno di questi chip è sufficiente copiare i dati delle righe di configurazione commentate, nell'area di informazione STEP4
230 | se si usano altri chip, usare questi dati come esempio per la configurazione corretta.
231 |
232 |
233 |
234 |
235 | Al termine e' possibile utilizzare le varie funzioni della libreria per gestire la memoria Flash
236 | Le funzioni sono autodescritte e commentate nel file z_flash_W25QXXX.c
237 |
238 |
239 |
240 |
241 |
242 | [Torna alla home page](../.)
243 |
244 |
245 |
246 |
247 |
--------------------------------------------------------------------------------
/2_ExternalLoader/README.md:
--------------------------------------------------------------------------------
1 | **Piu' sotto, al termine della lingua inglese trovi il testo in italiano. **_
2 | _**
Below the English text you'll find the Italian version**_
3 |
4 |
5 |
6 |
7 | # 2) Creating an EXTERNAL LOADER for STM32CubeProgrammer and STM32CubeIDE
8 | External Loader is a plug-in for STM32CubeProgrammer allowing to read/write an external memory through an STM32 uC.
9 | Through the library shown above it is possible to create an External Loader for an STM32 project having a Winbond external SPI Flash chip .
10 | Next, you will see how you can use the same external loader program in STM32CubeIDE to program external Flash memory directly while creating a project: CubeIDE uses the CubeProgrammer modules to program uC internal and external flash memory.
11 |
12 | ## "How to" create an External Loader for a specific project:
13 |
14 | Supposing you already defined the project configuration having an external flash memory (e.g. having defined the SPI port and CS pin and other details needed to communicate with Flash memory...)
15 | - Create a new project in CubeIDE.
16 | Give the project a name with this format:
17 | "external flash used"_"uC or board name"_"other info, if any, - e.g. SPI port, CS pin, etc."
18 | (character "_", dividing the three fields, must non be used elsewhere)
19 | Here some examples for a valid project name:
20 |
21 | W25Q80_STM32F411_SPI1
22 | W25Q80_BLACKPILL-F411 (third field is optional and here it is not used)
23 | W25Q64_NUCLEO32-L432_SPI1-PA4CS
24 | etc.
25 |
26 | - in CubeMX:
27 |
28 | - setup the SPI port and the CS pin for your flash memory. (see above details about SPI port configuration on CubeMX)
29 |
30 | Warning:
31 | if prototyping with a flash on a breadboard, in the External Loader project I recommend to setup an SPI port speed lower than 2Mbit/s
32 |
33 | - If on your board, you have available a led connected to a uC pin (GPIO_Output): you can use it in the External Loader. Useful for troubleshooting.
34 | give the uC led pin the name: LED.
35 | - in "Project Manager"->"Code Generator", mark "Generate peripheral initialization as a pair of '.c/.h' files per peripheral" to have peripheral initializing files separated by main.c
36 |
- generate "c" code.
37 |
38 | - in CubeIDE:
39 |
40 | -
41 | in Core/inc folder, copy files:
42 |
43 | z_flash_W25QXXX.h
44 | Dev_Inf.h
45 |
46 | -
47 | in Core/src folder, copy files:
48 |
49 | z_flash_W25QXXX.c
50 | Dev_Inf.c
51 | Loader_Src.c
52 |
53 | -
54 | in root folder, copy files:
55 |
56 | EL_linker.ld
57 | Post-Build command.txt
58 |
59 | -
60 | Setup "z_flash_W25QXXX.h"
61 |
62 | registering flash chip characteristics (see above the setup of "z_flash_W25QXXX.h" Step 1 to 4)
63 |
64 | -
65 | Setup Loader_Src.c:
66 |
67 | follow inside file: Step1, Step2, Step3
68 |
69 | -
70 | Setup Dev_Inf.c:
71 |
72 | update first struct field assigning a name to the External Loader: copy here the project name
73 | update third struct field indicating address where external flash is mapped
74 | leave unchanged other fields.
75 |
76 | -
77 | open file "Post-Build command.txt"
78 |
79 | copy the text row and paste it into the field in
80 | project->properties->C/C++ Build->settings->Build Steps->Post-Build Steps->Command
81 | click "Apply and Close"
82 |
83 | -
84 | edit EL_linker.ld
85 |
86 | -
87 | set the memory size as per uC available RAM:
88 | around line 30 you find text:
89 |
90 | ```sh
91 | (EL_linker.ld)
92 | ...
93 | RAM (xrw) : ORIGIN = 0x20000004, LENGTH = xxxK
94 | ...
95 | ```
96 |
97 | change value after "LENGTH =" indicating the uC RAM size
98 | -
99 | save changes
100 |
-
101 | in project->properties->C/C++ Build->settings->Tool Setting->MCU GCC Linker->General->linker script:
102 |
103 | 1 change the name of the .ld file into "EL_linker.ld". Parameter should become: ${workspace_loc:/${ProjName}/EL_linker.ld}
104 | 2 remove flag in Discard Unused Sections
105 | 3 click "Apply and Close"
106 |
107 |
108 |
109 |
110 | Now "Compile" the project: If everithing is good you'll find the file project_name.stldr into the project root folder
111 | THIS IS THE EXTERNAL LOADER
112 |
113 | ## "How to" add External Loader to STM32CubeProgrammer
114 |
115 |
116 | -
117 | Go to the STM32CubeProgrammer program folder
118 | (right-click the program icon and choose "open file location")
119 | -
120 | once in the STM32CubeProgrammer program folder go into "ExternalLoader" subfolder
121 |
-
122 | copy here the External Loader created (.stldr file)
123 | Opening STM32CubeProgrammer program you can select the new External Loader to access the external flash memory.
124 |
125 |
126 | ## "How to" add External Loader to STM32CubeIDE
127 |
128 | -
129 | Go to the STM32Cubeide program folder
130 | (right-click the program icon and choose "open file location")
131 | -
132 | once in the STM32CubeProgrammer program folder go subfolder:
133 | ./plugins/xxxxxx.externaltools.cubeprogrammer.xxxxx/tools/bin/ExternalLoader
134 |
135 | -
136 | copy here the External Loader created (.stldr file)
137 | Now the External Loader is available to CubeIDE and can be selected in "Run/Debug Settings":
138 | in CubeIDE, go to Project->Properties->Run/Debug Settings
139 | select the launch configuration file and click "Edit"
140 | go to "Debugger"
141 | scroll to "External Loaders" and click "Add"
142 | select, in the external loader list, the external loader to use.
143 | Click "OK" and "Apply" until you close all the "properties" windows open.
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 | [Back to the home page](../.)
152 |
153 |
154 |
155 |
156 |
157 | ---
158 |
159 | ---
160 |
161 |
162 |
163 |
164 | # creare un EXTERNAL LOADER per STM32CubeProgrammer e STM32CubeIDE
165 | External Loader e' un plug-in per STM32CubeProgrammer per leggere e/o programmare un chip di memoria gestito attraverso da un uC STM32.
166 | Attraverso la libreria di funzioni indicata sopra e' possibile creare un external loader per un progetto STM32 che ha una memoria SPI Flash Winbond esterna .
167 | Lo stesso plugin puo' essere utilizzato in STM32CubeIDE per programmare la memoria Flash direttamente durante la creazione di un progetto: CubeIDE usa i moduli CubeProgrammer per programmare la memoria flash interna od esterna del uC.
168 |
169 | ## "How to" come creare an External Loader relativo ad un progetto:
170 |
171 | Ipotizzando che hai gia' creato la configurazione del progetto di memoria esterna (es. hai già definito la porta SPI ed il pin CS da usare per la comunicazione con la memoria Flash...)
172 | - Crea un nuovo progetto in CubeIDE.
173 | Assegna al progetto un nome con questa forma:
174 | "external flash in uso"_"nome uC o board in uso"_"eventuali altre informazioni - es. Porta SPI, CS pin, ecc."
175 | (il carattere "_" separa i tre campi e non deve essere utilizzato nel nome se non per questa separazione)
176 | Esempi di nomi di progetto protrebbero essere:
177 |
178 | W25Q80_STM32F411_SPI1
179 | W25Q80_BLACKPILL-F411 (il terzo campo è opzionale e qui non c'e')
180 | W25Q64_NUCLEO32-L432_SPI1-PA4CS
181 | ecc.
182 |
183 | - in CubeMX:
184 |
185 | - configura una porta SPI ed il pin CS per la memoria flash (vedi sopra il dettaglio per la configurazione CubeMX)
186 | Attenzione:
187 | se usato in prototipazione, per External Loader e' consigliabile configurare una velocià inferiore a 2Mbit/s
188 | - Se hai un led a disposizione sulla scheda, puoi usarlo nell'External Loader (utile nel troubleshooting)
189 | dai al pin del led il nome: LED
190 | - in "Project Manager"->"Code Generator", spunta "Generate peripheral initialization as a pair of '.c/.h' files per peripheral" per avere i file di inizializzazione separati da main.c
191 |
- genera il codice.
192 |
193 | - in CubeIDE:
194 |
195 | -
196 | copiare in include folder
197 |
198 | z_flash_W25QXXX.h
199 | Dev_Inf.h
200 |
201 | -
202 | copiare in src folder
203 |
204 | z_flash_W25QXXX.c
205 | Dev_Inf.c
206 | Loader_Src.c
207 |
208 | -
209 | copiare in root folder
210 |
211 | EL_linker.ld
212 | Post-Build command.txt
213 |
214 | -
215 | definire i parametri in "z_flash_W25QXXX.h"
216 |
217 | definire le caratteristiche della memoria (vedi piu' sopra la configurazione di "z_flash_W25QXXX.h" da Step 1 a 4)
218 |
219 | -
220 | configurare Loader_Src.c:
221 |
222 | seguire gli step di configurazione: Step1, Step2, Step3
223 |
224 | -
225 | configurare Dev_Inf.c:
226 |
227 | aggiorna il primo campo della struct assegnando il nome all'External Loader: deve essere "rif alla flash" _ "rif alla board" _ "altre info (es versione)" (copiare il nome del progetto)
228 | aggiorna il terzo campo della struct indicando l'indirizzo su cui e' mappata la flash
229 | non modificare gli altri campi
230 |
231 | -
232 | aprire il file "Post-Build command.txt"
233 |
234 | copiare il contenuto e incollarlo nel campo in
235 | project->properties->C/C++ Build->settings->Build Steps->Post-Build Steps->Command
236 | scegliere "Apply and Close"
237 |
238 | -
239 | modificare EL_linker.ld
240 |
241 | -
242 | modificare la dimensione della memoria indicando la RAM disponibile sul uC:
243 | alla riga 30 trovi il testo:
244 |
245 | ```sh
246 | (EL_linker.ld)
247 | ...
248 | RAM (xrw) : ORIGIN = 0x20000004, LENGTH = xxxK
249 | ...
250 | ```
251 |
252 | modifica il valore dopo "LENGTH =" specificando la dimensione della RAM del uC
253 | -
254 | salvare
255 |
-
256 | in project->properties->C/C++ Build->settings->Tool Setting->MCU GCC Linker->General->linker script:
257 |
258 | 1 cambiare il nome del file .ld in "EL_linker.ld": dovrebbe essere: ${workspace_loc:/${ProjName}/EL_linker.ld}
259 | 2 togliere flag da Discard Unused Sections
260 | 3 cliccare "Apply and Close"
261 |
262 |
263 |
264 |
265 | "Compila" il progetto: se tutto e' andato bene trovi il file "nomeprogetto".stldr nella cartella di root
266 | Questo e' l'external loader
267 |
268 | ## "How to": come aggiungere un External Loader a STM32CubeProgrammer
269 |
270 | -
271 | Andare nel folder del programma STM32CubeProgrammer
272 | (ad esempio click-destro del mouse sull'icona del programma e scegliere "Apri percorso file")
273 | -
274 | raggiunta la cartella di STM32CubeProgrammer, scegliere la cartella "ExternalLoader"
275 |
-
276 | copiare qui il file .stldr creato
277 | Aprendo STM32CubeProgrammer si puo' selezionare il nuovo External Loader per accedere alla memoria Flash esterna
278 |
279 |
280 |
281 | ## "How to" add External Loader to STM32CubeIDE
282 |
283 | -
284 | Andare nel folder del programma STM32CubeIDE
285 | (ad esempio click-destro del mouse sull'icona del programma e scegliere "Apri percorso file")
286 | -
287 | Raggiunta la cartella di STM32CubeIDE, andare nella sottocartella:
288 | ./plugins/xxxxxx.externaltools.cubeprogrammer.xxxxx/tools/bin/ExternalLoader
289 | -
290 | Copiare qui il file .stldr creato
291 | Ora l'External Loader è disponibile in CubeIDE e puo' essere selezionato in "Run/Debug Settings":
292 | in CubeIDE andare in: Project->Properties->Run/Debug Settings
293 | selezionare il file di configurazione run e cliccare "Edit"
294 | selezionare "Debugger"
295 | sorrere la pagina fino a "External Loaders" e cliccare "Add"
296 | Selezionare, Nella lista degli external loader, il modulo da usare.
297 | Cliccare "OK" and "Apply" chidendo la finestra "Properties" aperta.
298 |
299 |
300 |
301 |
302 | [Torna alla home page](../.)
303 |
304 |
305 |
306 |
307 |
308 |
--------------------------------------------------------------------------------
/3_CubeIDE_mapping/README.md:
--------------------------------------------------------------------------------
1 | **Piu' sotto, al termine della lingua inglese trovi il testo in italiano. **_
2 | _**
Below the English text you'll find the Italian version**_
3 |
4 |
5 |
6 |
7 |
8 | # 3) "How to" setup an STM32CubeIDE project mapping an external flash memory
9 |
STILL UNDER DEVELOPEMENT
10 |
11 |
12 |
13 |
14 | [Back to the home page](../.)
15 |
16 |
17 |
18 |
19 |
20 | ---
21 |
22 |
23 |
24 |
25 |
26 | # "How to" configurare un progetto STM32CubeIDE mappando una memoria flash esterna
27 |
ANCORA IN SVILUPPO
28 |
29 |
30 |
31 |
32 | [Torna alla home page](../.)
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/4_TouchGFX_mapping/README.md:
--------------------------------------------------------------------------------
1 | **Piu' sotto, al termine della lingua inglese trovi il testo in italiano. **_
2 | _**
Below the English text you'll find the Italian version**_
3 |
4 |
5 |
6 |
7 | # 4) "How to" setup a TouchGFX project mapping an external flash memory
8 |
9 | Setup a TouchGFX project (following https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32)
10 | then:
11 | on CubeMX:
12 |
13 | on TouchGFX package:
14 |
15 | enable "External Data Reader"
16 |
17 | in External data reader: Memory base address
18 |
19 | set the memory base address (leave 0x90000000 if no specific needs t ochange it)
20 |
21 | in External data reader: Memory size
22 |
23 | indicate the size of the external flash memory
24 |
25 | other parameters
26 |
27 | leave unchanged
28 |
29 |
30 |
31 | generate "c" code.
32 |
33 | on CubeIDE:
34 |
35 | in include folder, copy file:
36 |
37 | z_flash_W25QXXX.h
38 |
39 | in src folder, copy file:
40 |
41 | z_flash_W25QXXX.c
42 |
43 | in root folder, copy files:
44 |
45 | Program_linker_include.txt
46 |
47 | Edit file main.h:
48 |
49 | open it
50 | modify /* USER CODE BEGIN Includes */ segment, adding the flash library include, this way:
51 |
52 |
53 |
54 | ```sh
55 | (main.h)
56 | ...
57 | /* Private includes ----------------------------------------------------------*/
58 | /* USER CODE BEGIN Includes */
59 | ...
60 | #include "z_flash_W25QXXX.h"
61 | ...
62 | /* USER CODE END Includes */
63 | ...
64 | ```
65 |
66 |
67 |
68 |
69 |
70 |
71 | Edit the linker script ( file):
72 |
73 | Open the linker script file and open also Program_linker_include.txt.
74 | From this last file copy the row indicating flash size and address (starting with "SPI_FLASH...").
75 | In the linker script, in the "Memories definition" area, paste the copied row, obtaining something like this:.
76 |
77 | ```sh
78 | (xxxxxxFLASH.ld)
79 | ...
80 | /* Memories definition */
81 | MEMORY
82 | {
83 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
84 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
85 | SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 1M
86 | }
87 | ...
88 | ```
89 |
90 |
91 | Do not modify RAM configuration,FLASH one or anyother memory area defined and handled by CubeMX
92 | Change "LENGTH" field of SPI_FLASH indicating the size of flash memory you are using
93 | "ORIGIN" field of SPI_FLASH must be the same value registred in CubeMX TouchGFX configuration
94 |
95 | From Program_linker_include.txt file copy rows of the ExtFlashSection
96 |
97 | ```sh
98 | (Program_linker_include.txt)
99 | ...
100 | ExtFlashSection :
101 | {
102 | *(ExtFlashSection ExtFlashSection.*)
103 | *(-gnu-linkonce.r.*)
104 | . = ALIGN(0x100);
105 | } >SPI_FLASH
106 | ...
107 | ```
108 |
109 | and paste them at the beginning of the SECTIONS area of the linker script. This way:
110 |
111 | ```sh
112 | (xxxxxxFLASH.ld)
113 | ...
114 |
115 | /* Sections */
116 | SECTIONS
117 | {
118 | ExtFlashSection :
119 | {
120 | *(ExtFlashSection ExtFlashSection.*)
121 | *(-gnu-linkonce.r.*)
122 | . = ALIGN(0x100);
123 | } >SPI_FLASH
124 | ...
125 | ```
126 | This will allow to move selected images to the external flash memory.
127 | If you want/need to move also fonts to the external memory copy also the FontFlashSection from Program_linker_include.txt and paste into linker file this way:
128 |
129 | ```sh
130 | (xxxxxxFLASH.ld)
131 | ...
132 |
133 | /* Sections */
134 | SECTIONS
135 | {
136 | ExtFlashSection :
137 | {
138 | *(ExtFlashSection ExtFlashSection.*)
139 | *(-gnu-linkonce.r.*)
140 | . = ALIGN(0x100);
141 | } >SPI_FLASH
142 |
143 | FontFlashSection :
144 | {
145 | *(FontFlashSection FontFlashSection.*)
146 | *(.gnu.linkonce.r.*)
147 | . = ALIGN(0x4);
148 | } >SPI_FLASH
149 |
150 | ...
151 | ```
152 |
153 |
154 |
155 |
156 | on TouchGFX Designer:
157 |
158 |
159 | From now on, when you are in TouchGFX Designer, you can move single pictures to the external flash assigning them to the ExtFlashSection:
160 |
161 |
162 |
If you want use external flash as the default storage for pictures set it into the Default Image Configuration:
163 |
164 |
165 |
166 | If you move fonts over the external flash memory, you ALWAYS MUST set fonts as "unmapped" in TouchGFX Designer configuration:
167 |
168 |
169 |
170 |
171 |
172 | ---
173 | >
174 | > WARNING
175 | >
176 | > External SPI flash memory is MUCH slower than uC internal flash.
177 | > Consider to move to the flash what you really need due to the limit of the uC internal flash
178 |
179 | ---
180 |
181 |
182 | add the external loader to CubeIDE
183 |
184 | Go to the STM32CubeIDE program folder
185 | (right-click the program icon and choose "open file location")
186 | once in the STM32CubeIDE program folder go to:
187 |
188 | plugins folder
189 | xxxxxx.externaltools.cubeprogrammer.xxxxx folder
190 | tools folder
191 | bin folder
192 | ExternalLoader folder
193 |
194 | here copy the external loader you previously made following above instructions
195 |
196 | configure CubeIDE to use the external loader
197 |
198 | in CubeIDE go to Project->Properties->Run/Debug Settings
199 | select the settings file and click "Edit"
200 | go to "Debugger"
201 | scroll to "External Loaders" and click "Add"
202 | select, in the external loader list, the external loader to use.
203 | Click "OK" and "Apply" until you close all the "properties" windows open.
204 |
205 |
206 |
207 |
208 |
209 | [Back to the home page](../.)
210 |
211 |
212 |
213 |
214 |
215 | ---
216 |
217 | ---
218 |
219 |
220 |
221 |
222 | # "How to" come configurare un progetto TouchGFX mappando una memoria flash esterna
223 | Configurare un progetto TouchGFX (segui ad esempio https://github.com/maudeve-it/ILI9XXX-XPT2046-STM32)
224 | poi:
225 | in CubeMX:
226 |
227 | nel pacchetto TouchGFX:
228 |
229 | abilitare "External Data Reader"
230 |
231 | in External data reader: Memory base address
232 |
233 | impostare l'indirizzo dove mappare la memoria (lasciare 0x90000000 se non si anno specifiche esigenze diverse)
234 |
235 | in External data reader: Memory size
236 |
237 | indicare la dimensione della memoria flash esterna
238 |
239 | altri parametri
240 |
241 | lasciare come sono
242 |
243 |
244 |
245 | generare il "codice c".
246 |
247 | su CubeIDE:
248 |
249 | nella cartella include, copiare il file:
250 |
251 | z_flash_W25QXXX.h
252 |
253 | nella cartella src, copiare il file:
254 |
255 | z_flash_W25QXXX.c
256 |
257 | nella cartella root, copiare i file:
258 |
259 | Program_linker_include.txt
260 |
261 | Modificare il file main.h:
262 |
263 | aprirlo
264 | modificare il segmento /* USER CODE BEGIN Includes */, aggiungendo la "include" per la libreria flash:
265 |
266 |
267 |
268 | ```sh
269 | (main.h)
270 | ...
271 | /* Private includes ----------------------------------------------------------*/
272 | /* USER CODE BEGIN Includes */
273 | ...
274 | #include "z_flash_W25QXXX.h"
275 | ...
276 | /* USER CODE END Includes */
277 | ...
278 | ```
279 |
280 |
281 |
282 |
283 | Editare lo script per il linker (file ):
284 |
285 | - aprire lo script per il linker e aprire anche Program_linker_include.txt
286 | - da questo ultimo file copiare la riga che indica la misura e l'indirizzo della memoria flash (inizia con "SPI_FLASH...")
287 | - nello script linker, nell'area "Memories definition", incolla la riga copiata, ottenendo una struttura come questa:
288 |
289 | ```sh
290 | (xxxxxxFLASH.ld)
291 | ...
292 | /* Memories definition */
293 | MEMORY
294 | {
295 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
296 | FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 512K
297 | SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 1M
298 | }
299 | ...
300 | ```
301 |
302 |
303 | Non modificare la configurazione RAM, FLASH, o qualunque altra riga si trovi qui: definita e gestita da CubeMX.
304 | Modificare il campo "LENGTH" di SPI_FLASH indicando la dimensione della memoria flash in uso.
305 | Il campo "ORIGIN" di SPI_FLASH deve avere lo stesso valore registrato nella configurazione TouchGFX in CubeMX.
306 | Dal file Program_linker_include.txt copia le righe relative a ExtFlashSection.
307 |
308 | ```sh
309 | (Program_linker_include.txt)
310 | ...
311 | ExtFlashSection :
312 | {
313 | *(ExtFlashSection ExtFlashSection.*)
314 | *(-gnu-linkonce.r.*)
315 | . = ALIGN(0x100);
316 | } >SPI_FLASH
317 | ...
318 | ```
319 |
320 | ed incollale all'inizio dell'area SECTIONS dello script linker. In questo modo:
321 |
322 | ```sh
323 | (xxxxxxFLASH.ld)
324 | ...
325 |
326 | /* Sections */
327 | SECTIONS
328 | {
329 | ExtFlashSection :
330 | {
331 | *(ExtFlashSection ExtFlashSection.*)
332 | *(-gnu-linkonce.r.*)
333 | . = ALIGN(0x100);
334 | } >SPI_FLASH
335 | ...
336 | ```
337 |
338 | questo permette di spostare immagini selezionate verso la memoria flash esterna.
339 |
340 | Volendo/dovendo spostrare anche i fontverso la memoria flash esterna, copia anche la sezione FontFlashSection da Program_linker_include.txt ed incollala nello script linker in questo modo:
341 |
342 | ```sh
343 | (xxxxxxFLASH.ld)
344 | ...
345 |
346 | /* Sections */
347 | SECTIONS
348 | {
349 | ExtFlashSection :
350 | {
351 | *(ExtFlashSection ExtFlashSection.*)
352 | *(-gnu-linkonce.r.*)
353 | . = ALIGN(0x100);
354 | } >SPI_FLASH
355 |
356 | FontFlashSection :
357 | {
358 | *(FontFlashSection FontFlashSection.*)
359 | *(.gnu.linkonce.r.*)
360 | . = ALIGN(0x4);
361 | } >SPI_FLASH
362 |
363 | ...
364 | ```
365 |
366 |
367 | - on TouchGFX Designer:
368 |
369 |
370 | A questo punto, in TouchGFX Designer, e' possibile spostare singole immagini verso la memoria flash esterna assegnandole a ExtFlashSection:
371 |
372 |
373 |
Volendo usare la memoria flash esterna come supporto "di default" per archiviare le immagini, impostare il suo valore in Default Image Configuration:
374 |
375 |
376 |
377 |
378 |
379 | Se si spostano i font sulla memoria flash esterna OCCORRE SEMPRE impostare i font come "unmapped" in configurazione di TouchGFX Designer:
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 | ---
388 | >
389 | > ATTENZIONE
390 | >
391 | > La memoria SPI flash esterna is MOLTO più lenta che la flash interna al uC.
392 | > Tenere in considerazione di spostare sulla flash esterna solo ciò che occorre veramente a causa dei limiti della flash interna al uC.
393 |
394 | ---
395 |
396 |
397 | - aggiungere l'external loader a CubeIDE
398 |
399 | andare alla cartella del programma to the STM32CubeIDE
400 | (tasto destro del mouse sull'icona del programma e scegli "Apri percorso file")
401 | raggiunta la cartella del programma STM32CubeIDEandare in:
402 |
403 | cartella plugins
404 | cartella xxxxxx.externaltools.cubeprogrammer.xxxxx
405 | cartella tools
406 | cartella bin
407 | cartella ExternalLoader
408 |
409 | copiare qui l'external loader precedentemente creato seguendo following le istruzioni indicate sopra.
410 |
411 | - Configurare CubeIDE per usare l'external loader:
412 |
413 | in CubeIDE andare in: Project->Properties->Run/Debug Settings
414 | cliccare "Edit"
415 | selezionare "Debugger"
416 | sorrere la pagina fino a "External Loaders" e cliccare "Add"
417 | Selezionare, Nella lista degli external loader, il modulo da usare.
418 | Cliccare "OK" and "Apply" chidendo la finestra "Properties" aperta.
419 |
420 |
421 |
422 |
423 |
424 |
425 | [Torna alla home page](../.)
426 |
427 |
428 |
429 |
430 |
--------------------------------------------------------------------------------
/4_TouchGFX_mapping/Senzanome.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maudeve-it/W25Qxxx_SPI_FLASH_STM32/8e1037d806360028409563a864b750f303a2a2df/4_TouchGFX_mapping/Senzanome.png
--------------------------------------------------------------------------------
/4_TouchGFX_mapping/designer1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maudeve-it/W25Qxxx_SPI_FLASH_STM32/8e1037d806360028409563a864b750f303a2a2df/4_TouchGFX_mapping/designer1.png
--------------------------------------------------------------------------------
/4_TouchGFX_mapping/designer2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/maudeve-it/W25Qxxx_SPI_FLASH_STM32/8e1037d806360028409563a864b750f303a2a2df/4_TouchGFX_mapping/designer2.png
--------------------------------------------------------------------------------
/Dev_Inf.c:
--------------------------------------------------------------------------------
1 | /*********************************************
2 | * @file Dev_Inf.c
3 | * @brief modified by Mauro linking W25Qxxx library
4 | * @date: 01 august 2023
5 | * @version V.1.0.0
6 | *
7 | *********************************************
8 | * Creating an new External loader you have to
9 | * configure the first and the third field in
10 | * below struct. Setup also Loader_Src.c
11 | *********************************************/
12 |
13 | #include "main.h"
14 | #include "Dev_Inf.h"
15 | #include "z_flash_W25QXXX.h"
16 |
17 |
18 | /* This structure containes information used by ST-LINK Utility to program and erase the device */
19 | #if defined (__ICCARM__)
20 | __root struct StorageInfo const StorageInfo = {
21 | #else
22 | struct StorageInfo const StorageInfo = {
23 | #endif
24 | "W25Q80_BLACKPILL-F411_LED", // Device Name + version number
25 | SPI_FLASH, // Device Type (that's from Dev_Inf.h)
26 | 0x90000000, // Device Start Address
27 | EXT_FLASH_SIZE, // Device Size in Bytes (that's from Flash interface package)
28 | EXT_FLASH_PAGE_SIZE, // Programming Page Size (that's from Flash interface package)
29 | 0xFF, // Initial Content of Erased Memory
30 | // Specify Size and Address of Sectors (view example below)
31 | EXT_FLASH_PAGE_NUM, EXT_FLASH_SECTOR_SIZE, // (that's from Flash interface package)
32 | 0x00000000, 0x00000000,
33 | };
34 |
35 | /* Sector coding example
36 | A device with succives 16 Sectors of 1KBytes, 128 Sectors of 16 KBytes,
37 | 8 Sectors of 2KBytes and 16384 Sectors of 8KBytes
38 |
39 | 0x00000010, 0x00000400, // 16 Sectors of 1KBytes
40 | 0x00000080, 0x00004000, // 128 Sectors of 16 KBytes
41 | 0x00000008, 0x00000800, // 8 Sectors of 2KBytes
42 | 0x00004000, 0x00002000, // 16384 Sectors of 8KBytes
43 | 0x00000000, 0x00000000, // end
44 | */
45 |
--------------------------------------------------------------------------------
/Dev_Inf.h:
--------------------------------------------------------------------------------
1 | /*********************************************
2 | * @file Dev_Inf.h
3 | * @brief modified by Mauro linking W25Qxxx library
4 | * @date: 01 august 2023
5 | * @version V.1.0.0
6 | *
7 | *********************************************
8 | * you shouldn't need to change anything here
9 | *********************************************/
10 |
11 | #define MCU_FLASH 1
12 | #define NAND_FLASH 2
13 | #define NOR_FLASH 3
14 | #define SRAM 4
15 | #define PSRAM 5
16 | #define PC_CARD 6
17 | #define SPI_FLASH 7
18 | #define I2C_FLASH 8
19 | #define SDRAM 9
20 | #define I2C_EEPROM 10
21 |
22 | #define SECTOR_NUM 10 // Max Number of Sector types
23 |
24 | struct DeviceSectors
25 | {
26 | unsigned long SectorNum; // Number of Sectors
27 | unsigned long SectorSize; // Sector Size in Bytes
28 | };
29 |
30 | struct StorageInfo
31 | {
32 | char DeviceName[100]; // Device Name and Description
33 | unsigned short DeviceType; // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...
34 | unsigned long DeviceStartAddress; // Default Device Start Address
35 | unsigned long DeviceSize; // Total Size of Device
36 | unsigned long PageSize; // Programming Page Size
37 | unsigned char EraseValue; // Content of Erased Memory
38 | struct DeviceSectors sectors[SECTOR_NUM];
39 | };
40 |
--------------------------------------------------------------------------------
/EL_linker.ld:
--------------------------------------------------------------------------------
1 | /*
2 | *****************************************************************************
3 | ** File : linker.ld
4 | ** modified by Mauro linking W25Qxxx library for an EL
5 | ** Date : 01 august 2023
6 | ** Version : V.1.0.0
7 | **
8 | **
9 | ** Just change the ram length
10 | ** not tested on H7
11 | **
12 | **
13 | *****************************************************************************
14 | */
15 |
16 | /* Entry Point */
17 | ENTRY(Init)
18 |
19 | /* Generate 2 segment for Loader code and device info */
20 | PHDRS {Loader PT_LOAD ; SgInfo PT_LOAD ; }
21 |
22 | /* Highest address of the user mode stack */
23 | _estack = ORIGIN(RAM) + LENGTH(RAM); /* end of RAM */
24 | /* Generate a link error if heap and stack don't fit into RAM */
25 | _Min_Heap_Size = 0x200; /* required amount of heap */
26 | _Min_Stack_Size = 0x400; /* required amount of stack */
27 |
28 | /* Specify the memory areas */
29 | MEMORY
30 | {
31 | RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 128K
32 | }
33 |
34 | /* Define output sections */
35 | SECTIONS
36 | {
37 | /* The startup code goes first into FLASH */
38 |
39 | .isr_vector :
40 | {
41 | . = . + 0x1FC;
42 | . = ALIGN(4);
43 | KEEP(*(.isr_vector)) /* Startup code */
44 | . = ALIGN(4);
45 | } >RAM :Loader
46 |
47 |
48 |
49 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >RAM
50 | .ARM : {
51 | __exidx_start = .;
52 | *(.ARM.exidx*)
53 | __exidx_end = .;
54 | } >RAM :Loader
55 |
56 | .preinit_array :
57 | {
58 | PROVIDE_HIDDEN (__preinit_array_start = .);
59 | KEEP (*(.preinit_array*))
60 | PROVIDE_HIDDEN (__preinit_array_end = .);
61 | } >RAM :Loader
62 |
63 | .init_array :
64 | {
65 | PROVIDE_HIDDEN (__init_array_start = .);
66 | KEEP (*(SORT(.init_array.*)))
67 | KEEP (*(.init_array*))
68 | PROVIDE_HIDDEN (__init_array_end = .);
69 | } >RAM :Loader
70 |
71 | .fini_array :
72 | {
73 | PROVIDE_HIDDEN (__fini_array_start = .);
74 | KEEP (*(SORT(.fini_array.*)))
75 | KEEP (*(.fini_array*))
76 | PROVIDE_HIDDEN (__fini_array_end = .);
77 | } >RAM :Loader
78 |
79 | /* used by the startup to initialize data */
80 | _sidata = LOADADDR(.data);
81 |
82 | /* Initialized data sections goes into RAM, load LMA copy after code */
83 | .data :
84 | {
85 | . = ALIGN(4);
86 | _sdata = .; /* create a global symbol at data start */
87 | *(.data) /* .data sections */
88 | *(.data*) /* .data* sections */
89 |
90 | . = ALIGN(4);
91 | _edata = .; /* define a global symbol at data end */
92 | } >RAM :Loader
93 |
94 |
95 | /* Uninitialized data section */
96 | . = ALIGN(4);
97 | .bss :
98 | {
99 | /* This is used by the startup in order to initialize the .bss secion */
100 | _sbss = .; /* define a global symbol at bss start */
101 | __bss_start__ = _sbss;
102 | *(.bss)
103 | *(.bss*)
104 | *(COMMON)
105 |
106 | . = ALIGN(4);
107 | _ebss = .; /* define a global symbol at bss end */
108 | __bss_end__ = _ebss;
109 | } >RAM :Loader
110 |
111 | /* The program code and other data goes into FLASH */
112 | .text :
113 | {
114 | . = ALIGN(4);
115 | *(.text) /* .text sections (code) */
116 | *(.text*) /* .text* sections (code) */
117 | /* *(.glue_7) */ /* glue arm to thumb code */
118 | /* *(.glue_7t) */ /* glue thumb to arm code */
119 | /* *(.eh_frame) */
120 |
121 | KEEP (*(.init))
122 | KEEP (*(.fini))
123 |
124 | . = ALIGN(4);
125 | _etext = .; /* define a global symbols at end of code */
126 | } >RAM :Loader
127 |
128 | .Dev_info :
129 | {
130 | KEEP(*Dev_Inf.o ( .rodata* ))
131 | } :SgInfo
132 |
133 |
134 | /* Constant data goes into FLASH */
135 | .rodata :
136 | {
137 | . = ALIGN(4);
138 | *(.rodata) /* .rodata sections (constants, strings, etc.) */
139 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */
140 | . = ALIGN(4);
141 | } >RAM :Loader
142 |
143 |
144 | /* User_heap_stack section, used to check that there is enough RAM left */
145 | ._user_heap_stack :
146 | {
147 | . = ALIGN(4);
148 | PROVIDE ( end = . );
149 | PROVIDE ( _end = . );
150 | . = . + _Min_Heap_Size;
151 | . = . + _Min_Stack_Size;
152 | . = ALIGN(4);
153 | } >RAM :Loader
154 |
155 |
156 |
157 |
158 | .ARM.attributes 0 : { *(.ARM.attributes) }
159 | }
160 |
161 |
162 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Mauro De Vecchi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Loader_Src.c:
--------------------------------------------------------------------------------
1 | /*********************************************
2 | * @file Dev_Inf.c
3 | * @brief modified by Mauro linking W25Qxxx library
4 | * @date: 01 august 2023
5 | * @version V.1.0.0
6 | *
7 | *********************************************
8 | * Creating an new External loader you have to
9 | * follow the below step 1 to 3 and set the
10 | * first field in the struct of Dev_Inf.c
11 | *********************************************/
12 |
13 | #include "main.h"
14 | #include "spi.h"
15 | #include "gpio.h"
16 | #include "z_flash_W25QXXX.h"
17 |
18 | #define EXT_FLASH_ADDR_MASK 0x0FFFFFFF
19 |
20 | /* STEP 1 *************************************
21 | * remove comment in below #define if you have
22 | * a led for the External Loader.
23 | * On CubeMX, led's pin label must be LED
24 | * ********************************************/
25 | #define IS_LED
26 |
27 | /* STEP 2 *************************************
28 | * change the below #define assigning Pin name
29 | * and port you used for led,
30 | * specify also pin level to turn on led
31 | * ********************************************/
32 | #ifdef IS_LED
33 | #define LED_PIN_ON GPIO_PIN_RESET //this is the GPIO level turning on led
34 | #endif //IS_LED
35 |
36 | /* STEP 3 *************************************
37 | * change the init function to call as per SPI
38 | * port used (hint: that's the function
39 | * available in spi.c, check it)
40 | * ********************************************/
41 | void LOC_SPI_Init(){
42 | MX_SPI1_Init(); // !!! this line needs to be aligned to the SPI port used! Check this function: it is defined in spi.c !!!
43 | }
44 |
45 |
46 | extern void SystemClock_Config(void);
47 |
48 | /**********************************************
49 | * roughly waints for "Delay" ms before return
50 | * set uC clock speed. Tested on M4, maybe has
51 | * to change using other uC.
52 | * ********************************************/
53 | void LOC_Delay(uint32_t Delay) {
54 | const uint32_t clock=100; // uC MHz
55 | volatile uint32_t delay1; //
56 | volatile uint32_t multiplier=35; // that's "converting" Delay value into time spent in the below loop
57 | volatile uint32_t k;
58 | delay1=Delay;
59 | for (k=0; (k<(delay1*clock*multiplier)); k++) {};
60 | }
61 |
62 |
63 |
64 | uint32_t HAL_GetTick(void) {
65 | return 1;
66 | }
67 |
68 |
69 | void HAL_Delay(uint32_t Delay) {
70 | LOC_Delay(Delay);
71 | }
72 |
73 |
74 | HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
75 | return HAL_OK;
76 | }
77 |
78 |
79 |
80 | void LOC_LedOn(){
81 | #ifdef IS_LED
82 | HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, LED_PIN_ON);
83 | #endif //IS_LED
84 |
85 | }
86 |
87 | void LOC_LedOff(){
88 | #ifdef IS_LED
89 | HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, !LED_PIN_ON);
90 | #endif //IS_LED
91 | }
92 |
93 |
94 |
95 | int Init (void){
96 |
97 | *(uint32_t*)0xE000EDF0 = 0xA05F0000; //enable interrupts in debug
98 |
99 | SystemInit();
100 |
101 | /* ADAPTATION TO THE DEVICE
102 | *
103 | * change VTOR setting for H7 device
104 | * SCB->VTOR = 0x24000000 | 0x200;
105 | *
106 | * change VTOR setting for other devices
107 | * SCB->VTOR = 0x20000000 | 0x200;
108 | *
109 | */
110 |
111 | SCB->VTOR = 0x20000000 | 0x200;
112 |
113 | __set_PRIMASK(0); //enable interrupts
114 |
115 | HAL_Init();
116 | uint8_t result;
117 |
118 | SystemClock_Config();
119 |
120 | MX_GPIO_Init();
121 | LOC_SPI_Init();
122 |
123 | LOC_LedOn();
124 | result = Flash_Init();
125 | LOC_LedOff();
126 |
127 | __set_PRIMASK(1); //disable interrupts
128 |
129 | return result;
130 |
131 | }
132 |
133 |
134 |
135 | /**
136 | * Description :
137 | * Read data from the device
138 | * Inputs :
139 | * Address : Write location
140 | * Size : Length in bytes
141 | * buffer : Address where to get the data to write
142 | * outputs :
143 | * R0 : "1" : Operation succeeded
144 | * "0" : Operation failure
145 | * Note: Mandatory for all types except SRAM and PSRAM
146 | */
147 | int Read (uint32_t Address, uint32_t Size, uint8_t* buffer){
148 | __set_PRIMASK(0); //enable interrupts
149 | Address = Address & EXT_FLASH_ADDR_MASK;
150 | LOC_LedOn();
151 | Flash_Read(Address, buffer, Size);
152 | LOC_LedOff();
153 | __set_PRIMASK(1); //disable interrupts
154 | return 1;
155 | }
156 |
157 |
158 |
159 | /**
160 | * Description :
161 | * Write data to the device
162 | * Inputs :
163 | * Address : Write location
164 | * Size : Length in bytes
165 | * buffer : Address where to get the data to write
166 | * outputs :
167 | * R0 : "1" : Operation succeeded
168 | * "0" : Operation failure
169 | * Note: Mandatory for all types except SRAM and PSRAM
170 | */
171 | int Write (uint32_t Address, uint32_t Size, uint8_t* buffer){
172 | __set_PRIMASK(0); //enable interrupts
173 | Address = Address & EXT_FLASH_ADDR_MASK;
174 | LOC_LedOn();
175 | Flash_Write(Address, buffer, Size);
176 | LOC_LedOff();
177 | __set_PRIMASK(1); //disable interrupts
178 | return 1;
179 | }
180 |
181 |
182 |
183 |
184 | /**
185 | * Description :
186 | * Erase the full chip
187 | * Inputs :
188 | * None
189 | * outputs :
190 | * R0 : "1" : Operation succeeded
191 | * "0" : Operation failure
192 | * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
193 | */
194 | int MassErase (void){
195 | __set_PRIMASK(0); //enable interrupts
196 | LOC_LedOn();
197 | Flash_ChipErase();
198 | LOC_LedOff();
199 | __set_PRIMASK(1); //disable interrupts
200 | return 1;
201 | }
202 |
203 |
204 |
205 | /**
206 | * Description :
207 | * Erase a full sector in the device
208 | * Inputs :
209 | * SectrorAddress : Start of sector
210 | * Size : Size (in WORD)
211 | * InitVal : Initial CRC value
212 | * outputs :
213 | * R0 : "1" : Operation succeeded
214 | * "0" : Operation failure
215 | * Note: Not Mandatory for SRAM PSRAM and NOR_FLASH
216 | */
217 | int SectorErase (uint32_t EraseStartAddress ,uint32_t EraseEndAddress) {
218 | EraseStartAddress = EraseStartAddress & EXT_FLASH_ADDR_MASK;
219 | EraseEndAddress = EraseEndAddress & EXT_FLASH_ADDR_MASK;
220 |
221 | __set_PRIMASK(0); //enable interrupts
222 | EraseStartAddress = (EraseStartAddress - (EraseStartAddress % EXT_FLASH_BLOCK_SIZE));
223 | while (EraseEndAddress>=EraseStartAddress) {
224 | LOC_LedOn();
225 | Flash_BErase64k(EraseStartAddress);
226 | LOC_LedOff();
227 | EraseStartAddress += EXT_FLASH_BLOCK_SIZE;
228 | }
229 | __set_PRIMASK(1); //disable interrupts
230 | return 1;
231 | }
232 |
233 |
234 |
235 | /**
236 | * Description :
237 | * Calculates checksum value of the memory zone
238 | * Inputs :
239 | * StartAddress : Flash start address
240 | * Size : Size (in WORD)
241 | * InitVal : Initial CRC value
242 | * outputs :
243 | * R0 : Checksum value
244 | * Note - Optional for all types of device
245 | * NOTE - keeping original ST algorithm: not verified and optimized
246 | */
247 | uint32_t CheckSum(uint32_t StartAddress, uint32_t Size, uint32_t InitVal) {
248 | uint8_t missalignementAddress = StartAddress%4;
249 | uint8_t missalignementSize = Size ;
250 | int cnt;
251 | uint32_t Val;
252 | //uint8_t value;
253 |
254 | StartAddress-=StartAddress%4;
255 | Size += (Size%4==0)?0:4-(Size%4);
256 |
257 | for(cnt=0; cnt>(8*k) & 0xff);
267 | }
268 | missalignementAddress=0;
269 | }
270 | else if((Size-missalignementSize)%4 && (Size-cnt) <=4)
271 | {
272 | for (uint8_t k=(Size-missalignementSize); k<=3;k++){
273 | InitVal += (uint8_t) (Val>>(8*(k-1)) & 0xff);
274 | }
275 | missalignementSize=2 * missalignementSize - Size;
276 | }
277 | else
278 | {
279 | for (uint8_t k=0; k<=3;k++){
280 | InitVal += (uint8_t) (Val>>(8*k) & 0xff);
281 | }
282 | }
283 | StartAddress+=4;
284 | }
285 |
286 | return (InitVal);
287 | }
288 |
289 |
290 | /**
291 | * Description :
292 | * Verify flash memory with RAM buffer and calculates checksum value of
293 | * the programmed memory
294 | * Inputs :
295 | * FlashAddr : Flash address
296 | * RAMBufferAddr : RAM buffer address
297 | * Size : Size (in WORD)
298 | * InitVal : Initial CRC value
299 | * outputs :
300 | * R0 : Operation failed (address of failure)
301 | * R1 : Checksum value
302 | * Note: Optional for all types of device
303 | * NOTE - keeping original ST algorithm: not verified and optimized
304 | */
305 |
306 | uint64_t Verify (uint32_t MemoryAddr, uint32_t RAMBufferAddr, uint32_t Size, uint32_t missalignement){
307 | #define BUF_SIZE 2
308 | uint32_t InitVal = 0;
309 | uint32_t VerifiedData = 0;
310 | // uint8_t TmpBuffer = 0x00;
311 | uint64_t checksum;
312 | Size*=4;
313 | uint8_t Buffer[BUF_SIZE];
314 | uint32_t LocAddr = MemoryAddr & EXT_FLASH_ADDR_MASK;
315 | uint32_t posBuf;
316 |
317 | checksum = CheckSum((uint32_t)LocAddr + (missalignement & 0xf), Size - ((missalignement >> 16) & 0xF), InitVal);
318 |
319 | while (Size>VerifiedData)
320 | {
321 | LOC_LedOn();
322 | Flash_Read(MemoryAddr+VerifiedData, Buffer, BUF_SIZE);
323 | LOC_LedOff();
324 |
325 | posBuf=0;
326 | while ((Size>VerifiedData) && (posBuf<1024)) {
327 | if (Buffer[posBuf] != *((uint8_t*)RAMBufferAddr+VerifiedData))
328 | return ((checksum<<32) + MemoryAddr+VerifiedData);
329 | posBuf++;
330 | VerifiedData++;
331 | }
332 | }
333 |
334 | return (checksum<<32);
335 | }
336 |
--------------------------------------------------------------------------------
/Post-Build command.txt:
--------------------------------------------------------------------------------
1 | cmd.exe /C copy /Y "${BuildArtifactFileBaseName}.elf" "..\${BuildArtifactFileBaseName}.stldr"
--------------------------------------------------------------------------------
/Program_linker_include.txt:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SPI_FLASH (r) : ORIGIN = 0x90000000, LENGTH = 1M
5 |
6 |
7 |
8 |
9 |
10 | ExtFlashSection :
11 | {
12 | *(ExtFlashSection ExtFlashSection.*)
13 | *(-gnu-linkonce.r.*)
14 | . = ALIGN(0x100);
15 | } >SPI_FLASH
16 |
17 | FontFlashSection :
18 | {
19 | *(FontFlashSection FontFlashSection.*)
20 | *(-gnu-linkonce.r.*)
21 | . = ALIGN(0x100);
22 | } >SPI_FLASH
23 |
24 |
25 | /* attributes for variables declaration */
26 | /* __attribute__((section("ExtFlashSection"))) __attribute__((aligned(4))) */
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | **Piu' sotto, al termine della lingua inglese trovi il testo in italiano. **_
2 | _**
Below the English text you'll find the Italian version**_
3 |
4 |
5 |
6 | # A W25QXXX SPI FLASH memory library for STM32:
7 | ## including support functions for External Loaders developing
8 | ## and for the flash memory integration on TouchGFX projects
9 |
10 | In this GitHub page, you'll find a library for STM32 (HAL) handling Winbond SPI Flash memories (W25Qxxx).
11 | Library includes functions you need to create External Loaders for STM32CubeProgrammer and STM32CubeIDE.
12 | Inside library you'll find also functions, that TouchGFX needs to handle an external flash memory, reading font and images via a direct transfer flash->display (without needing a cache in RAM).
13 | So, you can use this software for 4 distinct purposes:
14 |
15 | - handling an external W25Q flash directly reading and writing over it
16 | - create an External Loader to program flash memory via CubeProgrammer
17 | - add the created external loader to a CubeIde project in order to directly flash the external chip while creating projects (as same as the uC internal flash memory)
18 | - add an external flash to a TouchGFX project. TouchGFX provides tools making an external flash, handled via an external loader, absolutely transparent to the project.
19 |
20 |
21 |
22 | ---
23 |
24 |
25 |
26 | ### GitHub Folders
27 |
28 | This guide is divided in 4 chapters following the above list.
29 |
30 | - [handling an external W25Q flash with this library](./1_Flash_library)
31 | - [creating and using an External Loader for the SPI flash](./2_ExternalLoader)
32 | - [mapping an external SPI flash on CubeIDE projects](./3_CubeIDE_mapping)
33 | - [using an external SPI flash in TouchGFX projects](./4_TouchGFX_mapping)
34 |
35 |
36 |
37 | ---
38 |
39 |
40 |
41 | Current version of library handles SPI communication via:
42 |
43 | - standard SPI: W25Q handles QuadSPI but this library communicate using Standard SPI
44 | - polling mode: no "interrupt mode" nor "DMA mode"
45 |
46 | See youtube videos to know advantages of this approach.
47 |
48 | ### Helping videos
49 | Here you can find video documentation about these topics:
50 |
51 | ||||
52 | |---|---|---|
53 | |Part one| "how to" use library, "how to" develop an EL (specific video for this repository), "how to" install an EL into CubeProgrammer|https://youtu.be/KlG2doCkREM|
54 | |Part two| "how to" create a low-RAM-demanding EL|https://youtu.be/zv0w_vhTTTo|
55 | |Part three| "how to" use the SPI flash in TouchGFX projects, "how to" install an EL into CubeIDE|https://youtu.be/PO_2ZE1NQF0|
56 | |Part four| "how to" map an external SPI flash memory in a CubeIDE project|https://youtu.be/K-7X8WKNu7c|
57 |
58 |
59 |
60 | ### Sources
61 | The sofware you find on this page, the page content and above videos, were developed upon this documentation:
62 |
63 | |||
64 | |---|---|
65 | |Winbond W25Qxxx web page|https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash|
66 | |Jedec SFDP standard (requires registration)|https://www.jedec.org/document_search/field_doc_type/148?search_api_views_fulltext=JESD216|
67 | |STM Mooc - External QSPI loader how to|https://www.st.com/content/st_com/en/support/learning/stm32-education/stm32-moocs/external_QSPI_loader.html|
68 | |STM repository of external flash loaders|https://github.com/STMicroelectronics/stm32-external-loader|
69 | |STM32CubeProgrammer documentation|https://www.st.com/resource/en/user_manual/um2237-stm32cubeprogrammer-software-description-stmicroelectronics.pdf|
70 | |STM32 Graphics: external QSPI flash in TouchGFX|https://youtu.be/RMgVo_uCTbw|
71 | |TouchGFX: Using Serial Flash for images and fonts|https://support.touchgfx.com/docs/development/scenarios/using-serial-flash|
72 |
73 |
74 |
75 |
76 | ### Please note
77 | ---
78 | > The software, schemes and PCB designs in this set of repositories are provided just for
79 | > illustrative purposes, sharing the personal experience on this project.
80 | > The software and design artifacts are provided as-is without any mantainence and without
81 | > warranty of any kind. There is no guarantee of fitness for any particular application,
82 | > and no guarantee of completeness and correctness.
83 | > By downloading and using these materials the user accepts all risks associated with the
84 | > run of the software and construction and use of the circuits, boards and devices described.
85 |
86 | ---
87 |
88 | ---
89 |
90 |
91 |
92 |
93 |
94 | # Una libreria sulle memorie SPI Flash W25QXXXX per STM32:
95 | ## incluse le funzioni di supporto per creare External Loader,
96 | ## e per integrare la memoria flash in progetti TouchGFX
97 |
98 | In questa pagina GitHub trovi una libreria di funzioni per STM32 (HAL) per la gestione di memorie SPI Flash Winbond (W25Qxxxx).
99 | La libreria include anche le funzioni richieste per generare External Loader per STM32CubeProgrammer o per STM32CubeIDE.
100 | Nella libreria sono presenti anche le funzioni richeste per integrare una memoria Flash esterna in progetti TouchGFX per memorizzare font ed immagini con trasferimento diretto flash->display (non è richiesta cache in RAM).
101 | Puoi utilizzare quindi questo software per 4 scopi distinti:
102 |
103 | - gestire una memoria flash esterna W25Q, leggendo o scrivendo direttamente sopra
104 | - creare un External Loader per programmare la memoria flash via CubeProgrammer
105 | - aggiungere l'external loader creato ad un progetto CubeIde allo scopo di programmare direttamente il chip esterno durante la creazione del progetto (come fatto per la flash interna al uC)
106 | - aggiungere la memoria flash esterna ad un progetto TouchGFX. TouchGFX fornisce strumenti per rendere la memoria flash, gestita tramite un external loader, totalmente trasparente al progetto.
107 |
108 |
109 |
110 | ---
111 |
112 |
113 | ### Folder GitHub
114 |
115 | Questa guida è suddivisa in 4 capitoli seguendo lo schema sopra.
116 | - [gestire una memoria flash W25Q attraverso questa libreria](./1_Flash_library)
117 | - [creare and usare an External Loader per le memorie flash SPI](./2_ExternalLoader)
118 | - [mappare una flash SPI esterna sui progetti CubeIDE](./3_CubeIDE_mapping)
119 | - [usare una flash SPI esterna in progetti TouchGFX](./4_TouchGFX_mapping)
120 |
121 |
122 |
123 | ---
124 |
125 |
126 |
127 | La versione attuale della libreria gestisce la comunicazione SPI via:
128 |
129 | - standard SPI: W25Q gestisce QuadSPI ma questa libreria usa Standard SPI
130 | - polling mode: non viene usato "interrupt mode" nè "DMA mode"
131 |
132 | Vedi i video youtube sotto per conoscere i vantaggi di questo approccio.
133 |
134 |
135 | ### Supporto video
136 | Qui puoi trovare video relativi ai temi trattati in questa pagina
137 |
138 | ||||
139 | |---|---|---|
140 | |Prima parte|"how to" - come usare la libreria (video specifico per questa repository), come creare un EL, come installare un EL in Cubeprogrammer|https://youtu.be/KlG2doCkREM|
141 | |Seconda parte|"how to" - creare EL a basso consumo di RAM |https://youtu.be/zv0w_vhTTTo|
142 | |Terza parte|"how to" - come usare la memoria FLash SPI in un progetto TouchGFX, come installare un EL in CubeIDE per programmare la memoria flash in un progetto TouchGFX|https://youtu.be/PO_2ZE1NQF0|
143 | |Quarta parte|"how to" - come mappare una memoria flash SPI in un progetto CubeIDE|https://youtu.be/K-7X8WKNu7c|
144 |
145 | ### Referimenti
146 | Il software, questa pagina ed i video sopra sono stati sviluppati sulla base di questa documentazione:
147 |
148 | |||
149 | |---|---|
150 | |Winbond W25Qxxx web page|https://www.winbond.com/hq/product/code-storage-flash-memory/serial-nor-flash|
151 | |Jedec SFDP standard (requires registration)|https://www.jedec.org/document_search/field_doc_type/148?search_api_views_fulltext=JESD216|
152 | |STM Mooc - External QSPI loader how to|https://www.st.com/content/st_com/en/support/learning/stm32-education/stm32-moocs/external_QSPI_loader.html|
153 | |STM repository of external flash loaders|https://github.com/STMicroelectronics/stm32-external-loader|
154 | |STM32CubeProgrammer documentation|https://www.st.com/resource/en/user_manual/um2237-stm32cubeprogrammer-software-description-stmicroelectronics.pdf|
155 | |STM32 Graphics: external QSPI flash in TouchGFX|https://youtu.be/RMgVo_uCTbw|
156 | |TouchGFX: Using Serial Flash for images and fonts|https://support.touchgfx.com/docs/development/scenarios/using-serial-flash|
157 |
158 |
159 |
160 |
161 |
162 | ### Nota bene
163 |
164 | ---
165 |
166 | > Il software e gli schemi di progetto come i layout PCB in questa serie di repository
167 | > sono messe a disposizione con puro intento illustrativo e di condivisione dell'esperienza fatta.
168 | > Il software e gli elementi del progetto sono messi a disposizione "allo stato in cui sono"
169 | > senza impegno di manutenzione e senza garanzie di alcun tipo. Piu' esplicitamente, non c'e' garanzia di
170 | > adeguatezza a soddisfare specifiche esigenze, ne' di completezza o correttezza di alcuna parte.
171 | > Scaricando ed utilizzando questo materiale l'utilizzatore accetta il rischio associato all'esecuzione del programma e alla
172 | > realizzazione ed all'utilizzo del circuito e dei componenti descritti in questo archivio.
173 |
174 | ---
175 |
176 |
177 |
--------------------------------------------------------------------------------
/z_flash_W25QXXX.c:
--------------------------------------------------------------------------------
1 | /*********************************************
2 | * @file Z_FLASH_W25QXXX.c
3 | * @author mauro
4 | * @date: 01 august 2023
5 | * @version V.1.0.0
6 | *
7 | *********************************************
8 | * this version of library uses just polling
9 | * mode transmission
10 | * this version of library uses standard SPI
11 | *********************************************
12 | * it needs Z_FLASH_W25QXXX.h configuration
13 | *********************************************/
14 |
15 |
16 | #include "main.h"
17 | #include "z_flash_W25QXXX.h"
18 |
19 | #define SPI_IS_BUSY (HAL_GPIO_ReadPin(FLASH_CS_GPIO_Port, FLASH_CS_Pin)==GPIO_PIN_RESET)
20 |
21 | extern SPI_HandleTypeDef FLASH_SPI_PORT;
22 |
23 |
24 |
25 |
26 | /******************************************
27 | * @brief enable Flash SPI port
28 | * any command on Flash, any transmission, must start with
29 | * a Chip Select and terminate with a Chip Unselect.
30 | * So testing CS pin let understand if a
31 | * transmission is still running:
32 | * before selecting chip a test over the same CS let
33 | * understand if previous transmission terminated
34 | ******************************************/
35 | void Flash_Select(void) {
36 | while (SPI_IS_BUSY) {}
37 | HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_RESET);
38 | }
39 |
40 |
41 |
42 |
43 | /******************************************
44 | * @brief disable Flash SPI
45 | * verifying that there is no a running data transfer
46 | ******************************************/
47 | void Flash_UnSelect(void) {
48 | // CS pin must be low (selected flash) until previous transmission is completed
49 | #ifdef EXT_FLASH_SPI_POLLING_MODE
50 | HAL_GPIO_WritePin(FLASH_CS_GPIO_Port, FLASH_CS_Pin, GPIO_PIN_SET); //unselect
51 | #endif // FLASH_SPI_POLLING_MODE
52 | }
53 |
54 |
55 |
56 |
57 | void Flash_Receive(uint8_t* data, uint16_t dataSize){
58 | HAL_SPI_Receive (&FLASH_SPI_PORT , data, dataSize, HAL_MAX_DELAY);
59 | }
60 |
61 |
62 |
63 | /**********************************************************************
64 | * @BRIEF engages SPI port tranferring data to Flash
65 | * just using Polling mode (TouchGFX requires this function)
66 | * @PARAM data buffer data to send
67 | * dataSize number of bytes in "data" to be sent
68 | *********************************************************************/
69 | void Flash_Polling_Transmit(uint8_t* data, uint16_t dataSize){
70 | HAL_SPI_Transmit(&FLASH_SPI_PORT , data, dataSize, HAL_MAX_DELAY);
71 | }
72 |
73 |
74 |
75 |
76 | /**************************
77 | * @BRIEF engages SPI port tranferring data to Flash
78 | * Macro parameter DISPL_DMA_CUTOFF defines if transmission is Poling or DMA
79 | * you need to set this macro even using TouchGFX (having its own configuration parameter:
80 | * set DISPL_DMA_CUTOFF and CubeMX parameter to the same value)
81 | * @PARAM data buffer data to send
82 | * dataSize number of bytes in "data" to be sent
83 | **************************/
84 | void Flash_Transmit(uint8_t* data, uint16_t dataSize){
85 | #ifndef EXT_FLASH_SPI_POLLING_MODE
86 | if (dataSize> 16) & 0xFF;
136 | buffer[2] = (addr >> 8) & 0xFF;
137 | buffer[3] = addr & 0xFF;
138 | buffer[4] = W25_DUMMY;
139 | Flash_Select();
140 | Flash_Transmit(buffer, (FLASH_READ_COMMAND == W25_READ ? 4 : 5)); // "normal/slow" read command doesn't need sending dummy byte
141 |
142 | // dataSize is 32 bit, spi_receive handles 16bit transfers, so I have to loop...
143 | while (dataSize) {
144 | data_to_transfer = ((dataSize>0xFFFF) ? 0xFFFF : (uint16_t)dataSize);
145 | Flash_Receive(data, data_to_transfer);
146 | data+=data_to_transfer;
147 | dataSize-=data_to_transfer;
148 | }
149 | Flash_UnSelect();
150 | }
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | /***********************************************************************
160 | * @BRIEF it writes into a single FLASH page
161 | * function doesn't check for the BUSY flag in SR1
162 | * function doesn't check for the EEPROM writing enabled
163 | * function doesn't wait for the writing complete
164 | * function doesn't check for the EEPROM page boundary override
165 | * @PARAM addr EEPROM address to start writing
166 | * data buffer containing data to write into EEPROM
167 | * dataSize number of bytes to write
168 | ***********************************************************************/
169 | void Flash_SimpleWriteAPage(uint32_t addr, uint8_t* data, uint16_t dataSize){
170 | uint8_t buffer[4];
171 | buffer[0] = W25_PAGE_P;
172 | buffer[1] = (addr >> 16) & 0xFF;
173 | buffer[2] = (addr >> 8) & 0xFF;
174 | buffer[3] = addr & 0xFF;
175 | Flash_Select();
176 | Flash_Transmit(buffer, 4);
177 | Flash_Transmit(data, dataSize);
178 | Flash_UnSelect();
179 | }
180 |
181 |
182 |
183 |
184 |
185 |
186 | /***********************************************************************
187 | * @BRIEF function writing into EEPROM
188 | * Handling "write enable" commands
189 | * It splits (if needed) received data into the single pages,
190 | * lounching writing sessions for each page
191 | * and waiting the writing complete each time
192 | * @PARAM addr EEPROM address to start writing
193 | * data buffer containing data to write into EEPROM
194 | * dataSize number of bytes to write
195 | ***********************************************************************/
196 | void Flash_Write(uint32_t addr, uint8_t* data, uint32_t dataSize){
197 | uint8_t buffer[4];
198 | uint16_t quota;
199 | uint32_t inpage_addr;
200 |
201 | if (dataSize==0)
202 | return;
203 |
204 | // quota is the data size trasferred until now
205 | quota=0;
206 |
207 | // define the starting write position inside the first Flash page to write...
208 | inpage_addr=addr & (EXT_FLASH_PAGE_SIZE-1);
209 |
210 | // ... so I can detect if more than 1 Flash page has still to be written
211 | while ((dataSize-quota+inpage_addr)>EXT_FLASH_PAGE_SIZE){
212 | //loop here inside, until more than 1 Flash page...
213 |
214 | Flash_Select();
215 | buffer[0] = W25_W_ENABLE;
216 | Flash_Transmit(buffer, 1);
217 | Flash_UnSelect();
218 | Flash_SimpleWriteAPage(addr+quota,data+quota,(EXT_FLASH_PAGE_SIZE-inpage_addr));
219 | quota+=(EXT_FLASH_PAGE_SIZE-inpage_addr);
220 | // having aligned data to page border on the first writing
221 | // next writings start from 0 position inside a page
222 | inpage_addr=0;
223 | Flash_WaitForWritingComplete();
224 | }
225 | // now just the final Flash page...
226 | if (dataSize-quota) {
227 | Flash_Select();
228 | buffer[0] = W25_W_ENABLE;
229 | Flash_Transmit(buffer, 1);
230 | Flash_UnSelect();
231 | Flash_SimpleWriteAPage(addr+quota,data+quota,dataSize-quota);
232 | Flash_WaitForWritingComplete();
233 | }
234 | }
235 |
236 |
237 |
238 |
239 |
240 | /**********************************
241 | * @BRIEF Erase to 0XFF all bytes in a 4k block
242 | * 4k block bounary is 0x1000, that means:
243 | * 0x1000, 0x2000, 0x3000, ...
244 | * waiting the writing complete in each page
245 | * @PARAM addr starting erase address
246 | * (it must be a 4k sector boundary)
247 | *********************************/
248 | void Flash_SErase4k(uint32_t addr){
249 | uint8_t buffer[4];
250 | Flash_Select();
251 | buffer[0] = W25_W_ENABLE;
252 | Flash_Transmit(buffer, 1);
253 | Flash_UnSelect();
254 |
255 | buffer[0] = W25_S_ERASE4K;
256 | buffer[1] = (addr >> 16) & 0xFF;
257 | buffer[2] = (addr >> 8) & 0xFF;
258 | buffer[3] = addr & 0xFF;
259 | Flash_Select();
260 | Flash_Transmit(buffer, 4);
261 | Flash_UnSelect();
262 | Flash_WaitForWritingComplete();
263 | }
264 |
265 |
266 |
267 |
268 | /**********************************
269 | * @BRIEF Erase to 0XFF all bytes in a 32k block
270 | * 32k block bounary is 0x08000, that means:
271 | * 0x008000, 0x010000, 0x018000, ...
272 | * waiting the writing complete in each page
273 | * @PARAM addr starting erase address
274 | * (it must be a 32k block boundary)
275 | *********************************/
276 | void Flash_BErase32k(uint32_t addr){
277 | uint8_t buffer[4];
278 | Flash_Select();
279 | buffer[0] = W25_W_ENABLE;
280 | Flash_Transmit(buffer, 1);
281 | Flash_UnSelect();
282 |
283 | buffer[0] = W25_B_ERASE32K;
284 | buffer[1] = (addr >> 16) & 0xFF;
285 | buffer[2] = (addr >> 8) & 0xFF;
286 | buffer[3] = addr & 0xFF;
287 | Flash_Select();
288 | Flash_Transmit(buffer, 4);
289 | Flash_UnSelect();
290 | Flash_WaitForWritingComplete();
291 | }
292 |
293 |
294 |
295 |
296 |
297 | /**********************************
298 | * @BRIEF Erase to 0XFF all bytes in a 64k block
299 | * 64k block bounary is 0x08000, that means:
300 | * 0x010000, 0x020000, 0x030000, ...
301 | * waiting the writing complete in each page
302 | * @PARAM addr starting erase address
303 | * (it must be a 64k block boundary)
304 | *********************************/
305 | void Flash_BErase64k(uint32_t addr){
306 | uint8_t buffer[4];
307 | Flash_Select();
308 | buffer[0] = W25_W_ENABLE;
309 | Flash_Transmit(buffer, 1);
310 | Flash_UnSelect();
311 |
312 | buffer[0] = W25_B_ERASE64K;
313 | buffer[1] = (addr >> 16) & 0xFF;
314 | buffer[2] = (addr >> 8) & 0xFF;
315 | buffer[3] = addr & 0xFF;
316 | Flash_Select();
317 | Flash_Transmit(buffer, 4);
318 | Flash_UnSelect();
319 | Flash_WaitForWritingComplete();
320 | }
321 |
322 |
323 |
324 |
325 | /**********************************
326 | * @BRIEF Full chip erase to 0XFF
327 | * Chip Erase may need up to 100s
328 | * (typ. 20s)
329 | * waiting the writing complete in each page
330 | *********************************/
331 | void Flash_ChipErase(){
332 | uint8_t buffer[4];
333 | Flash_Select();
334 | buffer[0] = W25_W_ENABLE;
335 | Flash_Transmit(buffer, 1);
336 | Flash_UnSelect();
337 |
338 | buffer[0] = W25_CH_ERASE;
339 | Flash_Select();
340 | Flash_Transmit(buffer, 1);
341 | Flash_UnSelect();
342 | Flash_WaitForWritingComplete();
343 | }
344 |
345 |
346 |
347 |
348 |
349 | /**********************************
350 | * @BRIEF Initiates a powerdown
351 | * after a powerDown only accepted a porweUp command
352 | * opwerDown operation is 3us long
353 | *********************************/
354 | void Flash_PowerDown(){
355 | uint8_t buffer[4];
356 |
357 | buffer[0] = W25_POWERDOWN;
358 | Flash_Select();
359 | Flash_Transmit(buffer, 1);
360 | Flash_UnSelect();
361 | }
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 | /**********************************
370 | * @BRIEF Release from powerdown (3 us to restart) or read device ID
371 | *********************************/
372 | void Flash_PowerUp(){
373 | uint8_t buffer[4];
374 |
375 | buffer[0] = W25_POWERUP_ID;
376 | Flash_Select();
377 | Flash_Transmit(buffer, 1);
378 | Flash_UnSelect();
379 | HAL_Delay(1);
380 | }
381 |
382 |
383 |
384 |
385 | /**********************************
386 | * @BRIEF read device id from chip
387 | * @RETURN device id
388 | *********************************/
389 | uint8_t Flash_ReadDevID(){
390 | uint8_t buffer[4];
391 | uint8_t data;
392 |
393 | buffer[0] = W25_POWERUP_ID;
394 | buffer[1] = W25_DUMMY;
395 | buffer[2] = W25_DUMMY;
396 | buffer[3] = W25_DUMMY;
397 | Flash_Select();
398 | Flash_Transmit(buffer, 4);
399 | Flash_Receive(&data, 1);
400 | Flash_UnSelect();
401 | return data;
402 | }
403 |
404 |
405 |
406 |
407 |
408 | uint16_t Flash_ReadManufactutrerAndDevID() {
409 | uint8_t buffer[4];
410 | uint16_t data;
411 |
412 | buffer[0] = W25_POWERUP_ID;
413 | buffer[1] = W25_DUMMY;
414 | buffer[2] = W25_DUMMY;
415 | buffer[3] = W25_DUMMY;
416 | Flash_Select();
417 | Flash_Transmit(buffer, 4);
418 | Flash_Receive((uint8_t*)&data, 2);
419 | Flash_UnSelect();
420 | return data;
421 | }
422 |
423 |
424 |
425 |
426 | /******************************************************************
427 | * @RETURN 32bit value divided in 4 bytes:
428 | * (1)MSB dummy
429 | * (2) Jedec Manufacturer ID
430 | * (3) Memory Type
431 | * (4) Capacity
432 | ******************************************************************
433 | * Memory Capacity code:
434 | * 10H -> 5Mb 11H -> 10Mb 12H -> 20Mb
435 | * 13H -> 40Mb 14H -> 80Mb 15H -> 16Mb
436 | * 16H -> 32Mb 17H -> 64Mb 18H -> 128Mb
437 | * 19H -> 256Mb 20H -> 512Mb 21H -> 1Gb
438 | ******************************************************************/
439 | uint32_t Flash_ReadJedecID() {
440 | uint8_t buffer[4];
441 | uint8_t data[3];
442 | uint32_t result;
443 |
444 | buffer[0] = W25_JEDEC_ID;
445 | Flash_Select();
446 | Flash_Transmit(buffer, 1);
447 | Flash_Receive(data, 3);
448 | Flash_UnSelect();
449 | result=((data[0]<<16) | (data[1] <<8) | data[2]);
450 | return result;
451 | }
452 |
453 |
454 |
455 |
456 | /*********************************
457 | * @RETURN 256byte SFDP register content:
458 | *********************************/
459 | void Flash_ReadSFDP(uint8_t* data) {
460 | uint8_t buffer[5];
461 | buffer[0] = W25_R_SFPD_REG;
462 | for (uint8_t k=1;k<5;k++)
463 | buffer[k]=0;
464 | Flash_Select();
465 | Flash_Transmit(buffer, 5);
466 | Flash_Receive(data, 256);
467 | Flash_UnSelect();
468 | }
469 |
470 |
471 |
472 |
473 |
474 | /*********************************
475 | * @BRIEF testing chip alive and kicking
476 | * reading SFDP record, it must return
477 | * a string beginning with "SFDP"
478 | * @RETURN 1 test passed
479 | * 0 no
480 | *********************************/
481 | uint8_t Flash_TestAvailability() {
482 | uint8_t data[256];
483 | uint8_t test=1;
484 |
485 | for (uint8_t k=0;k!=254;k++)
486 | data[k]=0xFF;
487 | Flash_ReadSFDP(data);
488 | if (data[0]!='S')
489 | test=0;
490 | if (data[1]!='F')
491 | test=0;
492 | if (data[2]!='D')
493 | test=0;
494 | if (data[3]!='P')
495 | test=0;
496 | return test;
497 | }
498 |
499 |
500 |
501 |
502 | /******************************************************************
503 | * @BRIEF reading manufacutrer and device ID
504 | * checking if connected device is a Winbond Flash
505 | ******************************************************************/
506 | uint8_t Flash_Init(){
507 | uint32_t JedecID;
508 | HAL_Delay(6); // supposing init is called on system startup: 5 ms (tPUW) required after power-up to be fully available
509 | Flash_Reset();
510 | if (!Flash_TestAvailability())
511 | return 0;
512 | JedecID=Flash_ReadJedecID() ; //select the memSize byte
513 | if (((JedecID >> 16) & 0XFF) != 0xEF) // if ManufacturerID is not Winbond (0xEF)
514 | return 0;
515 | return 1; //return memSize as per table in Flash_ReadJedecID() definition
516 | }
517 |
518 |
519 |
520 |
521 |
522 | void Flash_Reset(){
523 | uint8_t command;
524 | command = W25_RESET_EN;
525 | Flash_Select();
526 | Flash_Transmit(&command, 1);
527 | Flash_UnSelect();
528 | command = W25_RESET;
529 | Flash_Select();
530 | Flash_Transmit(&command, 1);
531 | Flash_UnSelect();
532 | HAL_Delay(1); // 30us needed by resetting
533 | }
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 | void DataReader_WaitForReceiveDone(){
542 | // nothing to do, being reading always in polling mode
543 | return;
544 | }
545 |
546 | void DataReader_ReadData(uint32_t address24, uint8_t* buffer, uint32_t length){
547 | Flash_Read(address24, buffer, length);
548 | }
549 |
550 |
551 | void DataReader_StartDMAReadData(uint32_t address24, uint8_t* buffer, uint32_t length){
552 | //currently using polling mode even if requested DMA
553 | Flash_Read(address24, buffer, length);
554 | }
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
--------------------------------------------------------------------------------
/z_flash_W25QXXX.h:
--------------------------------------------------------------------------------
1 | /*********************************************
2 | * @file Z_FLASH_W25QXXX.h
3 | * @author mauro
4 | * @date: 01 august 2023
5 | * @version V.1.0.0
6 | *
7 | *********************************************
8 | * this version of library uses just polling
9 | * mode transmission
10 | * this version of library uses standard SPI
11 | *********************************************
12 | * configure below STEP1 and STEP4.
13 | * Do not change STEP2 and STEP3 in this version
14 | *********************************************/
15 |
16 | #ifndef INC_Z_FLASH_W25QXXX_H_
17 | #define INC_Z_FLASH_W25QXXX_H_
18 |
19 |
20 |
21 | /*||||||||||| USER/PROJECT PARAMETERS |||||||||||*/
22 |
23 | /****************** STEP 1 ******************
24 | **************** PORT PARAMETERS *****************
25 | ** properly set the below th 2 defines to address
26 | ******** the SPI port defined on CubeMX *********
27 | **************************************************/
28 | #define FLASH_SPI_PORT hspi1
29 | #define FLASH_SPI SPI1
30 |
31 |
32 | /****************** STEP 2 *******************
33 | **************** FLASH READING MODE ***************
34 | **** FLASH_MODE 1 -> Fast mode
35 | **** FLASH_MODE 2 -> Dual mode // NOT IMPLEMENTED
36 | **** FLASH_MODE 4 -> Quad mode // NOT IMPLEMENTED
37 | **** otherwise -> Standard Mode (warning: standard mode not available if SPO port speed is above 50 MHZ)
38 | **** SPI port must be previously correctly defined via CubeMX
39 | **************************************************/
40 | #define EXT_FLASH_MODE 1
41 |
42 |
43 |
44 | /***************** STEP 3 *****************
45 | ************* SPI COMMUNICATION MODE **************
46 | *** enable SPI mode want, uncommenting ONE row ****
47 | **** (Setup the same configuration on CubeMX) *****
48 | ***************************************************/
49 | #define EXT_FLASH_SPI_POLLING_MODE
50 | //#define EXT_FLASH_SPI_DMA_MODE // (mixed: polling/DMA, see below) NOT IMPLEMENTED
51 |
52 |
53 |
54 | /***************** STEP 4 *****************
55 | *********** set below information as per *************
56 | ********* chip memory used in the project *********
57 | ***************************************************/
58 | /* active information */
59 | #define EXT_FLASH_PAGE_SIZE 0x0100 //256b page size (bits)
60 | #define EXT_FLASH_SECTOR_SIZE 0x1000 //4kB sector size (bytes)
61 | #define EXT_FLASH_BLOCK_SIZE 0x00010000 //64kB block size (bytes)
62 | #define EXT_FLASH_SIZE 0X00100000 //1MB-8Mb total size (bytes)
63 | #define EXT_FLASH_PAGE_NUM 0x1000 //4096 pages
64 | #define EXT_FLASH_SECTOR_NUM 0x0100 //256 sectors
65 | #define EXT_FLASH_BLOCK_NUM 0x0010 //16 blocks
66 |
67 |
68 | /* here values for the W25Q80DV/DL chips
69 | #define EXT_FLASH_PAGE_SIZE 0x0100 //256b page size (bits)
70 | #define EXT_FLASH_SECTOR_SIZE 0x1000 //4kB sector size (bytes)
71 | #define EXT_FLASH_BLOCK_SIZE 0x00010000 //64kB block size (bytes)
72 | #define EXT_FLASH_SIZE 0X00100000 //1MB-8Mb total size (bytes)
73 | #define EXT_FLASH_PAGE_NUM 0x1000 //4096 pages
74 | #define EXT_FLASH_SECTOR_NUM 0x0100 //256 sectors
75 | #define EXT_FLASH_BLOCK_NUM 0x0010 //16 blocks
76 | */
77 |
78 | /* here values for the W25Q64JV chips
79 | #define EXT_FLASH_PAGE_SIZE 0x0100 //256b page size (bits)
80 | #define EXT_FLASH_SECTOR_SIZE 0x1000 //4kB sector size (bytes)
81 | #define EXT_FLASH_BLOCK_SIZE 0x00010000 //64kB block size (bytes)
82 | #define EXT_FLASH_SIZE 0X00800000 //8MB-64Mb total size (bytes)
83 | #define EXT_FLASH_PAGE_NUM 0x8000 //32768 pages
84 | #define EXT_FLASH_SECTOR_NUM 0x0800 //2048 sectors
85 | #define EXT_FLASH_BLOCK_NUM 0x0080 //128 blocks
86 | */
87 |
88 | /* here values for the W25Q128JV chips
89 | #define EXT_FLASH_PAGE_SIZE 0x0100 //256b page size (bits)
90 | #define EXT_FLASH_SECTOR_SIZE 0x1000 //4kB sector size (bytes)
91 | #define EXT_FLASH_BLOCK_SIZE 0x00010000 //64kB block size (bytes)
92 | #define EXT_FLASH_SIZE 0X01000000 //16MB-128Mb total size (bytes)
93 | #define EXT_FLASH_PAGE_NUM 0x00010000 //32768 pages
94 | #define EXT_FLASH_SECTOR_NUM 0x1000 //4096 sectors
95 | #define EXT_FLASH_BLOCK_NUM 0x0100 //256 blocks
96 | */
97 |
98 |
99 |
100 |
101 | #define EXT_FLASH_DMA_CUTOFF 20 //that's related to uC DMA and SPI. You can leave it unchanged
102 |
103 | /*|||||||| END OF USER/PROJECT PARAMETERS ||||||||*/
104 |
105 |
106 |
107 |
108 | /*||||||||||||||| DEVICE PARAMETERS ||||||||||||||||||*/
109 | // W25QXX EEPROM family commands
110 |
111 | #define W25_RESET_EN 0x66 //sequence is 0x66 + 0x99 + 30us delay
112 | #define W25_RESET 0x99 //sequence is 0x66 + 0x99 + 30us delay
113 | #define W25_W_ENABLE 0x06
114 | #define W25_READ 0x03
115 | #define W25_FREAD 0x0B
116 | #define W25_FREAD_DUAL 0x3B
117 | #define W25_FREAD_QUAD 0x6B
118 | #define W25_PAGE_P 0x02
119 | #define W25_S_ERASE4K 0x20
120 | #define W25_B_ERASE32K 0x52
121 | #define W25_B_ERASE64K 0xD8
122 | #define W25_CH_ERASE 0xC7
123 | #define W25_POWERDOWN 0xB9
124 | #define W25_POWERUP_ID 0xAB
125 | #define W25_JEDEC_ID 0x9F
126 | #define W25_R_SR1 0x05
127 | #define W25_R_SFPD_REG 0x5A
128 |
129 | /* unused commands
130 | #define W25_SR_W_ENABLE 0x50
131 | #define W25_W_DISABLE 0x04
132 | #define W25_DEVICE_ID 0x90
133 | #define W25_UNIQUE_ID 0x4B
134 | #define W25_FREAD_DUAL_IO 0xBB
135 | #define W25_FREAD_QUAD_IO 0xEB
136 | #define W25_EP_SUS 0x75
137 | #define W25_EP_RES 0x7A
138 | #define W25_W_SR1 0x01
139 | #define W25_R_SR2 0x35
140 | #define W25_W_SR2 0x31
141 | #define W25_R_SR3 0x15
142 | #define W25_W_SR3 0x11
143 | #define W25_R_SFPD_REG 0x5A
144 | #define W25_E_SEC_REG 0x44
145 | #define W25_P_SEC_REG 0x42
146 | #define W25_R_SEC_REG 0x48
147 | #define W25_G_BL_LOCK 0x7E
148 | #define W25_G_BL_UNLK 0x98
149 | #define W25_R_BL_LOCK 0x3D
150 | #define W25_I_BL_LOCK 0x36
151 | #define W25_I_BL_UNLK 0x39
152 | #define W25_EP_SUSPEND 0x75
153 | #define W25_EP_RESUME 0x75
154 | end of unused commands */
155 | // W25QXX EEPROM family commands
156 |
157 | #define W25_DUMMY 0x00 //dummy MUST be 0x00, in "read manufacturer"
158 |
159 | // bit masks of W25QXX SR1, SR2, SR3 registers
160 | #define SR1_BIT_BUSY (01U) //status only: 1 means busy device
161 |
162 | /* unused bitmasks
163 | #define SR1_BIT_WEL (02U) //status only: 1 means write enabled. set by W25_W_ENABLE command
164 | #define SR1_BIT_BP0 (04U) //writable: block protect bit 0
165 | #define SR1_BIT_BP1 (08U) //writable: block protect bit 1
166 | #define SR1_BIT_BP2 (10U) //writable: block protect bit 2
167 | #define SR1_BIT_TB (20U) //writable: top(=1)/bottom(=0) starting, block protection bit
168 | #define SR1_BIT_SEC (40U) //writable: sector(4kb)/block(64kb) block protection (1=sector)
169 | #define SR1_BIT_SRP (80U) //writable: set SR registers protection (together with SRL)
170 | #define SR2_BIT_SRL (01U) //writable: set SR registers protection (together with SRL)
171 | #define SR2_BIT_QE (02U) //writable: enable (=1) QUAD SPI mode. if =0 SPI is Standard/Dual
172 | #define SR2_BIT_LB1 (08U) //OTP: 1 means Security Register 1 is permanently set readonly
173 | #define SR2_BIT_LB2 (10U) //OTP: 1 means Security Register 2 is permanently set readonly
174 | #define SR2_BIT_LB3 (20U) //OTP: 1 means Security Register 3 is permanently set readonly
175 | #define SR2_BIT_CMP (40U) //writable: complement protect: reverse protection of BP0-1-2,TB,SEC
176 | #define SR2_BIT_SUS (80U) //writable: suspend status: 1 indicates erase/program suspended
177 | #define SR3_BIT_WPS (04U) //writable: Write protect scheme: 1=using individual block flag, 0=using BPx, etc, flags
178 | #define SR3_BIT_DRV0 (20U) //writable: sets output driver strength
179 | #define SR3_BIT_DRV1 (40U) //writable: sets output driver strength
180 | end of W25QXX SR1, SR2, SR3 registers bitmasks */
181 |
182 |
183 | #if FLASH_MODE == 1
184 | #define FLASH_READ_COMMAND W25_FREAD
185 | #elif FLASH_MODE == 2
186 | #define FLASH_READ_COMMAND W25_FREAD_DUAL
187 | #elif FLASH_MODE == 4
188 | #define FLASH_READ_COMMAND W25_FREAD_QUAD
189 | #else
190 | #define FLASH_READ_COMMAND W25_READ
191 | #endif
192 |
193 |
194 |
195 | /*||||||||||| END OF DEVICE PARAMETERS ||||||||||||*/
196 |
197 |
198 | void Flash_Read(uint32_t addr, uint8_t* data, uint32_t dataSize);
199 | void Flash_Write(uint32_t addr, uint8_t* data, uint32_t dataSize);
200 | //void Flash_WaitForWritingComplete();
201 | void Flash_SErase4k(uint32_t addr);
202 | void Flash_BErase32k(uint32_t addr);
203 | void Flash_BErase64k(uint32_t addr);
204 | void Flash_ChipErase();
205 | void Flash_PowerDown();
206 | uint8_t Flash_ReadDevID();
207 | uint16_t Flash_ReadManufactutrerAndDevID();
208 | uint32_t Flash_ReadJedecID();
209 | void Flash_ReadSFDP(uint8_t* data);
210 | void Flash_Reset();
211 | uint8_t Flash_Init(); //initialization: includes availability test and reset
212 | void DataReader_WaitForReceiveDone();
213 | void DataReader_ReadData(uint32_t address24, uint8_t* buffer, uint32_t length);
214 | void DataReader_StartDMAReadData(uint32_t address24, uint8_t* buffer, uint32_t length);
215 |
216 |
217 |
218 |
219 |
220 | #endif /* INC_Z_FLASH_W25QXXX_H_ */
221 |
--------------------------------------------------------------------------------