time --- các hàm liên quan đến thời gian

Mô-đun time cung cấp các hàm để lấy thời gian và ngày tháng hiện tại, đo khoảng thời gian và tạo độ trễ.

Epoch Thời gian: Các OpenMV Cams dựa trên Alif và i.MX RT sử dụng epoch POSIX là 1970-01-01 00:00:00 UTC. Các OpenMV Cams dựa trên STM32 sử dụng epoch là 2000-01-01 00:00:00 UTC. Năm epoch có thể xác định tại thời điểm chạy với gmtime(0)[0].

Duy trì ngày/giờ lịch thực tế: Điều này yêu cầu Real Time Clock (RTC). Trên OpenMV Cam, thời gian hệ thống được cung cấp bởi đối tượng machine.RTC. Thời gian lịch hiện tại có thể được thiết lập với machine.RTC().datetime(tuple) và được duy trì bởi một trong các cách sau:

  • Pin dự phòng (thành phần tùy chọn trên một số OpenMV Cams).

  • Giao thức thời gian qua mạng như ntptime (yêu cầu kết nối mạng).

  • Cài đặt thủ công mỗi lần khởi động. RTC thường được duy trì qua các lần soft reset, nhưng sẽ mất khi mất nguồn điện trừ khi có pin dự phòng.

Nếu thời gian lịch không được duy trì, các hàm dưới đây tham chiếu đến thời gian tuyệt đối hiện tại sẽ không hoạt động như mong đợi.

Hàm

time.gmtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]
time.localtime(secs: int | None = None) Tuple[int, int, int, int, int, int, int, int]

Chuyển đổi thời gian secs được biểu thị bằng giây kể từ Epoch (xem ở trên) thành một tuple 8 phần tử chứa: (year, month, mday, hour, minute, second, weekday, yearday) Nếu secs không được cung cấp hoặc là None, thì thời gian hiện tại từ RTC sẽ được sử dụng.

Hàm gmtime() trả về tuple ngày-giờ theo UTC, và localtime() trả về tuple ngày-giờ theo giờ địa phương.

Định dạng của các mục trong tuple 8 phần tử là:

  • year bao gồm thế kỷ (ví dụ: 2014).

  • month là 1-12

  • mday là 1-31

  • hour là 0-23

  • minute là 0-59

  • second là 0-59

  • weekday là 0-6 cho Thứ Hai-Chủ Nhật

  • yearday là 1-366

time.mktime(date_time_tuple: Tuple[int, int, int, int, int, int, int, int]) int

Đây là hàm nghịch đảo của localtime. Đối số của nó là một tuple đầy đủ 8 phần tử biểu thị thời gian theo localtime. Nó trả về một số nguyên là số giây kể từ epoch thời gian.

time.sleep(seconds: float) None

Ngủ trong số giây đã cho. seconds có thể là số thực dấu phẩy động để ngủ trong một phần giây. Để có độ trễ chi tiết hơn hoặc chỉ số nguyên, hãy dùng các hàm sleep_ms()sleep_us().

Gọi sleep(), bao gồm sleep(0) được đảm bảo sẽ gọi các hàm callback đang chờ xử lý.

time.sleep_ms(ms: int) None

Trễ trong số millisecond đã cho, nên là dương hoặc 0.

Hàm này sẽ trễ ít nhất số millisecond đã cho, nhưng có thể mất lâu hơn nếu có các xử lý khác cần thực hiện, ví dụ như các interrupt handler hoặc các luồng khác. Truyền vào 0 cho ms vẫn cho phép các xử lý khác này xảy ra. Dùng sleep_us() để có độ trễ chính xác hơn.

Gọi sleep_ms(), bao gồm sleep_ms(0) được đảm bảo sẽ gọi các hàm callback đang chờ xử lý.

time.sleep_us(us: int) None

Trễ trong số microsecond đã cho, nên là dương hoặc 0.

Hàm này cố gắng cung cấp độ trễ chính xác ít nhất us microsecond, nhưng có thể mất lâu hơn nếu hệ thống có các xử lý ưu tiên cao hơn cần thực hiện.

time.ticks_ms() int

Trả về bộ đếm millisecond tăng dần với điểm tham chiếu tùy ý, cuộn vòng sau một giá trị nào đó.

Giá trị cuộn vòng không được hiển thị rõ ràng, nhưng chúng ta sẽ gọi nó là TICKS_MAX để đơn giản hóa việc thảo luận. Chu kỳ của các giá trị là TICKS_PERIOD = TICKS_MAX + 1. TICKS_PERIOD được đảm bảo là lũy thừa của hai, nhưng có thể khác nhau giữa các port. Giá trị chu kỳ giống nhau được sử dụng cho tất cả các hàm ticks_ms(), ticks_us(), ticks_cpu() (để đơn giản). Vì vậy, các hàm này sẽ trả về giá trị trong khoảng [0 .. TICKS_MAX], toàn phần, tổng cộng TICKS_PERIOD giá trị. Lưu ý rằng chỉ các giá trị không âm được sử dụng. Trong hầu hết các trường hợp, bạn nên coi các giá trị được trả về bởi các hàm này là không trong suốt. Các thao tác duy nhất có thể thực hiện là các hàm ticks_diff()ticks_add() được mô tả bên dưới.

Lưu ý: Thực hiện các phép toán toán học tiêu chuẩn (+, -) hoặc các toán tử quan hệ (<, <=, >, >=) trực tiếp trên các giá trị này sẽ dẫn đến kết quả không hợp lệ. Thực hiện các phép toán toán học và sau đó truyền kết quả làm đối số cho ticks_diff() hoặc ticks_add() cũng sẽ dẫn đến kết quả không hợp lệ từ các hàm sau.

time.ticks_us() int

Tương tự như ticks_ms() ở trên, nhưng tính bằng microsecond.

time.ticks_cpu() int

Tương tự như ticks_ms()ticks_us(), nhưng với độ phân giải cao nhất có thể trong hệ thống. Thường đây là clock của CPU, và đó là lý do hàm được đặt tên như vậy. Nhưng không nhất thiết phải là clock CPU, một nguồn thời gian khác có sẵn trong hệ thống (ví dụ: bộ định thời độ phân giải cao) có thể được sử dụng thay thế. Đơn vị thời gian chính xác (độ phân giải) của hàm này không được chỉ định ở cấp mô-đun time, nhưng tài liệu cho một port cụ thể có thể cung cấp thêm thông tin chi tiết hơn. Hàm này được dùng cho việc đo hiệu suất rất chi tiết hoặc các vòng lặp thời gian thực rất chặt chẽ. Tránh dùng nó trong mã portable. Nó khả dụng trên tất cả các OpenMV Cams.

time.ticks_add(ticks: int, delta: int) int

Dịch chuyển giá trị ticks theo một số đã cho, có thể là dương hoặc âm. Cho một giá trị ticks, hàm này cho phép tính giá trị ticks delta ticks trước hoặc sau nó, theo định nghĩa số học modular của các giá trị tick (xem ticks_ms() ở trên). Tham số ticks phải là kết quả trực tiếp của lời gọi đến các hàm ticks_ms(), ticks_us(), hoặc ticks_cpu() (hoặc từ lời gọi trước đó đến ticks_add()). Tuy nhiên, delta có thể là bất kỳ số nguyên hoặc biểu thức số nào. ticks_add() hữu ích để tính thời hạn cho các sự kiện/tác vụ. (Lưu ý: bạn phải dùng hàm ticks_diff() để làm việc với thời hạn.)

Ví dụ:

# Find out what ticks value there was 100ms ago
print(ticks_add(time.ticks_ms(), -100))

# Calculate deadline for operation and test for it
deadline = ticks_add(time.ticks_ms(), 200)
while ticks_diff(deadline, time.ticks_ms()) > 0:
    do_a_little_of_something()

# Find out TICKS_MAX used by this port
print(ticks_add(0, -1))
time.ticks_diff(ticks1: int, ticks2: int) int

Đo sự khác biệt ticks giữa các giá trị trả về từ các hàm ticks_ms(), ticks_us(), hoặc ticks_cpu(), dưới dạng giá trị có dấu có thể cuộn vòng.

Thứ tự đối số giống như toán tử trừ, ticks_diff(ticks1, ticks2) có cùng nghĩa với ticks1 - ticks2. Tuy nhiên, các giá trị được trả về bởi ticks_ms(), v.v. có thể cuộn vòng, vì vậy việc sử dụng phép trừ trực tiếp trên chúng sẽ tạo ra kết quả không đúng. Đó là lý do ticks_diff() cần thiết, nó thực hiện số học modular (hay cụ thể hơn là số học vòng) để tạo ra kết quả đúng ngay cả với các giá trị cuộn vòng (miễn là chúng không quá xa nhau, xem bên dưới). Hàm trả về giá trị có dấu trong khoảng [-TICKS_PERIOD/2 .. TICKS_PERIOD/2-1] (đó là định nghĩa khoảng điển hình cho số nguyên nhị phân có dấu theo two's-complement). Nếu kết quả âm, nghĩa là ticks1 xảy ra trước ticks2 theo thời gian. Ngược lại, nghĩa là ticks1 xảy ra sau ticks2. Điều này chỉ đúng khi ticks1ticks2 cách nhau không quá TICKS_PERIOD/2-1 ticks. Nếu điều đó không thỏa mãn, kết quả không đúng sẽ được trả về. Cụ thể, nếu hai giá trị tick cách nhau TICKS_PERIOD/2-1 ticks, giá trị đó sẽ được hàm trả về. Tuy nhiên, nếu TICKS_PERIOD/2 ticks thực tế đã qua giữa chúng, hàm sẽ trả về -TICKS_PERIOD/2 thay thế, tức là giá trị kết quả sẽ cuộn vòng sang khoảng âm của các giá trị có thể.

Lý do không chính thức của các ràng buộc trên: Giả sử bạn bị nhốt trong một phòng không có phương tiện nào để theo dõi thời gian trôi qua ngoài một chiếc đồng hồ 12 vạch tiêu chuẩn. Nếu bạn nhìn vào mặt đồng hồ bây giờ, và không nhìn lại trong 13 giờ nữa (ví dụ: nếu bạn ngủ lâu), thì khi bạn cuối cùng nhìn lại, có vẻ như chỉ có 1 giờ trôi qua. Để tránh nhầm lẫn này, hãy nhìn đồng hồ thường xuyên. Ứng dụng của bạn cũng nên làm như vậy. Phép ẩn dụ "ngủ quá lâu" cũng ánh xạ trực tiếp vào hành vi ứng dụng: đừng để ứng dụng của bạn chạy bất kỳ tác vụ đơn lẻ nào quá lâu. Chạy các tác vụ theo từng bước và theo dõi thời gian ở giữa.

ticks_diff() được thiết kế để đáp ứng các mẫu sử dụng đa dạng, bao gồm:

  • Polling với timeout. Trong trường hợp này, thứ tự các sự kiện được biết trước, và bạn chỉ xử lý kết quả dương của ticks_diff()

    # Wait for GPIO pin to be asserted, but at most 500us
    start = time.ticks_us()
    while pin.value() == 0:
        if time.ticks_diff(time.ticks_us(), start) > 500:
            raise TimeoutError
    
  • Lập lịch sự kiện. Trong trường hợp này, kết quả ticks_diff() có thể âm nếu một sự kiện bị trễ:

    # This code snippet is not optimized
    now = time.ticks_ms()
    scheduled_time = task.scheduled_time()
    if ticks_diff(scheduled_time, now) > 0:
        print("Too early, let's nap")
        sleep_ms(ticks_diff(scheduled_time, now))
        task.run()
    elif ticks_diff(scheduled_time, now) == 0:
        print("Right at time!")
        task.run()
    elif ticks_diff(scheduled_time, now) < 0:
        print("Oops, running late, tell task to run faster!")
        task.run(run_faster=true)
    

Lưu ý: Đừng truyền các giá trị time() cho ticks_diff(), bạn nên sử dụng các phép toán toán học thông thường trên chúng. Nhưng lưu ý rằng time() có thể (và sẽ) cũng tràn số. Điều này được biết đến là https://en.wikipedia.org/wiki/Year_2038_problem .

time.time() int

Trả về số giây, dưới dạng số nguyên, kể từ Epoch, giả sử rằng RTC cơ bản được cài đặt và duy trì như mô tả ở trên. Nếu RTC không được cài đặt, hàm này trả về số giây kể từ điểm tham chiếu thời gian đặc thù của port (đối với các board nhúng không có RTC có pin dự phòng, thường là kể từ khi khởi động hoặc reset). Nếu bạn muốn phát triển ứng dụng MicroPython portable, bạn không nên dựa vào hàm này để cung cấp độ chính xác cao hơn giây. Nếu bạn cần độ chính xác cao hơn, timestamp tuyệt đối, hãy dùng time_ns(). Nếu thời gian tương đối chấp nhận được thì dùng các hàm ticks_ms()ticks_us(). Nếu bạn cần thời gian lịch, gmtime() hoặc localtime() không có đối số là lựa chọn tốt hơn.

Khác biệt so với CPython

Trong CPython, hàm này trả về số giây kể từ Unix epoch (1970-01-01 00:00 UTC) dưới dạng giá trị dấu phẩy động, thường với độ chính xác microsecond. Trên OpenMV Cam, nó trả về một số nguyên với độ chính xác một giây -- phần cứng không thể biểu diễn cả khoảng thời gian dài lẫn độ chính xác dưới giây trong kiểu float -- và epoch khác nhau theo board (xem Epoch Thời gian ở trên). Nếu không có RTC có pin dự phòng đã được cài đặt, nó thay vào đó đếm giây kể từ khi khởi động/reset.

time.time_ns() int

Tương tự như time() nhưng trả về nanosecond kể từ Epoch, dưới dạng số nguyên (thường là số nguyên lớn, vì vậy sẽ cấp phát trên heap).

Hàm khởi tạo

class time.clock

Trả về một đối tượng clock.

Phương thức

tick() None

Bắt đầu theo dõi thời gian đã trôi qua.

fps() float

Dừng theo dõi thời gian đã trôi qua và trả về FPS (số khung hình trên giây) hiện tại.

Luôn gọi tick trước khi gọi hàm này.

avg() float

Dừng theo dõi thời gian đã trôi qua và trả về thời gian trôi qua trung bình hiện tại tính bằng millisecond.

Luôn gọi tick trước khi gọi hàm này.

reset() None

Đặt lại đối tượng clock.