ARM dengan inti Cortex Mx (menggunakan STM32F10x sebagai contoh)

Mikrokontroler ARM Cortex M3 STM32F103c8t6 banyak digunakan sebagai mikrokontroler 32-bit untuk proyek-proyek amatir. Adapun hampir semua mikrokontroler, ada SDK untuk itu, termasuk, antara lain, file header C ++ untuk menentukan pinggiran pengontrol.
Dan di sana, port serial, misalnya, didefinisikan sebagai struktur data, dan sebuah instance dari struktur ini terletak di area alamat yang disediakan untuk register dan kami memiliki akses ke area ini melalui pointer ke alamat tertentu.
Bagi mereka yang belum pernah menemukan ini sebelumnya, saya akan menjelaskan sedikit bagaimana itu didefinisikan, pembaca yang sama yang akrab dengan ini dapat melewati deskripsi ini.
Struktur ini dan turunannya dijelaskan sebagai berikut:
typedef struct { __IO uint32_t CR1; . . . __IO uint32_t ISR; } USART_TypeDef;
Anda dapat melihat lebih detail di sini
stm32f103xb.h β 800 kBDan jika Anda hanya menggunakan definisi dalam file ini, Anda harus menulis seperti ini (contoh menggunakan register status port serial):
Dan Anda harus menggunakannya karena solusi eksklusif yang ada yang dikenal sebagai CMSIS dan HAL terlalu kompleks untuk digunakan dalam proyek-proyek amatir.
Tetapi jika Anda menulis dalam C ++, maka Anda dapat menulis seperti ini:
Referensi yang dapat diubah diinisialisasi dengan pointer. Ini sedikit melegakan, tapi menyenangkan. Lebih baik lagi, tentu saja, untuk menulis kelas pembungkus kecil di atas ini, sementara teknik ini masih berguna.
Tentu saja, saya ingin segera menulis kelas pembungkus ini melalui port serial (EUSART - extended universal serial asinhronous reseiver-transmitter), sangat menarik, dengan fitur-fitur canggih, transceiver asinkron serial dan dapat menghubungkan mikrokontroler kecil kami ke sistem desktop atau laptop, tetapi mikrokontroler Cortex dibedakan oleh sistem pencatatan jam kerja yang dikembangkan dan Anda harus mulai dari itu, dan kemudian mengkonfigurasi pin I / O yang sesuai untuk bekerja dengan periferal, karena dalam seri STM32F1xx, seperti pada berkaki mikrokontroler lainnya ARM Cortex tidak bisa hanya mengkonfigurasi pin port input atau output dan bekerja pada saat yang sama dengan pinggiran.
Baiklah, mari kita mulai dengan menyalakan timing. Sistem jam disebut register RCC untuk kontrol jam dan juga mewakili struktur data, penunjuk yang ditetapkan nilai alamat tertentu.
typedef struct { . . . } RCC_TypeDef;
Bidang struktur ini dinyatakan seperti ini, di mana __IO mendefinisikan volatile:
__IO uint32_t CR;
sesuai dengan register dari RCC, dan bit individual dari register ini dihidupkan atau fungsi jam dari pinggiran mikrokontroler. Semua ini dijelaskan dengan baik dalam
dokumentasi (pdf) .
Pointer ke struktur didefinisikan sebagai
#define RCC ((RCC_TypeDef *)RCC_BASE)
Bekerja dengan bit register tanpa menggunakan SDK biasanya terlihat seperti ini:
Berikut adalah penyertaan port A.
Anda dapat mengaktifkan dua atau lebih bit sekaligus
Itu terlihat agak tidak biasa untuk C ++, atau sesuatu yang tidak biasa. Akan lebih baik untuk menulis secara berbeda, seperti ini, misalnya, menggunakan OOP.
Ini terlihat lebih baik, tetapi pada abad XXI kita akan melangkah lebih jauh, menggunakan C ++ 17 dan menulis menggunakan templat dengan sejumlah parameter parameter yang bahkan lebih indah:
Di mana Rcc didefinisikan seperti ini:
Dari sini, kita akan mulai membangun pembungkus di atas register jam. Pertama, kita mendefinisikan kelas dan pointer (tautan) ke sana.
Pada awalnya, saya ingin menulis dalam standar C ++ 11/14 menggunakan membongkar parameter templat fungsi secara rekursif. Artikel bagus tentang ini disediakan di akhir artikel, di bagian tautan.
Pertimbangkan panggilan ke fungsi aktifkan jam port:
Rcc.PortOn<GPort::A>();
GCC akan menyebarkannya ke sekumpulan perintah:
ldr r3, [pc, #376] ; (0x8000608 <main()+392>) ldr r0, [r3, #24] orr.w r0, r0, #4 str r0, [r3, #24]
Apakah itu berhasil? Periksa selanjutnya
Rcc.PortOn<GPort::A, GPort::B, GPort::C>();
Sayangnya, GCC yang tidak terlalu naif menyebarkan panggilan rekursi trailing secara terpisah:
ldr r3, [pc, #380] ; (0x8000614 <main()+404>) ldr r0, [r3, #24] orr.w r0, r0, #4 ; APB2ENR |= GPort::A str r0, [r3, #24] ldr r0, [r3, #24] orr.w r0, r0, #28 ; APB2ENR |= Gport::B | GPort::C str r0, [r3, #24] #24]
Dalam membela GCC, saya harus mengatakan bahwa ini tidak selalu terjadi, tetapi hanya dalam kasus yang lebih kompleks, yang akan terlihat ketika menerapkan kelas port I / O. Nah, C ++ 17 sedang terburu-buru untuk membantu. Menulis ulang kelas TRCC menggunakan kemampuan gulir bawaan.
Sekarang ternyata:
ldr r2, [pc, #372] ; (0x800060c <main()+396>) ldr r0, [r2, #24] orr.w r0, r0, #28 ; APB2ENR |= Gport::A | Gport::B | GPort::C str r0, [r3, #24]
Dan kode kelas menjadi lebih sederhana.
Kesimpulan: C ++ 17 memungkinkan kita untuk menggunakan templat dengan sejumlah variabel parameter untuk mendapatkan set instruksi minimum yang sama (bahkan ketika optimasi dimatikan) yang diperoleh saat menggunakan karya klasik dengan mikrokontroler melalui definisi register, tetapi pada saat yang sama kita mendapatkan semua keuntungan dari pengetikan C ++ yang kuat, memeriksa selama kompilasi, digunakan kembali melalui struktur kelas dasar dari kode, dan sebagainya.
Berikut ini sesuatu yang ditulis dalam C ++
Rcc.PortOn<Port::A, Port::B, Port::C>();
Dan teks klasik pada register:
RCC->APB2 |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN;
buka dalam satu set instruksi yang optimal. Berikut adalah kode yang dihasilkan oleh GCC (optimasi off -Og):
ldr r2, [pc, #372] ; (0x800060c <main()+396>) [ RCC] ldr r0, [r2, #0] ; r0 = RCC->APB2 // [ APB2] orr.w r0, r0, #160 ; r0 |= 0x10100000 str r0, [r2, #0] ; RCC->APB2 = r0
Sekarang Anda harus terus bekerja dan menulis kelas port input-output. Bekerja dengan bit port I / O rumit oleh fakta bahwa empat bit dialokasikan untuk konfigurasi satu port leg, dan dengan demikian 64 bit konfigurasi diperlukan pada port 16-bit, yang dibagi menjadi dua register CRL dan CRH 32-bit. Plus, lebar bitmask menjadi lebih dari 1. Tapi di sini, menggulir melalui C ++ 17 menunjukkan kemampuannya.

Selanjutnya, kelas TGPIO akan ditulis, serta kelas untuk bekerja dengan periferal lain, port serial, I2C, SPI, DAP, timer, dan banyak lagi, yang biasanya ada dalam mikrokontroler ARM Cortex dan kemudian mungkin akan berkedip dengan LED semacam itu.
Tetapi lebih lanjut tentang itu di catatan berikutnya.
Sumber proyek di github .
Artikel internet digunakan untuk menulis catatan
Templat dengan sejumlah variabel argumen di C ++ 11 .
Inovasi dalam template .
C ++ Inovasi Bahasa 17. Bagian 1. Konvolusi dan derivasi .
Daftar tautan ke dokumentasi untuk mikrokontroler STM .
Makro Parameter VariabelArtikel tentang Khabr yang mendorong saya untuk menulis catatan ini
Lampu lalu lintas di Attiny13 .
Julian Assange ditangkap oleh polisi InggrisRuang sebagai memori yang tidak jelasDitulis 04/12/2019 - Selamat Hari Kosmonotika!
PS
Gambar STM32F103c8t6 dari CubeMX.
Sebagai titik awal, teks yang dibuat oleh ekstensi Eclips untuk bekerja dengan
GNU MCU Eclipse ARM Tertanam dan
mikrokontroler STM CubeMX digunakan , mis. Ada file standar C ++, _start () dan _init () fungsi, definisi vektor interupsi diambil dari Eclipse MCU ARM Embedded, dan register inti Cortex M3 dan file kerja berasal dari proyek yang dibuat oleh CubeMX.
PPSPada KDPV debugging dengan pengontrol STM32F103c8t6 diwakili. Tidak semua orang memiliki papan seperti itu, tetapi tidak sulit untuk membelinya, namun, ini berada di luar cakupan artikel ini.