Proyek ini tidak mengandung ArduinoProyek ini awalnya seharusnya terlihat berbeda - struktur monumental yang terdiri dari alas dengan kaleng dan pompa, akuarium dipasang di atasnya dan oasis tomat di atasnya. Sebuah air terjun direncanakan di surga oasis tomat, dan bentuk kehidupan ikan di akuarium, persyaratan utama yang adalah kemampuan untuk makan penghuni akuarium yang tidak terencana dan menjaga gelas tetap bersih; kandidat utamanya adalah somiki dan gurami. Seperti yang mungkin sudah Anda duga, moto saya adalah "kemalasan adalah mesin kemajuan" (dan apa yang dapat Anda lakukan agar Anda tidak membersihkan akuarium dan tidak menyirami tomat).Sebuah monumen untuk moto ini mungkin akan didirikan jika belum runtuh pada tahap koordinasi draf garis besar dengan istrinya. Dia tidak terinspirasi oleh ide menjadikan bandura ini dekorasi utama ruang tamu, dan bahkan air terjun tidak meyakinkannya tentang hal ini. Tetapi gagasan tentang sistem otonom, simbiosis biologi dan elektronik, tidak mau terbang keluar dari kepalaku, dan proyek itu menyempit ke ukuran pot bunga - aquaponik berubah menjadi hidroponik, kehidupan ikan diselamatkan.Gagasan utama hidroponik adalah penggunaan larutan nutrisi berair alih-alih tanah. Ini memungkinkan urutan besarnya untuk mempercepat pertumbuhan tanaman. Namun, seseorang tidak bisa hanya menurunkan akar ke dalam air - mereka membutuhkan oksigen, yang tanpanya mereka akan mulai mati. Dalam hal ini, ada pilihan - terus-menerus meniup air dengan kompresor, seperti di akuarium, atau secara berkala membasahi akar dengan larutan nutrisi, dan tiriskan setelah beberapa waktu. Opsi pertama memiliki kelemahan - senandung kompresor yang konstan. Pilihan kedua memiliki keunggulan - sebagian besar akar berada di udara, pernapasan aktif, dan efek mempercepat pertumbuhan harus lebih besar. Selain itu, mereka direndam dalam substrat butiran berpori khusus yang mempertahankan kelembaban. Pilihannya jelas, saya mengambil opsi kedua sebagai dasar.Dalam kasus ikan, sistem bisa berubah menjadi hampir sepenuhnya tertutup - sekresi ikan diproses oleh bakteri khusus dalam biofilter, produk olahan diumpankan ke tanaman, lapisan pasir menyaring air, air bersih dikembalikan ke akuarium. Dalam kasus yang ideal, pakan kadang-kadang ditaburkan ke pengumpan otomatis, dan tomat berkumpul dari semak-semak. Tapi itu tidak tumbuh bersama, mungkin itu menjadi lebih baik - siapa yang tahu bagaimana urutan melalui surat dari bakteri yang diperlukan akan berakhir.Akibatnya, perangkat tanaman tomat mengambil kontur. Dua kapal - yang bawah dengan air, yang atas dengan substrat dan tanaman. Untuk banjir kita akan menggunakan pompa Cina kecil dengan motor DC, untuk drainase kita akan menggunakan siphon otomatis. Prinsip pengoperasian siphon dalam video:Hidroponik dengan siphon yang serupa:Otak perangkat adalah mikrokontroler ATMEGA328P (hanya karena placer sudah dekat). Tugasnya termasuk mengelola banjir dan pembuangan sesuai dengan jadwal, memantau ketinggian air di dalam tangki dan menandakan kekurangannya, mengendalikan iluminasi tanaman (kami ingin memiliki panjang siang minimum minimum tertentu; ketika cahaya alami berakhir, cahaya buatan secara bertahap menyala), antarmuka pengguna untuk melihat status, manajemen, dan konfigurasi seluruh ekonomi ini. Jelas, ini membutuhkan semacam solusi untuk sensor ketinggian air, sensor cahaya, jam waktu nyata, dan semacam terminal pengguna.Sebelum menjelaskan detailnya, daftar sumber daya proyek:Di sini Anda dapat melihat foto hasil dan proses pembuatannya.Video pendek:Proyek ini tersedia di GitHub . Di sana, dalam rilis, file dengan proyek bagian elektronik di KiCAD dan proyek desain dan peluit di SolidWorks ditata (file STL untuk pencetakan dilampirkan).Fitur-fitur perakitan firmware—
« ». , , , USB AVR (, , , , ), . - , , 'ADK_ROOT' , 'scons'.
Skema bagian elektronik:
Rincian lebih lanjut, deskripsi tentang jebakan dan sedikit kode. Deskripsi masalah perangkat lunak di bagian paling akhir . Mungkin seseorang akan tertarik untuk melihat contoh baru bekerja dengan I2C, valcoder, modul RTC, dan tampilan grafik. Semua kode dalam proyek ditulis "dari awal" tanpa menggunakan solusi pihak ketiga (karena saya bisa).Sensor level air
Masalah paling sensitif diputuskan terlebih dahulu. Tentu saja ada varian dari beberapa jenis pelampung sehingga, misalnya, akan memindahkan rel tempat kode Gray diterapkan, dan sensor optik akan dibaca. Tapi itu benar-benar terlihat tidak dapat diandalkan. Pencarian di eBay tidak membuahkan hasil - ada saklar apung (level yang diinginkan tercapai atau tidak), atau elektroda terbenam dan bacaan berdasarkan konduktivitas medium, tetapi ini segera dicatat, karena komposisi air akan terus berubah seiring dengan konduktivitas dari penambahan pupuk dan pembubaran kotoran dari substrat. Akibatnya, muncul ide untuk menggunakan pengintai ultrasonik, salah satu yang biasanya ditempatkan pada robot yang berbeda. Seperti yang direncanakan, sensor ditempatkan di tutup tangki dan sinyal dipantulkan langsung dari permukaan air. Telah dibeli HC-SR04 (pilihan nilai terkecil dari jarak kerja minimum - ia memiliki 2cm),dan konsepnya diperiksa pada seember air. Ternyata itu bekerja untuk dirinya sendiri (ada kekhawatiran bahwa tidak akan ada pantulan normal dari permukaan air, atau bahwa tidak akan ada cukup directivity balok dan akan ada pantulan yang tidak diinginkan dari dinding tangki). By the way, pengintai juga merupakan opsi cadangan, tetapi inframerah. Di permukaan air seharusnya melempar pelampung dengan reflektor. Satu-satunya masalah adalah jarak kerja minimum mereka 10cm (dari yang saya temukan), yang sudah sedikit banyak untuk dimensi yang diberikan.Di permukaan air seharusnya melempar pelampung dengan reflektor. Satu-satunya masalah adalah jarak kerja minimum mereka 10cm (dari yang saya temukan), yang sudah sedikit banyak untuk dimensi yang diberikan.Di permukaan air seharusnya melempar pelampung dengan reflektor. Satu-satunya masalah adalah jarak kerja minimum mereka 10cm (dari yang saya temukan), yang sudah sedikit banyak untuk dimensi yang diberikan.
Menurut hasil proyek, pendekatan ini bekerja, dan dapat digunakan dalam praktik, tidak ada masalah yang diperhatikan. Sebaiknya mengambil tindakan untuk mengisolasi papan dari kelembaban (penyegelan dalam case). Itu hanya sensornya sendiri yang tetap terbuka, mungkin masih ada.Antarmuka sensornya sederhana - pulsa dikirim ke input pemicu, yang memicu sinyal gema. Sebuah pulsa dihasilkan pada output gema, yang panjangnya sama dengan waktu dari awal radiasi hingga penerimaan sinyal gema yang dipantulkan. Dengan mengukur panjang pulsa, mengetahui kecepatan suara dan fakta bahwa sinyal menuju ke objek dan kembali, Anda dapat menghitung jarak. Dalam proyek ini, ini diterapkan di kelas LevelGauge. Untuk mengukur panjang pulsa, kemampuan perangkat keras “input input” MK AVR digunakan. Dalam hal ini, pengatur waktu perangkat keras diatur ulang pada tepi naik dari pulsa, dan pada nilai pengukur waktu ke bawah, perangkat keras disimpan dalam register ICR1, dan interupsi dihasilkan. Dengan demikian, dimungkinkan untuk mengukur durasi pulsa dengan akurasi yang cukup dan konsumsi waktu prosesor minimum.Bahkan dengan model sensor ini, sebuah kesalahan diperhatikan - ketika daya diterapkan, garis gema tetap terus aktif. Dia memintas dengan menerapkan denyut nadi ke pelatuk dan menunggu sampai siklus gema-lokasi pertama berlalu.Lampu latar
Lampu latar dibuat oleh tiga pegangan LED. Saya menekuk bingkai segitiga dari profil aluminium, menempelkan LED di atasnya dengan epoksi. Saya memesan stabilizer China saat ini dengan daya 700mA. Sekitar tiga volt jatuh pada setiap dioda, stabilizer memerlukan perbedaan antara voltase input dan output setidaknya dua volt, dan saya akan memberi daya seluruh wunderwafer dari catu daya 12 volt. Dari sini mudah untuk menghitung mengapa tepatnya tiga LED.Dioda berwarna putih hangat. Bagiku itu alami, spektrum matahari dan semua itu. Tetapi seperti yang saya ketahui kemudian, setelah saya memesannya, tanaman biasanya menggunakan kombinasi merah dan biru. Seperti yang saya pahami, seluruh pertanyaan hanya dalam efisiensi. Jika Anda memiliki pertanian besar dengan pencahayaan sepanjang waktu, maka Anda tertarik bahwa semua energi yang dihabiskan dihabiskan untuk selamanya. Di bawah pencahayaan putih, daun hijau akan mencerminkan komponen hijau, sebagian besar energi yang dihabiskan untuk pencahayaan akan terbuang sia-sia.

Fitur penting dari stabilizer adalah adanya input untuk pengaturan PWM, yang saya gunakan untuk menyesuaikan kecerahan. Ini adalah penggaruk Cina lainnya. Pertama, ternyata hanya fungsional saat ini on / off. Artinya, saya berharap bahwa arus keluaran tidak akan dimodulasi, dan nilainya akan tergantung pada siklus tugas sinyal PWM, tetapi arus hanya mengulangi pulsa pada input kontrol. Tapi ini tidak terlalu buruk, penyergapan lain adalah bahwa regulator bereaksi tidak memadai terhadap PWM dengan frekuensi yang cukup tinggi. Saya harus menurunkannya ke 300Hz, di mana ia bekerja lebih atau kurang normal. Sinyal PWM dihasilkan oleh mikrokontroler dalam perangkat keras menggunakan salah satu timer.
Bagian penting lain dari perakitan lampu latar adalah sensor cahaya. Fototransistor dipilih dalam peran ini. Dan ya, ada dua di antaranya - satu di atas LED untuk mengukur cahaya alami, yang kedua di bawah LED untuk memberikan umpan balik. Benar, fungsi ekstensi siang hari otomatis belum diterapkan, seperti di musim panas, dan itu tidak perlu (dan motivasi adalah masalah serius). Diasumsikan bahwa segera setelah sensor pertama mendeteksi penurunan tingkat pencahayaan (dan waktu yang dialokasikan untuk siang hari belum kedaluwarsa), lampu diatur sehingga sensor kedua menghasilkan tingkat yang sesuai dengan iluminasi yang diinginkan. Untuk melakukan ini, Anda perlu menerapkan kontroler PID sederhana dalam kode. Tetapi sementara di antarmuka Anda hanya dapat melihat pembacaan sensor saat ini, dan secara manual memutar kecerahan cahaya latar yang diinginkan.Perhatikan koneksi sensor. Masing-masing dari mereka memiliki dua rentang tetap, yang dipilih dengan menghubungkan ke nol resistor yang sesuai. Kaki mikrokontroler, terhubung ke resistor kedua, pada saat ini ditransfer ke keadaan resistansi tinggi. Anda dapat menyalakan kedua resistor secara bersamaan, maka akan ada tiga rentang pengukuran tetap. Sinyal dari resistor emitor dilewatkan melalui sirkuit RC untuk menyaring pulsa modulasi - cahaya dari LED berdenyut bersama dengan sinyal PWM pada regulator saat ini.Pompa
Cina paling murah, persneling, dengan motor DC. Penyergapan, tentu saja, tersedia. Terlepas dari kenyataan bahwa ia mengatakan 12V, itu tidak berfungsi untuk waktu yang lama pada tegangan ini. Satu terbakar sebelum perakitan struktur. Skema menyediakan PWM untuk itu, daya maksimum dikonfigurasi di antarmuka, dalam praktiknya tidak ditetapkan di atas 70%. Sudah pada level ini, dia melolong liar di tempat kerja, tetapi sebagian besar waktu dia bekerja pada daya yang jauh lebih rendah - sekitar 30% dan bergemuruh dengan tenang. Tentang mode operasinya di bawah ini, dalam deskripsi logika flooding. Kapasitor yang lebih besar (C8 dalam diagram) harus diposisikan lebih dekat ke sirkuit daya pompa, jika tidak akan ada gangguan besar ke seluruh rangkaian (dalam praktiknya, ternyata regulator saat ini untuk LED paling sensitif terhadap mereka, musik ringan dimulai).Jam waktu nyata
Ada ide gila untuk menggunakan sumber daya mikrokontroler untuk tujuan ini. Generator jam kuarsa memiliki akurasi yang cukup baik, di proyek lain pendekatan ini bekerja dengan baik. Tetapi masalahnya adalah benar-benar semua timer perangkat keras sudah diambil untuk keperluan lain. Tidak ada pilihan selain menemukan modul RTC eksternal. Puji orang Cina, mereka ada di sana dan murah.
Modul berdasarkan DS3231 memiliki antarmuka I2C, catu daya redundannya sendiri - waktu tidak akan salah dengan pemadaman. Ada keluaran berliku-liku pada beberapa frekuensi tetap - 1 kHz, 4 kHz dan 8 kHz. Ini sangat berguna untuk sinyal audio - sekali lagi, Anda tidak perlu memuat MCU, dan tidak ada timer gratis untuk ini. EEPROM 32Kbit adalah bonus, tetapi tidak digunakan dalam proyek ini.Anehnya, ini sangat akurat - dalam beberapa bulan waktu kehilangan kekuatannya selama beberapa detik. Dia menyatakan bahwa dia memperhitungkan pengaruh suhu pada frekuensi generator, dan tampaknya ini berfungsi. Namun, jika waktu berlalu, ada kemungkinan koreksi frekuensi perangkat lunak. Pembacaan sensor suhu tersedia, dan dalam proyek ini ditampilkan di antarmuka.Kelas Rtc bertanggung jawab untuk bekerja dengan modul ini dalam kode.Tampilan
Saya sudah lama ingin melakukan sesuatu dengan tampilan grafis. Mencari yang termurah dengan antarmuka I2C memberi opsi ini.
Monochrome OLED menampilkan 128x64 piksel berdasarkan pada pengontrol SSD1306 yang agak populer. Saat memilih, Anda harus hati-hati melihat deskripsi - chip yang sama mendukung antarmuka lain, kecuali I2C, dan ada opsi tanpa itu. Atau mereka menulis bahwa ini universal, mendukung I2C juga, tetapi dalam kenyataannya akan perlu untuk sedikit memodifikasi papan dengan mengatur ulang nol ke situs lain. Oleh karena itu, jika Anda berencana untuk menggunakan I2C, lebih baik untuk memilih satu di mana hanya I2C ditampilkan di papan tulis, akan ada lebih sedikit keributan dengan papan yang tidak memiliki hampir semua dokumentasi (dokumentasi hanya untuk chip). Versi ini bekerja dari 5V, board memiliki regulator 3.3V yang diperlukan untuk controller. Saya bertemu ulasan yang dalam beberapa versi mungkin tidak.Layar umumnya puas. Saya perhatikan hanya satu fitur yang tidak menyenangkan - kecerahan baris piksel tergantung pada berapa banyak piksel yang menyala di dalamnya. Semakin terang, semakin rendah kecerahan. Kontras antara garis mungkin mencolok jika area yang terisi penuh dengan beberapa elemen sempit bergantian di layar. Namun dalam praktiknya, ini tidak terlihat dalam gambar saya dan tidak mencolok.Pengontrol dapat dikonfigurasikan untuk beroperasi dalam berbagai mode untuk menampilkan isi memori layar pada matriks piksel. Itu lebih nyaman bagi saya ketika setiap byte dipetakan ke kolom vertikal delapan piksel tinggi, dan kolom pergi secara horizontal dari kiri ke kanan, mengisi layar dengan garis delapan piksel tinggi. Dalam mode ini, lebih mudah untuk menggambar teks.Seringkali, suatu pendekatan dipraktikkan di mana memori tampilan diduplikasi dalam RAM MCU - pertama, semua tindakan dengan gambar dilakukan dalam RAM, dan kemudian semua piksel yang diubah disalin ke memori tampilan. Dalam proyek ini, pendekatan ini tidak digunakan untuk menghemat sumber daya. Semua tempat yang diubah digambar ulang langsung dalam memori tampilan.Seperti yang disarankan dalam komentar, tampilan OLED memudar seiring waktu. Saya juga menduga ini (mengingat apa itu screen saver), dan menyediakan tampilan untuk dimatikan setelah beberapa menit setelah aktivitas terakhir pada kontrol. Menyala saat menyalakan atau menekan encoder.Dalam kode, bekerja dengan layar diimplementasikan di kelas Tampilan.Valkoder:
Menurut pendapat saya, valcoder adalah opsi kontrol terbaik untuk perangkat seperti itu yang memiliki setidaknya beberapa antarmuka pengguna. Ini kompak dan sangat nyaman. Lebih mudah bagi mereka untuk membuka dan memilih item menu, mengubah nilai parameter apa pun, beralih mode, dll.Untuk menghubungkan, diperlukan tiga kaki input mikrokontroler. Satu untuk tombol (Anda dapat menekan pegangan), dua untuk valcoder itu sendiri. Ada sinyal kode Gray dari encoder . Pada setiap langkah belokan, satu bit berubah pada dua baris. Urutan menentukan arah rotasi.Semuanya tampak sederhana, tetapi, tampaknya, pengembang tidak selalu dapat membuat dukungan berkualitas tinggi untuk perangkat semacam itu. Sebagai contoh, pada printer 3D saya ada papan RAMPS dan papan dengan layar dan encoder yang sama persis terhubung. Firmware Marlin bekerja dengannya, tetapi pengalaman menggunakannya sangat buruk - tidak ada perasaan dapat diandalkan - ketika Anda mengklik kenop saat memutar kenop, antarmuka sering berhenti pada item menu yang salah atau nilai parameter yang diharapkan. Dengan rotasi cepat, rasanya klik dilewati. Pada titik tertentu, beralih tidak dimulai saat klik, tetapi di suatu tempat di antaranya, sangat tidak menyenangkan. Ya, apa itu Marlin, saya terkadang memiliki perasaan yang sama pada sistem multimedia bawaan di mobil. Dalam hal ini, beberapa tips (dan, tentu saja, lihat kode di sekitar kelas RotEnc).Pertama, poin yang cukup jelas bagi siapa saja yang menghubungkan tombol apa saja ke mikrokontroler - Anda harus berurusan dengan bouncing. Encoder mekanis ini dan jalur sinyalnya, pada kenyataannya, adalah tombol yang sama, dan mereka juga memiliki obrolan. Pertama, kami memfilter obrolan, lalu kami memproses urutan status garis sinyal. Mungkin ada valcoder dengan sensor optik, itu sudah tergantung pada skema pemrosesan sinyal dari mereka. Jika kaki-kaki sebuah fototransistor dibawa keluar secara langsung, maka ia dapat berputar di sana dengan rotasi lambat, tetapi jika ada skema pemrosesan yang memperkenalkan histeresis, maka penindasan perangkat lunak tidak diperlukan. Tetapi perangkat seperti itu lebih mahal dan jarang digunakan dalam perangkat amatir, yang paling umum adalah mekanik, beberapa dolar banyak.Kedua, poin yang sedikit kurang jelas, mungkin salah satu dari yang Marlin terbakar - pegangan memiliki posisi stabil selama rotasi - klik (klik). Model ini memiliki empat langkah dari urutan kode untuk setiap klik. Jadi, Anda perlu merespons klik, dan bukan pada langkah-langkah urutannya. Dan yang paling penting adalah melakukan sinkronisasi dengan posisi stabil. Banyak yang cukup memasukkan STEPS_PER_CLICK konstan, dan, misalnya, merespons setiap langkah keempat. Tetapi masalahnya adalah bahwa sinyalnya tidak sempurna, urutannya mungkin tidak sepenuhnya benar. Dengan ejaan tertentu, kode dapat “tersesat”, sebagai akibatnya, setiap langkah keempat akan diperoleh di suatu tempat di tengah klik, yang akan membuat pengguna tidak nyaman. Pada saat yang sama, posisi tetap dari pegangan untuk model tertentu sesuai dengan nilai kode tetap,itu harus dilampirkan padanya.Ketiga, sekali lagi, titik yang cukup jelas bagi pengembang sistem mikrokontroler yang kurang lebih berpengalaman - gunakan interupsi perangkat keras untuk mengubah keadaan jalur input. Minimal, akan ada sedikit risiko "kehilangan" langkah-langkah urutan. Tetapi secara umum, seperti yang Anda tahu, gangguan adalah segalanya bagi kami. MCU harus tidur bila memungkinkan, bangun hanya dengan gangguan - baik dari luar, atau dari timer untuk melakukan tugas yang tertunda. Ini adalah prinsip-prinsip desain arsitektur sistem yang baik.Desain secara keseluruhan
Itu terbuat dari bahan improvisasi dan berbagai bagian yang dicetak pada printer 3D dari ABS.Prinsip pengoperasian siphon diilustrasikan dalam video di atas. Bagi saya, ini adalah tabung PVC eksternal, dan tabung internal dengan corong di ujungnya. Untuk sifon klasik, diperlukan lutut lain, tetapi sudah sulit untuk membuatnya secara konstruktif. Ketika masalah dengan saluran pembuangan ditemukan, bak mandi kecil tersangkut di dinding tangki bagian bawah, di mana ujung pipa bagian dalam direndam, dan menciptakan ketahanan terhadap saluran pembuangan, sehingga sifon dapat bekerja.Ternyata ABS adalah bahan yang sangat hidrofobik. Air benar-benar tidak meluap melalui itu, saya bahkan harus mengulang corong siphon. Layak mempertimbangkan properti ini, tidak mungkin membuat sistem hidrolik miniatur (misalnya, saya ingin membuat permukaan panduan pada corong sifon, untuk memutar air, untuk meningkatkan respons sifon. Tetapi dengan dimensi dan hidrofobisitas ABS seperti ini tidak masuk akal) .Saya juga mencoba untuk merekatkan semuanya bersama-sama dengan pistol lem panas. Itu tidak berhasil - pada awalnya semuanya tampak kuat, tetapi setelah beberapa hari jatuh sendiri. Pilihan terbaik adalah Asia Tengah. Detail, bahkan di bawah air, pegang erat-erat.Kesalahan perhitungan terbesar dalam desain adalah wadah transparan. Saya benar-benar lupa tentang fakta bahwa air mekar dalam cahaya. Saya harus membungkusnya dengan bahan buram. Nah, Anda dapat secara berkala menambahkan kalium permanganat untuk disinfeksi, ini tampaknya tidak membahayakan tanaman.Algoritma flooding adalah sebagai berikut - pertama pompa dihidupkan dengan daya rendah dan mengisi seluruh tangki bagian atas dengan diam-diam. Proses ini dimonitor oleh sensor level. Ketika air mulai meluap melalui corong siphon, level penurunan di tangki bawah berhenti, yang terdeteksi oleh sensor. Aliran kecil yang dibuat dengan daya rendah tidak cukup untuk memicu siphon. Pompa berhenti, volume yang dipompa ke tangki atas diingat. Akar disimpan dalam larutan selama beberapa menit, setelah itu pompa hidup kembali. Pertama, pada daya rendah, sampai air mencapai corong lagi (selama downtime, ia turun lebih rendah karena efek siphon), dan ketika tingkat corong tercapai, pompa beralih ke daya yang meningkat, menyediakan aliran yang cukup untuk menyedot siphon untuk beroperasi. Aliran melalui siphon dijamin lebih tinggi dari aliran pompa,akibatnya, level di tangki bawah mulai naik, ini terdeteksi oleh sensor dan pompa berhenti.Siklus banjir mulai secara berkala, pada interval waktu tetap dan dapat dikonfigurasi, dari subuh hingga senja. Menurut rencana, fajar seharusnya diperbaiki oleh sensor cahaya, dan panjang siang hari, jika perlu, diperluas ke nilai yang ditetapkan, tetapi sampai saat itu tangan belum mencapainya. Waktu subuh hanya diatur dalam pengaturan.Dan di mana C ++ 11?
Mungkin seseorang akan meragukan bahwa C ++ 11 dapat berguna dalam pemrograman mikrokontroler (di antara mereka yang umumnya sadar bahwa mikrokontroler dapat diprogram dalam C ++). Saya akan mencoba memberikan contoh spesifik tentang manfaat C ++ 11 di area ini (selain hal-hal kecil yang jelas menyenangkan seperti constexpr, override, default, dll.).Penempatan sumber daya string
Banyak orang tahu bahwa RAM dalam mikrokontroler adalah sumber daya yang sangat terbatas. Ini bisa menjadi masalah jika aplikasi Anda, misalnya, memiliki antarmuka pengguna, dan program Anda menggunakan sejumlah baris yang cukup besar. Jika dalam kode untuk menulis sesuatu sepertiPromptUser("Are you sure you want to format SD-card?");
maka garis yang dilewati dalam argumen akan ditempatkan di bagian data yang diinisialisasi (selanjutnya, perilaku kompiler GCC untuk platform AVR) - yaitu, di area RAM, yang saat startup (sebelum fungsi utama dipanggil) diinisialisasi dari memori flash program. Fungsi PromptUser () akan memberikan pointer ke lokasi yang diinginkan dalam RAM. Jika Anda menggunakan pendekatan serupa di seluruh program, maka RAM akan berakhir dengan cepat (di ATMEGA328P yang digunakan dalam proyek ini hanya 2 kilobyte, dan ini juga untuk BSS, heap dan stack). Untuk mengatasi batasan ini, fungsi-fungsi seperti PromptUser () belajar untuk bekerja bukan dengan pointer ke RAM, tetapi dengan pointer ke suatu wilayah dalam memori flash program. Anda dapat membaca dari sana hanya dengan bantuan instruksi khusus, yang, misalnya, dalam avr-libc dibungkus dengan fungsi keluarga eeprom_read_ [byte | word | dword | ...].Dalam hal ini, string pertama-tama harus ditempatkan dalam variabel yang dilengkapi dengan atribut PROGMEM, yang memberitahu kompiler bahwa ia harus ditempatkan dalam memori program .char prompt[] PROGMEM = "Are you sure you want to format SD-card?";
PromptUser(prompt);
Ini merepotkan jika Anda ingin mendeklarasikan semua lini secara terpusat. Maka Anda harus terlebih dahulu mendeklarasikan deklarasi mereka di file header:extern char prompt[] PROGMEM;
Dan dalam file .cpp terpisah, tentukan:char prompt[] PROGMEM = "Are you sure you want to format SD-card?";
Duplikasi kode, yang tidak baik, dan sangat merepotkan ketika ada banyak baris seperti itu. Ya, ini dapat dielakkan dengan membuat makro yang rumit, dan termasuk file header dalam file .cpp yang terpisah, di mana makro akan diperluas ke dalam definisi, sementara dalam konteks lain akan diperluas ke dalam deklarasi. Tetapi dengan C ++ 11 ada opsi yang lebih bersih jika Anda menggunakan inisialisasi anggota kelas ketika mendeklarasikan. Di file header, deklarasikan kelas dengan baris:#define DEF_STR(__name, __text) \
const char __name[sizeof(__text)] = __text;
class Strings {
public:
DEF_STR(Prompt, "Are you sure you want to format SD-card?")
DEF_STR(OtherString, "...")
…
} __attribute__((packed));
extern const Strings strings PROGMEM;
Dalam file .cpp:const Strings strings PROGMEM;
Sekarang semua baris dideklarasikan di satu tempat, ditempatkan dalam memori program, dan Anda dapat mengaksesnya seperti ini:PromptUser(strings.prompt);
Dalam proyek ini, pendekatan berdasarkan prinsip yang sama digunakan untuk menentukan bitmap - berbagai gambar yang ditampilkan pada tampilan grafis.
struct Bitmap {
const u8 *data;
u8 numPages,
numColumns;
} __PACKED;
template<u8... data>
constexpr static u8
Bitmap_NumDataBytes()
{
return sizeof...(data);
}
#define DEF_BITMAP(__name, __numPages, ...) \
const u8 __CONCAT(__name, __data__) \
[Bitmap_NumDataBytes<__VA_ARGS__>()] = { __VA_ARGS__ }; \
const Bitmap __name { \
reinterpret_cast<const u8 *>(OFFSETOF(Bitmaps, __CONCAT(__name, __data__))), \
__numPages, \
sizeof(__CONCAT(__name, __data__)) / __numPages};
class Bitmaps {
public:
DEF_BITMAP(Thermometer, 1,
0b01101010,
0b10011110,
0b10000001,
0b10011110,
0b01101010
)
DEF_BITMAP(Sun, 1,
0b00100100,
0b00011000,
0b10100101,
0b01000010,
0b01000010,
0b10100101,
0b00011000,
0b00100100
)
...
};
extern const Bitmaps bitmaps PROGMEM;
Perbedaannya adalah bahwa selain data gambar itu sendiri, perlu juga untuk menempatkan atribut (ukuran gambar). Setiap byte mendefinisikan kolom delapan piksel. Kolom dapat mengisi satu atau lebih baris, jumlahnya ditunjukkan oleh parameter kedua setelah nama. Ternyata ketinggian bitmap harus kelipatan delapan untuk lebar sewenang-wenang, yang cukup dapat diterima untuk proyek ini.Literal biner
Anda mungkin sudah memperhatikan bahwa bitmap pada contoh sebelumnya menggunakan literal biner untuk menentukan. Ini benar-benar sangat nyaman - Anda dapat mengedit bitmap sederhana langsung dalam kode, terutama jika editor memungkinkan penyorotan. Misalnya, definisi karakter font dalam file font.h:
Templat variadik
Di mana kemudian tanpa mereka itu. Nah, misalnya, perintah untuk pengontrol tampilan dapat memiliki panjang satu hingga beberapa byte. Dikirim dengan kode berikut:SendCommand(Command::DISPLAY_ON);
SendCommand(Command::SET_COM_PINS, COM_PINS | COM_PINS_ALTERNATIVE);
SendCommand(Command::SET_COLUMN_ADDRESS, curVp.minCol, curVp.maxCol);
Nyaman bukan?
template <typename... TByte>
void
SendCommand(TByte... bytes)
{
cmdSize = sizeof...(bytes);
controlSent = false;
cmdInProgress = true;
SetCmdByte(sizeof...(bytes) - 1, bytes...);
i2cBus.RequestTransfer(DISPLAY_ADDRESS, true,
CommandTransferHandler);
}
template <typename... TByte>
inline void
SetCmdByte(int idx, u8 byte, TByte... bytes)
{
cmdBuf[idx] = byte;
SetCmdByte(idx - 1, bytes...);
}
inline void
SetCmdByte(int, u8 byte)
{
cmdBuf[0] = byte;
}
File variant.h menjelaskan kelas yang secara samar-samar menyerupai boost :: varian menggunakan templat variadic. Ini digunakan untuk mengatur halaman antarmuka pengguna. Intinya adalah lagi dalam menghemat memori - di mana manajemen memori dinamis adalah kemewahan yang tidak dapat diterima, Anda harus mengelak (walaupun 2K masih banyak, Anda tidak bisa mengelak, tetapi dalam garis ATMEGA yang sama ukurannya mencapai 512 byte, dan setiap byte per Akun). Di antarmuka saya, satu halaman ditampilkan di layar pada waktu tertentu. Dengan demikian, untuk semua halaman Anda dapat menggunakan memori yang sama, yang disebut penyatuan dalam C. Untuk kelas dalam C ++, ini biasanya disebut varian. Tidak seperti penyatuan, kita harus ingat untuk memanggil destruktor dari konten sebelumnya sebelum memanggil konstruktor yang baru. Variant<MainPage,
Menu,
LinearValueSelector,
TimeSelector> curPage;
...
template <class TPage>
static constexpr u8
GetPageTypeCode()
{
return decltype(curPage)::GetTypeCode<TPage>();
}
...
curPage.Engage(nextPageTypeCode, page);
Untuk kompilasi, binutils GCC dan GNU digunakan untuk platform AVR (di Ubuntu ada paket gcc-avr yang sudah jadi). Rincian proses perakitan diberikan di atas. Parameter untuk kompilator terlihat seperti ini (defund dan inklusi spesifik proyek dihilangkan): Tautan: Konversi bagian kode ke format hex: Buat gambar EEPROM: Firmware untuk mikrokontroler: PS Tomat pertama sudah matang, dan rasanya tidak enak. Ternyata, mereka tidak menyukai sesuatu dalam makanan. Mungkin harus mengubah budaya.avr-g++ -o build/native-debug/src/firmware/cpu/lighting.cpp.o -c -fno-exceptions -fno-rtti -std=c++1y -Wall -Werror -Wextra -ggdb3 -Os -mcall-prologues -mmcu=atmega328p -fshort-wchar -fshort-enums src/firmware/cpu/lighting.cpp
avr-g++ -o build/native-debug/src/firmware/cpu/cpu -mmcu=atmega328p build/native-debug/src/firmware/cpu/adc.cpp.o build/native-debug/src/firmware/cpu/application.cpp.o …
avr-objcopy -j .text -j .data -O ihex build/native-debug/src/firmware/cpu/cpu build/native-debug/src/firmware/cpu/cpu_rom.hex
avr-objcopy -j .eeprom --change-section-lma .eeprom=0 -O ihex build/native-debug/src/firmware/cpu/cpu build/native-debug/src/firmware/cpu/cpu_eeprom.hex
avrdude -p atmega328p -c avrisp2 -P /dev/avrisp -U flash:w:build/native-debug/src/firmware/cpu/cpu_rom.hex:i