اطلاعیه

Collapse
No announcement yet.

آموزش AVR-GCC

Collapse
X
 
  • فیلتر
  • زمان
  • Show
Clear All
new posts

    #16
    پاسخ : آموزش ( avrstudio5 ( AVRGCC

    در Visual Studio برای مدیریت خاص پروژه های حجیم این امکان هست که قبل و بعد از کامپایل کردن پروژه ها فرمان خاصی انجام بشه. مثلا بخوایین قبل از اجرا یک backup از فایلها بگیرین. یا توسط یک برنامه کمکی تغییری در فایل هگز نهایی انجام بدین. این event ها میتونه به شما این امکان رو بده که برنامه ای رو در حین یا بعد کامپایل اجرا کنین.
    آقا من الان یک ساعته دارم مطالبتون رو میخونم اما هیچی نمیفهمم واقعا که :cry2:

    اگه میشه یه خورده بیشتر توضیح بدید که میدونم شدنش هم احتمالا دردی رو دوا نکنه چون مشکل جای دیگس :angry:

    جناب آقا مجتبی! این فرمی که شما فرمودین فقط یکجا درست هست ...
    خوب پس میشه اینجوری گفت که رشته اول داخل حافظه فلش ریخته میشه و بعد برای نمایش داخل sram لود میشه؟

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

    دیدگاه


      #17
      پاسخ : آموزش ( avrstudio5 ( AVRGCC

      خوب پس میشه اینجوری گفت که رشته اول داخل حافظه فلش ریخته میشه و بعد برای نمایش داخل sram لود میشه؟
      ببین! یه پروژه رو کامپایل کن و فایل lst شو نگاه کن خودت متوجه میشی که چکار داره میکنه. اینجوری بیشتر برات ملموس میشه.

      خوب دلیل سوالم این بود...
      میدونی توی اسمبلی چطوری متغیر تعریف میکنن؟ مثلا یک متغیر از نوع int بخوای تعریف کنی باید بگی:


      .comm myInt,2


      اینجا من اسمی از آدرس نیاوردم که! فقط گفتم یک متغیر 2 بایتی بنام myInt دارم. کجا قرار میگیره؟ مهم نیست. چون آدرسش رو اسمبلر به صورت ضمنی حساب میکنه و در فاز link مطلق میشه. تا این حد اگر بخوای به ساختار های داخلی یک کامپایلر نزدیک بشی باید ساختار های عمومی کامپایلر ها رو بشناسی. برای پرهیز از دردسر توصیه میکنم فعلا دنبال اینا نری. فقط استفاده کن.

      دیدگاه


        #18
        پاسخ : آموزش ( avrstudio5 ( AVRGCC

        سلام
        یه سوال داشتم
        نحوه ی صفر و یک کردن پایه ها تو این ورژن چیه ؟؟
        مثل اینکه توابع cbi و sbi دیگه نیستند ؟؟؟؟
        لطفا کامل توزیح بدین
        ممنون

        دیدگاه


          #19
          پاسخ : آموزش ( avrstudio5 ( AVRGCC

          میدونی توی اسمبلی چطوری متغیر تعریف میکنن؟ مثلا یک متغیر از نوع int بخوای تعریف کنی باید بگی:
          .comm myInt,2
          دقیقا اینم نمیدونستم ، من میومدم از LD , LDS ,ST,STS استفاده میکردم

          نحوه ی صفر و یک کردن پایه ها تو این ورژن چیه ؟؟
          مثل اینکه توابع cbi و sbi دیگه نیستند ؟؟؟؟
          ماکرو های CBI و SBI توی فایل سرایندیavrlibdefs.h هست که داخل avrlib هست و باید از اونجا توی پوشه برنامتون کپی و بعد داخل برنامه اینکلودش کنید البته خوب میتونید خودتون هم تعریفشون کنید مثلا :

          #define SBI(x,y) (x |= (y)) /* Set bit y in byte x*/
          #define CBI(x,y) (x &= (~y)) /* Clear bit y in byte x*/
          #define CHECKBIT(x,y) (x & (y)) /* Check bit y in byte x*/




          دیدگاه


            #20
            پاسخ : آموزش ( avrstudio5 ( AVRGCC

            دقیقا اینم نمیدونستم ، من میومدم از LD , LDS ,ST,STS استفاده میکردم
            اینی که شما میگی نحوه دسترسی به یک حافظه هست. با تعریفش فرق میکنه. مثلا فرض کن سورسی بفرم زیر داریم:

            int n;

            n++;


            یک معادل اسمبلی میتونه این باشه:

            .comm n,2 // sizeof(int)=2



            lds r24,n
            lds r25,n+1
            adiw r24,1
            sts n,r24
            sts n+1,r25


            همینطور که میبینی من از دستور comm برای معرفی متغیر دو بایتی (هم سایز یک int) استفاده کردم ولی با همون lds و sts به این متغیر دسترسی پیدا کردم. اینکه متغیر n کجای حافظه نشسته مهم نیست. مسئله اینه که بتونم براش حافظه تخصیص بدم. اما یک مورد خیلی مهم!

            این دستور در اصل حافظه تخصیص نمیده! میگه که 2 بایت حافظه به اسم n بطور مشترک استفاده شده. یعنی اگه در سورس های دیگه ای همین دستور استفاده بشه من حیث المجموع فقط 2 بایت بطور مشترک منظور میشه.
            برای اینکه این تعریف فقط برای یک سورس معتبر باشه باید سورس مثل سورس زیر نوشت:


            C: static int n;

            asm: .lcomm n,2


            یعنی تمامی این comm. های هم اسم با همدیگه ترکیب میشن و به اندازه بزرگترینشون حافظه تخصیص داده میشه.

            دیدگاه


              #21
              پاسخ : آموزش ( avrstudio5 ( AVRGCC

              ببخشید یه مشکله دیگه ؛
              هر کاری میکنم این .comm n,2 رو نمیتونه (باید بگم چی ؟ ) اسمبل کنه (درسته ؟) البته وقتی مینویسم ادیتور رنگش رو آبی میکنه ولی اسمبل نمیشه

              این .comm جزو رهنمود های اسمبلر به حساب میاد ؟ من توی هلپ نرم افزار تو قسمت Assembler directives نه .comm و نه .lcomm رو نمیبینم ! اینها اصولا چی هستند؟و اینکه مثل این دو مورد آیا موارد دیگه این چنینی هم هست ؟

              ممنون

              دیدگاه


                #22
                پاسخ : آموزش ( avrstudio5 ( AVRGCC

                اینها جز لغات کلیدی اسمبلر avr-gcc هستن. و نه اسمبلر avrasm شرکت اتمل. بنا براین help رو اشتباهی نگردین.
                ضمنا آره چیزای دیگه ای هم هستن. (ابتدای لغت کلیدی با نقطه شروع میشه ها) مثلا:


                .comm
                .lcomm
                .global
                .extern


                اولی برای معرفی متغیر مشترک که بطور عمومی تعریف شده باشه کاربرد داره.
                دومی مثل اولی هست ولی فقط برای تعریف متغیر درون همون سورس مربوطه استفاده میشه.
                سومی مشخص میکنه این تابع یا label باید از جاهای دیگه قابل شناسایی باشه. مثل اینکه بخواییم یک تابع اسمبلی تعریف کنیم و از داخل روتین های C صدا کنیم.
                چهارمی مشخص میکنه که اسمی جای دیگه تعریف شده. که معمولا به آدرسی درون کد یا داده ثابت درون فلش برمیگرده.

                ضمنا اینجا خیلی چیزا با C فرق میکنه. مثلا section های متفاوت در فلش. که باز هم توصیه میکنم موردی برین جلو چون ممکنه گیج بشین.

                دیدگاه


                  #23
                  پاسخ : آموزش ( avrstudio5 ( AVRGCC

                  با اجازه استاد عزیز جناب آقازاده

                  8. نوشتن برنامه ترکیبی از c و اسمبلی :

                  خوب بعضی اوقات نیاز داریم که بعضی روتین ها رو به صورت کاملا بهینه از لحاظ سرعت اجرا بنویسیم و چون نمیشه گفت که کامپایلر دقیقا بهترین کد ما رو به چه صورت کامپایل میکنه و یا برای دسترسی به بعضی موارد که نمیشه با زبون c به اون ها دسترسی پیدا کرد نیاز مند این هستیم تا از زبون اسمبلی استفاده کنیم چیزی که من تا اینجا متوجه شدم اینه که میشه اینکار رو به دو صورت انجام داد :
                  1. in line asm : توی این روش میشه توی همون سورس c با استفاده از فورم زیر کد های اسمبلیمون رو به برنامه اضافه کنیم :

                  asm(code : output operand list : input operand list [: clobber list]);

                  که چون به نظرم من ریزه کاری های این روش زیاده و به نظرم از روش دوم سخت تر بود زیاد روش کار نکردم نمیدونم احتمالا اشتباه میکنم ولی به نظرم روش دوم بهتر و کلی تر امد به هر حال از اساتید در خواست میکنم در این مورد بنده رو راهنمایی کنند .
                  2. combine C program with external ASM : توی این روش سورس c و اسمبلی رو جدا مینویسیم و خود کامپایلر عمل لینک رو انجام میده برای این منظور ابتدا یک فایل جدید رو به صورت زیر ایجاد میکنیم :

                  بعد

                  بعد


                  سپس با کلیک بر روی ok پروژه ایجاد شده و فایل .c باز میشود که برنامه به زبان c در آن نوشته میشود .

                  حال برای نوشتن سورس اسمبلی باید یک فایل با پسوند "S."( اس باید به حروف بزرگ باشه) میاییم در قسمت سمت راست در منوی اتو هاید solution explorer راست کلیک کرده و گزینه add و سپس new item.. رو به ترتیبی که در زیر میبینید اضافه میکنیم ، اسم فایل رو انتخاب و پسوند اون رو S. میزاریم



                  بعد


                  حالا میاییم کد سی مون رو داخل فایل c. مینوسیم و کد اسملیمون رو داخل فایل S. ، بعد با زدن f7 برنامه رو کامپایل میکنیم
                  برای مثال کد های زیر رو وارد و نتیجه رو با دیباگر نرمافزار تست کنید :

                  کد c :

                  کد:
                  #include <avr/io.h> 
                  
                   uint8_t asmfunc(void); 
                  
                   int main(void){ 
                    PORTB = asmfunc(); 
                    while(1); 
                   }



                  کد اسمبلی :


                  کد:
                   #define __SFR_OFFSET 0 
                   #include <avr/io.h> 
                  
                    .global asmfunc 
                  
                   asmfunc: 
                    ldi R24, 0xAA 
                    ret



                  توی اینجا portb برابر با 0xAA میشه

                  با تشکر از clawson از avrfreaks

                  پایان قسمت هفتم

                  دیدگاه


                    #24
                    پاسخ : آموزش ( avrstudio5 ( AVRGCC

                    نوشتن inline assembly در کامپایلر gcc یک مقدار پیچیده هست. ولی برای این کار یک pdf کوچیک از سایت gnu.org موجود هست بنام GCCAVR Inline Assembler Cook Book.pdf با حجمی برابر با 52 کیلو بایت. خیلی جاها صرف میکنه که کد داخل فایلهای اسمبلی نوشته نشه و مستقیما داخل روال C صدا زده بشه. چون در خیلی از مواقع کامپایلر برای کاهش حجم یا افزایش سرعت یک تابع رو در تابع های صدا زننده الحاق میکنه. بعضی جاها هم صرف نمیکنه که کد اسمبلی نوشته بشه. مثلا فرض کنین (طبق مثال pdf مربوطه) بخواییم روالی بنویسیم که بایتهای بالا و پایین یک int رو با هم عوض کنه. میتونیم در حالت عادی اینجوری کد بنویسیم:

                    به زبان C:

                    int swab(int n)
                    {
                    unsigned char *s = (unsigned char *)&n;
                    unsigned char c;

                    c = s[0];
                    s[0] = s[1];
                    s[1] = c;
                    return n;
                    }


                    به زبان اسمبلی در فایل مجزا:

                    .global swab
                    swab:
                    mov r0,r24
                    mov r24,r25
                    mov r25,r0
                    ret


                    و بفرم inline:

                    asm volatile(
                    "mov __tmp_reg__, %A0" "\n\t"
                    "mov %A0, %B0" "\n\t"
                    "mov %B0, __tmp_reg__" "\n\t"
                    : "=r" (value)
                    : "0" (value)
                    );


                    که البته این کار سه حسن اساسی داره (البته در این مورد بخصوص):
                    حجم کد کمتر.
                    عدم وابستگی به دو رجیستر r25 و r24.
                    عدم صدا کردن و برگشت از تابع و نتیجتا کد سریعتر.

                    محض اطلاع هم: رجیستر r0 به عنوان __tmp_reg__ معرفی شده. value مقداری هست که باید دو بایتش با هم عوض بشه. مقدار خروجی در value قرار میگیره. مقدار ورودی هم در value هست. و هیچ رجیستر دیگه ای عوض نمیشه.
                    asm(code : output operand list : input operand list : clobber list);

                    یک مورد مهم دیگه.
                    وقتی یک تابع صدا زده میشه فرض بر این هست که مقدار یک سری رجیستر ها میتونن عوض بشن ولی بقیه مطلقا مقدارشون عوض نمیشه. در این حالت اگر مقدار یک رجیستر باید قبل و بعد از صدا کردن یک تابع ثابت بمونه باید این رجیستر ها در پشته یا در رجیستر های دیگه نگهداری بشه. اما برای کد های inline چون لیست رجیستر هایی که عوض میشن قید میشه عملا این فرم باعث میشه کد بهینه تری تولید بشه.

                    دیدگاه


                      #25
                      پاسخ : آموزش ( avrstudio5 ( AVRGCC

                      نوشتن inline assembly در کامپایلر gcc یک مقدار پیچیده هست. ولی برای این کار یک pdf کوچیک از سایت gnu.org موجود هست بنام GCCAVR Inline Assembler Cook Book.pdf با حجمی برابر با 52 کیلو بایت
                      من اینو دیده بودم اتفاقا اینو دیدم (البته فکر کنم از http://gcc.gnu.org دانلود کردم)بیخیال inline asm شدم، خیلی ریزه کاری داشت نمیدونم شاید اون موقع با حوصله نخوندم ولی حتما میخونمش و دیافت هام رو مینویسم تا اشتباهاتم رو لطف کنید و بهم بگید

                      در خیلی از مواقع کامپایلر برای کاهش حجم یا افزایش سرعت یک تابع رو در تابع های صدا زننده الحاق میکنه.
                      متوجه نشدم میشه یکم توضیح بدید ؟

                      int swab(int n)
                      {
                      unsigned char *s = (unsigned char *)&n;
                      unsigned char c;

                      c = s[0];
                      s[0] = s[1];
                      s[1] = c;
                      return n;
                      }
                      یکی نیست به من بگه تو که هنوز سی رو بلد نیستی جه به اسمبلی !

                      آقا من متوجه نشدم فقط فهمیدم :
                      یک تابع با ورودی و خروجی int تعریف کردید بعد توی تابع خط اول اشاره گر s تعریف و مقدارش برابر با بایت پایین آدرس متغیر n قرار گرفته بعد متغیر c تعریف شده بعد c برابر با آدرس بایت پایین n میشه بعد بایت پایین آدرس متغیر n برابر با بایت بالای آدرس متغیر n میشه و سپس بایت بالای آدرس متغیر n برابر با بایت پایین آدرس متغیر n میشه (تا اینجا درسته ؟) بعد مقدار n برگردونده میشه ، خوب اینطوری که برای جابجایی اصلا آدرس ها عوض شدند که نه فقط صرفا مقادیر بایت بالا و پایین ؟

                      که البته این کار سه حسن اساسی داره (البته در این مورد بخصوص):
                      حجم کد کمتر.
                      عدم وابستگی به دو رجیستر r25 و r24.
                      عدم صدا کردن و برگشت از تابع و نتیجتا کد سریعتر.
                      خوب به نظر میرسه حسن اول و سوم یک حسن بوده و دلیلش هم تنها صدا نکردن باشه و حسن دوم هم خوب بالاخره که داره از رجیستر ها استفاده میشه یعنی تمام کدهایی که توی inline اسمبلی نوشته میشه تنها قابلیت استفاده از رجیستر R1و R0 رو دارند رجیستر هایی که کامپالیر به اون ها اصلا دست نمیزنه ؟ خوب این محدودیت ایجاد نمیکنه برای انجام محاسبات ؟ فقط به خاطر استفاده از PUSH و POP (بیییب) هست که حجم کد زیاد میشه ؟ (ببخشید میدونم دارم یه چیزایی میپرسم که شاید بدرد...ام نخوره ولی بالاخره میگن پرسیدن عیب نیست دیگه)

                      یک مورد مهم دیگه.
                      وقتی یک تابع صدا زده میشه فرض بر این هست که مقدار یک سری رجیستر ها میتونن عوض بشن ولی بقیه مطلقا مقدارشون عوض نمیشه. در این حالت اگر مقدار یک رجیستر باید قبل و بعد از صدا کردن یک تابع ثابت بمونه باید این رجیستر ها در پشته یا در رجیستر های دیگه نگهداری بشه. اما برای کد های inline چون لیست رجیستر هایی که عوض میشن قید میشه عملا این فرم باعث میشه کد بهینه تری تولید بشه.
                      خوب اگر مثلا بخواهیم روتین یک وقفه و یا یک تابع رو که فکر میکنم بیشتر کار برد combine C program with external ASM باشه که باز هم هیچ فرقی در سرعت وجود نداره چون با وجود محدودیته فرضی که من متوجه شدم در استفاه از تنها چند رجیستر مجبور بشیم از حافظه استفاده کنیم ؟ که باز همون بشه ؟(احساس میکنم این سوالم به اون یکی سوال داره جفپا میزنه )


                      در مورد نوشتن روتین برای وقفه ها توی فایل اسمبلی توی IAR در ابتدا برای معرفی وقفه و نشان دادن محل آدرس بردار وقفه اینجوری مینوشتم :


                      NAME TIMER1_OVF_vect
                      #include "ioavr.h"
                      extern c_timer1_ovf
                      COMMON INTVEC(0x10) ; Code in interrupt vector segment
                      ORG TIMER1_OVF_vect ; Place code at interrupt vector
                      RJMP c_timer1_ovf ; Jump to assembler interrupt function


                      حالا توی AVRGCC باید چجوری بنویسم؟
                      (ببخشید اگه وقت نمیکنید توضیح بدید میشه یه منبع (به غیر از http://gcc.gnu.org/ ) معرفی کنید که برم ببینم اصلا چی به چیه بعد دریافتام رو بنویسم بعد اشکالام رو بگیرید)

                      دیدگاه


                        #26
                        پاسخ : آموزش ( avrstudio5 ( AVRGCC

                        کامپایلر gcc؛ کامپایلر خوبی هست. سعی کنین یک سری از کدهای خودتون رو کامپایل و نتیجه رو ببینین. اما چند مورد:
                        1- رجیستر r0 برای مقاصد temporary استفاده میشه. بنابراین اینجا کار جوکر رو بازی میکنه.
                        2- از اونجایی که عدد صفر خیلی جاها استفاده میشه و عملیاتی روی مقدار صفر معمولا منجر به بار کردن عدد صفر داخل یک رجیستر میشه قبل از main یا اول وقفه ها یا بعد از دست خوردن این رجیستر مثل جایی که از دستور mul استفاده میشه مقدار این رجیستر صفر میشه.
                        3- خیلی وقتها هست که حجم کد تولید شده یک تابع اگر بصورت inline استفاده بشه کمتر از حالتی هست که بطور عادی صداش کنیم.اینجور مواقع کامپایلر با اون مثل یک ماکروی خاص برخورد میکنه. مثلا فرض کنیم تابعی به فرم زیر داریم:


                        unsigned char my_func(void)
                        {
                        return 5;
                        }


                        void main(void)
                        {
                        unsigned char a = my_func();
                        }


                        در حالت عادی انتظار داریم کدی مثل کد زیر تولید بشه:

                        my_func:
                        ldi r24,5
                        ret

                        main:
                        call my_func
                        ret


                        اما در این حالت حجم کد تولید شده 10 بایت هست. ولی در حالت بهینه سازی فقط این کد تولید میشه:

                        my_func:
                        ldi r24,5
                        ret

                        main:
                        ldi r24,5
                        ret


                        و تابع my_func هم حذف میشه. پس کد نهایی حجمی برابر با 4 بایت خواهد داشت.

                        یکی نیست به من بگه تو که هنوز سی رو بلد نیستی جه به اسمبلی !
                        مشخصا از تیپ صحبتتون برمیاد که شما مفهوم اشاره گر و آدرس براتون کاملا جا نیفتاده. توصیه میکنم اصلا به این کارها فعلا کاری نداشته باشین و همون کد C رو بنویسین. این تابع یک تابع ساده ای هست. ولی مجبورم یک تفسیر دو صفحه ای براتون بنویسم که راسیتش الان حالش نیست. :mrgreen:

                        جسارتا این یکی از مواردی هست که من موافق این میشم که کسی که تخصصی در کار نرم افزار نداره حداقل توی این سطح به این مسائل وارد نشه. البته این کار و نوشتن کد C توسط شما کار قشنگی هست. حداقل دارین سعی میکنین. علاقه یا اجبار یا هر چی. اما توصیه میکنم توی این سطح وارد نشین. فعلا ممکنه گیجتون کنه. چون در این حد کار کردن اطلاعات زیادی میخواد. یا تجربه خوبی. اگر خواستین بهترین کار اینه که کد c رو بنویسین و کد نهایی تولید شده توسط کامپایلر تون رو نگاه کنین. کم کم دستتون میاد که مشکل از چه قراره. اما از بابت رجیستر ها...

                        دیدگاه


                          #27
                          پاسخ : آموزش ( avrstudio5 ( AVRGCC

                          ...
                          فقط اینو بگم که برای صدا کردن تابعی که پارامتری از نوع int داره الزاما کامپایلر بایستی از رجیستر های r24 , r25 استفاده کنه. ولی وقتی بصورت inline بخواد این تابع رو استفاده کنه الزامی به رعایت این قاعده نداره. چون call انجام نمیشه و میتونه این تابع رو بطور ضمنی روی مثلا رجیستر های r16 , r17 استفاده کنه. اینجوری دست و بالش موقع تولید کد بازتره.

                          برای مشکل تغییر مقادیر رجیستر ها فرض کنین تابعی داریم که مثلا رجیستر r16 توش استفاده شده. در وسط کار میخواییم یک تابع رو صدا کنیم. چون فرض اولیه بر این هست که این یک رجیستر عمومی هست؛ و ضمنا احتمال این وجود داره که این رجیستر در تابع دوم ممکنه عوض بشه پس یا شما بایستی این رجیستر رو در جایی ذخیره کنین و یا ریسک تعویض مقدارش رو بجون بخرین. چون نمیدونیم (در حد کامپایلر) که تابع صدا شده آیا r16 رو دست میزنه یا نه.
                          ولی وقتی از inline assembly استفاده میکنیم. چون به کامپایلر اطلاع میدیم که r16 عوض شده یا نه؛ کامپایلر میتونه کد بهتری تولید کنه. تازه بعضی جاها آخرش پای کامپایلر هم لنگ میزنه.
                          مثلا فرض کنیم یک تایمر 8 بیتی داریم که قراره فقط کار کنه. مثلا اینجوری مینویسیم:


                          SIGNAL(SIG_OVERFLOW0)
                          {
                          TCNT0 = 10;
                          }


                          کد تولید شده معمولا چیزی معادل این میشه:

                          SIG_OVERFLOW0:
                          push r0
                          push r1
                          in r0,SREG
                          push r0
                          clr r1
                          push r24

                          ldi r24,10
                          out TCNT0,r24

                          pop r24
                          pop r0
                          out SREG,r0
                          pop r1
                          pop r0
                          reti


                          در صورتیکه میتونه این باشه:

                          SIG_OVERFLOW0:
                          push r24
                          ldi r24,10
                          out TCNT0,r24
                          pop r24
                          reti


                          و این کامپایلر خنگ اینو نمیفهمه. هم کد بزرگتری تولید میکنه و هم کد کند تری. ولی چون جز در بعضی اوقات چیز مهمی نیست، برای شما بهتره که از کد C استفاده کنین که همون کد حجیم تر و کند تر رو استفاده میکنه.

                          این مثال هم واضح هست. چون از حالت نامشخصی داخل کد میشیم. بایستی مقدار r1 صفر بشه. چون وقفه هست رجیستر 0 و 1 که ممکنه استفاده بشن باید جایی ذخیره و بعدا بازیابی بشن. ضمنا نباید بیت های رجیستر SREG دست بخوره. ولی مسئله اینجاست که هیچ کدوم از رجیستر های 0و 1 و SREG دست نمیخورن. پس الزامی به ذخیره مقدار قبلی اونها نیست. توجه شما رو به این نکته جلب میکنم که دستوراتی مثل push یا pop یا ldi یا out با SREG بطور مستقیم کاری ندارن.

                          برای نوشتن وقفه ها هم با لحاظ کردن شرایطی مثل اینایی که راجع به r0 , r1 توضیح دادم ساده ترین راهش اینه:


                          #include <avr/io.h>
                          #include <avr/interrupt.h>

                          .global SIG_OVERFLOW0
                          SIG_OVERFLOW0:
                          push r0
                          push r1
                          in r0,_SFR_IO_ADDR(SREG)
                          push r0
                          clr r1

                          ....

                          pop r0
                          out _SFR_IO_ADDR(SREG),r0
                          pop r1
                          pop r0
                          reti

                          دیدگاه


                            #28
                            پاسخ : آموزش ( avrstudio5 ( AVRGCC

                            من این کد شما رو داخل avr gcc نرم افزار avr studio کامپایل کردم

                            کد:
                            #include <avr/io.h>
                            
                            int main(void)
                            {
                              while(1)
                              {
                                //TODO:: Please write your application code 
                              }
                            }
                            int swab(int n)
                            {
                              unsigned char *s = (unsigned char *)&n;
                              unsigned char c;
                            
                              c = s[0];
                              s[0] = s[1];
                              s[1] = c;
                              return n;
                            }

                            و نتیجه اش توی حالت اوپتیمایزیشن os- شد این :


                            کد:
                            c to assembly.elf:   file format elf32-avr
                            
                            Sections:
                            Idx Name     Size   VMA    LMA    File off Algn
                             0 .text     0000008e 00000000 00000000 00000054 2**1
                                     CONTENTS, ALLOC, LOAD, READONLY, CODE
                             1 .stab     000006cc 00000000 00000000 000000e4 2**2
                                     CONTENTS, READONLY, DEBUGGING
                             2 .stabstr   00000085 00000000 00000000 000007b0 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             3 .debug_aranges 00000020 00000000 00000000 00000835 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             4 .debug_pubnames 00000024 00000000 00000000 00000855 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             5 .debug_info  000000c2 00000000 00000000 00000879 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             6 .debug_abbrev 00000081 00000000 00000000 0000093b 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             7 .debug_line  0000007e 00000000 00000000 000009bc 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             8 .debug_frame 00000030 00000000 00000000 00000a3c 2**2
                                     CONTENTS, READONLY, DEBUGGING
                             9 .debug_str  00000097 00000000 00000000 00000a6c 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             10 .debug_loc  00000065 00000000 00000000 00000b03 2**0
                                     CONTENTS, READONLY, DEBUGGING
                             11 .debug_pubtypes 00000012 00000000 00000000 00000b68 2**0
                                     CONTENTS, READONLY, DEBUGGING
                            
                            Disassembly of section .text:
                            
                            00000000 <__vectors>:
                              0:	0c 94 2a 00 	jmp	0x54	; 0x54 <__ctors_end>
                              4:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                              8:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                              c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             10:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             14:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             18:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             1c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             20:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             24:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             28:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             2c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             30:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             34:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             38:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             3c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             40:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             44:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             48:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             4c:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                             50:	0c 94 34 00 	jmp	0x68	; 0x68 <__bad_interrupt>
                            
                            00000054 <__ctors_end>:
                             54:	11 24    	eor	r1, r1
                             56:	1f be    	out	0x3f, r1	; 63
                             58:	cf e5    	ldi	r28, 0x5F	; 95
                             5a:	d4 e0    	ldi	r29, 0x04	; 4
                             5c:	de bf    	out	0x3e, r29	; 62
                             5e:	cd bf    	out	0x3d, r28	; 61
                             60:	0e 94 36 00 	call	0x6c	; 0x6c <main>
                             64:	0c 94 45 00 	jmp	0x8a	; 0x8a <_exit>
                            
                            00000068 <__bad_interrupt>:
                             68:	0c 94 00 00 	jmp	0	; 0x0 <__vectors>
                            
                            0000006c <main>:
                             */ 
                            
                            #include <avr/io.h>
                            
                            int main(void)
                            {
                             6c:	ff cf    	rjmp	.-2   	; 0x6c <main>
                            
                            0000006e <swab>:
                              {
                                //TODO:: Please write your application code 
                              }
                            }
                            int swab(int n)
                            {
                             6e:	df 93    	push	r29
                             70:	cf 93    	push	r28
                             72:	00 d0    	rcall	.+0   	; 0x74 <swab+0x6>
                             74:	cd b7    	in	r28, 0x3d	; 61
                             76:	de b7    	in	r29, 0x3e	; 62
                             78:	9a 83    	std	Y+2, r25	; 0x02
                             7a:	89 83    	std	Y+1, r24	; 0x01
                              unsigned char *s = (unsigned char *)&n;
                              unsigned char c;
                            
                              c = s[0];
                              s[0] = s[1];
                              s[1] = c;
                             7c:	98 2f    	mov	r25, r24
                              return n;
                            }
                             7e:	8a 81    	ldd	r24, Y+2	; 0x02
                             80:	0f 90    	pop	r0
                             82:	0f 90    	pop	r0
                             84:	cf 91    	pop	r28
                             86:	df 91    	pop	r29
                             88:	08 95    	ret
                            
                            0000008a <_exit>:
                             8a:	f8 94    	cli
                            
                            0000008c <__stop_program>:
                             8c:	ff cf    	rjmp	.-2   	; 0x8c <__stop_program>


                            خوب چند تا سوال :
                            1. این c to assembly.elf: file format elf32-avr نوشته منظورش چیه من فایل elf.رو هر کاری کردم باز نشد اصلا میشه بگید elf. چیه ؟

                            2. منظور از Sections اینجا چیه ؟
                            اگه میشه بفر مایید هر ستون یعنی چی ؟

                            3. توی قسمت Disassembly of section .text:
                            چرا امده 20 بار (به اندازه بردار های وقفه مگا 16به غیر از ریست) دستور پرش رو به __bad_interrupt داده ؟

                            4. این اعدادی که بعد از ; میزاره نشوند دهنده چی هست ؟
                            مثلا :

                            کد:
                             54:	11 24    	eor	r1, r1
                             56:	1f be    	out	0x3f, r1	; 63
                             58:	cf e5    	ldi	r28, 0x5F	; 95
                             5a:	d4 e0    	ldi	r29, 0x04	; 4
                             5c:	de bf    	out	0x3e, r29	; 62
                             5e:	cd bf    	out	0x3d, r28	; 61


                            5. در اینجا :

                            کد:
                             6c:	ff cf    	rjmp	.-2   	; 0x6c <main>

                            یا مثلا این

                            کد:
                            8c:	ff cf    	rjmp	.-2   	; 0x8c <__stop_program>

                            یا مثلا این

                            کد:
                             72:	00 d0    	rcall	.+0   	; 0x74 <swab+0x6>

                            منظور از 2-. و 0+. چیه ؟

                            دیدگاه


                              #29
                              پاسخ : آموزش ( avrstudio5 ( AVRGCC

                              دارم بهتون هشدار میدم. ممکنه توی خاکی بزنین و برای خودتون دردسر درست کنین. اما محض اطلاع:
                              اگر یک وقفه فعال بشه ولی شما روالی برای اون ننویسین در صورت بروز وقفه بایستی چه اتفاقی بیفته؟ قاعدتا باید میکرو ریست بشه. اما ریست شدن میکرو اونهم در حالتی که آدرس بردار وقفه صفر باشه اتفاق میفته و این حالتی هست که اولا قابل ردیابی نیست. و ثانیا چون فقط با پرش به آدرس صفر ریست انجام شده ریست سخت افزاری انجام نشده. غیر از تمام اینها شما نمیتونین این مورد رو توی برنامه فیلتر کنین تا مشکل کد زدنتون رو چک کنین. برای رفع این موارد gcc میاد در آدرس هایی که روال وقفه تولید نشده آدرس لیبل __bad_interrupt رو میذاره و اون رو به صورت weak تعریف میکنه تا بتونین خودتون این "شبه وقفه" رو بازنویسی کنین.

                              elf یک فرمت فایل اجرایی مخصوص لینوکس هست که gcc برای تولید کد نهایی ازش استفاده میکنه. چون gcc یک کامپایلر عمومی هست و فقط قسمت تولید کد اش قابل تغییر هست. بنابراین میاد یک فرم عمومی و قابل انعطاف استفاده میکنه تا بشه با یک عمل پردازش ثانویه از یک فایل اجرایی جامع فایل نهایی اجرایی روی هر سیستمی رو دربیاره. ضمنا یک سری نرم افزار استاندارد برای debug هست (مثل gdb) که بایستی با یک فرمت خاص و کامل از لحاظ ساختاری کار کنن که این فرمت همون elf هست. در صورتیکه مثلا روی ویندوز این فرمت PE هست.

                              section یا بخش برای تقسیم بندی قسمت های متفاوت اطلاعات در فایل اجرایی نهایی بکار میره. مثلا بخش هایی برای قرار گیری boot loader یا اطلاعات ثابت بدون مقدار اولیه یا با مقدار اولیه و ثوابت برنامه که بایستی در AVR داخل فلش قرار بگیرن و قسمت های دیگه ای داره. برای اطلاع و چیره شدن روی این ساختار ها بایستی اطلاعاتی راجع به کامپایلر نویسی و ساختار های high level مورد استفاده داشته باشین. که برای یک شخصی که کارش این نیست (دروس نرم افزار رو پاس نکرده باشه) یه کمی اگر نگیم سنگین؛ گیج کننده هست.
                              این یکی از دلایلی هست که گفتم به این کارها کار نداشته باشین بهتره.

                              اون اعداد بعد از ; فقط یک کمک برای خواننده این فایل های listing هست. معادل دهدهی 0x3F میشه عدد 63.

                              فرم 2-. هم یعنی current_location منهای 2. پرشی که به یک موقعیت بدون اسم انجام شده.

                              اون فرم rcall 0 هم فقط برای ایجاد فضایی 2 بایتی ( یا در بعضی میکرو ها مثل 2560 برای 3 بایت) در پشته هست. اینکار حجم بسیار کمتری نسبت به کد های دیگه داره. توجه کنین که حداقل کد برای این کار (تخصیص 2 بایت فضا در پشته) استفاده از 2 تا push هست که خودش 4 بایت حجم کد داره.

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

                              دیدگاه


                                #30
                                پاسخ : آموزش ( avrstudio5 ( AVRGCC

                                ستونها هم به ترتیب آدرس دستورالعمل - هگز دستورات - خود دستورات و نهایتا کانت ها هست.
                                نه منظورم ستون ها ی این قسمت بود :

                                کد:
                                Sections:
                                Idx Name     Size   VMA    LMA    File off Algn
                                 0 .text     0000008e 00000000 00000000 00000054 2**1
                                         CONTENTS, ALLOC, LOAD, READONLY, CODE
                                 1 .stab     000006cc 00000000 00000000 000000e4 2**2
                                         CONTENTS, READONLY, DEBUGGING
                                 2 .stabstr   00000085 00000000 00000000 000007b0 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 3 .debug_aranges 00000020 00000000 00000000 00000835 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 4 .debug_pubnames 00000024 00000000 00000000 00000855 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 5 .debug_info  000000c2 00000000 00000000 00000879 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 6 .debug_abbrev 00000081 00000000 00000000 0000093b 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 7 .debug_line  0000007e 00000000 00000000 000009bc 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 8 .debug_frame 00000030 00000000 00000000 00000a3c 2**2
                                         CONTENTS, READONLY, DEBUGGING
                                 9 .debug_str  00000097 00000000 00000000 00000a6c 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 10 .debug_loc  00000065 00000000 00000000 00000b03 2**0
                                         CONTENTS, READONLY, DEBUGGING
                                 11 .debug_pubtypes 00000012 00000000 00000000 00000b68 2**0
                                         CONTENTS, READONLY, DEBUGGING


                                ببخشید لزوم انجام اینکار رو نفهمیدم :
                                اون فرم rcall 0 هم فقط برای ایجاد فضایی 2 بایتی ( یا در بعضی میکرو ها مثل 2560 برای 3 بایت) در پشته هست. اینکار حجم بسیار کمتری نسبت به کد های دیگه داره. توجه کنین که حداقل کد برای این کار (تخصیص 2 بایت فضا در پشته) استفاده از 2 تا push هست که خودش 4 بایت حجم کد داره.

                                دیدگاه

                                لطفا صبر کنید...