ممکن است گاهی اوقات به عنوان یک برنامهنویس با سایر همکاران به بحث و گفتگو در رابطه با اینکه چه ابزار توسعهای بهتر است و کدام نرمافزار نامناسب است پرداخته باشید. موضوع بحث این مقاله هم بررسی تخصصی یکی از این نرمافزار ها است که برای برنامه نویسی میکروکنترلر ها به زبان C انجام میشود.
به نظر من برای توسعه نرمافزار در ایران، معمولاً برنامه نویسان عامه به دو دسته تقسیم میشوند:
همین قضیه هم باعث شده تا فقط درصد کمی از برنامه نویسان میکروکنترلر ها به کامپایلر های توانمند تری مانند gcc روی بیاورند، که اکثر آنها هم از خط مشی پروژههای منبع باز پیروی میکنند. در ادامه سعی میکنم توضیح دهم که علت محبوبیت کدویژن در ایران چیست، و چرا من توصیه میکنم که برای کدنویسی AVR به سمت این نرمافزار نروید و از کامپایلر ها و IDE های بهتری استفاده کنید.
میکروکنترلر های خانواده AVR مانند سری ATmega و ATtiny به دلایل بسیاری جزو محبوب ترین میکروکنترلر های 8 بیتی هستند. در ایران هم با توجه به قیمت نسبتاً پایین و راحتی استفاده از این میکروکنترلر ها و همچنین تدریس در دانشگاه، این میکروکنترلر جزو انتخاب اول بسیاری از طراحان الکترونیک و برنامه نویسان است.
یکی از نرمافزار هایی هم که در حوزه برنامهنویسی برای میکروکنترلر های AVR در ایران استفاده میشود، کدویژن است که یک کامپایلر و IDE کامل مبتنی بر ANSI C است که برای کدنویسی به زبان C برای میکروکنترلر های AVR استفاده میشود.
(اگر تفاوت ANSI C با نگارشهای جدیدتر C و مزایای آنها را نمیدانید، در ادامه به شرح آن میپردازم.)
این نرمافزار به دلیل سادگی استفاده، امکان تولید خودکار کد با Wizard و ارائه کتابخانه برای موارد بدیهی (مانند LCD متنی و...) و همچنین کرک شدن در ایران (به خصوص دانشگاههای آزاد، شرکتها و...) بسیار معروف شده است و تقریباً از بدو نوشتن کد به زبان C برای AVR در ایران تدریس میشود و توسط شرکتها هم مورد استفاده قرار میگیرد.
این نرمافزار بهعنوان یک IDE یا محیط یکپارچه توسعهٔ نرمافزار، دارای تمامی بخشهای لازم مانند ویرایشگر متنی کد، کامپایلر، لینکر و پروگرامر است. نکته جالب توجه این است که تمامی این بخش ها توسط برنامهنویسی به نام Pavel Haiduc توسعه داده شده که توسط شرکتی که به نام HP InfoTech S.R.L دارد آن را به قیمت 178 یورو عرضه میکند.
قیمت این نرمافزار در ایران معادل 10 ملیون تومان در زمان نگارش این مقاله است، که در قیاس با پلتفرمی مانند GCC-AVR که بهصورت رایگان در ادامه به آن خواهم پرداخت مبلغ بسیار بالایی به نظر میرسد، چراکه سطح کیفی و تجربه کاربری برای برنامه نویس هم در این نرمافزار بسیار ضعیف است.
سایت سیسوگ چند مورد از دلایل استفاده این نرمافزار در ایران را توضیح داده است که من آن را نقل قول میکنم:
مساله بعدی که موجب محبوبیت این نرم افزار شده است، وجود کدویزارد می باشد. کدویزارد بخشی از این نرمافزار است که به شما اجازه میدهد پیکربندی اولیه میکروکنترلر را به صورت گرافیکال و ویژوال انجام دهید و نیازی به انجام این پیکر بندی ها به صورت دستی و کدی نداشته باشید.
بله، قطعاً این قابلیت جذابیت ویژه ای برای افراد مبتدی و حتی حرفه ای دارد، وقتی که بهراحتی و با چند کلیک درگاه SPI یا UART یا I2C را پیکربندی می کنید بدون این که درگیر رجیسترها و بیت ها شده باشید، در نگاه اول این فرایند بسیار فریبنده است و آدم خیال می کند که از شر رجیسترها و دستکاری بیت ها راحت شده است، اما واقعیت چیز دیگری است، چرا که تجربه ثابت کرده است در مسیر توسعه برنامه نیاز خواهید داشت که با تک تک بیت ها دست و پنجه نرم کنید و کدویزارد کمکی به شما نمیکند.
یک نکته مهم که من اضافه میکنم این است که به نظر من اساساً تعاریف این درگاهها بهصورت دستی یا حتی کدویزارد یک راهکار و طرز فکر اشتباه است، زمانی که شما کتابخانههای با کیفیت بسیار بالا، آسان جهت استفاده، و High-Performance دارید که امکان شخصی سازی خوبی را هم برای استفاده در پروژههایتان به شما میدهند.
به عنوان مثال: SPI.h، HardwareSerial.h و غیره که در AVR Toolchain وجود دارند و با استفاده از آن ها شما نیازی ندارید که به عنوان مثال پورت SPI یا UART میکروکنترلر را خودتان راه اندازی کنید یا با Timer های سخت افزاری درگیر شوید.
این کتابخانههای دائماً در حال بهروز شدن توسط برنامه نویسان هستند و در هر ورژن جدید از قابلیتها و بهبود های مدرن بهره میبرند، درحالی که کدویزارد در کدویژن محدود به آن میکروکنترلر و تفکرات یک برنامه نویس در آن دوران است. پس هیچ دلیلی ندارد که شما چه کد پیکربندی را خودتان بنویسید یا از کدویزارد استفاده کنید وقتی برای تمامی این بخش ها، کتابخانههای عالی و بهصورت منبع-باز وجود دارد.
از دیگر موارد محبوبیت کامپایلر کدویژن وجود کتابخانه های آماده آن است که کاربر را در راه اندازی دستگاه های کناری یاری می کند، مثلاً برای راه اندازی یک LCD کاراکتری جز این که کاربر در کدویزارد چند کلیک کند و پیکر بندی اولیه را انجام دهد، کار دیگری لازم نیست و خود کدویژن کتابخانه مربوطه رو در برنامه صدا میزند و کار پیکر بندی را برای شما انجام می دهد.
به دلیل اینکه برنامهنویس عامهی ایرانی دنبال سریعترین، سادهترین و راحتترین روش میرود، به استفاده از این کتابخانههای قدیمی و بهروز نشده اکتفا میکند، در حالی که نسخههای متنباز کتابخانه راهانداز LCD متنی (مانند LiquidCrystal که در آردوینو نیز استفاده شده است) هر سال از بهروز رسانیهای مفیدی بهرهمند میشوند که دارای قابلیتهای جدید تر و حذف اشکالات نرمافزاری و باگهایی است که حتماً به آن بر خوردهاید.
اولین و مهم ترین مسئله این است که کدویژن ساختار یک کامپایلر استاندارد را ندارد و از آن پشتیبانی نمی کند. در یک کامپایلر استاندارد ابتدا فایل ها به صورت مجزا به فایل آبجکت تبدیل می شوند و بعد از آن با استفاده از یک دستورالعمل به هم لینک می شوند و فایل خروجی برنامه حاصل می شود.
اگر نمیدانید که یک Linker چهکاری انجام میدهند و فقط معنای Compiler را میدانید، شما تنها نیستید. متأسفانه سیستم آموزشی ناقصی که بر دانشگاههای ایران (مانند دانشگاه آزاد) حکمفرا است نسبت به توضیح دادن کلیهی بخشهای یک Toolchain یا زنجیره افزار مبادرت میکنند و کلیه قسمت تبدیل کد به فایل هکس را بهطور کلی "کامپایل" خطاب میکنند.
بهصورت کوتاه در زمینه لینکر میتوان این تور توضیح داد که یک فایل اجرایی نهایی، حاوی بخشهای مختلفی از کد کامپایل شده میباشد که معمولاً این بخشهای مختلف بهصورت جداگانه کامپایل و تبدیل به فایل o (که یک آبجکت هست) تبدیل میشوند. سپس به کمک ابزار لینکر، این فایلهای آبجکت در کنار همدیگر قرار میگیرند تا خروجی اجرایی نهایی تولید شود.
یکی از مهمترین دلایل استفاده از لینکر به این صورت است که شما فقط منبع یا سورس کد پروژه خودتان را در این حالت کامپایل میکنید، و هر بار نیاز به کامپایل تمامی کتابخانههای استفاده شده (مانند libc ندارید.) تصور کنید اگر هر بار که کلید Compile پروژه را میزدید، لازم بود که همهی بخشهای پروژه کامپایل شود! حتی بخشهای که شما در آن هیچ تغییراتی نداده باشید.
اگر شما یک کامپیوتر Workstation با پردازنده Core i9 و تعداد زیادی هسته داشته باشید و کامپایلر از قابلیت Multi-threaded برای کامپایل استفاده کند شاید برای شما مهم نباشد که هر دفعه کل پروژه را Build کنید، ولی برای تمامی برنامهنویسان حرفهای و نیمه حرفه این مسئله که فقط قسمتهای تغییر پیدا کرده از سورس کد کامپایل شود قطعاً از اهمیت ویژهای برخوردار است.
یکی دیگر از مزایای لینکر هم به این صورت است که شما میتوانید فایل کتابخانه خود را به صورت کامپایل شده منتشر کنید، یا از فایل های منتشر شدهی کامپایل شده استفاده کنید که در کنار محافظت از منبع کد کتابخانه، سرعت Build فایل خروجی را نیز به نحو احسن افزایش میدهد.
این ساختار علاوه بر اصولی بودن مزایا خیلی زیادی دارد که به عنوان نمونه به سرعت کامپایل اشاره شد، زیرا که فقط فایل های تغییر کرده نیاز به کامپایل مجدد دارند و نیاز نیست هر سری کل فایل ها کامپایل شوند (بر خلاف چیزی که در کدویژن اتفاق می افتد). وقتی شما بتوانید فایل ها به صورت مجزا کامپایل کنید یعنی در واقع میتوانید کار گروهی بهتری در روال انجام پروژه داشته باشید، یا این که میتوانید به راحتی از کتابخانه های آماده ای که دیگران نوشته اند بدون کمترین تغییری استفاده کنید. در ضمن برنامه شما میتواند ترکیبی از فایل های اسمبلی و C و C++ باشد.
مسئله مهم بعدی، همانطور که در سایت خود کدویژن به آن اشاره شده است، عدم پشتیبانی کامل از زبان C مدرن است.
اگر در رابطه با تفاوت ANSI C با نگارشهای مدرن این زبان برنامه نویسی اطلاعات ندارید، این توضیح را بدهم که ANSI C اولین ورژن استاندرد زبان C است که بیش از 50 سال پیش در سال 1972 استاندارد سازی شد.
این نگارش از زبان برنامهنویسی تا مدتها تا سال 1990 استفاده شد و به دلیل پیشرفت تکنولوژی، سختافزار کامپیوتر و حتی خود مکانیزم کامپایلر ها ورژن های کاملتری از این زبان ارائه شد: مثل C98,C90,C95,C99,C11 که هر کدام بین سالهای 1990 تا 2011 ارائه شده اند.
متأسفانه کدویژن هیچ کدام از این نگارشهای بهروز را پشتیبانی نمیکند؛ پس در واقع وقتی که از کدویژن استفاده میکنید شما مجبور هستید از نسخه اولیه زبان C استفاده کنید که همین امر باعث میشود که بسیار به سختی کدنویسی کنید، زیرا هیچکدام از قابلیتهای روزمره، سریع و مدرنی که انتظارش را دارید در دسترس شما نیستند! حتی خیلی از کتابخانه های آماده موجود برای شما غیر قابل استفاده میشوند، زیرا توسعه دهندههای این کتابخانه ها هم از از قابلیتها استفاده کرده اند.
مثال اول: عدم پشتیبانی از آرایههایی که تعداد متغیر دارند:
// Variable-length array (C99 feature) // CodeVisionAVR will give an error: "array size must be constant" void foo (int n) { int arr[n]; // array size depends on n // do something with arr }
همانطور که در این مثال مشاهده می کنید، کدویژن قابلیت تعریف کردن یک آرایه که تعداد متغیری داشته باشد را ندارد! شاید شما به عنوان برنامهنویس از این مسئله چشم پوشی کنید یا به نحو دیگری نیازمندی خود را برطرف سازید، اما بیشتر اوقات این کار از لحاظ حفظ منابع محدود و اندک میکروکنترلر AVR به ضرر شما است.
مثال دوم: تعریف مقادیر بر اساس خلاف ترتیب struct با designator
// Designated initializer (C99 feature) // CodeVisionAVR will give an error: "invalid initializer" struct point { int x; int y; }; struct point p = {.y = 5, .x = 10}; // initialize y before x
در این مثال، ما یک struct تعریف کردهایم که در آن به y پیش از x مقدار داده ایم. در این حالت، که به عنوان برنامهنویس باید برای شما امری بدیهی باشد، کدویژن خطای invalid initializer می دهد!
مثال سوم: استفاده از instruction اسمبلی در کد C
// Inline assembly (GCC extension) // CodeVisionAVR will give an error: "unknown identifier" asm volatile("nop"); // insert a no-operation instruction
باز هم در این مثال از قابلیت ساده و پیش پا افتاده ای مانند استفاده از دستور اسمبلی NOP یا No Operation در کد ذکر شده است، که کدویژن حتی این قابلیت را نیز پشتیبانی نمیکند!
در واقع کدویژن از تمام قابلیت های زبان ANSI C هم استفاده نمی کند، به عنوان مثال شما در کدویژن قادر به تعریف اشاره گر به یک فانکشن نیستید (البته شاید در نسخه های جدید اضافه شده باشد.)
و نیز قابلیتهای بسیار دیگری مانند استفاده از attribute ها که در کدویژن هیچ پشتیبانیای از آن صورت نگرفته.
بیشترین مسئلهای که من را شخصاً در کدنویسی بسیار کلافه کرده بود این بود که در کامپایلر مزخرف کدویژن شما نمیتوانید یک متغیر را در وسط فانکشن تعریف کنید و حتماً باید در ابتدا تعریف شده باشد.
خب پس فایده استفاده از Dynamic Memory Allocation چیست وقتی که این امر در میکروکنترلر امکان پذیر است! یا مثال ساده تر، در صورتی که شما یک For Loop نوشته اید، ممکن است بخواهید که متغیر اندیس را بهصورت موقت در وسط تابع بهصورت U8 (همان Unsigned Byte) تعریف کنید. این وظیفه باید به یک کامپایلر خوب محول شود تا Initialization متغیر را به صورت خودکار انجام دهد و حتی با Optimization Pass انجا شده در کد، از افزودن متغیر های اضافی خودداری کند.
ولی در کد ویژن باید این شما باشید که همواره متغیر های اندیس را در بالای تابع لحاظ کنید تا خدای نکرده، کد ویژن از Initialize کردن یک متغیر توسط شما در وسط برنامه شگفت زده و ناراضی نشود!
مثال از کدی که من میخواهم استفاده کنم:
void foo() { int a = 10; // declare and initialize a variable printf("a = %d\n", a); // statement int b = 20; // declare and initialize another variable - ERROR printf("b = %d\n", b); // statement }
مثال از نحوه تغییری که باید به دلیل استفاده از کدویژن بدهم:
void foo() { int a = 10; // declare and initialize a variable int b = 20; // declare and initialize another variable printf("a = %d\n", a); // statement printf("b = %d\n", b); // statement }
علت این محدودیت هم این است که در استاندارد ANSI C89 که کدویژن از آن تبعیت میکند، نمیتوان یک متغیر را در وسط یک تابع تعریف کرد.
برای آن دسته از دوستانی هم که الآن می پرسند «خب این قابلیت چه فایدهای دارد؟» من علتهای زیر را لحاظ میکنم:
هر برنامهنویس C که در طی 20 سال گذشته (منظور از زمان C99) کد نویسی میکرده است قطعاً به مزایای قابلیتهایی که من نام بردم (به خصوص Scope متغیر ها) برخورده است. توجه شما را به کد زیر جلب میکنم:
void bar(int n) { printf("n = %d\n", n); // statement if (n > 0) { int a = n + 1; // declare and initialize a variable only if n is positive printf("a = %d\n", a); // statement } else { int b = n - 1; // declare and initialize a variable only if n is negative or zero printf("b = %d\n", b); // statement } }
اکنون شما همین کد را به صورت "کدویژنی" مشاهده میکنید. لطفاً خودتان قضاوت کنید کدام خوانا تر، آسان تر، بهتر و راحت تر است:
void bar(int n) { int a; // declare a variable at the beginning of the function int b; // declare another variable at the beginning of the function printf("n = %d\n", n); // statement if (n > 0) { a = n + 1; // initialize a variable only if n is positive printf("a = %d\n", a); // statement } else { b = n - 1; // initialize a variable only if n is negative or zero printf("b = %d\n", b); // statement } }
همانطور که مشاهده کردید، ما مجبور شدیم متغیر های a و b را هر دو به بالای تابع ببریم. علاوه بر این که این متغیر ها از عملکرد تابع ما دورتر شدند، اکنون متغیر b در شاخه if و متغیر a در شاخه else قابل دسترسی است که این امر ممکن است موجب خطای انسانی و نیز نشت حافظه گردد.
مسئله حائز اهمیت بعدی، وجود خطا ها و باگ های اساسی در این برنامه است که معلوم نیست به دلیل کرک شدن به وجود آمده اند یا به دلیل خطای برنامه نویسی، باگ هایی که گاهاً کلافه کننده و سرگردان کننده هستند، به عنوان مثال شاید ساعت ها و روز ها با برنامه کلنجار رفته باشید و بعد فهمیده باشید که خطا از برنامه نیست و از کامپایلر است، هیچ چیز به این اندازه کلافه کننده نخواهد بود.
بهعنوان برنامهنویس، این مسئله به نظر من بدترین مورد استفاده از کدویژن است. من شخصاً بر این باور نیستم که مشکل از کرک برنامه باشد و این نظر همکاران من است، ولی صرف نظر از اینکه علت باگ چه است، در مورد اینکه باگ ها وجود دارند هیچ شکی نیست! بار ها من این تجربه را داشته ام که برنامه میکروکنترلر درست نوشته شده است ولی به دلیل وجود یک باگ ناشناس (که ممکن است در نسخه بعدی برنامه مرتفع شده باشد) تیم ما خروجی ای گرفته است که ساعتها وقت را هدر داده تا در انتها به این نتیجه برسیم که مشکل از کد ما نیست، بلکه مشکل خود کدویژن است!
مسئله بعدی این است که وقتی شما از کدویژن استفاده می کنید، مجبور هستید که از ویرایشگر کد خود کدویژن استفاده کنید و قادر نخواهید بود که از ویرایشگر های پیشرفته ای مانند VS Code استفاده کنید. وقتی از ویرایشگر های پیشرفته استفاده می کنید هم توسعه برنامه خیلی سریعتر خواهد بود و هم پیدا کردن خطا های احتمالی برنامه راحت تر خواهد بود.
دقیقاً این مشکل دیگر من با برنامه کدویژن است، یک ویرایشگر کد داخلی یا Internal Code Editor که حتی به پای Notepad++ نمیرسد!
وقتی شما از ویرایشگر کد درست و معتبری مانند VS Code استفاده میکنید، یعنی تمامی قابلیتهایی که این ویرایشگر بسیار منحصر به فرد دارد را در اختیار دارید و در غیر از اینصورت، مانند استفاده از کد ویژن، آن ها از دست میدهید. قابلیتهایی نظیر:
یک مسئله مهم برای من در مورد کدویژن هم UI آن است که از زمان Windows XP تا کنون بهبودی نداشته است.
برخی از مواردی که بسیار آزار دهنده هستند، Freeze شدن محیط کاربری کد ویژن حین انجام عملیات کامپایل است که هر بار موجب بالا رفتن فشار خون و ضربان قلب می شود، و همچنین عدم پشتیبانی از High-DPI scaling بر روی نمایشگر های رزولوشن بالا موجب کدر شدن و بلور بودن متن و محیط کاری برنامه و اعمال درد به چشم می شود.
اگر شما هم مانیتوری داشته باشید که به عنوان مثال 125% یا 150% برای Scaling آن تعریف شده باشد، متوجه بلور شدن محیط نرم افزار میشوید. تنها راه جلوگیری از این مشکل، غیر فعال کردن System Scaling است که موجب کوچک شدن کلیه آیکونهای Toolbar برنامه و نازک شدن فونت میشود.
در صورتی که شما از یک لپ تاپ برند DELL یا Lenovo قدیمی که فاقد High Pixel Density میباشد استفاده کنید (که احتمالاً هنوز از ویندوز 7 هم استفاده میکنید) متوجه این نقطه ضعف در UI نخواهید شد، ولی هر برنامهنویس دیگری که از مانیتورهای با Scaling بالا استفاده میکند قطعاً متوجه این مشکل آزار دهنده خواهد شد.
مسئله نهایی، عدم منبع باز بودن نرمافزار کدویژن و هزینهی بالای آن در مقایسه با گزینههای دیگر است.
به نظر من، در ازای دریافت 178 یورو از کاربر حداقل انتظار می رود که برنامه نویس این پروژه نسبت به رفع این مشکلات اقدام کند، ولی مشخصاً این دسته از موارد خارج از حیطه اولویتهای وی است، و به دلیل اینکه این نرم افزار به صورت متن بسته و اصطلاحاً Commercial است، امکان اینکه سایر برنامهنویسان به برطرف کردن این مشکلات اقدام کنند هم وجود ندارد.
سوالی که همچنان باقی می ماند این است که اگر کدویژن نه، پس از چه جایگزینی میتوانیم استفاده کنیم، که در ادامه با معرفی AVR Toolchain و GCC به آن خواهیم پرداخت.
از نظر بسیاری (من جمله خودم)، کامپایلر GCC، بهترین کامپایلر دنیا هست، نه حتی یکی از بهترینها، بلکه تنها بهترین کامپایلر برای C و C++. شاید برخی ها این رأی را به LLVM یا Microsoft Visual C Compiler یا MSVC بدهند، ولی من در ادامه توضیحاتم را مبنی بر اینکه چرا از این کامپایلر استفاده کنیم را ارئه میدهم.
نکته حائز اهمیت این است که GCC یا Gnu C Compiler، یک کامپایلر همه کاره یا General Purpose و مربوط به پروژه GNU است که احتمالاً اسم آن در پروژههایی مانند کرنل لینوکس به گوشتان خورده باشد.
این کامپایلر قابلیت Target کردن (یا خروجی گرفتن) برای پلتفرمهای متعددی را دارد، مانند X86 و X86_64 که CPU های اینتل و AMD هستند و برای تولید برنامه ویندوز به کار میروند، ARM که محبوبترین ISA دنیا برای دستگاههای embedded (از جمله گوشی موبایل، روتر، رزپری پای و...) است، و نهایتاً AVR که ISA مورد بحث امروز ما است.
به دلیل گستردگی استفاده از GCC در زمینههای مختلف و در طی 35 سال استفاده از این کامپایلر، اکنون این کامپایلر به درجه ای از بلوغ رسیده است که به هیچعنوان قابل قیاس با سابقه کمتر از 20 سال کدویژن نیست.
این به معنای این است که پیدا کردن باگ در این کامپایلر تقریباً غیر ممکن است.
از دیگر مزایای استفاده از این کامپایلر هم می توان به موارد زیر اشاره نمود:
به دلیل این موارد، تمامی جایگزینهای برنامه نویسی به زبان C برای AVR از همین کامپایلر استفاده میکنند، حتی نرم افزار رسمی خود شرکت Atmel یا همان Microchip!
و بسیاری از IDE های دیگر.
اگر اسم نرمافزار Arduino یا همان آردوینو به گوشتان خورده باشد، لازم به ذکر است که این محیط هم از GCC برای کامپایل سورس کد استفاده میکند!
در نتیجه، با استفاده از این کامپایلر، ویژگی بسیار مفید سازگاری یا انطباق بیشتر کد شما با سایر IDE هم به جمله موارد مفید استفاده از این کامپایلر اضافه میشود.
ضمناً، این کامپایلر دارای Optimizer بسیار مفیدی است که میتواند کد شما را بدون خطا تا چندین مرحله بهینه کند که بعد تر به آن خواهیم پرداخت.
قبلاً با چند تن از دوستان در خصوص همین مسئله تبادلنظر داشتیم. دوستان معتقد بودند که کدویژن در کامپایل و بهینهسازی کدِ خروجی خیلی موفقتر از GCC عمل میکند و در استاندارد نبودن آن مهم نیست و بهینه بودن کدِ خروجی از درجه اهمیت بالاتری برخوردار است. هرچند به نظر بنده، استاندارد بودن از درجه اهمیت بالاتری برخوردار است؛ چرا که کد قابل حمل و توسعه میشود. اگر کد شما قابلحمل باشد، برای انتقال آن از AVR به ARM نیازی به بازنویسی و حذف بخشهای غیراستاندارد نیست.
در ادامه این نقل قول لازم است بیان کنم که GCC دارای Code Optimizer بسیار هوشمند تر و "قوی تری" نسبت به کدویژن است و علت اینکه برخی از دوستان فکر میکنند که کدویژن بهتر عمل میکند را نمیدانم، ولی فقط به دلیل نبود باگ در کدویژن که شده باشد استفاده از این کامپایلر کار دوستانه و انسانی ای است.
منظور از کد قابل حمل یا Port-able، کدی است که به راحتی بین پلتفرمها یا ISA قابل حمل باشد، یا اصطلاحاً "Port" شود. یعنی مثلاً شما کد یا کتابخانهای نوشتید که میتوانید همان را بین پروژه ARM و AVR استفاده کنید که خودش قابلیت مهم و کاربردیای هست. (در زمینه Keil IDE که برای ARM هست فعلاً حرف نمیزنم ولی از نظر من، Keil دست کمی از کدویژن ندارد.)
از طرفی برتری داشتن یک شبه کامپایلر که توسط یک شرکت با تعداد افراد محدود ایجاده شده (یعنی کدویژن)، از یک کامپایلر اپن سورس که جامعهای از برنامهنویسان آزاد برای توسعه آنوقت گذاشتهاند (یعنی gcc)، قابلپذیرش نیست. ولی از آنجاییکه هیچ چیز غیرممکن نیست، حتی خود غیرممکن (!) قرار بر این شد که قابلیت اپتیمایز کد و همچنین سرعت اجرای کد ایجادشده توسط این دو کامپایلر را بررسی کنیم تا بتوان راحتتر در خصوص آنها قضاوت کرد.
برای انجام تستها نیاز به یک سختافزار پایه است که کدهای ایجادشده توسط هر کامپایلر را بر روی آن پروگرام کرد و بتوان نتیجه آن را مشاهده کرد.
برای مقایسه این دو کامپایلر، از میکروکنترلر بسیار محبوب ATmega328P استفاده میکنیم که بر روی برد هایی مانند Arduino Uno R3 و Arduino Nano استفاده شده، و بر خلاف میکروکنترلر ATmega32 که متأسفانه در دانشگاههای ایران حکمفرمایی میکند، از هر لحاظ (غیر از تعداد پین ها) بهتر است. (این صحبت هم با استناد به صفحه رسمی معرفی محصول و مقایسه آن ها در سایت خود شرکت سازنده بیان می شود.)
این برد از پردازنده Atmega328p استفاده میکند که توسط کریستال 16 مگاهرتز کلاک آن تأمین میشود. برای اینکه بتوان نتیجه را ملموستر احساس کرد، فرکانس کاری میکرو را با تقسیم فرکانس ورودی بر 16 به یک مگاسیکل کاهش دادیم. در تمام برنامهها برای اندازهگیری سرعت اجرای برنامه، به کمک یک لاجیک آنالایز، سرعت صفر و یک شدن پایه PORTB.4 را اندازهگیری میکنیم که درواقع ملاکی از سرعت اجرای برنامه است؛ هرچه این پایه با سرعت بیشتری صفر و یک شود یعنی سرعت اجرای برنامه بیشتر و عملکرد کامپایلر در کامپایل کد بهتر است.
برای انجام این تست، از کدویژن ورژن 3.12 و نیز gcc ورژن 3.5 استفاده خواهد شد.
ایجاد شرایط یکسان برای اینکه به نتیجه مناسبی برای بهترین کامپایلر AVR برسیم با توجه به تفاوتهای کلیدی بین این دو کامپایلر مقداری سخت است اما ما سعی کردیم که دو کامپایلر را در بهترین حالت اپتیمایز کد قرار دهیم.
در ادامه، پنج برنامه تست از سایت سیسوگ گردآوری شده اند:
int main(void) { CLKPR = (1 << CLKPCE); CLKPR = (0 << CLKPCE) | (0 << CLKPS3) | (1 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0); DDRB |= 1 << 4; while (1) { PORTB ^= 1 << 4; } }
در این برنامه خروجی Toggle شده و از هیچ تأخیری هم استفاده نشده است.
int main(void) { float Var = 0; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { Var += 0.1f; PORTB = ((uint8_t)Var) << 4; } }
در این برنامه علاوه بر تغییر وضعیت خروجی، محاسبات سادهی اعداد اعشاری نیز صورت گرفته است.
uint8_t foo(uint32_t x) { x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; x = x + (x >> 8); x = x + (x >> 16); return x & 0x0000003F; } int main(void) { uint32_t x = 0xFAFAFAFA; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | 0<<CLKPS0); DDRB |= 1<<4; while(1) { PORTC = foo(x++); PORTB ^= 1<<4; } }
در این برنامه علاوه بر تغییر وضعیت خروجی، یک سری عملیات ریاضی و منطقی بر روی متغیرهای 32 بیتی انجامشده به همراه فراخواندن یک متد.
uint8_t foo(uint64_t x) { x = x - ((x >> 1) & 0x5555555555555555); x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333); x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F; x = x + (x >> 8); x = x + (x >> 16); return x & 0x000000000000003F; } int main(void) { uint64_t x = 0xFAFAFAFAFAFAFAFA; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { PORTC = foo(x++); PORTB ^= 1<<4; } }
این برنامه همانند برنامه سوم است، با این تفاوت که عملیات ریاضی و منطقی بر روی متغیرهای 64 بیتی انجام شده است.
int main(void) { uint32_t aaa , bbb, ccc; CLKPR=(1<<CLKPCE); CLKPR=(0<<CLKPCE) | (0<<CLKPS3) | (1<<CLKPS2) | (0<<CLKPS1) | (0<<CLKPS0); DDRB |= 1<<4; while(1) { aaa++; bbb++; ccc = aaa*(bbb^2); ccc = ccc/bbb; PORTB ^= 1<<4; } }
در این برنامه یک سری عملیات ریاضی بر روی متغیرها انجام شده و هم زمان خروجی تغییر وضعیت داده شده است.
خب کم کم داریم به جواب این سؤال که بهترین کامپایلر AVR چه کامپایلری است نزدیک میشویم.
همانطور که از نتایج به دست آمده مشخص است، در این بخش، کامپایلر GCC به صورت 100 درصد عملکرد بهتری نسبت به شبه کامپایلر کدویژن دارد.
توضیح در خصوص جواب برنامه 5 و اختلاف فاحش آن :
کامپایلر GCC دارای هوشمندی زیادی در اپتیمایز کد است. این کامپایلر با تشخیص قسمتهای غیرضروری در برنامه و حذف آنها، هم سرعت اجرا بالا میبرد و هم در حافظهی برنامه صرفهجویی میکند. اگر دقت کنید در برنامه شماره 5 یک سری عملیات ریاضی روی سه متغیر aaa و bbb و ccc انجام شده است که نتیجه عملیات فوق هیچ جا مورداستفاده قرار نگرفته است؛ به همین دلیل کامپایلر این عملیات را غیرضروری فرض و تمام روال آن را از برنامه حذف کرده است. به همین دلیل سرعت و حجم برنامه شماره 5 و 1 یکی است. اما شبه کامپایلر کدویژن دارای این هوشمندی نیست و نمیتواند قسمتهای غیرضروری برنامه را تشخیص دهد و آنها را حذف کند.
دقت داشته باشید شبه کامپایلر کدویژن اصلاً قادر به کامپایل کد نمونه 4 نبود. چراکه این شبه کامپایلر، از متغیرهای 64 بیتی پشتیبانی نمیکند.
بهترین کامپایلر AVR کدام است؟
همانطور که انتظار میرفت کامپایلر GCC در تمام موارد عملکردی بهمراتب بهتری از شبه کامپایلر کدویژن دارد. یکی از دلایل موفقیت صد درصدی کامپایلر GCC، متنباز بودن و توسعهی آن توسط برنامه نویسان مجرب و کارکشته سراسر جهان است. کامپایلر GCC در برنامههای زیادی مورد استفاده قرار گرفته است؛ ازجمله: AVR Studio یا Arduino و… . این نیز خود نشاندهندهی برتری این کامپایلر است.
توصیه میکنم اگر از کدویژن استفاده میکنید، آن را کنار بگذارید و برای حرفهایتر شدن، با GCC شروع به کدنویسی کنید. برای مثال، کدویژن یک کامپایلر کرک شده غیر استاندارد است و تنها آپشن ویرایشگر آن این است که وقتی یک پرانتز یا آکولاد را انتخاب میکنید، ابتدا و انتهای آن را برای شما به رنگ زرد در میآورد!
(از سایت سیسوگ جهت بررسی و مقایسه تخصصی این دو کامپایلر سپاسگزارم.)
در ادامه، چند نکته قابل ذکر است:
در صورتی که بخواهید از AVR Toolchain برای GCC استفاده کنید، باید طرز تفکر را اصطلاحاً Mindset خودتان را از کدویژن خارج کنید. چرا که کدویژن یک نرم افزار بکپارچه جامع IDE با ویرایشگر متن و کامپایلر داخلی خودش است که صرفاً جهت کار راه انداختن به درد میخورد، در صورتی که یک Toolchain حاوی همهی ابزار های لازم نظیر Compiler، Linker، Assembler و... می باشد، و معمولاً شما باید از یک IDE یا Code Editor استفاده کنید که من VS Code را بهترین ویرایشگر کن می دانم. ولی شاید برای کسی که علاقهی چندانی به برنامه نویسی ندارد و صرفاً میخواهد که کارش راه بیفتد علاقهای به راه اندازی Toolchain نداشته باشد و دنبال ساده ترین و راحت ترین راه بگردد که خب قطعاً کدویژن علی رغم تمامی مشکلات عنوان شده و باگ هایی که دارد، دقیقاً مناسب این دسته از افراد است.
بهعنوان مثال، کد ویژن تعدادی کتابخانه داخلی دارد و کدویزارد این نرمافزار هم به درد بسیاری از افراد میخورد که سر رشته ای از کتابخانههای جدید ندارند.
بلاخره تیم توسعه این نرم افزار هم زحمت کشیده و بخش های Code Editor و Compiler و... را برای این نرم افزار توسعه داده، ولی همانطور که ذکر کردم، کدویژن به معنای واقعی کلمه، بدترین IDE برای توسعه کد AVR هست و به نظر من فقط به دلیل تدریس در دانشگاه آزاد و استفاده در شرکت های ایرانی معروف شده است، ولی برنامه معتبری نمی باشد.
در صورتی که شما بخواهید از GCC استفاده کنید، میتوانید که Toolchain را جداگانه راه اندازی کنید و IDE شخصی مورد علاقه تان را راه اندازی کنید، اما همچنین میتوانید از IDE هایی که به صورت آماده این کار را انجام داده اند و در بالا ذکر شده اند استفاده کنید، مانند Atmel Studio و حتی آردوینو.
لازم به ذکر است که پلتفرم آردوینو صرفاً محدود به برد های آردوینو نیست! شما می توانید از نرم افزار آردوینو برای پروگرام بورد خودتان استفاده کنید، و تازه از مزایای Optiboot Bootloader، کتابخانه Wiring، و هزاران کتابخانه موجود در این پلتفرم که منبع باز هستند استفاده کنید. در بسیاری از منابع ذکر شده که در آردوینو دستورات فرق دارد، اما این جمله صحت ندارد زیرا آردوینو کماکان با GCC-AVR کامپایل می شود و کلیه Language Structure و Syntax استاندارد را پشتیبانی می کند.
حقیقت این است که به دلیل استفاده از کتابخانههایی مانند Wiring در آردوینو، شما دسترسی به توابع بیشتری مانند digitalWrite و analogWrite برای PWM دارید که افراد مبتدی وجود این دسته از توابع را با عدم پشتیبانی از Syntax استاندارد اشتباه میگیرند، در حالی که شما به راحتی میتوانید به عنوان مثال از ساختار PORTA تا PORTF در آردوینو نیز استفاده کنید و حتی این Syntax ها را همزمان با کتابخانه Wiring استفاده کنید!
نکته قابل توجه این است که gcc صرفاً یک کامپایلر است و برخلاف کدویژن که تعدادی متابخانه قدیمی در خودش دارد، gcc دارای هیچ کتابخانه ای برای قطعات و ماژول ها (مانند LCD) نیست و وظیفه نصب و استفاده از بهترین و بهروز ترین کتابخانهها (مانند LCD) را به عهده خود برنامهنویس گذاشته است.
مسئله بعدی هم این است که بهعنوان یک مهندس برنامهنویس توصیه میشود بهجای اینکه خود را به تعدادی برنامه سطح پاین یک بار مصرف مانند کدویژن محدود کنید، تکنولوژی اصلی زیر ساخت (مانند GCC) را فرا بگیرید تا امکان برنامه نویسی برای پلتفرمهای مختلف (حتی ARM را داشته باشید).
به عنوان مثال، فراگیری VS Code در آینده به شما به توسعه هر نوع کد (که حتی نامربوط به میکروکنترلرها باشد، مانند پایتون) کمک میکند، در صورتی که کدویژن و ویرایشگر آن تنها در زمینه AVR کاربردی است.
یک IDE که بر مبنای VS Code است و حاوی GCC و کتابخانههای Arduino که در بالا ذکر شد است، نرم افزار PlatformIO است که توسعه برای میکروکنترلر ها را بسیار لذت بخش و برای افراد مبتدی و حتی حرفه ای بسیار آسان و راحت کرده است. میتوانید به سراغ آموزش استفاده از این IDE بروید.
و توصیه آخر هم این است که در دنیای منبع باز یا اُپن-سورس سرمایه گذاری کنید! اگر به فکر پیشرفت و توسعه هستید، دیگر نرم افزارهای متن بسته و خسته که پولی هستند و کرک آن را دانلود میکنید به زودی کنار میروند و مشابه سایر محتوا هایی که در دانشگاه به شما تدریس میکنند به درد نخواهد خورد. پس بهتر است که هر چه سریعتر از جو بسته، تکراری، مرسوم و منسوخ توسعه برای این میکروکنترلر ها خارج شوید.