0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

如何使用Arduino millis函數(shù)執(zhí)行多任務(wù)處理

科技觀察員 ? 來源:circuitdigest ? 作者:Abhimanyu Pandit ? 2022-09-06 14:41 ? 次閱讀

多任務(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ù)。

pYYBAGMW60iAYtxhAABWB9UQiL8404.png

如圖所示,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í)行。

poYBAGMW60KALgZpAATs0CDzeeE572.png

所需組件

Arduino UNO

三個(gè) LED(任何顏色)

電阻(470、10k)

跳線

面包板

電路原理

演示使用Arduino Millis() 函數(shù)的電路圖 非常簡(jiǎn)單,無需附加太多組件,如下所示。

poYBAGMW6zuABeMrAACZlx-F4jI138.png

為多任務(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 = 假;

}

}

}

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4284

    瀏覽量

    62325
  • Arduino
    +關(guān)注

    關(guān)注

    187

    文章

    6458

    瀏覽量

    186504
  • 多任務(wù)處理
    +關(guān)注

    關(guān)注

    0

    文章

    2

    瀏覽量

    4788
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    關(guān)于多任務(wù)處理的問題

    本帖最后由 mr.pengyongche 于 2013-4-30 03:08 編輯 我用DSP6713BIOS做多任務(wù)處理,一個(gè)硬中斷,一個(gè)軟中斷,一個(gè)任務(wù),任務(wù)優(yōu)先級(jí)最低,作為
    發(fā)表于 07-10 16:11

    多任務(wù)編程多任務(wù)處理是指什么

    嵌入式Linux應(yīng)用編程-多任務(wù)編程多任務(wù)處理是指用戶可在同一時(shí)間內(nèi)運(yùn)行多個(gè)應(yīng)用程序,每個(gè)正在執(zhí)行的應(yīng)用程序被稱為一個(gè)任務(wù)。Linux就是一
    發(fā)表于 12-22 08:30

    任務(wù)裸機(jī)系統(tǒng)與多任務(wù)系統(tǒng)的區(qū)別在哪

    總體概述與任務(wù)裸機(jī)系統(tǒng)與多任務(wù)系統(tǒng)的區(qū)別任務(wù)的定義與切換裸機(jī)系統(tǒng)與多任務(wù)系統(tǒng)的區(qū)別裸機(jī)系統(tǒng)包括輪詢系統(tǒng),前后臺(tái)系統(tǒng)輪詢系統(tǒng):在進(jìn)行初始化過后,循環(huán)執(zhí)
    發(fā)表于 02-18 07:03

    什么是多任務(wù)系統(tǒng)?FreeRTOS任務(wù)與協(xié)程簡(jiǎn)析

    功能,初學(xué)者必須先掌握——任務(wù)的創(chuàng)建、刪除、掛起和恢復(fù)等操作。本章節(jié)分為如下幾部分:*什么是多任務(wù)系統(tǒng)*FreeRTOS任務(wù)與協(xié)程*初次使用*任務(wù)狀態(tài)*
    發(fā)表于 02-18 06:38

    【晶心科技ADP-Corvette-T1開發(fā)板試用體驗(yàn)】corvette T1的多任務(wù)調(diào)度學(xué)習(xí)

    arduino多任務(wù)并行首先是聲明自己的回調(diào)函數(shù)void t1Callback();void t2Callback();void t3Callback();然后創(chuàng)建任務(wù)也就是Task的
    發(fā)表于 07-10 23:36

    多任務(wù)系統(tǒng)中的堆棧使用

    現(xiàn)場(chǎng)及保存任務(wù)上下文(CPU寄存器)。在多任務(wù)處理開始之前執(zhí)行的C代碼將使用C棧。只有當(dāng)應(yīng)用程序的第一個(gè)任務(wù)開始運(yùn)行時(shí),棧才會(huì)從C棧中轉(zhuǎn)移到
    發(fā)表于 01-05 13:52

    使用<cont.h>進(jìn)行協(xié)作式多任務(wù)處理?

    編程,但我是電子產(chǎn)品的菜鳥,所以我很高興它運(yùn)行得很好)并且想要某種協(xié)作的多任務(wù)處理框架來管理它。我真的很喜歡但我無法讓它工作。即使在玩弄 Arduino 鏈接器參數(shù)時(shí),我也沒有運(yùn)氣。我查看了其他幾個(gè)庫,但我
    發(fā)表于 02-23 06:57

    求助,有沒有人能夠?qū)崿F(xiàn)協(xié)作式多任務(wù)處理?

    當(dāng)前實(shí)現(xiàn)的“多任務(wù)Arduino-on-ESP8266 才能為二進(jìn)制文件的 RF 部分提供服務(wù);這意味著使用 delay(0) 或 yield() 或確保 loop() 函數(shù)完成時(shí)間不超過
    發(fā)表于 02-24 06:35

    51單片機(jī)多任務(wù)同時(shí)執(zhí)行

    51單片機(jī)多任務(wù)同時(shí)執(zhí)行。RTX51 Tiny是一種實(shí)時(shí)操作系統(tǒng)(RTOS),可以用它來建立多個(gè)任務(wù)函數(shù))同時(shí)執(zhí)行的應(yīng)用(從宏觀上看是同時(shí)
    發(fā)表于 08-03 16:58

    arduino平臺(tái)millis()函數(shù)有什么作用?

    有關(guān)arduino平臺(tái)millis()函數(shù),有什么作用?
    發(fā)表于 10-20 06:30

    uCOS-II多任務(wù)編程設(shè)計(jì)

    uCOS-II多任務(wù)編程設(shè)計(jì)方法,在用戶任務(wù)函數(shù)中,必須包含至少一次對(duì)操作系統(tǒng)服務(wù)函數(shù)的調(diào)用,否則比其優(yōu)先級(jí)低的任務(wù)將無法得到運(yùn)行機(jī)會(huì),這是
    發(fā)表于 10-27 15:25 ?8次下載
    uCOS-II<b class='flag-5'>多任務(wù)</b>編程設(shè)計(jì)

    如何使用51單片機(jī)進(jìn)行多任務(wù)機(jī)制及應(yīng)用

    傳統(tǒng)的單片機(jī)程序一般采用單任務(wù)機(jī)制,單任務(wù)系統(tǒng)具有簡(jiǎn)單直觀、易于控制的優(yōu)點(diǎn)。然而由于程序只能按順序依次執(zhí)行,缺乏靈活性,只能使用中斷函數(shù)實(shí)時(shí)地處理
    發(fā)表于 04-15 18:24 ?8次下載
    如何使用51單片機(jī)進(jìn)行<b class='flag-5'>多任務(wù)</b>機(jī)制及應(yīng)用

    使用millis函數(shù)作為使用延遲的替代方法

    電子發(fā)燒友網(wǎng)站提供《使用millis函數(shù)作為使用延遲的替代方法.zip》資料免費(fèi)下載
    發(fā)表于 10-18 09:50 ?0次下載
    使用<b class='flag-5'>millis</b><b class='flag-5'>函數(shù)</b>作為使用延遲的替代方法

    PicoSem:Arduino框架下的Raspberry多任務(wù)

    電子發(fā)燒友網(wǎng)站提供《PicoSem:Arduino框架下的Raspberry多任務(wù).zip》資料免費(fèi)下載
    發(fā)表于 06-13 09:21 ?1次下載
    PicoSem:<b class='flag-5'>Arduino</b>框架下的Raspberry<b class='flag-5'>多任務(wù)</b>

    Arduino上的多任務(wù)處理

    電子發(fā)燒友網(wǎng)站提供《Arduino上的多任務(wù)處理.zip》資料免費(fèi)下載
    發(fā)表于 06-14 11:05 ?0次下載
    <b class='flag-5'>Arduino</b>上的<b class='flag-5'>多任務(wù)</b><b class='flag-5'>處理</b>