thời gian trì hoãn); vs if (millis () - trước> thời gian); và trôi


8

Trải qua một dự án cũ, tôi đã có mã trên hai Arduino Do trông giống như thế này

void loop()
{
  foo();
  delay(time);
}

tham gia với trái tim đa số các tài liệu về việc sử dụng delay();tôi recoded này như

void loop()
{
  static unsigned long PrevTime;
  if(millis()-PrevTime>time)
  {
    foo();
    PrevTime=millis();
  }
}

Tuy nhiên, điều này dường như đã tạo ra một tình huống trong đó hai thiết bị trôi dạt trong một khoảng thời gian mà trước đó chúng không

Câu hỏi của tôi có hai mặt:

  1. Tại sao sẽ if(millis()-PrevTime>time)gây ra trôi nhiều hơn delay(time)?
  2. Có cách nào để ngăn chặn sự trôi dạt này mà không quay trở lại delay(time)?

1
Thứ tự độ lớn của "khoảng thời gian" mà bạn nhận thấy sự trôi dạt là gì? Có phải hai thiết bị ở cùng một vị trí, do đó ở cùng nhiệt độ? Họ đang chạy trên một bộ dao động tinh thể hoặc một bộ cộng hưởng gốm?
jose có thể

Cá nhân tôi thích giải pháp của Majenko và luôn sử dụng nó (Tôi đặt mức tăng trước các hướng dẫn khác, nhưng đây chỉ là một ưu tiên). Tuy nhiên, lưu ý rằng thời gian này là CHÍNH XÁC 100ms, trong khi mã khác ( foo; delay;) có khoảng thời gian dài hơn 100ms (là 100ms + thời gian foo). Vì vậy, bạn sẽ trải nghiệm sự trôi dạt (nhưng đó là delaySW được thực hiện đang trôi dạt). Trong mọi trường hợp, hãy nhớ rằng ngay cả các triển khai hoàn toàn bằng nhau "trôi", bởi vì đồng hồ không bằng nhau; nếu bạn cần đồng bộ hóa hoàn toàn, hãy sử dụng tín hiệu để đồng bộ hóa hai chương trình.
frarugi87

Hai thiết bị nằm cạnh nhau, sau khi chạy từ Thứ Sáu lúc 17:00 đến Thứ Hai lúc 9:00, có 4 phút trôi. Tôi quyết định rằng tôi sẽ sử dụng ghim kỹ thuật số để đồng bộ hóa các đầu vào theo đề xuất của bạn
ATE-ANHE

"Hai thiết bị nằm cạnh nhau, ..." điều đó không có nghĩa là cơ chế định thời không chính xác, bạn đang nói về tỷ lệ lỗi ~ 800ppm, cao đối với hai bộ dao động tinh thể nhưng hợp lý cho cả một bộ cộng hưởng gốm. bạn phải so sánh nó với một tiêu chuẩn thời gian chính xác hợp lý để chắc chắn: các tinh thể thường trong phạm vi 20ppm và tcxo có thể thực hiện phụ 1ppm. đó sẽ là cách làm của tôi
dannyf

Câu trả lời:


10

Có một điều quan trọng mà bạn cần nhớ khi làm việc với thời gian trên Arudino dưới mọi hình thức:

  • Mọi hoạt động đều cần có thời gian.

Hàm foo () của bạn sẽ mất một khoảng thời gian. Thời gian đó là gì, chúng ta không thể nói.

Cách đáng tin cậy nhất để đối phó với thời gian là chỉ dựa vào thời gian để kích hoạt, chứ không phải làm việc khi cần kích hoạt tiếp theo.

Ví dụ, lấy như sau:

if (millis() - last > interval) {
    doSomething();
    last = millis();
}

Biến lastsẽ là thời gian quy trình được kích hoạt * cộng với thời gian doSomethingcần để chạy. Vì vậy, giả sử intervallà 100 và doSomethingmất 10ms để chạy, bạn sẽ nhận được kích hoạt ở 101ms, 212ms, 323ms, v.v. Không phải 100ms bạn mong đợi.

Vì vậy, một điều bạn có thể làm là luôn luôn sử dụng cùng một lúc bất kể bằng cách nhớ nó tại một thời điểm cụ thể (như Juraj gợi ý):

uint32_t time = millis();

if (time - last > interval) {
    doSomething();
    last = time;
}

Bây giờ thời gian đó doSomething()sẽ không có hiệu lực trên bất cứ điều gì. Vì vậy, bạn sẽ nhận được kích hoạt ở 101ms, 202ms, 303ms, v.v. Vẫn không hoàn toàn 100ms bạn muốn - bởi vì bạn đang tìm kiếm thêm 100ms đã qua - và điều đó có nghĩa là 101ms trở lên. Thay vào đó bạn nên sử dụng >=:

uint32_t time = millis();

if (time - last >= interval) {
    doSomething();
    last = time;
}

Bây giờ, giả sử rằng không có gì khác xảy ra trong vòng lặp của bạn, bạn sẽ có các kích hoạt ở 100ms, 200ms, 300ms, v.v. Nhưng lưu ý rằng bit: "miễn là không có gì khác xảy ra trong vòng lặp của bạn" ...

Điều gì xảy ra nếu một hoạt động mất 5ms xảy ra ở 99ms ...? Kích hoạt tiếp theo của bạn sẽ bị trì hoãn cho đến 104ms. Đó là một sự trôi dạt. Nhưng nó dễ chiến đấu. Thay vì nói "Thời gian đã ghi là bây giờ", bạn nói "Thời gian ghi được trễ hơn 100ms so với trước đây". Điều đó có nghĩa là bất kể sự chậm trễ nào bạn nhận được trong mã của mình, kích hoạt của bạn sẽ luôn ở các khoảng 100ms hoặc trôi trong tích tắc 100ms.

if (millis() - last >= interval) {
    doSomething();
    last += interval;
}

Bây giờ bạn sẽ nhận được kích hoạt ở 100ms, 200ms, 300ms, v.v. Hoặc nếu có sự chậm trễ trong các bit mã khác, bạn có thể nhận được 100ms, 204ms, 300ms, 408ms, 503ms, 600ms, v.v. Nó luôn cố gắng chạy gần khoảng thời gian có thể bất kể sự chậm trễ. Và nếu bạn có độ trễ lớn hơn khoảng thời gian, nó sẽ tự động chạy thói quen của bạn đủ thời gian để bắt kịp với thời gian hiện tại.

Trước khi bạn đã trôi dạt . Bây giờ bạn có jitter .


1

Bởi vì bạn đặt lại bộ hẹn giờ sau khi hoạt động.

static unsigned long PrevTime=millis();

unsigned long t = millis();

if (t - PrevTime > time) {
    foo();
    PrevTime = t;
}

Không. lưu ý rằng PrevTime là tĩnh.
dannyf

4
@dannyf, vâng, và?
Juraj

nếu bạn biết "tĩnh" nghĩa là gì, bạn sẽ biết tại sao câu trả lời của bạn không đúng.
dannyf

Tôi biết những gì tĩnh làm với biến cục bộ .. Tôi không hiểu tại sao bạn nghĩ câu trả lời của tôi có liên quan đến tĩnh. Tôi vừa di chuyển phần đọc hiện tại trước khi foo () được gọi.
Juraj

-1

Đối với những gì bạn đang cố gắng thực hiện, delay () là cách thích hợp để triển khai mã. Lý do bạn muốn sử dụng if (millis ()) là nếu bạn muốn cho phép vòng lặp chính tiếp tục lặp để mã của bạn hoặc một số mã khác ngoài vòng lặp đó có thể thực hiện xử lý khác.

Ví dụ:

long next_trigger_time = 0L;
long interval = DESIRED_INTERVAL;

void loop() {
   do_something(); // check a sensor or value set by an interrupt
   long m = millis();
   if (m >= next_trigger_time) {
       next_trigger_time = m + interval;
       foo();
   }
}

Điều này sẽ chạy foo () trong khoảng thời gian được chỉ định trong khi cho phép vòng lặp tiếp tục chạy ở giữa. Tôi đặt phép tính next_trigger_time trước lệnh gọi foo () để giúp giảm thiểu độ trôi, nhưng đó là điều không thể tránh khỏi. Nếu sự trôi dạt là một mối quan tâm đáng kể, hãy sử dụng bộ hẹn giờ ngắt hoặc một số loại đồng bộ hóa đồng hồ / hẹn giờ. Cũng nên nhớ rằng millis () sẽ kết thúc sau một khoảng thời gian và tôi đã không tính đến điều đó để giữ cho ví dụ mã đơn giản.


Ghét phải đề cập đến điều này: vấn đề tái đầu tư trong 52 ngày.

Tôi đã đề cập đến vấn đề tái đầu tư vào cuối câu trả lời của tôi.
ThatAintWorking

Vâng, giải quyết nó.

Phí tư vấn tiêu chuẩn của tôi $ 100 / giờ nếu bạn muốn tôi viết mã cho bạn. Tôi nghĩ những gì tôi đã viết là đủ liên quan.
ThatAintWorking

1
Bạn có biết rằng Majenko đã đăng một câu trả lời đầy đủ hơn và tốt hơn mà bạn không? Bạn có biết rằng mã của bạn không biên dịch? Điều đó long m - millis()không làm những gì bạn định làm? Đó là về nhà.

-4

mã của bạn là chính xác.

vấn đề bạn gặp phải là với millis (): nó sẽ bị thiếu một chút (số lượng dưới mức tối đa chỉ là 1ms, mỗi lần gọi).

giải pháp là với các dấu tick nhỏ hơn, như micros () - nhưng điều đó cũng sẽ được tính một chút.


2
Xin vui lòng, cung cấp một số bằng chứng hoặc một số tài liệu tham khảo cho, nó sẽ được tính một chút .
Edgar Bonet
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.