Hàm milis () Arduino là một hàm rất hữu ích trả về một số mili giây từ khi reset. Trong bài viết này, bạn có thể tìm hiểu cách sử dụng nó một cách hiệu quả để xác định thời gian và delay của sự kiện, cũng như tìm hiểu cách thức hoạt động chi tiết của nó.
Sử dụng milis Arduino làm bộ định thời delay
Hàm millis () trả về thời gian hiện tại tính bằng mili giây (1/1000 giây) tính từ khi bạn cấp nguồn cho bo mạch (hoặc reset nó). Đây một cách đo thời gian từ bên trong chương trình, khác hẳn với hàm delay () không đưa ra phản hồi về thời gian.
Đo khoảng thời gian bằng cách sử dụng millis (), là so sánh thời gian hiện tại với giá trị thời gian được lưu trữ trong một biến. Khi bạn đi một vòng lặp, bạn liên tục thực hiện một số phép toán đơn giản:
millis () - storage_time
Nó cung cấp cho bạn thời gian đã trôi qua tính bằng mili giây từ giá trị thời gian được lưu trữ.
Ví dụ mã bên dưới:
Đoạn mã ở trên dẫn đến biến "elapsed_time" đo thời gian tính bằng mili giây kể từ khi sự kiện xảy ra. Một sự kiện có thể là một lần nhấn nút hoặc một số hành động từ một phần khác của chương trình.
Nếu bạn muốn gửi một tin nhắn nối tiếp cách nhau một giây trong trường hợp này, sự kiện sẽ là truyền tin nhắn và bạn sẽ đợi cho đến khi thời gian trôi qua lớn hơn 1000 (1000ms = 1 giây) trước khi gửi tin nhắn nối tiếp tiếp theo (và lưu trữ lần nữa).
Hàm millis () được điều khiển bởi một ngắt hẹn giờ mili giây tăng một unsigned long mỗi khi nó kích hoạt và chỉ trả về giá trị của biến đó.
LED nhấp nháy sử dụng millis
Ví dụ sau đây là cách sử dụng millis () để có độ trễ không chặn (no blocking delay). Độ trễ không chặn là một loại độ trễ cho phép mã khác hoạt động ngay cả khi có một độ trễ hoạt động ở nơi khác. Điều này rất khác với việc sử dụng hàm delay () vì mã của bạn ngừng xử lý (ngoại trừ các ngắt) và không có tác dụng gì trong khoảng thời gian trễ.
Mã bên dưới có độ trễ không chặn là 500ms và độ trễ này được kích hoạt nhiều lần. Với cách này bạn có thể tạo bộ đếm thời gian milis Arduino có thời gian trễ với độ dài bất kỳ (lên đến 49,7 ngày).
Đối với một hành động cứ sau 12 giờ, bạn sẽ sử dụng khoảng thời gian trễ là (12 * 60 * 60 * 1000) = 43200000ms thay thế 500 bằng 43200000L (Chữ L cho biết giá trị long của trình biên dịch). Nếu bạn cần nhiều mili giây hơn, ví dụ: 3 ngày sẽ yêu cầu (3 * 24 * 60 * 60 * 1000) = 259200000L một lần nữa thay thế 500 bằng 259200000L.
Ví dụ 1: Mã độ trễ
Hoạt động mã: milis Arduino là hoạt động trì hoãn
Mã Arduino ở trên là hàm vòng lặp, trong trường hợp này, là mã duy nhất được yêu cầu; làm cho nội dung của hàm setup () trống vì bạn không cần khởi tạo bất cứ thứ gì, tức là bạn không cần khởi động cổng nối tiếp hoặc các giao diện SPI trong trường hợp này.
Hoạt động mã vòng lặp: Đối với độ trễ không chặn
Giá trị của oldtime được đặt thành giá trị millis () khi bắt đầu. Nếu giá trị của millis (), đang tăng lên mỗi mili giây, lớn hơn 500 lần đếm so với oldtime thì biểu thức điều kiện trong câu lệnh if trở thành true (đúng). Điều này có nghĩa là 500 mili giây đã trôi qua kể từ khi giá trị của oldtime được đặt, tức là độ trễ 500 mili giây.
Trong phần nội dung của câu lệnh if, đèn LED được bật tắt từ trạng thái trước đó của nó. Điều này có nghĩa là đèn LED nhấp nháy với tốc độ 1Hz (500ms bật và 500ms tắt) hoặc một lần một giây.
Vì millis () chỉ được kiểm tra bởi câu lệnh if có điều kiện, bạn có thể thêm mã khác trong vòng lặp để thực hiện công việc hữu ích khác, ví dụ sử dụng Arduino millis () để không dừng bộ xử lý.
MẸO: điều quan trọng là bạn đặt oldtime trong câu lệnh if thành giá trị millis () hiện tại để khoảng thời gian trễ tiếp theo có thể được thực hiện, nếu không biểu thức câu lệnh if sẽ luôn true sau lần đầu tiên biểu thức trở thành true.
Lưu ý: Loại uint32_t là cùng loại với "unsigned long". uint32_t được sử dụng trong lập trình nhúng vì nó chỉ định trực tiếp số lượng bit trong type, trong khi "unsigned long" có thể có số lượng bit khác nhau cho các trình biên dịch khác nhau.
Giới hạn milis Arduino
Vậy bạn có thể đo được bao lâu? Bạn cần biết vì tất cả các hệ thống đều có giới hạn nếu bạn vượt quá sẽ làm cho hệ thống bị lỗi.
Thời gian tối đa có thể đo được phụ thuộc vào loại biến được sử dụng để lưu trữ dữ liệu millis () là một unsigned long và việc sử dụng type này cho phép bạn đo chỉ hơn 49 ngày. Nếu dự án của bạn không thực hiện hơn 49 ngày thì không có vấn đề gì.
Đối với Arduino, giá trị tối đa từ millis () là:
4,294,967,295 hoặc (0xffffffff)
Điều này là do kiểu dữ liệu mili của Arduino là:
Unsigned long (cũng có thể được viết là uint32_t)
Trong đó bạn có thể dễ dàng nhìn thấy số bit trong type.
Số ngày tối đa cho milis ()
Số đếm mà một unsigned long có khả năng giữ là: pow (2,32) -1 hoặc 4,294,967,295 hoặc 4 tỷ 294 triệu 967 nghìn và 295. Vậy nếu mỗi lần đếm có giá trị là một phần nghìn giây thì đó là bao nhiêu ngày?
Đầu tiên chia cho 1000 cho giây, sau đó cho 60 cho phút, sau đó cho 60 cho giờ rồi chia cho 24 thì bằng = ~ 49,71 ngày.
Sau khoảng 50 ngày (hoặc hơn 49,71 ngày một chút), bộ đếm thời gian kết thúc bằng 0 và đây là sự cố tràn hàm milis Arduino.
Nếu bạn đang thiết kế một dự án phải kéo dài hơn 49 ngày thì điều này có thể gây ra sự cố bởi vì tại điểm kết thúc, thời gian đang tăng dần theo mili giây, đột nhiên về không. Nếu bạn ghi lại time stamp cho thiết bị ghi dữ liệu bằng milis () sau 49,71 ngày, time stamp sẽ trở về thời gian bắt đầu, tức là sai.
Một vấn đề khác là sử dụng bộ đếm thời gian delay, ví dụ: để nhấp nháy LED, trong đó bạn đợi bộ đếm thời gian đạt đến thời điểm hiện tại cộng với 500 mili giây, nếu bộ hẹn giờ nằm trong 500 mili giây đó thì thời gian chờ thay vào đó sẽ là 50 ngày, quá lâu để tắt LED!
MẸO: Nếu bạn muốn vượt quá 50 ngày, hãy thêm một biến khác hoạt động như các bit bổ sung vào phía trước unsigned long. Ví dụ một unsigned char sẽ kéo dài thời gian thêm 256 * 50 ngày. Tăng nó mỗi khi thời gian milis () kết thúc. Nhược điểm là bạn sẽ cần phải bao gồm số lượng 8 bit đó trong tất cả các phép tính.
Cách tránh tràn hàm millis trong arduino
Giả sử rằng bạn muốn có một hành động lặp lại sau mỗi 100 mili giây. Vì vậy, bạn viết mã như sau:
Thông thường, bạn sẽ cho rằng bo sẽ không hoạt động trong hơn 50 ngày, vì vậy bạn không xem xét đến tràn hàm. Bây giờ hãy xem xét vấn đề này và xem những gì sẽ xảy ra.
Giải pháp tràn Millis ()
Giải pháp sau đây tránh được vấn đề tràn nhưng chỉ khi bạn viết chính xác phần time dejection. Nếu bạn đảo ngược các term hoặc di chuyển chúng xung quanh, bạn sẽ không làm được.
Có thể tránh được hiện tượng tràn nhưng chỉ đối với các khoảng thời gian đo nhỏ hơn thời gian tràn tối đa (trường hợp sử dụng) và điều này là do thuộc tính về cách các số được lưu trữ trong bộ nhớ, tức là số học module và cách tính số nguyên. Đối với 4 byte unsigned long được sử dụng.
Trong toán học modulo, (trong C modulo được biểu diễn bằng dấu phần trăm%) các giá trị được giới hạn nhỏ hơn 1 so với số modulo Ví dụ:
9% 10 trả về 9
nhưng 10% 10 trả về 0
tương tự
19% 10 trả về 9
và 20% 10 trả về 0
Đây là cách giải quyết vấn đề tràn. Nhưng thay vì gọi toán tử modulo, thì nó đã hoạt động vì hạn chế của bộ nhớ 4 byte lưu trữ trong một unsigned long. Vì vậy, unsigned long sẽ tự động giới hạn giá trị từ 0 đến pow (2,32) -1.
Vì các số nguyên cũng sử dụng biểu diễn phần bù của hai nên bạn sẽ có được các số phù hợp khi trừ và cộng.
Ví dụ về tràn millis unsigned long
Giả sử ledtime được đặt thành giá trị lớn nhất của unsigned long:
ledtime = 0xffffffff hoặc pow (2,32) -1 hoặc 4294967295
Điều đó có nghĩa là khi phát hiện timeout theo được thực hiện thì millis () sẽ trả về một giá trị dương và giả sử rằng millis () trả về giá trị 1 trong lần phát hiện tiếp theo thì đối với các phép toán thông thường bạn sẽ có:
millis () - ledtime == 1 - 4294967295 = -4294967294
Phép toán bổ sung của hai và modulo cho unsigned long:
-4294967294 == 2
Vì vậy, đây là câu trả lời đúng mặc dù giá trị millis () nhỏ hơn giá trị thực của ledtime. Phép toán bổ sung của hai có nghĩa là $ fffffff được hiểu là một số âm ($ fffffff == -1). Vì thế:
ledtime (1) - ($ ffffffff) == (1) - (-1) == 2.
Vì vậy, mã phát hiện bộ đếm thời gian đã được viết theo cách bỏ qua các lỗi tràn!
Lưu ý rằng phương pháp này chỉ có thể chính xác để đo các chu kỳ delta và mã phải được viết như sau:
Hotline: 0979 466 469