Bài 6: Lập trình ESP32 Timer Millis và ngắt Timer

lap trinh esp32 timer millis va ngat timer

Chào các bạn, trong bài này mình sẽ hướng dẫn các bạn lập trình ESP32 Timer với 2 chủ đề chính là sử dụng Millis và tạo ngắt Timer. Bộ định thời hay Timer là ngoại vi rất cơ bản và rất hay được sử dụng trong lập trình vi điều khiển

Bài 6 trong Serie Lập trình ESP32 từ A tới Z

Timer là gì?

Timer hay bộ định thời là một ngoại vi của vi điều khiển có chức năng đếm xung, từ đó có thể cài đặt được thời gian nhất định. Theo công thức

Time = T(clock) x N (số lần đêm)

Với

  • T là chu kì của 1 xung cấp vào Timer
  • N là số lần đếm của Timer

Trong vi điều khiển Timer thường được sử dụng như bộ hẹn giờ, lặp lại một hành động nhất định khi tới đúng thời điểm.

Hàm Delay, Millis và ngắt Timer trong ESP32

Trong STM32 có 2 hàm phổ biến để hẹn giờ đó là delay()millis()

Hàm delay()

Hàm delay cực kì phổ biến và rất hay được sử dụng trong các chương trình nhúng. Bằng cách truyền vào 1 tham số kiểu int, tương ứng với số mili giây (ms) mà MCU sẽ chờ trong đó. Ví dụ:

loop()
{
  digitalWrite(LED, HIGH);
  delay(1000);
  digitalWrite(LED, LOW);'
  delay(1000);
}

Giải thích:

Trong hàm lặp vô hạn loop() chúng ta bật Led bằng digitalWrite(), sau đó chờ trong vòng 1s (1000 ms) thì tắt Led, rồi lại chờ 1s thì bật led. Lặp lại liên tục như vậy.

Thực tế hàm delay chính là một vòng lặp có điều kiện. Khi MCU xử lý tới hàm này, chúng sẽ đi vào vòng lặp tới khi điều kiện được thỏa mãn thì sẽ thoát ra

  • Ưu điểm: Dễ dàng thao tác
  • Nhược điểm: Chương trình sẽ không thể xử lý các tác vụ khác khi đang delay, dẫn tới chậm, mất dữ liệu ….

Hàm mills()

Hàm millis() có nhiệm vụ trả về một số unsigned long – là thời gian (tính theo mili giây) kể từ lúc mạch Arduino bắt đầu chương trình của bạn. Nó sẽ tràn số và quay số 0 (sau đó tiếp tục tăng).

Khi dùng hàm millis() chúng ta sẽ lấy giá trị đo được hiện tại trừ đi giá trị đo được tại thời điêm trước đó => khoảng chênh lệch giữa 2 thời điểm. Sau đó so sánh với 1 số là thời gian delay mong muốn. Nếu giá trị đó bằng hoặc vượt quá sẽ xử lý các tác vụ bên trong.

Hàm millis() còn thường được gọi là ngắt mềm.

  • Ưu điểm: Chương trình vẫn có thể xử lý được các tác vụ khác khi đang chờ
  • Nhược điểm: Thiếu tính chính xác, nếu vi điều khiển phải xử lý một tác vụ dài hơn thời gian delay, chúng ta sẽ không thể chạy các tác vụ đúng thời điểm, mà phải cho vi điều khiển xử lý hết rồi mới chạy tới

Ngắt Timer

Vậy để khắc phục 2 nhược điểm của cách trên chúng ta có Ngắt Timer.

  • Ưu điểm: Với ngắt Timer chúng ta có thể xử lý được các tác vụ trong chương trình chính mà vẫn có thể xử lý các tác vụ khác một cách lập tức khi tới thời gian.
  • Nhược điểm: Nó có thể bị xung đột với các loại ngắt khác nếu không biết cách xử lý tốt, gây nên treo chương trình…

Lập trình Esp32 sử dụng Timer hẹn giờ bật tắt Led

Sơ đồ nguyên lý

Chúng ta sẽ sử dụng chân 16 để điều khiển Led. Sơ đồ nguyên lý như sau

esp32 pwm
Điều khiển Led bằng Timer

Bật tắt led sử dụng Millis()

Cách xử dụng millis() như sau:

#include <Arduino.h>

const int ledPin =  16;      // chân led

// biến trạng thái Led
int ledState = LOW;

// Giá trị lần cuối cùng được cập nhật
unsigned long previousMillis = 0;   

const long interval = 1000; // giá trị delay (milliseconds)

void setup() {
  // set led là ouput
  pinMode(ledPin, OUTPUT);
}

void loop() {

  // khởi tạo một biến lưu giá trị hiện tại của Timer
  unsigned long currentMillis = millis();

  //lấy giá trị hiện tại trừ đi giá trị trước đó và so sánh với khoản thời gian delay
  if (currentMillis - previousMillis >= interval) {
    // nếu giá trị đó bằng hoặc vượt qua
    // lưu giá trị hiện tại thành giá trị lần cuối cập nhật
    previousMillis = currentMillis;

    // đảo giá trị led States
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }

    // ghi giá trị đó lên Led
    digitalWrite(ledPin, ledState);
  }
  //nếu giá trị chưa vượt qua thì không làm gì cả
}

 

Với kiểu làm này, nếu trong loop() mình để một lệnh delay(5000) nghĩa là chờ 5s thì bài toán nháy Led mỗi 1s sẽ bị sai, do vi điều khiển không thể quay lại kiểm tra millis() được

timer esp32

Vậy nên hãy lưu ý khi dùng hàm millis() nhé!

Bật tắt Led sử dụng Ngắt Timer

Cách xử lý ngắt Timer trong ESP32 như sau:

#include <Arduino.h>

const int ledPin =  16;      // chân led
// biến trạng thái Led
int ledState = LOW;

hw_timer_t* timer = NULL; //khơi tạo timer
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; 

// hàm xử lý ngắt
void IRAM_ATTR onTimer() {   
  portENTER_CRITICAL_ISR(&timerMux); //vào chế độ tránh xung đột
  digitalWrite(ledPin, ~ledState); // đảo giá trị Led
  portEXIT_CRITICAL_ISR(&timerMux); // thoát 
}
 
void setup() {
  Serial.begin(115200);
  //khởi tạo chân led là output
  pinMode(ledPin,OUTPUT);

  //khơit tạo timer với chu kì 1us vì thạch anh của ESP chạy 8MHz
  timer = timerBegin(0, 80, true);
  //khởi tạo hàm xử lý ngắt ngắt cho Timer
  timerAttachInterrupt(timer, &onTimer, true);
  //khởi tạo thời gian ngắt cho timer là 1s (1000000 us)
  timerAlarmWrite(timer, 1000000, true);
  //bắt đầu chạy timer
  timerAlarmEnable(timer);
}
 
void loop() {
 //do nothing
}

Với trương trình này, chúng ta có thể xử lý bất kì tác vụ nào trong Loop() mà không ảnh hưởng tới việc nhấp nháy của Led.

Kết luận

Học cách sử dụng ESP32 Timer rất có ích khi xử lý nhiều tác vụ khác nhau, tuy rằng ESP32 chỉ có 1 Timer, vì vậy khả năng xử lý không được tốt cho lắm. Thế nhưng bù lại ESP32 có thể sử dụng RTOS ( hệ điều hành thời gian thực mình sẽ có bài học về sau), điều này làm giảm đi đáng kể công việc của Timer.

Nếu thấy bài viết này có ích, hãy chia sẻ cho bạn bè. Và cùng gia nhập những người nghiện lập trình tại đây nhé: Hội anh em nghiện lập trình

 

 

 

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *