OS real-time AQUA RTOS untuk MK AVR di lingkungan BASCOM AVR

Ketika menulis untuk kode MK lebih rumit daripada "berkedip cahaya", pengembang dihadapkan pada keterbatasan yang melekat dalam pemrograman linier dalam gaya "supercycle plus interrupts". Pemrosesan interupsi membutuhkan kecepatan dan keringkasan, yang mengarah pada penambahan flag pada kode dan menjadikan gaya proyek “super cycle dengan interupsi dan flag”.

Jika kompleksitas sistem bertambah, maka jumlah flag yang saling tergantung tumbuh secara eksponensial, dan proyek dengan cepat berubah menjadi “kode pasta” yang sulit dibaca dan dikelola.

Penggunaan sistem operasi real-time membantu untuk menyingkirkan "kode pasta" dan mengembalikan fleksibilitas dan pengelolaan ke proyek MK yang kompleks.
Beberapa sistem operasi real-time koperasi telah dikembangkan dan cukup populer untuk mikrokontroler AVR. Namun, semuanya ditulis dalam C atau Assembler dan tidak cocok untuk mereka yang memprogram MK di lingkungan BASCOM AVR, merampas mereka dari alat yang berguna untuk menulis aplikasi serius.

Untuk memperbaiki kekurangan ini, saya mengembangkan RTOS sederhana untuk lingkungan pemrograman BASCOM AVR, yang saya bawa ke perhatian pembaca.

gambar

Bagi banyak orang, gaya pemrograman MK yang akrab adalah yang disebut sepeda super . Kode dalam hal ini terdiri dari serangkaian fungsi, prosedur, dan deskriptor (konstanta, variabel), mungkin yang perpustakaan, umumnya disebut "kode latar belakang", serta loop tak terbatas besar yang terlampir dalam konstruk do-loop . Saat start up, peralatan dari MK itu sendiri dan perangkat eksternal diinisialisasi terlebih dahulu, konstanta dan nilai awal variabel diatur, dan kemudian kontrol ditransfer ke supercycle tak terbatas ini.
Kesederhanaan supercycle sudah jelas. Sebagian besar tugas dilakukan oleh MK, karena satu atau lain cara siklus. Kerugiannya juga jelas: jika beberapa perangkat atau sinyal membutuhkan reaksi segera, MK akan memberikannya tidak lebih cepat dari siklus yang berputar. Jika durasi sinyal lebih pendek dari periode siklus, sinyal seperti itu akan dilewati.

Pada contoh di bawah ini, kami ingin memeriksa apakah tombolnya ditekan :

do ' -  if button = 1 then '     '  -  loop 

Tentunya, jika "beberapa kode" bekerja cukup lama, MK mungkin tidak memperhatikan tombol yang ditekan sebentar.

Untungnya, MK dilengkapi dengan sistem interupsi yang dapat menyelesaikan masalah ini: semua sinyal kritis dapat "digantung" pada interupsi dan pawang dapat ditulis untuk masing-masingnya. Jadi tingkat berikutnya muncul: siklus super dengan gangguan .
Contoh di bawah ini menunjukkan struktur program dengan supercycle dan interupsi yang memproses klik tombol:

 on button button_isr '    enable interrupts ' ***  *** do ' -  loop end '   button_isr: '  -    return 

Namun, menggunakan interupsi menimbulkan masalah baru: kode interrupt handler harus secepat dan sesingkat mungkin; di dalam interupsi, fungsionalitas MK terbatas. Karena AVR MK tidak memiliki sistem interupsi hierarkis, interupsi lain tidak dapat terjadi di dalam interupsi - AVR mereka dinonaktifkan pada saat ini. Jadi interupsi harus dieksekusi secepat mungkin, jika tidak interupsi lain (dan mungkin yang lebih penting) akan dilewati dan tidak diproses.

Memori interupsi
Bahkan, karena berada di dalam interupsi, MK dapat mencatat fakta interupsi lain dalam register khusus, yang memungkinkannya untuk diproses nanti. Namun, gangguan ini tidak dapat segera diproses.

Oleh karena itu, kita tidak dapat menulis sesuatu yang rumit dalam interrupt handler, terutama jika kode ini harus mengalami penundaan - karena sampai penundaan bekerja, MK tidak akan kembali ke program utama (supercycle) dan akan menjadi tuli terhadap gangguan lain.

Karena itu, di dalam interrupt handler Anda seringkali hanya perlu menandai fakta acara dengan bendera - milik Anda untuk setiap acara - dan kemudian memeriksa dan memproses bendera di dalam siklus super. Ini, tentu saja, memperpanjang waktu reaksi terhadap peristiwa tersebut, tetapi setidaknya kita tidak melewatkan sesuatu yang penting.

