ARM: ko nors saugojimas fleše

Posted: 2016-04-05 in Darbeliai
Žymos:,

AVR procesoriai turi EEPROM gabaliuką. Taip pat turi specialią EEMEM kompiliatoriaus direktyvą pažymėti, kad kintamasis guli būtent EEPROM’e. Na, o štai STM32F10x procesoriukai EEPROM neturi. Regis, Cortex M4 iš mikrovaldiklių šeimos turi EEPROM’o.

Užtai visi mikrovaldikliai turi Flash atminties gabaliuką, kurion rašoma firmwarė. Bet firmwarė užima ne visą flešą, nebent jau labai smarkiai prikodijama ir kitaip iki lubų užkopiama. Visgi būkim biedni (be EEPROM’o), bet teisingi: vienas kitas flešo puslapėlis tikrai lieka laisvas ir kiek pasistengus ten galima pasikavoti savo duomenų bei juos perrašinėti.


Flash tipo atmintis | Darau, blė

Svarbu išsiaiškinti flešo ir EEPROM skirtumus. O jie paprasti. Pagrindinė savybė, už kurią EEPROM myli — rašymas ir skaitymas po vieną baitą. Netgi faktiškai po vieną bitą kartais, jei jau būsime priekabūs, tik kad taip retai smulkinamasi. Tad sakykime, jog po baitą. EEPROM dėvisi ir po maždaug 100000 perrašymo ciklų nustoja veikti. Dėl to reikia pagalvoti apie EEPROM apkrovos paskirstymą ir kitas subtilybes. Tačiau tai ne bėda ir tų algoritmų gražiam EEPROM išnaudojimui prigalvota daug, tik naršykit.

Flešinė atmintis tuo tarpu skirstoma puslapiais. Įprasti dydžiai yra 1 kB, 2 kB, 4 kB ir pan. Duomenis iš flešų galima nuskaitinėti baito tikslumu, o va įrašinėti jau kaip sudizaininta. Konkrečiai STM32F10x atveju į flešą galima įrašyti 16 bitų duomenis. Po mažiau — ne. Bet pagrindinė „bėda“ tame, kad kartą įrašius ant viršaus įrašinėti negalima. Iš naujo galima įrašinėti tik išvalius (suformatavus) visą puslapį. Tačiau nepagalvokit, kad vieną 16 bitų skaičių įrašius jau visas puslapis „susigadina“. Ne, jis nesusigadina. Jį visą galima įrašinėti po vieną 16 bitų duomeną. Sakykim, tokių duomenų į 1024 baitų puslapį telpa 512 vienetų. Taigi į šviežiai formatuotą puslapį galima įrašinėti 16 bitų duomenukus 512 kartų iš eilės, vis tolėliau. Taip su flešu ir dirbama: puslapiai gabaliukais įrašinėjami, o kai reikia, formatuojami. Nebegalima pakeisti puslapyje įrašytų duomenų.

Ir tai netgi ne visai taip. Čia iš „gyvenimiškos“ pusės taip gaunasi, kad įrašytų duomenų dažniausiai nepavyksta pakeisti. O realybėje yra kiek kitaip.

Labiausiai paplitusi flešinė atmintis yra NAND tipo, kuri įrašinėjama tik puslapiais. Tuo tarpu mikrovaldikliuose dėl poreikio įrašinėti mažesniais vienetais naudojama NOR atmintis.

Be to, flešinė atmintis, priešingai, nei manoma, yra mažiau atspari už EEPROM. Ji nusibaigia po 10000 perrašymų. T.y. 10 kartų trumpiau traukia, nei EEPROM.

Kai flešinė atmintis yra „tuščia“ arba „formatuota“, joje visos bitų celiukės yra „vieneto“ loginėje būsenoje. Todėl „tuščia“ flešinė atmintis yra pripildyta vienetukais: 1 1 1 1 1 1 1 1 1 1…

Arba „F“ raidukėmis, jei šešioliktainiu kodu žiūrėsma 🙂 Aišku, ane?

„Įrašymas“ į flešinę atmintį yra galimybė pakeisti kai kuriuos vienetukus nuliukais. Sakykim, norim į tuščią baitą (sakykim, kad baitą), kurio reikšmė yra 0xFF arba dvejetainis 11111111, įrašyti skaičiuką, sakykim, penki. Penki yra 0x05 arba dvejetainis 00000101. Paimamas šis skaičiukas, pavartomas kažkaip, pritaikoma „NOR“ operacija, norimai flešo celei praleidžiama atitinkama elektra ir ji įgyja va tokią reikšmę. Trinama ši atmintis puslapiais, nes trynimui padaromas vienas didelis elektros krūvio elektrodas visam celiukių (bitų) masyvui.

