สารบัญ:

บทช่วยสอน AVR Assembler 3: 9 ขั้นตอน
บทช่วยสอน AVR Assembler 3: 9 ขั้นตอน

วีดีโอ: บทช่วยสอน AVR Assembler 3: 9 ขั้นตอน

วีดีโอ: บทช่วยสอน AVR Assembler 3: 9 ขั้นตอน
วีดีโอ: AVR® Insights - Episode 3 - How To Use Ports: Enabling Pins and Setting Pins High 2024, กรกฎาคม
Anonim
AVR Assembler บทช่วยสอน 3
AVR Assembler บทช่วยสอน 3

ยินดีต้อนรับสู่บทช่วยสอนหมายเลข 3!

ก่อนที่เราจะเริ่มต้นฉันต้องการสร้างประเด็นทางปรัชญา อย่ากลัวที่จะทดลองกับวงจรและโค้ดที่เรากำลังสร้างในบทช่วยสอนเหล่านี้ เปลี่ยนสายไฟ เพิ่มส่วนประกอบใหม่ ถอดส่วนประกอบ เปลี่ยนบรรทัดของรหัส เพิ่มบรรทัดใหม่ ลบบรรทัด และดูว่าเกิดอะไรขึ้น! มันยากมากที่จะทำลายสิ่งใดและถ้าคุณทำใครจะสน? ไม่มีสิ่งใดที่เราใช้อยู่ รวมทั้งไมโครคอนโทรลเลอร์ที่มีราคาแพงมาก และการให้ความรู้อยู่เสมอเพื่อดูว่าสิ่งต่างๆ จะล้มเหลวได้อย่างไร ไม่เพียงแต่คุณจะรู้ว่าอะไรไม่ควรทำในครั้งต่อไป แต่ที่สำคัญกว่านั้น คุณจะรู้ว่าทำไมไม่ทำ หากคุณเป็นเหมือนฉัน เมื่อคุณยังเป็นเด็กและได้ของเล่นชิ้นใหม่ ไม่นานนักก่อนที่คุณจะแยกชิ้นส่วนเพื่อดูว่าอะไรทำให้ถูกต้อง? บางครั้งของเล่นก็ได้รับความเสียหายอย่างไม่สามารถแก้ไขได้ แต่ก็ไม่ใช่เรื่องใหญ่อะไร การปล่อยให้เด็กได้สำรวจความอยากรู้อยากเห็นของเขาแม้กระทั่งของเล่นที่แตกหักคือสิ่งที่ทำให้เขากลายเป็นนักวิทยาศาสตร์หรือวิศวกรแทนที่จะเป็นเครื่องล้างจาน

วันนี้เราจะมาต่อวงจรง่ายๆ กันก่อน แล้วค่อยมาว่ากันในทฤษฎี ขออภัยเกี่ยวกับเรื่องนี้ แต่เราต้องการเครื่องมือ! ฉันสัญญาว่าเราจะชดเชยสิ่งนี้ในบทช่วยสอน 4 ซึ่งเราจะสร้างวงจรที่จริงจังกว่านี้ และผลลัพธ์ที่ได้จะออกมาดีมาก อย่างไรก็ตาม วิธีที่คุณต้องทำในบทช่วยสอนทั้งหมดนี้เป็นวิธีที่ช้ามากและครุ่นคิด หากคุณเพียงแค่ไถนา สร้างวงจร คัดลอกและวางโค้ด จากนั้นเรียกใช้ แน่นอนว่ามันจะได้ผล แต่คุณจะไม่เรียนรู้อะไรเลย คุณต้องคิดเกี่ยวกับแต่ละบรรทัด หยุดชั่วคราว. การทดลอง. ประดิษฐ์. หากคุณทำแบบนั้น เมื่อจบบทช่วยสอนที่ 5 คุณจะเลิกสร้างสิ่งเจ๋งๆ และไม่ต้องการการสอนอีกต่อไป มิฉะนั้น คุณกำลังดูมากกว่าการเรียนรู้และสร้างสรรค์

ยังไงก็ตาม ปรัชญาพอ มาเริ่มกันเลย!

ในบทช่วยสอนนี้ คุณจะต้อง:

  1. บอร์ดต้นแบบของคุณ
  2. LED
  3. สายต่อ
  4. ตัวต้านทานประมาณ 220 ถึง 330 โอห์ม
  5. คู่มือชุดคำสั่ง: www.atmel.com/images/atmel-0856-avr-instruction-se…
  6. เอกสารข้อมูล: www.atmel.com/images/Atmel-8271-8-bit-AVR-Microco…
  7. ออสซิลเลเตอร์คริสตัลอื่น (ไม่จำเป็น)

นี่คือลิงค์ไปยังชุดการสอนทั้งหมด:

ขั้นตอนที่ 1: การสร้างวงจร

การสร้างวงจร
การสร้างวงจร

วงจรในบทช่วยสอนนี้ง่ายมาก โดยพื้นฐานแล้วเราจะเขียนโปรแกรม "กะพริบตา" ดังนั้นสิ่งที่เราต้องมีคือสิ่งต่อไปนี้

ต่อ LED เข้ากับ PD4 จากนั้นต่อตัวต้านทาน 330 โอห์ม จากนั้นต่อกราวด์ เช่น.

PD4 - LED - R(330) - GND

และนั่นก็คือ!

ทฤษฎีจะเป็นเรื่องยากแม้ว่า…

ขั้นตอนที่ 2: เหตุใดเราจึงต้องการความคิดเห็นและไฟล์ M328Pdef.inc

ฉันคิดว่าเราควรเริ่มต้นด้วยการแสดงให้เห็นว่าเหตุใดไฟล์รวมและความคิดเห็นจึงมีประโยชน์ ไม่มีสิ่งใดที่จำเป็นจริง ๆ และคุณสามารถเขียน ประกอบ และอัปโหลดโค้ดในลักษณะเดียวกันโดยไม่มีพวกเขา และมันจะทำงานได้ดีอย่างสมบูรณ์ (แม้ว่าไม่มีไฟล์รวม คุณอาจได้รับการร้องเรียนจากแอสเซมเบลอร์ - แต่ไม่มีข้อผิดพลาด)

นี่คือรหัสที่เราจะเขียนในวันนี้ ยกเว้นว่าฉันได้ลบความคิดเห็นและไฟล์รวมแล้ว:

.device ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16, 0x05 ออก 0x25, r16 ldi r16, 0x01 sts 0x6e, r16 sei clr r16 ออก 0x26, r16 sbi 0x0a, 0x04 sbi 0x0b, 0bcall cbi 0x0b, 0x04 rcall c rjmp bc: clr r17 d: cpi r17, 0x1e brne d ret e: inc r17 cpi r17, 0x3d brne PC+2 clr r17 reti