Dengan demikian, tingkat kerumitan berikutnya muncul - supercycle dengan interupsi dan bendera .

Kode berikut ditunjukkan:

 on button button_isr '    enable interrupts ' ***  *** do ' -  if button_flag = 1 then '     button_flag = 0 '     end if '  -  loop end ' ***    *** button_isr: button_flag = 1 return 

Sejumlah besar program untuk MK dibatasi oleh hal ini. Namun, program semacam itu biasanya masih kurang lebih sederhana. Jika Anda menulis sesuatu yang lebih rumit, maka jumlah bendera mulai tumbuh seperti bola salju, dan kodenya menjadi semakin membingungkan dan tidak terbaca. Selain itu, dalam contoh di atas, masalah dengan keterlambatan belum terselesaikan. Tentu saja, Anda dapat "menggantung" interupsi terpisah pada timer, dan di dalamnya ... juga mengontrol berbagai flag. Tetapi ini membuat program ini benar-benar jelek, jumlah bendera yang saling bergantung tumbuh secara eksponensial, dan bahkan pengembang sendiri hampir tidak dapat menemukan "kode pasta" semacam itu dengan segera. Mencoba untuk menemukan kesalahan atau memodifikasi kode seringkali menjadi sama dalam upaya mengembangkan proyek baru.

Bagaimana mengatasi masalah "kode pasta" dan membuatnya lebih mudah dibaca dan dikelola? Sistem operasi (OS) datang untuk menyelamatkan. Di dalamnya, fungsionalitas yang harus dilaksanakan MK dibagi menjadi tugas-tugas yang operasinya dikendalikan oleh OS.

Jenis sistem operasi untuk MK


Sistem operasi untuk MK dapat dibagi menjadi dua kelas besar: OS dengan crowding out dan OS kooperatif. Dalam salah satu dari sistem operasi ini, tugas dikontrol oleh prosedur khusus yang disebut dispatcher . Dalam OS dengan crowding out, operator secara independen setiap saat mengalihkan eksekusi dari satu tugas ke tugas lain, mengalokasikan masing-masing sejumlah siklus clock (mungkin berbeda, tergantung pada prioritas tugas). Pendekatan ini secara keseluruhan berfungsi dengan baik, memungkinkan Anda untuk tidak melihat konten tugas sama sekali: Anda dapat menulis setidaknya kode tugas

 1: goto 1 

- dan masih sisa tugas (termasuk yang ini) akan dilakukan. Namun, OS preemptive membutuhkan banyak sumber daya (memori prosesor dan siklus jam), karena setiap switch harus benar-benar menyimpan konteks tugas yang akan dinonaktifkan dan memuat konteks resume. Konteks di sini mengacu pada isi register mesin dan tumpukan (BASCOM menggunakan dua tumpukan - satu perangkat keras untuk alamat pengirim subprogram dan satu perangkat lunak untuk melewati argumen). Tidak hanya beban seperti itu yang membutuhkan banyak siklus prosesor, tetapi juga konteks dari setiap tugas perlu disimpan di suatu tempat untuk sementara waktu hingga bekerja. Dalam prosesor "besar", awalnya berorientasi pada multitasking, fungsi-fungsi ini sering didukung dalam perangkat keras, dan mereka memiliki lebih banyak sumber daya. Dalam AVR MK tidak ada dukungan perangkat keras untuk multitasking (semuanya perlu dilakukan "secara manual"), dan memori yang tersedia kecil. Oleh karena itu, mengganti OS, meskipun ada, tidak terlalu cocok untuk MK sederhana.

Hal lain adalah OS kooperatif . Di sini tugas itu sendiri mengontrol pada titik apa untuk mentransfer kontrol ke operator, yang memungkinkannya untuk memulai tugas lain. Selain itu, tugas di sini diperlukan untuk melakukan ini - jika tidak, eksekusi kode akan macet. Di satu sisi, tampaknya pendekatan ini mengurangi keandalan keseluruhan: jika suatu tugas macet, ia tidak akan pernah memanggil operator, dan seluruh sistem akan berhenti merespons. Di sisi lain, kode linier atau supercycle tidak lebih baik dalam hal ini - karena mereka dapat membeku dengan risiko yang persis sama.

Namun, OS kooperatif memiliki keunggulan penting. Karena di sini programmer menentukan momen perpindahan dirinya, itu tidak dapat terjadi secara tiba-tiba, misalnya, ketika tugasnya bekerja dengan beberapa sumber daya atau di tengah menghitung ekspresi aritmatika. Oleh karena itu, dalam OS kooperatif, dalam banyak kasus, Anda dapat melakukannya tanpa mempertahankan konteksnya. Ini secara signifikan menghemat waktu dan memori prosesor, dan karenanya terlihat jauh lebih cocok untuk implementasi pada AVR MK.