Taigi NOR flešo „programavimas“ arba „įrašymas“ reiškia kai kurių vienetukų pakeitimą nuliukais ir taip išsaugant norimą reikšmę. Galima tiem patiem skaičiukam pritaikyti „įrašymą“ dar kartą, tik tada jau vien likę vienetukai pasikeis į nuliukus, o vienetukai jau įrašytų nuliukų nepaveiks. Tiesiog vieną flešo bitą galima pakeisti vieną kartą: iš vieneto į nulį. O paskui jau viskas, kol visas puslapis nebus „ištrintas“ ir vėl masiškai pakeistas į vienetukus, įrašymas nebegalimas.

Nors tai neaktualu, bendram išsilavinimui pasakysiu, kad NAND atmintis gali būti ir įrašinėjama tik puslapiais. O STM32F10x šeimoje minimalus įrašomas duomenukas yra 16 bitų.

Žinant visus šiuos niuansus galima sugalvoti ir kokį nors „apėjimą“, kaip flešinę atmintį panaudoti vietoj EEPROM ar tiesiog panaudoti saviems duomenims saugoti. To prireikia, jei kuriame konfigūruojamą aparatėlį. T.y. tokį, kuris daro ne tik tai, kam suprogramuojamas, bet ir tai, kam yra galutinio vartotojo nustatomas. Na, sakykim, elektroninės radijos atveju galima įsiminti paskutinę nustatytą radijo stotį ar pan. Arba, išmanaus lituoklio atveju, mėgstamiausią kanifolijos uostytojo temperatūrą 🙂

Patys ST yra pateikę pavyzduką, kaip pasidaryti EEPROM emuliaciją su vidine flešine atmintimi. Principas ten toks, kad į flešinę atmintį rašomas 16 bitų duomenukas su dar vienu 16 bitų duomenuku — identifikatoriumi. Atminties puslapiai gauna po specialią antraštę, kad emuliatorius žinotų, kuris iš jų naudojamas. Paskui, kai reikia rasti duomenuką su konkrečiu identifikatoriumi, pradedama kažko ieškoti nuo puslapio galo, kol randama kažkas, kas nėra ištisi vienetukai. Na, tokio vienetukais užpildyto identifikatoriaus nereikėtų naudoti 🙂

Svarbu tai, kad naudojami keli flešo puslapiai. Kai jau į puslapį nebelieka vietos rašymui, jo aktualūs duomenukai perkeliami į kitą puslapį, o senasis suformatuojamas.

Šitą patį metodą aš nusprendžiau nusikopijuoti ir panaudoti savo reikmėms, tik kiek kitaip. ST pateiktame pavyzdy galima įrašinėti tik 16 bitų duomenis. Tuo tarpu mano galva tikrai yra patogu visokias konfigūracijas saugotis didesnėse struktūrose. Na, galima ir labai taupyti, bandyti sugrūsti bitukus į vieną baitą ar panašiai, ypač jei konfigūracija yra kokia nors on/off tipo. Bet galima ir netaupyti, kaip jau patogiau. Aš savo galutiniam projektui neapsisprendžiau dar, ar bandyt sugrūdinėt bitukus, ar nesukt galvos ir pasidaryti patogų programavimą. Kadangi jaučiu, jog procesoriuko atminties užteks, tai turbūt pasilengvinsiu gyvenimą ir pasidarysiu struktūras.

Taigi mano mintis maždaug tokia. Aktyviam flešinės atminties puslapiui irgi nustatyti kokią nors antraštę. Naudoti, be abejo, kelis puslapius pakaitomis, jei vienas persipildytų. Įrašinėti bet kokius duomenis: baitus, shortus, wordus, struktūras ir masyvus pabaičiui į flešą taip, kad paskui galima būtų iš ten su memcpy užkraut į RAM ar tiesiai iš flešo ir nuskaitinėt su pointeriu. Be abejo, po įrašytų duomenų būtų įrašomas dar vienas 16 bitų skaičiukas, kurio viršutiniame baite būtų saugomas įrašytos struktūros dydis baitais, o apatiniame — unikalus identifikatorius. Manau, kad 256 identifikatorių tikrai per akis, man kokių 10-20 tereikės. O kartu saugomas struktūros dydis palengvins flešo skenavimą, duomenų ten paiešką ir užkrovimą RAMan, jei reikės. Tai va, maždaug taip. Bepildomas flešinis puslapiukas atrodytų maždaug šitaip (16 bitų gabaliukais):

55 55 <-- aktyviai naudojamo puslapio antraštė
0D 0F <-- duomenukai
5C 4A <-- duomenukai
66 00 <-- duomenukai
05 00 <-- 5 baitų duomenų struktūra su identifikatoriumi 0
62 45 <-- duomenukai
02 01 <-- 2 baitų duomenų struktūra su identifikatoriumi 1
77 42 <-- duomenukai
51 DE <-- duomenukai
04 02 <-- 4 baitų duomenų struktūra su identifikatoriumi 2
12 AF <-- duomenukai
2C 88 <-- duomenukai
04 03 <-- 4 baitų duomenų struktūra su identifikatoriumi 3
0A 42 <-- duomenukai
51 DE <-- duomenukai
04 02 <-- 4 baitų duomenų struktūros su identifikatoriumi 2 naujesnė versija
FF FF <-- tuščias flešo puslapio likutis
FF FF
...

Darbas su šitokiais duomenimis taip panašiai ir vyksta, kaip ST siūlytame modelyje. Sakykim, liepiam surasti mums struktūrą su identifikatoriumi 0x03. Funkcija skaito nuo puslapio galo, kol randa kažką, kas nėra 0xFFFF. Tada tikrina apatinį baitą ir žiūri, ar jis atitinka ieškomą identifikatorių. Ne, neatitinka. Tada pointerį perkelia per tiek baitų, kokį skaičiuką rado įrašytą aukštesniame baite ir vėl tikrina apatinį baitą (t.y. kitą identifikatorių).

Kai reikia įrašyti kažkokius duomenukus, jie įrašomi į visų kitų duomenukų galą. Todėl naujausios versijos visada bus arčiausiai puslapio pabaigos. Kai puslapis prisipildo, visų duomenukų su unikaliais identifikatoriais paskutinės versijos perkeliamos į naują puslapį, o senasis puslapis išvalomas. Na, ir taip sukamasi tarp flešo puslapių, priklausomai nuo to, kiek jų išskiriame. Du yra minimumas, nebent pasiliktume galimybę laikinai viską nusikopijuoti į RAM ir tik po to išvalius puslapį vėl viską supūsti atgal.

Jei dar neatsibodo skaityti, nesidžiaukit. Varom toliau, į darbą su STM32F10x flešiukais.

Yra krūvelė flešo valdymo registrų. Aš apie juos nepasakosiu, tą galite pasiskaityti ten, kur duoda. Arba pasižiūrėti stm32f10x_flash.c failiuką beigi duotus pavyzdukus. Tikrai viskas paprasta. ST programuotojai paruošė biblioteką, kuri gali formatuoti flešo puslapius po vieną arba visus iš karto, įrašinėti 16 arba 32 bitų duomenukus, užrakinti/atrakinti flešą ir dar ten visko.

Va tik gaila, kad neparuošė dar vienos funkcijikės, kuri į flešą purptelėtų bet kokio norimo dydžio pointerį (ar tai masyvą). Tai teko biškį išsikapstyti pačiam.

Paprastai flešo atmintis yra „užrakinta“ ir į ją nieko neįrašysi. Atrakinama į tam tikrą registrą vieną po kito įrašius specialius „slaptažodžius“. Jie, aišku, nėra kokie nors slapti, iš bibliotekėlės galite pasiimti. Arba tiesiog naudoti FLASH_Unlock() ir FLASH_Lock().

Įrašymas į flešą vyksta kažkokio flešo adreso pointeriui priskiriant kažkokį kintamuką. Bet šis veiksmas nėra visai paprastas, reikia dar aplinkui daug lietaus šokių sušokti. Seka yra maždaug tokia:

  1. Atrakinam flešą
  2. Pasitikrinam dėl visa ko statusą: ar paskutinė flešo operacija pavyko
  3. Įjungiam CR registre „programavimo“ (t.y. įrašymo) bituką
  4. Įrašom į flešą: pointeriui priskiriam kažką reikiamo
  5. Vėl tikrinam statusą ir laukiam, kol duomenys bus įrašyti — tai užtrunka
  6. Jei dar turim duomenų, grįžtam į 4 punktą
  7. CR registre išjungiam programavimo bituką
  8. Užrakinam flešą

Skaityti flešą galima bet kada, bet kur ir bile kaip. const kintamieji ir taip fleše saugomi. Skaitymui flešas pasiekiamas tiesiog per savo adresą, t.y. pointeriais.

Kad galėtumėm su flešu darkytis, reikia žinoti jo adresus ir ribas. STM32F10x procesoriuose flešas prasideda nuo adreso 0x08000000. STM32F103C8T6 procesoriuose puslapiai yra 1024 baitų dydžio. High Density ir Connectivity Line, jei neklystu, puslapėliai yra 2048, t.y. dviejų kilobaitų.

STM32F103C8T6 turi 64 kB flešą. Taigi, flešo adresai yra nuo 0x08000000 iki 0x0800FFFF. Tai galim manyti, kad keli paskutiniai puslapiai bus tikrai laisvi. Kad ir keturi kokie ar dar daugiau. Sakykim, kad norim naudoti keturis puslapius nuo galo, tai pirmojo jų adresas bus 0x0800F000.

