Pokud přicházíte k čipu RP2040 ze světa Linuxových počítačů jako já, možná budete překvapeni, když zjistíte, že zápis uživatelských/aplikačních dat do integrované 2MB energeticky nezávislé paměti není tak jednoduchý jako je volání funkcí pro práci se soubory open(), read(), write() a close() na Linuxu. Protože to není ani disk, ani EEPROM, ale flash. Diskové operace zajišťuje operační systém, a programátor má práci docela snadnou. Hlavní rozdíl je v tom, že u disku se o mazání dat příliš nestaráme a SSD disk vydrží poměrně hodně zápisů (terabajty), EEPROM je vymazatelná po bytech a vydrží přibližně 100 000 cyklů zápisu, zatímco flash lze vymazat pouze po relativně velkých kouscích a opotřebuje se podstatně dříve, možná po cca 20 000 cyklech (nebo po 100 000 cyklech, kdo ví). Na Picu máme flash paměť poměrně velkou, takže problém s její trvanlivostí je o něco méně bolestivý (zvláště pokud jsme opatrní – čtěte dále).

Díky těmto a dalším úvahám je práce přímo s integrovanou flash pamětí RP2040 trochu složitější, pokud jste v něm nováčkem. V současné době potřebujete k mazání a zápisu použít několik funkcí specifických pro čip (ano, to jsou samostatné funkce) a budete muset navíc dodržovat některá poměrně přísná omezení kvůli způsobu, jakým hardware funguje. Pozitivní je, že adresní prostor celého flashe je u procesoru ARM paměťově mapován, takže čtení z něj je absolutní radost. Tento článek se pokouší toto vše rozvést a vyvrcholit jednoduchým až středně pokročilým příkladem kódu, který funguje na Raspberry Pi Pico.

Tabulka 1. Adresní prostor — konfigurace paměti
Jméno Začátek Velikost Atributy

FLASH

0x10000000

0x00200000

xr

RAM

0x20000000

0x00040000

xrw

SCRATCH_X

0x20040000

0x00001000

xrw

SCRATCH_Y

0x20041000

0x00001000

xrw

default

0x00000000

0xffffffff

Jedna flash paměť na všechno

Nejprve něco o rozložení flash paměti. Tato flash paměť je stejné zařízení, na které ukládá programový kód vaší aplikace mikrokontroléru. To znamená několik věcí. První a nejzřejmější je, že nechcete poškodit kód aplikace tím, že jej přepíšete svými daty. Naštěstí se kód aplikace vždy nachází na začátku flashe. Měli byste tedy zajistit, aby vaše data byla naopak zapsána na konci. V hlavičkových souborech je několik užitečných maker, která to usnadňují:

PICO_FLASH_SIZE_BYTES // Celková velikost RP2040 flash paměti v bajtech
FLASH_SECTOR_SIZE     // Velikost sektoru v bajtech (sektor je minimum, co můžete smazat)
FLASH_PAGE_SIZE       // Velikost stránky v bajtech (stránka je minimum, co můžete zapsat)

Abychom k tomu přidali nějaká čísla, na čipu RP2040 je PICO_FLASH_SIZE_BYTES 2 MB nebo 2097152 bajtů. FLASH_SECTOR_SIZE je 4K nebo 4096 bajtů. FLASH_PAGE_SIZE je 256 bajtů. Já budu ukládat na flash konfigurační údaje pro připojení Wifi a nastavení Wireguardu, abych to nemusel mít natvrdo v kódu. Takže potřebuji pouze jeden sektor flash paměti – minimální množství, které můžete vymazat. Měl by to být poslední fyzický sektor, který eliminuje možnost, že budou data zasahovat do programového kódu. Tento sektor začíná na adrese:

PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE

Tato hodnota může být a bude použita přímo jako adresa programování flash, viz níže. To udrží moje uživatelská data daleko od programového kódu. Provedeme později jeden trik s cmake, který naprosto vyloučí, že by mohla být data přepsána programem a program přepsán daty.

SDK funkce pro práci s flash pamětí

V SDK pro Raspberry Pi Pico se nacházejí dvě funkce pro práci s flash pamětí:

