多任務處理將計算機帶入了一場革命,其中一個或多個程序可以同時運行,從而提高了效率、靈活性、適應性和生產(chǎn)力。在嵌入式系統(tǒng)中,微控制器還可以處理多任務并同時執(zhí)行兩個或多個任務,而不會停止當前指令。
在本教程中,我們將學習Arduino 如何使用 Arduino millis 函數(shù)執(zhí)行多任務處理。通常在 Arduino 中使用delay()函數(shù)來執(zhí)行LED 閃爍等周期性任務,但此 delay() 函數(shù)會暫停程序一段確定的時間,并且不允許執(zhí)行其他操作。所以這篇文章解釋了我們?nèi)绾伪苊馐褂?delay() 函數(shù)并將其替換為 millis()以同時執(zhí)行多個任務并使 Arduino 成為一個多任務控制器。
什么是多任務處理?
多任務處理只是意味著同時執(zhí)行多個任務或程序。幾乎所有操作系統(tǒng)都具有多任務處理功能。這種操作系統(tǒng)被稱為MOS(多任務操作系統(tǒng))。MOS 可以是移動或桌面 PC 操作系統(tǒng)。計算機中多任務處理的一個很好的例子是,當用戶同時運行電子郵件應用程序、互聯(lián)網(wǎng)瀏覽器、媒體播放器、游戲時,如果用戶不想使用該應用程序,如果不關(guān)閉,它就會在后臺運行。最終用戶同時使用所有這些應用程序,但操作系統(tǒng)采用這個概念有點不同。讓我們討論一下操作系統(tǒng)如何管理多任務。
如圖所示,CPU 將時間分成三個相等的部分,并將每個部分分配給每個任務/應用程序。這就是大多數(shù)系統(tǒng)中多任務處理的方式。Arduino Multitasking的概念幾乎相同,只是時間分布會有所不同。由于 Arduino 與筆記本電腦/手機/PC 相比以低頻運行且 RAM 運行,因此分配給每個任務的時間也會有所不同。Arduino 還有一個廣泛使用的delay()函數(shù)。但在開始之前,讓我們討論一下為什么我們不應該在任何項目中使用delay()函數(shù)。
為什么要使用 millis() ?
為了克服使用延遲帶來的問題,開發(fā)人員應該使用millis()函數(shù),一旦你習慣了它就很容易使用,它會使用100%的CPU性能而不會在執(zhí)行指令時產(chǎn)生任何延遲。millis()是一個函數(shù),它只返回自 Arduino 板開始運行當前程序而不凍結(jié)程序以來經(jīng)過的毫秒數(shù)。大約 50 天后,該時間數(shù)將溢出(即回到零)。
就像Arduino有delayMicroseconds()一樣,它也有micro版本的millis()作為micros()。micros 和 millis 之間的區(qū)別在于,micros() 將在大約 70 分鐘后溢出,而 millis() 則為 50 天。因此,根據(jù)應用程序,您可以使用millis() 或micros()。
使用毫秒()而不是延遲():
要使用millis()進行計時和延遲,您需要記錄并存儲動作發(fā)生的時間以開始時間,然后每隔一段時間檢查定義的時間是否已經(jīng)過去。如前所述,將當前時間存儲在一個變量中。
無符號長 currentMillis = millis();
我們需要另外兩個變量來確定是否已經(jīng)過了所需的時間。我們已將當前時間存儲在currentMillis變量中,但我們還需要知道計時周期何時開始以及該周期有多長。因此聲明了 Interval 和previousMillis。間隔將告訴我們時間延遲,previosMillis 將存儲事件最后發(fā)生的時間。
unsigned long previousMillis; 無符號長周期 = 1000;
為了理解這一點,讓我們以一個簡單的閃爍 LED 為例。period = 1000 將告訴我們 LED 將閃爍 1 秒或 1000 毫秒。
常量 int ledPin = 4; // 連接的 LED 引腳號 int ledState = LOW; // 用于設(shè)置 LED 狀態(tài) unsigned long previousMillis = 0; //將存儲上次 LED 閃爍的時間 const long period = 1000; // 以毫秒為單位閃爍的周期 void setup() { pinMode(ledPin, OUTPUT); // 將 ledpin 設(shè)置為輸出 } void loop() { unsigned long currentMillis = millis(); // 存儲當前時間 if (currentMillis - previousMillis >= period) { // 檢查是否經(jīng)過了 1000ms previousMillis = currentMillis; // 保存上次閃爍 LED 的時間 if (ledState == LOW) { // 如果 LED 關(guān)閉,則將其打開,反之亦然 ledState = HIGH; } 其他 { ledState = 低; } digitalWrite(ledPin, ledState);//設(shè)置帶ledState的LED再次閃爍 } }
在這里,語句《if (currentMillis - previousMillis 》= period)》檢查 1000 毫秒是否已過。如果 1000 毫秒過去了,則 LED 閃爍并再次進入相同狀態(tài)。這種情況還在繼續(xù)。就是這樣,我們已經(jīng)學會了使用毫秒而不是延遲。這樣它就不會在特定的時間間隔內(nèi)停止程序。
Arduino 中的中斷與其他微控制器中的工作方式相同。Arduino UNO 板有兩個獨立的引腳,用于在 GPIO 引腳 2 和 3 上附加中斷。我們在Arduino 中斷教程中詳細介紹了它,您可以在其中了解有關(guān)中斷及其使用方法的更多信息。
在這里,我們將通過同時處理兩個任務來展示 Arduino 多任務處理。這些任務將包括兩個 LED 以不同的時間延遲閃爍以及一個按鈕,該按鈕將用于控制 LED 的開/關(guān)狀態(tài)。所以三個任務將同時執(zhí)行。
所需組件
Arduino UNO
三個 LED(任何顏色)
電阻(470、10k)
跳線
面包板
電路原理圖
演示使用Arduino Millis() 函數(shù)的電路圖 非常簡單,無需附加太多組件,如下所示。
為多任務處理編程 Arduino UNO
為多任務編程 Arduino UNO 只需要上面解釋的 millis() 工作原理背后的邏輯。建議在開始對 Arduino UNO 進行多任務編程之前,一次又一次地練習使用millis閃爍 LED ,以使邏輯清晰并讓自己對 millis() 感到滿意。在本教程中,中斷還與 millis() 同時用于多任務處理。該按鈕將是一個中斷。因此,只要產(chǎn)生中斷,即按下按鈕,LED 就會切換到 ON 或 OFF 狀態(tài)。
編程從聲明連接 LED 和按鈕的引腳號開始。
詮釋 led1 = 6; 詮釋 led2 = 7; int toggleLed = 5; int 按鈕 = 2;
接下來我們編寫一個變量來存儲 LED 的狀態(tài)以備將來使用。
詮釋 ledState1 = 低; 詮釋 ledState2 = 低;
正如上面閃爍示例中所解釋的,period 和 previousmillis 的變量被聲明為比較并為 LED 生成延遲。第一個 LED 每 1 秒閃爍一次,另一個 LED 在 200ms 后閃爍。
unsigned long previousMillis1 = 0; 常量長周期1 = 1000; unsigned long previousMillis2 = 0; 常量長周期2 = 200;
另一個毫秒函數(shù)將用于生成去抖動延遲,以避免多次按下按鈕。將有與上述類似的方法。
int debouncePeriod = 20; int debounceMillis = 0;
這三個變量將用于存儲按鈕的狀態(tài)為中斷、切換 LED 和按鈕狀態(tài)。
bool buttonPushed = false; int ledChange = 低; 詮釋最后狀態(tài)=高;
定義引腳的動作,哪個引腳將作為 INPUT 或 OUTPUT 工作。
pinMode(led1,輸出); pinMode(led2,輸出); pinMode(toggleLed,輸出); pinMode(按鈕,輸入);
現(xiàn)在通過附加中斷與 ISR 和中斷模式的定義來定義中斷引腳。請注意,建議在聲明attachInterrupt()函數(shù)時使用digitalPinToInterrupt(pin_number)將實際的數(shù)字引腳轉(zhuǎn)換為特定的中斷號。
attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE);
中斷子程序被編寫,它只會改變buttonPushed標志。需要注意的是,中斷子程序要盡可能的短,所以盡量寫,盡量減少多余的指令。
無效 pushButton_ISR() { buttonPushed = true; }
循環(huán)首先將毫秒值存儲在 currentMillis 變量中,該變量將存儲每次循環(huán)迭代時經(jīng)過的時間值。
無符號長 currentMillis = millis();
多任務處理共有三個功能,1 秒閃爍一個 LED,200 毫秒閃爍第二個 LED,如果按下按鈕,則關(guān)閉/打開 LED。所以我們將寫三個部分來完成這個任務。
第一個是通過比較經(jīng)過的毫秒數(shù)每 1 秒切換一次 LED 狀態(tài)。
if (currentMillis - previousMillis1 >= period1) { previousMillis1 = currentMillis; 如果(ledState1 == 低){ ledState1 = 高; } 其他 { ledState1 = 低; } digitalWrite(led1, ledState1); }
類似地,第二次它通過比較經(jīng)過的毫秒數(shù)每 200 毫秒后切換一次 LED。解釋已經(jīng)在本文前面進行了解釋。
if (currentMillis - previousMillis2 >= period2) { previousMillis2 = currentMillis; 如果(ledState2 == 低){ ledState2 = 高; } 其他 { ledState2 = 低; } digitalWrite(led2, ledState2); }
最后,buttonPushed標志被監(jiān)控??,在產(chǎn)生 20ms 的去抖動延遲后,它只是切換 LED 的狀態(tài),對應于作為中斷附加的按鈕。
if (buttonPushed = true) // 檢查是否調(diào)用了 ISR { if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // 產(chǎn)生 20ms 的去抖延遲以避免多次按下 { debounceMillis = currentMillis; // 保存最后的去抖動延遲時間 if (digitalRead(pushButton) == LOW && lastState == HIGH) // 按下按鈕后改變LED { ledChange = ! 領(lǐng)導改變; digitalWrite(toggleLed, ledChange); 最后狀態(tài) = 低; } else if (digitalRead(pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushed = 假; } }
這樣就完成了Arduino millis?() 教程。請注意,為了習慣使用millis(),只需練習在其他一些應用程序中實現(xiàn)此邏輯即可。
/* 使用 Arduino millis() 函數(shù)進行多任務處理
作者:CircuitDigest (circuitdigest.com)
*/
詮釋 led1 = 6; // led1 連接在引腳 6
int led2 = 7; // led1 連接在引腳 7
int toggleLed = 5; // 按鈕控制的 LED 連接在引腳 5
int pushButton = 2; // 將按鈕連接到引腳 2,這也是中斷引腳
詮釋 ledState1 = 低;// 判斷 led1 和 led2 的狀態(tài)
int ledState2 = LOW;
unsigned long previousMillis1 = 0; //存儲上次 LED1 閃爍的時間
const long period1 = 1000; // led1 閃爍的時間,單位為 ms
unsigned long previousMillis2 = 0; //存儲上次 LED2 閃爍的時間
const long period2 = 200; // led1 閃爍的時間,單位為 ms
int debouncePeriod = 20; // 20ms 的去抖動延遲
int debounceMillis = 0; // 類似于previousMillis
bool buttonPushed = false; // 中斷例程按鈕狀態(tài)
int ledChange = LOW; // 跟蹤 LED 狀態(tài) last
int lastState = HIGH; // 跟蹤最后一個按鈕狀態(tài)
無效設(shè)置(){
pinMode(led1,輸出);// 將引腳定義為輸入或輸出
pinMode(led2, OUTPUT);
pinMode(toggleLed,輸出);
pinMode(按鈕,輸入);
attachInterrupt(digitalPinToInterrupt(pushButton), pushButton_ISR, CHANGE); // 使用中斷 pin2
}
無效 pushButton_ISR()
{
buttonPushed = true; // ISR 應該盡可能短
}
void loop() {
unsigned long currentMillis = millis(); // 存儲當前時間
if (currentMillis - previousMillis1 >= period1) { // 檢查是否經(jīng)過了 1000ms
previousMillis1 = currentMillis; // 保存上次閃爍 LED 的時間
if (ledState1 == LOW) { // 如果 LED 關(guān)閉,則將其打開,反之亦然
ledState1 = HIGH; //更改下一次迭代的 LED 狀態(tài)
} else {
ledState1 = LOW;
}
digitalWrite(led1, ledState1); //用ledState設(shè)置LED再次閃爍
}
if (currentMillis - previousMillis2 >= period2) { // 檢查是否經(jīng)過了 1000ms
previousMillis2 = currentMillis; // 保存上次閃爍 LED 的時間
if (ledState2 == LOW) { // 如果 LED 關(guān)閉,則將其打開,反之亦然
ledState2 = HIGH;
} 其他 {
ledState2 = 低;
}
digitalWrite(led2, ledState2);//設(shè)置帶ledState的LED再次閃爍
}
if (buttonPushed = true) // 檢查是否調(diào)用了 ISR
{
if ((currentMillis - debounceMillis) > debouncePeriod && buttonPushed) // 產(chǎn)生 20ms 的去抖延遲以避免多次按下
{
debounceMillis = currentMillis; // 保存最后的去抖動延遲時間
if (digitalRead(pushButton) == LOW && lastState == HIGH) // 按下按鈕后改變LED
{
ledChange = ! 領(lǐng)導改變;
digitalWrite(toggleLed, ledChange);
最后狀態(tài) = 低;
}
else if (digitalRead(pushButton) == HIGH && lastState == LOW)
{
lastState = HIGH;
}
buttonPushed = 假;
}
}
}
-
函數(shù)
+關(guān)注
關(guān)注
3文章
4327瀏覽量
62569 -
Arduino
+關(guān)注
關(guān)注
188文章
6468瀏覽量
186952 -
多任務處理
+關(guān)注
關(guān)注
0文章
2瀏覽量
4790
發(fā)布評論請先 登錄
相關(guān)推薦
評論