- Đa nhiệm là gì?
- Tại sao phải bỏ qua delay () trong Arduino?
- Tại sao phải sử dụng millis ()?
- Thành phần bắt buộc
- Sơ đồ mạch
- Lập trình Arduino UNO cho đa nhiệm
Đa nhiệm đã đưa máy tính đến một cuộc cách mạng nơi một hoặc nhiều chương trình có thể chạy đồng thời, làm tăng hiệu quả, tính linh hoạt, khả năng thích ứng và năng suất. Trong các hệ thống nhúng, bộ vi điều khiển cũng có thể xử lý Đa nhiệm và thực hiện đồng thời hai hoặc nhiều tác vụ mà không cần dừng các lệnh hiện tại.
Ở đây trong hướng dẫn này, chúng ta sẽ tìm hiểu Cách Arduino thực hiện Đa nhiệm với hàm milis Arduino. Nói chung, hàm delay () được sử dụng trong Arduino cho một tác vụ định kỳ như Đèn LED nhấp nháy nhưng hàm delay () này sẽ tạm dừng chương trình trong một thời gian nhất định và không cho phép các hoạt động khác thực hiện. Vì vậy, bài viết này giải thích cách chúng ta có thể tránh sử dụng hàm delay () và thay thế nó bằng hàm millis () để thực hiện nhiều tác vụ đồng thời và làm cho Arduino trở thành bộ điều khiển Đa nhiệm. Trước khi đi vào chi tiết, hãy bắt đầu với Đa nhiệm cơ bản.
Đa nhiệm là gì?
Đa nhiệm đơn giản có nghĩa là thực hiện đồng thời nhiều tác vụ hoặc chương trình cùng một lúc. Hầu hết tất cả các hệ điều hành đều có tính năng đa nhiệm. Loại hệ điều hành này được gọi là MOS (hệ điều hành đa nhiệm). MOS có thể là Hệ điều hành máy tính di động hoặc máy tính để bàn. Ví dụ điển hình về đa nhiệm trong máy tính là khi người dùng chạy ứng dụng email, trình duyệt internet, trình phát đa phương tiện, trò chơi, cùng một lúc và nếu người dùng không muốn sử dụng ứng dụng, nó sẽ chạy ở chế độ nền nếu không đóng. Người dùng cuối sử dụng tất cả các ứng dụng này cùng một lúc nhưng hệ điều hành có khái niệm này hơi khác một chút. Hãy thảo luận về cách OS quản lý đa nhiệm.
Như trong hình, CPU chia thời gian thành ba phần bằng nhau và gán từng phần cho từng tác vụ / ứng dụng. Đây là cách đa nhiệm được thực hiện trong hầu hết các hệ thống. Khái niệm này sẽ gần như giống nhau đối với Đa nhiệm Arduino, ngoại trừ việc phân bố thời gian sẽ khác một chút. Vì Arduino chạy với tần suất thấp và RAM so với Laptop / Mobile / PC nên thời gian cho mỗi tác vụ cũng sẽ khác nhau. Arduino cũng có một hàm delay () được sử dụng rộng rãi. Nhưng trước khi bắt đầu, hãy thảo luận về lý do tại sao chúng ta không nên sử dụng hàm delay () trong bất kỳ dự án nào.
Tại sao phải bỏ qua delay () trong Arduino?
Nếu tài liệu tham khảo về Arduino được xem xét thì có hai loại hàm trì hoãn, hàm thứ nhất là delay () và thứ hai là delayMicroseconds (). Cả hai chức năng đều giống nhau về độ trễ tạo ra. Sự khác biệt duy nhất là, trong hàm delay (), số nguyên tham số được truyền là mili giây tức là nếu chúng ta viết delay (1000) thì độ trễ sẽ là 1000 mili giây tức là 1 giây. Tương tự trong hàm delayMicroseconds (), tham số được truyền là tính bằng micro giây, tức là nếu chúng ta viết delayMicroseconds (1000), thì độ trễ sẽ là 1000 micro giây tức là 1 mili giây.
Ở đây có vấn đề, cả hai chức năng tạm dừng chương trình trong khoảng thời gian trôi qua trong chức năng trì hoãn. Vì vậy, nếu chúng ta đưa ra độ trễ là 1 giây thì bộ xử lý không thể chuyển sang lệnh tiếp theo cho đến khi hết 1 giây. Tương tự nếu độ trễ là 10 giây thì chương trình sẽ dừng trong 10 giây và bộ xử lý sẽ không cho phép thực hiện các lệnh tiếp theo cho đến khi hết 10 giây. Điều này cản trở hiệu suất của vi điều khiển về tốc độ và thực thi các lệnh.
Ví dụ tốt nhất để giải thích nhược điểm của chức năng trì hoãn là sử dụng hai nút nhấn. Hãy xem xét chúng tôi muốn chuyển đổi hai đèn LED bằng cách sử dụng hai nút nhấn. Vì vậy, nếu một nút nhấn được nhấn thì đèn LED tương ứng sẽ phát sáng trong 2 giây, tương tự nếu nhấn nút thứ hai thì đèn LED sẽ phát sáng trong 4 giây. Nhưng khi chúng ta sử dụng delay (), nếu người dùng nhấn nút đầu tiên thì chương trình sẽ dừng trong 2 giây và nếu người dùng nhấn nút thứ hai trước khi trễ 2 giây, thì bộ vi điều khiển sẽ không chấp nhận đầu vào như chương trình. trong giai đoạn tạm dừng.
Tài liệu chính thức của Arduino đề cập rõ ràng điều này trong phần mô tả hàm Notes và Warnings of delay (). Bạn có thể xem qua và kiểm tra điều này để làm rõ hơn.
Tại sao phải sử dụng millis ()?
Để khắc phục sự cố do sử dụng độ trễ, nhà phát triển nên sử dụng hàm millis () dễ sử dụng khi bạn đã thành thói quen và nó sẽ sử dụng 100% hiệu suất CPU mà không tạo ra bất kỳ độ trễ nào trong việc thực hiện các hướng dẫn. millis () là một hàm chỉ trả về lượng mili giây đã trôi qua kể từ khi bảng Arduino bắt đầu chạy chương trình hiện tại mà không đóng băng chương trình. Số thời gian này sẽ tràn (tức là trở về 0), sau khoảng 50 ngày.
Giống như Arduino có delayMicroseconds (), nó cũng có phiên bản vi mô của mili () là micros (). Sự khác biệt giữa micro và millis là, micros () sẽ tràn sau khoảng 70 phút, so với millis () là 50 ngày. Vì vậy, tùy thuộc vào ứng dụng, bạn có thể sử dụng millis () hoặc micros ().
Sử dụng millis () thay vì delay ():
Để sử dụng millis () cho thời gian và độ trễ, bạn cần ghi lại và lưu trữ thời gian mà hành động đã diễn ra để bắt đầu thời gian và sau đó kiểm tra xem thời gian xác định đã trôi qua hay chưa. Vì vậy, như đã nêu, hãy lưu trữ thời gian hiện tại trong một biến.
unsigned long currentMillis = millis ();
Chúng ta cần thêm hai biến nữa để tìm xem thời gian cần thiết đã trôi qua chưa. Chúng tôi đã lưu trữ thời gian hiện tại trong biến currentMillis nhưng chúng tôi cũng cần biết rằng khoảng thời gian bắt đầu từ khi nào và khoảng thời gian là bao lâu. Vì vậy, Interval và trước đóMillis được khai báo. Khoảng thời gian sẽ cho chúng tôi biết thời gian trễ và previosMillis sẽ lưu trữ lần cuối cùng sự kiện xảy ra.
unsigned long trước đóMillis; kỳ dài không dấu = 1000;
Để hiểu điều này, chúng ta hãy lấy một ví dụ về một đèn LED nhấp nháy đơn giản. Khoảng thời gian = 1000 sẽ cho chúng ta biết rằng đèn LED sẽ nhấp nháy trong 1 giây hoặc 1000ms.
const int ledPin = 4; // số chân LED được kết nối int ledState = LOW; // dùng để đặt trạng thái LED unsigned long beforeMillis = 0; // sẽ lưu trữ lần cuối cùng LED nhấp nháy const long period = 1000; // khoảng thời gian nhấp nháy trong ms void setup () { pinMode (ledPin, OUTPUT); // đặt ledpin làm đầu ra } void loop () { unsigned long currentMillis = millis (); // lưu trữ thời gian hiện tại if (currentMillis - beforeMillis> = period) {// kiểm tra xem 1000ms đã qua chưa trước đóMillis = currentMillis; // lưu lần cuối cùng bạn nhấp nháy đèn LED if (ledState == LOW) {// nếu đèn LED tắt, hãy bật nó và ngược lại ledState = HIGH; } else { ledState = LOW; } digitalWrite (ledPin, ledState); // đặt đèn LED với ledState nhấp nháy lại } }
Đây, tuyên bố
Ngắt trong Arduino hoạt động giống như trong các vi điều khiển khác. Bảng Arduino UNO có hai chân riêng biệt để gắn ngắt trên chân GPIO 2 và 3. Chúng tôi đã trình bày chi tiết trong Hướng dẫn về ngắt Arduino, nơi bạn có thể tìm hiểu thêm về Ngắt và cách sử dụng chúng.
Ở đây chúng tôi sẽ hiển thị Đa nhiệm Arduino bằng cách xử lý hai tác vụ cùng một lúc. Các tác vụ sẽ bao gồm nhấp nháy của hai đèn LED trong thời gian trễ khác nhau cùng với một nút nhấn sẽ được sử dụng để điều khiển trạng thái BẬT / TẮT của đèn LED. Vì vậy, ba nhiệm vụ sẽ được thực hiện đồng thời.
Thành phần bắt buộc
- Arduino UNO
- Ba đèn LED (Bất kỳ màu nào)
- Kháng cự (470, 10k)
- Người nhảy
- Breadboard
Sơ đồ mạch
Sơ đồ mạch để chứng minh việc sử dụng Arduino Millis () fuction rất dễ dàng và không có nhiều linh kiện để gắn như hình dưới đây.
Lập trình Arduino UNO cho đa nhiệm
Lập trình Arduino UNO cho đa nhiệm sẽ chỉ yêu cầu logic đằng sau cách hoạt động của millis () đã được giải thích ở trên. Bạn nên thực hành sử dụng lại đèn LED nhấp nháy bằng mili để làm rõ ràng logic và làm cho bạn thoải mái với mili () trước khi bắt đầu lập trình Arduino UNO cho đa nhiệm. Trong hướng dẫn này, ngắt cũng được sử dụng đồng thời với millis () cho đa nhiệm. Nút sẽ là một ngắt. Vì vậy, bất cứ khi nào một ngắt được tạo ra tức là nhấn nút nhấn, đèn LED sẽ chuyển sang trạng thái ON hoặc OFF.Chương trình bắt đầu với việc khai báo số pin nơi các đèn LED và Nút nhấn được kết nối.
int led1 = 6; int led2 = 7; int toggleLed = 5; int pushButton = 2;
Tiếp theo chúng ta viết một biến để lưu trữ trạng thái của đèn LED để sử dụng trong tương lai.
int ledState1 = LOW; int ledState2 = LOW;
Cũng như đã giải thích ở trên trong ví dụ nhấp nháy, các biến cho chu kỳ và chạy trước được khai báo để so sánh và tạo ra độ trễ cho đèn LED. Đèn LED đầu tiên nhấp nháy sau mỗi 1 giây và đèn LED khác nhấp nháy sau 200 mili giây.
unsigned long beforeMillis1 = 0; const dài kỳ1 = 1000; unsigned long beforeMillis2 = 0; const dài kỳ2 = 200;
Một chức năng mili khác sẽ được sử dụng để tạo ra độ trễ gỡ lỗi để tránh việc nhấn nhiều nút nhấn. Sẽ có cách làm tương tự như trên.
int debouncePeriod = 20; int debounceMillis = 0;
Các ba biến sẽ được sử dụng để lưu trữ các trạng thái của nút nhấn như ngắt, bật tắt đèn LED và nút nhấn nhà nước.
bool buttonPushing = false; int ledChange = LOW; int lastState = CAO;
Xác định hoạt động của chân mà chân đó sẽ hoạt động như INPUT hoặc OUTPUT.
pinMode (led1, OUTPUT); pinMode (led2, OUTPUT); pinMode (toggleLed, OUTPUT); pinMode (pushButton, INPUT);
Bây giờ xác định chân ngắt bằng cách gắn ngắt với định nghĩa của ISR và Chế độ ngắt. Lưu ý rằng nên sử dụng digitalPinToInterrupt (pin_number) khi khai báo hàm AttachInterrupt () để dịch chân số thực sang số ngắt cụ thể.
attachmentInterrupt (digitalPinToInterrupt (pushButton), pushButton_ISR, CHANGE);
Chương trình con ngắt được viết và nó sẽ chỉ thay đổi cờ buttonPushing. Lưu ý rằng, chương trình con ngắt phải càng ngắn càng tốt, vì vậy hãy cố gắng viết nó và giảm thiểu các hướng dẫn thừa.
void pushButton_ISR () { buttonPushing = true; }
Vòng lặp bắt đầu bằng việc lưu trữ giá trị mili trong biến currentMillis sẽ lưu giá trị thời gian đã trôi qua mỗi khi vòng lặp lặp lại.
unsigned long currentMillis = millis ();
Có tổng cộng ba chức năng trong đa nhiệm, nhấp nháy một đèn LED ở 1 giây, Đèn LED thứ hai nhấp nháy ở 200ms và Nếu nhấn nút nhấn thì chuyển TẮT / BẬT LED. Vì vậy, chúng tôi sẽ viết ba phần để thực hiện nhiệm vụ này.
Đầu tiên là chuyển đổi trạng thái LED sau mỗi 1 giây bằng cách so sánh mili đã trôi qua.
if (currentMillis - beforeMillis1> = period1) {beforeMillis1 = currentMillis; if (ledState1 == LOW) { ledState1 = HIGH; } else { ledState1 = LOW; } digitalWrite (led1, ledState1); }
Tương tự như vậy, thứ hai nó bật tắt đèn LED sau mỗi 200ms bằng cách so sánh mili đã trôi qua. Lời giải thích đã được giải thích trước đó trong bài viết này.
if (currentMillis - beforeMillis2> = period2) {beforeMillis2 = currentMillis; if (ledState2 == LOW) { ledState2 = HIGH; } else { ledState2 = LOW; } digitalWrite (led2, ledState2); }
Cuối cùng, cờ buttonPushing được theo dõi và sau khi tạo ra độ trễ gỡ lỗi là 20ms, nó chỉ chuyển đổi trạng thái của đèn LED tương ứng với nút nhấn được gắn làm ngắt.
if (buttonPushing = true) // kiểm tra xem ISR có được gọi không { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushing) // tạo độ trễ gỡ lỗi 20ms để tránh nhấn nhiều lần { debounceMillis = currentMillis; // lưu thời gian trễ lần gỡ lỗi cuối cùng if (digitalRead (pushButton) == LOW && lastState == HIGH) // thay đổi đèn led sau khi nhấn nút nhấn { ledChange =! ledChange; digitalWrite (toggleLed, ledChange); lastState = LOW; } else if (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushing = false; } }
Điều này kết thúc Hướng dẫn Arduino millis (). Lưu ý rằng để có thói quen với millis (), chỉ cần thực hành triển khai logic này trong một số ứng dụng khác. Bạn cũng có thể mở rộng nó để sử dụng động cơ, động cơ servo, cảm biến và các thiết bị ngoại vi khác. Trong trường hợp có bất kỳ nghi ngờ nào thì vui lòng viết thư cho diễn đàn của chúng tôi hoặc bình luận bên dưới.
Toàn bộ mã và Video để chứng minh việc sử dụng hàm mili trong Arduino được cung cấp bên dưới.