Multitasking pada mikrokontroler PIC

17

Multitasking penting hari ini. Saya bertanya-tanya bagaimana kita bisa mencapainya dalam mikrokontroler dan pemrograman tertanam. Saya merancang sistem yang didasarkan pada mikrokontroler PIC. Saya telah merancang firmware-nya dalam MplabX IDE menggunakan C dan kemudian merancang aplikasi untuk itu di Visual Studio menggunakan C #.

Karena saya sudah terbiasa menggunakan utas dalam pemrograman C # di desktop untuk mengimplementasikan tugas paralel, apakah ada cara untuk melakukan hal yang sama dalam kode mikrokontroler saya? MplabX IDE menyediakan pthreads.htetapi itu hanya sebuah rintisan tanpa implementasi. Saya tahu ada dukungan FreeRTOS tetapi menggunakannya yang membuat kode Anda lebih kompleks. Beberapa forum mengatakan bahwa interupsi juga dapat digunakan sebagai multi tasking tapi saya tidak berpikir interupsi setara dengan utas.

Saya merancang sistem yang mengirimkan beberapa data ke UART dan pada saat yang sama ia perlu mengirim data ke situs web melalui ethernet (kabel). Seorang pengguna dapat mengontrol output melalui situs web tetapi outputnya HIDUP / MATI dengan penundaan 2-3 detik. Jadi itulah masalah yang saya hadapi. Apakah ada solusi untuk multi tasking di mikrokontroler?

Pesawat terbang
sumber
Thread dapat digunakan hanya pada prosesor yang menjalankan OS, karena utas adalah bagian dari proses, dan proses hanya digunakan dalam OS.
TicTacToe
@Olaola, Anda benar. Tapi bagaimana dengan pengendali?
Pesawat
2
Kemungkinan duplikat RTOS untuk Sistem Tertanam
Roger Rowland
1
Bisakah Anda menjelaskan mengapa Anda memerlukan multitasking sejati dan tidak dapat mengimplementasikan perangkat lunak Anda secara wajar berdasarkan pendekatan tugas round-robin atau loop pilih () atau sejenisnya?
whatsisname
2
Yah, seperti yang sudah saya katakan, saya mengirim & menerima data ke uart dan pada saat yang sama mengirim & menerima data ke ethernet. Terlepas dari ini, saya juga perlu menyimpan data dalam kartu SD seiring dengan waktu, jadi ya DS1307 RTC terlibat dan EEPROM juga terlibat. Sampai sekarang saya hanya memiliki 1 UART tetapi mungkin setelah beberapa hari saya akan mengirim & menerima data dari 3 modul UART. Situs web juga akan menerima data dari 5 sistem berbeda yang dipasang di tempat yang jauh. Ini semua harus paralel tetapi benar bukan paralel, tetapi dengan penundaan beberapa detik. !
Pesawat

Jawaban:

20

Ada dua jenis utama sistem operasi multitasking, preemptive dan kooperatif. Keduanya memungkinkan beberapa tugas untuk didefinisikan dalam sistem, perbedaannya adalah bagaimana pengalihan tugas bekerja. Tentu saja dengan prosesor inti tunggal, hanya satu tugas yang benar-benar berjalan pada suatu waktu.

Kedua jenis OS multitasking ini membutuhkan tumpukan terpisah untuk setiap tugas. Jadi ini menyiratkan dua hal: pertama, bahwa prosesor memungkinkan tumpukan untuk ditempatkan di mana saja di RAM dan karena itu memiliki instruksi untuk memindahkan penunjuk tumpukan (SP) di sekitar - yaitu tidak ada tumpukan perangkat keras tujuan khusus seperti yang ada pada low-end PIC. Ini membuat PIC10, 12 dan 16 seri.

Anda dapat menulis OS hampir seluruhnya dalam bahasa C, tetapi pengalih tugas, tempat SP bergerak harus berada dalam perakitan. Di berbagai waktu saya telah menulis pengalih tugas untuk PIC24, PIC32, 8051, dan 80x86. Nyali semuanya sangat berbeda tergantung pada arsitektur prosesor.

Persyaratan kedua adalah bahwa ada cukup RAM untuk menyediakan banyak tumpukan. Biasanya satu ingin setidaknya beberapa ratus byte untuk tumpukan; tetapi bahkan hanya dengan 128 byte per tugas, delapan tumpukan akan membutuhkan 1K byte RAM - Anda tidak harus mengalokasikan tumpukan ukuran yang sama untuk setiap tugas. Ingat Anda membutuhkan tumpukan yang cukup untuk menangani tugas saat ini, dan semua panggilan ke subrutin bersarangnya, tetapi juga tumpukan ruang untuk panggilan interupsi karena Anda tidak pernah tahu kapan akan terjadi.

Ada metode yang cukup sederhana untuk menentukan berapa banyak tumpukan yang Anda gunakan untuk setiap tugas; misalnya Anda dapat menginisialisasi semua tumpukan ke nilai tertentu, katakan 0x55, dan jalankan sistem untuk sementara waktu lalu berhenti dan periksa memori.

Anda tidak mengatakan PIC seperti apa yang ingin Anda gunakan. Kebanyakan PIC24 dan PIC32 akan memiliki banyak ruang untuk menjalankan OS multitasking; PIC18 (satu-satunya PIC 8-bit yang memiliki tumpukan dalam RAM) memiliki ukuran RAM maksimum 4K. Jadi itu sangat rapuh.

Dengan multitasking kooperatif (yang lebih sederhana dari keduanya), pengalihan tugas hanya dilakukan ketika tugas "menyerahkan" kontrolnya kembali ke OS. Ini terjadi setiap kali tugas perlu memanggil rutin OS untuk melakukan beberapa fungsi yang akan ditunggu, seperti permintaan I / O atau panggilan waktu. Ini membuatnya lebih mudah bagi OS untuk berganti tumpukan, karena tidak perlu untuk menyimpan semua register dan informasi negara, SP hanya dapat dialihkan ke tugas lain (jika tidak ada tugas lain yang siap dijalankan, tumpukan menganggur adalah diberikan kontrol). Jika tugas saat ini tidak perlu membuat panggilan OS tetapi telah berjalan untuk sementara waktu, itu perlu menyerahkan kontrol secara sukarela untuk menjaga sistem responsif.

Masalah dengan multitasking kooperatif adalah jika tugas tersebut tidak pernah menyerah kontrol, itu dapat merusak sistem. Hanya itu dan rutinitas interupsi apa pun yang kebetulan diberikan kontrol yang dapat berjalan, sehingga OS tampaknya akan terkunci. Ini adalah aspek "kooperatif" dari sistem ini. Jika pengawas waktu diimplementasikan yang hanya mengatur ulang ketika switch tugas dilakukan, maka dimungkinkan untuk menangkap tugas-tugas yang salah ini.

Windows 3.1 dan sebelumnya adalah sistem operasi kooperatif, yang sebagian mengapa kinerja mereka tidak begitu bagus.

Preemptive multitasking lebih sulit untuk diterapkan. Di sini, tugas tidak diharuskan untuk menyerahkan kontrol secara manual, tetapi sebaliknya setiap tugas dapat diberikan jumlah maksimum waktu untuk menjalankan (katakanlah 10 ms), dan kemudian sakelar tugas dilakukan ke tugas yang dapat dijalankan berikutnya jika ada. Ini mengharuskan penghentian tugas secara sewenang-wenang, menyimpan semua informasi status, dan kemudian mengalihkan SP ke tugas lain dan memulainya. Ini membuat pengalih tugas lebih rumit, membutuhkan lebih banyak tumpukan, dan memperlambat sistem sedikit.

Untuk multitasking kooperatif dan preemptive, interupsi dapat terjadi kapan saja yang sementara akan mencegah tugas berjalan.

Seperti yang ditunjukkan supercat dalam komentar, satu kelebihan yang dimiliki multitasking koperasi adalah lebih mudah untuk berbagi sumber daya (mis. Perangkat keras seperti ADC multi-saluran atau perangkat lunak seperti memodifikasi daftar yang ditautkan). Terkadang dua tugas menginginkan akses ke sumber daya yang sama pada saat yang sama. Dengan penjadwalan preemptive, OS dimungkinkan untuk berpindah tugas di tengah satu tugas menggunakan sumber daya. Jadi kunci diperlukan untuk mencegah tugas lain masuk dan mengakses sumber daya yang sama. Dengan multitasking kooperatif, ini tidak perlu karena tugas mengontrol kapan akan melepaskannya kembali ke OS.

tcrosley
sumber
3
Keuntungan dari multitasking kooperatif adalah bahwa dalam banyak kasus tidak perlu menggunakan kunci untuk mengoordinasikan akses ke sumber daya. Ini akan cukup untuk memastikan bahwa tugas selalu meninggalkan sumber daya dalam keadaan dapat dibagikan setiap kali mereka melepaskan kendali. Melakukan banyak tugas preemptive jauh lebih rumit jika suatu tugas dapat dimatikan sementara itu memegang kunci pada sumber daya yang dibutuhkan oleh tugas lain. Dalam beberapa kasus, tugas kedua mungkin berakhir diblokir lebih lama daripada yang seharusnya di bawah sistem koperasi, karena tugas memegang kunci akan mengabdikan sistem ...
supercat
1
... sumber daya penuh untuk menyelesaikan tindakan yang (pada sistem pre-emptive) akan membutuhkan kunci, sehingga membuat objek yang dijaga tersedia untuk tugas kedua.
supercat
1
Sementara multitasker kooperatif membutuhkan disiplin, memastikan bahwa persyaratan waktu akan dipenuhi kadang-kadang bisa lebih mudah di bawah multitasker kooperatif daripada di bawah yang preemptive. Karena sangat sedikit kunci yang perlu dipegang pada sakelar tugas, sistem sakelar tugas putaran lima robin di mana tugas diminta untuk tidak melebihi 10 ms tanpa menghasilkan, dikombinasikan dengan sedikit logika yang mengatakan "Jika tugas X mendesak perlu dijalankan, jalankan selanjutnya ", akan memastikan bahwa tugas X tidak harus menunggu lebih dari 10 ms begitu ia memberi tanda sebelum dijalankan. Sebaliknya, jika sebuah tugas membutuhkan kunci yang tugas X ...
supercat
1
... akan perlu tetapi akan diganti oleh switcher pre-emptive sebelum melepaskannya, X mungkin tidak bisa melakukan sesuatu yang berguna sampai penjadwal CPU berkeliling untuk menjalankan tugas pertama. Kecuali jika penjadwal menyertakan logika untuk mengenali dan menangani inversi prioritas, mungkin diperlukan beberapa saat sebelum berkeliling untuk membiarkan tugas pertama menyelesaikan bisnisnya dan melepaskan kunci. Masalah seperti itu tidak dapat diselesaikan, tetapi menyelesaikannya membutuhkan banyak kerumitan yang bisa dihindari dalam sistem koperasi. Sistem koperasi bekerja sangat baik kecuali untuk satu gotcha: ...
supercat
3
Anda tidak perlu banyak tumpukan di koperasi jika Anda membuat kode secara berkelanjutan. Intinya, kode Anda terbagi dalam fungsi void foo(void* context)- fungsi logika pengontrol (kernel) menarik satu penunjuk dan sepasang penunjuk fungsi dari antrian dan menyebutnya satu per satu. Fungsi itu menggunakan konteks untuk menyimpan variabel dan semacamnya dan kemudian dapat menambahkan mengirimkan kelanjutan ke antrian. Fungsi-fungsi itu harus kembali dengan cepat agar tugas-tugas lain ada di CPU. Ini adalah metode berbasis peristiwa yang hanya membutuhkan satu tumpukan.
ratchet freak
16

Threading disediakan oleh sistem operasi. Di dunia tertanam kami biasanya tidak memiliki OS ("bare metal"). Jadi ini meninggalkan opsi berikut:

  • Lingkaran polling utama klasik. Fungsi utama Anda memiliki beberapa saat (1) yang mengerjakan tugas 1 lalu mengerjakan tugas 2 ...
  • Main loop + flag ISR: Anda memiliki ISR ​​yang melakukan fungsi kritis-waktu dan kemudian memberi tahu loop utama melalui variabel flag yang perlu dilayani oleh tugas tersebut. Mungkin ISR menempatkan karakter baru di buffer melingkar, dan kemudian memberi tahu loop utama untuk menangani data ketika siap untuk melakukannya.
  • Semua ISR: Banyak logika di sini dijalankan dari ISR. Pada pengontrol modern seperti ARM yang memiliki beberapa tingkat prioritas. Ini dapat memberikan skema "mirip-ulir" yang kuat, tetapi juga bisa membingungkan untuk debug sehingga harus dicadangkan hanya untuk batasan waktu kritis.
  • RTOS: Kernel RTOS (difasilitasi oleh timer ISR) dapat memungkinkan untuk beralih di antara beberapa utas eksekusi. Anda menyebutkan FreeRTOS.

