Sử dụng millis () và micros () trong một thói quen ngắt


13

Các tài liệu cho attachInterrupt()biết:

... millis()dựa vào các ngắt để đếm, do đó, nó sẽ không bao giờ tăng trong ISR. Vì delay()yêu cầu ngắt để hoạt động, nó sẽ không hoạt động nếu được gọi bên trong ISR. micros()hoạt động ban đầu, nhưng sẽ bắt đầu hoạt động thất thường sau 1-2 ms. ...

Làm thế nào micros()khác với millis()(tất nhiên ngoại trừ cho độ chính xác của họ)? Có phải cảnh báo trên có nghĩa là sử dụng micros()bên trong một thói quen gián đoạn luôn là một ý tưởng tồi?

Bối cảnh - Tôi muốn đo mức chiếm xung thấp , vì vậy tôi cần kích hoạt thói quen của mình khi tín hiệu đầu vào thay đổi và ghi lại thời gian hiện tại.

Câu trả lời:


16

Các câu trả lời khác là rất tốt, nhưng tôi muốn giải thích về cách làm micros()việc. Nó luôn luôn đọc bộ đếm thời gian phần cứng hiện tại (có thể TCNT0) liên tục được cập nhật bởi phần cứng (trên thực tế, cứ sau 4 giờ lại là do bộ đếm trước 64). Sau đó, nó thêm vào số đếm tràn Timer 0, được cập nhật bằng ngắt tràn bộ định thời (nhân với 256).

Do đó, ngay cả trong ISR, bạn có thể dựa vào việc micros()cập nhật. Tuy nhiên, nếu bạn chờ quá lâu thì bạn sẽ bỏ lỡ bản cập nhật tràn, và sau đó kết quả trả về sẽ giảm xuống (tức là bạn sẽ nhận được 253, 254, 255, 0, 1, 2, 3, v.v.)

Điều này là micros()- hơi đơn giản để loại bỏ định nghĩa cho các bộ xử lý khác:

