Sử dụng ngắt trên Arduino. Cách thực hiện các tác vụ song song (Chủ đề) trong chương trình Arduino

Nói chung, Arduino không hỗ trợ song song hóa nhiệm vụ thực sự hoặc đa luồng. Nhưng có thể với mỗi lần lặp lại chu kỳ vòng() hướng dẫn bộ vi điều khiển kiểm tra xem đã đến lúc thực hiện một số thao tác bổ sung, nhiệm vụ nền. Trong trường hợp này, người dùng sẽ thấy rằng một số tác vụ đang được thực hiện đồng thời.

Ví dụ: hãy nháy đèn LED ở một tần số nhất định, đồng thời tạo ra âm thanh tăng giảm giống như tiếng còi báo động từ bộ phát áp điện. Chúng tôi đã kết nối cả đèn LED và bộ phát Piezo với Arduino nhiều lần. Hãy lắp ráp mạch như trong hình.

Nếu bạn kết nối đèn LED với chân kỹ thuật số không phải là "13", đừng quên điện trở giới hạn dòng điện khoảng 220 ohms.

2 Điều khiển bộ phát LED và áp điện sử dụng toán tử delay()

Hãy viết một bản phác thảo như thế này và tải nó lên Arduino.

Const int soundPin = 3; /* khai báo một biến với số PIN, mà phần tử áp điện được kết nối */ const int ledPin = 13; // khai báo một biến có số chân LED thiết lập void() ( pinMode(soundPin, OUTPUT); // khai báo chân 3 là đầu ra. pinMode(ledPin, OUTPUT); // khai báo chân 13 là đầu ra. } vòng lặp trống() (// Điều khiển âm thanh: tone(soundPin, 700); // tạo ra âm thanh có tần số trễ 700 Hz(200); giai điệu(soundPin, 500); // ở tần số trễ 500 Hz(200); giai điệu(soundPin, 300); // ở tần số trễ 300 Hz(200); giai điệu(soundPin, 200); // ở tần số trễ 200 Hz(200); // Điều khiển LED: digitalWrite(ledPin, HIGH); // độ trễ ánh sáng(200); digitalWrite(ledPin, THẤP); // tắt độ trễ(200); }

Sau khi bật nó lên, rõ ràng là bản phác thảo không được thực hiện chính xác như chúng ta cần: cho đến khi còi báo động hoạt động hoàn toàn, đèn LED sẽ không nhấp nháy, nhưng chúng ta muốn đèn LED nhấp nháy trong lúcâm thanh của còi báo động. Vấn đề ở đây là gì?

Thực tế là vấn đề này không thể giải quyết theo cách thông thường. Các tác vụ được thực hiện bởi bộ vi điều khiển một cách tuần tự. Nhà điều hành trì hoãn() trì hoãn việc thực hiện chương trình trong một khoảng thời gian xác định và cho đến khi thời gian này hết hạn, lệnh sau các chương trình sẽ không được thực thi. Vì điều này, chúng tôi không thể đặt thời lượng thực hiện khác nhau cho từng tác vụ trong vòng lặp vòng() các chương trình. Vì vậy, bạn cần phải mô phỏng đa nhiệm bằng cách nào đó.

3 Quy trình song song không có toán tử "delay()"

Một tùy chọn trong đó Arduino sẽ thực hiện các nhiệm vụ giả song song đã được các nhà phát triển Arduino đề xuất. Bản chất của phương pháp này là với mỗi lần lặp lại chu trình vòng() chúng tôi kiểm tra xem đã đến lúc nhấp nháy đèn LED (thực hiện tác vụ nền) hay chưa. Và nếu nó đã đến thì chúng ta đảo ngược trạng thái của đèn LED. Đây là một loại tùy chọn bỏ qua cho người vận hành trì hoãn().

Const int soundPin = 3; // biến có số chân của phần tử áp điện const int ledPin = 13; // biến có số chân LED const long ledInterval = 200; // Khoảng thời gian nhấp nháy của đèn LED, ms. int ledState = THẤP; // trạng thái ban đầu của LED unsigned long previousMillis = 0; // lưu trữ thời gian kích hoạt đèn LED trước đó thiết lập void() ( pinMode(soundPin, OUTPUT); // đặt chân 3 làm đầu ra. pinMode(ledPin, OUTPUT); // đặt chân 13 làm đầu ra. } vòng lặp trống() (// Điều khiển âm thanh: tone(soundPin, 700); độ trễ (200); giai điệu(soundPin, 500); độ trễ (200); giai điệu(soundPin, 300); độ trễ (200); giai điệu(soundPin, 200); độ trễ (200); // Đèn LED nhấp nháy: // thời gian kể từ khi Arduino được bật, ms: unsigned long currentMillis = millis(); // Nếu đã đến lúc chớp mắt, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // thì hãy nhớ thời điểm hiện tại if (ledState == LOW) ( // và đảo ngược trạng thái của đèn LED ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // chuyển trạng thái của đèn LED) }

Nhược điểm đáng kể phương pháp này là phần mã trước khối điều khiển LED phải được thực thi nhanh hơn khoảng thời gian nhấp nháy của đèn LED "ledInterval". Nếu không, việc chớp mắt sẽ xảy ra ít thường xuyên hơn mức cần thiết và chúng ta sẽ không nhận được hiệu quả khi thực hiện các nhiệm vụ song song. Cụ thể, trong bản phác thảo của chúng tôi, thời lượng thay đổi âm thanh còi báo động là 200+200+200+200 = 800 ms và chúng tôi đặt khoảng thời gian nhấp nháy của đèn LED là 200 ms. Nhưng đèn LED sẽ nhấp nháy với khoảng thời gian 800 ms, gấp 4 lần Hơn nữa những gì chúng tôi đã hỏi.

Nói chung, nếu mã sử dụng toán tử trì hoãn(), trong trường hợp này rất khó để mô phỏng sự song song giả, vì vậy nên tránh nó.

TRONG trong trường hợp này Bộ điều khiển âm thanh còi báo động cũng cần kiểm tra xem thời gian đã đến hay chưa và không sử dụng. trì hoãn(). Nhưng điều này sẽ làm tăng số lượng mã và làm cho chương trình khó đọc hơn.

4 Sử dụng Thư viện ArduinoThreadđể tạo các chủ đề song song

Để giải quyết vấn đề, chúng tôi sẽ sử dụng một thư viện tuyệt vời ArduinoChủ đề, cho phép bạn dễ dàng tạo các quy trình giả song song. Nó hoạt động theo cách tương tự, nhưng cho phép bạn tránh viết mã để kiểm tra thời gian - liệu bạn có cần thực hiện một tác vụ trong vòng lặp này hay không. Điều này làm giảm số lượng mã và cải thiện khả năng đọc của bản phác thảo. Hãy kiểm tra thư viện đang hoạt động.


Trước hết, hãy tải xuống kho lưu trữ thư viện từ trang web chính thức và giải nén nó vào một thư mục thư viện/ môi trường phát triển Arduino IDE. Sau đó đổi tên thư mục ArduinoThread-master V. ArduinoChủ đề.

Sơ đồ kết nối sẽ giữ nguyên. Chỉ có mã chương trình sẽ thay đổi.

#bao gồm // kết nối thư viện ArduinoThread const int soundPin = 3; // biến có số chân của phần tử áp điện const int ledPin = 13; // biến có số chân LED Thread ledThread = Thread(); // tạo một thread điều khiển LED Thread soundThread = Thread(); // tạo một thread điều khiển còi báo động thiết lập void() ( pinMode(soundPin, OUTPUT); // khai báo chân 3 là đầu ra. pinMode(ledPin, OUTPUT); // khai báo chân 13 là đầu ra. ledThread.onRun(ledBlink); // gán tác vụ cho thread ledThread.setInterval(1000); // đặt khoảng thời gian phản hồi, ms soundThread.onRun(sound); // gán một tác vụ cho thread soundThread.setInterval(20); // đặt khoảng thời gian phản hồi, ms } vòng lặp trống() (// Kiểm tra xem đã đến lúc đèn LED chuyển đổi chưa: if (ledThread. ShouldRun()) ledThread.run(); // bắt đầu chuỗi // Kiểm tra xem đã đến lúc thay đổi âm báo còi chưa: if (soundThread. ShouldRun()) soundThread.run(); // bắt đầu chủ đề } // Luồng LED: void ledBlink() ( bool tĩnh ledStatus = false; // Trạng thái đèn LED Bật/Tắt ledStatus = !ledStatus; // đảo ngược trạng thái digitalWrite(ledPin, ledStatus); // bật/tắt đèn LED } // Luồng còi báo động: âm thanh trống() (âm int tĩnh = 100; // cao độ âm thanh, âm Hz(soundPin, ton); // bật còi báo động ở mức "ton" Hz if (ton )

Trong chương trình chúng tôi tạo hai chủ đề - chủ đề dẫnchủ đề âm thanh, mỗi cái thực hiện thao tác riêng: một cái nhấp nháy đèn LED, cái thứ hai điều khiển âm thanh của còi báo động. Trong mỗi lần lặp của vòng lặp, đối với mỗi luồng, chúng ta kiểm tra xem đã đến lúc thực hiện nó hay chưa. Nếu nó đến, nó sẽ được khởi chạy để thực thi bằng phương thức chạy(). Điều chính là không sử dụng toán tử trì hoãn(). Mã cung cấp giải thích chi tiết hơn.


Hãy tải mã vào bộ nhớ Arduino và chạy nó. Bây giờ mọi thứ hoạt động chính xác như bình thường!

Độ trễ trong Arduino đóng một vai trò rất lớn. Nếu không có chúng, ngay cả ví dụ đơn giản nhất về Blink, nhấp nháy đèn LED sau một khoảng thời gian xác định, cũng không thể hoạt động. Nhưng hầu hết những người mới lập trình đều biết rất ít về độ trễ thời gian và chỉ sử dụng độ trễ Arduino mà không biết tác dụng phụ của lệnh này. Trong bài viết này tôi sẽ nói chi tiết về các chức năng tạm thời và các tính năng sử dụng chúng trong môi trường phát triển Arduino IDE.

Arduino có một số nhiều đội khác nhau, chịu trách nhiệm làm việc với thời gian và tạm dừng:

  • trì hoãn()
  • độ trễMicro giây()
  • mili()
  • vi mô()

Chúng khác nhau về độ chính xác và có những đặc điểm riêng cần được tính đến khi viết mã.

Sử dụng chức năng trì hoãn arduino

Cú pháp

Độ trễ Arduino là lệnh đơn giản nhất và thường được người mới bắt đầu sử dụng. Về cơ bản, đó là độ trễ làm tạm dừng chương trình trong số mili giây được chỉ định trong ngoặc đơn. (Có 1000 mili giây trong một giây.) Giá trị tối đa có thể là 4294967295 ms, xấp xỉ bằng 50 ngày. Hãy xem một ví dụ đơn giản cho thấy rõ cách hoạt động của lệnh này.

Void setup() ( pinMode(13, OUTPUT); ) void loop() ( digitalWrite(13, HIGH); // gửi tín hiệu caođến độ trễ 13 chân (10000); // tạm dừng 10000ms hoặc 10 giây digitalWrite13, THẤP); // gửi tín hiệu thấp đến độ trễ chân 13 (10000); // tạm dừng 10000ms hoặc 10 giây)

Trong phương pháp cài đặt Chúng tôi chỉ định rằng chân 13 sẽ được sử dụng làm đầu ra. Trong phần chính của chương trình, tín hiệu cao đầu tiên được gửi đến chân, sau đó chúng tôi thực hiện độ trễ 10 giây. Trong thời gian này, chương trình dường như bị đình chỉ. Sau đó, tín hiệu thấp được đưa ra và lại có độ trễ và mọi thứ bắt đầu lại từ đầu. Kết quả là chúng ta nhận được rằng chân này được cung cấp luân phiên 5 V hoặc 0.

Bạn cần hiểu rõ rằng trong thời gian tạm dừng sử dụng độ trễ, công việc của chương trình sẽ bị tạm dừng và ứng dụng sẽ không nhận được bất kỳ dữ liệu nào từ các cảm biến. Đây là nhược điểm lớn nhất của việc sử dụng chức năng trì hoãn Arduino. Bạn có thể khắc phục hạn chế này bằng cách sử dụng ngắt, nhưng chúng ta sẽ nói về vấn đề này trong một bài viết riêng.

Ví dụ về độ trễ với đèn LED nhấp nháy

Một mạch ví dụ để minh họa cách hoạt động của hàm trễ.
Bạn có thể xây dựng một mạch với đèn LED và điện trở. Sau đó chúng ta có thể làm điều đó ví dụ tiêu chuẩn– đèn LED nhấp nháy. Để thực hiện việc này, bạn cần kết nối một đèn LED có tiếp điểm dương với chân cắm mà chúng tôi đã chỉ định là đầu ra. Chúng tôi kết nối chân tự do của đèn LED với mặt đất thông qua điện trở khoảng 220 Ohms (có thể nhiều hơn một chút). Bạn có thể xác định cực bằng cách nhìn vào bên trong của nó. Cốc lớn bên trong nối với cực âm, chân nhỏ nối với cực dương. Nếu đèn LED của bạn là mới, thì bạn có thể xác định cực tính bằng chiều dài của dây dẫn: chân dài là dương, chân ngắn là âm.

chức năng delayMicro giây

Chức năng này là một dạng tương tự hoàn toàn của độ trễ, ngoại trừ đơn vị đo của nó không phải là mili giây mà là micro giây (trong 1 giây có 1.000.000 micro giây). Giá trị tối đa sẽ là 16383, tương đương 16 mili giây. Độ phân giải là 4, nghĩa là số sẽ luôn là bội số của bốn. Một đoạn ví dụ sẽ trông như thế này:

DigitalWrite(2, CAO); // gửi tín hiệu cao tới chân 2 delayMicroseconds(16383); // tạm dừng 16383 µs digitalWrite(2, THẤP); // gửi tín hiệu thấp đến chân 2 delayMicroseconds(16383); // tạm dừng 16383 µs

Vấn đề với delayMicroseconds hoàn toàn giống với vấn đề với delay - các hàm này hoàn toàn "treo" chương trình và nó thực sự bị treo trong một thời gian. Tại thời điểm này, không thể làm việc với các cổng, đọc thông tin từ cảm biến và thực hiện các phép toán. Đối với đèn nhấp nháy tùy chọn này phù hợp, nhưng người dùng có kinh nghiệm không sử dụng nó cho dự án lớn, vì những thất bại như vậy là không cần thiết ở đó. Vì vậy, tốt hơn hết bạn nên sử dụng các chức năng được mô tả bên dưới.

Hàm Millis thay vì độ trễ

Hàm millis() sẽ cho phép bạn thực hiện độ trễ không chậm trễ trên Arduino, từ đó tránh được những thiếu sót phương pháp trước đó. Giá trị tối đa của tham số millis giống với giá trị của hàm trễ (4294967295ms hoặc 50 ngày).

Sử dụng millis, chúng tôi không dừng việc thực thi toàn bộ bản phác thảo mà chỉ đơn giản cho biết Arduino nên “bỏ qua” khối mã chính xác mà chúng tôi muốn tạm dừng trong bao lâu. Không giống như độ trễ millis, nó không tự dừng lại bất cứ thứ gì. Lệnh này nó chỉ đơn giản trả về cho chúng ta từ bộ hẹn giờ tích hợp của vi điều khiển số mili giây đã trôi qua kể từ khi bắt đầu. Mỗi lần gọi vòng lặp, chúng ta tự đo thời gian đã trôi qua kể từ đó cuộc gọi cuối mã của chúng tôi và nếu chênh lệch thời gian nhỏ hơn thời gian tạm dừng mong muốn thì chúng tôi sẽ bỏ qua mã. Ngay khi chênh lệch lớn hơn khoảng tạm dừng bắt buộc, chúng tôi thực thi mã, lấy thời gian hiện tại bằng cách sử dụng cùng một mili giây và ghi nhớ nó - lần này sẽ là điểm bắt đầu mới. TRONG chu kỳ tiếp theo việc đếm ngược sẽ bắt đầu từ điểm mới và chúng tôi sẽ lại bỏ qua mã cho đến khi chênh lệch mới giữa millis và giá trị đã lưu trước đó của chúng tôi đạt đến mức tạm dừng mong muốn một lần nữa.

Trì hoãn không chậm trễ bằng cách sử dụng millis yêu cầu nhiều mã hơn, nhưng với sự trợ giúp của nó, bạn có thể nhấp nháy đèn LED và tạm dừng bản phác thảo mà không cần dừng hệ thống.

Dưới đây là một ví dụ minh họa rõ ràng công việc của nhóm:

Thời gian dài chưa được ký; // Biến để lưu trữ điểm tham chiếu void setup() ( Serial.begin(9600); ) void loop() ( /* Tại thời điểm này, quá trình thực thi analog delay() bắt đầu. Tính toán sự khác biệt giữa thời điểm hiện tại và thời điểm hiện tại điểm tham chiếu đã lưu trước đó. Nếu chênh lệch lớn hơn giá trị mong muốn thì hãy thực thi mã. Nếu không, không làm gì */ if (millis() - time > 10000)( // Thay vì 10000, hãy thay thế giá trị tạm dừng mà bạn cần tính thời gian. = millis(); Serial.println ("10 giây" ) )

Đầu tiên chúng tôi giới thiệu biến thời gian, biến này sẽ lưu trữ số mili giây. Theo mặc định, giá trị của biến là 0. Trong phần chính của chương trình, chúng ta kiểm tra điều kiện: nếu số mili giây kể từ khi bắt đầu vi điều khiển trừ đi số ghi trong biến định thời lớn hơn 10000 thì hành động xuất thông báo tới màn hình cổng được thực hiện và giá trị thời gian hiện tại được ghi vào biến. Do hoạt động của chương trình, thông báo 10 giây sẽ được hiển thị trên màn hình cổng cứ sau 10 giây. Phương pháp này cho phép bạn nhấp nháy đèn LED không chậm trễ.

Chức năng micros thay vì độ trễ

Chức năng này cũng có thể thực hiện trì hoãn mà không cần sử dụng lệnh trì hoãn. Nó hoạt động chính xác giống như mili giây, nhưng nó tính micro giây thay vì mili giây với độ phân giải 4 μs. Giá trị tối đa của nó là 4294967295 micro giây hoặc 70 phút. Nếu nó tràn, giá trị chỉ được đặt lại về 0, đừng quên nó.

Bản tóm tắt

Nền tảng Arduino cung cấp cho chúng ta một số cách để thực hiện độ trễ trong dự án của mình. Sử dụng độ trễ, bạn có thể nhanh chóng tạm dừng việc thực hiện bản phác thảo, nhưng đồng thời bạn sẽ chặn hoạt động của bộ vi điều khiển. Sử dụng lệnh millis cho phép bạn thực hiện không chậm trễ trong Arduino, nhưng điều này sẽ yêu cầu lập trình nhiều hơn một chút. Chọn Cách tốt nhất tùy thuộc vào sự phức tạp của dự án của bạn. Theo quy định, trong các bản phác thảo đơn giản và có độ trễ dưới 10 giây, độ trễ được sử dụng. Nếu logic vận hành phức tạp hơn và cần có độ trễ lớn thì tốt hơn nên sử dụng millis thay vì độ trễ.

Tối ưu hóa các chương trình Arduino của bạn bằng các ngắt - cách đơn giảnđể đáp ứng các sự kiện trong thời gian thực!

Chúng tôi đang làm gián đoạn đường truyền của mình...

Hóa ra, có một cơ chế tuyệt vời (nhưng chưa được sử dụng) được tích hợp trong tất cả Arduinos, lý tưởng để theo dõi các sự kiện trong thời gian thực. Cơ chế này được gọi là ngắt. Công việc của ngắt là đảm bảo bộ xử lý phản hồi nhanh chóng với các sự kiện quan trọng. Khi tìm thấy tín hiệu nhất định Một ngắt (như tên gọi của nó gợi ý) làm gián đoạn mọi việc mà bộ xử lý đang thực hiện và thực thi một số mã được thiết kế để phản hồi lại lệnh gọi. nguyên nhân bên ngoài, ảnh hưởng đến Arduino. Sau khi mã này được thực thi, bộ xử lý sẽ quay lại công việc ban đầu nó đang làm như không có chuyện gì xảy ra!

Điều đáng kinh ngạc ở đây là các ngắt cho phép bạn tổ chức chương trình của mình để phản hồi nhanh chóng và hiệu quả với các sự kiện quan trọng không dễ dàng đoán trước được trong vòng lặp chương trình. Trên hết, các ngắt cho phép bộ xử lý thực hiện những việc khác thay vì lãng phí thời gian chờ đợi một sự kiện xảy ra.

Nút ngắt

Hãy bắt đầu với ví dụ đơn giản: Sử dụng ngắt để theo dõi các lần nhấn nút. Để bắt đầu, chúng ta sẽ lấy một bản phác thảo có thể bạn đã thấy: ví dụ về "Nút" có trong Arduino IDE (bạn có thể tìm thấy nó trong thư mục "Ví dụ", kiểm tra menu Tệp → Ví dụ → 02. Kỹ thuật số → Nút ).

Nút int intPin = 2; // số pin bằng nút const int ledPin = 13; // số chân có đèn LED int ButtonState = 0; // biến để đọc trạng thái của nút void setup() ( // đặt chân LED thành đầu ra: pinMode(ledPin, OUTPUT); // đặt chân nút thành đầu vào: pinMode(buttonPin, INPUT); ) void loop () ( // đọc trạng thái của nút: nútState = digitalRead(buttonPin); // kiểm tra xem nút có được nhấn hay không. // nếu được nhấn thì nútState ở mức CAO: if (buttonState == CAO) ( // bật đèn LED: digitalWrite(ledPin, HIGH); ) else ( // tắt đèn LED: digitalWrite(ledPin, LOW); ) )

Không có gì gây sốc hay ngạc nhiên về những gì bạn thấy ở đây: tất cả những gì chương trình thực hiện lặp đi lặp lại đều đi qua loop() và đọc giá trị của nútPin . Hãy giả sử trong giây lát rằng bạn muốn làm điều gì đó nhiều hơn với loop(), điều gì đó hơn là chỉ đọc trạng thái đầu ra. Đây là lúc sự gián đoạn có ích. Thay vì liên tục theo dõi trạng thái của pin, chúng ta có thể giao công việc này cho một ngắt và tự do loop() để thực hiện những gì chúng ta cần trong thời gian chờ đợi! Mã mới sẽ trông như thế này:

Nút int intPin = 2; // số pin bằng nút const int ledPin = 13; // số chân có đèn LED biến động int ButtonState = 0; // biến để đọc trạng thái của nút void setup() ( // đặt chân LED thành đầu ra: pinMode(ledPin, OUTPUT); // đặt chân nút thành đầu vào: pinMode(buttonPin, INPUT); // đính kèm một ngắt đối với vectơ đính kèm ISRInterrupt (0, pin_ISR, CHANGE); void loop() ( // Không có gì ở đây! ) void pin_ISR() ( ButtonState = digitalRead(buttonPin); digitalWrite(ledPin, ButtonState); )

Chế độ vòng lặp và ngắt

Bạn sẽ nhận thấy một vài thay đổi ở đây. Điều đầu tiên và rõ ràng nhất là loop() hiện không chứa hướng dẫn nào! Chúng ta có thể làm mà không cần chúng, vì tất cả công việc trước đây được thực hiện trong câu lệnh if/else giờ được thực hiện trong tính năng mới pin_ISR() . Loại hàm này được gọi là trình xử lý ngắt: công việc của nó là chạy nhanh, xử lý ngắt và cho phép bộ xử lý quay trở lại chương trình chính (tức là nội dung của loop()). Có một số điều cần cân nhắc khi viết trình xử lý ngắt: điểm quan trọng, mà bạn có thể thấy được phản ánh trong đoạn mã trên:

  • trình xử lý phải ngắn gọn và súc tích. Bạn không muốn làm gián đoạn vòng lặp chính lâu!
  • Trình xử lý không có tham số đầu vào hoặc giá trị trả về. Tất cả các thay đổi phải được thực hiện đối với các biến toàn cục.

Có lẽ bạn đang thắc mắc: làm sao chúng ta biết khi nào ngắt sẽ kích hoạt? Điều gì gây ra nó? Hàm thứ ba, được gọi trong hàm setup(), thiết lập một ngắt cho toàn bộ hệ thống. Hàm AttachInterrupt() này nhận ba đối số:

  1. một vectơ ngắt xác định chân nào có thể tạo ra ngắt. Bản thân đây không phải là số pin mà là tham chiếu đến một vị trí trong bộ nhớ mà bộ xử lý Arduino phải theo dõi để xem liệu có xảy ra gián đoạn hay không. Không gian này trong vectơ này tương ứng với một chân bên ngoài cụ thể và không phải tất cả các chân đều có thể tạo ra ngắt! Trên Arduino Uno, chân 2 và 3 với vectơ ngắt lần lượt là 0 và 1, có thể tạo ra các ngắt. Để biết danh sách các chân có thể tạo ra các ngắt, hãy xem tài liệu về hàm đính kèm của Arduino;
  2. Tên hàm xử lý ngắt: xác định mã sẽ được thực thi khi đáp ứng điều kiện kích hoạt ngắt;
  3. chế độ ngắt, xác định hành động chân nào sẽ kích hoạt ngắt. Arduino Uno hỗ trợ bốn chế độ ngắt:
    • RISING - kích hoạt ngắt cạnh tăng trên chân ngắt;
    • TUYỆT VỜI - kích hoạt gián đoạn rơi;
    • THAY ĐỔI - phản ứng với bất kỳ thay đổi nào về giá trị của chân ngắt;
    • THẤP - Gọi bất cứ khi nào pin ở mức thấp.

Và tóm lại, thiết lập AttachInterrupt() của chúng tôi tương ứng với việc theo dõi vectơ ngắt 0 (chân 2) để phản hồi ngắt bằng pin_ISR() và gọi pin_ISR() bất cứ khi nào có thay đổi trạng thái trên chân 2.

Bay hơi

Một điểm đáng lưu ý khác là trình xử lý ngắt của chúng tôi sử dụng biến nútState để lưu trữ trạng thái đầu ra. Kiểm tra định nghĩa của nútState: thay vì thuộc loại int , chúng tôi đã xác định nó thuộc loại int dễ bay hơi . Có chuyện gì vậy? dễ bay hơi là một từ khóa ngôn ngữ C áp dụng cho các biến. Điều đó có nghĩa là giá trị của biến không nằm dưới toàn quyền kiểm soát các chương trình. Nghĩa là, giá trị của nútState có thể thay đổi và thay đổi thành giá trị mà bản thân chương trình không thể dự đoán được - trong trường hợp này là đầu vào của người dùng.

Một cái khác thứ hữu ích trong từ khóa dễ bay hơi là để bảo vệ khỏi mọi tối ưu hóa ngẫu nhiên. Hóa ra, các trình biên dịch còn làm được một số việc nữa Nhiệm vụ bổ sung khi chuyển đổi mã nguồn của chương trình thành mã thực thi của máy. Một trong những nhiệm vụ này là loại bỏ những phần không sử dụng mã nguồn biến từ mã máy. Vì biến ButtonState không được sử dụng hoặc gọi trực tiếp trong các hàm loop() hoặc setup() nên có nguy cơ trình biên dịch có thể loại bỏ biến này dưới dạng biến không được sử dụng. Rõ ràng điều này là sai - chúng ta cần biến này! Từ khóa dễ bay hơi có tác dụng phụ là báo cho trình biên dịch biết rằng biến đó nên được để yên.

Loại bỏ các biến không sử dụng khỏi mã là tính năng chức năng, không phải lỗi trình biên dịch. Đôi khi mọi người để lại các biến không được sử dụng trong mã và chiếm bộ nhớ. Nó không phải như vậy một vấn đề lớn, nếu bạn đang viết chương trình C cho máy tính có RAM hàng gigabyte. Tuy nhiên, trên Arduino ĐẬP có hạn và bạn không muốn lãng phí nó! Ngay cả các trình biên dịch C cho máy tính cũng sẽ thực hiện chính xác điều tương tự, mặc dù có rất nhiều trình biên dịch có sẵn. bộ nhớ hệ thống. Để làm gì? Vì lý do tương tự mà mọi người tự dọn dẹp sau chuyến dã ngoại - đó là thực hành tốt, đừng để rác lại phía sau.

Tổng hợp

Ngắt là một cách đơn giản để giúp hệ thống của bạn phản hồi nhanh hơn với các tác vụ nhạy cảm về thời gian. Họ cũng có lợi ích bổ sung- giải phóng vòng lặp loop() chính, cho phép nó tập trung vào nhiệm vụ chính của hệ thống (tôi thấy rằng việc sử dụng các ngắt có xu hướng giữ cho mã của tôi có tổ chức hơn một chút: dễ dàng hơn để xem đoạn mã chính được thiết kế để làm gì và những sự kiện định kỳ nào được xử lý bằng các ngắt). Ví dụ hiển thị ở đây là trường hợp sử dụng cơ bản nhất của ngắt; bạn có thể sử dụng để đọc dữ liệu từ thiết bị I2C, Truyền không dây và nhận dữ liệu, thậm chí là khởi động hoặc dừng động cơ.

Có dự án gián đoạn thú vị nào không? Để lại ý kiến ​​​​của bạn dưới đây!

Nói chung, Arduino không hỗ trợ song song hóa nhiệm vụ thực sự hoặc đa luồng. Nhưng có thể với mỗi lần lặp lại chu kỳ vòng() hướng dẫn bộ vi điều khiển kiểm tra xem đã đến lúc thực hiện một số tác vụ nền bổ sung hay chưa. Trong trường hợp này, người dùng sẽ thấy rằng một số tác vụ đang được thực hiện đồng thời.

Ví dụ: hãy nháy đèn LED ở một tần số nhất định, đồng thời tạo ra âm thanh tăng giảm giống như tiếng còi báo động từ bộ phát áp điện. Chúng tôi đã kết nối cả đèn LED và bộ phát Piezo với Arduino nhiều lần. Hãy lắp ráp mạch như trong hình.

Nếu bạn kết nối đèn LED với chân kỹ thuật số không phải là "13", đừng quên điện trở giới hạn dòng điện khoảng 220 ohms.

2 Điều khiển bộ phát LED và áp điện sử dụng toán tử delay()

Hãy viết một bản phác thảo như thế này và tải nó lên Arduino.

Const int soundPin = 3; /* khai báo một biến có số chân mà phần tử áp điện được kết nối */ const int ledPin = 13; // khai báo một biến có số chân LED thiết lập void() ( pinMode(soundPin, OUTPUT); // khai báo chân 3 là đầu ra. pinMode(ledPin, OUTPUT); // khai báo chân 13 là đầu ra. } vòng lặp trống() (// Điều khiển âm thanh: tone(soundPin, 700); // tạo ra âm thanh có tần số trễ 700 Hz(200); giai điệu(soundPin, 500); // ở tần số trễ 500 Hz(200); giai điệu(soundPin, 300); // ở tần số trễ 300 Hz(200); giai điệu(soundPin, 200); // ở tần số trễ 200 Hz(200); // Điều khiển LED: digitalWrite(ledPin, HIGH); // độ trễ ánh sáng(200); digitalWrite(ledPin, THẤP); // tắt độ trễ(200); }

Sau khi bật nó lên, rõ ràng là bản phác thảo không được thực hiện chính xác như chúng ta cần: cho đến khi còi báo động hoạt động hoàn toàn, đèn LED sẽ không nhấp nháy, nhưng chúng ta muốn đèn LED nhấp nháy trong lúcâm thanh của còi báo động. Vấn đề ở đây là gì?

Thực tế là vấn đề này không thể giải quyết theo cách thông thường. Các tác vụ được thực hiện bởi bộ vi điều khiển một cách tuần tự. Nhà điều hành trì hoãn() trì hoãn việc thực thi chương trình trong một khoảng thời gian xác định và cho đến khi hết thời gian này, các lệnh sau của chương trình sẽ không được thực thi. Vì điều này, chúng tôi không thể đặt thời lượng thực hiện khác nhau cho từng tác vụ trong vòng lặp vòng() các chương trình. Vì vậy, bạn cần phải mô phỏng đa nhiệm bằng cách nào đó.

3 Quy trình song song không có toán tử "delay()"

Một tùy chọn trong đó Arduino sẽ thực hiện các nhiệm vụ giả song song đã được các nhà phát triển Arduino đề xuất. Bản chất của phương pháp này là với mỗi lần lặp lại chu trình vòng() chúng tôi kiểm tra xem đã đến lúc nhấp nháy đèn LED (thực hiện tác vụ nền) hay chưa. Và nếu nó đã đến thì chúng ta đảo ngược trạng thái của đèn LED. Đây là một loại tùy chọn bỏ qua cho người vận hành trì hoãn().

Const int soundPin = 3; // biến có số chân của phần tử áp điện const int ledPin = 13; // biến có số chân LED const long ledInterval = 200; // Khoảng thời gian nhấp nháy của đèn LED, ms. int ledState = THẤP; // trạng thái ban đầu của LED unsigned long previousMillis = 0; // lưu trữ thời gian kích hoạt đèn LED trước đó thiết lập void() ( pinMode(soundPin, OUTPUT); // đặt chân 3 làm đầu ra. pinMode(ledPin, OUTPUT); // đặt chân 13 làm đầu ra. } vòng lặp trống() (// Điều khiển âm thanh: tone(soundPin, 700); độ trễ (200); giai điệu(soundPin, 500); độ trễ (200); giai điệu(soundPin, 300); độ trễ (200); giai điệu(soundPin, 200); độ trễ (200); // Đèn LED nhấp nháy: // thời gian kể từ khi Arduino được bật, ms: unsigned long currentMillis = millis(); // Nếu đến lúc nhấp nháy, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // sau đó ghi nhớ thời gian hiện tại if (ledState == LOW) ( // và đảo ngược trạng thái của đèn LED ledState = CAO; ) khác ( ledState = THẤP; ) digitalWrite(ledPin, ledState); // chuyển trạng thái LED) }

Một nhược điểm đáng kể của phương pháp này là phần mã trước khối điều khiển LED phải được thực thi nhanh hơn khoảng thời gian nhấp nháy của đèn LED “ledInterval”. Nếu không, việc chớp mắt sẽ xảy ra ít thường xuyên hơn mức cần thiết và chúng ta sẽ không nhận được hiệu quả khi thực hiện các nhiệm vụ song song. Cụ thể, trong bản phác thảo của chúng tôi, thời lượng thay đổi âm thanh còi báo động là 200+200+200+200 = 800 ms và chúng tôi đặt khoảng thời gian nhấp nháy của đèn LED là 200 ms. Nhưng đèn LED sẽ nhấp nháy với khoảng thời gian 800 ms, dài gấp 4 lần so với thời gian chúng ta đặt.

Nói chung, nếu mã sử dụng toán tử trì hoãn(), trong trường hợp này rất khó để mô phỏng sự song song giả, vì vậy nên tránh nó.

Trong trường hợp này, bộ điều khiển âm thanh còi báo động cũng cần kiểm tra xem thời gian đã đến hay chưa và không sử dụng. trì hoãn(). Nhưng điều này sẽ làm tăng số lượng mã và làm cho chương trình khó đọc hơn.

4 Sử dụng Thư viện ArduinoThreadđể tạo các chủ đề song song

Để giải quyết vấn đề, chúng tôi sẽ sử dụng một thư viện tuyệt vời ArduinoChủ đề, cho phép bạn dễ dàng tạo các quy trình giả song song. Nó hoạt động theo cách tương tự, nhưng cho phép bạn tránh viết mã để kiểm tra thời gian - liệu bạn có cần thực hiện một tác vụ trong vòng lặp này hay không. Điều này làm giảm số lượng mã và cải thiện khả năng đọc của bản phác thảo. Hãy kiểm tra thư viện đang hoạt động.


Trước hết, hãy tải xuống kho lưu trữ thư viện từ trang web chính thức và giải nén nó vào một thư mục thư viện/ Môi trường phát triển Arduino IDE. Sau đó đổi tên thư mục ArduinoThread-master V. ArduinoChủ đề.

Sơ đồ kết nối sẽ giữ nguyên. Chỉ có mã chương trình sẽ thay đổi.

#bao gồm // kết nối thư viện ArduinoThread const int soundPin = 3; // biến có số chân của phần tử áp điện const int ledPin = 13; // biến có số chân LED Thread ledThread = Thread(); // tạo một thread điều khiển LED Thread soundThread = Thread(); // tạo một thread điều khiển còi báo động thiết lập void() ( pinMode(soundPin, OUTPUT); // khai báo chân 3 là đầu ra. pinMode(ledPin, OUTPUT); // khai báo chân 13 là đầu ra. ledThread.onRun(ledBlink); // gán tác vụ cho thread ledThread.setInterval(1000); // đặt khoảng thời gian phản hồi, ms soundThread.onRun(sound); // gán một tác vụ cho thread soundThread.setInterval(20); // đặt khoảng thời gian phản hồi, ms } vòng lặp trống() (// Kiểm tra xem đã đến lúc đèn LED chuyển đổi chưa: if (ledThread. ShouldRun()) ledThread.run(); // bắt đầu chuỗi // Kiểm tra xem đã đến lúc thay đổi âm báo còi chưa: if (soundThread. ShouldRun()) soundThread.run(); // bắt đầu chủ đề } // Luồng LED: void ledBlink() ( bool tĩnh ledStatus = false; // Trạng thái đèn LED Bật/Tắt ledStatus = !ledStatus; // đảo ngược trạng thái digitalWrite(ledPin, ledStatus); // bật/tắt đèn LED } // Luồng còi báo động: âm thanh trống() (âm int tĩnh = 100; // cao độ âm thanh, âm Hz(soundPin, ton); // bật còi báo động ở mức "ton" Hz if (ton )

Trong chương trình chúng tôi tạo hai chủ đề - chủ đề dẫnchủ đề âm thanh, mỗi cái thực hiện thao tác riêng: một cái nhấp nháy đèn LED, cái thứ hai điều khiển âm thanh của còi báo động. Trong mỗi lần lặp của vòng lặp, đối với mỗi luồng, chúng ta kiểm tra xem đã đến lúc thực hiện nó hay chưa. Nếu nó đến, nó sẽ được khởi chạy để thực thi bằng phương thức chạy(). Điều chính là không sử dụng toán tử trì hoãn(). Mã cung cấp giải thích chi tiết hơn.


Hãy tải mã vào bộ nhớ Arduino và chạy nó. Bây giờ mọi thứ hoạt động chính xác như bình thường!

Hướng dẫn

Nói chung, Arduino không hỗ trợ song song hóa nhiệm vụ thực sự hoặc đa luồng.
Nhưng mỗi lần vòng lặp “loop()” được lặp lại, bạn có thể chỉ định để kiểm tra xem đã đến lúc thực hiện một số tác vụ nền bổ sung hay chưa. Trong trường hợp này, người dùng sẽ thấy rằng một số tác vụ đang được thực hiện đồng thời.
Ví dụ: hãy nhấp nháy ở một tần số nhất định, đồng thời tạo ra âm thanh tăng giảm giống như tiếng còi báo động từ bộ phát áp điện.
Chúng tôi đã kết nối cả đèn LED và Arduino với Arduino nhiều lần. Hãy lắp ráp mạch như trong hình. Nếu bạn kết nối đèn LED với chân kỹ thuật số không phải là "13", đừng quên điện trở giới hạn dòng điện khoảng 220 ohms.

Hãy viết một bản phác thảo như thế này và tải nó lên Arduino.
Sau khi nhìn vào bảng, rõ ràng bản phác thảo không được thực hiện chính xác như chúng ta cần: cho đến khi còi báo động hoạt động hoàn toàn, đèn LED sẽ không nhấp nháy và chúng ta muốn đèn LED nhấp nháy TRONG KHI còi báo động. Vấn đề ở đây là gì?
Thực tế là vấn đề này không thể giải quyết theo cách thông thường. Các tác vụ được thực hiện bởi bộ vi điều khiển một cách tuần tự. Toán tử "delay()" trì hoãn việc thực thi chương trình trong một khoảng thời gian xác định và cho đến khi hết thời gian này, các lệnh sau trong chương trình sẽ không được thực thi. Vì điều này, chúng ta không thể đặt thời gian thực hiện khác nhau cho từng tác vụ trong vòng lặp "loop()" của chương trình.
Vì vậy, bạn cần phải mô phỏng đa nhiệm bằng cách nào đó.

Một tùy chọn trong đó Arduino sẽ thực hiện các nhiệm vụ giả song song được các nhà phát triển Arduino đề xuất trong bài viết https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.
Bản chất của phương pháp là với mỗi lần lặp lại vòng lặp (), chúng tôi kiểm tra xem đã đến lúc nhấp nháy đèn LED (thực hiện tác vụ nền) hay chưa. Và nếu nó đã đến thì chúng ta đảo ngược trạng thái của đèn LED. Đây là một kiểu bỏ qua cho "delay()".
Một nhược điểm đáng kể của phương pháp này là phần mã trước khối điều khiển LED phải được thực thi lâu hơn khoảng thời gian nhấp nháy của đèn LED “ledInterval”. Nếu không, việc chớp mắt sẽ xảy ra ít thường xuyên hơn mức cần thiết và chúng ta sẽ không nhận được hiệu quả khi thực hiện các nhiệm vụ song song. Cụ thể, trong bản phác thảo của chúng tôi, thời lượng thay đổi âm thanh còi báo động là 200+200+200+200 = 800 ms và chúng tôi đặt khoảng thời gian nhấp nháy của đèn LED là 200 ms. Nhưng đèn LED sẽ nhấp nháy với khoảng thời gian 800 ms, gấp 4 lần so với thời gian chúng ta đặt. Nói chung, nếu mã của bạn sử dụng toán tử "delay()" thì rất khó để mô phỏng tính song song giả, vì vậy bạn nên tránh nó.
Trong trường hợp này, bộ điều khiển còi báo động cũng cần kiểm tra xem thời gian đã đến hay chưa và không sử dụng “delay()”. Nhưng điều này sẽ làm tăng số lượng mã và làm cho chương trình khó đọc hơn.

Để giải quyết vấn đề này, chúng tôi sẽ sử dụng thư viện ArduinoThread tuyệt vời, cho phép bạn dễ dàng tạo các quy trình giả song song. Nó tương tự nhưng cho phép bạn không viết mã để kiểm tra thời gian - liệu bạn có cần thực hiện một tác vụ trong chu trình này hay không. Điều này làm giảm số lượng mã và cải thiện khả năng đọc của bản phác thảo. Hãy kiểm tra thư viện đang hoạt động.
Trước hết, hãy tải xuống kho lưu trữ thư viện từ trang web chính thức https://github.com/ivanseidel/ArduinoThread/archive/master.zip và giải nén nó vào thư mục “thư viện” của môi trường phát triển Arduino IDE. Sau đó đổi tên thư mục "ArduinoThread-master" thành "ArduinoThread".

Sơ đồ kết nối sẽ giữ nguyên. Chỉ có mã chương trình sẽ thay đổi. Bây giờ nó sẽ trông giống như trong hình nhỏ.
Trong chương trình, chúng tôi tạo hai luồng, mỗi luồng thực hiện thao tác riêng: một luồng nhấp nháy đèn LED, luồng thứ hai điều khiển âm thanh của còi báo động. Trong mỗi lần lặp của vòng lặp, đối với mỗi luồng, chúng ta kiểm tra xem đã đến lúc thực hiện nó hay chưa. Nếu nó đến, nó sẽ được khởi chạy để thực thi bằng phương thức "run()". Điều chính là không sử dụng toán tử "delay()".
Mã cung cấp giải thích chi tiết hơn.
Hãy tải mã vào bộ nhớ Arduino và chạy nó. Bây giờ mọi thứ hoạt động chính xác như bình thường!

Sự nhiệt tình, tham công tiếc việc hoặc tham vọng ngày càng tăng theo nghĩa đen là thúc đẩy chúng ta đảm nhận càng nhiều nhiệm vụ và nghĩa vụ càng tốt. Kết quả là, vào đêm trước kỳ báo cáo, đầu bạn tràn ngập những nhiệm vụ chưa hoàn thành và “danh sách việc cần làm” có xu hướng dài bằng một cuộn giấy dán tường.

Bạn sẽ cần

  • - người tổ chức
  • - phần mềm tổ chức thời gian làm việc (ví dụ: hẹn giờ ChromoDoro - Ứng dụng Google http://clck.ru/9ZC1)
  • - Lịch trình nhiệm vụ hoặc “danh sách việc cần làm” dưới dạng bảng.
  • - nhãn dán và bút đánh dấu

Hướng dẫn

"Gửi bạn và tôi"

Tất cả các nhiệm vụ được chia thành những việc cần phải hoàn thành hoặc có thể giao cho người ít bận rộn hơn. Có rất nhiều nhà quản lý có trách nhiệm quá mức tin rằng họ phải tự mình gánh vác công việc của toàn bộ bộ phận. Vì vậy, bố mẹ không cho phép bạn buộc dây giày vì bố mẹ giỏi việc đó hơn. Bạn không thể cướp và tước đi cơ hội thể hiện khả năng của cấp dưới. Ở nhà cũng vậy. Thay vì tự hứa với mình mỗi đêm là sửa vòi nước nhỏ giọt, bạn cần gọi thợ sửa ống nước và gạch nhiệm vụ đó ra khỏi danh sách.

“Hãy từ bỏ sự trì hoãn”

Hiện tượng tâm lý này có nhiều loại và theo đó là nhiều định nghĩa, nhưng nhìn chung đó là sự miễn cưỡng khi bắt đầu giải quyết vấn đề. Đôi khi bước đầu tiên khó thực hiện vì nỗi sợ thất bại trong tiềm thức, đôi khi hoàn toàn miễn cưỡng chuyển từ trạng thái lười biếng thoải mái sang trạng thái không thoải mái. Thông thường, một người tự nhủ: “Tôi sẽ ăn một quả táo, chơi bài và sau đó…”. Chúng ta cần phải thay đổi suy nghĩ của mình. Và biến táo và solitaire thành phần thưởng cho nỗ lực của bạn. Và câu thần chú hàng ngày sẽ như sau: “Nếu tôi làm việc trong 15 phút, tôi sẽ ăn một quả táo! Tôi xứng đáng được như vậy”. Nhưng nghệ thuật quản lý thời gian không hề dễ dàng.

Nhóm nhiệm vụ

Trong kinh doanh, đây có thể là những nhiệm vụ liên quan đến lĩnh vực công việc (hậu cần hoặc định giá). Học sinh có các khối chuyên đề của tài liệu đang được nghiên cứu. Người tiên tiến chia căn hộ thành các khu. “Phòng tắm”, “hành lang”, “không gian cạnh TV” - mỗi người đều có cách tiếp cận riêng. Và dành không quá 15 phút cho mỗi khu vực để lập lại trật tự. Điều này đủ để dễ dàng duy trì sự sạch sẽ, tiết kiệm thời gian và không phát điên với cảm giác tội lỗi vì những nhiệm vụ chưa hoàn thành. Bên cạnh mỗi nhóm nhiệm vụ, bạn nên viết tỷ lệ phần trăm gần đúng của công việc đã hoàn thành. Kết quả công việc có thể nhìn thấy được góp phần tạo ra sự thoải mái về mặt tâm lý và làm tăng sự lo lắng, điều này cản trở việc hoàn thành nhiệm vụ.

Động lực

Nếu nó tồn tại thì thủ thuật kỹ thuật sẽ tối ưu hóa công việc. Nếu có vấn đề với nó, lời nhắc và nhãn dán điện tử sẽ gây khó chịu. Vì vậy, điều quan trọng là phải hiểu được lợi ích của việc giải quyết vấn đề và tiếp cận công việc một cách có ý thức. Nếu tâm trí lang thang, bị phân tâm bởi những điều vô nghĩa dễ chịu, thì có thể trong đó không có đủ niềm vui. Người trầm cảm khó có thể hoạt động hiệu quả hơn nhiều. Điều này có nghĩa là những khuyến khích để giải quyết vấn đề, nguồn cảm hứng và thái độ sáng tạo tích cực đặc biệt phải được tìm kiếm từ bên ngoài.

Video về chủ đề

ghi chú

Tất cả các nhiệm vụ có thể được chia thành bốn loại nổi tiếng: quan trọng và không khẩn cấp, quan trọng và khẩn cấp, không quan trọng và khẩn cấp, không quan trọng và không khẩn cấp. Khi đó sẽ dễ dàng hơn trong việc tính toán thời gian hoàn thành từng nhiệm vụ. Điều chính là không quên bao gồm mười lăm phút nghỉ giải lao.

Lời khuyên hữu ích

Đừng xịt. Tất cả chúng ta đều cố gắng sống theo sự mong đợi của người khác. Nhưng chúng ta phải học cách nói: “Dừng lại”. Để hoàn thành một nhiệm vụ một cách hiệu quả, bạn cần tập trung hoàn toàn vào nó. Và để làm được điều này, rõ ràng, bạn cần phải làm việc theo một hướng, dồn hết nỗ lực của tâm trí để giải quyết vấn đề.