Saya akan menyarankan Anda menggunakan skema paling sederhana di atas yang akan berfungsi untuk aplikasi Anda. Dari apa yang Anda jelaskan, saya akan memiliki paket penghasil loop utama dan menempatkannya dalam buffer bundar. Kemudian minta driver berbasis UART ISR yang menyala setiap kali byte sebelumnya selesai mengirim hingga buffer dikirim, kemudian menunggu konten buffer yang lebih banyak. Pendekatan serupa untuk ethernet.

Houston Fortney
sumber
3
Ini adalah jawaban yang sangat berguna karena membahas akar masalah (cara melakukan multitask pada sistem tertanam yang kecil, dan bukannya utas sebagai solusi). Sebuah paragraf tentang bagaimana hal itu dapat diterapkan pada pertanyaan awal akan luar biasa, mungkin termasuk pro dan kontra dari masing-masing skenario.
David
8

Seperti pada prosesor single-core yang melakukan multitasking perangkat lunak nyata tidak mungkin. Jadi, Anda harus berhati-hati untuk beralih di antara beberapa tugas satu arah. RTOS yang berbeda menangani itu. Mereka memiliki penjadwal dan berdasarkan pada centang sistem mereka akan beralih di antara tugas yang berbeda untuk memberi Anda kemampuan multitasking.

Konsep-konsep yang terlibat dalam melakukannya (menyimpan dan memulihkan konteks) cukup rumit, jadi melakukan ini secara manual mungkin akan sulit dan membuat kode Anda lebih kompleks dan karena Anda belum pernah melakukannya sebelumnya, akan ada kesalahan di dalamnya. Saran saya di sini adalah menggunakan RTOS yang diuji seperti FreeRTOS.

Anda menyebutkan bahwa interupsi memberikan tingkat multitasking. Ini agak benar. Interrupt akan mengganggu program Anda saat ini di titik mana pun dan mengeksekusi kode di sana, itu sebanding dengan dua sistem tugas di mana Anda memiliki 1 tugas dengan prioritas rendah dan lainnya dengan prioritas tinggi yang selesai dalam satu irisan waktu penjadwal.

Jadi Anda bisa menulis interrupt handler untuk timer berulang yang akan mengirim beberapa paket melalui UART, lalu biarkan sisa program Anda dieksekusi selama beberapa milidetik dan kirim beberapa byte berikutnya. Dengan begitu Anda semacam mendapatkan kemampuan multitasking terbatas. Tetapi Anda juga akan memiliki interupsi yang agak lama yang mungkin merupakan hal yang buruk.

Satu-satunya cara nyata untuk melakukan banyak tugas sekaligus pada MCU single-core adalah dengan menggunakan DMA dan peripheral karena mereka bekerja secara independen dari core (DMA dan MCU berbagi bus yang sama, sehingga mereka bekerja sedikit lebih lambat ketika keduanya aktif). Jadi, sementara DMA mengocok byte ke UART, inti Anda bebas mengirim barang ke ethernet.

Gudang senjata
sumber
2
terima kasih, DMA terdengar menarik. Saya pasti akan mencarinya.!
Pesawat
Tidak semua seri PIC memiliki DMA.
Matt Young
1
Saya menggunakan PIC32;)
Pesawat
6

Jawaban lain sudah menggambarkan opsi yang paling sering digunakan (loop utama, ISR, RTOS). Berikut opsi lain sebagai kompromi: Protothreads . Ini pada dasarnya lib sangat ringan untuk utas, yang menggunakan loop utama dan beberapa makro C, untuk "mengemulasi" RTOS. Tentu saja ini bukan OS lengkap, tetapi untuk utas "sederhana" itu bisa bermanfaat.

