Saya mengalami kesulitan memahami manajemen memori.
Dokumentasi Arduino mengatakan, adalah mungkin untuk menjaga konstanta seperti string atau apa pun yang saya tidak ingin ubah selama runtime dalam memori program. Saya pikir itu tertanam di suatu tempat di segmen kode, yang harus cukup mungkin di dalam arsitektur von-Neumann. Saya ingin memanfaatkan itu untuk membuat menu UI saya pada LCD mungkin.
Tapi saya bingung dengan instruksi itu untuk hanya membaca dan mencetak data dari memori program:
strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
Serial.println( buffer );
Kenapa saya harus menyalin konten sialan ke RAM sebelum mengaksesnya? Dan jika ini benar, lalu apa yang terjadi pada semua kode? Apakah ini juga dimuat ke RAM sebelum eksekusi? Bagaimana kode (32kiB) ditangani kemudian dengan hanya 2kiB RAM? Di mana goblin-goblin kecil itu membawa disket?
Dan yang lebih menarik: Apa yang terjadi pada konstanta literal seperti dalam ungkapan ini:
a = 5*(10+7)
Apakah 5, 10 dan 7 benar-benar disalin ke RAM sebelum memuatnya ke dalam register? Aku tidak percaya itu.
sumber
string_table
array. Array itu mungkin 20KB, dan tidak akan pernah muat dalam memori (bahkan sementara). Namun Anda dapat memuat hanya satu indeks menggunakan metode di atas.Jawaban:
AVR adalah keluarga arsitektur Harvard yang dimodifikasi , jadi kode disimpan dalam flash saja, sedangkan data ada terutama dalam RAM ketika sedang dimanipulasi.
Dengan mengingat hal itu, mari kita jawab pertanyaan Anda.
Anda tidak perlu per se, tetapi dengan kode default mengasumsikan bahwa data ada dalam RAM kecuali jika kode tersebut dimodifikasi untuk secara khusus mencari di flash untuk itu (seperti dengan
strcpy_P()
).Nggak. Arsitektur Harvard. Lihat halaman Wikipedia untuk detail selengkapnya.
Pembukaan yang dihasilkan oleh kompiler menyalin data yang harus dimodifikasi / dimodifikasi menjadi SRAM sebelum menjalankan program yang sebenarnya.
Tidak tahu Tetapi jika Anda melihat mereka maka tidak ada yang bisa saya lakukan untuk membantu.
Tidak Kompiler mengevaluasi ekspresi pada waktu kompilasi. Apa pun yang terjadi tergantung pada baris kode di sekitarnya.
sumber
const uint8_t test1[5]= { 0x54, 0x65, 0x73, 0x74, 0x31 }; const uint8_t bla[9]= { 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x61, 0x62, 0x6c, 0x62 }; const uint8_t Menu[4]= { 0x3d, 0x65, 0x6e, 0x75};
bagaimana cara membawa data ini ke flash dan kemudian ke fungsi SPI.transfer (), yang membutuhkan satu uint8_t per panggilan.Ini adalah cara
Print::print
mencetak dari memori program di perpustakaan Arduino:__FlashStringHelper*
adalah kelas kosong yang memungkinkan fungsi kelebihan beban seperti cetak untuk membedakan pointer ke memori program dari satu ke memori normal, karena keduanya dilihatconst char*
oleh kompiler (lihat /programming/16597437/arduino-f- apa-apa-itu-sebenarnya-lakukan )Jadi Anda bisa membebani
print
fungsi untuk layar LCD Anda sehingga butuh__FlashStringHelper*
argumen, sebut sajaLCD::print
, lalu gunakanlcd.print(F("this is a string in progmem"));' to call it.
F () `adalah makro yang memastikan string ada di memori program.Untuk menetapkan standar string (agar kompatibel dengan cetak Arduino bawaan) saya telah menggunakan:
Saya pikir alternatif akan menjadi sesuatu seperti
yang akan menghindari para
__FlashStringHelper
pemain.sumber
Semua konstanta awalnya dalam memori program. Di mana lagi mereka berada ketika listrik mati?
Ini sebenarnya arsitektur Harvard .
Kamu tidak. Bahkan ada instruksi perangkat keras (LPM - Load Program Memory) yang memindahkan data langsung dari memori program ke register.
Saya punya contoh teknik ini dalam output Arduino Uno ke monitor VGA . Dalam kode itu ada font bitmap yang disimpan dalam memori program. Itu dibaca dari on-the-fly dan disalin ke output seperti ini:
Pembongkaran garis-garis tersebut menunjukkan (sebagian):
Anda dapat melihat bahwa satu byte memori program disalin ke R30, dan kemudian segera disimpan ke dalam register USART UDR0. Tidak ada RAM yang terlibat.
Namun ada kerumitan. Untuk string normal, kompiler mengharapkan untuk menemukan data dalam RAM bukan PROGMEM. Mereka adalah ruang alamat yang berbeda, dan karena itu 0x200 dalam RAM adalah sesuatu yang berbeda dari 0x200 dalam PROGMEM. Jadi kompiler mengalami kesulitan menyalin konstanta (seperti string) ke dalam RAM pada saat startup program, sehingga tidak perlu khawatir mengetahui perbedaannya nanti.
Pertanyaan bagus. Anda tidak akan lolos dengan string konstan lebih dari 2 KB, karena tidak akan ada ruang untuk menyalin semuanya.
Itu sebabnya orang-orang yang menulis hal-hal seperti menu dan hal-hal bertele-tele lainnya, mengambil langkah-langkah tambahan untuk memberikan string atribut PROGMEM, yang menonaktifkan mereka disalin ke dalam RAM.
Jika Anda menambahkan atribut PROGMEM Anda harus mengambil langkah-langkah untuk memberi tahu kompilator bahwa string ini berada dalam ruang alamat yang berbeda. Membuat salinan lengkap (sementara) adalah satu cara. Atau hanya mencetak langsung dari PROGMEM, satu byte setiap kali. Contohnya adalah:
Jika Anda melewatkan fungsi ini sebagai penunjuk ke string di PROGMEM, ia melakukan "pembacaan khusus" (pgm_read_byte) untuk menarik data dari PROGMEM daripada RAM, dan mencetaknya. Perhatikan bahwa ini membutuhkan satu siklus clock tambahan, per byte.
Tidak, karena mereka tidak harus seperti itu. Itu akan dikompilasi menjadi instruksi "muat literal ke register". Instruksi itu sudah ada dalam PROGMEM, jadi literal sekarang ditangani. Tidak perlu menyalinnya ke RAM dan kemudian membacanya kembali.
Saya memiliki deskripsi panjang tentang hal-hal ini di halaman Menempatkan data konstan ke dalam memori program (PROGMEM) . Itu memiliki kode contoh untuk mengatur string, dan array string, cukup mudah.
Itu juga menyebutkan makro F () yang merupakan cara mudah hanya mencetak dari PROGMEM:
Sedikit kompleksitas preprosesor memungkinkan kompilasi menjadi fungsi pembantu yang menarik byte dalam string dari PROGMEM byte pada suatu waktu. Tidak diperlukan penggunaan menengah RAM.
Cukup mudah untuk menggunakan teknik itu untuk hal-hal selain Serial (mis. LCD Anda) dengan menurunkan pencetakan LCD dari kelas Print.
Sebagai contoh, di salah satu perpustakaan LCD yang saya tulis, saya melakukan hal itu:
Poin kuncinya di sini adalah untuk memperoleh dari Print, dan menimpa fungsi "write". Sekarang fungsi utama Anda melakukan apa pun yang dibutuhkan untuk menghasilkan karakter. Karena berasal dari Print, Anda sekarang dapat menggunakan makro F (). misalnya.
sumber