題:
PIC微控制器上的多任務
Aircraft
2015-10-07 14:31:13 UTC
view on stackexchange narkive permalink

這些天來,多任務處理很重要。我不知道我們如何在微控制器和嵌入式編程中實現它。我正在設計一個基於PIC微控制器的系統。我已經使用C在MplabX IDE中設計了其固件,然後使用C#在Visual Studio中為其設計了一個應用程序。

由於我已經習慣於在桌面上的C#編程中使用線程來實現並行任務,有沒有辦法在我的微控制器代碼中做同樣的事情? MplabX IDE提供了 pthreads.h ,但這只是一個存根,沒有實現。我知道有FreeRTOS支持,但是使用它會使您的代碼更複雜。某個論壇上說,中斷也可以用作多任務處理,但我認為中斷不等同於線程。

我正在設計一個系統,該系統將一些數據發送到UART,同時需要通過(有線)以太網將數據發送到網站。用戶可以通過網站控制輸出,但是輸出會以2-3秒的延遲打開/關閉。這就是我面臨的問題。對於微控制器中的多任務有什麼解決方案嗎?

線程只能在運行OS的處理器上使用,因為線程是進程的一部分,而進程僅在OS中使用。
@Zola是的,您是對的。但是,對於控制器該怎麼辦?
http://www.microchip.com/forums/fb.aspx?m=233317對此進行檢查。
[嵌入式系統RTOS]的可能副本(http://electronics.stackexchange.com/questions/824/rtos-for-embedded-systems)
您能解釋為什麼需要真正的多任務處理,而又無法基於循環任務方法或select()循環或類似方法合理地實現軟件嗎?
好了,正如我已經說過的,我正在向uart發送和接收數據,同時又向以太網發送和接收數據。除此之外,我還需要將數據與時間一起保存在SD卡中,因此可以使用DS1307 RTC,還可以使用EEPROM。到目前為止,我只有1個UART,但可能幾天后我將要從3個UART模塊發送和接收數據。網站還將接收來自遠程安裝的5個不同系統的數據。所有這些都必須是並行的,但不是並行的,而是要延遲幾秒鐘。!
如果您想要的起點比freeRTOS陡峭,請訪問RIOS-scheduler,網址為http://www.riosscheduler.org/。RIOS-scheduler尚不完整,但旨在學習此主題。
@user247245感謝RIOS。看起來很有趣,一定會嘗試
@user247245看起來`RIOS`只有一個文件'RIMS.h`,並且所有內容都包含在其中。不知道它是否可以工作,但是想嘗試一下。在哪裡可以下載此文件。?
只是為了增加參考。[PIC上嵌入式多任務處理的基礎](http://www.embedded.com/design/mcus-processors-and-socs/4007247/The-basics-of-embedded-multitasking-on-a-PIC-Part-1-重入式多任務處理)。2007年的Embedded.com文章。
RIMS.h與scheduduler無關,它只是仿真程序RIMS的頭。如果您了解AVR,請改用以下翻譯:http://www.cs.ucr.edu/~vahid/rios/rios_avr.htm
這是RIMS,(對此一無所知)http://www.cs.ucr.edu/~vahid/pes/RITools/
六 答案:
tcrosley
2015-10-07 16:13:28 UTC
view on stackexchange narkive permalink

多任務操作系統主要有兩種:搶占式和協作式。兩者都允許在系統中定義多個任務,不同之處在於任務切換的工作方式。當然,只有一個核心處理器,一次實際上只運行一個任務。

兩種類型的多任務操作系統都需要為每個任務分配單獨的堆棧。因此,這意味著兩件事:首先,處理器允許將堆棧放置在RAM中的任何位置,並因此具有指令來移動堆棧指針(SP)-即,不存在像低端那樣的專用硬件堆棧PIC的。這樣就省去了PIC10、12和16系列。在不同的時候,我都為PIC24,PIC32、8051和80x86編寫了任務切換器。膽量完全不同,具體取決於處理器的體系結構。

第二個要求是要有足夠的RAM來提供多個堆棧。通常,一個堆棧至少需要數百個字節。但是,即使每個任務只有128個字節,八個堆棧也將需要1K字節的RAM-不過,您不必為每個任務分配相同大小的堆棧。請記住,您需要足夠的堆棧來處理當前任務,以及對其嵌套子例程的任何調用,還需要一個中斷調用的堆棧空間,因為您不知道何時會發生該事件。

有相當簡單的方法確定每個任務使用多少堆棧;例如,您可以將所有堆棧初始化為特定值,例如0x55,然後運行系統一段時間,然後停止並檢查內存。

您沒有說要使用哪種PIC。大多數PIC24和PIC32將有足夠的空間來運行多任務OS。 PIC18(唯一在RAM中具有堆棧的8位PIC)的最大RAM大小為4K。因此,這還真不容易。

使用協作式多任務處理(兩者中的較簡單者),僅當任務將其控制權“交給”操作系統時才執行任務切換。每當任務需要調用OS例程以執行它將等待的某些功能(例如I / O請求或計時器調用)時,就會發生這種情況。這使操作系統更容易切換堆棧,因為不必保存所有寄存器和狀態信息,因此可以將SP切換到另一個任務(如果沒有其他任務可以運行,則空閒堆棧是給定控制權)。如果當前任務不需要進行OS調用但已經運行了一段時間,則需要自動放棄控制以保持系統響應。

協作多任務處理的問題在於,如果該任務永不放棄控制權,它可以佔用系統。只有它和碰巧得到控制的所有中斷例程都可以運行,因此操作系統似乎會鎖定。這是這些系統的“合作”方面。如果實現了僅在執行任務切換時才重置的看門狗計時器,則有可能捕獲這些錯誤的任務。

Windows 3.1和更早的操作系統是協作操作系統,這部分原因是它們的性能沒有

搶先式多任務處理更難實現。在此,不需要手動放棄任務,而是可以為每個任務提供最大的運行時間(例如10毫秒),然後如果有任務,則將任務切換到下一個可運行任務。這要求任意停止一個任務,保存所有狀態信息,然後將SP切換到另一個任務並啟動它。這會使任務切換器變得更加複雜,需要更多的堆棧,並使系統速度降低一點。 / p>

正如supercat在評論中指出的那樣,協作多任務處理的一個優點是共享資源更加容易(例如,諸如多通道ADC之類的硬件或諸如修改鍊錶之類的軟件)。有時,兩個任務希望同時訪問同一資源。通過搶占式調度,操作系統可能會使用資源在一項任務的中間切換任務。因此,必須使用 locks 來防止其他任務進入並訪問同一資源。對於協作式多任務處理,這不是必需的,因為該任務控制著何時將其自身釋放回操作系統。

協作式多任務處理的優點在於,在大多數情況下,不必使用鎖來協調對資源的訪問。確保任務在放棄控制時始終將資源保持在可共享狀態就足夠了。如果搶占式多任務處理在鎖定另一個任務所需資源的同時可能被切換出去,則它會更加複雜。在某些情況下,第二個任務可能最終被阻塞的時間比在協作系統下要更長,因為持有鎖的任務本來就是在系統的專用上。
...用於完成在先發製人系統上需要鎖定的操作的全部資源,從而使受保護的對象可用於第二個任務。
@supercat-重新鎖定好點。忘了那個。
儘管協作式多任務處理程序需要紀律,但在協作式多任務處理程序下,確保搶占計時要求有時比在搶先式任務下更容易。由於在一個任務開關之間只需要保持很少的鎖,所以一個五任務循環任務開關係統要求任務在不屈服的情況下不超過10ms,並結合了一個小邏輯,即“如果任務X緊急,需要運行,然後再運行”,這將確保任務X發出信號後,不必再等待10毫秒以上才能開始運行。相比之下,如果任務需要鎖定,則哪個任務X ...
...將是必需的,但是在釋放它之前先被搶占式切換器切換出,X可能無法做任何有用的事情,直到CPU調度程序開始運行第一個任務。除非調度程序包含識別和處理優先級反轉的邏輯,否則它可能需要一段時間才能使第一個任務完成其業務並釋放鎖。這樣的問題不是無法解決的,但是解決它們需要很多複雜性,而這在協作系統中是可以避免的。協作系統工作出色,但有一個陷阱:...
重要的是,在編寫各種功能時,儘早做出決定,哪些功能將允許任務切換,哪些功能將不允許。如果承諾不執行任務切換的功能花費的時間比預期的要長,則可能有必要在每個呼叫站點檢查和/或重新處理代碼,以不要求承諾[反過來可能需要檢查和/或修改稱為新修改方法的方法等。]
@supercat我在您的回答中添加了非常簡短的評論內容。謝謝。
如果您連續編寫代碼,則不需要多個堆棧進行協作。本質上,您的代碼分為函數`void foo(void * context)`控制器邏輯(內核)提取隊列的一個指針和函數指針對,並一次調用它。該函數使用上下文存儲其變量等,然後可以將提交延續添加到隊列中。這些功能必須快速返回,以讓其他任務在CPU中發揮作用。這是基於事件的方法,僅需要單個堆棧。
協作式多任務處理的另一個優點是,它消除了每個任務創建1個堆棧的需要,因此為您打開了大門。這可能是協作多任務實現(例如Protothreads)的最大優勢:堆棧在每個任務開關之間完全解開。這不僅可以最大程度地利用微控制器上的RAM,而且還消除了任務溢出堆棧的可能性。對於高可靠性的應用程序,這是非常好的。關於如何可靠地檢測堆棧溢出(MMU,保護帶等)的文獻很多,但是還沒有防彈解決方案。
Houston Fortney
2015-10-07 14:51:43 UTC
view on stackexchange narkive permalink

線程是由操作系統提供的。在嵌入式世界中,我們通常沒有OS(“裸機”)。因此,這留下了以下選項:

  • 經典的主輪詢循環。您的主要功能有一個while(1),它執行任務1然後執行任務2 ...
  • 主循環+ ISR標誌:您有一個ISR,它執行時間緊迫的功能,然後向主循環發出警報通過任務需要服務的標誌變量。 ISR也許在循環緩衝區中放置了一個新字符,然後在準備好時告訴主循環處理數據。
  • 所有ISR:此處的大部分邏輯都是從ISR執行的。在具有多個優先級的現代控制器(如ARM)上。這可以提供功能強大的“類似於線程”的方案,但也可能使調試混亂,因此應僅將其保留用於關鍵的時序約束。
  • RTOS:RTOS內核(由計時器ISR促進)可以允許在多個執行線程之間切換。您提到了FreeRTOS。

我建議您使用適用於您的應用程序的上述最簡單的方案。根據您的描述,我將讓主循環生成數據包並將其放入循環緩衝區中。然後有一個基於UART ISR的驅動程序,該驅動程序將在發送完前一個字節時觸發,直到發送緩衝區為止,然後等待更多緩衝區內容。以太網的類似方法。

這是一個非常有用的答案,因為它解決了問題的根源(如何在小型嵌入式系統上執行多任務,而不是將線程作為解決方案)。關於如何將其應用於原始問題的段落非常棒,也許包括每種情況的利弊。
Arsenal
2015-10-07 14:46:51 UTC
view on stackexchange narkive permalink

就像在任何單核處理器中一樣,不可能進行真正的軟件多任務處理。因此,您必須小心以一種方式在多個任務之間切換。不同的RTOS正在處理此問題。他們有一個調度程序,並根據系統刻度在不同的任務之間切換,從而為您提供多任務處理能力。

這樣做的概念(上下文保存和還原)非常複雜,因此手動進行可能會很困難,並使您的代碼更加複雜,並且因為您之前從未做過,所以會有錯誤。我的建議是像FreeRTOS一樣使用經過測試的RTOS。

您提到中斷提供了一定程度的多任務處理。這是真的。中斷將在任何時候中斷當前程序並在其中執行代碼,這相當於兩個任務系統,在該系統中,您有一個低優先級的任務,而另一個高優先級的任務在調度程序的一個時間範圍內完成。

因此,您可以為循環計時器編寫一個中斷處理程序,該計時器將通過UART發送一些數據包,然後讓程序的其餘部分執行幾毫秒,然後發送接下來的幾個字節。這樣,您就獲得了有限的多任務處理能力。但是您也會有一個較長的中斷,這可能是一件壞事。

在單核MCU上同時執行多個任務的唯一真正方法是使用DMA和外圍設備因為它們獨立於內核工作(DMA和MCU共享同一條總線,所以當它們都處於活動狀態時,它們的工作速度會變慢)。因此,當DMA將字節改組到UART時,您的內核可以自由地將內容髮送到以太網。

謝謝,DMA聽起來很有趣。我一定會搜索的!
並非所有系列的PIC都有DMA。
我正在使用PIC32;)
erebos
2015-10-07 15:08:19 UTC
view on stackexchange narkive permalink

其他答案已經描述了最常用的選項(主循環,ISR,RTOS)。這是另一個折衷方案:協議線程。它基本上是線程的非常輕量級的庫,它使用主循環和一些C宏來“模擬” RTOS。當然,它不是完整的操作系統,但是對於“簡單”線程來說可能會有用。

從哪裡可以下載Windows的源代碼?我認為它僅適用於Linux。
@CZAbhinav它應獨立於操作系統,並且可以在[此處](http://dunkels.com/adam/pt/download.html)獲得最新下載。
我現在在Windows中並使用MplabX,我認為它在這裡沒有用。總之感謝。!
尚未聽說過原型線程,聽起來像是一種有趣的技術。
@CZAbhinav您在說什麼?它是C代碼,與您的操作系統無關。
@MattYoung C代碼不適用於Windows,因為它在tar.gz中可用
@CZAbhinav這是一個7z文件。
@CZAbhinav-您可以下載一個名為7zip的免費程序,該程序將在Windows上打開該文件:http://www.7-zip.org/download.html
@erebos protoThreads很棒,但是它們仍然不能執行並行任務。它執行第一項任務,然後移至第二項
slebetman
2015-10-08 05:56:48 UTC
view on stackexchange narkive permalink

我的最小時間RTOS的基本設計在幾個微型系列上並沒有太大變化。基本上,這是驅動狀態機的計時器中斷。中斷服務程序是OS內核,而主循環中的switch語句是用戶任務。設備驅動程序是I / O中斷的中斷服務程序。

基本結構如下:

 無符號字符滴答; void中斷處理程序(void){device_driver_A() ; device_driver_B(); if(T0IF){TMR0 = TICK_1MS; T0IF = 0; //重置計時器中斷滴答++; }} void main(void){init(); while(1){//週期性任務:if(tick%10 == 0){//大約每10毫秒task_A(); task_B(); } if(tick%55 == 0){//大約每55毫秒task_C(); task_D(); } //需要運行每個循環的任務:task_E(); task_F(); }}  

這基本上是一個協作式多任務處理系統。編寫任務時永遠不要進入無限循環,但是我們不在乎,因為任務在事件循環中運行,因此無限循環是隱式的。這與面向事件/無阻塞語言(如javascript或go)的編程風格類似。

您可以在我的RC發射器軟件中看到這種架構風格的示例(是的,我實際上使用它來實現遙控飛機,所以對我來說,防止我墜毀飛機並可能殺死人是至關重要的): https://github.com/slebetman/pic-txmod。它基本上具有3個任務-2個作為有狀態設備驅動程序實現的實時任務(請參閱ppmio資料)和1個實現混合邏輯的後台任務。因此,基本上它與您的Web服務器類似,因為它具有2個I / O線程。

我不會真正稱其為“協作多任務”,因為這與必須執行多項操作的任何其他微控制器程序並沒有實質性區別。
Trevor Page
2015-10-08 02:46:27 UTC
view on stackexchange narkive permalink

儘管我很欣賞這個問題專門詢問了嵌入式RTOS的使用,但在我看來,被問到的更廣泛的問題是“如何在嵌入式平台上實現多任務處理”。

我強烈建議您至少暫時不要使用嵌入式RTOS。我建議這樣做,因為我認為至關重要的是,首先要通過由簡單的任務調度程序和狀態機組成的極其簡單的編程技術來學習如何實現任務“並發”。

為了非常簡單地解釋這個概念,需要完成的每個工作模塊(即每個“任務”)都有一個特定的功能,必須定期調用(“選中”)該模塊才能完成某些工作東東。模塊保留其自己的當前狀態。然後,您有一個主無限循環(調度程序)調用模塊函數。

粗略圖示:

  for(;;){main_lcd_ui_tick();網絡_tick();} ... //在您的LCD UI模塊中:void main_lcd_ui_tick(void){check_for_key_presses(); update_lcd();} ... //在您的網絡模塊中:voidnetworking_tick(void){//'勾選'TCP / IP庫。在此示例中,我定期// //調用Keil的TCP / IP庫的主要功能。 main_TcpNet();}  

像這樣的單線程編程結構,使您可以從主調度程序循環中定期調用主狀態機函數,這在嵌入式編程中無處不在,因此我強烈鼓勵首先要熟悉並熟悉OP,然後再直接使用RTOS任務/線程。

我在一種嵌入式設備上工作,該設備具有硬件LCD界面,內部Web服務器,電子郵件客戶端, DDNS客戶端,VOIP和許多其他功能。儘管我們確實使用了RTOS(Keil RTX),但使用的單個線程(任務)的數量非常少,並且如上所述,大多數“多任務”都可以實現。

給出一些演示此概念的庫示例:

  1. Keil網絡庫。整個TCP / IP堆棧可以單線程運行;您會定期調用main_TcpNet(),該操作會迭代TCP / IP堆棧和從庫(例如Web服務器)中編譯的任何其他網絡選項。請參閱 http://www.keil.com/support/man/docs/rlarm/rlarm_main_tcpnet.htm。誠然,在某些情況下(可能不在此答案的範圍內),您確實達到了開始使用線程或變得有用的地步(尤其是在使用阻塞的BSD套接字時)。 (更多的注意:新的V5 MDK-ARM實際上產生了一個專用的以太網線程-但我只是想提供一個例子。)

  2. Linphone VOIP庫。 linphone庫本身是單線程的。您有足夠的時間間隔調用 iterate()函數。請參閱 http://www.linphone.org/docs/liblinphone-javadoc/org/linphone/core/LinphoneCore.html#iterate()。 (這是一個糟糕的例子,因為我在嵌入式Linux平台上使用了它,而linphone的依賴庫無疑會產生線程,但這只是為了說明這一點。)對於OP概述的特定問題,問題似乎在於UART通信必須與某些網絡同時進行(通過TCP / IP傳輸數據包)。我不知道您實際上在使用哪個網絡庫,但是我認為它具有需要經常調用的主要功能。您需要編寫處理UART數據發送/接收的代碼,以類似的方式進行結構化,作為可以通過定期調用主函數來迭代的狀態機。

感謝您的出色解釋,我使用的是微芯片提供的TCP / IP庫,它是非常龐大的複雜代碼。我以某種方式設法將其分解並根據我的要求使其可用。我一定會嘗試您的方法之一!
玩得開心:)在許多情況下,使用RTOS絕對會讓您的生活變得更輕鬆。在我看來,使用線程(任務)從某種意義上講簡化了編程工作,因為您可以避免將任務分解為狀態機。相反,您只需像在C#程序中一樣編寫任務代碼,就可以像創建唯一的任務代碼一樣創建任務代碼。探索這兩種方法至關重要,並且隨著您進行更多的嵌入式編程,您開始了解哪種方法在每種情況下都是最佳的。
我也更喜歡使用線程選項。:)


該問答將自動從英語翻譯而來。原始內容可在stackexchange上找到,我們感謝它分發的cc by-sa 3.0許可。
Loading...