Lima tahun menggunakan C ++ untuk proyek-proyek mikrokontroler dalam produksi

Dalam artikel ini saya akan memberitahu Anda bagaimana saya mentransfer perusahaan tempat saya bekerja selama lebih dari lima tahun dari mengelola proyek untuk mikrokontroler ke C ke C ++ dan apa yang terjadi (spoiler: semuanya buruk).

Sedikit tentang dirimu


Saya mulai menulis di bawah mikrokontroler C, hanya memiliki pengalaman sekolah dengan Pascal, kemudian saya belajar assembler dan menghabiskan sekitar 3 tahun mempelajari berbagai arsitektur mikrokontroler dan periferal mereka. Kemudian ada pengalaman kerja nyata dalam C # dan C ++ dengan studi paralel mereka, yang memakan waktu beberapa tahun. Setelah periode ini, saya kembali dan untuk waktu yang lama kembali ke pemrograman mikrokontroler, sudah memiliki dasar teori yang diperlukan untuk bekerja pada proyek nyata.

Tahun pertama


Saya tidak menentang gaya prosedural C, tetapi perusahaan yang memulai praktik nyata saya pada proyek nyata menggunakan "pemrograman C dalam gaya berorientasi objek." Itu terlihat seperti ini.

typedef const struct _uart_init { USART_TypeDef *USARTx; uint32_t baudrate; ... } uart_cfg_t; int uart_init (uart_cfg_t *cfg); int uart_start_tx (int fd, void *d, uint16_t l); int uart_tx (int fd, void *d, uint16_t l, uint32_t timeout); 

Pendekatan ini memiliki keuntungan sebagai berikut:

  1. kode terus menjadi kode C. Keuntungan berikut mengikuti dari ini:
    • lebih mudah untuk mengontrol "objek", karena mudah untuk melacak siapa dan di mana menyebabkan apa dan dalam urutan apa (dengan pengecualian gangguan, tetapi tidak dalam artikel ini);
    • untuk menyimpan "pointer ke objek" itu cukup untuk mengingat fd yang dikembalikan;
    • jika "objek" dihapus, maka ketika Anda mencoba menggunakannya, Anda akan menerima kesalahan yang sesuai dalam nilai pengembalian fungsi;
  2. abstraksi objek semacam itu di atas HAL yang digunakan di sana memungkinkan untuk menulis objek yang dapat dikustomisasi untuk tugas dari struktur inisialisasi sendiri (dan dalam kasus kurangnya fungsi HAL, orang dapat menyembunyikan akses ke register di dalam "objek").

Cons:

  1. jika seseorang menghapus "objek", dan kemudian membuat yang baru dari tipe yang berbeda, mungkin saja yang baru mendapatkan fd dari yang lama dan perilaku selanjutnya tidak akan ditentukan. Perilaku ini dapat dengan mudah diubah dengan biaya konsumsi memori kecil untuk daftar tertaut alih-alih menggunakan array dengan nilai kunci (array untuk setiap indeks dan menyimpan pointer ke struktur objek).
  2. tidak mungkin untuk secara statis menandai memori di bawah "objek global". Karena di sebagian besar aplikasi "objek" dibuat sekali dan tidak dihapus lebih lanjut, itu tampak seperti "penopang". Di sini, ketika membuat objek, akan dimungkinkan untuk melewatkan pointer ke struktur internalnya, yang dialokasikan secara statis selama tata letak, tetapi ini selanjutnya akan membingungkan kode inisialisasi dan memecah enkapsulasi.

Ketika ditanya tentang mengapa C ++ tidak dipilih ketika membangun seluruh infrastruktur, mereka menjawab sesuatu seperti berikut: "Yah, C ++ mengarah ke biaya tambahan yang kuat, biaya memori yang tidak terkendali, serta file firmware yang besar dan dapat dieksekusi." Mungkin mereka benar. Memang, pada saat awal desain, hanya ada GCC 3.0.5, yang tidak bersinar dengan keramahan khusus untuk C ++ (kami masih harus bekerja dengannya untuk menulis program di bawah QNX6). Tidak ada constexpr dan C ++ 11/14, yang memungkinkan Anda untuk membuat objek global, yang intinya adalah data di area .data, dihitung pada tahap kompilasi dan metode untuknya.

Untuk pertanyaan itu, mengapa tidak menulis di register - saya mendapat jawaban yang jelas bahwa penggunaan "objek" memungkinkan Anda untuk mengkonfigurasi jenis aplikasi yang sama "dalam sehari".

Menyadari semua ini dan menyadari bahwa sekarang C ++ tidak sama dengan GCC 3.0.5, saya mulai menulis ulang bagian utama fungsi di C ++. Untuk memulai, bekerjalah dengan periferal perangkat keras mikrokontroler, kemudian periferal perangkat eksternal. Sebenarnya, ini hanya shell yang lebih nyaman daripada apa yang tersedia saat itu.

Tahun kedua dan ketiga


Saya menulis ulang semua yang saya butuhkan untuk proyek saya di C ++ dan terus menulis modul baru segera di C ++. Namun, ini hanya lebih dari C. Setelah menyadari bahwa saya tidak cukup menggunakan C ++, saya mulai menggunakan kekuatannya: templat, kelas header-only, constexpr, dan banyak lagi. Segalanya berjalan baik.

Tahun Keempat dan Kelima


  • semua objek bersifat global dan menyertakan tautan satu sama lain pada tahap kompilasi (sesuai dengan arsitektur proyek);
  • semua objek dialokasikan memori pada tahap tata letak;
  • berdasarkan objek kelas untuk setiap pin;
  • sebuah objek yang merangkum semua pin untuk menginisialisasi mereka dengan satu metode;
  • objek kontrol RCC yang merangkum semua objek yang ada di bus perangkat keras;
  • BISA <-> proyek konverter RS485 sesuai dengan protokol pelanggan berisi 60 objek;
  • seandainya ada sesuatu yang berada pada level itu di HAL atau level kelas dari suatu objek, Anda tidak hanya harus “memperbaiki masalah”, tetapi juga memikirkan cara memperbaikinya sehingga perbaikan ini bekerja pada semua kemungkinan konfigurasi modul ini. ;
  • templat dan constexpr yang digunakan tidak dapat dihitung sebelum melihat peta, file asm dan bin dari firmware terakhir (atau meluncurkan debugging di mikrokontroler);
  • dalam hal terjadi kesalahan dalam templat, pesan dengan panjang sepertiga dari konfigurasi proyek dari GCC adalah output. Membaca dan memahami sesuatu darinya merupakan prestasi tersendiri.

Ringkasan


Sekarang saya mengerti yang berikut:
  1. penggunaan "konstruktor modul universal" hanya mempersulit program. Jauh lebih mudah untuk menyesuaikan register konfigurasi untuk proyek baru daripada menyelidiki hubungan antara objek, dan kemudian juga di perpustakaan HAL;
  2. jangan takut untuk menggunakan C ++ karena takut itu akan "melahap banyak memori" atau "kurang dioptimalkan daripada C". Tidak, tidak. Anda perlu takut bahwa menggunakan objek dan banyak lapisan abstraksi akan membuat kode tidak terbaca, dan men-debug itu akan menjadi prestasi heroik;
  3. jika Anda tidak menggunakan sesuatu yang “menyulitkan”, seperti templat, warisan, dan pesona C ++ yang menarik lainnya, lalu mengapa menggunakan C ++ sama sekali? Hanya demi benda? Apakah itu sepadan? Dan demi objek global statis tanpa menggunakan / menghapus baru dilarang pada beberapa proyek?

Kesimpulannya, kita dapat mengatakan bahwa kesederhanaan nyata menggunakan C ++ ternyata hanyalah alasan untuk berulang kali meningkatkan kompleksitas proyek tanpa peningkatan kecepatan atau memori.

Source: https://habr.com/ru/post/id461113/


All Articles