Beberapa waktu yang lalu saya ingin belajar assembler, dan setelah membaca literatur yang relevan, sudah waktunya untuk berlatih. Sebenarnya, itu akan dibahas lebih lanjut. Awalnya saya berlatih di Arduino Uno (Atmega328p), sekarang saya memutuskan untuk melanjutkan dan mengambil STM32. STM32F103C8 benar-benar jatuh ke tangan saya di atasnya dan percobaan lebih lanjut akan diadakan.
Alat-alatnya
Saya menggunakan alat-alat berikut:
- Notepad ++ - untuk menulis kode
- GNU Assembler Compiler
- STM32 ST-LINK Utility + ST-LINK V2 - untuk menginstal kode pada mikrokontroler dan debugging
Mulai
Tujuan utama bahasa rakitan bagi saya adalah belajar. Karena Anda tidak pernah tahu di mana harus menemukan masalah lain yang menarik, diputuskan untuk menulis semuanya dari awal. Tugas utama adalah memahami cara kerja vektor interupsi. Tidak seperti Atmega di STM32, vektor interrupt tidak berisi instruksi lompat:
jmp main
Alamat khusus tertulis di dalamnya, dan selama interupsi, prosesor itu sendiri mengganti alamat yang ditentukan dalam vektor dalam register PC. Berikut adalah contoh dari vektor interrupt saya:
.org 0x00000000 SP: .word STACKINIT RESET: .word main NMI_HANDLER: .word nmi_fault HARD_FAULT: .word hard_fault MEMORY_FAULT: .word memory_fault BUS_FAULT: .word bus_fault USAGE_FAULT: .word usage_fault .org 0x000000B0 TIMER2_INTERRUPT: .word timer2_interupt + 1
Saya ingin menarik perhatian pembaca pada fakta bahwa baris pertama bukanlah vektor reset, tetapi nilai-nilai dimana stack akan diinisialisasi. Segera setelah itu, ada reset vektor diikuti oleh 5 vektor interupsi wajib (NMI_HANDLER - USAGE_FAULT).
Pengembangan
Hal pertama yang saya lakukan adalah sintaks assembler ARM. Bahkan selama studi tentang vektor interupsi, saya menemukan referensi ke fakta bahwa ARM memiliki 2 jenis instruksi jempol dan bukan jempol. Dan Cortex-M3 (STM32F103C8 yaitu Cortex-M3) hanya mendukung satu set instruksi Jempol. Saya menulis instruksi secara ketat sesuai dengan dokumentasi, tetapi untuk beberapa alasan assembler mengutuk mereka.
Diperlukan register tanpa pergeseran
Ternyata di awal program
.syntax bersatu
ini memberi tahu assembler bahwa Anda dapat menggunakan instruksi Thumb dan non-Thumb secara bersamaan.
Hal berikutnya yang saya temui adalah port GPOI default yang dinonaktifkan. Untuk membuatnya berfungsi, antara lain, Anda perlu mengatur nilai yang sesuai di register RCC (reset dan clock control). Saya menggunakan PORT C, ini bisa dihidupkan dengan menetapkan bit 4 (penomoran bit dari awal) di RCC_APB2ENR (jam periferal mengaktifkan register 2).
LED berkedip lebih lanjut. Pertama-tama, seperti di Arduino, Anda perlu mengatur pin untuk rekaman. Ini dilakukan melalui GPIOx_CRL (register kontrol rendah) atau GPIOx_CRH (register kontrol tinggi). Di sini perlu untuk membatalkan bahwa untuk setiap pin 4 bit bertanggung jawab dalam salah satu register ini (register 32 bit). 2 bit (MODEy) menentukan kecepatan data maksimum dan konfigurasi pin 2 bit (CNF). Saya menggunakan PORT C pin 14, untuk ini saya menetapkan bit [25:24] = 10 dan bit [27:26] = 00 dalam register GPIOx_CRH.
Agar diode menyala, Anda perlu mengatur bit yang sesuai di GPIOx_ODR (register data keluaran). Dalam kasus saya, bit 14. Ini bisa mengakhiri contoh sederhana ini dengan membuat fungsi penundaan dan meletakkannya dalam satu lingkaran, tetapi saya tidak bisa melakukan ini. Saya memutuskan untuk mengatur penghentian waktu ... Ternyata itu sia-sia, terutama karena penghitung waktu terlalu cepat untuk tugas semacam ini.
Saya tidak akan menjelaskan secara detail pengaturan timer, yang tertarik pada kode di
Github . Idenya sederhana, dalam satu siklus mengirim prosesor ke Idle, keluar dari Idle oleh timer untuk menyalakan / mematikan LED dan kembali ke Idle. Tetapi timer bekerja jauh lebih cepat daripada saya berhasil melakukan semua hal di atas, karena itu saya harus memasukkan penghitung tambahan.
Penghitung adalah variabel 32 bit yang seharusnya di SRAM. Dan kemudian menyapu lain menungguku. Ketika saya memprogram di Atmega untuk menempatkan variabel dalam SRAM, melalui .org saya mengatur alamat awal memori di mana blok data sebenarnya ditempatkan. Sekarang, setelah membaca sedikit tentang inisialisasi memori, saya tidak yakin apakah ini benar, tetapi berhasil. Dan saya memutuskan untuk melakukan hal yang sama dengan STM32. Alamat mulai memori di STM32F103C8 adalah 0x20000000. Dan ketika saya melakukannya .org di alamat ini, saya mendapat biner 512mb. Ini mengirim saya beberapa malam untuk merokok manual. Saya masih tidak mengerti 100% cara kerjanya, tetapi sejauh yang saya mengerti bagian .data menempatkan nilai-nilai dimana variabel harus diinisialisasi ke dalam file yang dapat dieksekusi, tetapi pada saat dijalankan programmer harus menginisialisasi nilai-nilai variabel dalam memori. Tolong perbaiki saya jika saya salah. Saya akhirnya membuat variabel seperti ini:
.section .bss .offset 0x20000000 flash_counter: .word
Menginisialisasi di awal fungsi utama dan LED berkedip. Saya harap artikel ini membantu seseorang. Jika Anda memiliki pertanyaan, saya akan dengan senang hati menjawabnya.