ویرگول
ورودثبت نام
KAYT33N  | کیان
KAYT33N | کیانصبحا مهندس مکانیک، شبا برنامه نویس علاقه مند اوپن سورس و هر نوع ساختن https://github.com/KAYT33N
KAYT33N  | کیان
KAYT33N | کیان
خواندن ۷ دقیقه·۳ ماه پیش

قسمت ۲: چشمک زدن با میکروکنترلر

اگر از دنبال کنندگان این مجموعه مقالات برنامه نویسی برای میکروکنترلر ها با C باشید و طبق مقالات قبل، سیستم خود را آماده برنامه نویسی AVR و برد و مدار اتصالات بین میکروکنترلر و آردینو را برقرار کرده باشید، پس آماده اید که از برد آردینیوتون به عنوان ISP استفاده کنید و اولین برنامه خود را که به زبان C نوشته اید بر روی میکروکنترلر دلخواهتون آپلود کنید.

در این قسمت یاد میگیریم که چطوری کدی بنویسیم که باعث روشن و خاموش شدن یک LED کوچک بشود، این اتفاق شاید در نظر شما قدم بزرگی نیاد ولی میتونید به اون به چشم Hello World! در دنیای برنامه نویسی Embedded نگاه کنید که باعث میشه بتونید اولین کد خودتون رو بدون کمک آردینو بنویسید!

در این مقاله : یکم راجع به DDRXو PINX و PORTX توضیح میدیم، یک پروژه حداقلی رو با زبان C مینویسیم که یکی از پایه های میکروکنترلرمون رو خاموش و روشن بکنه، اونو با avr-gcc کامپایل و لینک میکنیم، با avr-objcopy ترجمش میکنیم و بعد از آماده کردن مدار، کد رو با avrdude آپلود میکنیم تا led بهمون چشمک بزنه.


پورت، پین و رجیستر ها

پین های ورودی و خروجی میکروکنترلرهای AVR مثل ATmega16 در دسته های ۸ تایی به نام های (A, B, C, و غیره) گروه بندی میشوند و وضعیت هر پین از طریق سه رجیستر ۸ بیتی تعیین میشود:

  • رجیستر DDRx: Data Diretion register :

وضعیت ورودی یا خروجی بودن پین را مشخص میکند. ۰ به معنی ورودی و ۱ به معنی خروجی میباشد؛ برای مثال، کد زیر تمام پین های گروه B را خروجی میکند.

DDRB = 0b11111111;
  • رجیستر PORTx: Port Register :

برای پین های خروجی، مقدار ۰ یا ۱ به معنی خاموش یا روشن کردن (high/low) پین است و در پین های ورودی، به معنی فعال یا غیر فعال کردن مقاومت داخلی pull-up برای پین مورد نظر است.

  • رجیستر PINx: Pin Register :

در پین های ورودی، وضعیت بالا یا پایین (high/low) بودن پین رو در خودش داره.

کار با رجسیترها

برای کار راحت تر با رجسیترها ترجیحا موارد زیر رو یاد بگیرید و یا دم دستتون داشته باشید که بتونید راحت ازشون استفاده بکنید.

اعداد در مبنای ۱۰ و ۲ و ۱۶
اعداد در مبنای ۱۰ و ۲ و ۱۶

در صورت امکان و برای جلوگیری از خستگی، ابتدا راجع به عملگرهای بیتی مثل AND, OR, XOR و استفاده از آنها در زبان C مطالعه کنید.

مقادیر hex مبنا ۱۶

مقدار مبنای ۱۶ 0xF0 با مقدار مبنای ۲ 0b11110000 برابر بوده و ۴ بیت با ارزش یک رجیستر ۸ بیتی را برابر با ۱ قرار میدهد؛ همچنین مقادیر 0x0F و 0xFF نیز به ترتیب برابر با مقادیر 0b00001111 و 0b11111111 میباشند. برای مثال برای روشن کردن ۴ پین کم ارزش گروه B میتوانید از کد زیر استفاده کنید :

// روشن کردن ۴ پین اول // و خاموش کردن ۴ پین دوم PORTB = 0x0F; // روشن کردن ۴ پین اول // بدون تغییر دادن وضعیت ۴ پین دوم PORTB |= 0x0F;

مقادیر باینری مبنا ۲

برای روشن کردن PB4 و دست نخوردن مقادیر بقیه پین ها :

PORTB |= (1 << PB4);

برای خاموش کردن کردن PB4 و دست نخوردن مقادیر بقیه پین ها :

PORTB &= ~(1 << PB4);

برای تغییر وضعیت پین PB4 به صورت toggle ( در صورت روشن بودن خاموش و در صورت خاموش بودن روشن شود) :

PORTB ^= (1 << PB4);

دقت کنید که با جایگزین کردن PORTB در تکه کدهای بالا با DDRB میتوانید وضعیت ورودی یا خروجی بودن پین PB4 رو تعیین کنید.

طرز تهیه LED چشمک زن

۱. نوشتن کد

ما از پایه شماره ۲۱ میکروکنترلر ATmega16 برای روشن و خاموش کردن led مورد نظرمون استفاده میکنیم، این پایه در گروه D قرار داره و شمارش داخل گروه PD7 هستش. این کدها را در فایل main.c قرار میدهیم.

// برای کار با رجیستر ها #include <avr/io.h> // برای ایجاد تاخیر بین روشن و خاموش شدن #include <util/delay.h> int main(void) { // تنظیم پایه به عنوان خروجی DDRD |= (1 << PD7); while (1) { // حلقه اصلی برنامه PORTD |= (1 << PD7); // روشن کردن لامپ _delay_ms(500); // تاخیر نیم ثانیه ای PORTD &= ~(1 << PD7); // خاموش کردن لامپ _delay_ms(500); // تاخیر نیم ثانیه ای } }

همچنین برای کوتاه تر کردن کد میتوانستید از کد زیر در حلقه اصلی استفاده کنید که مقدار پایه شماره ۷ از گروه D را در هر تکرار از ۰ به ۱ و بالعکس تغییر میدهد.

while (1) { // حلقه اصلی برنامه PORTD ^= (1 << PD7); // روشن و خاموش کردن لامپ _delay_ms(500); // تاخیر نیم ثانیه ای }

چند نکته درباره کد

  • دلیل وجود نداشتن مقدار بازگشتی return در تابع main این است که کد های embeded باید دائما تکرار بشوند و در یک حلقه بینهایت قرار بگیرند.

  • فرآیند زمان سنجی در توابعی مانند delay به وجود و درستی مقدار F_CPU وابسته میباشد که یا در هنگام کامپایل و یا در بدنه کد نیاز به تعریف شدن دارد که ما در هنگام کامپایل تعریف میکنیم.

  • در زبان C فقط تابع main وجود دارد و در حقیقت آردینو از کد زیر برای اجرای کد های شما استفاده میکند.

int main(void){ setup(); loop(); }
  • حجم این کد پس از کامپایل حدود ۲۰۰ بیت میشود که در برابر حجم کیلوبایتی آردینو بسیار ناچیز است.

۲. بستن مدار

خیلی پیچیدش نمیکنیم، کلا یک مقاومت نیاز دارید با یک LED، پایه مثبت LED رو به پایه PD7 میکروکنترلرتون ( برای atmega16 پایه شماره ۲۱ ) متصل کنید و پایه منفی رو هم با مقاومت به GND متصل کنید.

۳. کامپایل و لینک کردن و تهیه فایل hex

برای استفاده از avrdude فلش کردن کدی که در مرحله قبل نوشتیم، نیاز به یک فایل .hex داریم که شامل کد های کامپایل شده ما باشد؛ با ترمینال به محل پروژه بروید و به ترتیب فرمان های زیر را وارد کنید :

  • کامپایل C به .o

avr-gcc -Os -DF_CPU=1000000 -mmcu=m16 -c main.c -o firmware.o

مقدار DF_CPU برابر فرکانس کاری میکروکنترلر است که در قسمت قبلی مقاله با تغییر فیوزها تنظیم کردید.

مقدار mmcu برابر مدل میکروکنترلر است که ما از ATmega16 استفاده میکنیم.

  • لینک کردن و تهیه فایل .elf

avr-gcc -DF_CPU=1000000 -mmcu=m16 -o firmware.elf firmware.o
  • ترجمه فایل .hex از فایل .elf

avr-objcopy -O ihex firmware.elf firmware.hex

۴. فلش کردن hex با avrdude

دقت کنید که برای فلش کردن کد نیاز دارید که ابتدا مدار ISP که در مقاله قبل بستیم رو به مدار فعلی اضافه کنید.

avrdude -c arduino -p m16 -P /dev/ttyACM0 -b 19200 -U flash:w:firmware.hex
  • مقدار c برابر با مدل ISP میباشد که ما از Arduino as ISP استفاده میکنیم.

  • مقدار P برابر پورت اتصال پروگرمر میباشد که در لینوکس به صورت پیشفرض ttyACM0 میباشد.

در صورت وجود هرگونه خطا در هنگام فلش کردن، میتوانید به مقاله قبل و بخش رفع عیب avrdude مراجعه کنید.

چند نکته کوچیک

نتیجه این پروژه قرار بود این باشه که لامپی که داخل مدار قرار دادیم نیم ثانیه روشن و خاموش بشه.

اگه لامپ روشن یا خاموش نمیشه :

  • چک کنید که آیا پین درستی رو استفاده میکنید ؟

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

  • و در نهایت حلقه رو بررسی کنید که آیا به درستی پین رو ۰ و ۱ یک میکنید یا نه

اگه لامپ خیلی سریع یا کند خاموش و روشن میشه

  • مقدار DF_CPU رو اشتباه وارد کردید، میتونید با استفاده از مقاله قبلی و بخش فیوزها، متوجه بشید که فرکان میکروکنترلرتون چقدر تنظیم شده و در صورت نیاز اون رو یا مقدار DF_CPU رو تغییر بدید.

۵. تهیه فایل make

به دلیل تکراری بودن روند کامپایل و فلش کردن کد از C به میکروکنترلر و برای اتوماسیون کردن این فرآیند، میتوانیم یک فایل به نام Makefile بدون پسوند در روت پروژه بسازیم و کد های زیر را در آن کپی کنیم :

FLNAME = main.c TARGET = atmega16a DF_CPU = 1000000 ISP_PORT= /dev/ttyACM0 BAUD = 19200 hex: avr-gcc -Os -DF_CPU=$(DF_CPU) -mmcu=$(TARGET) -c $(FLNAME) -o firmware.o avr-gcc -DF_CPU=$(DF_CPU) -mmcu=$(TARGET) -o firmware.elf firmware.o avr-objcopy -O ihex firmware.elf firmware.hex flash: avrdude -c arduino -p m16 -P $(ISP_PORT) -b $(BAUD) -U flash:w:firmware.hex all: hex flash

ازین به بعد با باز کردن ترمینال و رفتن به روت پروژه و وارد کردن فرمان های زیر، میتونیم فرآیند آپلود کد رو راحت تر طی کنیم؛ همینطور میتونید، در صورت نیاز با تغییر متغیر های تعریف شده چند خط اول فایل، اون رو با توجه به نیازتون شخصی سازی کنید.

# کامپایل کردن کد make hex # آپلود کردن کد کامپایل شده make flash # کامپایل و فلش کردن کد make all

جمع بندی و قدم بعدی

یه روز دیگه، یه قدم دیگه به سمت پروژه نهایی این دوره! تونستید با C و یک میکروکنترلر و بدون کمک آردینو اولین چراغ این مسیر رو روشن کنید، بعدش خاموش کنید، دوباره روشن کنید و همینطور تا ابد.

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

برنامه نویسیavrarduino
۰
۰
KAYT33N  | کیان
KAYT33N | کیان
صبحا مهندس مکانیک، شبا برنامه نویس علاقه مند اوپن سورس و هر نوع ساختن https://github.com/KAYT33N
شاید از این پست‌ها خوشتان بیاید