void flash_range_erase (uint32_t flash_offs, size_t count) (1)
void flash_range_program (uint32_t flash_offs, const uint8_t * data, size_t count) (2)
1 Vymazání části flash paměti.
2 Zápis dat do části flash paměti.

Prototypy těchto funkcí a stejně tak makra FLASH_SECTOR_SIZE a FLASH_PAGE_SIZE se nacházejí v hlavičkovém souboru hardware/flash.h

Vymazání části flash paměti

Funkce flash_range_erase() nastaví byty flash paměti na hodnotu 0xff (výmaz paměti), začátek oblasti je flash_offs a velikost count je počet sektorů, které mají být vymazány. count musí být celočíselný násobek FLASH_SECTOR_SIZE (n \* 4096 bajtů). flash_offs je adresa ofsetu od začátku flash paměti, musí to být zase násobek 4096.

Chceme vymazat poslední sektor flash paměti:

Příklad vymazání 1 sektoru (4096 bajtů) na konci flash paměti — flash_range_erase()
uint32_t flash_offs = PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE;

flash_range_erase(flash_offs, 1*FLASH_SECTOR_SIZE);

Zápis dat do části flash paměti

Zápis dat do flash paměti provádí funkce flash_range_program(), která zapíše 256 bajtů dat do paměti. Parametr uint32_t flash_off je adresa flash paměti, začátek kam se budou data zapisovat. Ukazatel const uint8_t *data nám ukazuje na naše data co budeme zapisovat (pokud jsou naše data jiného typu, prostě je přetypujeme na const uint8_t *) a size_t count je velikost našich dat. Měl by to být násobek 256 bajtů.

Zakázání přerušení během mazání a zápisu na flash

Z důvodů, kterým moc nerozumím, ale které jsou pravděpodobně svázány s tím, jak ARM procesor vykonává namapovaný kód z paměti, mazání a zápis do flash paměti nejsou bezpečné z hlediska vláken a přerušení (thread-safe and interrupt-safe). Proto je potřeba před začátkem mazání nebo zápisu zakázat přerušení. Vaše aplikace přerušení používat nemusí, ale pokud třeba je použito USB k stdio, tak tam přerušení budou použita zcela jistě. Musíme tedy do našeho zdrojového kódu přidat #include <hardware/sync.h> a použít funkce pro uložení a zakázání přerušení save_and_disable_interrupts() před manipulací s flash pamětí. Čtení z flsh paměti se to nijak netýká. Po ukončení zápisu musíme obnovit přerušení funkcí restore_interrupts().

Příklad zápisu do flash se zakázáním přerušení
#include <hardware/sync.h>
#include <hardware/flash.h>

// ....

char buf[256] = "Nejaka moje data, co chci zapsat na flash.";
uint32_t ints = save_and_disable_interrupts();
flash_range_program(PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE, (uint8_t *) buf, FLASH_PAGE_SIZE);
restore_interrupts(ints);

Kromě toho, pokud spouštíte kód na obou jádrech ARM, musíte ručně zajistit, aby během mazání/programování nedocházelo k žádným přístupům flash z druhého jádra. Doufejme, že pokud jste tak pokročilí, nečtete to tady, protože jako programátor amatér si v současné době uvědomuji tyto schopnosti na architektuře mikrokontroléru pouze okrajově.

Čtení z flash paměti

Jádra ARM mají namapován celý adresní prostor flash paměti. To znamená, že programově jednoduše vytvoříte ukazatel, nastavíte jeho adresu na požadované místo ve flashi a načtete hodnotu přímo, jako by byla v RAM. Díky tomu je procházení flash paměti pro čtení velmi jednoduché, protože stačí zvýšit ukazatel ve smyčce a jít na to. Jedna drobná věc, kterou musíte vzít v úvahu, je, že RAM je zahrnuta v tomto adresním prostoru. To znamená, že adresy flash mapované v paměti budou kompenzovány velikostí paměti RAM ve srovnání s adresami, které jsme použili výše při mazání a programování paměti flash. Na RP2040 jsou XIP_BASE bajty RAM. Takže když počítáte adresy pro čtení, použijte to jako další offset. Zde vezmu int z prvních čtyř bajtů posledního sektoru:

// Flash-based address of the last sector
#define FLASH_TARGET_OFFSET (PICO_FLASH_SIZE_BYTES - FLASH_SECTOR_SIZE)

int *p, addr, value;

// Compute the memory-mapped address, remembering to include the offset for RAM
addr = XIP_BASE +  FLASH_TARGET_OFFSET;
p = (int *) addr; // Place an int pointer at our memory-mapped address
value = *p; // Store the value at this address into a variable for later use
}

Protože budu používat trik s úpravou linkeru, adresu pro čtení addr budu získávat jinak. Nejdříve popíši danou úpravu, která nám zabezpečí, že naše uživatelská data budou v bezpečí před náhodným přepsáním kódem programu. Pro 4k blok dat to je směšné, protože 2MBytový kód stěží uděláme, ale pokud budeme potřebovat pro naše data třeba 1MByte, tak to stojí za to.

Trik s linkerem zabraňující přepisu dat

Raspberry Pi Pico SDK používá k sestavení programu cmake a výsledné sestavení programu dělá linker ld. Upravíme konfiguraci linkeru tak, aby nemohla být naše uživatelská data náhodou přepsána programem. Defaultní konfigurace linkeru se nachází v souboru pico-sdk/src/rp2_common/pico_standard_link/memmap_default.ld, v tomto souboru se nachází i konfigurace paměti. Upravíme tento soubor tak, aby poslední sektor flash paměti byl pro naše data:

memmap_custom.ld
  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
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
/* Based on GCC ARM embedded samples.
   Defines the following symbols for use by code:
    __exidx_start
    __exidx_end
    __etext
    __data_start__
    __preinit_array_start
    __preinit_array_end
    __init_array_start
    __init_array_end
    __fini_array_start
    __fini_array_end
    __data_end__
    __bss_start__
    __bss_end__
    __end__
    end
    __HeapLimit
    __StackLimit
    __StackTop
    __stack (== StackTop)
*/

__PERSISTENT_STORAGE_LEN = 4k ;  /* naše velikost vyhrazené paměti */ (1)

MEMORY
{
    FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k - __PERSISTENT_STORAGE_LEN
    FLASH_PERSISTENT(rw) : ORIGIN = 0x10000000 + (2048k - __PERSISTENT_STORAGE_LEN) , LENGTH = __PERSISTENT_STORAGE_LEN (2)
    RAM(rwx) : ORIGIN =  0x20000000, LENGTH = 256k
    SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k
    SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k
}

ENTRY(_entry_point)

