Eksperimen multitasking kecil dalam mikrokontroler

Dalam salah satu catatan sebelumnya, penulis mencoba untuk berpendapat bahwa ketika memprogram mikrokontroler, pengalihan tugas sederhana akan berguna dalam situasi di mana menggunakan sistem operasi waktu-nyata terlalu banyak, dan loop komprehensif untuk semua tindakan yang diperlukan terlalu kecil ( Dia berkata, sama seperti Count de La Fer). Lebih tepatnya, tidak terlalu sedikit, tetapi terlalu bingung.


Dalam catatan selanjutnya, direncanakan untuk merampingkan akses ke sumber daya yang dibagikan oleh beberapa tugas menggunakan antrian berdasarkan ring buffer (FIFOs) dan tugas terpisah yang ditugaskan khusus untuk ini. Setelah tersebar untuk tugas-tugas yang berbeda tindakan-tindakan yang tidak terkait satu sama lain, kami berhak untuk mengharapkan kode yang lebih terlihat. Dan jika pada saat yang sama kita mendapatkan kenyamanan dan kesederhanaan, mengapa tidak mencobanya?


Jelas, mikrokontroler tidak dirancang untuk menyelesaikan tugas yang mungkin dilakukan pengguna. Kemudian, mungkin, pengalih tugas semacam itu akan terbukti cukup memadai dalam banyak situasi. Singkatnya, percobaan kecil tidak mungkin menyakitkan. Karena itu, agar tidak berdasar, hamba Anda yang rendah hati memutuskan untuk menulis sesuatu dan menguji coretan-coretannya.


Dalam mikrokontroler, saya harus mengatakan, persyaratan untuk memperhitungkan waktu sebagai sesuatu yang penting dan sulit lebih umum daripada komputer tujuan umum. Melampaui kerangka dalam kasus pertama setara dengan tidak dapat dioperasi, dan dalam kasus kedua, itu hanya mengarah pada peningkatan waktu tunggu, yang cukup dapat diterima jika saraf sudah beres. Bahkan ada dua istilah "soft real time" dan "hard real time".


Biarkan saya mengingatkan Anda, kami berbicara tentang pengendali dengan inti Cortex-M3,4,7. Hari ini adalah keluarga yang sangat umum. Dalam contoh di bawah ini, kami menggunakan mikrokontroler STM32F303, yang merupakan bagian dari papan STM32F3DISCOVERY.


Switch adalah file assembler tunggal.
Assembler tidak takut pada penulis, tetapi, sebaliknya, menginspirasi harapan bahwa kecepatan maksimum akan tercapai.


Awalnya, logika paling sederhana dari operasi sakelar direncanakan, yang disajikan pada Gambar 1 untuk delapan tugas.



Dalam skema ini, tugas mengambil porsi waktu mereka satu per satu dan hanya dapat memberikan sisa kutu mereka dan, jika perlu, kemudian melewati beberapa kutu mereka. Logika ini terbukti baik, karena ukuran kuantum dapat dibuat kecil. Dan inilah tepatnya yang diperlukan agar tidak segera mengangkat tugas yang baru saja terjadi gangguan, dan juga untuk meningkatkan dan kemudian menurunkan prioritasnya. Paket yang baru saja diterima akan diam-diam menunggu 200-300 mikrodetik sampai tugasnya menerima centangnya. Dan jika kita memiliki Cortex-M7 yang beroperasi pada frekuensi 216 MHz, maka 20 mikrodetik untuk satu centang cukup masuk akal, karena akan membutuhkan kurang dari setengah mikrodetik untuk beralih. Dan tugas apa pun dari contoh di atas tidak akan pernah lebih dari 140 mikrodetik terlambat.


Namun, dengan peningkatan jumlah tugas, bahkan dengan ukuran kuantum waktu yang sangat kecil, keterlambatan dalam permulaan aktivitas tugas yang diminta mungkin tidak lagi menyenangkan. Berdasarkan hal ini, dan juga memperhitungkan bahwa hanya sebagian kecil dari tugas yang benar-benar membutuhkan waktu nyata, diputuskan untuk sedikit memodifikasi logika sakelar. Itu ditunjukkan pada Gambar 2.



Sekarang kita hanya memilih sebagian dari tugas yang menerima kuantum keseluruhan, dan memilih hanya satu centang untuk sisanya, di mana mereka bergantian dalam permainan. Dalam hal ini, subrutin inisialisasi menerima parameter input, yaitu nomor posisi, mulai dari mana semua tugas akan terpengaruh dalam hak dan akan berbagi satu centang. Pada saat yang sama, skema lama tetap tersedia, untuk ini cukup dengan menetapkan nilai parameter ke nol atau jumlah total tugas. Biaya peralihan meningkat hanya dengan beberapa instruksi assembler.


Dua skema serupa digunakan untuk memungkinkan akses ke sumber daya bersama. Yang pertama, yang disebutkan dalam catatan sebelumnya, menggunakan beberapa FIFO (atau buffer bundar dengan jumlah produsen pesan) dan tugas pencocokan terpisah. Ini dirancang untuk berkomunikasi dengan dunia luar dan tidak memerlukan harapan dari tugas yang menghasilkan pesan. Hanya diperlukan untuk memastikan bahwa antrian tidak ramai.


