در قسمت قبلی آموزش، در مورد میکروکنترلر و میکروپروسسور و تفاوت بین آنها بحث شد. در این جلسه به بررسی معماری داخلی AVR، انواع حافظهها، نحوه عملکرد CPU و واحدهای مختلف در میکروکنترلر AVR میپردازیم.
در دوره فعلی، سرفصل آموزشها بهگونهای تنظیمشدهاند که ضمن اشارهای کوتاه به سختافزار داخلی، جنبه عملی و کاربردی داشته باشند تا هنرجو در کمترین زمان ممکن به نتیجه برسد. همچنین در سری آموزشهای قبلی AVR که منبع آن، ویدئوهای آموزشی جناب مهندس کی نژاد است، به ساختار داخلی AVR توجه ویژهتری شده است. بنابراین اگر میخواهید با جزئیات بیشتر سختافزاری آشنا شوید، حتماً مطالعهای بر روی این دوره نیز داشته باشید.
برنامهنویس میتواند با زبانهای مختلفی برای AVR برنامه بنویسد، ازجمله C,PYTHON,BASIC,Pascal,CPP (Arduino) و… اما درنهایت پس از کامپایل شدن، برنامه نوشتهشده به زبان ماشین یا اسمبلی تبدیل میگردد. زبان ماشین درواقع تعدادی دستور سطح پایین (صفر و یکی) و قابلفهم برای ماشین میباشد. این دستورات با توجه به نوع معماری CPU متفاوت است. بازدهی هر یک از زبانها یا روشهای مختلف، ممکن است کمی باهم تفاوت داشته باشند.
معماری CISC
در این نوع معماری، دستورات طولانی و پیچیده است و نوشتن برنامه را ساده میکند، اما سرعت اجرا را کندتر میکند. در این نوع معماری، هدف اصلی سادهتر کردن برنامهنویسی است. بنابراین در ساخت این نوع ریزپردازندهها از ترانزیستورهای بیشتری استفادهشده است. بنابراین مصرف انرژی و دمای ایجادشده بالاتر میرود.
معماری RISC
معماری ریسک، نقطه مقابل معماری CISC است. در این معماری دستورات ساده و کم هستند، اما اجرای آنها سریعتر است. معماری هسته میکروکنترلر AVR از نوع RISC میباشد. ویژگی دستورات AVR طول ثابت 16 یا 32 بیت است که باعث میشود رمزگشایی آن توسط CPU سادهتر شود. تعداد محدود دستورات در معماری RISC، نوشتن برنامه به زبان اسمبلی را مشکلتر میکند. بنابراین منطقی است که از یک زبان سطح بالا برای برنامهنویسی بر روی آن استفاده شود. برای مثال، زبان C یک زبان میانی است که برنامهنویس قادر خواهد بود مستقیماً با حافظه در ارتباط باشد. با توجه به ساختار و قدرت این زبان و همچنین وجود کامپایلر قدرتمند و متن بازی مثل GCC، زبان C یکی از بهترین زبانهای برای برنامهنویسی بهینه بر روی تراشههای AVR میباشد.
به بلوک دیاگرام کلی و معماری AVR دقت کنید:
همانطور که در بلوک دیاگرام کل AVR هم میبینید، پردازنده اصلی AVR از طریق گذرگاههای داده، به واحدهای مختلفی متصل شده است. این واحدها ممکن است در شمارههای مختلف میکروکنترلر AVR متفاوت باشند.
حافظه برنامه یا فلش رام
پسازاینکه برنامه کامپایل شد و به دستورات سطح پایین، مطابق با معماری AVR تبدیل شد، با عمل پروگرام کردن در میکروکنترلر در حافظه پایدار Flash میکروکنترلر قرار میگیرد. حافظه Flash از جنس ROM میباشد. نحوه ریختن برنامه بر روی AVR و مفهوم بوت لودر را در جلسات بعدی یاد خواهید گرفت. در حالت عادی، محتویات این حافظه برای CPU فقط خواندنی است و با قطع برق نیز پاک نمیشود. دستورات ماشین، یکییکی و با اعمال هر پالس ساعت به CPU، فراخوانی شده و اجرا میشوند. CPU بهطور مداوم در حال فراخوانی و اجرای دستورات است. به عمل فراخوانی دستورات از حافظه فلش، اصطلاحاً Fetch کردن و به اجرای آن EXECUTE میگویند.
حافظه SRAM
حافظه SRAM از جنس RAM است و نقشی همانند RAM در کامپیوترهای شخصی را دارد. داخل خود پردازنده 32 عدد ثبات (رجیستر) وجود دارد که برنامهنویس برای انجام محاسبات میتواند از آنها استفاده کند. اما اگر در حین اجرای برنامه به حافظه موقت بیشتری نیاز شد، میتوان از حافظه SRAM استفاده کرد. برای مثال، متغیرهای محلی و پشته در این حافظه قرار میگیرند. محتویات این حافظه با قطع برق پاک میشوند. یک میکروکنترلر بدون SRAM هم میتواند کار کند، اما برنامهنویسی برای آن بسیار سخت میشود. برای مثال، میکروکنترلر ATtiny11 این نوع حافظه را ندارد.
حافظه EEPROM
اطلاعاتی که در حین اجرای برنامه بهدست میآیند و میخواهیم آنها را ذخیره کنیم تا با قطع برق پاک نشوند، در حافظه EEPROM قرار میگیرند. برای مثال، فرض کنید کاربر استفادهکننده از دستگاه شما، تنظیمات یا مقادیری را به آن وارد میکند، اما میخواهیم بار بعدی که دستگاه روشن میشود، این اطلاعات را مجدداً از حافظه داخلی بخوانیم و مجدداً از کاربر درخواست ورود اطلاعات نشود، در اینجا باید از حافظه EEPROM استفاده کنیم. محتویات این حافظه توسط CPU قابلتغییر هستند. اما نسبت به حافظه RAM سرعت کمتری دارند. همچنین شما فقط به تعداد محدودی میتوانید در این نوع حافظه اطلاعات بنویسید. برای مثال، در اتمگا 16 شما تنها میتوانید 100000 بار درون یک بایت اطلاعات بنویسید ولی برای خواندن اطلاعات محدودیتی وجود ندارد. بنابراین دقت کنید که هیچگاه عمل نوشتن در حافظه EEPROM را درون حلقههای تکرار یا هرجایی که ممکن است ناخواسته تعداد دفعات زیادی اجرا شود، قرار ندهید!
بلوک پردازنده AVR
همانطور که بالاتر در بلوک دیاگرام کلی AVR دیدید، واحد پردازنده از حافظه Flash و RAM مجزا میباشد، بنابراین دقت داشته باشید که تصویر بالا بلوک دیاگرام پردازنده AVR نیست. درواقع یک بلوک دیاگرام ساده از نحوه عملکرد معماری AVR است. وظیفه Program counter شمارش دستورات اجراشده میباشد. هر دستوری که توسط CPU انجام شد، یکی به مقدار شمارنده برنامه اضافه میشود. با توجه به این شماره، دستور بعدی از حافظه Flash برای اجرا صدا زده میشود. دستوری که از حافظه فلش واکشی میشود، درون Instruction Register یا ثبات دستور قرار میگیرد. در مرحله بعد، دستور توسط بلوک Instruction decode رمزگشایی میشود و فرامین لازم برای اجرای دستور به ALU ارسال میشود. در حین اجرای برنامه، ممکن است به 32 رجیستر داخلی CPU نیاز باشد. این رجیسترها درون Register File قرار دارند. همچنین اگر نیاز به حافظه موقت (RAM) بیشتری نیاز داشته باشد، از Data Memory استفاده میشود. اگر در حین محاسبات نیاز باشد تا پشتهای درون حافظه RAM ایجاد شود، این پشته توسط Stack Pointer مدیریت میشود. رجیستر وضعیت یا Status Register نیز پرچمهایی مثل وقوع سرریز، عدد منفی، وجود وقفه و… را در حین انجام اعمال ریاضی و منطقی واحد ALU نشان میدهد. این رجیستر قبلاً در سایت بهطور مفصل توضیح دادهشده است.
واحد های جانبی
اگر به بلوک دیاگرام کلی AVR دقت کنید، چهار گذرگاه (بأس) به CPU متصل هستند که دو تای آنها گذرگاه داده (دیتا بأس) هستند. شما به کمک گذرگاههای داده میتوانید به واحدهای مختلف متصل شوید. هرکدام از این گذرگاهها شرایط خاصی برای آدرسدهی دارند. برای مثال خود CPU مستقیماً نمیتواند به گذرگاههای داده متصل شود. به همین دلیل از رجیستر های R26 تا R31 داخل CPU بهعنوان رجیستر های اشارهگر استفاده میکند. همچنین برنامهنویس با استفاده از دستورات اسمبلی مثل IN,OUT,LD و… مشخص میکند که CPU به کدام گذرگاهها متصل شود. خوشبختانه، به لطف زبانهای سطح بالاتر و کتابخانههای آن، تا حد زیادی با دستورات اسمبلی درگیر نخواهید شد. برنامهنویس سطح میانی یا بالا، عملاً برای کار با هر یک از واحدهای جانبی، تنها نیاز به صدازدن آدرس رجیستر آن واحد دارد! برای مثال، اگر شما بخواهید از طریق پایههای AVR اطلاعاتی را ارسال کنید، ابتدا میبایست از طریق رجیستر DDRx درگاه موردنظر را خروجی کنید و سپس با استفاده از رجیستر PORTx مقدار باینری دلخواه را بر روی پایههای آن قرار دهید!
منبع:سیسوگ