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