SECTIONS
{
    /* Second stage bootloader is prepended to the image. It must be 256 bytes big
       and checksummed. It is usually built by the boot_stage2 target
       in the Raspberry Pi Pico SDK
    */

    .flash_begin : {
        __flash_binary_start = .;
    } > FLASH

    .boot2 : {
        __boot2_start__ = .;
        KEEP (*(.boot2))
        __boot2_end__ = .;
    } > FLASH

    ASSERT(__boot2_end__ - __boot2_start__ == 256,
        "ERROR: Pico second stage bootloader must be 256 bytes in size")

    /* The second stage will always enter the image at the start of .text.
       The debugger will use the ELF entry point, which is the _entry_point
       symbol if present, otherwise defaults to start of .text.
       This can be used to transfer control back to the bootrom on debugger
       launches only, to perform proper flash setup.
    */

    .text : {
        __logical_binary_start = .;
        KEEP (*(.vectors))
        KEEP (*(.binary_info_header))
        __binary_info_header_end = .;
        KEEP (*(.reset))
        /* TODO revisit this now memset/memcpy/float in ROM */
        /* bit of a hack right now to exclude all floating point and time critical (e.g. memset, memcpy) code from
         * FLASH ... we will include any thing excluded here in .data below by default */
        *(.init)
        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .text*)
        *(.fini)
        /* Pull all c'tors into .text */
        *crtbegin.o(.ctors)
        *crtbegin?.o(.ctors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
        *(SORT(.ctors.*))
        *(.ctors)
        /* Followed by destructors */
        *crtbegin.o(.dtors)
        *crtbegin?.o(.dtors)
        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
        *(SORT(.dtors.*))
        *(.dtors)

        *(.eh_frame*)
        . = ALIGN(4);
    } > FLASH

    .rodata : {
        *(EXCLUDE_FILE(*libgcc.a: *libc.a:*lib_a-mem*.o *libm.a:) .rodata*)
        . = ALIGN(4);
        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.flashdata*)))
        . = ALIGN(4);
    } > FLASH

    .ARM.extab :
    {
        *(.ARM.extab* .gnu.linkonce.armextab.*)
    } > FLASH

    __exidx_start = .;
    .ARM.exidx :
    {
        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
    } > FLASH
    __exidx_end = .;

    /* Machine inspectable binary information */
    . = ALIGN(4);
    __binary_info_start = .;
    .binary_info :
    {
        KEEP(*(.binary_info.keep.*))
        *(.binary_info.*)
    } > FLASH
    __binary_info_end = .;
    . = ALIGN(4);

    /* End of .text-like segments */
    __etext = .;


    .section_persisitent : {
        "ADDR_PERSISTENT" = .;
    } > FLASH_PERSISTENT

   .ram_vector_table (COPY): {
        *(.ram_vector_table)
    } > RAM

    .data : {
        __data_start__ = .;
        *(vtable)

        *(.time_critical*)

        /* remaining .text and .rodata; i.e. stuff we exclude above because we want it in RAM */
        *(.text*)
        . = ALIGN(4);
        *(.rodata*)
        . = ALIGN(4);

        *(.data*)

        . = ALIGN(4);
        *(.after_data.*)
        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__mutex_array_start = .);
        KEEP(*(SORT(.mutex_array.*)))
        KEEP(*(.mutex_array))
        PROVIDE_HIDDEN (__mutex_array_end = .);

        . = ALIGN(4);
        /* preinit data */
        PROVIDE_HIDDEN (__preinit_array_start = .);
        KEEP(*(SORT(.preinit_array.*)))
        KEEP(*(.preinit_array))
        PROVIDE_HIDDEN (__preinit_array_end = .);

        . = ALIGN(4);
        /* init data */
        PROVIDE_HIDDEN (__init_array_start = .);
        KEEP(*(SORT(.init_array.*)))
        KEEP(*(.init_array))
        PROVIDE_HIDDEN (__init_array_end = .);

        . = ALIGN(4);
        /* finit data */
        PROVIDE_HIDDEN (__fini_array_start = .);
        *(SORT(.fini_array.*))
        *(.fini_array)
        PROVIDE_HIDDEN (__fini_array_end = .);

        *(.jcr)
        . = ALIGN(4);
        /* All data end */
        __data_end__ = .;
    } > RAM AT> FLASH

    .uninitialized_data (COPY): {
        . = ALIGN(4);
        *(.uninitialized_data*)
    } > RAM

    /* Start and end symbols must be word-aligned */
    .scratch_x : {
        __scratch_x_start__ = .;
        *(.scratch_x.*)
        . = ALIGN(4);
        __scratch_x_end__ = .;
    } > SCRATCH_X AT > FLASH
    __scratch_x_source__ = LOADADDR(.scratch_x);

    .scratch_y : {
        __scratch_y_start__ = .;
        *(.scratch_y.*)
        . = ALIGN(4);
        __scratch_y_end__ = .;
    } > SCRATCH_Y AT > FLASH
    __scratch_y_source__ = LOADADDR(.scratch_y);

    .bss  : {
        . = ALIGN(4);
        __bss_start__ = .;
        *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.bss*)))
        *(COMMON)
        . = ALIGN(4);
        __bss_end__ = .;
    } > RAM

    .heap (COPY):
    {
        __end__ = .;
        end = __end__;
        *(.heap*)
        __HeapLimit = .;
    } > RAM

    /* .stack*_dummy section doesn't contains any symbols. It is only
     * used for linker to calculate size of stack sections, and assign
     * values to stack symbols later
     *
     * stack1 section may be empty/missing if platform_launch_core1 is not used */

    /* by default we put core 0 stack at the end of scratch Y, so that if core 1
     * stack is not used then all of SCRATCH_X is free.
     */
    .stack1_dummy (COPY):
    {
        *(.stack1*)
    } > SCRATCH_X
    .stack_dummy (COPY):
    {
        *(.stack*)
    } > SCRATCH_Y

    .flash_end : {
        __flash_binary_end = .;
    } > FLASH


    /* stack limit is poorly named, but historically is maximum heap ptr */
    __StackLimit = ORIGIN(RAM) + LENGTH(RAM);
    __StackOneTop = ORIGIN(SCRATCH_X) + LENGTH(SCRATCH_X);
    __StackTop = ORIGIN(SCRATCH_Y) + LENGTH(SCRATCH_Y);
    __StackOneBottom = __StackOneTop - SIZEOF(.stack1_dummy);
    __StackBottom = __StackTop - SIZEOF(.stack_dummy);
    PROVIDE(__stack = __StackTop);

    /* Check if data + heap + stack exceeds RAM limit */
    ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed")

    ASSERT( __binary_info_header_end - __logical_binary_start <= 256, "Binary info must be in first 256 bytes of the binary")
    /* todo assert on extra code */
}
1 Zde je velikost naší vyhrazené paměti flash
2 Zde pojmenujeme naší vyhrazenou pamět, určíme její začátek a délku.

Touto úpravou se nám změnila konfigurace paměti takto:

Tabulka 2. Upravený adresní prostor — konfigurace paměti s vyhrazenou flash pro data
Jméno Začátek Velikost Atributy

FLASH

0x10000000

0x001ff000

xr

FLASH_PERSISTENT

0x101ff000

0x00001000

rw

RAM

0x20000000

0x00040000

xrw

SCRATCH_X

0x20040000

0x00001000

xrw

SCRATCH_Y

0x20041000

0x00001000

xrw

default

0x00000000

0xffffffff

Můžeme si to přečíst ve výsledném souboru build/PersistentStorage.elf.map v části Memory Configuration.

Do sestavovacího souboru CMakeLists.txt vpašujeme naši upravenou konfiguraci linkeru nastavením pico_set_linker_script():

CMakeLists.txt
 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
# Specialni CMakeLists.txt pro ukladani do flash pameti

cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD pico_w)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)


# Pull in Raspberry Pi Pico SDK (must be before project)
include(pico_sdk_import.cmake)

project(PersistentStorage C CXX ASM)

# Initialise the Raspberry Pi Pico SDK
pico_sdk_init()

# Add executable. Default name is the project name, version 0.1

add_executable(PersistentStorage PersistentStorage.c )

pico_set_linker_script(PersistentStorage ${CMAKE_SOURCE_DIR}/memmap_custom.ld) (1)

pico_set_program_name(PersistentStorage "PersistentStorage")
pico_set_program_version(PersistentStorage "0.1")

pico_enable_stdio_uart(PersistentStorage 0)
pico_enable_stdio_usb(PersistentStorage 1)

# Add the standard library to the build
target_link_libraries(PersistentStorage
pico_stdlib
hardware_flash
hardware_sync
)

pico_add_extra_outputs(PersistentStorage)
1 Zde je naše upravená konfigurace linkeru, kterou bude používat cmake.

Úpravou našeho linkovacího skriptu nám ve sestaveném programu vzniknul symbol ADDR_PERSISTENT (viz řádek 130 memmap_custom.ld), který budeme používat jako adresu začátku našeho datového bloku ve flashi.