Skema kedua juga menggunakan tugas terpisah untuk memungkinkan akses, tetapi memperkenalkan harapan karena mengelola sumber daya internal di kedua arah. Tindakan ini tidak dapat dikaitkan dengan waktu. Gambar 3 menunjukkan komponen-komponen dari rangkaian kedua.



Elemen utama di dalamnya adalah buffer permintaan, sesuai dengan jumlah tugas yang diinginkan, dan satu indikator akses. Pengoperasian desain ini cukup sederhana. Tugas di sebelah kiri mengirimkan permintaan akses ke tempat yang dialokasikan khusus untuknya (misalnya, tugas 2 menulis 1 ke Permintaan 2). Tugas - operator memilih siapa yang memperbolehkan dan menulis nomor tugas yang dipilih dalam bendera resolusi. Tugas yang telah menerima izin melakukan aksinya dan menulis tanda akhir akses ke permintaan, nilai 0xFF. Penjadwal, melihat bahwa permintaan dihapus, mengatur ulang tanda izin, mengatur ulang permintaan sebelumnya dan mengatur ulang permintaan dari tugas lain.


Dua proyek uji di bawah IAR dan deskripsi papan STM32F3DISCOVERY yang digunakan dapat dilihat di sini . Dalam proyek pertama, ATS303 hanya memeriksa kinerjanya dan mendebugnya. Semua LED yang terpasang pada papan ini sangat berguna. Tidak ada yang terluka.


Draf kedua BTS303 menguji dua opsi alokasi sumber daya yang disebutkan. Di dalamnya, tugas 1 dan 2 menghasilkan pesan pengujian yang diterima oleh operator. Untuk berkomunikasi dengan operator, saya harus menambahkan syal dengan port COM TTL, seperti yang ditunjukkan pada foto di bawah ini.



Operator menggunakan emulator terminal. Saya pikir pembaca akan memaafkan penulis untuk warna tabung lunak. Ini terlihat seperti ini.



Untuk memulai seluruh sistem, sebelum menyelesaikan interupsi, langkah-langkah awal diperlukan di tubuh nol tugas utama (), yang disajikan di bawah ini.


void main_start_task_switcher(U8 border); U8 task_run_and_return_task_number((U32)t1_task); U8 task_run_and_return_task_number((U32)t2_task); U8 task_run_and_return_task_number((U32)t3_human_link); U8 task_run_and_return_task_number((U32)t4_human_answer); U8 task_run_and_return_task_number((U32)task_5); U8 task_run_and_return_task_number((U32)task_6); U8 task_run_and_return_task_number((U32)task_7); 

Pada baris ini, sakelar pertama mulai, dan kemudian, pada gilirannya, tujuh tugas yang tersisa.


Berikut adalah set panggilan minimum yang diperlukan untuk pekerjaan itu.


  void task_wake_up_action(U8 taskNumber); 

Panggilan ini digunakan dalam interupsi dari pengatur waktu perangkat keras pengguna. Tantangan dari tugas itu sendiri berbicara untuk diri mereka sendiri.


  void release_me_and_set_sleep_steps(U32 ticks); U8 get_my_number(void); 

Semua fungsi ini ada di file assembler switch. Ada beberapa fungsi lagi yang berguna untuk pengujian, tetapi tidak diperlukan.


Dalam proyek BTS303, tugas 3 menerima perintah operator dari luar dan mengirimnya jawaban untuk mereka yang datang dari tugas 4. Tugas 4 menerima perintah dari operator dari tugas 3 dan menjalankannya dengan kemungkinan jawaban. Tugas 3 juga menerima pesan dari tugas 1 dan 2 dan mengirimkannya melalui UART ke terminal emulator (misalnya, dempul).


Tugas 0 (utama) melakukan beberapa pekerjaan tambahan, misalnya, memeriksa jumlah kata yang tidak terpengaruh di area susun setiap tugas. Informasi ini dapat diminta oleh operator dan mendapatkan gagasan tentang penggunaan tumpukan. Awalnya, untuk setiap tugas, area tumpukan 512 byte (128 kata) dialokasikan dan perlu untuk memantau (setidaknya pada tahap debugging) bahwa area ini tidak mendekati meluap.


Tugas 5 dan 6 melakukan perhitungan pada beberapa variabel floating point umum. Untuk melakukan ini, mereka meminta akses ke sana dari tugas 7.


Ada fitur tambahan lain yang bisa dilihat di proyek uji. Ini dirancang agar Anda dapat membangkitkan tugas tidak setelah jumlah kutu telah kedaluwarsa, tetapi setelah waktu yang ditentukan, dan sepertinya ini.


  void wake_me_up_after_milliSeconds(U32 timeMS); 

Untuk implementasinya, timer perangkat keras tambahan juga diperlukan, yang juga diterapkan dalam kasus uji.


Seperti yang Anda lihat, daftar semua panggilan yang diperlukan cocok pada satu halaman.

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


All Articles