Peralihan tugas dalam BASCOM AVR


Untuk mengimplementasikan pengalihan tugas dalam lingkungan BASCOM AVR, kode tugas, yang masing-masing diimplementasikan sebagai prosedur normal, di suatu tempat harus memanggil dispatcher - juga diimplementasikan sebagai prosedur normal.
Bayangkan bahwa kita memiliki dua tugas, yang masing-masing di beberapa tempat kodenya disebut oleh operator.

 sub task1() do '  1 '  loop end sub ' ---------------------------------- sub task2() do '  2 '  loop end sub 

Misalkan tugas 1 dieksekusi. Mari kita lihat apa yang terjadi pada stack ketika menjalankan "panggilan dispatcher":

kembalikan alamat ke kode utama (2 byte)
atas tumpukan -> kembalikan alamat ke Tugas 1 yang disebut dispatcher (2 byte)

Bagian atas tumpukan akan menunjuk ke alamat instruksi di Tugas 1, yang mengikuti panggilan dispatcher (instruksi loop dalam contoh kita).

Tujuan dari dispatcher dalam kasus paling sederhana adalah untuk mentransfer kontrol ke Tugas 2. Pertanyaannya adalah bagaimana melakukan ini? (misalkan operator sudah tahu alamat Tugas 2).

Untuk melakukan ini, operator harus menarik alamat pengembalian ke Tugas 1 dari tumpukan (dan di suatu tempat untuk diingat), dan meletakkan alamat Tugas 2 di tempat ini di tumpukan, dan kemudian memberikan perintah kembali. Prosesor akan mengekstrak alamat yang ditempatkan di sana dari tumpukan dan, alih-alih kembali ke Tugas 1, akan melanjutkan ke pelaksanaan Tugas 2.

Pada gilirannya, ketika Tugas 2 memanggil operator, kami juga menarik tumpukan dan menyimpan alamat yang memungkinkan untuk kembali ke Tugas 2, dan memuat alamat tugas 1 yang disimpan sebelumnya ke tumpukan. Berikan perintah kembali dan kami akan berada pada titik kelanjutan dari Tugas 1 .

Akibatnya, kami jadi berantakan:

Tugas 1 -> Dispatcher -> Tugas 2 -> Dispatcher -> Tugas 1 ....

Tidak buruk! Dan itu berhasil. Tapi, tentu saja, ini tidak cukup untuk OS yang secara praktis dapat digunakan. Lagi pula, tidak selalu dan tidak semua tugas harus bekerja - misalnya, mereka dapat mengharapkan sesuatu (berakhirnya waktu tunda, penampilan beberapa sinyal, dll.). Jadi, tugas harus memiliki status (KARYA, SIAP, DIHARAPKAN, dll.). Selain itu, alangkah baiknya jika tugas diberikan prioritas . Kemudian, jika lebih dari satu tugas siap untuk dieksekusi, operator akan melanjutkan tugas yang memiliki prioritas tertinggi.

AQUA RTOS


Untuk mengimplementasikan ide yang diuraikan, koperasi OS AQUA RTOS dikembangkan, yang menyediakan layanan yang diperlukan untuk tugas-tugas dan memungkinkan penerapan multitasking koperasi dalam lingkungan AVR BASCOM.

Pemberitahuan Penting Mengenai Mode Prosedur di BASCOM AVR
Sebelum memulai deskripsi AUQA RTOS, harus dicatat bahwa lingkungan BASCOM AVR mendukung dua jenis prosedur pengalamatan. Ini diatur oleh submode config = new | tua
Dalam hal menentukan opsi lama, kompiler, pertama, akan mengkompilasi semua kode secara linear, terlepas dari apakah itu digunakan di suatu tempat atau tidak, dan kedua, prosedur tanpa argumen yang dirancang dalam gaya sub nama / sub akhir akan dianggap sebagai prosedur , ditata dengan gaya nama: / kembali. Ini memungkinkan kita untuk mengirimkan alamat prosedur sebagai label sebagai argumen ke prosedur lain dengan menggunakan pengubah bylabel. Ini juga berlaku untuk prosedur yang dirancang dalam gaya sub nama / gaya akhir sub (Anda harus memberikan nama prosedur sebagai label).
Pada saat yang sama, submode = mode lama memberlakukan beberapa batasan: prosedur tugas tidak boleh berisi argumen; kode file yang terhubung melalui $ include dimasukkan dalam proyek umum secara linier, oleh karena itu, bypass harus disediakan dalam file yang terhubung - mulai dari awal hingga akhir menggunakan goto dan label.
Dengan demikian, dalam AQUA RTOS, pengguna harus menggunakan hanya notasi prosedur lama dalam gaya task_name: / kembali untuk tugas, atau menggunakan sub nama / sub akhir yang lebih umum, menambahkan submode pengubah = tua pada awal kodenya, dan memotong di file yang disertakan label goto / sertakan kode file / label:.

