اطلاعیه

Collapse
No announcement yet.

آموزش winavr و avrlib

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

    آموزش winavr و avrlib

    سلام

    ( قبل از شروع یه موضوع مهمی هست که بگم ... من دنبال یه دوست قدیمی به نام "علی قره باقی" می گردم. هیچ نشونی ازش ندارم. اگه کسی رو با این نام می شناسین حتما با پیغام خصوصی بهم بگین. دیگه در این مورد پستی هم اینجا ندین. ممنون)

    __________________________________________________ ________________________________________________
    برای راحتی کار دوستان مطالب مهم رو همراه با لینک شروع بحث فهرست بندی کردم. البته لینک ها مربوط به اولین پست درارتباط با اون موضوع مشخص شده، و باید ادامه پست ها رو هم ببینین. ضمنا غیر از این موضوعات، نکات متفرقه متعددی بین پست ها مشاهده میشه...
    آخرین ویرایش : 1 مرداد 89

    شروع winavr
    راه اندازی LCD کاراکتری + ادامه نکات ابتدایی
    راه اندازی ADC + ادامه نکات winavr
    تایمر
    ارتباط سریال uart + کتابخونه
    مقایسه تولید کد در کدویژن و winavr
    کار با رجیسترها بصورت بیتی
    راه اندازی LCD گرافیکی
    ذخیره رشته در flash
    نکات اکلیپس برای winavr
    ISR و ویژگی های آن (تعریف وقفه)
    دسترسی به حافظه eeprom
    __________________________________________________ ________________________________________________



    همونطور که وعده داده بودم می خوام تو این تاپیک مطالبی در مورد شروع کار با winavr بنویسم. دیگه نمی خوام از مزایای winavr بگم. خودتون می دونین دیگه بخاطر پن سورس بودنش، اونقدر براش کتابخونه نوشته شده که هیچ کامپایلری رو دستش بلند نمیشه! یادگرففتن winavr و دید زدن کتابخونه هاش باعث میشه اصول برنامه نویسی میکرو و سخت افزار های جانبی رو از پایه یادبگیریم. و یکی از مهمترین نتیجه هاش اینه که اگه یه سخت افزار جدیدی پیدا کردیم که کتابخونه برنامه نویسی براش نبود، خودمون بتونبم راه انداریش کنیم.
    بدی winavr اینه که اوایل یکم بدقلقه و نمیشه زود باهاش کنار اومد! من تو این تاپیک سعی می کنم کمک کنم نا از سد شروع کار با winavr بگذرین. فقط برای شروع باید یکم حوصله بخرج بدین. کم کم winavr براتون لذت بخش میشه! البته اینم بگم خودم تازه کارم و کمکتون احتیاج دارم. امیداورم همه به هم کمک کنیم تا winavr تو ایران بین همه جا باز کنه و هرکی کار جدیدی با avr انجام داد، اون رو بصورت یه فایل کتابخونه winavr در احتیار همه قرار بده.
    خوب بریم سر اصل مطلب
    Winavr رو که نصب کنید یه نرم افزار داره به نام programmers notepad که باید کدهاتون رو تو اون بنویسین. من پیشنهاد می کنم نرم اقزار Avrstudio (نسخه 4.12 به بعد) رو هم نصب کنید و از اون برای کدنویسی استفاده کنین. محیط user friendly تری داره و دردسرهای makefile رو هم نداره. قابلیت شبیه سازی و دیباگ کردن هم که یکی از مهمترین امکاناتش هست. (البته من هنوز نتونستم دیباگ کنم!!)
    با خود فایل نصب winavr کتابخونه های زیادی ارائه نمیشه. برای اینکه زیاد درگیر کدنویسی و ریزکاری ها نباشین باید با توجه به پروژه تون، فایل های کتابخونه ای winavr تهیه کنید. avrlib نام یه سری کتابخونه که توسط شخصی بنام Pascal Stang نوشته شده و کار برنامه نویس های winavr رو خیلی راحت می کنه. هدف منم تو این تاپیک بررسی چندتا از کتابخونه های avrlib هست. کافیه چندتا از این هدرها رو بررسی کنیم و یاد بگیرین. اونوقت برای انجام هرکاری تو winavr باید کتابخونش رو از تو avrlib یا اینترنت پیدا کنین و خیلی راحت مشکلتون حل میشه. خوبیش اینه برای هرکاری کتابخونه های متنوع و زیادی هست و میتونین بهترینش رو انتخاب کنین. امیدوارم شما هم کمک کنید و کتابخونه هایی که پیدا می کنین و یاد میگیرین رو اینجا معرفی کنین.
    دانلود avrlib
    http://www.mil.ufl.edu/~chrisarnold/components/microcontrollerBoard/AVR/avrlib
    فعلا با avrlib کاری نداریم.
    برای فراخوانی فایل های کتابخوانه ای در winavr از دستور زیر استفاده می کنیم :
    #include <filename>
    این دستور توانایی فراخوانی فایل های هدر خود winavr موجود در پوشه include رو داره.
    مهمترین فایل هدر که تو هر پروژه winavr باید فراخوانی بشه هدر io.h هست. چون این فایل در زیرشاخه avr از پوشه include قرار داره (بعنی …\include\avr\io.h) باید با کد زیر فراخوانی بشه.
    #include <avr/io.h>
    این دستور برای معرفی رجیسترهای میکرو به کامپایلره. برای همه میکروهای avr مشترکه. هدر io.h با توجه به mfile رجیسترهای avr موردنظر رو تشخیص میده. خوب الان به رجیسترهای میکرو دسترسی دارین.
    فایل بعدی هدر delay.h در زیرشاخه util
    #include <util/delay.h>
    این هدر شامل توابع ایجاد تاخیر _delay_ms و _delay_us است. که مشخصه دیگه، اولی میلی ثانیه و دومی میکرو ثانیه.

    (تگ کد مشکل داره! چپ چین نمیشه ! منم که نمی تونم تو یه تاپیک پست رو بذارم! ادامه پست بعدی...)
    https://www.linkedin.com/in/mohammadhosseini69

    http://zakhar.blog.ir

    #2
    پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

    تو winavr عملگر یا تابعی مخصوص کار با یک بیت وجود نداره. مثلا نمی تونیم بنویسیم DDRA.2=1
    برای کار بر روی یک بیت باید از عملگرهای باینری و منطقی استفاده کنیم. مثل and و or و not و ... (& و | و ! و << و >> ^ و ...). این روش ها رو خودتون می دونین. اما می خوام دو تابع (البته به شکل ماکرو نوشته شدن) مهم avrlib رو معرفی کنم که تو تمام کتابخونه های avrlib استفاده شده.
    فایل avrlib که دانلود کردین رو از حالت فشرده خارج کنین. وارد پوشه avrlib بشین. چندتا پوشه و کلی فایل .c و .h هست. فایل های کتابخونه ای معمولا بصورت یک جفت فایل (هدر با پسوند h و کد سی با پسوند c) هستن. معمولا تو فایل هدر، ماکروها و سرآیند (پروتوتایپ) توابع تعریف و توضیح داده میشه. و تو فایل .c کد کامل تابع نوشته شده. یکی از پوشه های مهم avrlib پوشه conf (مخفف configuration) هست که شامل هدرهای مخصوص تنظیم راه اندازی سخت افزارهای جانبی مثل lcd است.
    از پوشه avrlib ، فایل هدر acrlibdefs.h رو کپی کنید تو پوشه پروژه تون (کنار فایل .c پروژه(. چون این هدر رو خودتون اضافه کردین برای فراخوانیش باید دستور include رو اینطوری بنویسین :
    #include “avrlibdef.h”
    دقت کنید که تا متوجه فرق این دستور include با دستور قبل بشین. اگه بخوایم از یکی از فایل های خود winavr استفاده کنیم نام اون رو بین <.....> مینویسیم. اما اگه بخوایم فایل دیگه ای خودمون اضافه کنیم، اون فایل رو کپی می کنیم تو پوشه پروژه و اسم فایل رو بین "...." مینویسیم.
    تو هدر avrlibdefs چندتا ماکرو تعریف شده. اون دو تا تابع (یا ماکرو) که می خواستم بگم تو این هدر تعریف شدن. sbi و cbi
    sbi برای یک کردن بیت و cbi برای صفر کردن بیت استفاده میشه.
    فرض کنید می خوایم یه led رو با یکی از پایه های میکرو روشن کنیم و یه کلید هم بذاریم که با اون خاموش بشه.به کد زیر دقت کنین.
    #include <avr/io.h>
    #include <util/delay.h>
    #include "avrlibdefs.h"
    int main()
    {
    DDRA = 0xff; //output
    DDRB = 0x00; //input
    PORTB = 0xff; //pullup
    sbi(PORTA, 0); //PORTA.0=1
    _delay_ms(1000);

    while (1)
    {
    if(!(PINB & 0b100))
    cbi(PORTA, 0); //PORTA.0=0

    }
    }
    پورت A خروجی و پورت B ورودی تعریف شده. مقاومت های پول پ پورت B هم فعال شده. دستور sbi(PORTA, 0); بیت شماره صفر PORTA رو 1 می کنه. بعدش یه تاخیر 1000 میلی ثانیه ای ایجاد میشه. بعدش تو یه حلقه دائم چک میشه که اگه بیت شماره 2 رجیستر PINB صفر شد (پایه B2 با سوییچ به زمین وصل بشه) دستور cbi رو اجرا می کنه. cbi(PORTA, 0) بیت 0 پورت A رو صفر می کنه.
    برای جلسه اول کافیه. جلسه بعد با بررسی کتابخونه lcd به جزئیات بیشتری می پردازیم. شرمنده یکم روده درازی کردم! اصل مطلب از جلسه بعد شروع میشه.
    چندتا نکته :
    1- تو avrstudio – منوی project/configuration options بخش General، میکرو avr و فرکانس کلاک رو حتما تعیین کنید.
    2 – مورد خاصی یادم نمیاد !!
    https://www.linkedin.com/in/mohammadhosseini69

    http://zakhar.blog.ir

    دیدگاه


      #3
      پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

      چند تا نکته مهم:
      1- برای چپ براست نوشتن به تاپیک زیر سری بزنید:
      http://www.eca.ir/forum2/index.php?topic=24162.0

      2- اگر شما در codevision میبینین که براحتی میشه عملیات روی بیت ها انجام داد بخاطر اینه که این یک کامپایلر خاص هستش. اصلا این یک فرم نوشتاری غلط در زبان C هست که DDRA.2 = 1 !
      ایضا WinAVR نسخه کامپایلر GCC برای AVR هست. چون دقیقا همین کامپایلر برای پروسسور های دیگه ای مثل 386 و ARM هم تغییر داده شده.

      3- الزاما بخاطر قدرتمند بودن WinAVR نبایستی codevision پر از باگ رو کنار گذاشت چون میشه براحتی برای استفاده از code wizard برای تولید کد مشخصات سخت افزاری از cv استفاده کرد ولی بقیه کارها رو با WinAVR انجام داد.

      4- ایضا خیلی وقتها برای یک تست کوچیک احتیاجی به سر و کله زدن به WinAVR نیست چون هر چیزی رو بهر چیزی ساخته اند.

      دیدگاه


        #4
        پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

        سلام
        قسمت دوم
        این قسمت خیلی مهم و نکات مهمی رو می خوام بگم تا با استفاده از کتابخونه ها آشنا بشین. فقط یکم صبر و حوصله بخرج بدین و کم نیارین تا شیرینی winavr رو بچشین! می خوایم یه ال سی دی کاراکتری رو با avrlib راه اندازی کنیم.
        وارد پوشه avrlib بشین. یه نگاهی به فایل ها بندازین. 2 تا فایل lcd.h و lcd.c وجود داره. lcd.h رو باز کنید. (می تونین با notepad باز کنین. من از notepad++ استفاده می کنم. چون از قدرت درک و شعور بالایی برخورداره! زبون های مختلف حالیش میشه و کدها رو رنگی می کنه). همونطور که می بینین اول این فایل هدر توضیح داده که این فایل میتونه ال سی دی های کاراکتری رو درایو کنه. یکم بیاین پایین تا به اول کدها برسین. دوتا فایل include کرده. اول global.h
        خوب فعلا lcd.h رو کنار بذاریم. فایل هدر global.h خیلی مهم! تو هر پروژه ای که بخواید از avrlib استفاده کنید، حتما باید global.h رو هم از پوشه Avrlib\conf تو پوشه پروژه و در کنار فایل سی قرار بدین. از اونجایی که این فایل تو زیرشاخه conf قرار داره پس حتما حدس زدین که باید شامل تنظیمات سخت افزاری باشه. global.h رو از زیرشاخه conf کپی کنید تو پوشه پروژه و بازش کنید تا این فایل رو بررسی کنیم. همونطور که می بینید دو تا فایل دیگه رو include کرده. avrlibdefs.h و avrlibtypes.h . این دو فایل تو پوشه avrlib هستن. اینا رو هم کپی کنید کنار پروژه. از avrlibdefs.h قسمت قبل استفاده کردیم (sbi و cbi). می تونید بازش کنید و چندتا ماکرو دیگه که توش تعریف شده رو هم ببینین. از این ماکروها تو فایل های کتابخونه avrlib خیلی استفاده شده. cbi و sbi رو که گفتیم. outb و outw که بنظر میرسه تفاوتی با هم ندارن، دو تا پارامتر دارن که مقدار اولی رو برابر دومی قرار میدن. inb و inw هم که پارامتر خودشون رو بعنوان خروجی جایگذاری می کنن. هدف از تعریف این ماکروها این بوده که کدهاش خواناتر باشه. sei هم برای فعال کردن وقفه سراسری، و cli هم احتمالا برای غیرفعال کردن وقفه ها باشه. avrlibtypes.h رو باز کنید. تو این فایل با دستور typedef برای چندتا از نوع داده ها یه اسم دیگه مشخص کرده. مثلا u08 به معنای متغیر unsigned (بدون علامت) 8 بیتی به جای unsigned char. تو کتابخونه های avrlib از این اسم های خلاصه استفاده شده. اینا رو فقط بشناسین که بعدا که کدها رو خواستین بخونین سردرگم نشین. خوب برگردین به global.h . همونطور که می بینید تو فایل global.h باید فرکانس کلاک میکرو مشخص بشه. تمام کدهای avrlib براساس فرکانسی که اینجا تعریف شده عمل می کنند. این کد رو پیدا کنید:
        #define F_CPU 7372800

        این ماکرو یه ثایت تعریف میکنه که فرکانس کلاک رو بطور پیشفرض 7.37MHZ تعیین کرده. با توجه به تنظیمات میکرو این مقدار رو تغییر بدین (مثلا 1000000) و تغییرات رو ذخیره کنید. خوب پس تا اینجا خلاصه اینکه برای هر پروژه باید فایل های global و avrlibfes و avrlibtypes رو کپی کنید تو پوشه پروژه و فرکانس کلاک رو هم در فایل global معرفی کنید. کارایی که تاحالا کردیم ربطی به ال سی دی نداشت. برگردیم سراغ ال سی دی. خوب با توجه به توضیحاتی که تو lcd.h نوشته شده بود فهمیدیم که از این کتابخونه باید برای راه اندازی ال سی دی استفاده کنیم. پس فایل های lcd.h و lcd.c رو کپی کنید تو پوشه پروژه. lcd.h رو باز کنید. می بینید که بعد از include <global.h> یه فایل دیگه به نام lcdconf.h رو هم include کرده. از اسمش مشخصه که این فایل تو پوشه conf قرار داره و برای تنظیم سخت افزاری ال سی دی بکار میره. lcdconf.h رو از زیرشاخه avrlib\conf کپی کنید تو پوشه پروژه و بازش کنید. توضیحاتش رو خوب بخونید. گفته ال سی دی رو به دو روش می تونین راه اندازی کنین. روش اول MEMORY_INTERFACE که احتیاج به یه سخت افزار بین میکرو ال سی دی داره. از این روش استفاده نمی کنیم. روش دوم PORT_INTERFACE که ال سی دی مستقیم به میکرو وصل میشه. اینم خودش به دو روش دسترسی 8bit و 4bit انجام میشه. ما می خوایم از دومی که شامل 4 خط داده میشه استفاده کنیم. همونطور که گفته باید پین های داده 4تا7 استفاده بشه. در روش PORT_INTERFACE سه پایه کنترلی RS و RW و E هم داریم که باید اینا رو هم به میکرو وصل کنیم (پس درکل شد 7 پایه). همه اینا رو تو توضیحات lcdconf.h نوشته. حتما بخونیدش. خیلی جالبه ما رو با پشته صحنه خیلی کارها که تا حالا میکردیم آشنا میکنه! تازه این اولشه! کدها رو هم باید ببینین! در ادامه فایل lcdconf.h میرسیم به این قسمت :

        // Enable one of the following interfaces to your LCD
        //#define LCD_MEMORY_INTERFACE
        #define LCD_PORT_INTERFACE

        گفته یکی از روش ها رو انتخاب کنید. بطور پیشفرض define LCD_MEMORY_INTERFACE به حالت کامنت در اومده (حذف شده) و LCD_PORT_INTERFACE تعریف شده. ادامه کد :

        // Enter the parameters for your chosen interface'
        // if you chose the LCD_PORT_INTERFACE:
        #ifdef LCD_PORT_INTERFACE
        ...
        https://www.linkedin.com/in/mohammadhosseini69

        http://zakhar.blog.ir

        دیدگاه


          #5
          پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

          ifdef (یا همون if defined) یه دستور شرطی پیش پردازنده که بررسی می کنه آیا پارامتری که بعدش میاد تعریف شده یا نه.
          پس ifdef LCD_PORT_INTERFACE چک میکنه اگه ما حالت PORT_INTERFACE رو انتخاب کردیم (همون define بالا( دستورات درون بلوک شرط (برای تعیین پایه ها) رو اجرا می کنه. پس الان باید متوجه شده باشین که اون دو تا دستور define که بالاتر بود، چطوری در ادامه مشخص کردن که می خوایم از چه روشی استفاده کنیم. با این موارد زیاد مواجه میشیم.
          #ifndef LCD_CTRL_PORT
          // port and pins you will use for control lines
          #define LCD_CTRL_PORT PORTC
          #define LCD_CTRL_DDR DDRC
          #define LCD_CTRL_RS 2
          #define LCD_CTRL_RW 3
          #define LCD_CTRL_E 4
          #endif

          ifndef برخلاف ifdef عمل می کنه. همونطور که میبینید در صورتی که LCD_CTRL_PORT تعریف نشده باشه، دستورات درون بلوک شرط برای تعیین پایه های کنترلی اجرا میشن. در حالت پیشفرض از پایه های 2 و 3 و 4 پورت C استفاده شده. خیلی راحت میتونید هم پورت و هم شماره پایه های رو تغییر بدین و در این مورد کاملا آزادین. راستی در مورد پایه های ال سی دی هم بگم که اولی زمین، دومی 5 ولت، سومی تنظیم کنتراست، 3 تا پایه بعدی به ترتیب RS و RW و E ، و 8 تا بعدی پایه ورودی و خروجی داده های به ترتیب D0 تا D7. به ادامه کد توجه کنید:
          #ifndef LCD_DATA_POUT
          // port you will use for data lines
          #define LCD_DATA_POUT PORTA
          #define LCD_DATA_PIN PINA
          #define LCD_DATA_DDR DDRA
          // access mode you will use (default is 8bit unless 4bit is selected)
          //#define LCD_DATA_4BIT
          #endif

          این قسمت برای تعیین پورت داده میکرو که باید به ال سی دی وصل بشه. درحالت پیشفرض پورت A استفاده شده. توجه کنید که در ادامه گفته روش دسترسی 8 بیتی حالت پیشفرضه. یعنی 8 بیت پورت A استفاده میشه برای ارسال اطلاعات. برای اینکه اعلام کنیم دسترسی 4 بیتی می خوایم، باید //#define LCD_DATA_4BIT رو از حالت کامنت در بیاریم. داخل توابع فایل lcd.c بررسی شده که آیا این ثابت تعریف شده یا نه (با همون ifdef) که اطلاعات با 4 خط داده بفرسته یا 8خط. درصورتی که 4 بیتی رو انتخاب کنیم باید پایه های D4 تا D7 ال سی دی رو به پایه های 4تا7 پورت مورد نظر میکرو وصل کنیم و همچنین با تغییر در قسمت قبل، میتونیم پایه های کنترلی رو هم به همین پورت وصل کنیم (مثلا پایه های 0 تا 2) فقط دقت کنید که یکی از پایه های کنترلی پایه 4 که باید تغییر بدین...
          مثلا ببنید من کدها رو اینطوری تغییر دادم تا از پورت A استفاده کنم :
          #ifdef LCD_PORT_INTERFACE
          #ifndef LCD_CTRL_PORT
          // port and pins you will use for control lines
          #define LCD_CTRL_PORT PORTA
          #define LCD_CTRL_DDR DDRA
          #define LCD_CTRL_RS 0
          #define LCD_CTRL_RW 1
          #define LCD_CTRL_E 2
          #endif
          #ifndef LCD_DATA_POUT
          // port you will use for data lines
          #define LCD_DATA_POUT PORTA
          #define LCD_DATA_PIN PINA
          #define LCD_DATA_DDR DDRA
          // access mode you will use (default is 8bit unless 4bit is selected)
          #define LCD_DATA_4BIT //important !! <==
          #endif
          #endif

          در ادامه کدهای مربوط به MEMORY_INTERFACE که چون LCD_MEMORY_INTERFACE رو define نکردیم پس وارد بلوک شرطش نمیشه. فایل رو ذخیره کنید.
          برگردیم به فایل lcd.h، برین قسمت آخر کدهای فایل که پروتوتایپ (یا تعریف) توابعی که میتونیم برای ال س دی استفاده کنیم، نوشته شده. (همونجا که نوشته high level functions). توابع رو همراه با توضیح می بینید. lcdInit برای راه اندازی ال سی دی. lcdHome، lcdClear و lcdGotoXY هم که مشخصه... به این تابع دقت کنید :
          void lcdPrintData(char* data, u08 nBytes);

          برای نوشتن روی ال سی دی بکار میره. پارامتر اول رشته کاراکتری رو میگیره. و پارامتر دوم از نوع u08 (باید بدونین چیه! avrlibtypes ...) تعداد بایت(کاراکتر) از رشته که میخوایم نوشته بشه. کد این توابع تو فایل lcd.c نوشته شده. فایل lcd.c رو باز کنید. فایل هایی که include کرده رو ببنین:
          #include <avr/io.h>
          #include <avr/pgmspace.h>
          #include "global.h"
          #include "timer.h"
          #include "lcd.h"

          2 تا اولی که معلومه چون بین علامت <> هست مربوط به خود winavr میشه. ولی بعد از اونا هدر timer.h رو می بینید. خوب این فایل رو هم باید به پروژه اضافه کنید. وارد پوشه avrlib که بشین timer.h همراه با timer.c هست. این دو تا رو کپی کنید تو پوشه پروژه. در قسمت های بعدی این هدر رو هم بررسی می کنیم. فعلا کاری باهاش نداریم. فقط چون lcd.c به timer نیاز داره، این دو فایل رو کپی می کنیم کنار پروژه. البته یه نگاهی به این دو تا فایل هم میندازیم که ببنیم اینا فایلی include نکردن که ما تو پروژه نداریم...؟ تو فایل timer.c یه فایل دیگه هم include شده: rprintf.h ، بنابراین فایل های rprintf.h و rprintf.c رو هم از avrlib کپی می کنیم تو پوشه پروژه. با یه نگاهی به این دو تا فایل می بینیم که اینا دیگه چیزی کم ندارن.
          خوب بریم سراغ نوشتن کد برنامه. این کد رو بنویسین :
          #include <avr/io.h>
          #include "lcd.c"
          int main()
          {
          while(1){}
          }

          اگه یه مروری بکنید فایل اصلی که ما نیاز داریم lcd.c هست. و بقیه فایل ها یکی یکی درون lcd.c باز می شوند. که مهمترین اونا lcd.h و توی فایل lcd.h هم فایل lcdconf.h.
          https://www.linkedin.com/in/mohammadhosseini69

          http://zakhar.blog.ir

          دیدگاه


            #6
            پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

            پروژه رو کامپایل کنید. کامپایلر error میده ! :
            D:\projects\winavr\avrliblcd\default/../lcd.c:330: undefined reference to `delay_us'

            دوبار رو این خطا کلیک کنید تا نشونتون بده... یه قسمت از تابع lcdInit در فایل lcd.c یه باگ وجود داره! این دستور :
            delay(60000); // wait 60ms

            جلوش توضیح داده تاخیر 60 میلی ثانیه ای میخواد ایجاد کنه. تغییرش بدین به :
            _delay_ms(60);

            و فایل هدر مورد نیازش رو هم تو فایل اصلی پروژه include کنید. (می دونید فایلش چیه دیگه! قسمت اول آموزش گفتم...)راستی این تابع بازم باگ داره!! آخرش که از lcdLoadCustomChar استفاده کرده. اونایی که پارامترشون فرد، مشکل دارن و تو یه حلقه بی نهایت می افتن!! نمی دونم چرا!! این دیگه با شما. فعلا حذفشون می کنیم:
            // load the first 8 custom characters
            lcdLoadCustomChar((u08*)LcdCustomChar,0,0);
            //lcdLoadCustomChar((u08*)LcdCustomChar,1,1);
            lcdLoadCustomChar((u08*)LcdCustomChar,2,2);
            //lcdLoadCustomChar((u08*)LcdCustomChar,3,3);
            lcdLoadCustomChar((u08*)LcdCustomChar,4,4);
            //lcdLoadCustomChar((u08*)LcdCustomChar,5,5);
            lcdLoadCustomChar((u08*)LcdCustomChar,6,6);
            //lcdLoadCustomChar((u08*)LcdCustomChar,7,7);

            این کار باعث میشه تابع نمایش progressbar ال سی دی درست کار نکنه. فعلا مهم نیست. برنامه رو کامل می کنم:
            #include <avr/io.h>
            #include <util/delay.h>
            #include "lcd.c"
            char *str = "Test LCD!";
            int main()
            {
            lcdInit();
            lcdPrintData(str, 9);
            _delay_ms(2000);
            lcdGotoXY(0,1);
            lcdPrintData("avr gcc", 7);
            _delay_ms(5000);
            lcdClear();
            }


            -جالبه که سرعتش نسبت به کدویژن خیلی بیشتره!! حتما امتحان کنید. سرعت اجرای تابع init و همینطور نوشتن رو ال سی دی.
            -البته حجم کد نهایی یکم زیاد شده. این مربوط میشه به موضوع optimization که قبلا تو فروم بحث شده و منم فعلا در این مورد زیاد وارد نیستم.
            -روش هایی هست که کاری کنیم احتیاج به کپی کردن کتابخونه ها تو پوشه پروژه نباشه (البته غیر از فایل های conf). تعریف environment variable که من فقط تونستم تو خط فرمان و کامپایل با خط فرمان ازش جواب بگیرم. ولی تو avrstudio و pn نه!!
            -پیشنهاد می کنم کد توابع تو فایل lcd.c رو یه نگاهی بندازین. اینکه چطوری با ال سی دی حرف زده!!
            با یکمی جست و جو تو اینترنت میتونید کتابخونه های قوی تری برای ال سی دی پیدا کنید. مثل lcd_4bit . الان دیگه هر کتابخونه ای بگیرید می تونید خودتون باهاش کار کنید. در همه موارد همینطوره و کتابخونه های متنوعی وجود داره. دقت کنید که خیلی هاشون برای کار در کنار avrlib نوشته شدن...
            من سعی کردم هرچی که فکر میکنم مفیده رو بگم. نمیدونم تونستم خوب مطالب رو برسونم یا نه. اگه سوال یا اشکالی بود بپرسین.
            با ابن مطالب دیگه باید راه افتاده باشین و خودتون هرکاری خواستین با winavr بکنین. بهرحال من قسمت بعدی کتابخونه a2d رو معرفی می کنم. (البته نه دیگه با این جزئیات).
            موفق باشین
            https://www.linkedin.com/in/mohammadhosseini69

            http://zakhar.blog.ir

            دیدگاه


              #7
              پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

              جالبه که سرعتش نسبت به کدویژن خیلی بیشتره!! حتما امتحان کنید. سرعت اجرای تابع init و همینطور نوشتن رو ال سی دی.
              -البته حجم کد نهایی یکم زیاد شده. این مربوط میشه به موضوع optimization که قبلا تو فروم بحث شده و منم فعلا در این مورد زیاد وارد نیستم.
              اگه حجم کد زیاد شده و می خواید اونو کم کنید,Make File رو با Note Pad یا Pn باز کنید و OPT رو پیدا کنید و مقدارشو مساوی s بزارید )OPT=s).با این کار کد شما برای کم شدن حجم بهینه میشه.اگه هم کلا با بهینه سازی حال نکردید به جای s عدد 0 رو بزارید تا بهینه ساز غیرفعال شه.
              [img width=477 height=100]http://www.eca.ir/pic/upload/agazade.png[/img]

              دیدگاه


                #8
                پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                سلام
                قسمت سوم
                یادم رفته بود بگم، ال سی دی که در قسمت قبل با Avrlib کدش رو نوشتیم، تو پروتیوس شبیه سازی نمیشه!! شایدم پروتیوس من خرابه! بهرحال درعمل که جواب میده.
                در این قسمت می خوایم ببینیم چطوری میشه از امکانات ویژه میکرو استفاده کرد. برای نمونه مبدل آنالوگ به دیجیتال (adc یا a2d) میکرو راه اندازی می کنیم. در مورد بسیاری از امکانات دیگه میکرو، مثل وقفه خارجی، تایمر، ارتباط سریال و در کل بسیاری از کتابخونه های winavr ، عملکرد مشابهی خواهیم داشت. کمی هم به وقفه ها می پردازیم.
                یه نگاهی که به avrlib بندازین، همون اولش دو تا فایل a2d.h و a2d.c رو می بینید. a2d.h رو باز کنید. همونطور که میبینین توضیح داده که برای مبدل دیجیتال به آنالوگ استفاده میشه. در قسمت اول این فایل تمام تنظیمات نرم افزاری مورد نیاز برای راه اندازی adc آورده شده (یه چیزی شبیه کدویزارد در کدویژن).
                به کد قسمت اول کد توجه کنید.
                // A2D clock prescaler select
                // *selects how much the CPU clock frequency is divided
                // to create the A2D clock frequency
                // *lower division ratios make conversion go faster
                // *higher division ratios make conversions more accurate
                #define ADC_PRESCALE_DIV2 0x00 ///< 0x01,0x00 -> CPU clk/2
                #define ADC_PRESCALE_DIV4 0x02 ///< 0x02 -> CPU clk/4
                #define ADC_PRESCALE_DIV8 0x03 ///< 0x03 -> CPU clk/8
                #define ADC_PRESCALE_DIV16 0x04 ///< 0x04 -> CPU clk/16
                #define ADC_PRESCALE_DIV32 0x05 ///< 0x05 -> CPU clk/32
                #define ADC_PRESCALE_DIV64 0x06 ///< 0x06 -> CPU clk/64
                #define ADC_PRESCALE_DIV128 0x07 ///< 0x07 -> CPU clk/128
                // default value
                #define ADC_PRESCALE ADC_PRESCALE_DIV64
                // do not change the mask value
                #define ADC_PRESCALE_MASK 0x07

                حتما متوجه شدین که این قسمت کد، همه حالت های مختلف انتخاب فرکانس a2d (بصورت تقسیمی از کلاک) رو تعریف کرده. مثلا ADC_PRESCALE_DIV64 فرکانس کلاک رو به 64 قسمت تقسیم میکنه و با توجه به کد حالت پیش فرض (ADC_PRESCALE) هم برابر همین مقدار است. اگه خواستین تغییرش بدین. هرچند مهم نیست. چون تابعی برای این انتخاب وجود داره. راستی یادتون نرفته که فایل های a2d.c و a2d.h رو کپی کنید تو پوشه پروژه!؟ (همینطور global و avrlibdefs و ...). همینطور که می بینید، تذکر داده که اگه از تقسیم کوچکتری (فرکانس بیشتر) استفاده کنید عمل تبدیل سریعتر انجام میشه، اما تقسیم بزرگتر (فرکانس کمتر) دقت بیشتری داره.
                قسمت بعدی انتخاب ولتاژ مرجع
                // A2D voltage reference select
                // *this determines what is used as the
                // full-scale voltage point for A2D conversions
                #define ADC_REFERENCE_AREF 0x00 ///< 0x00 -> AREF pin, internal VREF turned off
                #define ADC_REFERENCE_AVCC 0x01 ///< 0x01 -> AVCC pin, internal VREF turned off
                #define ADC_REFERENCE_RSVD 0x02 ///< 0x02 -> Reserved
                #define ADC_REFERENCE_256V 0x03 ///< 0x03 -> Internal 2.56V VREF
                // default value
                #define ADC_REFERENCE ADC_REFERENCE_AVCC
                // do not change the mask value
                #define ADC_REFERENCE_MASK 0xC0

                حالت های مختلف ولتاژ مرجع رو می بینید. توضیحات خط به خط کاملا واضح هست. حالت پیش فرض هم که avcc انتخاب شده.
                قسمت بعد هم کانال های ورودی adc رو تعریف کرده.
                // channel defines (for reference and use in code)
                // these channels supported by all AVRs with A2D
                #define ADC_CH_ADC0 0x00
                #define ADC_CH_ADC1 0x01
                #define ADC_CH_ADC2 0x02
                #define ADC_CH_ADC3 0x03
                #define ADC_CH_ADC4 0x04
                #define ADC_CH_ADC5 0x05
                #define ADC_CH_ADC6 0x06
                #define ADC_CH_ADC7 0x07
                …

                تعریف هایی که دیدین، در توابع مربوط به adc بکار گرفته میشه. بریم به سراغ توابع adc . قسمت آخر فایل adc.h : // function prototypes

                void a2dInit(void);

                این تابع، ADC را فعال میکنه و تنظیماتش رو انجام میده.
                void a2dSetPrescaler(unsigned char prescale);

                این تابع نسبت تقسیم فرکانس کلاک برای adc رو مقداردهی می کنه. پارامتر ورودیش هم یکی از همون ثابت هایی هست که تعریف شده بود. مثل ADC_PRESCALE_DIV32 . می بینید که در توضیحات بالای این تابع گفته که از طریق تابع a2dInit فراخوانی میشه (با پارامتر ورودی مقدار پیش فرض تقسیم).
                void a2dSetReference(unsigned char ref);

                تابع انتخاب ولتاژ مرجع. پارامتر ورودی باید یکی از ثابت های معرفی شده باشه. این تابع هم از طریق تابع a2dInit با پارامتر ورودی مرجع پیش فرض فراخوانی میشه.
                void a2dSetChannel(unsigned char ch);

                انتخاب کانال ورودی ADC
                void a2dStartConvert(void);

                شروع عملیات تبدیل
                unsigned short a2dConvert10bit(unsigned char ch);

                ورودی این تابع یکی از کانال های ورودی adc است و خروجی آن، تبدیل آنالوگ به دیجیتال اون کانال، با 10 بیت است (حداکثر 1023). a2dConvert8bit هم عملکرد مشابه دارد با این تفاوت که خروجی اون 8 بیتی (حداکثر 255) است. بنظر میرسه این تابع همه کار میکنه و نیاز به a2dSetChannel و a2dStartConvert نیست! پس کاربرد اونا چیه؟ فعلا بریم a2d.c رو ببینیم. این فایل رو باز کنید. کد تابع a2dInit رو ببینید. همونطور که می بینین، در این تابع تنظیم فرکانس و ولتاژ مرجع با مقدار پیشفرض انجام شده :
                a2dSetPrescaler(ADC_PRESCALE); // set default prescaler
                a2dSetReference(ADC_REFERENCE); // set default reference

                اگر می خواین تنظیمات رو تغییر بدین، باید این کار رو با توابعی که معرفی شد، و البته بعد از فراخوانی a2dInit() انجام بدین.
                کد تابع a2dConvert10bit رو ببینین. این تابع شامل کدهای a2dSetChannel و a2dStartConvert هم میشه و در صورت فراخوانی a2dConver10bit، نیازی به استفاده از دو تابع دیگر نیست. a2dConvert8bit فقط 2 بیت کم ارزش از اون 10 بیت رو حذف میکنه.
                https://www.linkedin.com/in/mohammadhosseini69

                http://zakhar.blog.ir

                دیدگاه


                  #9
                  پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                  یه برنامه با adc ینویسیم :
                  #include <avr/io.h>
                  #include <util/delay.h>
                  #include <stdlib.h>
                  #include <string.h>
                  #include "lcd.c"
                  #include "a2d.c"
                  int num;
                  char str[10];
                  int main()
                  {
                  cbi(DDRA, 3); //input
                  cbi(PORTA, 3); //disable pullup
                  lcdInit();
                  a2dInit();
                  a2dSetPrescaler(ADC_PRESCALE_DIV8);
                  a2dSetReference(ADC_REFERENCE_AREF);
                  while(1)
                  {
                  lcdClear();
                  num = a2dConvert8bit(ADC_CH_ADC3); //num = a2dConvert8bit(3);
                  itoa(num, str, 10);
                  lcdPrintData(str,strlen(str));
                  _delay_ms(100);
                  }
                  return 0;
                  }

                  در این برنامه (برای atmega16) از کانال 3 adc برای تبدیل آنالوگ به دیجیتال استفاده شده. بنابراین این پایه رو ورودی و مقاومت پول پ رو هم غیر فعال می کنیم. برای پایه های کنترلی ال سی دی از پایه های 0 و 1 و2 پورت A استفاده شده و چهار پایه 4تا7 پورتA هم خطوط داده ال سی دی رو تشکیل میدن. دیگه معلومه برنامه چیکار میکنه. هر 100 میلی ثانیه ورودی کانال 3 رو می خونه و رو ال سی دی نمایش میده. برای اونایی که c کار نکردن بگم که تابع itoa از هدر stdlib.h (از winavr) یه عدد integer رو به رشته تبدیل میکنه. برای این هدر winavr یه پارامتر سوم هم میخواد که انگار مبنا هست! باید 10 باشه. strlen هم که تو هدر string.h هست، یه رشته میگیره و طول تعداد کاراکترهای اون رو برمیگردونه. بد نیست این دو تا هدر که الان گفتم رو باز کنید و توابع شون رو ببینید.
                  در ادامه میخوایم از وقفه adc استفاده کنیم.
                  دیدیم که a2dConvert8bit همه کارها رو میکنه. یعنی کانال ورودی رو میگیره. عمل تبدیل رو شروع میکنه. منتظر میمونه تا تبدیل انجام بشه. بعد نتیجه رو خروجی میده. اما اگه بخوایم از وقفه استفاده کنیم چی؟
                  a2d.c زو باز کنید و includeها رو ببینین. یه فایل از winavr به نام interrupt.h رو include کرده. دو تابع مهم این فایل sei و cei هست که اولی وقفه ها رو فعال و دومی غیرفعال میکنه. آخرین خط تابع a2dInit رو ببینید که با تابع sei() وقفه سراسری فعال شده.
                  میدونیم که توابع وقفه خروجی ندارن (حتی بصورت void) و ورودیشون هم یه بردار وقفه خاص هست. تابع وقفه مربوط به a2d رو می تونین آخر همین فایل a2d.c پیدا کنید.
                  //! Interrupt handler for ADC complete interrupt.
                  SIGNAL(SIG_ADC)
                  {
                  // set the a2d conversion flag to indicate "complete"
                  a2dCompleteFlag = TRUE;
                  }

                  SIG_ADC بردار وقفه adc است. یه توضیحی بدم که از کجا اومده. گفته بودیم ما تو هر برنامه هدر io.h رو include می کنیم. این هدر هم با توجه به mfile تشخیص میده که از چه آی سی میخوایم استفاده کنیم و هدر مربوط به اون رو فراخوانی میکنه. (مثلا iom16.h برای atmega16). و تو هدر مربوط به اون آی سی خاص، این بردارهای وقفه و رجیسترها و آدرس ها و... وجود داره.
                  اطلاعات ما از adc اینه که بعد از فعال کردن بیت مربوط به شروع عملیات تبدیل (تابع a2dStartConvert رو ببینین) ، مقدار آنالوگ به دیجیتال تبدیل میشه و در پایان تبدیل، وقفه فعال میشه. فعلا تو این وقفه فقط یه خط کد هست که پایان عملیات تبدیل رو اعلام میکنه. تغییرش میدیم :
                  SIGNAL(SIG_ADC)
                  {
                  // set the a2d conversion flag to indicate "complete"
                  num = inb(ADCL) | (inb(ADCH)<<8);
                  num = num>>2;
                  a2dCompleteFlag = TRUE;
                  a2dStartConvert();
                  }

                  خط اول مقدار تبدیل یافته رو از رجیسترها می خونه و تو متغیر num مینویسه (رجوع کنید به خط آخر تابع a2dStartConvert10bit). در خط یعد، 2 بیت شیفت دادیم به راست که 2 بیت کم ارزش حذف بشه (میخوایم 8 بیت بشه). خط آخر هم دوباره عملیات تبدیل رو شروع میکنه. منظور اینه که عملیات تبدیل بدون دخالت ما توسط وقفه انجام میشه. یعنی اگه یه برنامه پیچیده ای داشتیم، adc رو میدیم به وقفه خودش و هرموقع که خواستیم متغیر num رو میخونیم. ضمنا متغیر num رو هم باید تو همون فایل lcd.c تعریف کنید (بعد از includeها).
                  کد برنامه مون اینطوری میشه :
                  #include <avr/io.h>
                  #include <util/delay.h>
                  #include <stdlib.h>
                  #include <string.h>
                  #include "lcd.c"
                  #include "a2d.c"

                  char str[10];

                  int main()
                  {
                  cbi(DDRA, 3);
                  cbi(PORTA, 3);
                  lcdInit();
                  a2dInit();
                  a2dSetPrescaler(ADC_PRESCALE_DIV8);
                  a2dSetReference(ADC_REFERENCE_AREF);
                  a2dSetChannel(ADC_CH_ADC3);
                  a2dStartConvert();
                  while(1)
                  {
                  lcdClear();
                  itoa(num, str, 10);
                  lcdPrintData(str,strlen(str));
                  _delay_ms(100);
                  }
                  return 0;
                  }

                  همینطور که می بینید باید از دو تابع a2dSetChannel و a2dStartConvert استفاده کنیم و اینا رو بی دلیل تعریف نکرده.
                  رسیدیم به پایان قسمت سوم. امیدوارم مفید بوده باشه. موفق باشید. منم دعا کنید ...
                  https://www.linkedin.com/in/mohammadhosseini69

                  http://zakhar.blog.ir

                  دیدگاه


                    #10
                    پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                    رفقا نظرتون درمورد تاپیک چیه؟؟ 2 روز پست نذاشتم تاپیک رفت!! سوالی حرفی بحثی چیزی ندارین؟! ادامه بدم؟ البته اصل مطالب رو گفتم...
                    https://www.linkedin.com/in/mohammadhosseini69

                    http://zakhar.blog.ir

                    دیدگاه


                      #11
                      پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                      ما سخت مشتاقیم
                      برا ادامه
                      مطالب جالبی هست
                      اگه کامنت نمی دیم برا اینکه تاپیک رو شلوغ نکنیم

                      یه پیشنهاد: اگه مطالب جدید رو تو صفحه اول بزاری کمو زیادی ها انجام شه PDF رو تو پست اول بزنی مطالب جدید رو جایگزین صفحه اول کنی
                      هرچه سعی کنیم لایه های نرم افزاری زیاد کرده و از سخت افزار دور کنیم مشکلات(باگ ها) کمتر خواهد شد(امنیت بیشتری خواهید داشت)
                      بهترین جواب دهنده برای سوال شما، خود شما هستید البته بعد تلاش،پشتکار و مطالعه بیشتر
                      میدونی مشکل ما کجاست؟سرمایه و مغز ها رو نمیتونیم یکجا جمع کنیم...

                      تعدادی ماژول GPS GP5MX1513F1 با بالاتریت حساسیت -170db به قیمت خرید بفروش میرسد

                      دیدگاه


                        #12
                        پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                        سلام
                        من انتظار داشتم تو تاپیک شرکت کنید و بحث راه بندازین. اما فعلا که ...
                        این قسمت هدر timer.h رو بررسی می کنیم. البته همونطور که گفتم، مطالب اصلی که بلد بودم و بنظر مهم بود رو مطرح کردم. از اینجا به بعد من چیز زیادی بلد نیستم. فقط میتونم چندتا هدر رو معرفی کنم که البته با توجه به مطالب قسمت های قبلی خودتون خیلی راحت میتونین راه اندازیشون کنید. بهرحال...
                        timer.h رو باز کنید. و برید به این قسمت :

                        // Timer/clock prescaler values and timer overflow rates
                        ...

                        یه سری توضیحات از منابع فرکانسی تایمر رو آورده. برای هر تایمر 8 منبع فرکانس داریم که اینجا برای چند نمونه مقدارشون رو حساب کرده (جلوتر تعریف این منابع آورده شده). مثلا برای کریستال 8 مگاهرتز:

                        // for 8MHz crystal
                        // 0 = STOP (Timer not counting)
                        // 1 = CLOCK tics= 8MHz 8bitoverflow= 31250Hz 16bit= 122.070Hz
                        // 2 = CLOCK/8 tics= 1MHz 8bitoverflow= 3906.25Hz 16bit= 15.259Hz
                        // 3 = CLOCK/64 tics= 125kHz 8bitoverflow= 488.28Hz 16bit= 1.907Hz
                        // 4 = CLOCK/256 tics= 31250Hz 8bitoverflow= 122.07Hz 16bit= 0.477Hz
                        // 5 = CLOCK/1024 tics= 7812.5Hz 8bitoverflow= 30.52Hz 16bit= 0.119Hz
                        // 6 = External Clock on T(x) pin (falling edge)
                        // 7 = External Clock on T(x) pin (rising edge)

                        اولی که تایمر نمیشمره. دومی تایمر با فرکانس کلاک میکرو شمارش میکنه و سومی با یک هشتم فرکانس کلاک میکرو و ... ببینیم چه اطلاعاتی در مورد هر کدوم داده. برای مثال :

                        // 2 = CLOCK/8 tics= 1MHz 8bitoverflow= 3906.25Hz 16bit= 15.259Hz

                        فرکانس شمارش یک هشتم کلاک میکرو، که با فرض 8MHz بودن کلاک، این فرکانس 1 مگاهرتز میشه. دو مقدار بعدی، فرکانس overflow شدن تایمر است. اولی برای تایمر 8 بیتی و دومی 16 بیتی. مثلا اگه تایمر 8 بیتی باشه، 0 تا 255 میشمره. و به ازای هر 256 شمارش یک overflow داریم. پس این فرکانس میشه یک 1MHz/256 که برابر با 3906.25Hz
                        دو تا آخری هم از پایه T(x) رو لبه پاببن رونده یا بالا رونده فرکانس شمارش رو میگیره.
                        بریم پایین تر که این حالت ها رو تعریف کرده :

                        #define TIMER_CLK_STOP 0x00 ///< Timer Stopped
                        #define TIMER_CLK_DIV1 0x01 ///< Timer clocked at F_CPU
                        #define TIMER_CLK_DIV8 0x02 ///< Timer clocked at F_CPU/8
                        #define TIMER_CLK_DIV64 0x03 ///< Timer clocked at F_CPU/64
                        #define TIMER_CLK_DIV256 0x04 ///< Timer clocked at F_CPU/256
                        #define TIMER_CLK_DIV1024 0x05 ///< Timer clocked at F_CPU/1024
                        #define TIMER_CLK_T_FALL 0x06 ///< Timer clocked at T falling edge
                        #define TIMER_CLK_T_RISE 0x07 ///< Timer clocked at T rising edge
                        #define TIMER_PRESCALE_MASK 0x07 ///< Timer Prescaler Bit-Mask

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

                        #define TIMERRTC_CLK_STOP 0x00 ///< RTC Timer Stopped
                        #define TIMERRTC_CLK_DIV1 0x01 ///< RTC Timer clocked at F_CPU
                        #define TIMERRTC_CLK_DIV8 0x02 ///< RTC Timer clocked at F_CPU/8
                        #define TIMERRTC_CLK_DIV32 0x03 ///< RTC Timer clocked at F_CPU/32
                        ...

                        ادامه کد :

                        #define TIMER0PRESCALE TIMER_CLK_DIV8 ///< timer 0 prescaler default
                        #define TIMER1PRESCALE TIMER_CLK_DIV64 ///< timer 1 prescaler default
                        #define TIMER2PRESCALE TIMERRTC_CLK_DIV64 ///< timer 2 prescaler default

                        تو این قسمت مقدار پیشفرض تقسیم فرکانسی تایمر تعیین شده که با صدا کردن تابع فعال سازی تایمر، این مقادیر به عنوان منبع فرکانس تایمر تنظیم میشه. اگه خواستین میتونین تغییرش بدین (با همون ثابت هایی که بالا تعریف شده). البته نیازی نیست. چون تایع مخصوص این کار هم نوشته شده.
                        ادامه کد :

                        // interrupt macros for attaching user functions to timer interrupts
                        // use these with timerAttach( intNum, function )
                        #define TIMER0OVERFLOW_INT 0
                        #define TIMER1OVERFLOW_INT 1
                        #define TIMER1OUTCOMPAREA_INT 2
                        #define TIMER1OUTCOMPAREB_INT 3
                        #define TIMER1INPUTCAPTURE_INT 4
                        #define TIMER2OVERFLOW_INT 5
                        #define TIMER2OUTCOMPARE_INT 6
                        #ifdef OCR0 // for processors that support output compare on Timer0
                        #define TIMER0OUTCOMPARE_INT 7
                        #define TIMER_NUM_INTERRUPTS 8
                        #else
                        #define TIMER_NUM_INTERRUPTS 7
                        #endif

                        برای هرکدوم از وقفه های تایمرها یه ثابت تعریف شده. مثلا TIMER1OVERFLOW_INT برای وقفه سریز تایمر صفر. جلوتر کاربردشون رو می بینیم.
                        بریم سراغ توابع... مهم ها رو می گم.
                        توابع فعال سازی و مقداردهی اولیه تایمرها. مثل این تابع برای تایمر صفر :
                        void timer0Init(void); ///< initialize timer0
                        یادتون هست که این تابع منبع فرکانس تایمر رو برابر مقدار پیش فرضی که تعیین کرده بودیم قرار میده. برای منبع فرکانسی از توابعی که در ادامه اومده استفاده می کنیم :

                        void timer0SetPrescaler(u08 prescale); ///< set timer0 prescaler

                        مثلا اگه بخوایم از لبه پایین رونده منبع فرکانس خارجی رو پایه T0 استفاده کنیم، اینطوری می نویسیم : (با توجه به ثابت هایی که تعریف شده بود)

                        timer0SetPrescaler(TIMER_CLK_T_FALL);

                        در ادامه می رسیم به دو تابع مهم!

                        //! Attach a user function to a timer interrupt
                        void timerAttach(u08 interruptNum, void (*userFunc)(void) );
                        //! Detach a user function from a timer interrupt
                        void timerDetach(u08 interruptNum);

                        تابع timerAttach برای فعال کردن وقفه یه تایمر استفاده میشه. پارامتر اولش وقفه مورد نظر (همون وقفه هایی که بالاتر تعریف شد مثل TIMER0OVERFLOW_INT) و پارامتر دوم، یه تابع که به عنوان وقفه اجرا بشه. قضیه اینه که ما یه تابع تعریف می کنیم و اون رو به وقفه ای که می خوایم نسبت میدیم. مثلا کارمون رو راحت می کنه!
                        https://www.linkedin.com/in/mohammadhosseini69

                        http://zakhar.blog.ir

                        دیدگاه


                          #13
                          پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                          خوب فعلا یه برنامه بنویسیم :

                          #include <avr/io.h>
                          #include <util/delay.h>
                          #include <stdlib.h>
                          #include <string.h>
                          #include "lcd.c"
                          #include "timer.c"

                          char str[10];
                          int overflow_counter=0;
                          void timer_overflow(void);

                          int main()
                          {
                          sei();

                          DDRB = 0x00; //input T0
                          PORTB = 0xff;
                          timer0Init();
                          timer0SetPrescaler(TIMER_CLK_T_RISE);
                          timerAttach(TIMER0OVERFLOW_INT, timer_overflow);

                          TCNT0 = 0; //reset timer value
                          while(1)
                          {
                          _delay_ms(50);
                          lcdClear();
                          itoa(TCNT0, str, 10);
                          lcdPrintData(str, strlen(str));
                          lcdGotoXY(0,1);
                          itoa(overflow_counter, str, 10);
                          lcdPrintData(str, strlen(str));
                          }
                          return 0;
                          }

                          void timer_overflow(void)
                          {
                          overflow_counter++;
                          }

                          تابع timer_overflow رو تعریف کردم و نسبت دادم به سرریز تایمر صفر. تو این تابع یه متغیر که بیانگر تعداد سرریز تایمر هست یکی اضافه میشه. به نحوه استفاده از تابع timerAttach دقت کنید.
                          منبع فرکانس تایمر لبه بالا رونده پایه T0 انتخاب و این پایه هم ورودی شده. همونطور که می بینید تو خط اول ال سی دی مقدار شمارنده تایمر و خط دوم تعداد سرریز نوشته میشه.
                          یه نکته مهم دستور sei() برای فعال کردن وقفه سراسری که نباید فراموش کنید. تو فایل timer.h یه تابع به نام timerInit هست که همه تایمرها رو فعال میکنه. تو این تابع از sei استفاده شده. ولی تو توابع جدایی که برای فعال کردن تک تک تایمرها نوشته شده، این دستور جا مونده!
                          خوب برگردیم به timer.h
                          چندتا تابع دیگه داره. timer0GetOverflowCount تعداد سرریز تایمر صفر رو برمیگردونه. timer0ClearOverflowCount شمارنده تعداد سرریز رو صفر می کنه. این دو تابع برای تایمر 2 هم هست.
                          در ادامه یه سری تابع برای PWM تایمر 1 تعریف شده. بس که ساده هستن نمیشه چیزی درموردشون گفت. بنظرم یه برنامه کافی باشه:

                          int num;
                          char str[10];

                          int main()
                          {
                          sbi(DDRD, 5);
                          sbi(PORTD, 5);
                          timer1Init();
                          timer1SetPrescaler(TIMER_CLK_DIV8);
                          timer1PWMInit(8); // period/resolution to use for PWM output in timer bits //8=>maximum PWM duty cycle in 255 //10=>max … 1023
                          timer1PWMAOn();

                          timer1PWMASet(100);
                          _delay_ms(2000);
                          timer1PWMASet(255);
                          _delay_ms(2000);
                          return 0;

                          }
                          رسیدیم به پایان این قسمت. البته تایمر چیزی نیست که احتیاجی به کتابخونه داشته باشه. حتی کدویزارد کدویژن هم می تونه کمکمون کنه! ولی بهرحال برای آشنایی بیشتر با winavr اینم بررسی کردیم. مخصوصا اینکه چطوری تابع وقفه معرفی میشه.
                          اگه سوالی بود بپرسین.
                          میگم یکم دلسردم کردینا...!
                          یه روز پست ندم تاپیک میشه ستاره سهیل!! نمی دونم چیزایی که مینویسم بدرد می خوره و میتونه کمک کنه برین طرف winavr یا نه ... سعی خودمو می کنم...
                          موفق باشین.
                          https://www.linkedin.com/in/mohammadhosseini69

                          http://zakhar.blog.ir

                          دیدگاه


                            #14
                            پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                            سلام
                            خیلی ممنون از راهنمایی هاتون . اگه امکان داره درباره UART هم یه کمکی بکنید . من این کدهارو نوشتم ولی در دریافت اطلاعات مشکل دارم. من باید چند بایت رو پشت سر هم بفرستم و چند بایت ( که نمیدونم چند بایته ) رو پشت سر هم دریافت کنم . با دوتا lib تست کردم یکی تو فرستادن مشکل داشت و این هم توی دریافت :

                            کد:
                            #include <avr/io.h>
                            // include I/O definitions (port names, pin names, etc)
                            #include "global.h"
                            // include our global settings
                            #include "uart.h"
                            // include uart function library
                            #include <util/delay.h>
                            // include printf function library
                            
                            #ifndef F_CPU
                            #define F_CPU 12000000UL
                            #endif
                            
                            // Global Values
                            unsigned int in_data_len=0;
                            
                            //Send some Bytes To UART From an array with known length
                            void write_uart_bytes(char output_val[],unsigned int data_len)
                            {
                            		uartSendBuffer(output_val,data_len);	
                            }
                            
                            //Read some Bytes from UART and Save Them in array with Unknown length
                            void read_uart_bytes(char input_val[])
                            {
                            	unsigned long int j_count;
                            	in_data_len=0;	
                            	input_val[0]=0x00;
                            	input_val[1]=0x00;
                            	input_val[3]=0x00;
                            	
                            
                            	for(j_count=0;j_count<21000;j_count++)
                              {
                                /*
                                 * Get received character from ringbuffer
                                 * uart_getc() returns in the lower byte the received character and 
                                 * in the higher byte (bitmask) the last receive error
                                 * UART_NO_DATA is returned when no data is available.
                                 *
                                 */
                            		
                            		input_val[in_data_len]=uartGetByte();
                                if ( input_val[0]==0x4D )
                                {
                                  j_count = 0;
                            			in_data_len++;
                                }
                            		if(input_val[0]==0x4D&&input_val[1]==0x58&&in_data_len>=(input_val[3]+5))
                            			//uartFlushReceiveBuffer();
                            			break;
                            		
                              }
                            			
                            }
                            
                            
                            void main()
                            {	
                            	char output_val[10],input_val[10];
                            // initialize our libraries
                            // initialize the UART (serial port)
                            uartInit();
                            // set the baud rate of the UART for our debug/reporting output
                            uartSetBaudRate(57600);
                            // initialize rprintf system
                            
                            	sei();	//enable all interrupts
                            
                            	// Set Output Datas In an array
                            	output_val[0] = 0x4D;
                            	output_val[1] = 0x58;
                            
                            	output_val[2] = 0x10;
                            
                            	output_val[3] = 0x03;
                            
                            	output_val[4] = 0x42;
                            
                            	output_val[5] = 0x00;
                            	output_val[6] = 0x00;
                            
                            	output_val[7] = 0xFA;
                            	
                            
                            	while(1)
                            	{
                            	
                            	write_uart_bytes(output_val,8); // Send datas with 8 bytes length 
                            	
                            	read_uart_bytes(input_val); // Read datas
                            		
                            	_delay_ms(1000);
                            
                            	{
                            {
                            اگه lib دیگه ای دارید ما رو هم به فیض برسونید
                            فرق ما با جهان غرب در اینه که اونا هیچی نمیدونن! و تحقیق میکنند ولی ما همه چیزو می دونیم! و تحقیق برای ما معنی نداره!!!!!!!!!!!! .(از یه آدم اینکاره)

                            دیدگاه


                              #15
                              پاسخ : آموزش winavr و avrlib - مهاجرت از codevision به winavr avr gcc

                              تو avrlib از تابع و هدر rprintf برای ارسال اطلاعات استفاده میشه (ارسال رشته ای. حتی برای ال سی دی هم می فرسته). تابع ارسال خود uart کامل نیست (تو فایلش به شکل کامنت نوشته شده). ضمنا برای دریافت هم برنظرم بهتره از وقفه دریافت استفاده کنی. آموزش بعدی رو در مورد سریال مینویسم. (البته شما که خودت استادی :redface: حالا من مینویسم به این امید که مشکلت حل بشه...)
                              https://www.linkedin.com/in/mohammadhosseini69

                              http://zakhar.blog.ir

                              دیدگاه

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