สวยเรียบง่ายใช่มั้ย? ฮ่าๆๆ หากคุณประกอบและอัปโหลดไฟล์นี้ คุณจะทำให้ไฟ LED กะพริบในอัตรา 1 กะพริบต่อวินาทีโดยกะพริบนาน 1/2 วินาทีและการหยุดชั่วคราวระหว่างกะพริบนาน 1/2 วินาที

อย่างไรก็ตาม การดูโค้ดนี้แทบไม่ช่วยให้กระจ่าง หากคุณต้องเขียนโค้ดแบบนี้และต้องการแก้ไขหรือนำไปใช้ใหม่ในอนาคต คุณจะต้องลำบาก

ลองใส่ความคิดเห็นและรวมไฟล์กลับเข้าไปเพื่อให้เราสามารถทำความเข้าใจได้

ขั้นตอนที่ 3: Blink.asm

นี่คือรหัสที่เราจะพูดถึงในวันนี้:

;************************************

; เขียนโดย: 1o_o7; วันที่:; รุ่น: 1.0; บันทึกไฟล์เป็น: blink.asm; สำหรับ AVR: atmega328p; ความถี่สัญญาณนาฬิกา: 16MHz (อุปกรณ์เสริม);************************************; ฟังก์ชั่นโปรแกรม:---------------------------; นับวินาทีโดยกะพริบไฟ LED;; PD4 - LED - R(330 โอห์ม) - GND;;--------------------------------------.nolist.include "./m328Pdef.inc".list;==============; ประกาศ:.def temp = r16.def overflows = r17.org 0x0000; ตำแหน่งหน่วยความจำ (PC) ของตัวจัดการการรีเซ็ต rjmp Reset; jmp มีค่าใช้จ่าย 2 รอบ cpu และ rjmp ราคาเพียง 1; ดังนั้นเว้นแต่คุณจะต้องกระโดดมากกว่า 8k ไบต์; คุณต้องการ rjmp เท่านั้น ไมโครคอนโทรลเลอร์บางตัวเท่านั้น; มี rjmp และไม่ใช่ jmp.org 0x0020; ตำแหน่งหน่วยความจำของตัวจัดการโอเวอร์โฟลว์ Timer0 rjmp overflow_handler; ไปที่นี่หากเกิดการขัดจังหวะล้น timer0;============ รีเซ็ต: ldi temp, 0b00000101 ออก TCCR0B, temp; ตั้งค่า Clock Selector Bits CS00, CS01, CS02 เป็น 101; สิ่งนี้ทำให้ Timer Counter0, TCNT0 เข้าสู่โหมด FCPU/1024; ดังนั้นจึงติ๊กที่ CPU freq/1024 ldi temp, 0b00000001 sts TIMSK0, temp; ตั้งค่าบิต Timer Overflow Interrupt Enable (TOIE0); ของ Timer Interrupt Mask Register (TIMSK0) sei; เปิดใช้งานการขัดจังหวะทั่วโลก - เทียบเท่ากับ "sbi SREG, I" clr temp out TCNT0, temp; เริ่มต้นตัวจับเวลา/ตัวนับเป็น 0 sbi DDRD, 4; ตั้งค่า PD4 เป็นเอาต์พุต;======================; ตัวหลักของโปรแกรม: กะพริบ: sbi PORTD, 4; เปิด LED บน PD4 rcall ดีเลย์; ดีเลย์จะเป็น 1/2 วินาที cbi PORTD, 4; ปิด LED บน PD4 rcall ดีเลย์; ความล่าช้าจะเป็น 1/2 วินาที rjmp กะพริบ; วนกลับไปที่การหน่วงเวลาเริ่มต้น: clr overflows; ตั้งค่าโอเวอร์โฟลว์เป็น 0 sec_count: โอเวอร์โฟลว์ cpi, 30; เปรียบเทียบจำนวนล้นและ 30 brne sec_count; สาขาที่จะกลับไป sec_count ถ้าไม่เท่ากับ ret; หากมีการโอเวอร์โฟลว์ 30 ครั้ง ให้กลับไปกะพริบ overflow_handler: inc overflows; เพิ่ม 1 ให้กับตัวแปรโอเวอร์โฟลว์ cpi โอเวอร์โฟลว์ 61; เปรียบเทียบกับ 61 brne PC+2; Program Counter + 2 (ข้ามบรรทัดถัดไป) ถ้าไม่เท่ากับ clr overflows; ถ้าเกิดโอเวอร์โฟลว์ 61 ครั้ง ให้รีเซ็ตตัวนับเป็นศูนย์ reti; กลับจากการขัดจังหวะ

อย่างที่คุณเห็น ความคิดเห็นของฉันสั้นกว่านี้เล็กน้อย เมื่อเราทราบคำสั่งในชุดคำสั่งแล้ว เราไม่จำเป็นต้องอธิบายในความคิดเห็น เราแค่ต้องอธิบายสิ่งที่เกิดขึ้นจากมุมมองของโปรแกรมเท่านั้น

เราจะพูดคุยกันถึงสิ่งที่ทั้งหมดนี้ทำทีละส่วน แต่ก่อนอื่น เรามาลองทำความเข้าใจกับมุมมองระดับโลกกันก่อน เนื้อหาหลักของโปรแกรมทำงานดังนี้

ก่อนอื่นเราตั้งค่าบิต 4 ของ PORTD ด้วย "sbi PORTD, 4" ซึ่งจะส่ง 1 ถึง PD4 ซึ่งทำให้แรงดันไฟฟ้าอยู่ที่ 5V บนพินนั้น นี่จะเป็นการเปิดไฟ LED จากนั้นเราข้ามไปที่รูทีนย่อย "การหน่วงเวลา" ซึ่งนับ 1/2 วินาที (เราจะอธิบายวิธีการดำเนินการนี้ในภายหลัง) จากนั้นเรากลับไปกะพริบและล้างบิต 4 บน PORTD ซึ่งตั้งค่า PD4 เป็น 0V และด้วยเหตุนี้จึงปิด LED จากนั้นเราหน่วงเวลาอีก 1/2 วินาที จากนั้นกระโดดกลับไปที่จุดเริ่มต้นของการกะพริบอีกครั้งด้วย "rjmp Blink"

คุณควรเรียกใช้โค้ดนี้และดูว่าโค้ดนี้ทำงานตามที่ควรจะเป็นหรือไม่

และที่นั่นคุณมีมัน! นั่นคือทั้งหมดที่รหัสนี้ทำทางกายภาพ กลไกภายในของสิ่งที่ไมโครคอนโทรลเลอร์กำลังทำอยู่นั้นมีความเกี่ยวข้องมากกว่าเล็กน้อย และนั่นคือเหตุผลที่เรากำลังทำบทแนะนำนี้ ดังนั้นเรามาพูดถึงแต่ละส่วนกัน

ขั้นตอนที่ 4:.org Assembler Directives