erebos
sumber
dari mana saya dapat mengunduh kode sumbernya untuk windows? Saya pikir ini hanya tersedia untuk linux.!
Pesawat
@CZAbhinav Seharusnya OS independen dan Anda bisa mendapatkan unduhan terbaru di sini .
erebos
Saya di windows sekarang dan menggunakan MplabX, saya pikir ini tidak berguna di sini. Pokoknya terima kasih.!
Pesawat
Belum pernah mendengar tentang protothreads, kedengarannya seperti teknik yang menarik.
Arsenal
@CZAbhinav Apa yang kamu bicarakan? Ini kode C dan tidak ada hubungannya dengan sistem operasi Anda.
Matt Young
3

Desain dasar saya untuk RTOS irisan waktu minimal tidak banyak berubah pada beberapa keluarga mikro. Ini pada dasarnya penghenti waktu mengendarai mesin negara. Rutin layanan interupsi adalah kernel OS sedangkan pernyataan switch di loop utama adalah tugas pengguna. Driver perangkat adalah interupsi rutinitas layanan untuk interupsi I / O.

Struktur dasar adalah sebagai berikut:

unsigned char tick;

void interrupt HANDLER(void) {
    device_driver_A();
    device_driver_B();
    if(T0IF)
    {
        TMR0 = TICK_1MS;
        T0IF = 0;   // reset timer interrupt
        tick ++;
    }
}

void main(void)
{
    init();

    while (1) {
        // periodic tasks:
        if (tick % 10 == 0) { // roughly every 10 ms
            task_A();
            task_B();    
        }
        if (tick % 55 == 0) { // roughly every 55 ms
            task_C();
            task_D();    
        }

        // tasks that need to run every loop:
        task_E();
        task_F();
    }
}

Ini pada dasarnya adalah sistem multitasking yang kooperatif. Tugas ditulis untuk tidak pernah memasukkan loop infinite tetapi kami tidak peduli karena tugas dijalankan dalam loop event sehingga infinite loop implisit. Ini adalah gaya pemrograman yang mirip dengan bahasa yang berorientasi acara / nonblocking seperti javascript atau go.

Anda dapat melihat contoh gaya arsitektur ini dalam perangkat lunak pemancar RC saya (ya, saya benar-benar menggunakannya untuk menerbangkan pesawat RC sehingga agak aman untuk mencegah saya menabrak pesawat saya dan berpotensi membunuh orang): https://github.com / slebetman / pic-txmod . Ini pada dasarnya memiliki 3 tugas - 2 tugas real-time diimplementasikan sebagai driver perangkat stateful (lihat hal-hal ppmio) dan 1 tugas latar belakang menerapkan logika pencampuran. Jadi pada dasarnya ini mirip dengan server web Anda karena memiliki 2 utas I / O.

Slebetman
sumber
1
Saya tidak akan benar-benar memanggil 'multitasking kooperatif', karena itu benar-benar tidak jauh berbeda dari program mikrokontroler lain yang harus melakukan banyak hal.
whatsisname
2

Sementara saya menghargai bahwa pertanyaan yang secara khusus ditanyakan tentang penggunaan RTOS yang tertanam, terpikir oleh saya bahwa pertanyaan yang lebih luas yang ditanyakan adalah "bagaimana mencapai multitasking pada platform tertanam".

Saya sangat menyarankan Anda untuk melupakan menggunakan RTOS tertanam setidaknya untuk saat ini. Saya menyarankan ini karena saya pikir sangat penting untuk terlebih dahulu belajar tentang bagaimana mencapai tugas 'concurrency' dengan menggunakan teknik pemrograman yang sangat sederhana yang terdiri dari penjadwal tugas sederhana dan mesin negara.

Untuk menjelaskan konsepnya dengan sangat singkat, setiap modul pekerjaan yang perlu dilakukan (yaitu masing-masing 'tugas') memiliki fungsi tertentu yang harus disebut ('dicentang') secara berkala agar modul tersebut dapat melakukan beberapa hal. Modul mempertahankan statusnya sendiri saat ini. Anda kemudian memiliki loop infinite utama (penjadwal) yang memanggil fungsi-fungsi modul.

Ilustrasi kasar:

for(;;)
{
    main_lcd_ui_tick();
    networking_tick();
}


...

// In your LCD UI module:
void main_lcd_ui_tick(void)
{
    check_for_key_presses();
    update_lcd();
}

...

// In your networking module:
void networking_tick(void)
{
    //'Tick' the TCP/IP library. In this example, I'm periodically
    //calling the main function for Keil's TCP/IP library.
    main_TcpNet();
}

Struktur pemrograman single-threaded seperti ini di mana Anda secara berkala memanggil fungsi-fungsi mesin negara utama dari loop scheduler utama ada di mana-mana dalam pemrograman tertanam, dan inilah mengapa saya akan sangat mendorong OP untuk terbiasa dan nyaman dengan itu terlebih dahulu, sebelum menyelam langsung menggunakan Tugas / utas RTOS.

Saya bekerja pada jenis perangkat tertanam yang memiliki antarmuka LCD perangkat keras, server web internal, klien email, klien DDNS, VOIP, dan banyak fitur lainnya. Meskipun kami menggunakan RTOS (Keil RTX), jumlah utas individu (tugas) yang digunakan sangat kecil dan sebagian besar 'multitasking' dicapai seperti dijelaskan di atas.

Untuk memberikan beberapa contoh perpustakaan yang menunjukkan konsep ini:

  1. Perpustakaan jaringan Keil. Seluruh tumpukan TCP / IP dapat dijalankan single-threaded; Anda secara berkala memanggil main_TcpNet (), yang mengulangi tumpukan TCP / IP dan opsi jaringan lain yang telah Anda kompilasi dari perpustakaan (mis. server web). Lihat http://www.keil.com/support/man/docs/rlarm/rlarm_main_tcpnet.htm . Memang, dalam beberapa situasi (mungkin di luar lingkup jawaban ini) Anda mencapai titik di mana ia mulai bermanfaat atau perlu menggunakan utas (terutama jika menggunakan pemblokiran soket BSD). (Catatan lebih lanjut: V5 MDK-ARM baru sebenarnya memunculkan thread Ethernet khusus - tapi saya hanya mencoba memberikan ilustrasi.)

  2. Pustaka VOIP Linphone. Pustaka linphone itu sendiri berulir tunggal. Anda memanggil iterate()fungsi pada interval yang cukup. Lihat http://www.linphone.org/docs/liblinphone-javadoc/org/linphone/core/LinphoneCore.html#iterate () . (Sedikit contoh buruk karena saya menggunakan ini pada platform Linux yang tertanam dan perpustakaan dependensi linphone tidak diragukan lagi menelurkan thread, tapi sekali lagi ini untuk menggambarkan suatu hal.)

Kembali ke masalah khusus yang digariskan oleh OP, masalahnya tampaknya menjadi fakta bahwa komunikasi UART harus terjadi pada saat yang sama dengan beberapa jaringan (mengirimkan paket melalui TCP / IP). Saya tidak tahu perpustakaan jaringan apa yang sebenarnya Anda gunakan, tetapi saya menganggap itu memiliki fungsi utama yang perlu sering dipanggil. Anda perlu menulis kode Anda yang berhubungan dengan pengiriman / penerimaan data UART agar disusun dengan cara yang sama, sebagai mesin negara yang dapat diulang dengan panggilan berkala ke fungsi utama.

Trevor Page
sumber
2
Terima kasih atas penjelasan yang bagus ini, saya menggunakan pustaka TCP / IP yang disediakan oleh microchip dan ini adalah kode kompleks yang sangat besar. Saya entah bagaimana berhasil memecahnya menjadi beberapa bagian dan membuatnya dapat digunakan sesuai dengan kebutuhan saya. Saya pasti akan mencoba salah satu pendekatan Anda.!
Pesawat
Bersenang-senang :) Menggunakan RTOS pasti membuat hidup lebih mudah dalam banyak situasi. Dalam pandangan saya, menggunakan utas (tugas) membuat upaya pemrograman jauh lebih mudah di satu sisi, karena Anda dapat menghindari keharusan memecah tugas Anda menjadi mesin negara. Alih-alih, Anda hanya menulis kode tugas sama seperti di program C # Anda, dengan kode tugas Anda dibuat seolah-olah itu satu-satunya yang ada. Sangat penting untuk mengeksplorasi kedua pendekatan, dan saat Anda melakukan lebih banyak pemrograman tertanam, Anda mulai merasakan pendekatan mana yang terbaik dalam setiap situasi.
Trevor Halaman
Saya juga lebih suka menggunakan opsi threading. :)
Pesawat