最近在學習嵌入式開發的內容,正好有一個開發任務涉及到對于定時器中斷的使用,今天正好找到了這個相關的庫:ESP32TimerInterrupt
ESP32TimerInterrupt庫的下載鏈接(適用于Arduino IDE)
進入到這個地址后直接下載該庫的壓縮包,不用解壓,直接通過arduino IDE添加即可!
然后關于該庫的使用,GitHub中也給出了example來告訴大家如何使用。
下面開始例程的分析
- 使用的開發IDE:arduino IDE
- 使用的板子:ESP32S3
- 例程:TimerInterruptTest.ino
首先要使用這個定時器中斷,要引入定時器中斷的庫,即包含這個庫的頭文件:
#include "ESP32TimerInterrupt.h"
然后定義中斷調用時,輸出電平的管腳:
#define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32
#define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32
然后編寫中斷處理函數:
bool IRAM_ATTR TimerHandler0(void * timerNo)
{static bool toggle0 = false; // 定義狀態值為false//timer interrupt toggles pin PIN_D19digitalWrite(PIN_D19, toggle0); // 調用該中斷時,在ESP的GPIO9輸出toggle0的狀態值toggle0 = !toggle0; // 將狀態反轉return true;
}
關于該函數的解釋及參數說明
bool IRAM_ATTR TimerHandler0(void * timerNo) 是一個中斷服務例程(ISR)的聲明,它用于ESP32的硬件定時器中斷處理。下面是對該函數聲明的詳細解釋:
- bool:函數的返回類型為布爾值,這意味著函數將返回 true 或 false。在中斷服務例程中返回 true 通常表示中斷被成功處理,而返回 false 可能表示中斷未被處理或處理中出現錯誤。
- IRAM_ATTR:這是一個宏定義,用于指定該函數需要放置在指令RAM(Instruction RAM,IRAM)中。在ESP32中,將中斷服務例程放在IRAM中可以確保它們被快速執行,因為IRAM的訪問速度比普通RAM快。這是在處理高速中斷時推薦的做法。
- TimerHandler0:這是ISR函數的名稱,它遵循了一定的命名約定,表明這是針對特定定時器(在這個例子中是 Timer 0)的中斷處理函數。
- (void * timerNo):這是傳遞給ISR的參數,timerNo 是一個指向 void 類型的指針,通常用于傳遞與定時器相關的信息或上下文。參數的具體使用取決于中斷服務例程的設計和需求。
第二個中斷處理函數
bool IRAM_ATTR TimerHandler1(void * timerNo)
{static bool toggle1 = false;//timer interrupt toggles outputPindigitalWrite(PIN_D3, toggle1);toggle1 = !toggle1;return true;
}
注:解釋與參數說明同上,唯一的區別是,這里的中斷用的Timer 1,上面的中斷處理函數用的Timer 0。
接下來定義中斷的持續時間(毫秒)和間隔時間(微秒)
#define TIMER0_INTERVAL_MS 1000 // 間隔時間單位是微秒
#define TIMER0_DURATION_MS 5000 // 持續時間單位是毫秒#define TIMER1_INTERVAL_MS 3000
#define TIMER1_DURATION_MS 15000
對兩個定時器進行初始化
// Init ESP32 timer 0 and 1
ESP32Timer ITimer0(0);
ESP32Timer ITimer1(1);
編寫配置函數
void setup()
{pinMode(PIN_D19, OUTPUT);pinMode(PIN_D3, OUTPUT);// 開啟串口Serial.begin(115200);// 如果串口連接建立或者板子運行超過5秒則退出循環while (!Serial && millis() < 5000);// 延遲500毫秒delay(500);// 利用串口打印信息Serial.print(F("\nStarting TimerInterruptTest on "));Serial.println(ARDUINO_BOARD);Serial.println(ESP32_TIMER_INTERRUPT_VERSION);Serial.print(F("CPU Frequency = "));Serial.print(F_CPU / 1000000);Serial.println(F(" MHz"));// Using ESP32 => 80 / 160 / 240MHz CPU clock ,// For 64-bit timer counter// For 16-bit timer prescaler up to 1024// Interval in microsecs// 下面定時器間隔設置為1000000微秒,即1秒if (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)){// 如果定時器中斷設置成功,則串口打印定時器設置成功的信息,并打印板子運行時間Serial.print(F("Starting ITimer0 OK, millis() = "));Serial.println(millis());}else// 否則打印定時器無法設置Serial.println(F("Can't set ITimer0. Select another freq. or timer"));// Interval in microsecsif (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)){Serial.print(F("Starting ITimer1 OK, millis() = "));Serial.println(millis());}elseSerial.println(F("Can't set ITimer1. Select another freq. or timer"));Serial.flush();
}
編寫循環函數
void loop()
{static unsigned long lastTimer0 = 0;static unsigned long lastTimer1 = 0;static bool timer0Stopped = false;static bool timer1Stopped = false;if (millis() - lastTimer0 > TIMER0_DURATION_MS){// 如果板子距離上一次中斷觸發的時間大于定時器持續時間,則更新lastTimer0的值lastTimer0 = millis(); // 檢查定時器是否開啟,如果開啟就關掉,如果關掉就開啟if (timer0Stopped){Serial.print(F("Start ITimer0, millis() = "));Serial.println(millis());ITimer0.restartTimer(); // 重啟定時器Timer 0}else{Serial.print(F("Stop ITimer0, millis() = "));Serial.println(millis());ITimer0.stopTimer(); // 關閉定時器Timer 0}// 進行狀態轉換timer0Stopped = !timer0Stopped;}// 如果板子距離上一次中斷觸發的時間大于定時器持續時間,則更新lastTimer1的值if (millis() - lastTimer1 > TIMER1_DURATION_MS){lastTimer1 = millis();// 檢查定時器是否開啟,如果開啟就關掉,如果關掉就開啟if (timer1Stopped){Serial.print(F("Start ITimer1, millis() = "));Serial.println(millis());ITimer1.restartTimer(); // 重啟定時器Timer 1}else{Serial.print(F("Stop ITimer1, millis() = "));Serial.println(millis());ITimer1.stopTimer(); // 關閉定時器Timer 1}timer1Stopped = !timer1Stopped;}
}
完整代碼
#include "ESP32TimerInterrupt.h"// Don't use PIN_D1 in core v2.0.0 and v2.0.1. Check https://github.com/espressif/arduino-esp32/issues/5868
// Don't use PIN_D2 with ESP32_C3 (crash)
#define PIN_D19 19 // Pin D19 mapped to pin GPIO9 of ESP32
#define PIN_D3 3 // Pin D3 mapped to pin GPIO3/RX0 of ESP32// With core v2.0.0+, you can't use Serial.print/println in ISR or crash.
// and you can't use float calculation inside ISR
// Only OK in core v1.0.6-
bool IRAM_ATTR TimerHandler0(void * timerNo)
{static bool toggle0 = false;//timer interrupt toggles pin PIN_D19digitalWrite(PIN_D19, toggle0);toggle0 = !toggle0;return true;
}bool IRAM_ATTR TimerHandler1(void * timerNo)
{static bool toggle1 = false;//timer interrupt toggles outputPindigitalWrite(PIN_D3, toggle1);toggle1 = !toggle1;return true;
}#define TIMER0_INTERVAL_MS 1000
#define TIMER0_DURATION_MS 5000#define TIMER1_INTERVAL_MS 3000
#define TIMER1_DURATION_MS 15000// Init ESP32 timer 0 and 1
ESP32Timer ITimer0(0);
ESP32Timer ITimer1(1);void setup()
{pinMode(PIN_D19, OUTPUT);pinMode(PIN_D3, OUTPUT);Serial.begin(115200);while (!Serial && millis() < 5000);delay(500);Serial.print(F("\nStarting TimerInterruptTest on "));Serial.println(ARDUINO_BOARD);Serial.println(ESP32_TIMER_INTERRUPT_VERSION);Serial.print(F("CPU Frequency = "));Serial.print(F_CPU / 1000000);Serial.println(F(" MHz"));// Using ESP32 => 80 / 160 / 240MHz CPU clock ,// For 64-bit timer counter// For 16-bit timer prescaler up to 1024// Interval in microsecsif (ITimer0.attachInterruptInterval(TIMER0_INTERVAL_MS * 1000, TimerHandler0)){Serial.print(F("Starting ITimer0 OK, millis() = "));Serial.println(millis());}elseSerial.println(F("Can't set ITimer0. Select another freq. or timer"));// Interval in microsecsif (ITimer1.attachInterruptInterval(TIMER1_INTERVAL_MS * 1000, TimerHandler1)){Serial.print(F("Starting ITimer1 OK, millis() = "));Serial.println(millis());}elseSerial.println(F("Can't set ITimer1. Select another freq. or timer"));Serial.flush();
}void loop()
{static unsigned long lastTimer0 = 0;static unsigned long lastTimer1 = 0;static bool timer0Stopped = false;static bool timer1Stopped = false;if (millis() - lastTimer0 > TIMER0_DURATION_MS){lastTimer0 = millis();if (timer0Stopped){Serial.print(F("Start ITimer0, millis() = "));Serial.println(millis());ITimer0.restartTimer();}else{Serial.print(F("Stop ITimer0, millis() = "));Serial.println(millis());ITimer0.stopTimer();}timer0Stopped = !timer0Stopped;}if (millis() - lastTimer1 > TIMER1_DURATION_MS){lastTimer1 = millis();if (timer1Stopped){Serial.print(F("Start ITimer1, millis() = "));Serial.println(millis());ITimer1.restartTimer();}else{Serial.print(F("Stop ITimer1, millis() = "));Serial.println(millis());ITimer1.stopTimer();}timer1Stopped = !timer1Stopped;}
}