บทความนี้กล่าวถึงการใช้ตัวฮาร์ดแวร์ตั้งเวลาหรือไทม์เมอร์ (Timer) ทั้ง 4 ของ ESP32 เพื่อใช้สำหรับการให้โปรแกรมทำงานเมื่อเป็นตามค่าเวลาที่กำหนดไว้ ความแตกต่างระหว่างการใช้ไทม์เมอร์กับการหน่วงเวลา time.sleep()/time.sleep_ms()/time.sleep_us() คือ การหน่วงเวลาคือการวนรอบเพื่อให้หน่วยประมวลผลเสียเวลาไปการวนรอบเพื่อให้ครบกับระยะเวลาที่กำหนด ขณะที่ไทม์เมอร์ใช้หลักการให้ฟังก์ชันทำงานทุกครั้งเมื่อถึงคาบเวลาที่กำหนดไว้ ดังนั้น ขณะที่ไทม์เมอร์ไม่ได้เรียกฟังก์ชันให้ทำงาน หน่วยประมวลผลมีเวลาเหลือหรือค่าว่างงาน (idle time) สำหรับประมวลผลอื่น ๆ ได้ และมีความแตกต่างกับการขัดจังหวะหรืออินเทอร์รัพต์ตรงที่เป็นการขัดจังหวะด้วยตัวตั้งเวลาแทนการขัดจังหวะจากสถานะของขาที่เชื่อมต่อกับวงจรภายนอก
ไทม์เมอร์
ไทม์เมอร์ หรือตัวตั้งเวลาของ ESP32 มีฮาร์แวร์ไทม์เมอร์จำนวน 2 กลุ่ม ในแต่ละกลุ่มประกอบไปด้วยไทม์เมอร์ฮาร์ดแวร์ย่อยอีก 2 ตัว โดยทั้ง 4 เป็นไทม์เมอร์แบบ 64 บิตที่ทำงานแบบ 16 บิต pre-scalars และตัวนับค่าขึ้น/ลงขนาด 64 บิต ที่รองรับการทำงานรีโหลดค่าอัตโนมัติ
การเรียกใช้คลาสไทม์เมอร์สำหรับไมโครคอนโทรลเลอร์ ESP32 ทำด้วยการเรียกใช้คลาส Timer ที่อยู่ภายใต้คลาส machine ดังนี้
from machine import Timer
การสร้างวัตถุตัวตั้งเวลามีรูปแบบของการสร้างดังนี้ โดย หมายเลขไทม์เมอร์ คือตัวเลขลำดับของไทม์เมอร์ที่ต้องการใช้งาน ซึ่งได้แก่ค่า 0, 1, 2 และ 3
วัตถุ = Timer( หมายเลขไทม์เมอร์ )
เมื่อมีวัตถุประเภทไทม์เมอร์ที่พร้อมใช้งานให้กำหนดค่าการทำงานของตัวตั้งเวลานี้ด้วยคำสั่ง init( ) ดังรูปแบบการใช้งานต่อไปนี้
วัตถุ.init( period, mode, callback )
โดยที่
- period คือ ค่าคาบเวลาการทำงานของตัวตั้งเวลา มีหน่วยเป็นมิลลิวินาที (millisecond)
- mode คือ โหมดการทำงานของตัวตั้งเวลา ซึ่งมี 2 โหมดทำงานดังนี้
- Timer.ONE_SHOT สำหรับถูกเรียกทำงานเพียงครั้งเดียว
- Timer.PERIODIC สำหรับให้เรียกใช้ฟังก์ชัน callback ทุกคาบเวลาที่กำหนด
- callback คือ ฟังก์ชันที่ถูกเรียกเมื่อไทม์เมอร์ทำงาน
เมื่อต้องการยกเลิกการถือครองตัวตั้งเวลาที่สร้างไว้สามารถกระทำได้ด้วยคำสั่งดังต่อไปนี้
วัตถุ.deinit( )
ตัวอย่างโปรแกรม demoTimer0.py เป็นการให้ลำโพงเปล่งเสียง 1 ครั้งเมื่อครบเวลาที่ตั้งไว้ 2 วินาที โดยเชื่อมต่อลำโพงเข้ากับขา GPIO25 โดยโปรแกรมจะมีเสียงบี๊ป 1 ครั้งถึงแม้มีการหน่วงเวลา 5 วินาที
###########################################
# demoTimer0.py
# (C) 2021, JarutEx
# https://www.jarutex.com
###########################################
from machine import Timer
from machine import Pin
from time import sleep_ms
def beep(x):
spkPin = Pin( 25, Pin.OUT )
spkPin.on()
sleep_ms(50)
spkPin.off()
try:
tmr0 = Timer(0)
except:
print("cannot construct Timer(0)!")
try:
tmr0.init( period=2000, mode=Timer.ONE_SHOT, callback=beep)
except:
print("cannot init()!")
sleep_ms(5000)
if (tmr0):
tmr0.deinit()
ตัวอย่างโปรแกรม demoTimer1.py เป็นการเปลี่ยนรูปแบบของการเปล่งเสียงให้เกิดขึ้นทุก ๆ 2 วินาทีโดยการกำหนดโหมดเป็น Timer.PERIODIC และโปรแกรมมีเสียงบี๊ป 2 ครั้ง คือ ครั้งแรกเมื่อผ่านไป 2 วินาที ครั้งที่ 2 เมื่อเข้าสู่วินาทีที่ 4 หลังจากนั้นอีก 1 วินาทีเป็นสิ้นสุดการหน่วงเวลา 5 วินาที และจบโปรแกรม
###########################################
# demoTimer1.py
# (C) 2021, JarutEx
# https://www.jarutex.com
###########################################
from machine import Timer
from machine import Pin
from time import sleep_ms
def beep(x):
spkPin = Pin( 25, Pin.OUT )
spkPin.on()
sleep_ms(50)
spkPin.off()
try:
tmr0 = Timer(0)
except:
print("cannot construct Timer(0)!")
try:
tmr0.init( period=2000, mode=Timer.PERIODIC, callback=beep)
except:
print("cannot init()!")
sleep_ms(5000)
if (tmr0):
tmr0.deinit()
สรุป
จากบทความนี้จะพบว่า การนำไทม์เมอร์มาใช้งานสามารถตั้งค่าให้ฟังก์ชันทำงานตามคายเวลาที่กำหนดหรือทำงานเมื่อถึงเวลาที่ตั้งไว้ ซึ่งถือว่าเป็นการขัดจังหวะประเภทหนึ่งแต่เป็นการขัดจังหวะที่เกิดจากแหล่งต้นทางที่เป็นฮาร์ดแวร์ไทม์เมอร์ซึ่งมีให้ใช้งาน 4 ตัว ดังนั้น เมื่อได้เรียนรู้วิธีการสั่งงานไมโครคอนโทรลเลอร์ในหลากหลายรูปแบบเพื่อให้ทำงานในหลายลักษณะจะทำให้ผู้เขียนโปรแกรมใช้งานชิพนั้นได้มีประสิทธิภาพมากขึ้น สุดท้ายขอให้สนุกกับการเขียนโปรแกรมครับ
แหล่งอ้างอิง
- Espressif. “General Purpose Timer”
- Micropython, “Quick Reference for ESP8266”
- Micropython. “Quick Reference for ESP32”
- Micropython, “class Timer – control hardware timers”
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-07-15, 2021-07-16, 2021-10-20