Status tugas AQUA RTOS


Status berikut didefinisikan untuk tugas-tugas dalam AQUA RTOS:

 OSTS_UNDEFINE OSTS_READY OSTS_RUN OSTS_DELAY OSTS_STOP OSTS_WAIT OSTS_PAUSE OSTS_RESTART 

Jika tugas belum diinisialisasi, tugas ini diberikan status OSTS_UNDEFINE .
Setelah inisialisasi, tugas memiliki status OSTS_STOP .
Jika tugas siap untuk dieksekusi , tugas itu diberikan status OSTS_READY .
Tugas yang sedang berjalan memiliki status OSTS_RUN .
Dari sana, ia dapat pergi ke status OSTS_STOP, OSTS_READY, OSTS_DELAY, OSTS_WAIT, OSTS_PAUSE .
Status OSTS_DELAY memiliki tugas yang memenuhi penundaan .
Status OSTS_WAIT ditugaskan untuk tugas-tugas yang menunggu semafor, acara, atau pesan (lebih banyak tentang mereka di bawah).

Apa perbedaan antara status OSTS_STOP dan OSTS_PAUSED ?
Jika karena alasan tertentu tugas menerima status OSTS_STOP , maka dimulainya kembali tugas tersebut (setelah menerima status OSTS_READY ) akan dilakukan dari titik masuknya, yaitu. dari awal. Dari status OSTS_PAUSE, tugas akan terus bekerja di tempat ditangguhkan.

Manajemen Status Tugas


Baik OS itu sendiri dapat secara otomatis mengelola tugas, serta pengguna, dengan memanggil layanan OS. Ada beberapa layanan manajemen tugas (nama-nama semua layanan OS dimulai dengan awalan OS_ ):

 OS_InitTask(task_label, task_prio) OS_Stop() OS_StopTask(task_label) OS_Pause() OS_PauseTask(task_label) OS_Resume() OS_ResumeTask(task_label) OS_Restart() 

Masing-masing dari mereka memiliki dua opsi: OS_service dan OS_serviceTask (kecuali untuk layanan OS_InitTask , yang hanya memiliki satu opsi; layanan OS_Init menginisialisasi OS itu sendiri).

Apa perbedaan antara OS_service dan OS_serviceTask ? Metode pertama bertindak pada tugas itu sendiri yang menyebabkannya; yang kedua memungkinkan Anda untuk mengatur sebagai argumen penunjuk ke tugas lain dan, dengan demikian, untuk mengelola yang lain dari satu tugas.

Tentang OS_Resume
Semua layanan manajemen tugas, kecuali OS_Resume dan OS_ResumeTask, secara otomatis memanggil pengelola tugas setelah pemrosesan. Sebaliknya, layanan OS_Resume * hanya mengatur status tugas ke OSTS_READY. Status ini akan diproses hanya ketika operator secara eksplisit dipanggil.

Prioritas dan antrian tugas


Seperti disebutkan di atas, dalam sistem nyata, beberapa tugas mungkin lebih penting, sementara yang lain mungkin sekunder. Oleh karena itu, fitur yang berguna dari OS adalah kemampuan untuk menetapkan prioritas tugas. Dalam hal ini, jika ada beberapa tugas siap pakai pada saat yang sama, OS pertama akan memilih tugas dengan prioritas tertinggi. Jika semua tugas jadi memiliki prioritas yang sama, OS akan menjalankannya dalam lingkaran, dalam urutan yang disebut "carousel" atau round-robin.

Dalam AQUA RTOS, prioritas diberikan ke tugas ketika diparaf melalui panggilan ke layanan OS_InitTask , yang menerima alamat tugas sebagai argumen pertama dan angka dari 1 hingga 15 sebagai argumen kedua. Angka yang lebih rendah berarti prioritas yang lebih tinggi . Selama pengoperasian OS, perubahan prioritas yang ditugaskan untuk tugas tidak disediakan.

Keterlambatan


Dalam setiap tugas, penundaan diproses secara terpisah dari tugas-tugas lain.
Jadi, sementara OS sedang mengerjakan penundaan satu tugas, yang lain dapat dieksekusi.
Untuk organisasi keterlambatan disediakan layanan OS_Delay | OS_DelayTask . Argumennya adalah jumlah milidetik yang tugasnya ditunda . Karena dimensi argumen adalah dword , penundaan maksimum adalah 4294967295 ms, atau sekitar 120 jam, yang tampaknya cukup untuk sebagian besar aplikasi. Setelah memanggil layanan penundaan, operator dipanggil secara otomatis, yang mentransfer kontrol ke tugas-tugas lain selama durasi penundaan.

