3.2. การจับเวลา¶
โมดูล time รวมฟังก์ชันสำหรับการหลับ (หยุดสคริปต์เป็นระยะเวลาที่กำหนด) และสำหรับวัดว่าบางสิ่งใช้เวลานานเท่าใด บนไมโครคอนโทรลเลอร์สิ่งเหล่านี้ไม่ใช่สิ่งที่มีก็ดี แต่เป็นวิธีที่สคริปต์กำหนดจังหวะการโต้ตอบกับโลกภายนอก -- ต้องดึงพินให้สูงนานเท่าใด, รอระหว่างตัวอย่างนานเท่าใด, นานเท่าใดนับตั้งแต่กดปุ่มครั้งล่าสุด
3.2.1. การหลับ¶
ฟังก์ชันหลับสามตัวบล็อกสคริปต์เป็นระยะเวลาที่ร้องขอ:
time.sleep(s)-- หยุดเป็นเวลาsวินาที รับ float ดังนั้นtime.sleep(0.5)รอครึ่งวินาทีtime.sleep_ms(ms)-- หยุดเป็นเวลาmsมิลลิวินาที อาร์กิวเมนต์ต้องเป็นจำนวนเต็มtime.sleep_us(us)-- หยุดเป็นเวลาusไมโครวินาที
import time
print("now")
time.sleep_ms(500)
print("half a second later")
ใช้ time.sleep_ms() สำหรับความต้องการ "รอสักครู่" ทั่วไป และใช้ time.sleep_us() เฉพาะเมื่อต้องการความแม่นยำสูง time.sleep() แบบธรรมดาก็ใช้ได้เช่นกัน แต่ตัวแปรที่รับอาร์กิวเมนต์จำนวนเต็มหลีกเลี่ยงการแปลงทศนิยมและอ่านได้เป็นธรรมชาติมากกว่าสำหรับช่วงเวลาสั้น
Sleep เป็นการเรียกแบบ บล็อก ขณะที่สคริปต์กำลังหลับ จะไม่ทำอะไรอื่น -- ไม่อ่านพิน ไม่ให้บริการ UART ไม่อัปเดต LED ใช้ sleep เมื่อการบล็อกคือสิ่งที่ต้องการ ใช้รูปแบบไม่บล็อกด้านล่างเมื่อไม่ต้องการ
3.2.2. การอ่านนาฬิกา¶
ในการวัดว่าโค้ดบางส่วนใช้เวลานานเท่าใด อ่านนาฬิกาก่อนและหลัง:
time.ticks_ms()-- ค่า tick ปัจจุบันในหน่วยมิลลิวินาทีtime.ticks_us()-- เหมือนกันในหน่วยไมโครวินาที
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_ms() - start
print("took", elapsed, "ms")
วิธีนี้ใช้ได้ ส่วนใหญ่ ตัวนับ tick จะ roll over (ย้อนกลับเป็นศูนย์) หลังจาก tick จำนวนมากแต่จำกัด และการลบแบบธรรมดาข้าม wrap นั้นให้ผลลัพธ์จำนวนลบหรือบวกที่ผิดพลาดอย่างมาก
ตัวนับ tick จะย้อนกลับเป็นศูนย์เมื่อถึงขีดจำกัดจำนวนเต็ม การลบแบบธรรมดาข้าม wrap นั้นผิด¶
3.2.3. ticks_diff¶
เพื่อรับ tick ที่ผ่านไปอย่างถูกต้อง แม้ข้าม wrap ให้ใช้ time.ticks_diff():
import time
start = time.ticks_ms()
do_work()
elapsed = time.ticks_diff(time.ticks_ms(), start)
print("took", elapsed, "ms")
ลำดับอาร์กิวเมนต์คือ ticks_diff(later, earlier) -- นิพจน์อ่านว่า "later อยู่ไกลแค่ไหนหลัง earlier" ผลลัพธ์เป็นจำนวนเต็มมีเครื่องหมาย บวกหมายความว่า later อยู่ในอนาคตจริงๆ ลบหมายความว่าอยู่ในอดีต ฟังก์ชันจัดการ wrap ภายใน
Tip
จับคู่ time.ticks_ms() / time.ticks_us() กับ time.ticks_diff() เสมอ การลบแบบดิบถูกต้อง ส่วนใหญ่ เวลาที่ผิดคือเมื่อสคริปต์ทำงานมาเป็นเวลานาน -- ซึ่งมักเป็นเวลาที่แย่ที่สุดในการดีบักปัญหาการจับเวลา
3.2.4. การจับเวลาแบบไม่บล็อก¶
สคริปต์ไมโครคอนโทรลเลอร์มักมีสิ่งที่ต้องทำ "ในเวลาเดียวกัน" มากกว่าหนึ่งอย่าง: อ่านปุ่ม, กะพริบ LED, poll เซนเซอร์, ให้บริการ UART sleep ไม่เหมาะกับสิ่งนี้ -- ขณะที่ sleep หนึ่งกำลังทำงาน งานอื่นจะหยุดนิ่ง
รูปแบบมาตรฐานคือการเก็บ เส้นตาย ต่องาน และตรวจสอบในแต่ละรอบว่าเส้นตายผ่านไปหรือยัง การตัดสินใจกระทำสร้างบน time.ticks_diff() ไม่ใช่ sleep:
import time
next_blink = time.ticks_ms()
next_poll = time.ticks_ms()
state = False
while True:
now = time.ticks_ms()
if time.ticks_diff(now, next_blink) >= 0:
state = not state
# update LED here
next_blink = time.ticks_add(next_blink, 500)
if time.ticks_diff(now, next_poll) >= 0:
# read sensor here
next_poll = time.ticks_add(next_poll, 100)
LED สลับทุก 500 มิลลิวินาที เซนเซอร์ถูก poll ทุก 100 มิลลิวินาที และไม่มีงานใดบล็อกอีกงาน time.ticks_add() เลื่อนเส้นตายไปข้างหน้าด้วยค่าเพิ่มที่กำหนดโดยไม่ผิดพลาดจาก wrap
รูปแบบนี้ -- ลูปเดียว หลายงานจับเวลา ไม่มี sleep ในตัว -- ปรากฏทุกที่ที่มีโค้ดฮาร์ดแวร์: การ debounce ปุ่มด้วยซอฟต์แวร์, การจัดลำดับมอเตอร์, timeout การอ่าน UART ฟังก์ชันสามตัวเดิม (time.ticks_ms(), time.ticks_diff(), time.ticks_add()) ครอบคลุมทุกกรณี