اطلاعیه

Collapse
No announcement yet.

آموزش WinAVR (سطح مبتدی و پیشرفته)

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

    آموزش WinAVR (سطح مبتدی و پیشرفته)

    سلام
    تصمیم گرفتم تو این تاپیک هر چیزی از winavr که حسش اومد بنویسم. پراکنده اما مفید.

    چگونه با WinAVR یک برنامه BootLoader بنویسیم؟
    اولش باید بگم برای نوشتن BootLoader چی لازم داریم :
    1. برنامه باید تو فایل hex از آدرس معیینی شروع بشه. یعنی اگه با ponyprog یه فایل hex رو باز کنید می بینید که از آدرس 0000 شروع شده که این اول flash memory هستش. در حالی که برنامه باید در آخر فظای Flash قرار بگیره که تو Mega16 مثلا 0x1c00 هستش.
    2. آشنایی با توابع فایل <avr/boot.h> که من خورم هم آشنا نیستم هنوز، ولی تو user guideش یه مثال داره که من از همون استفاده کردم.
    3. پریدن به آدرس مورد نظر در فظای فلش که تو برنامه نویسی C کمی قلق داره.

    خوب اول این برنامه رو فرظا نوشتیم. این برنامه بعد از پروگرام شدن روی میکرو یه برنامه دیگه که تو آرایه newProg قرار داره رو تو page شماره 0 از فظای فلش میریزه. کار Bootloader هم تقریبا همینه، فقط Bootloader از بیرون اطلاعات رو میگیره که من نخواستم مثال پیچیده بشه.
    وقتی این برنامه رو کامپایل می کنید، فایل hex از آدرس 0000 شروع میشه که این بده و ما میخوایم از آدرس 0x1c00 شروع بشه.


    حالا باید به WinAVR بگیم که برو تو آدرس 0x1c00 و قبلش رو خالی بذار. پس میریم تو منوی Project/Configuration option/memory settings و یه فیلد جدید به نام .text میسازیم،( نمیدونم اسم قحطی بود؟ولی فقط باید همین اسم باشه، این تعریف شده است) و ok میکنیم و بعد از کامپایل میبینم که برنامه از آدرس مورد نظر تو hex قرار گرفته. پوگرام کنید و حالش رو ببرید.




    برنامه ای که تو آرایه newProg ریختم یه چراق چشمک زن روی PORTC هستش. به PORTC چند تا LED بزنید تا نتیجه کار رو ببینید. برای اینکه مطمئن بیشد بعد از شروع چشمک دوباره با پروگرامر فلش میکرو رو Verify کنید که میبینید اررور میده

    فایل های پیوست شده
    بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
    اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

    #2
    پاسخ : ترفندهای WinAVR

    سلام
    تصمیم گرفتم یه سری نکات به تازه کارها بگم، چون نبود منابع فارسی کافی WinAVR رو مظلوم گذاشته.
    اولش از نصب شروع میکنم که بچه های کویر الکترونیک زحمتش رو کشیدن (فصل اول/معرفی کامپایلرها/WinAVR) :
    http://kavirelectronic.ir/winavr/

    بعد از اون یک سری آشنایی های اولیه لازمه تا کار رو شروع کنید.

    1. ورودی و خروجی کردن پورتها
    متاسفانه اکثر کاربران AVR اصلا متوجه نیستند که ساختار پورتهای AVR چطوری هستش و یعنی چی که ما یه پورت رو وردی و خروجی میکنیم.
    شکل زیر نقشه یکی از پایه های یکی از پورتهای میکروکنترلر AVR است که به طور سمبلیک PORTx.n انتخاب شده :


    در شکل میبینید که پورت دارای یک تقویت کننده جریان است که ورودی آن بیت n از رجیستر PORTx و فعال کننده آن بیت n از رجیستر DDRx است. بله مفهوم ورودی یا خروجی بودن فعال یا غیر فعال بودن این تقویت کننده جریان است. اگر DDRx.n صفر باشد پایه PORTx.n در وضعیت ورودی بوده و اگر DDRx.n یک باشد پایه PORTx.n در وضعیت خروجی قرار میگیرد. در بسکام وقتی شما دستور config... مینویسید کامپایلر همین کار را میکند و در کدویژن هم اگر به کدهایی که خود کدویژن تولید میکند توجه کنید متوجه میشوید که موضوع همین است.
    مثال :
    میخواهیم پایه های PA0 و PA6 را در وضعیت خروجی قرار دهیم، چه کار کنیم؟
    جواب : کافی است مقدار 0b01000001 یا 0x41 را در رجیستر DDRA بریزیم : DDRA = 0x41;

    حالا رجیستر PINx چیه؟
    همانطور که در شکل میبینید رجیسترهای DDRx و PORTx در حقیقت فلیپ فلاپ هایی هستند که توسط CPU ست یا ریست می شوند و مقدار آنها ربطی به صفر و یک بودن وضعیت فیزیکی پایه ندارد. یعنی اگر پایه PORTx.n به زمین یا VCC متصل باشد CPU نمی تواند متوجه شود. رجیستر PINx رجیستری است که فلیپ فلاپ های آن توسط مقدار ولتاژی روی پایه ست یا ریست می شوند و مرتبا مقدار آنها به روز میشود. پس اگر بخواهیم بفهمیم که وضعیت پایه توسط عوامل خارجی (مثل یه کلید یا شاسی یا سنسور) صفر یا یک شده است، باید از این رجیستر بخوانیم.

    مثال :
    میخواهیم ببینیم آیا کاربر کلیدی را که از پایه PA.4 به زمین وصل شده فشار داده است یا نه، چکار کنیم؟
    جواب : باید بیت 4 از رجیستر PINA را چک کنید.

    کد:
    if( (PINA&0x10) != 0)
    {
    }
    
    یا
    
    #include <compat/deprecated.h>
    .
    .
    .
    if( bit_is_clear(PINA,4))
    {
    }
    و در نهایت این نکته که مقاومت PullUpداخلی که با اون AND بالای شکل فعال میشه، زمانی فعال میشه که DDRx.n صفر باشه(یعنی پورت ورودی باشه) و PORTx.n یک باشه و بیت PUD(Pullup disable در رجیستر SFIO صفر باشه.

    پس حالا یه برنامه مینویسیم که اگر کلید PA0 به زمین زده شد، LED ها روی PORTC شروع به چشمک زدن بکنن :
    آها یادم رفت. در WinAVR بجای اینکه بیاید بنویسید

    کد:
    #include <mega16.h>
    یا
    #include <mega32.h>
    کافیه بنویسید:
    #include <avr\io.h>
    اینطوری خود کامپایلر با توجه به آی سی که در تنظیمات انتخاب کردید فایل مورد نظر رو انتخاب میکنه.


    کد:
    #include <avr\io.h>
    #include <compat/deprecated.h>
    #include <util/delay.h>
    
    int main()
    {
    	// config all pins of portC as output
    	DDRC = 0xff;
    	// enable pullups of porta.0
    	PORTA |= (1<<0); //or sbi(PORTA,0);
    
    	while(1)
    	{
    		if( bit_is_clear(PINA,0) )
    		{
    			if( PORTC == 0 )
    				PORTC = 1;
    			PORTC = PORTC << 1;
    			_delay_ms(300);
    		}
    	}
    }

    و این آخر آخر بگم که delay در WinAVR در فایل delay.h قرار داره که برای include کردنش باید مطابق بالا عمل کنید. حالا توابع
    کد:
    _delay_us(int us);
    _delay_ms(int ms);
    در اختیار شماست.
    بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
    اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

    دیدگاه


      #3
      پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)


      بهینه ساز کامپایلر winavr


      بهینه ساز کامپایلر چیست؟
      بهینه ساز یک قسمت از کامپایلر است که کدهای زائد را حذف می کند.در حال حاضر winavr قوی ترین بهینه ساز را بین کامپایلرهای avr دارد.البته این کار گاهی در عملکرد برنامه اختلال ایجاد می کند که می توان با دستور Volatile که بعدا در مورد آن توضیح می دهیم از این اختلال جلوگیری کرد.
      بهینه ساز ها با تنظیماتی در کامپایلر قابل تنظیم هستند و می توان سطوح مختلفی را برای کنترل نوع رفتار آنها انتخاب کرد. کامپایلر winavr چهار سطح بهینه سازی دارد.

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


      اشتباه در بهینه سازی کد

      Volatile ، این کلمه کلیدی بسیار کم در برنامه نویسی استفاده می شود.

      آیا تا به حال در برنامه های C/C++ این تجربه را کرده اید که ، کدی که نوشته اید ، تا زمانی که optimize (بهینه ساز هوشمندی که کامپایلر ها برای بالا بردن سرعت اجرا و کم حجم کردن برنامه از آن بهره می گیرند) خاموش است برنامه بخوبی کار می کند، ولی با فعال کردن این قابلیت در کامپیلر برنامه قاطی می کند و عملکرد آن مختل می شود.



      این اتفاقات به دلیل این است که شما تا به حال از کلمه Volatile در برنامه تان استفاده نکرده اید. Volatile یک کلمه توصیفی برای تعریف یک متغییر است. این کلمه به کامپایلر می گوید که این متغییر ممکن است هر زمانی تغییر کند( مثلا توسط در اینتراپت) پس کامپایلر هنگام بهینه سازی برنامه ، از حذف این متغییر و خطوطی که این متغییر در آنها استفاده شده صرف نظر می کند.



      قبل از توضیحات بیشتر تعریف متغییر از نوع Volatile را بررسی می کنیم.

      برای تعریف یک متغییر به صورت Volatile باید این کلمه را قبل یا بعد از نوع متغییر بیاوریم:

      کد:
      volatile int foo;
      یا
      int volatile foo;

      اشاره گرهای Volatile بسیار پر کاربرد هستند، چون بیشتر مواقع اشاره گرها قربانی بهینه ساز کامپایلر می شوند.


      کد:
      pointers to volatile variables:
      volatile int * foo; 
      int volatile * foo;
      Volatile pointers to non-volatile variables:
      int * volatile foo;
      volatile pointer to a volatile variable:
      int volatile * volatile foo;

      کجا از volatile استفاده کنیم؟
      در برنامه نویسی PC سه جا باید از این کلمه استفاده کنیم (اما کامپایلر های امروزی که برای PC هستند آنقدر تکامل یافته اند که چنین اشتباهی نمی کنند) ولی در برنامه نویسی میکرو تمام متغییر هایی عمومی که بین برنامه جاری و سرویس وقفه مشترک هستند باید volatile تعریف شوند تا مشکلی پیش نیاید.


      یک مثال :
      در این بر نامه که یک چشمک زن ساده است ، می بینید که اگر متغییر عمومی tick به صورت volatile تعریف نشود برنامه کار نمی کند.


      کد:
      #include <avr\io.h>
      #include <avr\interrupt.h>
      
      int tick = 0; 
      
      int main()
      {
      	DDRC = 0xff; //define portc as out put.
      	PORTA = 0xff; //enable internal pullups.
      	TCCR0 |= 5;  //enable and run timer 0.
      	TIMSK |= 1<<TOIE0;//enable timer 0 interrupt.
      	sei();			//enable global interrupt bit.
      
      	while(1)
      	{
      	  if(tick == 1)
      	  {
      	    PORTC = PORTC << 1;
      			if(PORTC == 0)
      				PORTC = 1;
      			tick = 0;
      	  }
      	}
      } 
      
      ISR(TIMER0_OVF_vect)
      {
      	tick = 1;
      }

      حالت دوم با volatile که کار می کند.


      کد:
      #include <avr\io.h>
      #include <avr\interrupt.h>
      
      volatile‏ int tick = 0; 
      
      int main()
      {
      	DDRC = 0xff; //define portc as out put.
      	PORTA = 0xff; //enable internal pullups.
      	TCCR0 |= 5;  //enable and run timer 0.
      	TIMSK |= 1<<TOIE0;//enable timer 0 interrupt.
      	sei();			//enable global interrupt bit.
      
      	while(1)
      	{
      	  if(tick == 1)
      	  {
      	    PORTC = PORTC << 1;
      			if(PORTC == 0)
      				PORTC = 1;
      			tick = 0;
      	  }
      	}
      } 
      
      ISR(TIMER0_OVF_vect)
      {
      	tick = 1;
      }
      فایل های پیوست شده
      بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
      اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

      دیدگاه


        #4
        پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

        سلام ........
        با تشکر از اقا مصطفی به خاطر مطالب سودمندی که گذاشتند .
        راستش امشب بیکار بودم گفتم شاید تو این تاپیک بتونم کمک کنم.
        در مورد volatile بدلیل اهمیت تقریبا بالا توی AVR-GCC بیشتر توضیح میدم.
        با اجازه :
        Const و volatile
        دو کلید واژه Const و volatile کاملا از یکدیگر مستقل بوده و در حقیقت متضاد هم میباشند volatile برای متغیرهایی استفاده میشود که ممکن است هر لحظه بدون اطلاع کامپایلر تغییر کنند و موارد استفاده ان عموما سه مورد زیر است :

        1.حافظه های Memory-mapped مثل بافر ورودی و خروجی سخت افزارها که هر لحظه ممکنه تغییر کنه مثلا در AVR میتونیم رجیستر های ورودی پورت ها رو مثال بزنیم.
        2.متغیر های سراسری که ممکنه بوسیله یک وقفه تغییر پیده کنند.
        3.متغیر های سراسری که توی چند thread مجزا دسترسی میشوند (هنگامیکه از multi-threading استفاده کنیم).

        موارد 1 و 3 که تقریبا منتفی میشه البته مورد 3 هنگامیکه بخوایم از برنامه های زمانبند (مثل U-Cos که قسمتی از کار سیستم عامل را شبیه سازی میکنند) استفاده کنیم اهمیت پیدا میکنه.
        اما مورد 2 چه زمانی اتفاق میفته؟
        تنها زمانی که یک متغیر سراسری در یک روتین وقفه دستیابی بشه (یا تابعی که از روتین وقفه فراخونی میشه) و حداقل در یک تابع "غیر وقفه ای" (که در روتین وقفه ای قرار نداشته باشد یا به عبارت دیگر بیتGlobal Interrupt Flag در اون لحظه 1 باشد و امکان وقفه وجود داشته باشد)نیز فراخوانی شود و بالعکس. oo:
        صبر کنید الان بیشتر توضیح میدم (برای مبتدی ها) مثلا شما یه متغیر به نام i دارین و و این متغیر در روتین وقفه خارجی 0 (int 0) (به طور مستقیم یا توسط تابعی) دستیابی میشه و همین متغیر در روتین وقفه TIMER 0 OVF نیز دستیابی میشه در این حالت نیازی به کلید واژه volatile برای این متغیر نیست چون زمانی که میکرو درگیر پردازش در Int0 است بیت (Global Interrupt Flag) صفر است و امکان ایجاد وقفه ای دیگر نیست.
        چون پست طولانی میشه مزایا و مشکلاتشو تو پست بعدی برسی میکنیم.


        Qt - A cross-platform application and UI framework

        با کامپایلرهای قدرتمند GCC در Linux و MinGw در Windows

        دیدگاه


          #5
          پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

          volatile (مزایا و معایب):
          مزیتش که کاملا تا الان مشخص شد الان مثال میزنم که واضحتر هم بشه .
          برنامه زیر رو در نظر بگیرید و با AVR-GCC کامپایل کنید(با مد s Optimize) میکرو رو مثلا MEGA8 بگیرید.



          کد:
          
          #include <avr/io.h>
          #include <avr/interrupt.h>
          #include <util/delay.h>
          	
          	void Function1 (void);
          	
          	int main(void)
          	{
          		DDRD=0xff;
          		TIMSK=(1<<TOIE2);		//Timer OVF interrupt is enable
          		TCCR2=(2<<CS20);		//Timer Start
          		
          		asm("sei"::);
          		Function1 ();
          		return(0);
          	}
          	/*************************************************************/
          	 uint8_t	i;		//Global Variable
          	 
          	void Function1 (void)
          	{
          			 for(;;)
          			 {
          			for(i=0;i<10;i++)
          			 //asm("nop"::);
          			 _delay_ms(100);
          			}
          	}
          	/*************************************************************/
          	ISR(TIMER2_OVF_vect)
          	{
          	PORTD=i;  //need i here
          	return;
          	}

          خوب با یه نگاه به برنامه تقریبا هممون انتظار داریم که روی پورت D به صورت مداوم تغییر کنه و احتمالا به ترتیب بشمره :question:
          اما پس از کامپایل و شبیه سازی چیز دیگری رو خواهید دید (خروجی تغییر نمیکنه) :surprised:
          حالا قبل از متغیر i از کلید واژه volatile استفاده کنید و مجددا کامپایل و برسی کنید بله این بار همونطور که انتظار داشتید شد.
          خب حالا دقیقا چه اتفاقی میفته که اینطوری میشه ؟ دقیقا به optimize مربوط میشه و تا حدودی به هوشمندی کامپایلر قدرتمند GCC برمیگرده که توی تحلیل کد اسمبلی انشالا پست بعدی برای علاقمندان برسی میکنیم. :agree:
          خب مزیتش رو تقریبا فهمیدیم اما ممکنه بپرسیم عیب این کلید واژه چیه چرا برای همه متغیرها استفاده نمیکنیم؟
          مهمترین عیبش به طور کلی افزایش حجم کد و زمان اجرایی برنامه که توضیح بیشتر هنگام تحلیل کد اسمبلی داده میشه .
          انشالا فردا در فرصت مناسب برای دوستانی که علاقه بیشتری دارن خروجی اسمبلی برنامه بالا رو میزارم و با هم تحلیلش میکنیم.


          Qt - A cross-platform application and UI framework

          با کامپایلرهای قدرتمند GCC در Linux و MinGw در Windows

          دیدگاه


            #6
            پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

            مطلب قبلی رو کامل کردم. از دوستانی هم که تو بحث شرکت کردن تشکر می کنم.
            البته مطالبی که میخواستم بگم رو sally جان گفت تقریبا :mrgreen:
            بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
            اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

            دیدگاه


              #7
              تحلیل اسمبلی در Avr-Gcc

              سلام .........
              تو این قسمت سعی میکنم یه تحلیل حرفه ای تر از اون چیزی که تا الان در مورد volatile گفته شد ارائه بدم تا الان گفتیم مشکل پیش میاد ولی چرا :question:. پیش از شروع میگم که نیاز به اگاهی کمی از زبان اسمبلی AVR داریم اگر چه سعی میشه بیشتر توضیح بدم که افراد مبتدی هم سردربیارن فقط حرفه ای ها حوصلشون سر نره.
              برنامه زیر رو کامپایل کنید (این برنامه هم همون مشکل رو داره که توضیح میدم فقط به خاطر کوچیک شدن حجم کد و فهم راحت تر تغییراتی ایجاد کردم.)
              بدون volatile کامپایل کنید (با همون مد Optimize Sو اگه از AVR Studio استفاده میکنید در قسمت تنظیمات Generate List File رو تیک بزنید.)


              کد:
              			 #include <avr/io.h>
              #include <avr/interrupt.h>
              #include <util/delay.h>
              	
              	void Function1 (void);
              	
              	int main(void)
              	{
              		DDRD=0xff;
              		TIMSK=(1<<TOIE2);		//Timer OVF interrupt is enable
              		TCCR2=(2<<CS20);		//Timer Start
              		asm("sei"::);
              		
              		for(;;)
              		 Function1 ();
              		 
              		return(0);
              	}
              	/*************************************************************/
              	uint8_t	i;		//Global Variable
              	 
              	void Function1 (void)
              	{
              			for(i=0;i<10;i++)
              			 asm("nop"::);
              	}
              	/*************************************************************/
              	ISR(TIMER2_OVF_vect)
              	{
              	PORTD=i;  //need i here
              	return;
              	}


              فایل با پسوند (LSS) رو با یه ویرایشگر متنی باز کنید. کد مربوط به Function1 رو به این صورت ملاظه میکنیم.

              کد:
              0000005e <Function1>:
               5e:	80 e0    	ldi	r24, 0x00	; 0
               60:	02 c0    	rjmp	.+4   	; 0x66 <Function1+0x8>
               62:	00 00    	nop
               64:	8f 5f    	subi	r24, 0xFF	; 255
               66:	8a 30    	cpi	r24, 0x0A	; 10
               68:	e0 f3    	brcs	.-8   	; 0x62 <Function1+0x4>
               6a:	80 93 60 00 	sts	0x0060, r24
               6e:	08 95    	ret
              اول این که میدونیم که Ram توی AVR از ادرس 0x60 شروع میشه و متغیر های عمومی (حتی اگر از کلید واژه Register استفاده کنید کامپایلر روی حافظه Ram تعریف میکنه (نه تنها در AVR-Gcc) ) و متغیر ها رو به ترتیب تعریف بهشون حافظه میده پس مطمئنا ادرس متغیر i میشه 0x60
              میبینید که رجیستر R24 رو با مقدار صفر پر کرده و دستور nop رو در خط بعدی اجرا میکنه و سپس یه واحد بهش اضافه میکنه (در واقع "1-" که میشه "0xFF" رو ازش کم میکنه که مثل اینه که داره اضافه میکنه بخاطر اینکه توی AVR دستور addi نداریم!)
              دو خط بعدی رجیستر رو با عدد 10 مقایسه میکنه و در صورت کوچکتر بودن ( Carry Flag یک باشه) دوباره میپره به دستور nop و بدنه حلقه رو انجام میده و ....
              بعد از اینکه به عدد 10 رسید حلقه تموم میشه و در اینجا (چون در حقیقت ما با یه متغر عمومی کار میکردیم ) کامپایلر اخرین مقدار شمارنده حلقه رو توی ادرس 0x0060 (ادرس متغیر i) ذخیره میکنه در نتیجه هر بار که توی وقفه تایمر این مقدار رو بخونیم همون عدد 10 خواهد بود و تغییر نمیکنه.
              تا اینجا فقط اینکه چه اتفاقی میفته رو برسی کردیم اما هنوز این سوال مونده که چرا :question:
              پاسخ : هیچ دستور محاسباتی و منطقی در AVR با حافظه RAM نمیتونه کار کنه (بر خلاف PIC) توی حافظه Ram فقط میتونیم بنویسیم یا بخونیم یعنی برای اضافه کردن یک واحد به i مقدار کنونی باید تو یه رجیستر خونده بشه (2 پالس) مقدار رجیستر اضافه بشه و در نهایت سر جای قبلی نوشته بشه (2 پالس)
              یعنی تو این مثال علاوه بر اضافه شدن 8 یا 4 بایت به کد ( LDS,STS چهار بایتی هستند اما LD,ST دو بایتی) زمان اجرای هر دور حلقه که 5 کلاک است 4 کلاک اضافه میشه یعنی تقریبا 2 برابر :eek:
              الان کاربرد volatile واضح شد "volatile متغیر رو دائم بروز نگه میداره و به Optimizer میگه که اجازه نداره برای افزایش سرعت مثلا در یک حلقه از رجیستر استفاده کنه و در انتها مقدار نهایی رو تو متغیر بریزه چیزی که الان میبینیم . :mrgreen:
              حالا وقتشه که با بکار بردن کلید واژه volatile برای متغیر i مجددا کامپایل کنید (میتونیم حدس بزنیم که چیکار میشه) کد به این صورت درمیاد

              کد:
              0000005e <Function1>:
               5e:	10 92 60 00 	sts	0x0060, r1
               62:	06 c0    	rjmp	.+12   	; 0x70 <Function1+0x12>
               64:	00 00    	nop
               66:	80 91 60 00 	lds	r24, 0x0060
               6a:	8f 5f    	subi	r24, 0xFF	; 255
               6c:	80 93 60 00 	sts	0x0060, r24
               70:	80 91 60 00 	lds	r24, 0x0060
               74:	8a 30    	cpi	r24, 0x0A	; 10
               76:	b0 f3    	brcs	.-20   	; 0x64 <Function1+0x6>
               78:	08 95    	ret
              خوب نتیجه رو میبینیم همونطور که گفتم هر بار متغیر i(ادرس 0x60) توی r24 لود میشه و یه واحد اضافه میشه و دوباره توی متغیر ذخیره میشه
              اما نکته ای که ممکنه برای یک انسان نامفهوم باشه اینه که چرا وقتی مقدار رو افزایش داده و توی ادرس 0x60 ذخیره کرده با وجود اینکه مقدار جدید تو رجیستر R24 قرار داره برای مقایسه با 10 دوباره این مقدار رو از متغیر لود میکنه ؟
              پاسخ : زیاد به دل نگیرین جز لاینفک سوتی های کامپایلره دیگه البته بعدا توضیح میدم که AVR-GCC توی مد Optimize S) به دلیل هوشمندی بالا (حذف قسمتهایی از کد که در روند برنامه تاثیر گذار نیستند و توانایی تحلیل کد و گاها تغییر شیوه برنامه و تعریف متغیرهای محلی به صورتی که عمل مشابه و با پیچیدگی کمتر و حجم کد کمتر انجام شود !) در مقایسه با کامپایلرهایی نظیر Codevision,IAR بی شک بهترین خروجی رو داره (لااقل از نظر زمانی) . انشالاه پست بعدی . :agree:


              Qt - A cross-platform application and UI framework

              با کامپایلرهای قدرتمند GCC در Linux و MinGw در Windows

              دیدگاه


                #8
                پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                واقعا وجود یک همچین تاپیکی لازم بود .

                جا داره همینجا از mostafahk عزیز که بحث رو شروع و ادامه دادند و sallysat عزیز که با حوصله مطالب را توضیح میدهند تشکر کنم . واقعا ممنونم. همونطور که میدونید winavr جدا مظلوم واقع شده و برای مثال حتی یک منبع جامع به زبان فارسی نداره.

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

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

                موفق باشید .

                دیدگاه


                  #9
                  پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                  یه چیزی که خیلی برای کاربرا سوال میشه اینه که چطوری میشه یه برنامه رو تو جند تا فایل نوشت؟
                  در واقع مطلبی که الان حسش گفتنش اومد :mrgreen: کلی تر از جواب این سواله، میخوام طریقه نوشتن کتابخانه و حد اقل استفاده از header file ها رو یکم روش بحث کنم و این حس در حالی اومده که قسمت آخر جومونگ داره پخش میشه

                  header file چیست؟
                  سر فایل یا همون اسم خارجیش که گفتم، فایل هایی هستند که یک سری تنظیمات و معرفی هایی رو راجع به کتابخانه به ما میدن. اگه یه فایل .h رو باز کنید ، میبینید که پر از توضیحاته و prototype توابع هستش. در واقع طراحان زبان C در نظر گرفتند که بیان کتابخانه ها رو 2 قسمت کنند، یکی فایل .h که همون header file هستش و قسمت دیگر هم که شرح توابع موجود در header file رو داره، میتونه چند حالت داشته باشه که در اینجا حالت سادش رو در نظر میگیریم، یه فایل با همون نام با پسوند .c یا .lib (در کدویژن بیشتر) .

                  برای مثال چند تا header file گذاشتم که میتونید ببینید توشون چیه.

                  حالا اصل موضوع:
                  اکثر header file ها که ما استفاده می کنیم، 2 فایل دارن. یکی که ضروری هستش، با پسوند .c و دیگری هم با پسوند .h
                  برای استفاده از اونها اینطوری کنید :
                  -اول فایل ها رو در محل پروژه کپی کنید.
                  -فایل .c را اضافه کنید.

                  -فایل .ا رو هم به این قسمت اضافه کنید.


                  تو برنامه فایل ها رو با " استفاده کنید :

                  کد:
                  #include "test.h"

                  حالا شما وقتی برنامه خیلی گنده میشه لازمه اون رو به فایل های ریزتر خرد کنید که توابع هر فایل یه سری کارها انجام بده. برای اینکار الان فقط یه قائده میگم که حفظ کنید:
                  - تو فایل .h بیاید اول فایل و قبل از هر چیزی این 2 خط رو بنویسید :

                  کد:
                  #ifndef AWORD
                  #define AWORD
                  و در ته ته فایل هم بنویسید:
                  #endif
                  بین این دو هر چی دوست دارید تابع بنویسید. میتونید توابع رو با کلاس فقط prototype بنویسید و شرحشون رو تو فایل هم نام با پسوند .c بزارید، ولی کتابخانه های کوچیک این کار رو نیاز ندارن. در فایلهای ضمیمه فایل uart.h رو میبینید که تکیه و .c نداره، ولی فایل های clcd.h و ks0108.h همراه دارن.

                  نمیدونم خیلی پراکنده گفتم ، امیدوارم مفید باشه.
                  یادتون نره، تو هر فایلی که بخواید از توابع موجود در فایل .h دیگه استفاده کنید، باید اون رد include کنید با " .
                  فایل های پیوست شده
                  بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
                  اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

                  دیدگاه


                    #10
                    پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                    سلام ..........
                    با تشکر از اقا مصطفی برای مطالب مفیدی که گذاشتند.
                    گاها ممکنه بعضی از افرادی کا تازه با AVR-GCC اشنا شده اند این سوال براشون مطرح بشه که مثلا چطوری مشابه library های استاندارد در avrlibc بسازند به صورتی که دیگه با سورس کد و هدر فایل رو با خودشون یدک نکشند و با هر بار کامپایل برنامه نیاز به کامپایل اون سورس نباشه :question:
                    library ها در کامپایلر های GCC و نیز کامپایلهای مشتق شده از ان با پسوند (a.) میباشند برای ساخت library نیاز به فایل object دارید.
                    برای روشن تر شدن با هم یه فایل ساده رو به کتابخونه تبدیل میکنیم.


                    mystr.h
                    کد:
                    #ifndef _MYSTRLIB_H_
                    #define _MYSTRLIB_H_
                    
                    extern unsigned char mstrcmp(const unsigned char *, const unsigned char *);
                    extern void mstrcpy(unsigned char *, const unsigned char *);
                    
                    #endif /* _MYSTRLIB_H_*/
                    ----------------------------------------------------------------------------

                    mystr.c
                    کد:
                    unsigned char mstrcmp(const unsigned char * str1, const unsigned char * str2)
                    	{
                    		for(;*str1==*str2&&*str1&&*str2;str1++,str2++);
                    		return	*str1-*str2;
                    	}
                    void mstrcpy(unsigned char * str1, const unsigned char * str2)
                    	{
                    	for(;*str2;*str1=*str2,str1++,str2++);
                    	}

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


                    main.c
                    کد:
                    #include <avr/io.h>
                    #include "mystr.h"
                    
                    unsigned char str1[]="abe";
                    unsigned char str2[]="abc";
                    
                    int main (void)
                    {
                    DDRD=0xff;
                    PORTD=mstrcmp(str1,str2);
                    while(1);
                    }
                    حالا از خط فرمان (cmd) وارد دایرکتوری که فایل mystr.c کامپایل شده است شوید (برای راحتی کار) و فرمان زیر را اجرا کنید
                    (اگر چند object file را میخواهید درون یک library قرار دهید تمام object file ها را پشت سر هم و با فاصله بنویسید)

                    avr-ar rcs libmystr.a mystr.o
                    دقت کنید که فایل object حتما در مسیر مورد نظر پس از کامپایل ایجاد شده باشد.
                    libmystr.a نام کتابخانه ایجاد شده میباشد کاراکترهای این نام الزاما حروف کوچک بوده و واژه "lib" قبل از نام کتابخانه و پسوند a. الزامی است.
                    خوب حالا فایل شما به یک library استاندارد تبدیل شد برای استفاده از ان بهتر است انرا در مسیر پیشفرض کتابخانه ها کپی کنید اگر از پکیج winavr استفاده میکنید

                    WinAVR\avr\lib
                    و همچنین هدر فایل را برای راحتی بیشتر در مسیر زیر کپی کنید (میتوانید در این مسیر یک دایرکتوری ایجاد کرده و انرا قرار دهید )

                    WinAVR\avr\include
                    من یک دایرکتوری در این مسیر به نام mylib ایجاد کرده و هدر فایل را درون ان قرار دادم.
                    خوب دیگه تموم شد حالا یه پروژه جدید ایجاد کنید و libmystr.a را به پروژه اضافه کنید (اگر از avr studio استفاده میکنید پنجره project options را باز کنید و از قسمت libraries این کار را انجام دهید دقت کنید که library مورد نظر به لیست اضافه شده است :eek
                    کد زیر را برای تست کامپایل و دیباگ کنید :applause:


                    main.c
                    کد:
                    #include <avr/io.h>
                    #include <mylib/mystr.h>
                    
                    unsigned char str1[]="abe";
                    unsigned char str2[]="abc";
                    
                    int main (void)
                    {
                    DDRD=0xff;
                    PORTD=mstrcmp(str1,str2);
                    while(1);
                    }

                    چه مواقعی بهتره Library درست نکنیم :question:
                    چون Library یک کد کامپایل شده است پس در مواردی که کد به IO یا به فرکانس کاری میکرو وابسته است نمیتونیم استفاده کنیم
                    (نه که نمیتونیم خوبشم میتونیم اما برنامه به یک پورت خاص یا یک میکروی خاص یا یک سرعت خاص و ... وابسته میشود)

                    در پست بعدی انشالاه در مورد ماکروها و کاربرد وسیعی که دارن صحبت خواهم کرد.


                    Qt - A cross-platform application and UI framework

                    با کامپایلرهای قدرتمند GCC در Linux و MinGw در Windows

                    دیدگاه


                      #11
                      پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                      تاپیک داشت خوب پیش می رفت!
                      بقیه اش چی شد؟
                      { خلاف قوانین - پاک شد }

                      دیدگاه


                        #12
                        پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                        سلام
                        یحث رسیده بود به ماکروها که من خودم زیاد نمیدونم ماکرو دقیقا به چی میگن. ولی برداشتی و اطلاعاتی که دارم اینه.
                        خیلی وقتها نوشتن یک معادله یا برنامه رو شلوغ میکنه یا بعضی وقتها یک سری دستورات مبهم هستن(حالا تو مثال معلوم میشه) که با استفاده از ماکروها میشه یه طوری این مشکل رو حل کرد.

                        برنامه زیر رو در نطر بگیرید :


                        کد:
                        #include <ave/io.h>
                        #include <util/delay.h>
                        
                        int main()
                        {
                          for(;;)
                          {
                           PORTA |= 0x80;
                           _delay_ms(300);
                           PORTA &= ~0x80;
                           _delay_ms(300);
                          }
                        }

                        خوب من وقتی به این برنامه نگاه میکنم سر در نمیارم این پایه PORTA.7 چیه که هی دارن صفر و یکش میکنن برنامه زیاد گویا نیست. اما من با استفاده از ماکرو یه جور دیگه مینویسم :


                        کد:
                        #include <ave/io.h>
                        #include <util/delay.h>
                        
                        #define LED_ON() PORTA|=0x80
                        #define LED_OFF() PORTA&= ~0x80
                        
                        int main()
                        {
                          for(;;)
                          {
                           LED_ON();
                           _delay_ms(300);
                           LED_OFF();
                           _delay_ms(300);
                          }
                        }

                        خوب یادم رفت بگم ماکروها تماما با دستور #define تعریف میشن. تو این برنامه یه دستور ساده به عنوان ماکرو تعریف شده. البته وجود اون پرانتز در اینجا ضروری نیست و میتونید هم تو تعریف ماکرو و هم موقع استفاده پاکش کنید. اما یه پله بالا تر :



                        کد:
                        #include <ave/io.h>
                        #include <util/delay.h>
                        
                        #define SHIFT(n) (1>>n)
                        
                        int main()
                        {
                          char i;
                          for(;;)
                          {
                           i++;
                        PORTA =    SHIFT(i);
                           if(i==8)
                             i=0;
                           _delay_ms(300);
                          }
                        }

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


                        کد:
                        #include <ave/io.h>
                        #include <util/delay.h>
                        
                        #define LEDR_ON() PORTA|=0x80
                        #define LEDR_OFF() PORTA&= ~0x80
                        
                        #define LEDG_ON() PORTA|=0x40
                        #define LEDG_OFF() PORTA&= ~0x40
                        
                        
                        #define LED_RED() LEDR_ON();LEDG_OFF()
                        #define LED_GREEN() LEDR_OFF();LEDG_ON()
                        
                        
                        int main()
                        {
                          for(;;)
                          {
                           LED_GREEN();
                           _delay_ms(300);
                           LED_RED();
                           _delay_ms(300);
                          }
                        }

                        خوب من بیشتر از این نمیدونم. دوستان اگه صحبتهای من رو اصلاح و کامل کنن ممنون میشم.
                        بت در بغل و به سجده پیشانی ما کافر زده خنده بر مسلمانی ما
                        اسلام به ذات خود ندارد عیبی هر عیب که هست در این مسلمانی ماست

                        دیدگاه


                          #13
                          پاسخ : آموزش WinAVR (سطح مبتدی و پیشرفته)

                          سلام :applause:
                          واقعا خسته نباشین من خودم با کدویژن کارکردن جدیدا دارم با winavr کارمیکنم . :biggrin:
                          ما منتظر بقیه اموزشاتون هستیم :applause: :applause: :job:

                          دیدگاه

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