Semafor


Semafor dalam AQUA RTOS adalah sesuatu seperti flag dan variabel yang tersedia untuk tugas. Mereka ada dua jenis - biner dan dapat dihitung. Yang pertama hanya memiliki dua status: gratis dan tertutup. Yang kedua adalah byte counter (layanan penghitungan semaphores dalam versi AQUA RTOS saat ini tidak diterapkan (saya seorang pemalas), jadi semua yang dikatakan di bawah ini hanya berlaku untuk semaphore biner).

Perbedaan antara semafor dan bendera sederhana adalah bahwa tugas dapat dibuat untuk menunggu rilis semafor yang ditentukan. Dalam beberapa hal, penggunaan semaphore benar-benar menyerupai jalur kereta api: setelah mencapai semaphore, komposisi (tugas) akan memeriksa semaphore, dan jika tidak terbuka, itu akan menunggu sinyal yang memungkinkan untuk muncul untuk melangkah lebih jauh. Pada saat ini, kereta lain (tugas) dapat terus bergerak (lari).

Dalam hal ini, semua pekerjaan hitam ditugaskan ke operator. Segera setelah tugas disuruh menunggu semafor, kontrol secara otomatis ditransfer ke operator, dan dia dapat memulai tugas lain - tepat sampai semafor yang ditentukan dibebaskan. Segera setelah status semafor berubah menjadi bebas , operator akan memberikan semua tugas yang menunggu semafor ini menjadi status siap ( OSTS_READY ), dan tugas tersebut akan dieksekusi dalam urutan prioritas dan prioritas.
Secara total, AQUA RTOS menyediakan 16 semaphore biner (angka ini pada prinsipnya dapat ditingkatkan dengan mengubah dimensi variabel di unit kontrol tugas, karena di dalamnya diimplementasikan sebagai bit flags).
Semaphore biner bekerja melalui layanan berikut:

 hBSem OS_CreateBSemaphore() OS_WaitBSemaphore(hBSem) OS_WaitBSemaphoreTask(task_label, hBSem) OS_BusyBSemaphore(hBSem) OS_FreeBSemaphore(hBSem) 

Sebelum menggunakan semaphore harus dibuat . Ini dilakukan dengan memanggil layanan OS_CreateBSemaphore , yang mengembalikan pengidentifikasi byte unik (pegangan) dari semaphore hBSem yang dibuat, atau melalui handler yang ditentukan pengguna menghasilkan kesalahan OSERR_BSEM_MAX_REACHED , yang menunjukkan bahwa jumlah semafor biner biner maksimum yang mungkin telah dicapai.

Anda dapat bekerja dengan pengenal yang diterima dengan mengirimkannya sebagai argumen ke layanan semafor lainnya.

Layanan OS_WaitBSemaphore | OS_WaitBSemaphoreTask menempatkan tugas (saat ini | ditentukan) dalam keadaan untuk menunggu rilis semafor hBSem jika semafor ini sibuk, dan kemudian mentransfer kontrol ke operator sehingga dapat memulai tugas-tugas lain. Jika semaphore gratis, kontrol transfer tidak terjadi, dan tugas hanya berlanjut.

Layanan OS_BusyBSemaphore dan OS_FreeBSemaphore mengatur semaphore hBSem menjadi sibuk atau bebas, masing-masing.

Penghancuran semaphores untuk menyederhanakan OS dan mengurangi jumlah kode tidak disediakan. Dengan demikian, semua semaphores yang dibuat bersifat statis.

Acara


Selain semafor, tugas dapat didorong oleh peristiwa. Satu tugas dapat diinstruksikan untuk mengharapkan peristiwa tertentu , dan tugas lain (serta kode latar belakang) dapat memberi sinyal acara ini. Pada saat yang sama, semua tugas yang menunggu acara ini akan menerima status siap dieksekusi ( OSTS_READY ) dan akan ditetapkan oleh operator untuk dieksekusi dalam urutan prioritas dan prioritas.

Peristiwa apa yang bisa ditanggapi tugas? Nah, misalnya:
  • gangguan;
  • terjadinya kesalahan;
  • pelepasan sumber daya (kadang-kadang lebih mudah untuk menggunakan semaphore untuk ini);
  • mengubah status jalur I / O atau menekan tombol pada keyboard;
  • menerima atau mengirim karakter melalui RS-232;
  • transfer informasi dari satu bagian aplikasi ke yang lain (lihat juga pesan).

Sistem acara diimplementasikan melalui layanan berikut:

 hEvent OS_CreateEvent() OS_WaitEvent(hEvent) OS_WaitEventTask(task_label, hEvent) OS_WaitEventTO(hEvent, dwTimeout) OS_SignalEvent(hEvent) 

Sebelum menggunakan suatu acara, Anda harus membuatnya . Ini dilakukan dengan memanggil fungsi OS_CreateEvent , yang mengembalikan pengidentifikasi byte unik (handle) untuk acara hEvent , atau melempar kesalahan OSERR_EVENT_MAX_REACHED melalui handler yang ditentukan pengguna, menunjukkan bahwa batas jumlah kejadian yang dapat dihasilkan dalam OS telah tercapai (maksimum 255 peristiwa berbeda).

Untuk membuat tugas menunggu acara hEvent , panggil OS_WaitEvent dalam kodenya, dengan meneruskan gagang acara sebagai argumen. Setelah memanggil layanan ini, kontrol akan ditransfer secara otomatis ke operator.

Berbeda dengan layanan semaphore, layanan acara menyediakan opsi untuk menunggu acara dengan batas waktu . Untuk melakukan ini, gunakan layanan OS_WaitEventTO . Argumen kedua di sini Anda dapat menentukan jumlah milidetik yang tugasnya harapkan terjadi. Jika waktu yang ditentukan telah kedaluwarsa, tugas akan menerima status siap untuk dieksekusi seolah-olah acara telah terjadi, dan akan ditetapkan oleh operator untuk melanjutkan eksekusi dalam urutan prioritas dan prioritas. Tugas tersebut dapat mempelajari tentang fakta bahwa itu bukan peristiwa, tetapi batas waktu, dengan memeriksa bendera global OS_TIMEOUT .

OS_SignalEvent , . , , , .


, : , – .
:

 hTopic OS_CreateMessage() OS_WaitMessage(hTopic) OS_WaitMessageTask(task_label, hTopic) OS_WaitMessageTO(hTopic, dwTimeout) OS_SendMessage(hTopic, wMessage) word_ptr OS_GetMessage(hTopic) word_ptr OS_PeekMessage(hTopic) string OS_GetMessageString(hTopic) string OS_PeekMessageString(hTopic) 

, . OS_CreateMessage , hTopic , OSERR_TOPIC_MAX_REACHED , , , .

hTopic , OS_WaitMessage , . . , hTopic .

OS_WaitMessageTO OS_WaitEventTO .

OS_SendMessage . , , – word . , , , , .

, BASCOM varptr , , :

 strMessage = "Hello, world!" OS_SendMessage hTopic, varptr (strMessage) 

OS_WaitMessage , , , , — . . word , , , . OS_GetMessage , OS_PeekMessage .

, , OS_GetMessageString OS_PeekMessageString , , , .


AQUA RTOS TIMER0 . , ( ) . , .. . 1 .

AQUA RTOS



, . OS_SIM = TRUE | FALSE , .

, OS_MAX_TASK , . , ( ), . , . , .


AQUA RTOS . OS_Init . . , – . , , – .

( ) – , . , - .

, AQUA RTOS :

 OS_Init my_err_trap '... '... '... sub my_err_trap(err_code as byte) print err_code end sub 


, :

 OS_InitTask task_1, 1 OS_InitTask task_2 , 1 '... OS_InitTask task_N , 1 



, , Arduino Nano V3. - (, test), bas-:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_1() declare sub task_2() '       led_1 alias portd.4 led_2 alias portd.5 config portd.4 = output config portd.5 = output ' ***    *** '   OS_Init my_err_trap '   OS_InitTask task_1, 1 OS_InitTask task_2 , 1 '      «» (OSTS_STOP) '    ,     ' «  » (OSTS_READY)   OS_ResumeTask OS_ResumeTask task_1 OS_ResumeTask task_2 '      OS_Sheduler end ' ***  *** sub task_1 () do toogle led_1 '   1 OS_delay 1000 '   1000  loop end sub sub task_2 () do toogle led_2 '   2 OS_delay 333 '   333  loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

D4 D5 Arduino ( , - ). 100...500 GND . . 2 0,66 .

. , ( , aliases), – , – .

«», « » (, – - , , ; ). OS_ResumeTask .

, . ? , ! . , , , end .

. , do – loop . – , , – , . OS_Delay . , .

OS_SIM = TRUE , , , .

, , , « », . , « », .

, (, task_1 ), ( end ) task_1 , , return , – , task_1 ( do task_1 ).

task_1 , , OS_delay , , , .

, , task_1 ( , OS_delay , .. loop ), , « », , task_2 . task_2 ( do task_2 ) return , – , task_2 .