Uděláme si ještě jednu užitečnou funkci, která nám vrátí tento symbol ADDR_PERSISTENT`, t.j. adresu začátku našeho datového bloku ve flashi:

#include <stdint.h>

inline uint32_t *getAddressPersistent() {
extern uint32_t ADDR_PERSISTENT[];
    return ADDR_PERSISTENT;
}

A nyní již celý program, který testuje, zda je všechno v řádku (nebo v pořádku :-).

PersistentStorage.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "flash_utils.h"
#include "hardware/flash.h"
#include "hardware/sync.h"
#include <string.h>

const uint8_t *flash_target_content;

#define MOJE_VELIKOST 1*FLASH_SECTOR_SIZE

// tiskne buffer jako hexadecimální čísla
void print_buf(const uint8_t *buf, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        printf("%02x", buf[i]);
        if (i % 16 == 15)
            printf("\n");
        else
            printf(" ");
    }
}

// tiskne buffer jako znaky
void print_chars(const uint8_t *buf, size_t len)
{
    for (size_t i = 0; i < len; ++i) {
        printf("%c", buf[i]);
        if (i % 16 == 15)
            printf("\n");
        else
            printf(" ");
    }
}

int main()
{
    char data[256] = "Jirka Chraska programuje Pico W a uklada data do flash pameti. Jeden sektor flashe se da prepsat 100 000 krat nez zdegraduje (znici se).\n";
    flash_target_content = (const uint8_t *) getAddressPersistent();
    uint32_t kam = PICO_FLASH_SIZE_BYTES - MOJE_VELIKOST;
    stdio_init_all();
    printf("\nStiskni klavesu.\n");
    getchar();
    printf("\nZapis do flashe.\n");
    printf("FLASH_MEM=%p FLASH_PAGE_SIZE=%d, FLASH_SECTOR_SIZE=%d, XIP_BASE=0x%x, kam=0x%x\n",getAddressPersistent(), FLASH_PAGE_SIZE, FLASH_SECTOR_SIZE, XIP_BASE, kam );
    sleep_ms(5000);

    printf("Erasing %d bytes of flash\n", MOJE_VELIKOST);
    printf("Writing %d bytes to flash\n", FLASH_PAGE_SIZE);
    // uložíme a zakážeme přerušení
    uint32_t ints = save_and_disable_interrupts();
    // vymažeme jeden sektor (4096 bytů ) flashe
    flash_range_erase(kam,FLASH_SECTOR_SIZE);
    // zapíšeme data do flashe
    flash_range_program(kam, data, FLASH_PAGE_SIZE);
    // obnovíme přerušení
    restore_interrupts(ints);
    // Přečteme, co jsme zapsali
    printf("Reading %d bytes from flash.\n",FLASH_PAGE_SIZE);
    print_buf(flash_target_content, FLASH_PAGE_SIZE);
    printf("\n");
    print_chars(flash_target_content, FLASH_PAGE_SIZE);
    printf("\nUlozeny retezec: \"%s\"\n\n", (char *) flash_target_content);
    sleep_ms(2000);
    return 0;
}
Výstup z minicomu
Vítejte v programu minicom 2.9

VOLBY: I18n
Port /dev/ttyACM0, 16:47:20

Stiskněte CTRL-A Z pro nápovědu o klávesách se zvláštním významem


Zapis do flashe.
FLASH_MEM=101FF000 FLASH_PAGE_SIZE=256, FLASH_SECTOR_SIZE=4096, XIP_BASE=0x10000000, kam=0x1ff000
Erasing 4096 bytes of flash
Writing 256 bytes to flash
Reading 256 bytes from flash.
4a 69 72 6b 61 20 43 68 72 61 73 6b 61 20 70 72
6f 67 72 61 6d 75 6a 65 20 50 69 63 6f 20 57 20
61 20 75 6b 6c 61 64 61 20 64 61 74 61 20 64 6f
20 66 6c 61 73 68 20 70 61 6d 65 74 69 2e 20 4a
65 64 65 6e 20 73 65 6b 74 6f 72 20 66 6c 61 73
68 65 20 73 65 20 64 61 20 70 72 65 70 73 61 74
20 31 30 30 20 30 30 30 20 6b 72 61 74 20 6e 65
7a 20 7a 64 65 67 72 61 64 75 6a 65 20 28 7a 6e
69 63 69 20 73 65 29 2e 0a 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

J i r k a   C h r a s k a   p r
o g r a m u j e   P i c o   W
a   u k l a d a   d a t a   d o
  f l a s h   p a m e t i .   J
e d e n   s e k t o r   f l a s
h e   s e   d a   p r e p s a t
  1 0 0   0 0 0   k r a t   n e
z   z d e g r a d u j e   ( z n
i c i   s e ) .









Ulozeny retezec: "Jirka Chraska programuje Pico W a uklada data do flash pameti. Jeden sektor flashe se da prepsat 100 000 krat nez zdegraduje (znici se).
"

Zdá se, že to funguje, hurá. Naučil jsem se programovat flash paměť.

Získané zkušenosti mohu využít při programování konfigurace Pico W s Wireguardem, Projekt 11: PicoW s implementací VPN Wireguard.

Zdroje a odkazy