Kehadiran EEPROM memberi pengembang alat yang nyaman untuk menyimpan parameter konfigurasi atau kondisi yang perlahan berubah sehingga pemadaman listrik harus bertahan. Pada artikel ini kita akan melihat bagaimana melakukan ini seaman dan senyaman mungkin agar tidak melupakan apa pun dan tidak mengingat apa yang tidak ada.
Misalkan kita memiliki variabel dan kita ingin menyimpannya dalam EEPROM. Tampaknya semua alat untuk ini ada di tangan kita:
#include <EEPROM.h> int my_var = DEFAULT_VALUE; EEPROM.get(MY_VAR_ADDR, my_var); my_var = NEW_VALUE; EEPROM.put(MY_VAR_ADDR, my_var);
Namun, pandangan yang lebih dekat mengungkapkan bahwa pendekatan ini menciptakan lebih banyak masalah daripada yang dipecahkan. Kami akan membahasnya secara berurutan.
1. Bagaimana memastikan bahwa kita membaca dengan tepat apa yang kita tulis (untuk menjamin
integritas )? Bayangkan gambar berikut. Kami menulis surat untuk diri kami sendiri jika kami mati mendadak karena kehilangan daya atau sinyal reset dan meletakkannya di laci meja. Dalam kehidupan selanjutnya, kita membuka laci meja, mengambil selembar kertas, membaca pesan dan melanjutkan misi kita. Masalahnya adalah bahwa di dalam kotak selalu ada lembaran kertas yang ditulis dengan teks acak. Jadi kita perlu cara untuk membedakan pesan yang benar dari yang acak. Orang bisa meyakinkannya tentang notaris, tetapi dalam kasus yang paling sederhana, tanda tangannya akan cukup jika kita memiliki cara untuk memverifikasi kebenarannya. Sebagai contoh, kita dapat menggunakan hasil dari ekspresi matematika tergantung pada teks sebagai tanda tangan, sehingga kemungkinan kebetulan acak cukup kecil. Dalam kasus paling sederhana, ini adalah CRC atau checksum. Ini akan melindungi kita tidak hanya dari membaca apa yang tidak kita tulis, tetapi juga dari membaca pesan yang rusak. Setelah semua, teks memudar dari waktu ke waktu, dan elektron dalam rana yang terisolasi bahkan kurang tahan lama - partikel akan terbang dari ruang angkasa dengan energi yang cukup, dan bit akan berubah. Tetapi ada cara lain untuk mendapatkan pesan yang rusak - ini bukan untuk menambahkannya sampai akhir. Ini tidak begitu eksotis, karena pada saat pencatatan, konsumsi saat ini meningkat tajam, yang dapat memicu kematian dini penulis.
2. Misalkan kita yakin akan kebenaran pesan, tetapi bagaimana saya bisa memastikan bahwa akulah yang menulisnya (untuk menjamin
keaslian ). Seperti kata pepatah, saya berbeda. Tiba-tiba, orang lain duduk di meja ini sebelum reinkarnasi saya, dan dia memiliki misi yang berbeda, dan untuk alasan apa saya sekarang akan dibimbing oleh pesannya? Jika kita memberikan catatan kita dengan label tertentu, akan lebih mudah bagi kita untuk membedakan label kita dari orang asing. Misalnya, label seperti itu bisa menjadi nama variabel yang kita simpan. Satu-satunya masalah adalah bahwa di EEPROM tidak ada banyak ruang untuk meletakkan nama variabel di sana, dan itu tidak nyaman untuk melakukannya, karena mereka memiliki panjang yang berbeda. Tapi untungnya ada cara yang lebih sederhana - Anda dapat menghitung checksum atas nama variabel dan menggunakannya sebagai jalan pintas. Pada saat yang sama, penting untuk menambahkan ukuran variabel dalam byte ke checksum ini agar tidak secara tidak sengaja membaca jumlah yang salah. Nah, demi kelengkapan, kami menambahkan pengenal numerik lain di sana, untuk menjamin membedakan variabel kami dari orang lain, bahkan jika mereka disebut sama. Kami menyebut nomor ini sebagai pengenal instan (terinspirasi oleh OOP jika nama variabel dianggap sebagai bidang objek). Jika kami meningkatkan misi kami ke versi baru yang radikal, sehingga pembaruan ini membuat semua yang lama tidak masuk akal disimpan, maka kita hanya perlu mengubah pengenal instan untuk membatalkan semua yang disimpan oleh versi lama.
3. Bagaimana saya bisa membuat operasi penulisan yang tidak lengkap membiarkan nilai lama yang disimpan tidak berubah? Artinya, operasi penyelamatan harus berhasil, atau seharusnya tidak memiliki efek yang dapat diamati sama sekali. Dengan kata lain, itu harus
atomik atau transaksional jika kita berbicara tentang transaksi yang turun ke pembaruan tanpa syarat dari satu nilai. Jelas, kami tidak dapat memastikan keaslian rekaman dengan menulis ulang nilai sebelumnya, kami harus menulis ke tempat baru sehingga nilai yang tersimpan lama tetap utuh, setidaknya sampai selesainya rekaman yang baru. Teknik ini sering disebut 'copy-on-write' jika hanya sebagian dari nilai yang disimpan diperbarui, tetapi bagian yang tetap tidak berubah masih disalin dan ditulis ke lokasi baru. Mengembangkan analogi kita, kita akan menulis surat kepada diri kita sendiri, membiarkan yang lama tidak tersentuh, tetapi memasok setiap surat dengan nomor seri yang meningkat sehingga dalam kehidupan kita selanjutnya kita memiliki kesempatan untuk menemukan huruf terakhir yang kita tulis. Namun, pada saat yang sama, muncul masalah baru - tempat di kotak tempat kami meletakkan surat-surat itu akan berakhir cepat atau lambat jika kita tidak membuang surat-surat lama yang telah menjadi tidak relevan. Mudah dipahami bahwa cukup hanya menyimpan 2 huruf - satu lama dan satu baru, mungkin sedang dalam proses penulisan. Karenanya, nomor surat juga tidak perlu banyak bit.
Anehnya, penulis tidak dapat menemukan satu implementasi yang akan memungkinkan organisasi penyimpanan data di EEPROM, sambil memastikan integritas, keaslian, dan atomisitas. Saya harus menulis sendiri ke
github.com/olegv142/NvTxUntuk menyimpan setiap variabel di EEPROM, 2 area berturut-turut digunakan - sel dengan struktur yang sama. Pengidentifikasi variabel dihitung berdasarkan ukurannya, label teks dan pengenal contoh ditulis dalam 2 byte pertama. Selanjutnya, data ditulis, diikuti oleh 2 byte checksum. Pada byte pertama, dua bit memiliki tujuan khusus. Bit yang paling signifikan adalah flag kebenaran, ketika menulis, itu selalu diatur ke satu. Bit orde rendah digunakan sebagai nomor bit-tunggal era, diperlukan untuk menemukan pesan terakhir. Rekaman dilakukan dalam sel 'dalam lingkaran'. Nomor era berubah setiap kali catatan dibuat di sel pertama. Oleh karena itu algoritma untuk menentukan sel yang terakhir direkam: jika zaman sel adalah sama, maka yang kedua ditulis terakhir, jika berbeda - maka yang pertama.
Bit yang benar tampaknya berlebihan, tetapi memiliki fungsi penting. Pertama-tama, kami membaca data yang disimpan dan memeriksa kebenaran kedua sel. Jika sel tidak lulus pemeriksaan untuk pengidentifikasi atau checksum yang benar, kami mereset bit kebenarannya. Operasi penulisan selanjutnya mungkin tidak memeriksa kebenaran sel, tetapi bergantung pada flag ini, yang mengurangi overhead sekitar 2 kali.
Mereka yang ingin mempelajari detail implementasi dapat melihat gambar dan kode di
repositori . Saya, agar tidak membuat pembaca bosan, teruslah gunakan. Fungsi menulis / membaca data masing-masing menerima 5 parameter, sehingga kenyamanan penggunaannya dikorbankan demi fleksibilitas. Tapi itu dengan murah hati dikompensasi oleh dua set makro, yang membuat menggunakan perpustakaan sesederhana dalam kasus EEPROM.get / put. Set makro pertama digunakan jika Anda hanya ingin menyimpan variabel ke alamat yang diberikan:
#include <NvTx.h> int my_var = DEFAULT_VALUE; bool have_my_var = NvTxGetAt(my_var, MY_VAR_ADDR); my_var = NEW_VALUE; NvTxPutAt(my_var, MY_VAR_ADDR);
Jika ada beberapa variabel yang akan disimpan, masing-masing harus menentukan alamat dan pada saat yang sama mempertimbangkan ukuran sehingga area memori tempat variabel disimpan tidak tumpang tindih. Untuk menyederhanakan tugas, set makro kedua mengimplementasikan alokasi alamat otomatis, dan melakukan ini
pada waktu kompilasi . Sebagai contoh,
perpustakaan Arduino-EEPROMEx dapat mengalokasikan memori saat runtime, sementara itu menyimpan alamat dalam RAM untuk setiap variabel yang disimpan. Pustaka
NvTx mengalokasikan ruang di EEPROM tanpa menambahkan apa pun ke kode yang dapat dieksekusi atau isi RAM.
#include <NvTx.h> int my_var = DEFAULT_VALUE; char my_string[16] = ""; NvPlace(my_var, MY_START_ADDR, MY_INST_ID); NvAfter(my_string, my_var); bool have_my_var = NvTxGet(my_var); my_var = NEW_VALUE; NvTxPut(my_var);
Makro NvPlace menetapkan alamat awal area EEPROM, tempat kami akan menyimpan variabel, dan pengidentifikasi contoh. Makro NvAfter cadangan wilayah memori untuk menyimpan argumen pertama segera setelah wilayah memori disediakan untuk yang kedua. Saat mengalokasikan memori, juga diverifikasi bahwa kami tidak melampaui ukuran EEPROM yang tersedia, dan juga kami tidak memesan area memori yang tumpang tindih (ini dapat terjadi jika dua NvAfter makro memiliki argumen kedua yang sama). Jika terjadi pelanggaran terhadap salah satu dari dua kondisi yang ditentukan, program tidak dapat dikompilasi. Mereka yang ingin berurusan dengan mekanisme alokasi memori akan menemukannya di file header
NvTx.h. Semua makro NvPlace dan NvAfter lakukan adalah mendefinisikan enumerasi, membentuk nama mereka berdasarkan nama variabel, dan juga menggunakan konstruksi idiomatis yang sangat berguna dari
waktu kompilasi yang ditegaskan .
Semoga perpustakaan
NvTx akan membantu pembaca menulis kode kelas industri yang andal.