เรารู้แล้วว่าคำสั่ง.nolist,.list,.include และ.def assembler ทำอะไรได้บ้างจากบทช่วยสอนก่อนหน้าของเรา มาดูโค้ด 4 บรรทัดที่ตามมากันก่อน:

.org 0x0000

jmp รีเซ็ต.org 0x0020 jmp overflow_handler

คำสั่ง.org จะบอกแอสเซมเบลอร์ว่าตำแหน่งใดใน "Program Memory" ที่จะใส่คำสั่งถัดไป ขณะที่โปรแกรมของคุณทำงาน "ตัวนับโปรแกรม" (ย่อมาจาก PC) จะมีที่อยู่ของบรรทัดปัจจุบันที่กำลังดำเนินการ ดังนั้นในกรณีนี้เมื่อพีซีอยู่ที่ 0x0000 จะเห็นคำสั่ง "jmp Reset" อยู่ในตำแหน่งหน่วยความจำนั้น เหตุผลที่เราต้องการใส่ jmp Reset ในตำแหน่งนั้นเพราะเมื่อโปรแกรมเริ่มทำงานหรือรีเซ็ตชิป พีซีจะเริ่มรันโค้ด ณ จุดนี้ อย่างที่เราเห็น เราเพิ่งบอกให้ "กระโดด" ไปที่ส่วน "รีเซ็ต" ทันที ทำไมเราถึงทำอย่างนั้น? นั่นหมายความว่าสองบรรทัดสุดท้ายข้างต้นกำลังถูกข้ามไป! ทำไม?

นั่นคือสิ่งที่น่าสนใจ ตอนนี้คุณจะต้องเปิดโปรแกรมดู pdf พร้อมแผ่นข้อมูล ATmega328p ฉบับสมบูรณ์ที่ฉันชี้ไปที่หน้าแรกของบทช่วยสอนนี้ (นั่นคือสาเหตุที่เป็นรายการที่ 4 ในส่วน "คุณต้องการ") หากหน้าจอของคุณเล็กเกินไป หรือคุณเปิดหน้าต่างมากเกินไปแล้ว (เช่นในกรณีของฉัน) คุณสามารถทำในสิ่งที่ฉันทำและวางไว้บน Ereader หรือโทรศัพท์ Android ของคุณ คุณจะใช้มันตลอดเวลาหากคุณวางแผนที่จะเขียนโค้ดแอสเซมบลี สิ่งที่ยอดเยี่ยมคือไมโครคอนโทรลเลอร์ทั้งหมดได้รับการจัดระเบียบในลักษณะที่คล้ายคลึงกัน ดังนั้นเมื่อคุณคุ้นเคยกับการอ่านเอกสารข้อมูลและการเข้ารหัสจากสิ่งเหล่านี้ คุณจะพบว่าการทำแบบเดียวกันสำหรับไมโครคอนโทรลเลอร์ตัวอื่นนั้นแทบจะกลายเป็นเรื่องเล็กน้อย ดังนั้นเราจึงกำลังเรียนรู้วิธีใช้ไมโครคอนโทรลเลอร์ทั้งหมดในแง่หนึ่ง ไม่ใช่แค่ atmega328p

โอเค ไปที่หน้า 18 ในแผ่นข้อมูล แล้วดูรูปที่ 8-2

นี่คือวิธีการตั้งค่าหน่วยความจำโปรแกรมในไมโครคอนโทรลเลอร์ คุณจะเห็นว่ามันเริ่มต้นด้วยที่อยู่ 0x0000 และแบ่งออกเป็นสองส่วน ส่วนแฟลชแอปพลิเคชันและส่วนแฟลชสำหรับบูต หากคุณอ้างอิงถึงหน้า 277 ตาราง 27-14 โดยสังเขป คุณจะเห็นว่าส่วนโปรแกรมแฟลชใช้ตำแหน่งตั้งแต่ 0x0000 ถึง 0x37FF และส่วนแฟลชสำหรับบูตใช้ตำแหน่งที่เหลือจาก 0x3800 ถึง 0x3FFF

แบบฝึกหัดที่ 1: หน่วยความจำของโปรแกรมมีกี่ตำแหน่ง เช่น. แปลง 3FFF เป็นทศนิยมและเพิ่ม 1 เนื่องจากเราเริ่มนับที่ 0 เนื่องจากแต่ละตำแหน่งหน่วยความจำกว้าง 16 บิต (หรือ 2 ไบต์) จำนวนไบต์ของหน่วยความจำทั้งหมดเป็นเท่าใด ตอนนี้แปลงเป็นกิโลไบต์โดยจำไว้ว่ามี 2^10 = 1024 ไบต์ในหนึ่งกิโลไบต์ ส่วนบูตแฟลชเปลี่ยนจาก 0x3800 เป็น 0x37FF นี่คือกี่กิโลไบต์ มีหน่วยความจำเหลืออยู่กี่กิโลไบต์ที่เราจะใช้ในการจัดเก็บโปรแกรมของเรา? กล่าวอีกนัยหนึ่งโปรแกรมของเราจะใหญ่แค่ไหน? สุดท้ายเราสามารถมีโค้ดได้กี่บรรทัด?

เอาล่ะ เมื่อเราทราบทั้งหมดเกี่ยวกับการจัดระเบียบหน่วยความจำโปรแกรมแฟลชแล้ว มาต่อกันที่การสนทนาเกี่ยวกับคำสั่ง.org กัน เราเห็นว่าตำแหน่งหน่วยความจำแรก 0x0000 มีคำแนะนำของเราให้ข้ามไปยังส่วนของเราที่เราระบุว่ารีเซ็ต ตอนนี้เรามาดูกันว่าคำสั่ง ".org 0x0020" ทำอะไร มันบอกว่าเราต้องการวางคำสั่งในบรรทัดถัดไปไว้ที่ตำแหน่งหน่วยความจำ 0x0020 คำแนะนำที่เราวางไว้คือการข้ามไปยังส่วนในโค้ดของเราที่เราได้ระบุว่า "overflow_handler" … ตอนนี้ทำไมเราถึงต้องการให้กระโดดนี้วางไว้ที่ตำแหน่งหน่วยความจำ 0x0020 หากต้องการทราบ เราเปิดไปที่หน้า 65 ในแผ่นข้อมูลและดูที่ตารางที่ 12-6

ตารางที่ 12-6 เป็นตาราง "รีเซ็ตและขัดจังหวะเวกเตอร์" และแสดงตำแหน่งที่พีซีจะไปเมื่อได้รับ "การขัดจังหวะ" ตัวอย่างเช่น หากคุณดูที่เวกเตอร์หมายเลข 1 "แหล่งที่มา" ของการขัดจังหวะคือ "รีเซ็ต" ซึ่งกำหนดเป็น "พินภายนอก การรีเซ็ตการเปิดเครื่อง การรีเซ็ตการหมดไฟ และการรีเซ็ตระบบ Watchdog" หมายความว่าหากมี สิ่งเหล่านั้นเกิดขึ้นกับไมโครคอนโทรลเลอร์ของเรา พีซีจะเริ่มรันโปรแกรมของเราที่ตำแหน่งหน่วยความจำของโปรแกรม 0x0000 แล้วคำสั่ง.org ของเราล่ะ? เราวางคำสั่งไว้ที่ตำแหน่งหน่วยความจำ 0x0020 และถ้าคุณมองลงไปที่ตาราง คุณจะเห็นว่าหากมีการโอเวอร์โฟลว์ Timer/Counter0 เกิดขึ้น (มาจาก TIMER0 OVF) การดำเนินการใดๆ ก็ตามที่อยู่ในตำแหน่ง 0x0020 ดังนั้นเมื่อใดก็ตามที่เกิดเหตุการณ์ดังกล่าว พีซีจะข้ามไปยังจุดที่เราระบุว่า "overflow_handler" เย็นใช่มั้ย? คุณจะเห็นได้ในไม่กี่นาทีว่าทำไมเราถึงทำเช่นนี้ แต่ก่อนอื่น เรามาจบขั้นตอนของบทช่วยสอนนี้กันก่อนดีกว่า

หากเราต้องการทำให้โค้ดของเราดูเรียบร้อยและเป็นระเบียบมากขึ้น เราควรแทนที่ 4 บรรทัดที่เรากำลังพูดถึงดังต่อไปนี้ (ดูหน้า 66):

.org 0x0000

rjmp รีเซ็ต; พีซี = 0x0000 reti; พีซี = 0x0002 reti; พีซี = 0x0004 reti; พีซี = 0x0006 reti; พีซี = 0x0008 reti; PC = 0x000A … reti; PC = 0x001E jmp overflow_handler: PC = 0x0020 reti: PC = 0x0022 … reti; PC = 0x0030 reti; พีซี = 0x0032

ดังนั้นหากมีการขัดจังหวะเกิดขึ้นก็จะเป็นเพียง "reti" ซึ่งหมายถึง "การกลับจากการขัดจังหวะ" และไม่มีอะไรเกิดขึ้นอีก แต่ถ้าเราไม่เคย "เปิดใช้งาน" อินเทอร์รัปต์ต่างๆ เหล่านี้ อินเตอร์รัปต์ต่างๆ เหล่านี้จะไม่ถูกใช้งาน และเราสามารถใส่โค้ดโปรแกรมลงในจุดเหล่านี้ได้ ในโปรแกรม "blink.asm" ปัจจุบันของเรา เราจะเปิดใช้งานการขัดจังหวะโอเวอร์โฟลว์ timer0 เท่านั้น (และแน่นอนว่าการขัดจังหวะการรีเซ็ตซึ่งเปิดใช้งานอยู่เสมอ) ดังนั้นเราจึงไม่รบกวนโปรแกรมอื่น

เราจะ "เปิดใช้งาน" การขัดจังหวะการโอเวอร์โฟลว์ timer0 ได้อย่างไร … นั่นคือหัวข้อของขั้นตอนต่อไปในบทช่วยสอนนี้

ขั้นตอนที่ 5: ตัวจับเวลา/ตัวนับ 0

ตัวจับเวลา/ตัวนับ 0
ตัวจับเวลา/ตัวนับ 0

ลองดูที่ภาพด้านบน นี่คือกระบวนการตัดสินใจของ "พีซี" เมื่อผู้มีอิทธิพลภายนอก "ขัดขวาง" การไหลของโปรแกรมของเรา สิ่งแรกที่ทำเมื่อได้รับสัญญาณจากภายนอกว่ามีการขัดจังหวะเกิดขึ้นคือตรวจสอบเพื่อดูว่าเราได้ตั้งค่าบิต "เปิดใช้งานการขัดจังหวะ" สำหรับการขัดจังหวะประเภทนั้นหรือไม่ หากเรายังไม่ได้ดำเนินการ มันก็จะรันโค้ดบรรทัดถัดไปของเราต่อไป หากเราได้ตั้งค่าบิตเปิดใช้งานการขัดจังหวะนั้นโดยเฉพาะ (เพื่อให้มี 1 ในตำแหน่งบิตนั้นแทนที่จะเป็น 0) มันจะตรวจสอบว่าเราได้เปิดใช้งาน "การขัดจังหวะทั่วโลก" หรือไม่ ถ้าไม่เช่นนั้นจะไปที่บรรทัดถัดไปอีกครั้ง ของรหัสและดำเนินการต่อ หากเราเปิดใช้งานอินเตอร์รัปต์ทั่วโลกด้วย มันจะไปที่ตำแหน่งหน่วยความจำโปรแกรมของอินเทอร์รัปต์ประเภทนั้น (ดังแสดงในตารางที่ 12-6) และดำเนินการคำสั่งที่เราวางไว้ที่นั่น มาดูกันว่าเราได้ใช้งานทั้งหมดนี้ในโค้ดของเราอย่างไร

ส่วนการรีเซ็ตป้ายกำกับของรหัสของเราเริ่มต้นด้วยสองบรรทัดต่อไปนี้:

รีเซ็ต:

อุณหภูมิ ldi, 0b00000101 ออก TCCR0B, อุณหภูมิ

อย่างที่เราทราบกันดีอยู่แล้ว ค่านี้จะโหลดเป็น temp (เช่น R16) ตัวเลขที่ตามมาทันที ซึ่งก็คือ 0b00000101 จากนั้นจะเขียนหมายเลขนี้ไปยังรีจิสเตอร์ที่เรียกว่า TCCR0B โดยใช้คำสั่ง "out" ทะเบียนนี้คืออะไร? ไปที่หน้า 614 ของแผ่นข้อมูลกัน นี้อยู่ตรงกลางของตารางสรุปการลงทะเบียนทั้งหมด ที่ที่อยู่ 0x25 คุณจะพบ TCCR0B (ตอนนี้คุณรู้แล้วว่าบรรทัด "out 0x25, r16" มาจากไหนในโค้ดเวอร์ชันที่ไม่มีความคิดเห็นของฉัน) เราเห็นโดยส่วนโค้ดด้านบนว่าเราได้ตั้งค่าบิตที่ 0 และบิตที่ 2 และล้างส่วนที่เหลือทั้งหมด เมื่อดูที่ตารางคุณจะเห็นว่านี่หมายความว่าเราได้ตั้งค่า CS00 และ CS02 แล้ว ตอนนี้ ให้ตรงไปที่บทในแผ่นข้อมูลที่เรียกว่า "8-bit Timer/Counter0 with PWM" โดยเฉพาะไปที่หน้า 107 ของบทนั้น คุณจะเห็นคำอธิบายเดียวกันกับรีจิสเตอร์ "Timer/Counter Control Register B" (TCCR0B) ที่เราเพิ่งเห็นในตารางสรุปการลงทะเบียน (เพื่อที่เราจะมาตรงนี้ได้ แต่ฉันต้องการให้คุณดูวิธีใช้ตารางสรุป สำหรับการอ้างอิงในอนาคต). แผ่นข้อมูลยังคงให้คำอธิบายของแต่ละบิตในการลงทะเบียนนั้นและสิ่งที่พวกเขาทำ เราจะข้ามทั้งหมดนั้นไปก่อนและเปลี่ยนหน้าเป็นตารางที่ 15-9 ตารางนี้แสดง "Clock Select Bit Description" ตอนนี้ดูตารางนั้นจนกว่าคุณจะพบบรรทัดที่สอดคล้องกับบิตที่เราเพิ่งตั้งค่าในรีจิสเตอร์นั้น บรรทัดบอกว่า "clk/1024 (จาก prescaler)" สิ่งนี้หมายความว่าเราต้องการให้ Timer/Counter0 (TCNT0) ทำเครื่องหมายที่อัตราซึ่งเป็นความถี่ของ CPU หารด้วย 1024 เนื่องจากเรามีไมโครคอนโทรลเลอร์ที่ป้อนด้วยคริสตัลออสซิลเลเตอร์ 16MHz หมายความว่าอัตราที่ CPU ดำเนินการตามคำสั่งคือ 16 ล้านคำสั่งต่อวินาที ดังนั้นอัตราที่ตัวนับ TCNT0 ของเราจะทำเครื่องหมายคือ 16 ล้าน/1024 = 15625 ครั้งต่อวินาที (ลองใช้บิตการเลือกนาฬิกาที่แตกต่างกันและดูว่าเกิดอะไรขึ้น - จำปรัชญาของเราได้หรือไม่) ให้หมายเลข 15625 อยู่ในใจของเราในภายหลังและไปยังโค้ดสองบรรทัดถัดไป:

อุณหภูมิ ldi, 0b00000001

sts TIMSK0, อุณหภูมิ

ตั้งค่าบิตที่ 0 ของรีจิสเตอร์ที่เรียกว่า TIMSK0 และล้างส่วนที่เหลือทั้งหมด หากคุณดูที่หน้า 109 ในแผ่นข้อมูล คุณจะเห็นว่า TIMSK0 ย่อมาจาก "Timer/Counter Interrupt Mask Register 0" และรหัสของเราได้ตั้งค่าบิตที่ 0 ซึ่งมีชื่อว่า TOIE0 ซึ่งย่อมาจาก "Timer/Counter0 Overflow Interrupt Enable" … ที่นั่น! ตอนนี้คุณเห็นว่าสิ่งนี้เกี่ยวกับอะไร ตอนนี้เรามี "ชุดบิตเปิดใช้งานขัดจังหวะ" ตามที่เราต้องการจากการตัดสินใจครั้งแรกในภาพของเราที่ด้านบน ตอนนี้ สิ่งที่เราต้องทำคือเปิดใช้งาน "การขัดจังหวะทั่วโลก" และโปรแกรมของเราจะสามารถตอบสนองต่อการขัดจังหวะประเภทนี้ได้ เราจะเปิดใช้งานการขัดจังหวะทั่วโลกในไม่ช้า แต่ก่อนที่เราจะทำคุณอาจสับสนกับบางสิ่ง.. ทำไมฉันถึงใช้คำสั่ง "sts" เพื่อคัดลอกลงในการลงทะเบียน TIMSK0 แทน "ออก" ปกติ

เมื่อใดก็ตามที่คุณเห็นฉัน ให้ใช้คำสั่งที่คุณไม่เคยเห็นมาก่อน สิ่งแรกที่คุณควรทำคือเปิดไปที่หน้า 616 ในแผ่นข้อมูล นี่คือ "บทสรุปชุดคำสั่ง" ตอนนี้หาคำสั่ง "STS" ซึ่งเป็นคำสั่งที่ฉันใช้ มันบอกว่าใช้ตัวเลขจากการลงทะเบียน R (เราใช้ R16) และตำแหน่ง "จัดเก็บโดยตรงไปยัง SRAM" k (ในกรณีของเราให้โดย TIMSK0) เหตุใดเราจึงต้องใช้ "sts" ซึ่งใช้เวลา 2 รอบนาฬิกา (ดูคอลัมน์สุดท้ายในตาราง) เพื่อจัดเก็บใน TIMSK0 และเราต้องการเพียง "out" ซึ่งใช้เวลาเพียงรอบนาฬิกาเดียวเพื่อจัดเก็บใน TCCR0B เพื่อตอบคำถามนี้ เราต้องกลับไปที่ตารางสรุปการลงทะเบียนของเราในหน้า 614 คุณเห็นว่าการลงทะเบียน TCCR0B อยู่ที่ 0x25 แต่ยังอยู่ที่ (0x45) ใช่ไหม ซึ่งหมายความว่าเป็นรีจิสเตอร์ใน SRAM แต่ก็เป็นรีจิสเตอร์บางประเภทที่เรียกว่า "พอร์ต" (หรือรีจิสเตอร์ i/o) หากคุณดูตารางสรุปคำสั่งข้างคำสั่ง "ออก" คุณจะเห็นว่าใช้ค่าจาก "รีจิสเตอร์ที่ทำงาน" เช่น R16 และส่งไปยัง PORT ดังนั้นเราจึงสามารถใช้ "ออก" เมื่อเขียนถึง TCCR0B และช่วยตัวเองให้มีวงจรนาฬิกา แต่ตอนนี้ค้นหา TIMSK0 ในตารางการลงทะเบียน คุณเห็นว่ามีที่อยู่ 0x6e นี่อยู่นอกช่วงของพอร์ต (ซึ่งเป็นตำแหน่ง 0x3F แรกของ SRAM เท่านั้น) ดังนั้นคุณต้องถอยกลับไปใช้คำสั่ง sts และใช้รอบนาฬิกา CPU สองรอบเพื่อทำ โปรดอ่านหมายเหตุ 4 ที่ส่วนท้ายของตารางสรุปคำสั่งในหน้า 615 ในขณะนี้ นอกจากนี้ โปรดสังเกตว่าพอร์ตอินพุตและเอาต์พุตทั้งหมดของเรา เช่น PORTD จะอยู่ที่ด้านล่างของตาราง ตัวอย่างเช่น PD4 เป็นบิต 4 ที่ที่อยู่ 0x0b (ตอนนี้คุณเห็นว่าข้อมูล 0x0b ทั้งหมดมาจากไหนในรหัสที่ไม่แสดงข้อคิดเห็นของฉัน!).. โอเค คำถามสั้นๆ คุณเปลี่ยน "sts" เป็น "ออก" แล้วดูอะไร เกิดขึ้น? จำปรัชญาของเราไว้! ทำลายมัน! อย่าเพิ่งเชื่อคำพูดของฉัน

เอาล่ะ ก่อนที่เราจะไปต่อ ให้เปิดหน้า 19 ในแผ่นข้อมูลสักครู่ คุณเห็นภาพของหน่วยความจำข้อมูล (SRAM) 32 รีจิสเตอร์แรกใน SRAM (ตั้งแต่ 0x0000 ถึง 0x001F) คือ "รีจิสเตอร์การทำงานทั่วไป" R0 ถึง R31 ที่เราใช้ตลอดเวลาเป็นตัวแปรในโค้ดของเรารีจิสเตอร์ 64 รายการถัดไปคือพอร์ต I/O สูงสุด 0x005f (เช่นที่เรากำลังพูดถึงซึ่งมีแอดเดรสที่ไม่ได้จัดวางอยู่ข้างๆ ในตารางรีจิสเตอร์ซึ่งเราสามารถใช้คำสั่ง "out" แทน "sts") ส่วนถัดไปของ SRAM จะมีรีจิสเตอร์อื่นๆ ทั้งหมดในตารางสรุปจนถึงที่อยู่ 0x00FF และสุดท้ายคือ SRAM ภายใน มาต่อกันที่หน้า 12 กันสักนิด คุณจะเห็นตารางของ "ทะเบียนการทำงานทั่วไป" ที่เราใช้เป็นตัวแปรเสมอ คุณเห็นเส้นหนาระหว่างตัวเลข R0 ถึง R15 และ R16 ถึง R31 ไหม บรรทัดนั้นคือเหตุผลที่เราใช้ R16 เป็นอันที่เล็กที่สุดเสมอ และฉันจะอธิบายเพิ่มเติมอีกเล็กน้อยในบทช่วยสอนถัดไป ซึ่งเราจะต้องใช้รีจิสเตอร์ที่อยู่ทางอ้อม 16 บิตสามตัว X, Y และ Z ฉันจะไม่ เข้าเรื่องนั้นแต่เนื่องจากเราไม่ต้องการมันในตอนนี้และเราก็จมอยู่กับที่ตรงนี้

พลิกกลับหนึ่งหน้าไปยังหน้า 11 ของแผ่นข้อมูล คุณจะเห็นไดอะแกรมของการลงทะเบียน SREG ที่ด้านบนขวา? คุณเห็นว่าบิต 7 ของรีจิสเตอร์นั้นเรียกว่า "ฉัน" ตอนนี้ไปที่หน้าและอ่านคำอธิบายของ Bit 7…. เย้! เป็นบิตเปิดใช้งานการขัดจังหวะทั่วโลก นั่นคือสิ่งที่เราต้องตั้งค่าเพื่อที่จะผ่านการตัดสินใจครั้งที่สองในไดอะแกรมของเราด้านบน และอนุญาตให้ตัวจับเวลา/ตัวนับโอเวอร์โฟลว์ขัดจังหวะในโปรแกรมของเรา ดังนั้นบรรทัดถัดไปของโปรแกรมของเราควรอ่าน:

sbi SREG ฉัน

ซึ่งตั้งค่าบิตที่เรียกว่า "I" ในการลงทะเบียน SREG อย่างไรก็ตาม มากกว่านี้ เราได้ใช้คำสั่ง

เซ

แทนที่. บิตนี้ถูกตั้งค่าบ่อยครั้งในโปรแกรมที่พวกเขาทำวิธีที่ง่ายขึ้น

ตกลง! ตอนนี้เราได้เตรียมโอเวอร์โฟลว์อินเตอร์รัปต์เพื่อให้ "jmp overflow_handler" ของเราทำงานทุกครั้งที่เกิดขึ้น

ก่อนที่เราจะไปต่อ มาดูการลงทะเบียน SREG (Status Register) อย่างรวดเร็ว เพราะสำคัญมาก อ่านสิ่งที่แต่ละธงแสดงถึง โดยเฉพาะอย่างยิ่ง คำแนะนำมากมายที่เราใช้จะตั้งค่าและตรวจสอบแฟล็กเหล่านี้ตลอดเวลา ตัวอย่างเช่น ต่อไปเราจะใช้คำสั่ง "CPI" ซึ่งแปลว่า "เปรียบเทียบทันที" ดูตารางสรุปคำสั่งสำหรับคำสั่งนี้และสังเกตว่ามีการตั้งค่าสถานะกี่ชุดในคอลัมน์ "แฟล็ก" ทั้งหมดนี้เป็นแฟล็กใน SREG และโค้ดของเราจะตั้งค่าและตรวจสอบอย่างต่อเนื่อง คุณจะเห็นตัวอย่างในไม่ช้า สุดท้ายบิตสุดท้ายของโค้ดส่วนนี้คือ:

clr อุณหภูมิ

ออก TCNT0, อุณหภูมิ sbi DDRD, 4

บรรทัดสุดท้ายนี่ค่อนข้างชัดเจน มันแค่ตั้งค่าบิตที่ 4 ของ Data Direction Register สำหรับ PortD ทำให้ PD4 เป็น OUTPUT

อันแรกตั้งค่าตัวแปร temp เป็นศูนย์ จากนั้นคัดลอกไปยังรีจิสเตอร์ TCNT0 TCNT0 คือ Timer/Counter0 ของเรา สิ่งนี้กำหนดให้เป็นศูนย์ ทันทีที่พีซีเรียกใช้บรรทัดนี้ timer0 จะเริ่มต้นที่ศูนย์และนับที่อัตรา 15625 ครั้งทุกวินาที ปัญหาคือ: TCNT0 เป็นการลงทะเบียน "8 บิต" ใช่ไหม ดังนั้นจำนวนที่ใหญ่ที่สุดที่การลงทะเบียน 8 บิตสามารถถือได้คืออะไร? ก็ 0b111111111 นั่นแหละ นี่คือหมายเลข 0xFF อันไหนคือ 255 คุณเห็นว่าเกิดอะไรขึ้น? ตัวจับเวลากำลังขยายเพิ่มขึ้น 15625 ครั้งต่อวินาที และทุกครั้งที่ถึง 255 จะ "ล้น" และกลับไปเป็น 0 อีกครั้ง ในเวลาเดียวกันเมื่อมันกลับไปที่ศูนย์ มันจะส่งสัญญาณ Timer Overflow Interrupt พีซีได้รับสิ่งนี้และคุณรู้ว่าตอนนี้มันทำอะไรได้บ้าง? ใช่. มันไปที่ตำแหน่งหน่วยความจำโปรแกรม 0x0020 และรันคำสั่งที่พบที่นั่น

ยอดเยี่ยม! ถ้าคุณยังอยู่กับฉัน แสดงว่าคุณเป็นซูเปอร์ฮีโร่ที่ไม่รู้จักเหน็ดเหนื่อย! ไปกันต่อเลย…

ขั้นตอนที่ 6: ตัวจัดการล้น

สมมติว่าการลงทะเบียน timer/counter0 เพิ่งล้น ตอนนี้เราทราบแล้วว่าโปรแกรมได้รับสัญญาณขัดจังหวะและรัน 0x0020 ซึ่งบอกโปรแกรม Counter, PC ให้ข้ามไปที่ป้ายกำกับ "overflow_handler" ต่อไปนี้เป็นรหัสที่เราเขียนหลังจากป้ายกำกับนั้น:

overflow_handler:

inc ล้น cpi ล้น 61 brne PC+2 clr ล้น reti

สิ่งแรกที่มันทำคือเพิ่มตัวแปร "ล้น" (ซึ่งเป็นชื่อของเราสำหรับการลงทะเบียนใช้งานทั่วไป R17) จากนั้นจึง "เปรียบเทียบ" เนื้อหาของโอเวอร์โฟลว์ด้วยหมายเลข 61 วิธีการทำงานของคำสั่ง cpi คือเพียงแค่ลบออก ตัวเลขสองตัวและถ้าผลลัพธ์เป็นศูนย์ มันจะตั้งค่าสถานะ Z ในการลงทะเบียน SREG (ฉันบอกคุณแล้วว่าเราจะเห็นการลงทะเบียนนี้ตลอดเวลา) หากตัวเลขทั้งสองเท่ากัน แฟล็ก Z จะเป็น 1 หากตัวเลขทั้งสองไม่เท่ากัน ก็จะเป็น 0

บรรทัดถัดไประบุว่า "brne PC+2" ซึ่งหมายถึง "สาขาหากไม่เท่ากัน" โดยพื้นฐานแล้ว มันจะตรวจสอบแฟล็ก Z ใน SREG และหากไม่ใช่หนึ่ง (เช่น ตัวเลขสองตัวไม่เท่ากัน หากเท่ากัน แฟล็กศูนย์จะถูกตั้งค่า) พีซีจะแยกสาขาไปที่ PC+2 หมายความว่าจะข้ามไป บรรทัดและตรงไปที่ "reti" ซึ่งส่งคืนจากการขัดจังหวะไปยังตำแหน่งใดก็ตามที่อยู่ในรหัสเมื่อการขัดจังหวะมาถึง หากคำสั่ง brne พบ 1 ในบิตแฟล็กศูนย์ มันจะไม่แตกแขนงออกไป แต่จะไปยังบรรทัดถัดไปแทน ซึ่งจะทำให้ clr โอเวอร์โฟลว์รีเซ็ตเป็น 0

ผลสุทธิของทั้งหมดนี้คืออะไร?

เราพบว่าทุกครั้งที่มีการโอเวอร์โฟลว์ตัวจับเวลา ตัวจัดการนี้จะเพิ่มค่าของ "โอเวอร์โฟลว์" ทีละตัว ดังนั้นตัวแปร "โอเวอร์โฟลว์" จึงนับจำนวนโอเวอร์โฟลว์ที่เกิดขึ้น เมื่อใดก็ตามที่จำนวนถึง 61 เราจะรีเซ็ตเป็นศูนย์

ทำไมในโลกนี้เราจะทำอย่างนั้น?

มาดูกัน. จำได้ว่าความเร็วสัญญาณนาฬิกาสำหรับ CPU ของเราคือ 16MHz และเรา "กำหนดล่วงหน้า" โดยใช้ TCCR0B เพื่อให้ตัวจับเวลานับเฉพาะที่อัตรา 15625 นับต่อวินาทีใช่ไหม และทุกครั้งที่จับเวลาถึง 255 ตัวจับเวลาจะล้น นั่นหมายความว่าล้น 15625/256 = 61.04 ครั้งต่อวินาที เรากำลังติดตามจำนวนโอเวอร์โฟลว์ด้วยตัวแปร "โอเวอร์โฟลว์" ของเรา และเรากำลังเปรียบเทียบตัวเลขนั้นกับ 61 ดังนั้นเราจะเห็นว่า "โอเวอร์โฟลว์" จะเท่ากับ 61 ทุกๆ วินาที! ดังนั้นตัวจัดการของเราจะรีเซ็ต "โอเวอร์โฟลว์" เป็นศูนย์ทุกๆ วินาที ดังนั้น หากเราเพียงแค่ตรวจสอบตัวแปร "โอเวอร์โฟลว์" และจดบันทึกทุกครั้งที่รีเซ็ตเป็นศูนย์ เราจะนับแบบวินาทีต่อวินาทีแบบเรียลไทม์ (โปรดทราบว่าในบทช่วยสอนถัดไป เราจะแสดงวิธีทำให้แม่นยำยิ่งขึ้น หน่วงเวลาเป็นมิลลิวินาทีแบบเดียวกับที่รูทีน "ดีเลย์" ของ Arduino ทำงาน)

ตอนนี้เราได้ "จัดการ" ตัวจับเวลาล้นขัดจังหวะแล้ว ตรวจสอบให้แน่ใจว่าคุณเข้าใจวิธีการทำงาน จากนั้นไปยังขั้นตอนต่อไปที่เราจะใช้ประโยชน์จากข้อเท็จจริงนี้

ขั้นตอนที่ 7: ล่าช้า

ตอนนี้เราได้เห็นแล้วว่ารูทีนโอเวอร์โฟลว์ตัวจัดการอินเตอร์รัปต์ตัวจัดการ "overflow_handler" ของตัวจับเวลาจะตั้งค่าตัวแปร "โอเวอร์โฟลว์" เป็นศูนย์หนึ่งครั้งในแต่ละวินาที เราสามารถใช้ข้อเท็จจริงนี้เพื่อออกแบบรูทีนย่อย "ดีเลย์"

ดูรหัสต่อไปนี้จากภายใต้การหน่วงเวลาของเรา: label

ล่าช้า:

clr ล้น sec_count: cpi ล้น, 30 brne sec_count ret

เราจะเรียกรูทีนย่อยนี้ทุกครั้งที่เราต้องการความล่าช้าในโปรแกรมของเรา วิธีทำงานคือตั้งค่าตัวแปร "โอเวอร์โฟลว์" เป็นศูนย์ก่อน จากนั้นจะเข้าสู่พื้นที่ที่มีป้ายกำกับ "sec_count" และเปรียบเทียบโอเวอร์โฟลว์กับ 30 หากไม่เท่ากัน ระบบจะแยกกลับไปที่ป้ายกำกับ sec_count และเปรียบเทียบซ้ำแล้วซ้ำอีก ฯลฯ จนกว่าจะเท่ากันในที่สุด (โปรดจำไว้ว่าตลอดเวลาที่สิ่งนี้เกิดขึ้น บนตัวจัดการการขัดจังหวะตัวจับเวลาของเรายังคงเพิ่มตัวแปรโอเวอร์โฟลว์อย่างต่อเนื่องและมีการเปลี่ยนแปลงทุกครั้งที่เราไปรอบ ๆ ที่นี่ เมื่อโอเวอร์โฟลว์เท่ากับ 30 ในที่สุดมันจะออกจากลูปและกลับไปที่ทุกที่ที่เราเรียกว่าล่าช้า: จาก ผลลัพธ์สุทธิคือ ล่าช้า 1/2 วินาที

แบบฝึกหัดที่ 2: เปลี่ยนรูทีน overflow_handler ดังต่อไปนี้:

overflow_handler:

inc ล้น reti

และรันโปรแกรม มีอะไรที่แตกต่างกัน? ทำไมหรือทำไมไม่?

ขั้นตอนที่ 8: กะพริบตา

สุดท้าย มาดูกิจวัตรการกะพริบตากัน:

กะพริบตา:

sbi PORTD, 4 rcall ล่าช้า cbi PORTD, 4 rcall ล่าช้า rjmp กะพริบ

ก่อนอื่นเราเปิด PD4 จากนั้นเราเรียกรูทีนย่อยการหน่วงเวลาของเรา เราใช้ rcall เพื่อที่ว่าเมื่อพีซีได้รับคำสั่ง "ret" มันจะกลับมาที่บรรทัดต่อจาก rcall จากนั้นรูทีนการหน่วงเวลาจะหน่วงเวลา 30 ครั้งในตัวแปรโอเวอร์โฟลว์ดังที่เราได้เห็น และนี่คือเกือบ 1/2 วินาทีพอดี จากนั้นเราปิด PD4 หน่วงเวลาอีก 1/2 วินาที แล้วกลับไปที่จุดเริ่มต้นอีกครั้ง

ผลลัพธ์คือไฟ LED กะพริบ!

ฉันคิดว่าตอนนี้คุณจะเห็นด้วยว่า "กะพริบตา" อาจไม่ใช่โปรแกรม "สวัสดีชาวโลก" ที่ดีที่สุดในภาษาแอสเซมบลี

แบบฝึกหัดที่ 3: เปลี่ยนพารามิเตอร์ต่างๆ ในโปรแกรมเพื่อให้ไฟ LED กะพริบในอัตราที่ต่างกัน เช่น วินาทีหรือ 4 ครั้งต่อวินาที เป็นต้น แบบฝึกหัดที่ 4: เปลี่ยนค่าเพื่อให้ไฟ LED เปิดและปิดตามระยะเวลาที่ต่างกัน ตัวอย่างเช่น เปิดเป็นเวลา 1/4 วินาทีแล้วปิดเป็นเวลา 2 วินาทีหรืออะไรทำนองนั้น แบบฝึกหัดที่ 5: เปลี่ยนบิตเลือกนาฬิกา TCCR0B เป็น 100 แล้วขึ้นไปที่โต๊ะต่อไป เมื่อใดที่มันแยกไม่ออกจากโปรแกรม "hello.asm" จากบทช่วยสอนที่ 1 แบบฝึกหัด 6 (ทางเลือก): หากคุณมีคริสตัลออสซิลเลเตอร์ที่แตกต่างกัน เช่น 4 MHz หรือ 13.5 MHz หรืออะไรก็ตาม ให้เปลี่ยนออสซิลเลเตอร์ 16 MHz ของคุณ บนเขียงหั่นขนมของคุณสำหรับอันใหม่และดูว่ามีผลต่ออัตราการกะพริบของ LED อย่างไร ตอนนี้คุณควรจะสามารถคำนวณได้อย่างแม่นยำและคาดการณ์ว่าจะส่งผลต่ออัตราอย่างไร

ขั้นตอนที่ 9: สรุป

สำหรับคนที่มาไกลถึงขั้นนี้แล้ว ขอแสดงความยินดีด้วย!

ฉันรู้ว่ามันค่อนข้างยากเมื่อคุณอ่านหนังสือและเงยหน้าขึ้นมองมากกว่าที่คุณคิดและทดลอง แต่ฉันหวังว่าคุณจะได้เรียนรู้สิ่งสำคัญต่อไปนี้:

  1. หน่วยความจำโปรแกรมทำงานอย่างไร
  2. SRAM ทำงานอย่างไร
  3. วิธีค้นหาการลงทะเบียน
  4. วิธีค้นหาคำแนะนำและรู้ว่าพวกเขาทำอะไร
  5. วิธีการใช้อินเตอร์รัปต์
  6. CP รันโค้ดอย่างไร SREG ทำงานอย่างไร และเกิดอะไรขึ้นระหว่างการอินเตอร์รัปต์
  7. วิธีทำลูป กระโดด และเด้งไปมาในโค้ด
  8. การอ่าน datasheet สำคัญแค่ไหน!
  9. เมื่อคุณรู้วิธีการทำทั้งหมดนี้สำหรับไมโครคอนโทรลเลอร์ Atmega328p แล้ว จะเป็นการเดินเค้กแบบสัมพัทธ์เพื่อเรียนรู้คอนโทรลเลอร์ใหม่ที่คุณสนใจ
  10. วิธีเปลี่ยนเวลา CPU เป็นเรียลไทม์และใช้ในรูทีนการหน่วงเวลา

ตอนนี้เรามีทฤษฎีมากมายที่สามารถเขียนโค้ดได้ดีขึ้นและควบคุมสิ่งที่ซับซ้อนมากขึ้นได้ บทช่วยสอนต่อไปเราจะทำอย่างนั้น เราจะสร้างวงจรที่ซับซ้อนมากขึ้น น่าสนใจยิ่งขึ้น และควบคุมมันอย่างสนุกสนาน

แบบฝึกหัดที่ 7: "ทำลาย" รหัสในรูปแบบต่างๆและดูว่าเกิดอะไรขึ้น! เด็กอยากรู้อยากเห็นทางวิทยาศาสตร์! คนอื่นล้างจานได้ใช่หรือไม่ แบบฝึกหัดที่ 8: ประกอบรหัสโดยใช้ตัวเลือก "-l" เพื่อสร้างไฟล์รายการ เช่น. "avra -l blink.lst blink.asm" และดูไฟล์รายการ เครดิตพิเศษ: รหัสที่ไม่ได้แสดงความคิดเห็นที่ฉันให้ไว้ตอนต้นและรหัสแสดงความคิดเห็นที่เราพูดถึงในภายหลังแตกต่างกัน! มีโค้ดบรรทัดหนึ่งที่แตกต่างกัน คุณสามารถหามันได้หรือไม่ ทำไมความแตกต่างนั้นไม่สำคัญ?

หวังว่าคุณจะสนุก! เจอกันใหม่ตอนหน้า…

แนะนำ: