فهرست مطالب:

آموزش AVR Assembler 3: 9 مرحله
آموزش AVR Assembler 3: 9 مرحله

تصویری: آموزش AVR Assembler 3: 9 مرحله

تصویری: آموزش AVR Assembler 3: 9 مرحله
تصویری: USART - Project 3 - Interrupt Reception - AVR Microcontroller Design Tutorials - MyElecProjects.com 2024, نوامبر
Anonim
آموزش AVR Assembler 3
آموزش AVR Assembler 3

به آموزش شماره 3 خوش آمدید!

قبل از شروع کار می خواهم یک نکته فلسفی را مطرح کنم. از آزمایش مدارها و کدی که ما در این آموزش ها ایجاد می کنیم نترسید. سیمهای اطراف را تغییر دهید ، اجزای جدید را اضافه کنید ، اجزا را خارج کنید ، خطوط کد را تغییر دهید ، خطوط جدید اضافه کنید ، خطوط را حذف کنید و ببینید چه اتفاقی می افتد! شکستن هر چیزی بسیار دشوار است و اگر این کار را بکنید ، چه کسی اهمیت می دهد؟ هیچ چیزی که ما از آن استفاده می کنیم ، از جمله میکروکنترلر ، بسیار گران نیست و همیشه باید دید که چگونه چیزها خراب می شوند. نه تنها خواهید فهمید که دفعه بعد چه کارهایی را نباید انجام دهید ، بلکه مهمتر از همه خواهید دانست که چرا این کار را نکنید. اگر شما هم مثل من هستید ، وقتی بچه بودید و اسباب بازی جدیدی تهیه می کردید ، مدت زیادی نگذشت که آن را تکه تکه کردید تا ببینید چه چیزی باعث تیک آن می شود؟ گاهی اوقات اسباب بازی به طور جبران ناپذیری آسیب می بیند اما مهم نیست. اجازه دادن به کودک برای کنجکاوی خود حتی در حد اسباب بازی های شکسته ، چیزی است که او را به جای ماشین ظرفشویی به یک دانشمند یا مهندس تبدیل می کند.

امروز ما قصد داریم یک مدار بسیار ساده را سیم کشی کنیم و سپس کمی به نظریه بپردازیم. با عرض پوزش ، اما ما به ابزار نیاز داریم! من قول می دهم که این کار را در آموزش 4 جبران می کنیم ، جایی که ما ساخت مدارهای جدی تری را انجام می دهیم و نتیجه بسیار جالب خواهد بود. با این حال ، روشی که شما برای انجام همه این آموزش ها نیاز دارید به روشی بسیار کند و تامل برانگیز است. اگر فقط در حال عبور هستید ، مدار را بسازید ، کد را کپی و جایگذاری کنید و آن را اجرا کنید ، مطمئناً کار می کند ، اما شما چیزی یاد نمی گیرید. شما باید در مورد هر خط فکر کنید. مکث کنید. آزمایش کنید اختراع کردن. اگر این کار را به این ترتیب انجام دهید ، در پایان آموزش پنجم ، از ساختن چیزهای جالب جلوگیری خواهید کرد و دیگر نیازی به آموزش نخواهید داشت. در غیر این صورت شما به جای یادگیری و خلق ، فقط تماشا می کنید.

در هر صورت ، فلسفه کافی است ، بیایید شروع کنیم!

در این آموزش شما نیاز دارید:

  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 اهم و سپس به Ground وصل کنید. یعنی

PD4 - LED - R (330) - GND

و همین!

اگرچه این نظریه سخت خواهد بود…

مرحله 2: چرا به نظرات و فایل M328Pdef.inc نیاز داریم؟

من فکر می کنم ما باید با نشان دادن اینکه چرا فایل شامل و نظرات مفید است شروع کنیم. هیچ کدام از آنها در واقع ضروری نیستند و می توانید بدون آنها کد را بنویسید ، مونتاژ و بارگذاری کنید و کاملاً خوب اجرا می شود (اگرچه بدون فایل شامل ممکن است برخی از شکایات از مونتاژ کننده دریافت کنید - اما بدون خطا)

در اینجا کدی است که ما امروز خواهیم نوشت ، با این تفاوت که من نظرات و فایل شامل را حذف کرده ام:

. دستگاه ATmega328P

.org 0x0000 jmp a.org 0x0020 jmp ea: ldi r16، 0x05 out 0x25، r16 ldi r16، 0x01 sts 0x6e، r16 sei clr r16 out 0x26، r16 sbi 0x0a، 0x04 sbi 0x0b، 0x04 b، sbi 0x0b b، 0x04 b، sbi 0x0b b 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؛ فرکانس ساعت: 16 مگاهرتز (اختیاری) ؛ **********************************؛ کارکرد برنامه: --------------------- ؛ با چشمک زدن LED ثانیه شمارش می کند. ؛ PD4 - LED - R (330 اهم) - GND ؛ ؛ --------------------------------------. فهرست. شامل "./m328Pdef.inc".list؛ ==============؛ اعلامیه ها:.def temp = r16.def سرریز = r17.org 0x0000؛ حافظه (PC) محل بازنشانی کنترل کننده rjmp بازنشانی ؛ هزینه jmp 2 چرخه پردازنده و هزینه rjmp تنها 1 است. بنابراین مگر اینکه شما نیاز به پرش بیش از 8k بایت داشته باشید. شما فقط به rjmp نیاز دارید بنابراین برخی از میکروکنترلرها فقط. rjmp داشته باشید نه jmp.org 0x0020؛ محل حافظه Timer0 overflow handler rjmp overflow_handler؛ در صورت وقفه سرریز timer0 به اینجا بروید ؛ =========== بازنشانی: ldi temp، 0b00000101 out TCCR0B، temp؛ بیت های انتخاب کننده ساعت CS00 ، CS01 ، CS02 را روی 101 تنظیم کنید ؛ این تایمر 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 روشن کنید ؛ تاخیر 1/2 ثانیه cbi PORTD ، 4 خواهد بود. LED را در تأخیر فراخوانی PD4 خاموش کنید ؛ تاخیر 1/2 ثانیه چشمک زدن rjmp خواهد بود. بازگشت به تاخیر شروع: clr overflows؛ سرریزها را روی 0 sec_count تنظیم کنید: سرریز cpi ، 30 ؛ مقایسه تعداد سرریزها و 30 brne sec_count؛ شاخه به عقب به sec_count اگر برابر نیست ret؛ اگر 30 سرریز رخ داده است ، به چشمک زدن overflow_handler باز می گردد: inc overflows؛ اضافه کردن 1 به overflows متغیر cpi overflows ، 61 ؛ مقایسه با 61 brne PC+2؛ برنامه شمارنده + 2 (از خط بعدی پرش کنید) در صورت سرریز نشدن clr برابر ؛ اگر 61 سرریز رخ داد شمارنده را به صفر reti تنظیم کنید. بازگشت از وقفه

همانطور که می بینید ، نظرات من در حال حاضر مختصرتر است. هنگامی که می دانیم دستورات موجود در مجموعه دستورات چه کار می کنند ، نیازی به توضیح آن در نظرات نداریم. ما فقط باید آنچه را که از نظر برنامه اتفاق می افتد توضیح دهیم.

ما در مورد آنچه همه اینها انجام می دهد صحبت خواهیم کرد ، اما ابتدا بیایید سعی کنیم دیدگاهی جهانی داشته باشیم. بدنه اصلی برنامه به شرح زیر عمل می کند.

ابتدا بیت 4 PORTD را با "sbi PORTD، 4" تنظیم می کنیم و این عدد 1 را به PD4 ارسال می کند که ولتاژ آن را به 5V در آن پین می رساند. با این کار LED روشن می شود. سپس به زیر روال "تأخیر" می پردازیم که 1/2 ثانیه را محاسبه می کند (بعدا توضیح خواهیم داد که چگونه این کار را انجام می دهد). سپس به چشمک زدن و پاک کردن بیت 4 در PORTD باز می گردیم که PD4 را روی 0V تنظیم می کند و بنابراین LED را خاموش می کند. سپس 1/2 ثانیه دیگر به تأخیر می اندازیم و سپس دوباره با "پلک زدن rjmp" به ابتدای پلک زدن باز می گردیم.

شما باید این کد را اجرا کنید و ببینید که آنچه را که باید انجام می دهد.

و آنجا شما آن را دارید! این تمام کاری است که این کد از نظر فیزیکی انجام می دهد. مکانیک داخلی آنچه میکروکنترلر انجام می دهد کمی بیشتر درگیر است و به همین دلیل است که ما این آموزش را انجام می دهیم. بنابراین اجازه دهید هر قسمت را به نوبت مورد بحث قرار دهیم.

مرحله 4:.org دستورالعمل های Assembler

ما قبلاً می دانیم که دستورالعمل های.nolist ،.list ،.include و.def از آموزش های قبلی ما چه می کنند ، بنابراین اجازه دهید ابتدا نگاهی به 4 خط کد بعد از آن بیاندازیم:

.org 0x0000

jmp Reset.org 0x0020 jmp overflow_handler

دستور.org به مونتاژ کننده می گوید که عبارت "Memory Program" را در کدام قسمت قرار دهد. همانطور که برنامه شما اجرا می شود ، "برنامه شمارنده" (که بعنوان رایانه شخصی شناخته می شود) حاوی آدرس خط فعلی در حال اجرا است. بنابراین در این حالت وقتی رایانه 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 حاوی دستورالعمل ما برای پرش به قسمتی است که ما آن را Reset نامگذاری کردیم. اکنون می بینیم که عبارت ".org 0x0020" چه می کند. این می گوید که ما می خواهیم دستورالعمل خط بعدی در محل حافظه 0x0020 قرار گیرد. دستورالعملی که ما در آنجا قرار داده ایم ، پرش به قسمتی از کد ما است که ما آن را "overflow_handler" برچسب گذاری کرده ایم … حالا چرا ما درخواست می کنیم که این پرش در محل حافظه 0x0020 قرار گیرد؟ برای اطلاع از این موضوع ، به صفحه 65 در برگه داده مراجعه کرده و به جدول 12-6 نگاهی می اندازیم.

جدول 12-6 جدولی از "Reset and Interrupt Vector" است و دقیقاً نشان می دهد که رایانه در صورت دریافت "وقفه" به کجا می رود. برای مثال ، اگر به بردار شماره 1 نگاه کنید ، "منبع" وقفه "RESET" است که به عنوان "پین خارجی ، تنظیم مجدد Power-on ، Brown-out Reset ، و تنظیم مجدد سیستم Watchdog" تعریف شده است. این موارد برای میکروکنترلر ما اتفاق می افتد ، کامپیوتر برنامه ما را در محل حافظه برنامه 0x0000 شروع می کند. پس دستورالعمل.org ما چطور؟ خوب ، ما یک فرمان را در محل حافظه 0x0020 قرار دادیم و اگر جدول را نگاه کنید ، خواهید دید که اگر سرریز تایمر/شمارنده 0 اتفاق بیفتد (از TIMER0 OVF) هر آنچه در محل 0x0020 باشد را اجرا می کند. بنابراین هر زمان که این اتفاق بیفتد ، رایانه شخصی به نقطه ای که ما برچسب "overflow_handler" برچسب زده ایم می رود. سرد است درسته؟ شما در یک دقیقه خواهید دید که چرا این کار را کردیم ، اما ابتدا بیایید این مرحله از آموزش را با یک مورد به پایان برسانیم.

اگر می خواهیم کد خود را مرتب تر و مرتب تر کنیم ، باید واقعاً 4 خطی را که در حال بحث در مورد آن هستیم با موارد زیر جایگزین کنید (به صفحه 66 مراجعه کنید):

.org 0x0000

rjmp بازنشانی ؛ PC = 0x0000 reti ؛ PC = 0x0002 reti؛ PC = 0x0004 reti؛ PC = 0x0006 reti؛ PC = 0x0008 reti؛ PC = 0x000A… reti؛ PC = 0x001E jmp overflow_handler: PC = 0x0020 reti: PC = 0x0022… reti؛ PC = 0x0030 reti؛ PC = 0x0032

بنابراین اگر وقفه معینی رخ دهد فقط "reti" یعنی "بازگشت از وقفه" و هیچ چیز دیگری اتفاق نمی افتد. اما اگر ما هرگز این وقفه های مختلف را "فعال" نکنیم ، از آنها استفاده نمی شود و می توانیم کد برنامه را در این نقاط قرار دهیم. در برنامه فعلی "blink.asm" ما فقط قصد داریم وقفه سرریز timer0 (و البته وقفه تنظیم مجدد که همیشه فعال است) را فعال کنیم و بنابراین با بقیه مزاحم نمی شویم.

چگونه می توانیم وقفه سرریز timer0 را "فعال" کنیم؟ … این موضوع مرحله بعدی ما در این آموزش است.

مرحله 5: تایمر/شمارنده 0

تایمر/شمارنده 0
تایمر/شمارنده 0

به تصویر بالا دقت کنید. این فرآیند تصمیم گیری در مورد "رایانه شخصی" است هنگامی که برخی از تأثیرات خارجی جریان برنامه ما را "قطع" می کند. اولین کاری که با دریافت سیگنالی از خارج مبنی بر وقوع وقفه انجام می دهد ، بررسی این است که آیا بیت فعال سازی وقفه را برای آن نوع وقفه تنظیم کرده ایم یا خیر. اگر چنین نکرده ایم ، فقط خط بعدی کد ما را اجرا می کند. اگر آن بیت فعال سازی وقفه خاص را تنظیم کرده باشیم (به طوری که در آن مکان 1 به جای 0 وجود داشته باشد) ، سپس بررسی می کند که آیا "وقفه های سراسری" را فعال کرده ایم یا خیر ، در غیر این صورت دوباره به خط بعدی می رود. کد و ادامه دهید اگر وقفه های سراسری را نیز فعال کرده ایم ، به مکان برنامه حافظه آن نوع وقفه می رود (همانطور که در جدول 12-6 نشان داده شده است) و هر فرمانی را که در آنجا قرار داده ایم اجرا می کند. بنابراین بیایید ببینیم چگونه همه اینها را در کد خود پیاده سازی کرده ایم.

بخش برچسب گذاری Reset کد ما با دو خط زیر شروع می شود:

بازنشانی:

دما ldi ، 0b00000101 out TCCR0B ، دما

همانطور که قبلاً می دانیم ، این عدد بلافاصله در دما (یعنی R16) بارگذاری می شود ، که 0b00000101 است. سپس این عدد را با استفاده از دستور "out" در رجیستری به نام TCCR0B می نویسد. این ثبت چیست؟ خوب ، بیایید به صفحه 614 صفحه داده برویم. این در وسط یک جدول خلاصه ای از تمام رجیسترها قرار دارد. در آدرس 0x25 TCCR0B را خواهید یافت. (اکنون می دانید که خط "out 0x25، r16" در نسخه کد من بدون نظر من از کجا آمده است). با بخش کد بالا می بینیم که بیت 0 و بیت دوم را تنظیم کرده ایم و بقیه را پاک کرده ایم. با مشاهده جدول می توانید متوجه شوید که این بدان معناست که ما CS00 و CS02 را تنظیم کرده ایم. اکنون بیایید به سراغ فصل در برگه داده بنام "تایمر 8 بیتی/Counter0 با PWM" برویم. به طور خاص ، به صفحه 107 آن فصل بروید. شما همان توصیف ثبت کننده "تایمر/شمارنده کنترل B" (TCCR0B) را مشاهده می کنید که ما در جدول خلاصه ثبت مشاهده کردیم (بنابراین می توانستیم مستقیماً به اینجا بیاییم ، اما می خواستم نحوه استفاده از جداول خلاصه را ببینید. برای مراجعات بعدی). برگه اطلاعات توضیحات مربوط به هر یک از بیت های موجود در آن ثبت نام و آنچه انجام می دهند را ادامه می دهد. ما در حال حاضر از همه این موارد صرف نظر کرده و صفحه را به جدول 15-9 تبدیل می کنیم. این جدول "Clock Select Bit Description" را نشان می دهد. حالا آن جدول را نگاه کنید تا خطی را پیدا کنید که مربوط به بیت هایی است که ما در آن ثبت نام قرار داده ایم. این خط می گوید "clk/1024 (from prescaler)". این بدان معناست که ما می خواهیم Timer/Counter0 (TCNT0) با سرعتی که فرکانس CPU بر 1024 تقسیم می کند تیک بزند. از آنجایی که میکروکنترلر ما از نوسانگر کریستالی 16 مگاهرتز تغذیه می کند ، به این معنی است که سرعت اجرای دستورالعمل 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" است. … آنجا! حالا شما می بینید این همه چیست. ما در حال حاضر "مجموعه وقفه فعال کردن بیت" را همانطور که از اولین تصمیم در تصویر خود در بالا می خواستیم ، داریم. بنابراین اکنون تنها کاری که باید انجام دهیم این است که "وقفه های جهانی" را فعال کنیم و برنامه ما قادر خواهد بود به این نوع وقفه ها پاسخ دهد. ما وقفه های جهانی را به زودی فعال می کنیم ، اما قبل از انجام این کار ممکن است با چیزی گیج شده باشید.

هر زمان که می بینید از دستورالعملی استفاده می کنید که قبلاً ندیده اید ، اولین کاری که باید انجام دهید این است که به صفحه 616 در برگه داده مراجعه کنید. این "خلاصه مجموعه دستورالعمل" است. اکنون دستورالعمل "STS" را که من استفاده کردم پیدا کنید. این نشان می دهد که یک شماره از یک رجیستر R (ما از R16 استفاده کردیم) و "ذخیره مستقیم در SRAM" مکان k (در مورد ما توسط TIMSK0 داده شده است) می گیرد. بنابراین چرا ما مجبور شدیم از "sts" که 2 چرخه ساعت (برای مشاهده آخرین ستون در جدول) استفاده می شود برای ذخیره در TIMSK0 استفاده کنیم و ما برای ذخیره در TCCR0B فقط به "خارج" که تنها یک چرخه ساعت زمان می برد نیاز داشتیم؟ برای پاسخ به این س weال باید به جدول خلاصه ثبت ما در صفحه 614 برگردیم. مشاهده می کنید که ثبت TCCR0B در آدرس 0x25 اما همچنین در (0x45) است؟ این بدان معناست که این یک ثبت کننده در SRAM است ، اما همچنین یک نوع خاص از ثبت نام است که به آن "پورت" (یا ثبت نام i/o) می گویند. اگر به جدول خلاصه دستورالعمل در کنار دستور "out" نگاه کنید ، می بینید که مقادیری از "رجیسترهای کار" مانند R16 گرفته و به PORT ارسال می کند. بنابراین می توانیم هنگام نوشتن به TCCR0B از "خارج" استفاده کنیم و یک چرخه ساعت را برای خود ذخیره کنیم. اما اکنون TIMSK0 را در جدول ثبت جستجو کنید. می بینید که آدرس 0x6e دارد. این خارج از محدوده پورت ها (که تنها مکانهای اولیه 0x3F SRAM هستند) است و بنابراین باید از دستور sts استفاده کنید و دو چرخه کلاک CPU را برای انجام آن انجام دهید. لطفاً توجه داشته باشید 4 را در انتهای جدول خلاصه دستورالعمل در صفحه 615 همین حالا بخوانید. همچنین توجه داشته باشید که همه پورت های ورودی و خروجی ما ، مانند PORTD در انتهای جدول قرار دارند. به عنوان مثال ، PD4 بیت 4 در آدرس 0x0b است (اکنون می بینید که همه موارد 0x0b در کد من بدون نظر از کجا آمده است!).. خوب ، سوال سریع: آیا "sts" را به "out" تغییر داده اید و می بینید چه چیزی اتفاق می افتد؟ فلسفه ما را به خاطر بسپارید! بشکن! فقط حرف من را برای چیزها قبول نکنید

خوب ، قبل از ادامه کار ، یک دقیقه به صفحه 19 در برگه اطلاعات برگردید. تصویر حافظه داده (SRAM) را مشاهده می کنید. اولین 32 ثبت در SRAM (از 0x0000 تا 0x001F) "ثبت کارکرد عمومی" R0 تا R31 است که ما همیشه به عنوان متغیر در کد خود از آنها استفاده می کنیم.64 رجیستر بعدی ، پورت های ورودی/خروجی تا 0x005f هستند (یعنی مواردی که ما در مورد آنها صحبت می کردیم ، آدرس های بدون پرانتزی در کنار آنها در جدول ثبت وجود دارد که می توانیم از دستور "out" به جای "sts" استفاده کنیم) بخش بعدی SRAM شامل تمام ثبت های دیگر در جدول خلاصه تا آدرس 0x00FF است و در نهایت بقیه SRAM داخلی است. حالا سریع ، بیایید یک ثانیه به صفحه 12 برگردیم. در آنجا جدولی از "رجیسترهای کار عمومی" را مشاهده می کنید که ما همیشه از آنها به عنوان متغیرهای خود استفاده می کنیم. آیا خط ضخیم بین اعداد R0 تا R15 و سپس R16 تا R31 را می بینید؟ این خط به همین دلیل است که ما همیشه از R16 به عنوان کوچکترین استفاده می کنیم و من در آموزش بعدی کمی بیشتر به آن می پردازم ، جایی که ما همچنین به سه ثبت آدرس غیر مستقیم 16 بیتی ، X ، Y و Z نیاز داریم. هر چند که هنوز به آن نیاز ندارید و در اینجا به اندازه کافی گرفتار شده ایم وارد آن شوید.

یک صفحه را به صفحه 11 برگ برگردانید. شما نمودار ثبت SREG را در بالا سمت راست مشاهده خواهید کرد؟ می بینید که بیت 7 آن ثبت نام "من" نام دارد. حالا صفحه را پایین بیاورید و توضیحات بیت 7 را بخوانید…. بله این بیت Global Interrupt Enable است. این همان چیزی است که ما باید تنظیم کنیم تا از تصمیم دوم در نمودار بالا عبور کنیم و اجازه دهیم وقفه سرریز تایمر/شمارنده در برنامه ما ایجاد شود. بنابراین خط بعدی برنامه ما باید بخواند:

sbi SREG ، I

که بیتی به نام "I" را در ثبات SREG تنظیم می کند. با این حال ، به جای این ، ما از دستورالعمل استفاده کرده ایم

sei

بجای. این بیت آنقدر در برنامه ها تنظیم شده است که آنها فقط روش ساده تری برای انجام آن ارائه کرده اند.

باشه! اکنون ما وقفه های سرریز را آماده کرده ایم تا "jmp overflow_handler" ما هر زمان که رخ می دهد اجرا شود.

قبل از حرکت ، نگاهی سریع به ثبت SREG (Status Register) بیندازید زیرا بسیار مهم است. بخوانید که هر یک از پرچم ها نشان دهنده چیست. به طور خاص ، بسیاری از دستورالعمل هایی که ما استفاده می کنیم این پرچم ها را همیشه تنظیم و بررسی می کند. به عنوان مثال ، بعداً از دستور "CPI" استفاده می کنیم که به معنی "مقایسه فوری" است. نگاهی به جدول خلاصه دستورالعمل این دستورالعمل بیندازید و توجه کنید که چند پرچم در ستون "پرچم" تنظیم کرده است. اینها همه پرچم های SREG هستند و کد ما آنها را تنظیم و به طور مداوم آنها را بررسی می کند. به زودی نمونه هایی را مشاهده خواهید کرد. در نهایت آخرین بیت این بخش از کد عبارت است از:

دمای clr

out TCNT0، temp sbi DDRD، 4

آخرین خط در اینجا کاملاً واضح است. فقط چهارمین بیت ثبت داده جهت را برای PortD تنظیم می کند که باعث خروجی PD4 می شود.

مورد اول دمای متغیر را صفر می کند و سپس آن را در ثبات TCNT0 کپی می کند. TCNT0 تایمر/شمارنده 0 ما است. با این کار صفر می شود. به محض اجرای رایانه توسط کامپیوتر ، تایمر 0 از صفر شروع می شود و در هر ثانیه 15625 بار محاسبه می شود. مشکل اینجاست: TCNT0 یک "8 بیتی" است ، درست است؟ بنابراین بزرگترین عددی که یک ثبت کننده 8 بیتی می تواند نگه دارد چقدر است؟ خوب 0b11111111 این است. این عدد 0xFF است. 255 است. بنابراین می بینید چه اتفاقی می افتد؟ تایمر 15625 بار در ثانیه افزایش می یابد و هر بار که به 255 برسد "سرریز" می شود و دوباره به 0 برمی گردد. همزمان با صفر شدن ، سیگنال وقفه سرریز تایمر را ارسال می کند. رایانه شخصی این را دریافت می کند و می دانید در حال حاضر چه می کند؟ بله به محل حافظه برنامه 0x0020 می رود و دستورالعمل هایی را که در آنجا پیدا می کند اجرا می کند.

عالی! اگر هنوز با من هستید پس یک ابرقهرمان خستگی ناپذیر هستید! بیا ادامه بدیم…

مرحله 6: کنترل کننده سرریز

بنابراین بیایید فرض کنیم که تایمر/counter0 ثبت نام به تازگی پر شده است. ما اکنون می دانیم که برنامه یک سیگنال وقفه دریافت می کند و 0x0020 را اجرا می کند که به برنامه شمارنده می گوید ، رایانه شخصی به برچسب "overflow_handler" بروید.

overflow_handler:

inc سرریز cpi سرریز ، 61 brne PC+2 clr سرریز reti

اولین کاری که می کند این است که متغیر "overflows" را افزایش می دهد (که نام ما برای ثبت کار عمومی R17 است) و سپس محتویات سرریزها را با عدد 61. "مقایسه" می کند. دو عدد و اگر نتیجه صفر باشد ، پرچم Z را در ثبت SREG قرار می دهد (من به شما گفتم که ما همیشه این رجیستر را خواهیم دید). اگر دو عدد برابر باشند ، پرچم Z یک خواهد بود ، اگر دو عدد برابر نباشند ، صفر خواهد بود.

خط بعدی می گوید "brne PC+2" که به معنی "اگر برابر نباشد" است. در اصل ، پرچم Z را در SREG بررسی می کند و اگر یک نباشد (یعنی دو عدد با هم برابر نباشند ، اگر یکسان باشند ، پرچم صفر تنظیم می شود) کامپیوتر به PC+2 منشعب می شود ، به این معنی که بعدی را رد می کند خط می رود و مستقیماً به "reti" می رود که هنگام وقوع وقفه از وقفه به هر مکانی که در کد بود برمی گردد. اگر دستور brne یک عدد در بیت پرچم صفر پیدا کند ، منشعب نمی شود و در عوض فقط به خط بعدی ادامه می دهد که با سرریز مجدد به 0 بازنشانی می شود.

نتیجه خالص همه اینها چیست؟

خوب ما می بینیم که هر بار که یک تایمر سرریز می شود ، این کنترلر مقدار "overflows" را یک برابر افزایش می دهد. بنابراین متغیر "سرریز" در حال شمارش تعداد سرریزها در صورت وقوع است. هرگاه عدد به 61 برسد آن را به صفر بازنشانی می کنیم.

حالا چرا در دنیا چنین می کنیم؟

اجازه بدید ببینم. به یاد بیاورید که سرعت کلاک ما برای پردازنده ما 16 مگاهرتز است و ما آن را با استفاده از TCCR0B "پیش فرض" کردیم تا تایمر فقط با سرعت 15625 شمارش در ثانیه حساب کند ، درست است؟ و هر بار که تایمر به تعداد 255 برسد ، سرریز می شود. بنابراین این بدان معناست که 15625/256 = 61.04 بار در ثانیه سرریز می شود. ما تعداد متغیرهای سرریز را با متغیر "سرریز" خود پیگیری می کنیم و این عدد را با 61. مقایسه می کنیم ، بنابراین می بینیم که "سرریز" در هر ثانیه یک برابر 61 می شود! بنابراین کنترل کننده ما "سرریز" را در هر ثانیه یک بار به صفر تنظیم می کند. بنابراین اگر بخواهیم به سادگی متغیر "overflows" را زیر نظر داشته باشیم و هر بار که آن را به صفر برسانیم توجه داشته باشیم ، ثانیه به ثانیه در زمان واقعی شمارش می کنیم (توجه داشته باشید که در آموزش بعدی نحوه دقیق تر نشان می دهیم. تأخیر در میلی ثانیه به همان شیوه ای که معمول "تاخیر" آردوینو کار می کند).

اکنون ما وقفه های سرریز تایمر را "مدیریت" کرده ایم. مطمئن شوید که نحوه عملکرد این روش را درک کرده اید و سپس به مرحله بعدی که ما از این واقعیت استفاده می کنیم بروید.

مرحله 7: تأخیر

اکنون که مشاهده کرده ایم که کنترل کننده وقفه سرریز زمان سنج ما "overflow_handler" متغیر "overflows" را در هر ثانیه یک برابر صفر می کند ، می توانیم از این واقعیت برای طراحی یک زیر برنامه "تأخیر" استفاده کنیم.

از زیر تأخیر ما به کد زیر نگاه کنید: label

تاخیر انداختن:

clr سرریز می کند sec_count: cpi سرریز می کند ، 30 brne sec_count ret

ما قصد داریم هر زمان که به تاخیر در برنامه خود نیاز داریم ، این زیر روال را صدا کنیم. روش کار این است که ابتدا متغیر "overflows" را روی صفر قرار می دهد. سپس وارد ناحیه ای با برچسب "sec_count" می شود و سرریزها را با 30 مقایسه می کند ، اگر برابر نباشند به برچسب sec_count برمی گردد و دوباره و دوباره و غیره مقایسه می شود تا در نهایت برابر شوند (به یاد داشته باشید که تمام مدت این کار ادامه دارد در تایمر ما ، کنترل کننده وقفه به افزایش سرریزهای متغیر ادامه می دهد و بنابراین هر بار که در اینجا می رویم تغییر می کند. هنگامی که سرریزها سرانجام برابر 30 می شود ، از حلقه خارج می شود و به هر جایی که تاخیر می نامیم باز می گردد: از. نتیجه خالص یک تاخیر 1/2 ثانیه ای

تمرین 2: روال overflow_handler را به موارد زیر تغییر دهید:

overflow_handler:

inc overflows reti

و برنامه را اجرا کنید آیا چیزی متفاوت است؟ چرا و چرا نه؟

مرحله هشتم: پلک بزنید

در نهایت اجازه دهید به روال پلک زدن نگاه کنیم:

پلک زدن:

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 تغییر دهید و سپس به بالا رفتن از جدول ادامه دهید. تمرینات 6 (اختیاری): اگر نوسان ساز کریستالی متفاوتی دارید ، مانند 4 مگاهرتز یا 13.5 مگاهرتز یا هر چیز دیگری ، نوسان ساز 16 مگاهرتز خود را تغییر دهید. روی تخته نورد خود برای صفحه جدید و ببینید که چگونه بر میزان چشمک زدن LED تأثیر می گذارد. اکنون باید بتوانید محاسبه دقیق را انجام داده و دقیقاً پیش بینی کنید که چگونه بر نرخ تأثیر می گذارد.

مرحله 9: نتیجه گیری

برای کسانی از شما سرسختی که تا اینجا پیش رفتند ، تبریک می گویم!

متوجه می شوم زمانی که شما بیشتر مطالعه می کنید و به دنبال آن هستید ، بسیار سخت است ، اما امیدوارم نکات مهم زیر را یاد گرفته باشید:

  1. نحوه عملکرد حافظه برنامه
  2. SRAM چگونه کار می کند
  3. نحوه جستجوی ثبت ها
  4. چگونه به دنبال دستورالعمل ها بگردیم و بدانیم آنها چه کار می کنند
  5. نحوه پیاده سازی وقفه ها
  6. چگونه CP کد را اجرا می کند ، SREG چگونه کار می کند و در وقفه ها چه اتفاقی می افتد
  7. چگونه می توان حلقه ها و پرش ها را انجام داد و در کد بازگردید
  8. خواندن برگه اطلاعات چقدر مهم است!
  9. هنگامی که می دانید چگونه همه اینها را برای میکروکنترلر Atmega328p انجام دهید ، یادگیری کنترلرهای جدید مورد علاقه شما یک راه حل نسبی خواهد بود.
  10. چگونه می توان زمان CPU را به زمان واقعی تغییر داد و از آن در برنامه های تاخیری استفاده کرد.

اکنون که نظریه های زیادی در اختیار داریم ، قادر به نوشتن کد بهتر و کنترل موارد پیچیده تر هستیم. بنابراین آموزش بعدی ما دقیقاً همین کار را انجام خواهیم داد. ما یک مدار پیچیده تر ، جالب تر ایجاد کرده و آن را به روشهای سرگرم کننده کنترل می کنیم.

تمرین 7: کد را به روش های مختلف "بشکنید" و ببینید چه اتفاقی می افتد! کنجکاوی علمی عزیزم! شخص دیگری می تواند ظرف ها را درست بشوید؟ تمرین 8: کد را با استفاده از گزینه "-l" برای ایجاد یک فایل لیست جمع آوری کنید. یعنی "avra -l blink.lst blink.asm" و نگاهی به فایل لیست بیندازید. اعتبار اضافی: کد بدون نظر که در ابتدا دادم و کد کامنتی که بعداً در مورد آن بحث می کنیم متفاوت است! یک خط کد متفاوت است. آیا میتوانی آن را بیابی؟ چرا این تفاوت اهمیتی ندارد؟

امیدوارم به شما خوش گذشته باشد! دفعه بعد میبینمت…

توصیه شده: