آذر میکرو ( دنیای میکروکنترلرها )

آذر میکرو ( دنیای میکروکنترلرها )

برنامه نویس میکروکنترل ها
آذر میکرو ( دنیای میکروکنترلرها )

آذر میکرو ( دنیای میکروکنترلرها )

برنامه نویس میکروکنترل ها

آموزش کار با ATMEL STUDIO و AVR-GCC

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

void (*function)(void) = 0x0000;

  

میکرو به اون آدرسی که نوشته شده پرش می کنه! مثلا با کد بالا میکرو تقریبا ریست میشه می تونید ادرس رو تغییر بدید و به بوت لودر پرش کنید و ...

اگه یه متغییری دارید که هم در توابع تغییر پیدا می کنن هم توی روتین وقفه باید اونو از نوع "volataile" تعریف کنید!

volataile int iranled=0xFFFF;

سعی کنید متغییر هایی که فقط توی یه تابع استفاده میشن رو محلی تعریف کنید تا حجم کمتری اشغال بشه اینجوری ممکن هست متغییر توی ریجیستر های عمومی یا استک قرار بگیره ولی در حالت عمومی یه آدرس بهش توی اسرم تعلق می گیره!

سعی کنید حلقه های خودتون رو به صورت کاهشی بنویسید!
چرا؟ چون در حالت افزایشی باید مدام با مقدار مشخص شده مقایسه بشه ولی در حالت کاهشی وقتی مقدار به صفر رسید فلگ Z فعال میشه!

بعضی وقت ها توی بررسی تساوی به جای استفاده از "==" از "=" استفاده می کنیم! تو هزار خط کد مشکل رو پیدا کن!

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

وقتی اشاره گر تعریف می کنید

int* p, q, r;

الان فقط اولی اشاره گر هست اگه خواستید همه اشاره گر باشن باید این جوری بنویسید!

int *p, *q, *r;

شروع آموزش ATMEL STUDIO با همون AVRSTUDIO
پیش نیاز
ATMEL STUDIO 6


لینک های مفید در زمینه آموزش AVR-GCC
-توضیح کتابخانه ها و توابع موجود

Extensions to the C Language Family-
USERMANUAL-
list of all documented functions, variables, defines, enums, and typedefs with links to the documentation-

اینم لینک کتابخونه کامل

چیزی که هست اینه که همه رو ATMELSTUDIO داره بیشتر مال WINAVR هست


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


اصولا تنظیم ریجیستر ها کار سختی هست هم اسمشون یادمون نمی مونه هم نحوه مقدار دهیش! برای همین من توصیه می کنم CODEVISION نسخه رایگانش رو بگیرید (رایگان برای این که 1- کار مارو راه میندازه 2- اون دنیا ...)
البته خیلی هم روش حساب نکنید بعضی مواقع سوتی میده (خودم سر سرعت دو سیمه یک هفته الاف شدم تا فهمیدم اشتباه مقدار دهی کرده!)

 

بعد از نصب برنامه و اجرای آن

New Project

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

این صفحه میاد

میکرو ی مورد نظر رو انتخاب کنید (mega16 ) 
در سمت چپ یکسری اطلاعات در موردش میاد حافظه و ...سخت افزار هایی که می تونن باهاش کار کنن ,     دیتاشیت و ...
OK
کلیک را کلیک کنید .....

اون قسمت سمت چپ برای شما جزو گروه راست هست که اگر میخواید درگ انجام بدید و بیاریدش اینجا اما این VA چی هست؟
این کد نویسی رو راحت می کنه یکی از قسمت هاش همین
outline هست که میبینید و همه ی توابع و متغییر ها و ... رو نشون میده تا سریع به اون جا برید!

به محیط خوش آمدید

VA:VAssistansX

کد نویسی را راحت می کنه یکی از قسمت هاش همین outline هست که میبینید و همه ی توابع و متغییر ها و ... رو نشون میده تا سریع به اون جا برید!
نشون دادن خطا ها و تکمیل کردن نوشته هم از کار هاش هست
برای تکمیل کردن یه کار جالبی هم میشه کرد مثلا دستور if رو بنویسید و بعدش TAB رو بزنید! آزمایش کردید منظورم رو متوجه می شید!

اما برای تنظیم کامپابلر برای پروژه خودتون در منوی پروژه به پروپرتیز برید و در این قسمت

می تونید تنظیمات رو انجام بدید شرح کامل در آینده!
اما حالا برای خالی از لطف نبودن این آموزش یه برنامه هم می نویسیم!

برای کسایی که C بلدن که توضیح نمیخواد!
با کلید F7 کامپایل میشه و اطلاعات کامپایلر نوشته میشه

اگه یه کمی بالا تر برید اطلاعات دیگه ای هم نوشته شده

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

یکی از اولین نیاز های برنامه نویسی با
C اشنایی با ریجیستر های مختلف هست و در صورت امکان آشنایی با دستورات اسمبلی.
هر دوتای این هارو در آخر دیتاشیت کامل میکرو ها به صورت خلاصه نوشته.
احتمالا همه ی شما کار دو ریجیستر DDRX و PORTX رو میدونید ولی در اکثر موارد به دسترسی بیتی به این ها نیاز داریم!
مثلا میخوایم بیت دوم از فلان ریجیستر رو یک کنیم یا مقدارش رو بخونیم.

اینجا کلا در مورد توابع بیتی توضیح میدم
.
این توابع در "AVR/SRF_Defs.h" هستند که به صورت پیش فرض فراخوانی میشند!
اولین تابع

bit_is_clear(sfr, bit)

این تابع در ابتدا آدرس ریجیستر را می خواهد (می توانید از نام آن ها هم استفاده کنید ) مثلا ( PORTC,TCNT0,0x33  ) و در قسمت دوم شماره بیت مورد نظر در صورت صفر بودن ان بیت مقداری نا صفر و در صورت یک بودن بیت مقدار صفر برگردانده میشود

تابع بعدی

bit_is_set(sfr, bit)

مانند بالایی با این تفاوت که در صورت صفر بودن بیت مقدار صفر و درصورت یک بودن آن مقداری ناصفر را باز میگرداند
-
تابع بعدی

loop_until_bit_is_set(sfr, bit)

مانند بالایی ها مقدار دهی می شود و تا یک شدن بیت مورد نظر منتظر می ماند.
-
تابع بعدی

loop_until_bit_is_clear(sfr, bit)

برعکس تابع قبلی منتظر صفر شدن بیت مورد نظر می ماند.

این تابع ارزش مکانی بیت در مبنی 10 را باز می گرداند.

_BV(bit)

در واقع 1 به اندازه ی مقدار داده شده به سمت چپ شیفت داده میشود.
-
اما ما برای یک یا صفر کردن ریجیستر ها نیز نیاز به توابعی داریم
برای این کار از این توابع نوشته شده (از یه بنده خدایی یاد گرفتم) استفاده کنید این هارو باید در پروژه خودتون بنویسید.

#define BIT(x) (1 << (x))
#define SETBITS(x,y) ((x) |= (y))
#define CLEARBITS(x,y) ((x) &= (~(y)))
#define SETBIT(x,y) SETBITS((x), (BIT((y))))
#define CLEARBIT(x,y) CLEARBITS((x), (BIT((y))))
#define BITSET(x,y) ((x) & (BIT(y)))
#define BITCLEAR(x,y) !BITSET((x), (y))
#define BITSSET(x,y) (((x) & (y)) == (y))
#define BITSCLEAR(x,y) (((x) & (y)) == 0)
#define BITVAL(x,y) (((x)>>(y)) & 1)

این توابع در اصل سه تابع هستند که بقیه از یکدیگر استفاده می کنند در واقع کار برخی از توابع قبلی رو انجام میدهند.

اولی BITVAL که مانند توابع قبلی اول ادرس و بعد شماره یه بیت را می گیرد و در صورت یک بودن بیت مقدار 1 و در صورت صفر بودن بیت 0 را باز میگرداند

 SETBIT
که مانند قبلی ها مقدار دهی میشود و بیت متناظر را یک می کند.
 CLEARBIT
نیز به هم چنین بیت متناظر را صفر می کند.

برای تغییر وضعیت یک بیت هم میتوانید این گونه عمل کنید

X^=_BV(Y)

که اگر می خواهید به صورت تابع استفاده کنید این کد رو اضافه کنید

#define TOG(x,y) (x)^_BV(y)

و مساوی پورت مورد نظر قرار دهید یعنی اینجوری استفاده کنید برای مثال

PORTC=TOG(PORTC,0)

برای پورت ها هم مقدار هایی شبیه PORTC,0 و PORTD,5 تعریف شده اند که اینها صرفا 1 که به اندازه ی عدد پورت شیفت داده شده هستند ،
-
برای ایجاد تاخیر دو کتابخانه جود دارد

میکرو یه دستوری به نام nop داره که توی C میتونید اینجوری استفاده ازش بکنید

asm("nop")

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

<util/delay.h>
<util/delay_basic.h>

این دقیقا چیز هایی هست که باید اینکلود کنید
اولین چیزی که باید انجام بدید تعیین سرعت Clock هست که اینجوری تعیین میشه

#define F_CPU 8000000UL

مثلا الان من کلاک 8 مگا رو تعریف کردم.
توجه کنید که قبل از فراخوانی کتابخانه این رو تعریف کرده باشید
اولیم کتابخانه

#define F_CPU 1000000UL  // 1 MHz
#include <util/delay.h>

توی این کتابخانه این دو تابع وجود دارد

void     _delay_ms (double __ms)

میزان تاخییر به میلی ثانیهمی باشد توجه کنید که یک متغییر رو نمیشه به این توابع ارسال کرد! ( باید عدد در Flash ذخیره شده باشه )
برای حفظ دقت باید اپتیمایز کردن کد رو فعال کنید
حداکثر تاخیر با دقیق حداکثر این است

262.14 ms / F_CPU in MHz

که اگه یه مقداری بیشتر به تابع بدید دقت به مرور کم میشود
و این حداکثر مقداری هست که میشه به تابع داد وبیشتر از این اورفیلو میشه

4294967.295 ms/ F_CPU

void     _delay_us (double __us)

این درحد نانو ثانیه تاخیر ایجاد می کنه
حداکثر عدد برای بیشترین دقت

void     _delay_us (double __us)

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

4294967.295 us/ F_CPU


کتاب خانه ی بعدی

#include <util/delay_basic.h>

کتابخانه ی قبلی هم از همین استفاده می کنه با این تفاوت که محاسبات رو خودش انجام میده

void _delay_loop_1     (     uint8_t      __count    )

ایجاد یک تاخیر با کانتر 8 بیتی
به تابع حداکثر 255 میشه ورودی داد
در فرکانس 1 مگ حداکثر 768 میکرو ثانیه میشه تاخیر ایجاد کرد

void _delay_loop_2     (     uint16_t      __count    )

ایجاد تاخیر با کانتر 16 بیتی
به تابع حداکثر 65535  میشه داد
در فرکانس 1 مگ حداکثر تاخیر 262 میلی ثانیه
-
پیشنهاد میکنم از همون کتابخونه ی قبلی استفاده کنید که این مشکلات رو نداشته باشید.


استفاده از  Watchdog


#include <avr/wdt.h>

این توابع داخلش هست که خوب نیازی به توضیح نداره

wdt_disable()
wdt_reset()
wdt_enable(value)

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

WDTO_15MS
WDTO_30MS
WDTO_60MS
WDTO_120MS
WDTO_250MS
WDTO_500MS
WDTO_1S
WDTO_2S
WDTO_4S
WDTO_8S

از اینکه میکرو یه شما اون مقدار خاص رو پشتیبانی میکنه اطمینان حاصل کیند

برای کار با
UART

سه راه دارید که ریجیستر هارو تنظیم کنید!
1-
استفاده از کتابخانه ی خود نرم افزار
2-
کدویزارد کرویژن
3-
فشار به مغز
دو روش دیگه با خودتون اولی رو من میگم
برای این کار از کتابخانه ی "setbaud" استفاده می کنیم
برای این کار این سه خط رو اضافه کنید

#define F_CPU 8000000
#define BAUD 9600
#include <util/setbaud.h>

طبق معمول نیاز به توضیحی که نداره!
فقط میزان خطا رو حواستون رو جمع کنید زیاد نشه!
بعد این قسمت رو اضافه کنید تا ریجیستر ها مقدار دهی بشن

UBRRH = UBRRH_VALUE;
UBRRL = UBRRL_VALUE;
#if USE_2X
      UCSRA |= (1 << U2X);
#else
      UCSRA &= ~(1 << U2X);
#endif

برای وقفه و اینا دیگه باید برید سراغ دیتاشیت یا کدویزارد!
اما با تنظیم این ها شما باید ریجیستر "UDR" رو مقدار دهی بکنید تا نوشته بشه یا بخونید! که این چیزی نیست که شما دنبالش باشید!
اصولا از دستور PRTINTF استفاده می کنیم اما این دستور هیچ چیزی رو ارسال نمی کنه! چرا؟ چون خروجی اون تعیین نشده و باید مشخص بکنید که خروجی اون باید به کجا بره برای انی کار از این کد استفاده می کنیم

extern FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE);
extern int uart_putchar(char c, FILE *stream);

و این یکی رو توی برنامه بنویسید که یک بار اجرا بشه

stdout = &mystdout;

حالا خروجی تابع printf به تابع زیر میره که باید به برنامه اضافش کنید!

extern int uart_putchar(char c, FILE *stream)
{
    if (c == '\n') uart_putchar('\r', stream);
    loop_until_bit_is_set(UCSRA, UDRE);
    UDR = c;
    return 0;
}

دقیقا منم نگرفتم که اون دو خط اولی چه کاری میکنن! به هر حال مارو به خواستمون میرسونن! شما میتونید برای کار های دیگه هم ازش استفاده کنید مثلا جای ارسال به UART توی یک رشته ذخیره کنید! کار جالبی میشه نه!
_

توابع مدیریت توان!


برای این کار این کتابخونه رو اضافه کنید!

#include <avr/sleep.h>

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

void     sleep_enable (void)
void     sleep_disable (void)
void     sleep_cpu (void)

ابتدا فعال میکنید بعد سی پی یو رو به اسلیپ می برید و بلافاصله بعد از بیدار شدن غیر فعال کنید تا دباره به خواب نرود!
البته اگر قبل از ان وقفه ای رو فعال نکرده باشید یا وقفه سراسری غیر فعال باشه اون وقت هست که دیگه میکرو به کما میره تا شما ریستش کنید

برخی میکرو ها این قابلیت رو دارد که قبل از به خواب رفتن brown down رو هم خاموش کنن برای این کار قبل از به خواب بردن میکرو این رو اضافه بکنید

sleep_bod_disable();

اما قبل از همه ی این ها نیاز هست مد خواب رو تعیین کنید: 1- زمستانی 2- عصرانه 3- اقما و ...
برای این کار قبل از فرا خوانی هر کدام از توابع بالا این کد رو اضافه کنید

set_sleep_mode(<mode>);

که به جای "<mode>" این ها رو می تونید بزارید

SLEEP_MODE_IDLE
SLEEP_MODE_PWR_DOWN
SLEEP_MODE_PWR_SAVE
SLEEP_MODE_ADC          
SLEEP_MODE_STANDBY      
SLEEP_MODE_EXT_STANDBY  
SLEEP_MODE_PWR_OFF

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

set_sleep_mode(SLEEP_MODE_ADC);
sleep_enable();
asm("sei");
sleep_cpu();
sleep_disable();

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


حافظه  EEPROM


حافظه EEPROM که در این پست قصد استفاده از آن را داریم نسبت به کدویژن دارای پیچیدگی بیشتری است!

ابتدا فایل مربوط به این حافظه را به برنامه اضافه می کنیم

#include <avr/eeprom.h>

یه ذره توابع موجود و اینا زیاد هست برای همین تو این لینک می تونید کامل ببینید ولی من اون هایی که نیاز دارم رو توضیح میدم.
اما قبل از هر چیز یه سری متغییر استفاده شده اند که لازم هست توضیح بدم اون هارو!
زیاد پیش میاد که توی برنامه هایی که می بینید از متغییر هایی از نوع "uint8_t" و اینا استفاده شده باشند!
این ها فرقی با قبلی ها ندارن ولی برای اینکه هر کامپایلری بتونه با برنامه کار کنه از این ها استفاده می کنن
توی این فایل که باید به برنامه اضافه کنید

#include <stdint.h>

لیست کاملی از این ها اضافه میشه و برای مثال توی C ما از "unsigned char" استفاده می کنیم پس "uint8_t" به اون ترجمه می شه ممکن هست فلان کامپایلر "un ch" باشه پس "uint8_t" به اون تبدیل میشه!
کلا این ها توی این فایل سرامد تعریف شدن!

Integer types having exactly the specified width
typedef signed char                            int8_t
typedef unsigned char                      uint8_t
typedef signed int                              int16_t
typedef unsigned int                          uint16_t
typedef signed long int                      int32_t
typedef unsigned long int                 uint32_t
typedef signed long long int             int64_t
typedef unsigned long long int         uint64_t

چیز های دیگه ای هم توی این فایل هست که فعلا زیاد کاری باهاشون نداریم ولی توی این لینک می تونید ببینید
اولین قدم می خوایم از آدرس 60 حافظه یه داده ای رو بخونیم!

# include <avr / eeprom .h>
void main ( void )
{
     uint8_t ByteOfData ;
     ByteOfData = eeprom_read_byte (( uint8_t *) 60) ;
}

برای خوندن دو بایت از حافظه یا به عبارتی متغییر int این گونه عمل می کنیم

uint16_t WordOfData ;
WordOfData = eeprom_read_word (( uint16_t *) 46) ;

این دستور از 46 و 47 داده رو می خونه و توی متغییر ذخیره می کنه
برای متغییر های بزرگ از

eeprom_read_dword()

و برای اعداد اعشاری از

eeprom_read_float()

استفاده می کنیم
برای نوشتن یک بایت در حافظه این گونه عمل می کنیم

uint8_t ByteOfData ;
ByteOfData = 0 x12 ;
eeprom_update_byte (( uint8_t *) 46, ByteOfData );

حالا متغییر ما رو توی قسمت 46 حافظه ذخیره کرد و همانطوری که انتظار دارید برای متغییر های 2 بایتی از

uint16_t WordOfData ;
WordOfData = 0 x1232 ;
eeprom_update_word (( uint16_t *) 46, WordOfData );

و دو باره برای متغییر های 4 بایتی از

eeprom_update_dword()

و اعشاری ها

eeprom_update_float()

حالا ما می تونیم دونه دونه بنویسیم یا بخونیم ولی نیاز هست گاهی که چند تا رو بخونیم مثلا ارایه ها یا یک رشته متنی که ذخیره شده!
برای این کار از این تابع استفاده می کنیم

void eeprom_read_block ( void * pointer_ram , const void * pointer_eeprom , size_t n)

شاید تعجب کنید که هیچ مقداری رو بر نمی گردونه !
این مقدار در متغییری که ادرسش رو به عنوان اولین ورودی به تابع دادید ذخیره میشه!
برای مثال ما از این برای خواندن 10 بایت پشت سر هم از ادرس 12 رو می بینیم

uint8_t StringOfData [10];
eeprom_read_block (( void *) StringOfData , ( const void *) 12, 10) ;

حالا شاید از من بپرسید این void این وسط چه فایده ای داره؟!
در جواب می گم در مثال های قبل با اندازه ما مثلا یک متغییر دو بایتی رو خوندیم که ممکن بود که علامت دار باشد یا نباشد و نوع آن را مشخص می کردیم در واقع برای تابع اهمیت داشت که چه مقداری رو می خواند!
اما گاهی ما نمی دانیم که داده ای که درحال خواندن آن هستیم چه نوع داده ای است! شاید هم اصلا مهم نباشد! برای همین از void استفاده می کنیم با این کار تابع فقط مغدار را می خواند و دیگر کاری به نوع داده ی خوانده شده (متن - اعشاری - صحیح - منفی و...) ندارد و عین آن را در خروجی ظاهر می کند در مثال بالا 10 بایت پشت سر هم خوانده شده و در 10 عضو ارایه نوشته می شود.
برای نوشتن در حافظه هم این گونه عمل میشود

uint8_t StringOfData [10] = " TEST ";
eeprom_update_block (( const void *) StringOfData , ( void *) 12, 10);

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

حال از خانه 12 به صورت متوالی برای 10 بایت در حافظه نوشته میشود که 5 تای آن مقداری که در متغییر ما موجود است نوشته میشود و بقیه مقدار 0 می گیرند

حالا ما کار با این حافظه رو یاد گرفتیم ولی حتما این چیزی نیست که ما بخوایم چون وقتی کمی تعداد متغییر ها زیاد میشن دیگه به خاطر سپردن آدرس ها کار ما نیست برای همین نیاز میشه که مثل بقیه متغییر ها ، متغییری برای حافظه تعیین بکنیم تا کار را برای ما ساده تر بکند!
برای این کار مانند تعریف متغییر ها عمل می کنیم با این تفاوت که "EEMEM" را به قبل آن ها اضافه می کنیم که به کامپایلر می گوید متغییر ها در حافظه EEPROM تعریف شوند
برای مثال

uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];

اما با این حال این به این معنی نیست که مانند متغییر های معمولی به طور مستقیم به آن ها دسترسی داشته باشیم

 برای مثال این کد کار نمی کند!

uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];
int main ( void )
{
    uint8_t SRAMchar ;
    SRAMchar = NonVolatileChar ;
}

این ها صرفا یک ادرس هستند برای کار با ان ها باید مثل قبل عمل کنید

# include <avr / eeprom .h>
uint8_t EEMEM NonVolatileChar ;
uint16_t EEMEM NonVolatileInt ;
uint8_t EEMEM NonVolatileString [10];
int main ( void )
{
    uint8_t SRAMchar ;
    uint16_t SRAMint ;
    uint8_t SRAMstring [10];
    SRAMchar = eeprom_read_byte (& NonVolatileChar );
    SRAMint = eeprom_read_word (& NonVolatileInt );
    eeprom_read_block (( void *) SRAMstring , ( const void *) NonVolatileString , 10);
}


کلا کامپایلر سه خروجی کامپایل شده به شما میده فایل هگز - فایل مربوط به EEPROM با نام EEP و ELF
در ELF هم اطلاعات مربوط به قسمت فلش هم EEPROM هم اطلاعات فیوزبیت ها ذخیره می شوند
اما در کل شاید ما بخواهیم مقدار پیش فرضی به متغییر خود بدهیم که در این صورت لازم است ما ان را پروگرام کنیم تا برای بار اول داخل EEPROM نوشته شود!
برای این کار کافی است که هنگام تعریف مقداری به آن نسبت دهیم!

uint8_t EEMEM SomeVariable = 12;

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


کار با وقفه ها


کار با وقفه هارو آموزش بدم برای این کار کتابخانه ی "AVR/INTERRUPT.H" رو به برنامه اضافه کنید
توجه کنید که تمامی ادرس های وقفه رو توی فایل اصلی بنویسید وگرنه خطا میده
اولین دستور ها

sei();
cli();

اولی وقفه ی سراسری رو فعال می کنه دومی غیر فعال
-
کلا برنامه ای که اجرا میشه توی یه قسمتی مثل زیر هست

ISR(ADC_vect){
//code here
}

مثلا این وقفه ی انالوگ به دبجیتال رو به این جا میاره
برای هر قسمتی اسم مخصوص به خودش رو داره این جا دنبالش بگردید
اگه یه وقفه ای رخ بده که هیچ چیزی براش تعریف نکرده باشید مثل بالا موجب میشه میکرو ریست بشه!
برای همین از این استفاده کنید که همه ی وقفه هایی که بردارشون مشخص نشده به اینجا برن

ISR(BADISR_vect)
{
    // user code here
}

اصولا وقتی وقفه رخ میده وقفه ی سراسری غیر فعال میشه و بعد فعال تا توی وقفه وقفه انجام نشه برای غیر فعال کردن این قابلیت این جوری استفاده کیند

ISR(XXX_vect, ISR_NOBLOCK)
{
  ...
}

اگه می خواید دو تا وقفه به یک جا برن اینجوری استفاده کنید

ISR(PCINT0_vect)
{
  ...
  // Code to handle the event.
}

ISR(PCINT1_vect, ISR_ALIASOF(PCINT0_vect));

الان دومی به همون اولی میره
اگه می خواید هیچ برنامه ای اجرا نشه اینو استفاده کنید

EMPTY_INTERRUPT(ADC_vect);

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

ISR(TIMER1_OVF_vect, ISR_NAKED)
{
  PORTB |= _BV(0);  // results in SBI which does not affect SREG
  reti();
}

اون reti اخر باید باشه چون باعث برگشت از وقفه میشه در ضمن بدونید که چون هیچ کد اضافی نمی شه ممکن هست داده های مربوط به برنامه که ذخیره شدن رو از بین ببرید پس اگه این کاره نیستید بی خیال شید
-
اینم باعث بازگشت از وقفه میشه به طور عادی نیازی به این نیست مگر جا هایی که گفتم

reti  ();

 

تغییر سطح پین ( فشرده شدن کلید )


 

سلام میتونید با استفاده از یک دستور if تغییر سطح پین رو تشخیص بدین به شکل زیر:

 

if(bit_is_set(PINB,1))_delay_us(1),function(); 

 

این برای حالتی بود که اگه پایه یک بشه فعال دستور مورد نظر انجام بشه ولی اگه بخواین صفر شدنش رو چک کنید به شکل زیر مینویسید:

 

if(bit_is_clear(PINB,1))_delay_us(1),function();  

 

به جای عبارت function() اسم تابعتون رو بنویسید که فراخوانی بشه .


وقتی که بخوایم زمانی رو با تایمر تولید کنیم از چه فرمول هایی باید استفاده کرد؟؟


باید با در نظر گرفتن فرکانس کریستال و همچنین مقدار تقسیم فرکانسی که برای تایمر انتخاب میکنی زمان مورد نظر رو دربیاری
به فرض مثال اگه بخوای زمان 1 ثانیه رو ایجاد کنی باید به تایمر هات نگاه کنی
میدونیم که تایمر0 هشت بیتی هستش و تایمر 1 شانزده بیتی
من به صورت پیشفرض با استفاده از تایمر یک اینو برای شما توضیح میدم
فرض کنید که فرکانس کریستال شما 8 مگاهرتز باشه و برای تایمر 1 هم تقسیم فرکانسی 256 رو در نظر گرفته باشید
پس:

8000000/256=31250  

خب در این حالت میدونیم که فرکانس کلاک وارد شده به تایمر 31250 بار در ثانیه اس
پس زمان هر پله بالا رفتن تایمر میشه: (تایمر درواقع همون کانتره)

1/31250=32us  

و چون میخوایم یک ثانیه بسازیم داریم:

1000000/32=31250  

پس عدد تایمر باید به 31250 برسه که بدونیم 1 ثانیه گذشته(با توجه به تنظیمات انجام شده)
اگه تایمر 1 رو تو مد ctc بذارین و عددش رو هم 31250 بذارین سر 1 ثانیه میتونید وقفه هم داشته باشید
امیدوارم جواب بقیه سوالهاتونم داده باشم
در مورد انتخاب تقسیم فرکانسی برای تایمر هم باید بگم که باید نگاه کنید که از بین اعداد پیشفرض کدوم عدد برای کار شما مناسبه این کار با تجربه براتون راحتتر میشه
برای ایجاد pwm هم دو مود داریم که مد fast pwm در حالت یک شیبه هستش و اون یکی دو شیبه ، تو fast pwm از روی شیب تایمر میتونید درصد دیوتی رو تغییر بدین و دو دوشیبه هم همینطور یعنی شما با تغییر ocrx میتونید تغییراتی در دیوتی سایکل ایجاد کنید


نظرات 6 + ارسال نظر
سسسسس سه‌شنبه 23 شهریور 1395 ساعت 16:59

محمدرضا یکشنبه 21 شهریور 1395 ساعت 10:54

باسلام و خسته نباشید
شروع خوب و پرباری بود ، ادامه بدید لطفاً
با سپاس فراوان

hg چهارشنبه 5 خرداد 1395 ساعت 22:43

سلام!
مطالبون عالیه!
من یه سری مشکل در مورد کار با avr gcc دارم ، مثلا میخوام یه برنامه ای تحت ویندوز بنویسم که طبق یه قواعدی یه سری کد به زبون سی تولید کنه!
خب حالا میخوام این کد ها را برای avr کامپایل کنم! بعد از سرچ کردن متوجه شدم که باید از avr gcc استفاده کنم که مجموعش را هم دانلود کردم ولی هیچ گونه اطلاعاتی در موردش ندارم!
ممنون میشم اگه راهنماییم کنید

محمد سه‌شنبه 21 اردیبهشت 1395 ساعت 18:05

سلام
ممنون از مطالب مفیدتون
من میخواستم بدونم برای پروگرم کردن میتونیم مستقیما از این برنامه استفاده کنیم؟

Carlos دوشنبه 2 فروردین 1395 ساعت 11:04

سلام و خسته نباشید
واقعا مطالب عالی بودن
ممنون

anahita سه‌شنبه 2 دی 1393 ساعت 15:56

سلام ممنون از اموزش خوبتون من نیاز به راهنمایی دارم ، مفهوم دستورهای زیر را متوجه نمی شم میشه بگید مفهومشون چیه و نمونه ی چه دستوری در codevision میشن
PORTB &= ~(1<<PORTB.1 یا
PORTB |= (1<<PWM_PIN1)

برای نمایش آواتار خود در این وبلاگ در سایت Gravatar.com ثبت نام کنید. (راهنما)
ایمیل شما بعد از ثبت نمایش داده نخواهد شد