ซึ่งเราจะเขียนถึงระดับรีจิสเตอร์ เพื่อให้เข้าใจถึงรายละเอียด และสามารถประยุกต์ใช้ทำอย่างอื่นได้ แต่ก็จะมีไลบรารี่ให้ดาวน์โหลดเช่นกันสำหรับผู้ที่ต้องการความสะดวกในการเขียนโคีด(ดูเพิ่มเติม https://playground.arduino.cc/Code/Timer1)
สำหรับโค๊ดตัวอย่างทั้งหมดที่ใช้ในบทความจะอยู่ที่
https://github.com/Menginventor/Timer1_example
ถ้าพร้อมแล้วมาเริ่มกันเลยครับ
Timer/Counter เป็นส่วนประกอบหนึ่งของไมโครคอนโทรลเลอร์ มีหน้าที่จัดการเกี่ยวกับการทำงานที่เป็นคาบเวลา (interval) โดยการทำงานดังกล่าว จะทำงานด้วยฮาร์ดแวร์และเป็นอิสระกับการประมวลผล จึงช่วยลดภาระการประมวลผลและมีความแม่นยำมากกว่าการทำงานด้วยซอฟแวร์
รูปแบบการใช้งาน Timer/Counter
การใช้งาน Timer สามารถแบ่งเป็น 4 รูปแบบคือ1. Timer Interrupt คือการเรียนฟังก์ชันใดๆ ทุกๆคาบเวลาที่กำหนด
2. สร้างสัญญาณ PWM
3. จับคาบเวลาของสัญญาณภายนอก
4. นับจำนวนลูกคลื่นจากสัญญาณภายนอก
ซึ่งในบทความนี้เราจะพูดถึงการใช้งาน Timer Interrupt เป็นหลัก โดยหัวข้ออื่นๆจะพูดในโอกาสต่อๆไป
Timer/Counterใน ATMEGA328 (Arduino)
ใน ATMEGA328 ประกอบด้วย Timer/Counter 3 ตัว ซึ่งที่รายระเอียดดังนี้1. Timer0 (8-bit) ใช้สำหรับฟังก์ชัน millis() , micros() , delay() และสร้างสัญญาณ PWM
บน pin 5,6
2. Timer1 (16-bit) สร้างสัญญาณ PWM บน pin 9,10 และนิยมใช้กับไลบรารี่อื่นๆ เช่น Servo.h
3. Timer2 (8-bit) สร้างสัญญาณ PWM บน pin 3,11
ซึ่ง Timer ตัวหนึ่ง สามารถใช้กับงานได้เพียงอย่างเดียวเท่านั้น เช่นถ้าใช้ไลบรารี่ Servo ซึ่งเรียกใช้ Timer1 ก็จะทำให้ใช้ PWM pin 9 , 10 ไม่ได้ หรือถ้าเราใช้ Timer1 ใน Timer Interrupt ก็จะใช้งานทั้งไลบรารี่ Servo และ PWM pin 9 , 10 ไม่ได้ (ยกเว้น Timer0 ที่ถูกตั้งค่า configuration ให้ทำงานตามข้อ 1 ได้ทั้งหมดพร้อมกับ และห้ามแก้ไข configuration เด็ดขาด)
![]() | ||||
ภาพจาก https://playground.arduino.cc/Learning/PortManipulation |
สังเกตว่า PWM pin จะมี OCnX กำกับไว้ทั้งหมด เช่น OC0A ที่ pin 6 คือ
"Timer/Counter 0" , "Output Compare Match" , "Output" , "A"
ส่วนประกอบของ Timer1
1. Counter
Counter ก็คือตัวนับจำนวน
- จะนับจำนวนของสัญญาณที่จ่ายทาง Count
- สามารถกำหนดทิศทางการนับ (นับขึ้นนับลง) ด้วยสัญญาณ Direction
- สามารถรีเซ็ตกลับเป็น 0 ได้ด้วยสัญญาณ Clear
โดยไมโครคอนโทรลเลอร์ สามารถอ่าน และเขียนค่า Counterใหม่ได้โดยใช้รีจิสเตอร์ TCNTx ซึ่งในที่นี้คือ TCNT1 (สำหรับ Timer1) มีขนาด 16-Bit หมายความว่า TCNT1 สามารถเริ่มนับตั้งแต่ 0 ถึง 65535 แล้วจึงโอเวอร์โฟลว์กลับมาเป็น 0 วนเวียนเช่นนี้
2. Clock Select
Clock select คือตัวเลือกสัญญาณที่จะนำมานับ สามารถเลือกได้โดยการกำหนดบิทCS (Clock Select) ได้แก่ CS10 , CS11 , CS12 (นั่นคือ Clock Select สำหรับ Timer1 บิทที่ 0 ,1 ,2 ตามลำดับ) โดยสามารถกำหนดได้ดังตาราง
สัญญาณที่นำมานับนั้น มาจาก 2 แหล่งคือ จาก System Clock ( CPU Clock) และ External Clock Source ถ้าเราเลือกใช้ System Clock เราสามารถเลือกใช้ pre-scaling ซึ่งเป็นการหารความถี่ได้ ดังที่แสดงในตาราง และถ้าเราเลือกใช้แหล่งสัญญาณจากภายนอก (External Clock Source ) เราสามารถเลือกได้ว่า จะให้ค่าใน Counter เปลี่ยนแปลงที่ขอบขึ้น หรือขอบลงของสัญญาณ
3. Control Logic
Control Logic จะเป็นตัวควบคุมรูปแบบการนับ ซึ่งจะมีผลมากในการสร้างสัญญาณ PWM โดยสามารถตั้งค่าการทำงานโดยกำหนดบิท WGM (Waveform Generation Mode) ตามตารางที่แสดงนี้
![]() |
Note : Bottom = 0 , Max = 0xFF (65535) |
ตัวอย่างที่ 1 (EX1)
จะเป็นการอ่านค่าจาก Counter มาแสดงที่ Serial Monitor โดยจะมีการ Set Register TCCR1 ซึ่งแบ่งเป็น A และ B โดยมีรายละเอียดดังนี้สำหรับบิท COM1A1,COM1A0,COM1B1,COM1B0 จะเป็นการกำหนด Compare Output Mode ซึ่งใช้ในการสร้าง PWM ดังนั้นจะยังไม่กล่าวถึงในบทความนี้ โดยการ set ทุกบิทข้างต้นให้เป็น 0 จะเป็นการกำหนดให้ OC1A และ OC1B ไม่เชื่อมต่อกับ Pin 9,10 ทำให้สามารถใช้ Pin 9,10 ในการทำงานอื่นๆได้
สำหรับบิท ICNC1 และ ICES1 จะถูกใช้ใน Input Capture Mode จึงตั้งให้เป็นค่า 0
เนื่องจากเราใช้งาน Timer ใน Normal Mode (นับไปเรื่อยๆ) ดังนั้น WGM จะถูกตั้งเป็น 0 ทั้งหมด
เมื่อเราตรวจสอบจากตาราง Waveform Generation Mode โดยเมื่อเราเลือกใช้ Mode 0 จะพบว่าค่าสูงสุดจะมีค่า (0xFFFF) = 65535 และเกิดการ Timer Overflow Interrupt ที่ค่า Max ก็คือ 65535 เช่นกัน
การเลือก Pre-Scale
เลือกจากความละเอียดของเวลา และ ความยาวช่วงเวลา ในตัวอย่างนี้เลือก (System clock ÷ 8) จะได้ความถี่เท่ากับ 16 MHz ÷ 8 = 2 MHz ดังนั้นคาบเวลาของสัญญาณ clk จะเท่ากับ 1/2 MHz จะได้ 0.5 Microseconds และนับเวลาได้สูงสุด 32.767 Milliseconds ต่อการ Overflow1 รอบ ดังนั้นจึงทำการเซ็ต TCCR1A = 0 และ TCCR1B = (1 << CS11)
ผลการทดลอง
จากผลการทดลองจะเห็นว่าค่า TCNT1 วิ่งอยู่ในช่วง 0 ถึง 65535 (เนื่องจากเรา print อยู่ใน loop ทำให้ไม่เห็นทุกการเปลี่ยนแปลง เพราะค่า TCNT วิ่งเร็วจนแสดงผลไม่ทัน อาจจะลองเพิ่มค่า pre-scaler เพื่อดูการเปลี่ยนแปลง)
ตัวอย่างที่ 2 (EX2)
ผลการทดลอง
จะเห็นว่าแต่ละครั้งที่ Overflow ค่า micros() จะห่างกัน 32768 microseconds ตามที่เราคำนวณตัวอย่างที่ 3 (EX3)
จะเห็นว่ามาการเซ็ท WGM12 เป็น 1 ทำให้ Waveform Generation Mode ทำงานใน Mode 4 จากการตรวจสอบตารางจะพบว่า Counter จะมีค่าสูงสุดเท่ากับ OCR1A (Output Compare Register Timer 1 , A) ซึ่งเป็น Register 16-bit ให้เรากำหนดค่าได้ตามต้องการ หลังจากที่ Counter มาถึงค่าที่กำหนด ค่าจะกลับไปเป็น 0 โดยอัตโนมัติ ตามชื่อ CTC (Clear To Compare) Modeจากนั้นเราเซ็ท OCIE1A (Output Compare Interrupt Enable) เป็น 1 ทำให้จะเกิดการอินเทอร์รัพ เรียก ISR ทุกครั้งที่ค่า Counter เท่ากับ OCR1A
จากการตั้งค่าทั้งสองนี้ เราจะได้ Interrupt ที่สามารถกำหนดคาบเวลาได้ตั้งแต่ 0 - 32768 Microseconds โดยมีความละเอียด 0.5 Microseconds ตามที่เราได้กำหนดไว้ใน Pre-scale โดยคาบเวลาในการอินเทอร์รัพแต่ละครับนั้น สามารถคำนวณได้จาก usToTicks(Time) - 1 (ส่วนที่ต้อง - 1 เพราะในการนับ 5 ครั้ง เรานับแค่ 0 - 4 ดังนั้นเราจึงเปรียบเทียบแค่ตัวที่ 4 นั่นคือ 5 - 1 นั้นเอง)
ผลการทดลอง
จากผลการทดลองจะเห็นว่าแต่ละครั้งที่มีการ Interrupt จะมีคาบเวลาเท่ากันคือ 25000 Microseconds (จะมีบางครั้งที่กลายเป็น 24996 , 25004 แต่ก็เฉลี่ยได้เท่าเดิม อาจจะเกิดจากการเหลือมของการจับเวลา) จากตรงนี้เราสามารถสร้าง Timer Interrupt ได้แล้ว ในคาบเวลาไม่เกิน 32768 Microseconds
ตัวอย่างที่ 4 (EX4)
แนวคิดคือ การ Overflow หนึ่งครั้ง เป็นเวลา 32768 Microseconds ถ้าต้องการเวลามากกว่านี้ ก็ให้โอเวอร์โฟลว์จนกว่าจะถึงค่าที่ต้องการแล้วจึงใช้ Output Compare จัดการกับเศษของคาบเวลาที่เหลือ
ไม่มีความคิดเห็น:
แสดงความคิดเห็น