- ทำไมต้องจับเวลาเมื่อเรามี Delay ()?
- ตัวจับเวลาไมโครคอนโทรลเลอร์ PIC:
- คำอธิบายการเขียนโปรแกรมและการทำงาน:
- แผนภาพวงจรและการจำลอง Proteus:
นี้จะเป็นกวดวิชาที่ห้าในของเราPIC ชุดการสอนซึ่งจะช่วยให้คุณเรียนรู้และใช้จับเวลาใน PIC16F877A ในบทช่วยสอนก่อนหน้านี้เราได้เริ่มต้นด้วย Introduction to PIC และ MPLABX IDE จากนั้นเราจึงเขียนโปรแกรม PIC ตัวแรกของเราเพื่อกะพริบ LED โดยใช้ PIC จากนั้นสร้าง LED Blinking Sequence โดยใช้ฟังก์ชันหน่วงเวลาในไมโครคอนโทรลเลอร์ PIC ตอนนี้ให้เราใช้ไฟ LED กระพริบเดียวกันลำดับซึ่งเราได้ใช้ในฮาร์ดแวร์กวดวิชาก่อนหน้านี้และมีนี้เราจะเรียนรู้วิธีการใช้จับเวลาใน PIC MCU เราเพิ่งเพิ่มปุ่มอีกหนึ่งปุ่มในบอร์ด LED สำหรับบทช่วยสอนนี้ อ่านบทแนะนำเพื่อเรียนรู้เพิ่มเติม
ตัวจับเวลาเป็นส่วนสำคัญอย่างหนึ่งสำหรับโปรแกรมเมอร์แบบฝังตัว ทุกแอปพลิเคชันที่เราออกแบบจะเกี่ยวข้องกับแอปพลิเคชั่นจับเวลาเช่นการเปิดหรือปิดบางอย่างหลังจากช่วงเวลาที่กำหนด โอเค แต่ทำไมเราต้องมีตัวจับเวลาในเมื่อเรามีมาโครหน่วงเวลาอยู่แล้ว (__delay_ms ()) ทำแบบเดียวกัน !!
ทำไมต้องจับเวลาเมื่อเรามี Delay ()?
มาโครการหน่วงเวลาเรียกว่าการหน่วงเวลา "การถ่ายโอนข้อมูล" เพราะระหว่างการทำงานของฟังก์ชั่นความล่าช้า MCU นั่งถ่ายโอนข้อมูลโดยเพียงแค่การสร้างความล่าช้า ในระหว่างกระบวนการนี้ MCU จะไม่สามารถรับฟังค่า ADC หรืออ่านข้อมูลใด ๆ จาก Registers ได้ ดังนั้นจึงไม่แนะนำให้ใช้ฟังก์ชัน Delay ยกเว้นการใช้งานเช่น LED กะพริบซึ่งการหน่วงเวลาไม่จำเป็นต้องแม่นยำหรือนาน
แมโครล่าช้านอกจากนี้ยังมีที่มาสั้น ๆ ต่อไป,
- ค่าของการหน่วงเวลาต้องเป็นค่าคงที่สำหรับมาโครการหน่วงเวลา ไม่สามารถเปลี่ยนแปลงได้ในระหว่างการทำงานของโปรแกรม ดังนั้นจึงยังคงเป็นโปรแกรมเมอร์ที่กำหนดไว้
- การหน่วงเวลาจะไม่แม่นยำเมื่อเทียบกับการใช้ตัวจับเวลา
- ไม่สามารถสร้างค่าความล่าช้าที่ใหญ่ขึ้นโดยใช้มาโครตัวอย่างเช่นมาโครการหน่วงเวลาครึ่งชั่วโมงไม่สามารถสร้างได้ ความล่าช้าสูงสุดที่สามารถใช้ได้ขึ้นอยู่กับ Crystal oscillator ที่ใช้
ตัวจับเวลาไมโครคอนโทรลเลอร์ PIC:
ในทางกายภาพตัวจับเวลาคือการลงทะเบียนที่มีค่าเพิ่มขึ้นอย่างต่อเนื่องเป็น 255 จากนั้นจะเริ่มต้นใหม่อีกครั้ง: 0, 1, 2, 3, 4… 255…. 0, 1, 2, 3…… ฯลฯ
PIC16F877A PIC MCU มีสามตัวจับเวลาโมดูล มีชื่อเป็น Timer0, Timer1 และ Timer2 Timer 0 และ Timer 2 เป็นตัวจับเวลาแบบ 8 บิตและ Timer 1 เป็นตัวจับเวลา 16 บิต ในบทช่วยสอนนี้เราจะใช้ Timer 0 สำหรับแอปพลิเคชันของเรา เมื่อเราเข้าใจ Timer 0 แล้วก็จะทำงานกับ Timer 1 และ Timer 2 ได้ง่ายเช่นกัน
ตัวจับเวลา / ตัวนับโมดูล Timer0 มีคุณสมบัติดังต่อไปนี้:
- ตัวจับเวลา / ตัวนับ 8 บิต
- อ่านและเขียนได้
- โปรแกรม Prescaler ซอฟต์แวร์ 8 บิต
- เลือกนาฬิกาภายในหรือภายนอก
- ขัดจังหวะเมื่อล้นจาก FFh ถึง 00h
- เลือกขอบสำหรับนาฬิกาภายนอก
ในการเริ่มใช้ตัวจับเวลาเราควรทำความเข้าใจกับคำศัพท์ใหม่ ๆ เช่นตัวจับเวลา 8 บิต / 16 บิต, ตัวจับเวลา, ตัวจับเวลา, ตัวจับเวลาขัดจังหวะและ Focs ตอนนี้ให้เราดูความหมายจริงๆ ดังที่ได้กล่าวไว้ก่อนหน้านี้มีทั้งตัวจับเวลาแบบ 8 บิตและ 16 บิตใน PIC MCU ของเราความแตกต่างที่สำคัญระหว่างพวกเขาคือตัวจับเวลา 16 บิตมีความละเอียดที่ดีกว่าตัวจับเวลา 8 บิต
Prescalerเป็นชื่อของส่วนหนึ่งของไมโครคอนโทรลเลอร์ซึ่งแบ่งนาฬิกาออสซิลเลเตอร์ก่อนที่จะถึงลอจิกที่เพิ่มสถานะตัวจับเวลา ช่วงของ id prescaler อยู่ระหว่าง 1 ถึง 256 และสามารถตั้งค่าของ Prescaler ได้โดยใช้ OPTION Register (ค่าเดียวกับที่เราใช้สำหรับตัวต้านทานแบบดึงขึ้น) ตัวอย่างเช่นถ้าค่าของ prescaler เป็น 64 แล้วสำหรับทุก 64 วันชีพจรจับเวลาจะเพิ่มขึ้นโดยที่ 1
เมื่อตัวจับเวลาเพิ่มขึ้นและเมื่อถึงค่าสูงสุด 255 มันจะทริกเกอร์การขัดจังหวะและเริ่มต้นตัวเองเป็น 0 อีกครั้ง การขัดจังหวะนี้เรียกว่า Timer Interrupt การขัดจังหวะนี้จะแจ้งให้ MCU ทราบว่าเวลานี้ผ่านไปแล้ว
FOSC ย่อมาจากความถี่ของ Oscillatorมันเป็นความถี่ของคริสตัลที่ใช้ เวลาที่ใช้ในการลงทะเบียน Timer ขึ้นอยู่กับค่าของ Prescaler และค่าของ Fosc
คำอธิบายการเขียนโปรแกรมและการทำงาน:
ในบทช่วยสอนนี้เราจะตั้งค่าปุ่มสองปุ่มเป็นสองอินพุตและ 8 LED เป็น 8 เอาท์พุท ปุ่มแรกจะใช้เพื่อตั้งค่าการหน่วงเวลา (500ms สำหรับการกดทุกครั้ง) และปุ่มที่สองจะใช้เพื่อเริ่มกะพริบตามลำดับตัวจับเวลา ตัวอย่างเช่นหากกดปุ่มแรกสามครั้ง (500 * 3 = 1500 มิลลิวินาที) การหน่วงเวลาจะถูกตั้งไว้เป็นเวลา 1.5 วินาทีและเมื่อกดปุ่มสองปุ่ม LED แต่ละดวงจะเปิดและปิดด้วยการหน่วงเวลาที่กำหนดไว้ล่วงหน้า ตรวจสอบวิดีโอสาธิตในตอนท้ายของบทช่วยสอนนี้
ขณะนี้มีพื้นฐานเหล่านี้ในใจให้เราดูที่โปรแกรมของเราได้รับในตอนท้ายในส่วนรหัส
ไม่เป็นไรถ้าคุณไม่ได้รับโปรแกรม แต่ถ้าคุณทำ !! ให้คุกกี้ตัวเองและถ่ายโอนโปรแกรมเพื่อเพลิดเพลินกับผลลัพธ์ของคุณ สำหรับคนอื่น ๆ ฉันจะแยกโปรแกรมออกเป็นส่วนที่มีความหมายและอธิบายให้คุณทราบว่าเกิดอะไรขึ้นในแต่ละบล็อก
เช่นเคยสองสามบรรทัดแรกของโค้ดคือการตั้งค่าการกำหนดค่าและไฟล์ส่วนหัวฉันจะไม่อธิบายสิ่งนี้เนื่องจากฉันได้ทำไปแล้วในบทเรียนก่อนหน้า
จากนั้นให้เราข้ามทุกบรรทัดและข้ามไปที่ฟังก์ชันหลักที่ว่างเปล่าซึ่งเรามีการกำหนดค่า PORT สำหรับ Timer0
เป็นโมฆะ main () {/ ***** การกำหนดค่าพอร์ตสำหรับ Timer ****** / OPTION_REG = 0b00000101; // Timer0 พร้อม freq ภายนอกและ 64 เป็น prescalar // เปิดใช้งาน PULL UPs TMR0 = 100; // โหลดค่าเวลา 0.0019968s; delayValue สามารถอยู่ระหว่าง 0-256 เท่านั้น TMR0IE = 1; // เปิดใช้งานบิตขัดจังหวะตัวจับเวลาในการลงทะเบียน PIE1 GIE = 1; // เปิดใช้งาน Global Interrupt PEIE = 1; // เปิดใช้งานการขัดจังหวะอุปกรณ์ต่อพ่วง / *********** ______ *********** /
เพื่อให้เข้าใจสิ่งนี้เราต้องดูที่ OPTION Register ในแผ่นข้อมูล PIC ของเรา
ตามที่กล่าวไว้ในบทช่วยสอนก่อนหน้านี้บิต 7ใช้เพื่อเปิดใช้งานตัวต้านทานแบบดึงขึ้นที่อ่อนแอสำหรับ PORTB ดูรูปด้านบนบิต 3ถูกสร้างเป็น 0 เพื่อสั่งให้ MCU ทราบว่าควรใช้พรีสเกลเลอร์ต่อไปนี้สำหรับตัวจับเวลาไม่ใช่สำหรับ WatchDogTimer (WDT) โหมดตั้งเวลาถูกเลือกโดยการล้างบิต 5 T0CS
(OPTION_REG <5>)
ตอนนี้bits2-0ใช้เพื่อตั้งค่า prescaler สำหรับตัวจับเวลา ดังแสดงในตารางด้านบนเพื่อกำหนดค่า Prescaler เป็น 64 บิตจะต้องตั้งค่าเป็น 101
ต่อไปให้เราดู Registers ที่เกี่ยวข้องกับ Timer0
ตัวจับเวลาจะเริ่มเพิ่มขึ้นเมื่อตั้งค่าและโอเวอร์โฟลว์หลังจากถึงค่า 256 เพื่อเปิดใช้งานการขัดจังหวะตัวจับเวลาในช่วงเวลานี้จำเป็นต้องตั้งค่ารีจิสเตอร์TMR0IEให้สูง ตั้งแต่จับเวลา 0 ตัวเองเป็นอุปกรณ์ต่อพ่วงเรามีการเปิดใช้งานอุปกรณ์ต่อพ่วงขัดจังหวะโดยการPEIE = 1 ในที่สุดเราต้องเปิดใช้งาน Global Interrupt เพื่อให้ MCU ได้รับแจ้งเกี่ยวกับ Interrupt ในระหว่างการดำเนินการใด ๆ ซึ่งทำได้โดยการทำให้GIE = 1
ความล่าช้า = ((256-REG_val) * (Prescal * 4)) / Fosc
สูตรข้างต้นใช้ในการคำนวณมูลค่าของ Delay
ที่ไหน
REG_val = 100;
Prescal = 64
Fosc = 20000000
ในการคำนวณให้
ความล่าช้า = 0.0019968 วินาที
ชุดบรรทัดถัดไปคือการตั้งค่าพอร์ต I / O
/ ***** การกำหนดค่าพอร์ตสำหรับ I / O ****** / TRISB0 = 1; // สั่ง MCU ว่าใช้ PORTB พิน 0 เป็นอินพุตสำหรับปุ่ม 1 TRISB1 = 1; // สั่ง MCU ว่าใช้ PORTB พิน 1 เป็นอินพุตสำหรับปุ่ม 1 TRISD = 0x00; // สั่ง MCU ว่าพินทั้งหมดบน PORT D เป็นเอาต์พุต PORTD = 0x00; // เริ่มต้นพินทั้งหมดเป็น 0 / *********** ______ *********** /
สิ่งนี้เหมือนกับบทช่วยสอนก่อนหน้านี้เนื่องจากเราใช้ฮาร์ดแวร์ตัวเดียวกัน ยกเว้นว่าเราได้เพิ่มปุ่มอื่นเป็นอินพุต โดยเส้นTRISB1 = 1
ถัดไปข้างในไม่มีที่สิ้นสุด ในขณะที่ วนซ้ำเรามีโค้ดสองบล็อก หนึ่งใช้เพื่อรับอินพุตตัวจับเวลาจากผู้ใช้และอีกอันเพื่อดำเนินการตามลำดับของการหน่วงเวลาบน LED ฉันได้อธิบายโดยใช้ความคิดเห็นต่อแต่ละบรรทัด
ในขณะที่ (1) {count = 0; // อย่าเรียกใช้ตัวจับเวลาขณะอยู่ในลูปหลัก // ******* รับการหน่วงเวลาจากผู้ใช้ **** ////// if (RB0 == 0 && flag == 0) // เมื่อ อินพุตที่ได้รับ {get_scnds + = 1; // get_scnds = get_scnds + http: // ค่าสถานะตัวแปรที่เพิ่มขึ้น = 1; } if (RB0 == 1) // เพื่อป้องกันแฟล็กการเพิ่มอย่างต่อเนื่อง = 0; / *********** ______ *********** /
ตัวแปรที่เรียกว่าget_scndsจะเพิ่มขึ้นทุกครั้งที่ผู้ใช้กดปุ่ม 1. ธง (ซอฟต์แวร์กำหนด) ตัวแปรที่ใช้ในการถือกระบวนการที่เพิ่มขึ้นจนกว่าผู้ใช้จะเอานิ้วของเขาจากปุ่ม
// ******* ดำเนินการตามลำดับด้วยความล่าช้า **** ////// while (RB1 == 0) {PORTD = 0b00000001 <
บล็อกถัดไปจะเริ่มทำงานถ้ากดปุ่มสองปุ่ม เนื่องจากผู้ใช้ได้กำหนดการหน่วงเวลาที่ต้องการแล้วโดยใช้ปุ่มหนึ่งและถูกบันทึกไว้ในตัวแปรget_scndsเราใช้ตัวแปรที่เรียกว่าhscndตัวแปรนี้ถูกควบคุมโดย ISR (Interrupt service routine)
ประจำบริการขัดจังหวะเป็นขัดจังหวะที่จะถูกเรียกว่าแต่ละครั้ง Timer0 เป็นล้น ให้เราดูว่า ISR ถูกควบคุมอย่างไรในบล็อกถัดไปเช่นเราต้องการเพิ่มการหน่วงเวลาครึ่งวินาที (0.5 วินาที) ในการกดแต่ละปุ่มจากนั้นเราต้องเพิ่มตัวแปร hscnd ทุกครึ่งวินาที เนื่องจากเราได้ตั้งโปรแกรมตัวจับเวลาให้โอเวอร์โฟลว์สำหรับทุกๆ 0.0019968 วินาที (~ 2 มิลลิวินาที) ดังนั้นในการ นับ ตัวแปรการนับครึ่งวินาทีควรเป็น 250 เพราะ 250 * 2ms = 0.5 วินาที ดังนั้นเมื่อนับได้ 250 (250 * 2ms = 0.5 วินาที) หมายความว่ามันผ่านไปครึ่งวินาทีแล้วดังนั้นเราจึงเพิ่ม hscnd ทีละ1 และเริ่มการนับเป็นศูนย์
เป็นโมฆะ interrupt timer_isr () {if (TMR0IF == 1) // Timer flag ถูกทริกเกอร์เนื่องจากตัวจับเวลาล้น {TMR0 = 100; // โหลดค่าตัวจับเวลา TMR0IF = 0; // ล้างการนับค่าสถานะขัดจังหวะตัวจับเวลา ++; } ถ้า (นับ == 250) {hscnd + = 1; // hscnd จะเพิ่มขึ้นทุก ๆ ครึ่งวินาทีนับ = 0; }}
ดังนั้นเราจึงใช้ค่านี้และเปรียบเทียบกับ hscnd ของเราและเปลี่ยน LED ของเราตามเวลาที่ผู้ใช้กำหนด นอกจากนี้ยังคล้ายกับบทช่วยสอนล่าสุด
เพียงเท่านี้เราก็มีโปรแกรมที่เข้าใจและใช้งานได้แล้ว
แผนภาพวงจรและการจำลอง Proteus:
ตามปกติให้ตรวจสอบผลลัพธ์โดยใช้ Proteus ก่อนฉันได้เชื่อมโยงไฟล์แผนผังของ Proteus ที่นี่
เพิ่มปุ่มลงในบอร์ด LED ก่อนหน้าและฮาร์ดแวร์ของเราก็พร้อมใช้งาน ควรมีลักษณะดังนี้:
หลังจากเชื่อมต่อเสร็จแล้วให้อัปโหลดรหัสและตรวจสอบผลลัพธ์ หากคุณมีปัญหาใด ๆ โปรดใช้ส่วนความคิดเห็น ตรวจสอบวิดีโอด้านล่างเพื่อทำความเข้าใจกระบวนการทั้งหมด