task_2 , , OS_delay , , , .

, , task_1 ( , OS_delay , .. loop ), , « », , task_2 . , task_1 , , . ( loop task_1 ), .

task_1 loop , « 1 – – 2 – » .


.

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_1() declare sub task_2() const OS_SIM = TURE '       '   dim hTopic as byte '     dim task_1_cnt as byte '    1 dim strMessage as string * 16 '  ' ***    *** OS_CreateMessage hTopic OS_Init my_err_trap OS_InitTask task_1 , 1 OS_InitTask task_2 , 1 OS_ResumeTask task_1 OS_ResumeTask task_2 OS_Sheduler end ' ***  *** sub task_1() do print "task 1" OS_Sheduler incr task_1_cnt '    1 if task_1_cnt > 3 then print "task 1 is sending message to task 2" strMessage = "Hello, task 2!" '    2 OS_SendMessage hTopic , varptr(strMessage) task_1_cnt = 0 end if loop end sub sub task_2() do print "task 2 is waiting messages..." '      1 OS_WaitMessage hTopic print "message recieved: " ; OS_GetMessageString (hTopic) loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

:

task 1
task 2 is waiting messages…
task 1
task 1
task 1
task 1 is sending message to task 2
task 1
message recieved: Hello, task 2!
task 2 is waiting messages…
task 1
task 1
...

, . 1 task 1 , , . 2 task 2 is waiting messages... , hTopic , , 1. task 1 . , , 2 , 1 incr , .
task_1_cnt 1 , , – loop task 1 . , , 2 , . .


:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $regfile = "m328pdef.dat" $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 '   declare sub my_err_trap (byval err_code as byte) declare sub task_scankeys() declare sub task_led_1() declare sub task_led_2() '        led_1 alias portd.4 led_2 alias portd.5 config portd.4 = output config portd.5 = output button_1 alias pind.6 button_2 alias pind.7 config portd.6 = input config portd.7 = input '   dim eventButton_1 as byte dim eventButton_2 as byte ' ***    *** eventButton_1 = OS_CreateEvent '       eventButton_2 = OS_CreateEvent OS_Init my_err_trap OS_InitTask task_scankeys , 1 OS_InitTask task_led_1 , 1 OS_InitTask task_led_2 , 1 OS_ResumeTask task_scankeys OS_ResumeTask task_led_1 OS_ResumeTask task_led_2 OS_Sheduler end ' ***  *** sub task_scankeys() do debounce button_1 , 0 , btn_1_click , sub debounce button_2 , 0 , btn_2_click , sub OS_Sheduler loop btn_1_click: OS_SignalEvent eventButton_1 return btn_2_click: OS_SignalEvent eventButton_2 return end sub sub task_led_1() do OS_WaitEvent eventButton_1 toggle led_1 loop end sub sub task_led_2() do OS_WaitEvent eventButton_2 toggle led_2 loop end sub ' **************************************************** '   sub my_err_trap(err_code as byte) print "OS Error: "; err_code end sub 

AQUA RTOS



, . ; , , . , : , 95…97°; (, GSM-), .


« + + » , . , .
:

  • ControlHeater()
  • ShowGoods()
  • / – AcceptMoney()
  • ScanKeys()
  • MakeChange()
  • ReleaseCoffee()
  • Alarm()

.
ControlHeater() , . , , . . 5.
ShowGoods() . , - . 8, .
ScanKeys() , . 3, 40 .
AcceptMoney() . , ScanKeys(), 20 .
MakeChange () . ReleaseCoffee() 10.
ReleaseCoffee() , . 2.
– , Alarm() – 1, , .

, . , EEPROM , .

 '    declare sub ControlHeater() declare sub ShowGoods() declare sub AcceptMoney() declare sub ScanKeys() declare sub MakeChange () declare sub ReleaseCoffee() declare sub Alarm() 

RTOS : ( , ) – .

, ReleaseCoffee() :

 sub ReleaseCoffee() do OS_WaitMessage bCoffeeSelection wItem = OS_GetMessage(bCoffeeSelection) Release wItem loop end sub 

ReleaseCoffee bCoffeeSelection , ( , ). , ReleaseCoffee() , , ( ) wItem OS_GetMessage . ReleaseCoffee() , :

 dim bCoffeeSelection as byte bCoffeeSelection = OS_CreateMessage() 

, ShowGoods() . ReleaseCoffee() , . :

 dim bGoodsReliased as byte bGoodsReliased = OS_CreateEvent() 

ReleaseCoffee() Release wItem bGoodsReliased :

 OS_SignalEvent bGoodsReliased 


, , , , . OS_Init :

 OS_Init Mailfuncion 

– , :

 sub Mailfuncion (bCoffeeErr) print "Mailfunction! Error #: "; bCoffeeErr if isErrCritical (bCoffeeErr) = 1 then CallService(bCoffeeErr) end if end sub 

( - : , GSM- ..), , , .


, , .. , . , OS_InitTask :

 OS_InitTask ControlHeater , 5 OS_InitTask ShowGoods , 8 OS_InitTask AcceptMoney , 3 OS_InitTask ScanKeys , 3 OS_InitTask MakeChange, 10 OS_InitTask ReleaseCoffee , 2 OS_InitTask Alarm , 1 

, , , , . . , OS_ResumeTask « »:

 OS_ResumeTask ControlHeater OS_ResumeTask ShowGoods OS_ResumeTask AcceptMoney OS_ResumeTask ScanKeys OS_ResumeTask MakeChange OS_ResumeTask ReleaseCoffee OS_ResumeTask Alarm 

, ; «» . OS_ResumeTask ( ), . , , , .


, . :

 OS_Sheduler 

end – .

:

 '    config submode = old $include "..\aquaRTOS_1.05.bas" $include "coffee_hardware.bas" '      '       Coffee_ $regfile = "m328pdef.dat" ' Arduino Nano v3 $crystal = 16000000 $hwstack = 48 $swstack = 48 $framesize = 64 Coffee_InitHardware '    '   declare sub Mailfuncion (byval bCoffeeErr as byte) '   declare sub ControlHeater () '   declare sub ShowGoods () '    declare sub AcceptMoney () '   declare sub ScanKeys () '   declare sub MakeChange () '      declare sub ReleaseCoffee () '   declare sub Alarm () '    '     Coffee_InitHardware () '   dim wMoney as long '    dim wGoods as long '   ' ***    *** '   OS_Init Mailfuncion '       dim bCoffeeSelection as byte bCoffeeSelection = OS_CreateMessage() '     dim bGoodsReliased as byte bGoodsReliased = OS_CreateEvent() '   OS_InitTask ControlHeater , 5 OS_InitTask ShowGoods , 8 OS_InitTask AcceptMoney , 3 OS_InitTask ScanKeys , 3 OS_InitTask MakeChange, 10 OS_InitTask ReleaseCoffee , 2 OS_InitTask Alarm , 1 '     OS_ResumeTask ControlHeater OS_ResumeTask ShowGoods OS_ResumeTask AcceptMoney OS_ResumeTask ScanKeys OS_ResumeTask MakeChange OS_ResumeTask ReleaseCoffee OS_ResumeTask Alarm '   OS_Sheduler end ' ***   *** ' ----------------------------------- sub ControlHeater() do select case GetWaterTemp() case is > 97 Coffee_HeaterOff '   case is < 95 Coffee_HeaterOn '   case is < 5 CallServce (WARNING_WATER_FROZEN) '   end select OS_Delay 60000 '  1  loop end sub ' ----------------------------------- sub ShowGoods() do LEDS = Coffee_GetDrinkSupplies() '    D, '         '   LEDS OS_WaitEvent bGoodsReliased '   " " loop end sub ' ----------------------------------- sub AcceptMoney() do wMoney = wMoney + ReadMoneyAcceptor() OS_Delay 20 loop end sub ' ----------------------------------- sub ScanKeys() do wGoods = ButtonPressed() if wMoney >= GostOf(wGoods) then OS_SendMessage bCoffeeSelection, wGoods '     bCoffeeSelection,  '     end if OS_Delay 40 loop end sub ' ----------------------------------- sub MakeChange() do OS_WaitEvent bGoodsReliased '   " " Refund wMoney loop end sub ' ----------------------------------- sub ReleaseCoffee() do OS_WaitMessage bCoffeeSelection '  bCoffeeSelection wItem = OS_GetMessage(bCoffeeSelection) '   Release wItem '    wMoney = wMoney – CostOf (wItem) '     OS_SignalEvent bGoodsReliased '     '  ,       : ' MakeChange  ShowGoods '  ,  ,     loop end sub ' ----------------------------------- sub Alarm() do OS_Delay 1000 if Hijack() = 1 then CallPolice() end if loop end sub ' ----------------------------------- ' ***    *** sub Mailfuncion (bCoffeeErr) print "Mailfunction! Error #: "; bCoffeeErr if isErrCritical (bCoffeeErr) = 1 then CallService() end if end sub 

, , . OS_SendMessage() , /. . , , , , .

AQUA RTOS


1.05


Q: AQUA?
A: , , « », , . , , , , « » WiFi-. , , , EEPROM , , - . . – « », , . , . AQUA.

Q: ?
A: . , , , , , . , . , , , , , , -, . , . , - ( ? – ) . .

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


All Articles