unsigned long micros() {
    unsigned long m;
    uint8_t oldSREG = SREG, t;
    cli();
    m = timer0_overflow_count;
    t = TCNT0;
    if ((TIFR0 & _BV(TOV0)) && (t < 255))
        m++;
    SREG = oldSREG;
    return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

Đoạn mã trên cho phép tràn (kiểm tra bit TOV0) để nó có thể đối phó với tràn trong khi ngắt bị tắt nhưng chỉ một lần - không có quy định nào để xử lý hai lần tràn.


TLDR;

  • Đừng trì hoãn trong ISR
  • Nếu bạn phải làm chúng, bạn có thể thời gian với micros()nhưng không millis(). Cũng delayMicroseconds()là một khả năng.
  • Đừng trì hoãn hơn 500 Lời nói hoặc hơn, hoặc bạn sẽ bỏ lỡ một tràn bộ đếm thời gian.
  • Ngay cả sự chậm trễ ngắn cũng có thể khiến bạn bỏ lỡ dữ liệu nối tiếp đến (ở mức 115200 baud, bạn sẽ nhận được một ký tự mới sau mỗi 87 Lời nói).

Không thể hiểu câu lệnh được đưa ra dưới đây được sử dụng trong micros (). Bạn có thể vui lòng giải thích? Vì ISR được viết, cờ TOV0 sẽ bị xóa ngay sau khi ISR ​​được nhập và do đó điều kiện dưới đây có thể không trở thành sự thật!. if ((TIFR0 & _BV (TOV0)) && (t <255)) m ++;
Rajesh

micros()không phải là ISR. Đó là một chức năng bình thường. Cờ TOV0 cho phép bạn kiểm tra phần cứng để xem liệu tràn bộ đếm thời gian đã xảy ra chưa (nhưng chưa được xử lý).
Nick Gammon

Vì bài kiểm tra được thực hiện với các ngắt, bạn biết rằng cờ sẽ không thay đổi trong quá trình kiểm tra.
Nick Gammon

Vì vậy, bạn có nghĩa là sau khi hàm cli () bên trong hàm micros (), nếu có bất kỳ sự cố tràn nào xảy ra, cần phải kiểm tra? Có lý. Mặt khác, mặc dù micros không phải là một chức năng, TIMER0_OVF_vect ISR sẽ xóa TOV0 trong TIFR0 là điều tôi nghi ngờ.
Rajesh

Ngoài ra tại sao TCNT0 được so sánh với 255 để xem nó có nhỏ hơn 255 không? Sau cli () nếu TCNT0 đạt 255 thì chuyện gì sẽ xảy ra?
Rajesh

8

Nó không sai khi sử dụng millis()hoặc micros()trong một thói quen gián đoạn.

Đó sai lầm khi sử dụng chúng không chính xác.

Điều chính ở đây là trong khi bạn đang trong một thói quen gián đoạn "đồng hồ không tích tắc". millis()micros()sẽ không thay đổi (tốt, micros()ban đầu sẽ, nhưng một khi nó đi qua điểm mili giây ma thuật trong đó yêu cầu một phần nghìn giây thì tất cả sẽ sụp đổ.)

Vì vậy, bạn chắc chắn có thể gọi millis()hoặc micros()tìm hiểu thời gian hiện tại trong ISR của mình, nhưng đừng hy vọng thời gian đó sẽ thay đổi.

Đó là sự thiếu thay đổi trong thời gian đang được cảnh báo trong trích dẫn bạn cung cấp. delay()dựa vào millis()thay đổi để biết bao nhiêu thời gian đã trôi qua. Vì nó không thay đổi delay()không bao giờ có thể kết thúc.

Vì vậy, về cơ bản millis()micros()sẽ cho bạn biết thời gian khi ISR ​​của bạn được gọi bất kể khi nào trong ISR của bạn, bạn sử dụng chúng.


3
Không, micros()cập nhật. Nó luôn luôn đọc thanh ghi hẹn giờ phần cứng.
Nick Gammon

4

Các cụm từ được trích dẫn không phải là một cảnh báo, nó chỉ là một tuyên bố về cách mọi thứ hoạt động.

Về bản chất, không có gì sai khi sử dụng millis()hoặc micros()trong một thói quen ngắt được viết đúng.

Mặt khác, làm bất cứ điều gì trong một thói quen ngắt được viết không đúng là định nghĩa sai.

Một thói quen gián đoạn mất hơn một vài micro giây để thực hiện công việc của nó, trong nhiều khả năng, được viết không đúng cách.

Tóm lại: Một thói quen ngắt được viết đúng sẽ không gây ra hoặc gặp phải vấn đề với millis()hoặc micros().

Chỉnh sửa: Liên quan đến việc tại sao micros () "bắt đầu hoạt động thất thường", như được giải thích trong một cuộc kiểm tra trên trang web của chức năng Arduino micros , micros()mã trên một Uno bình thường có chức năng tương đương với

unsigned long micros() {
  return((timer0_overflow_count << 8) + TCNT0)*(64/16);
}

Điều này trả về một chiều dài không dấu bốn byte bao gồm ba byte thấp nhất từ timer0_overflow_countvà một byte từ thanh ghi đếm bộ đếm thời gian-0.

timer0_overflow_countđược tăng lên khoảng một lần mỗi mili giây bởi TIMER0_OVF_vecttrình xử lý ngắt, như được giải thích trong một cuộc kiểm tra của trang web hàm arduino millis .

Trước khi một trình xử lý ngắt bắt đầu, phần cứng AVR sẽ vô hiệu hóa các ngắt. Nếu (ví dụ) một trình xử lý ngắt đã chạy trong năm mili giây mà các ngắt vẫn bị vô hiệu hóa, thì ít nhất bốn lần tràn 0 hẹn giờ sẽ bị bỏ qua. [Các ngắt được viết bằng mã C trong hệ thống Arduino không được phát hành lại (có khả năng xử lý chính xác nhiều lần thực thi chồng chéo trong cùng một trình xử lý) nhưng người ta có thể viết một trình xử lý ngôn ngữ lắp ráp reentrant mà reenables bị gián đoạn trước khi nó bắt đầu một quá trình tốn thời gian.]

Nói cách khác, bộ đếm thời gian tràn ra không được xếp chồng lên trên; Bất cứ khi nào xảy ra tràn trước khi ngắt từ tràn trước đó, bộ millis()đếm sẽ mất một phần nghìn giây và timer0_overflow_countlần lượt sự khác biệt cũng bị micros()sai bởi một phần nghìn giây.

Liên quan đến việc rút ngắn thời gian xử lý gián đoạn trên 500 giây, để ngăn chặn việc ngắt thời gian quá lâu, bạn có thể tăng lên dưới 1024 (s (ví dụ 1020) s) và millis()vẫn hoạt động, hầu hết thời gian. Tuy nhiên, tôi coi một trình xử lý ngắt mất hơn 5 as là một kẻ chậm chạp, hơn 10 as là lười biếng, hơn 20 as như ốc sên.


Bạn có thể giải thích rõ hơn về "cách mọi thứ hoạt động", đặc biệt là tại sao micros()"bắt đầu cư xử thất thường"? Và ý của bạn là "thói quen ngắt được viết đúng" là gì? Tôi giả sử nó có nghĩa là "ngắn hơn 500us" (để ngăn chặn việc ngắt bộ hẹn giờ quá lâu), "sử dụng các biến dễ bay hơi để liên lạc" và "không gọi mã thư viện" càng nhiều càng tốt, còn gì nữa không?
Petr Pudlák

@ PetrPudlák, xem chỉnh sửa
James Waldby - jwpat7
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.