Jeigu labai norite, galimas ir kitas variantas. Sakykim, bijote ir norite garantuotis, kad ir jūsų firmwarė, ir reikiami puslapiai konfigūracijai tilps į flešą. Tuomet galimas variantas — pagal puslapius lygiuotas didelis masyvas-konstanta. Tik pirmą kartą po firmwarės perrašymo to masyvo užimamus puslapius būtina formatuoti, nes jie bus nuliukais užpildyti. Va tokio kintamojo pavyzdys su pagalbiniais difainais:

#if defined (STM32F10X_HD) || defined (STM32F10X_HD_VL) || defined (STM32F10X_CL) || defined (STM32F10X_XL)
#define PAGE_SIZE ((uint16_t)2048)
#else
#define PAGE_SIZE ((uint16_t)1024)
#endif

#define PAGE_COUNT 4

const uint8_t flash_pages[PAGE_SIZE*PAGE_COUNT] __attribute__((aligned (PAGE_SIZE))) = {};

Tokiu atveju būsite garantuoti, kad firmwarė su konfigūracijos puslapiais tikrai tilps į flešą. Lygiavimo atributas nurodo kompiliatoriui padėti šitą kintamąjį į artimiausio laisvo puslapio pradžią (nurodant puslapio dydį). Tad šio masyvo pradžia tikrai tikrai visada sutaps su flešo puslapėlio pradžia. Tad laisvai pasiimsite ir pirmojo puslapio pointerį.

Pavyzdinis (tiksliau, testinis) programos kodas ir mano bibliotekėlė darkymuisi su flešu bus kitukart. Kol kas pasidžiaukit teorija 😀

Reklama
Komentarai
  1. dmb-220 parašė:

    o nepaprasciau koki eeprom prikabinti ir naudotis?

    • Darau, Blė parašė:

      Nu viskas atsiremia į kainą ir papildomo daikto priprojektavimą. Aš, sakykim, lengviau šitą galiu programiškai išspręst, nes tuo užsiimu savo malonumui. Tad neišeina tiesiogiai pasakyti, kaip yra paprasčiau. Be to, vis tiek: yra to flešo, galima naudotis. Didžiausias gal trūkumas, kad neišlieka duomenys perrašant firmwarę.

  2. Skirmantas parašė:

    Aš čia ne į temą. Tik truputį į temą. Man tas įrašymas pabičiui prisiminimus sukėlė.

    Aš kadais, t.y. seniai seniai, kai universitete mums rodė bitukus paketuose nupaišytus ir pasakojo apie protokolus, tai prigalvojau, jog įrenginiai tarpusavyje susišneka būtent dėl tų protokolų, o receiveris dekoduoja emiterio žinutę, nes gauna tai ką suprogramuotas gauti ir ten kur suprogramuotas.

    Ir tada aš sugalvojau, kad visas tas kodavimas ir slaptinimas yra tik tai duomenų daliai ir tik tame lygyje, kuriame dešifruotojas tikisi rasti tą slaptumą.

    O juk galima gal, pavyzdžiui, siųsti kokią nors prasmingą informaciją užkoduojant ją taip, kad ji kartu neštų ir kitą slaptą informaciją, kurią būtų galima dekoduoti arba prieš arba po konvencinio dekoderio, pastarajam nesukeliant jokių įtarimų. Pavyzdžiui: eini browsinti kokį nors serverį ir jis tau ten krauna html’us visokius ir ekrane matai ten paveiksliukus ir tekstą, o tuo pačiu, kol maigai tą puslapį – slaptasis dekoderis dekoduoja kokią nors slaptą žinutę iš to serverio 😀

    Tai va tokie prisiminimai užpuolė.

    • Darau, Blė parašė:

      Nu tai jo, klasikinis, net ir universitetuose pasakojamas pavyzdys, papildomų bitukų įkišimas į JPEG paveiksliukų matricas. T.y, kompresija pritaikoma tokia, kad matricos galiukas liktų nupjautas, o ten sukišami papildomi bitukai. Jie yra „nelabai svarbūs“ ir paveiksliuko nesugadina. Tačiau su papildomu softu galima juos iš ten iškrapštyti. O šiais laikais yra ir daugiau visokių bliadstvų.

      • Skirmantas parašė:

        Nu. Pavyzdžiui spjaudyti paketus tam tikra tvarka, kuri nesvarbi pirmąjam dekoderiui. Tiesa, tada lėtokai gautųsi, bet bliadstvų tai visokių prigalvoti eina.

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti / Keisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti / Keisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti / Keisti )

Google+ photo

Jūs komentuojate naudodamiesi savo Google+ paskyra. Atsijungti / Keisti )

Connecting to %s