أطراف وبوابات المعالجات AVR Xmega

تتميز معالجات Xmega بتنظيمها الفريد لوحداتها الداخلية. ومن أهم وحداتها الداخلية هي البوابات. حيث كل مجموعة من أطراف المعالج الخارجية ترتبط مع بوابة خاصة بها .
بالنسبة للعائلة ذات اللاحقة A4U من معالجات Xmega نجد أن لها 44 طرف خارجي، وتحتوي البوابات التالية :
– البوابة A ترتبط مع 8 أطراف خارجية وتسمى PORTA .
– البوابات PORTB و PORTE ترتبط كل منها مع 4 أطراف .
– وكذلك البوابات PORTC و PORTD أيضا ترتبط كل منها مع 8 أطراف خارجية.
الشكل التالي يبين توزيع الأطراف الخارجية والبوابات المرتبطة معها في معالجات Xmega AVR عائلة A4U

أطراف المعالجات A4U – XMEGA

كل بوابة من هذه البوابات لها مجموعة من المسجلات تابعة لها موجودة في الذاكرة . وظيفة هذه المسجلات إدارة عمل الأطراف الخارجية المرتبطة بالبوابة والتحكم بها.
وتكون المسجلات التابعة لكل بوابة موجودة في الذاكرة بجانب بعضها البعض ومرتبة كما في الشكل التالي :

المسجلات التابعة للبوابة في معالج XMEGA

وكل مسجل من هذه المسجلات طوله 8 خانات و له وظيفة محددة . وعدد هذه المسجلات أكثر من 20 مسجل . وسنتعرف على أهم هذه المسجلات.

  • المسجل DIR : وهو بطول 8 خانات . وظيفته تحديد إتجاه أطراف المعالج . وكل خانة من خاناته تحدد إتجاه أحد أطراف المعالج المرتبطة مع هذه البوابة.
    فإذا كانت قيمة الخانة الأولى مثلا 1 منطقي . هذا يعني أن الطرف الأول من البوابة هو طرف خرج .
    وإذا كانت قيمة الخانة الأولى 0 هذا يعني أن الطرف الأول هو دخل للمعالج.
  • المسجل OUT : وظيفته تحديد قيمة الجهد أو الحالة المنطقية على أطراف المعالج . وطوله 8 خانات بحيث كل خانة تحدد حالة طرف من الأطراف . فالخانة الأولى تحدد حالة الطرف الأول , والخانة الثانية تحدد حال الطرف الثاني وهكذا .
    فإذا وضعنا في الخانة الأولى القيمة 0 . ستصبح حالة الطرف المقابل لها ذات جهد منخفض.
    وإذا وضعنا القيمة 1 في الخانة . ستصبح حالة الطرف المقابل لها ذات جهد مرتفع.
  • المسجل IN : يستخدم لقرائة حالة أطراف المعالج المرتبطة مع هذه البوابة. وكذلك طوله 8 خانات وكل خانة من هذا المسجل تعبر عن حالة أحد أطراف المعالج .
    فإذا كانت قيمة الخانة الأولى 0 منطقي . هذا يدل أن الجهد على الطرف الأول من هذه البوابة هو جهد منخفض .
    أما إذا كانت قيمة الخانة الأولى 1 منطقي. فهذا يعني أن قيمة الجهد على الطرف الأول مرتفعة .

المسجلات OUTSET , OUTCLR , OUTTGL وظيفتها تعديل قيمة المسجل OUT . وطول كل منها 8 خانات .وتقوم بتغيير قيمة خانات محددة من المسجل OUT بدون التأثير على باقي الخانات .
فالمسجل OUTSET : يستخدم لتغيير قيمة خانات المسجل OUT لتصبح 1 منطقي .
فإذا وضعنا في أحد خاناته 1 منطقي ستتغير قيمة الخانة المقابلة لها في المسجل OUT وتصبح أيضا 1 منطقي . وهذا ينعكس على الطرف الخارجي المقابل للخانة ليصبح ذو جهد مرتفع.
أما إذا وضعنا في الخانة 0 منطقي. فهذا ليس له أي تأثير على الخانة المقابلة لها في المسجل OUT .
المسجل OUTCLR : له نفس وظيفة المسجل OUTSET ولكن بشكل معاكس.
بحيث لو وضعنا 1 منطقي في أحد خاناته سوف تقوم بتعديل قيمة الخانة المقابلة لها في المسجل OUT لتصبح 0 منطقي . وبالتالي تجبر طرف المعالج الخارجي المقابل لهذه الخانة ليصبح ذو جهد منخفض .
بينما لو وضعنا 0 منطقي في أحد خانات هذا المسجل فهذا لا يؤثر على قيمة الخانة المقابلة لها في المسجل OUT ولا على الأطراف الخارجية .
المسجل OUTTGL : وظيفته عكس الحالة السابقة للخانات في المسجل OUT .
فإذا وضعنا 1 منطقي في أحد خاناته . سوف تنعكس قيمة الخانة المقابلة لها في المسجل OUT .
أما إذا وضعنا 0 في خاناته . فهذا أيضا ليس له أي تأثير .

المسجلات DIRCLR, DIRSET, DIRTGL : وظيفتها تعديل قيمة المسجل DIR . كما هو الحال في المسجلات التي تغير قيمة المسجل OUT .
المسجل DIRSET يعدل الخانات في المسجل DIR لتصبح الأطراف المقابلة لها عبارة عن أطراف خرج .
المسجل DIRCLR يقوم بتعديل قيمة المسجل DIR . لتصبح أطراف المعالج المقابلة لها عبارة عن أطراف دخل .
أما المسجل DIRTGL يقوم بعكس الحالة السابقة للخانات في المسجل DIR .

هذه كانت أهم المسجلات الضرورية حاليا . وسنتعرف على باقي المسجلات فيما بعد .
الآن لو أردنا كتابة تعليمات لتغيير قيمة المسجل OUT الخاص بالبوابة A . ونضع فيه القيمة 0
وكذلك المسجل DIR نريد أن نضع فيه القيمة 15 التي تعني 0x0F في النظام السداسي عشر .
هنا نكتب التعليمات التالية :

PORTA.DIR = 15;
PORTA.OUT = 0; 

حيث PORTA هو اسم البوابة A . وتعني مجموعة مسجلات التحكم بالبوابة A .
والنقطة بعد اسم البوابة تستخدم للوصول إلى أحد المسجلات التابعة لها ، بحيث نكتب اسم المسجل بعد النقطة . وهذه من قواعد لغة البرمجة C بشكل عام .
يمكن في بيئة البرمجة Microchip Studio كتابة التعليمات السابقة بشكل أسهل كما يلي :

PORTA_DIR = 15;
PORTA_OUT = 0;

حيث PORTA_OUT تعني المسجل OUT التابع للبوابة A
وكذلك PORTA_DIR تعني المسجل DIR التابع للبوابة A

كتابة برنامج للتعامل مع أطراف المعالج

والآن لننتقل للتطبيق العملي ونقوم بكتابة برنامج بسيط بلغة البرمجة C للتعامل مع البوابات وأطراف المعالج .
لنفرض أنه لدينا المعالج ATxmega16A4U وقمنا بتوصيله حسب الدارة التالية :

من المخطط نجد المعالج يتصل مع المفتاح SW1 عبر طرف البوابة A الأول PA0 .
وكذلك يتصل مع الضوء D1 ذو اللون الأحمر RED عبر طرف البوابة B الأول PB0.
أما المفتاح RESET_SW وظيفته إعادة أقلاع المعالج والبدء بتنفيذ البرنامج من جديد .

والمطلوب كتابة برنامج لهذا المعالج يقوم بتشغيل الأضواء حسب حالة المفاتيح.
بحيث في الحالة الطبيعية يكون الضوء الأحمر في حالة تشغيل.
عند الضغط على المفتاح الأول SW1 ينطفئ الضوء الأحمر D1

من الناحية الكهربائية بالنظر للمخطط السابق نجد أن الطرف PA0 متصل عبر مقاومات 10 كيلو أوم مع التغذية. مما يجعل قيمة الجهد مرتفعة على هذا الطرف . أي الحالة الطبيعية على هذ الطرف هي 1 منطقي.
وعند الضغط على المفتاح يتم وصل طرف المعالج PA0 مع الأرضي . وتصبح قيمة الجهد 0 فولت على طرف المعالج. وبذلك نقول أن الضغط على المفتاح يسبب تغيير حالة الدخل لتصبح 0 منطقي.

أما الضوء فهو متصل بين طرف المعالج والأرضي عبر مقاومة صغيرة 470 أوم .
عندما يكون الجهد على طرف المعالج مرتفع أو 1 منطقي يكون الضوئ في حالة تشغيل و إضائة.
وعندما يكون الجهد على طرف المعالج هو 0 فولت يكون الضوء منطفئ .

لننتقل للجزء البرمجي وفي البداية سوف نلقي نظرة على البرنامج التالي . وسنشرح تعليماته بالتفصيل .

#include <avr/io.h>

void main()
{
	PORTA_DIRCLR = PIN0_bm;
	
	PORTB_DIRSET = PIN0_bm;
	
	while(1)
	{
		if((PORTA_IN & PIN0_bm) == 0)
		{
			PORTB_OUTCLR = PIN0_bm;
		}
		else
		{
			PORTB_OUTSET = PIN0_bm;
		}
				
	}
}

في بداية البرنامج يوجد السطر البرمجي التالي :

#include <avr/io.h>

هذا السطر يبدأ بالتعليمة include التي تخص بيئة البرمجة. ومهمتها تضمين ملف اسمه avr/io.h .
وهذا الملف يحتوي تعريفات تخص المعالج لتصبح معروفة في بيئة البرمجة . مثل أسماء المسجلات و الثوابت وأسماء وعناوين الوحدات الداخلية الخاصة بالمعالج .
حيث لا يمكن لبيئة البرمجة أن تعرف مثلا ما هو المقصود بالكلمة PORTA بدون تضمين هذا الملف .
ثم يليه الدالة الرئيسية main والتي يبدأ المعالج بتنفيذها مباشرة عند الاقلاع بعد توصيل الكهرباء للمعالج.
ولذلك نقوم بكتابة تعليمات برنامجنا في الدالة أو التابع main .

بشكل عام قبل قرائة أو تغيير حالة أطراف المعالج . يجب تحديد اتجاه هذه الأطراف.
لذلك في البداية نحدد اتجاه طرف البوابة A الأول المتصل مع المفتاح ، على أنه دخل للمعالج بالتعليمة التالية :

PORTA_DIRCLR = PIN0_bm ;

حيث المسجل PORTA_DIRCLR قلنا سابقا وظيفته تحديد إتجاه الأطراف التابعة للبوابة A على أنها دخل. وأن كل خانة من خانات هذا المسجل تخص أحد أطراف المعالج .
وبالتالي لنحدد الطرف الأول على أنه دخل يجب أن نضع في الخانة الأولى من المسجل القيمة 1.
أي أن القيمة حسب النظام الثنائي التي سنضعها في المسجل هي 00000001
ويمكن كتابة هذه القيمة بالنظام السداسي عشر هكذا 0x01 وهي ما يجب وضعه في المسجل.

لكن بيئة البرمجة Microchip Studio تقدم لنا بعض التسهيلات. من خلال القيم والثوابت المعرفة مسبقا. والموجودة في الملف avr/io.h الذي قمنا بتضمينه في البداية .
حيث تعتبر القيمة PIN0_bm عبارة عن رقم ثابت يمثل القيمة 1 . أي في النظام الثنائي 00000001 والتي يمكن كتابتها في النظام السداس عشر بالشكل 0x01
والجدول التالي يبين مجموعة الثوابت هذه وقيمها :

القيمة بالنظام الثنائيالقيمةإسم الثابت
000000010x01PIN0_bm
000000100x02PIN1_bm
000001000x04PIN2_bm
000010000x08PIN3_bm
000100000x10PIN4_bm
001000000x20PIN5_bm
010000000x40PIN6_bm
100000000x80PIN7_bm

تفيد هذه الثوابت للدلالة على أطراف المعالج بشكل مفهوم للقارئ.
حيث القيمة PIN0_bm تعني الخانة الأولى التي تحدد الطرف الأول من البوابة
وكذلك القيمة PIN1_bm تعني الخانة الثانية التي تحدد الطرف الثاني من البوابة وهكذا.
ولذلك قمنا بكتابة PIN0_bm بدلا من 0x01 في البرنامج وبذلك عند تنفيذ التعليمة السابقة تصبح قيمة المسجل PORTA_DIRCLR هي 00000001

بعدها ينتقل التنفيذ للتعليمة الثانية :

PORTB_DIRSET = PIN0_bm ;

كما في التعليمة السابقة ، هذه التعليمة تضع القيمة 00000001 في المسجل PORTB_DIRSET
هذا المسجل يحدد إتجاه أطراف البوابة B على أنها خرج . بحيث كل خانة تقابل طرف من الأطراف.
وبعد تنفيذ هذه التعليمة تصبح حالة الطرف الأول PB0 من البوابة B طرف خرج.

ثم ينتقل المعالج لتنفيذ الحلقة while . وهنا سنشرح بشكل مختصر ماذا تفعل هذه الحلقة . والتي تكتب في لغة البرمجة C بالشكل التالي :

while (/* شرط*/)
{
	/*
	تعليمات	
	*/
}

حيث تبدأ بالكلمة while وبعدها قوسين نكتب بينهما شرط معين وهو عبارة عن قيمة أو تابع أو تعليمة لها نتيجة منطقية 0 أو 1.
بعدها نكتب التعليمات التابعة لهذه الحلقة بين الأقواس المنحنية التي تحدد بداية ونهاية تعليمات الحلقة حسب قواعد لغة البرمجة C.
يتم تنفيذ التعليمات المكتوبة ضمن هذه الحلقة طالما الشرط الموجود بين قوسين محقق أي قيمته ليست 0 منطقي.

بالعودة للبرنامج السابق نجد أن الشرط للحلقة هو الرقم 1 . وهو قيمة ثابتة لا تتغير ودائما تعتبر محققة .
لذلك يتم تنفيذ التعليمات ضمن هذه الحلقة بشكل متكرر بدون توقف. وهذا ما نسميه حلقة لانهائية.

التعليمة الأولى ضمن هذه الحلقة هي التعليمة if والتي تعني التحقق من الشرط المكتوب بعدها بين قوسين .
فإذا كانت نتيجة الشرط لا تساوي القيمة 0 منطقي عندها يتم تنفيذ التعليمات التابعة لها .
وهنا في حالتنا الشرط هو :

(PORTA_IN & PIN0_bm) == 0

PORTA_IN هو المسجل IN من البوابة A . ويحتوي في خاناته على الحالة المنطقية التي تعبر عن الجهد الموجود على الأطراف الخارجية للبوابة .
أما التعليمة PORTA_IN & PIN0_bm فهي عبارة عن عملية ضرب منطقي لقيمة المسجل IN مع القيمة PIN0_bm والتي تساوي 0x01 أو 00000001 في النظام الثنائي .
فإذا كانت الخانة الأولة من المسجل IN تساوي 1 هذا يعني أن الجهد على الطرف PA0 مرتفع . أي أن المفتاح SW1 محرر وغير مغلق . وتكون نتيجة الضرب السابقة

PORTA_IN & PIN0_bm = 0x01 & 0x01 = 0x01 = 00000001


أما إذا كانت الخانة الأولى في المسجل IN قيمتها 0 منطقي . هذا يعني أن الجهد على الطرف PA0 منخفض ويساوي الصفر وذلك لأن المفتاح SW1 المتصل مع هذا الطرف مغلق . وتكون نتيجة الضرب السابقة هي

PORTA_IN & PIN0_bm = 0x00 & 0x01 = 0x00 = 00000000

وبالتالي يمكن من عملية الضرب المنطقي السابقة معرفة حالة الدخل على طرف المعالج والتي تعبر عن حالة المفتاح SW1.

ومنه نجد أن الشرط في التعليمة if يتحقق من أن القيمة الناتجة عن الضرب المنطقي السابق هي 0 منطقي . وتكون نتيجة هذا الشرط محققة عندما يكون المفتاح على الطرف PA0 مغلق .
وعندها يتم تنفيذ ما تتضمنه التعليمة if . وهو التعليمة التالية :

PORTB_OUTCLR = PIN0_bm;

وهي عملية وضع القيمة PIN0_bm والتي تساوي 0x01 في المسجل PORTB_OUTCLR .
وهذا يجعل حالة الطرف PB0 من البوابة B في حالة 0 منطقي ويكون الجهد على هذا الطرف منخفض ويساوي الصفر فولت . مما يجعل الضوء الأحمر ينطفئ .

أما إذا كان المفتاح محرر عندها يكون الجهد على الطرف PA0 مرتفع وتكون نتيجة الشرط غير محققة. وهنا ينتقل التنفيذ إلى التعليمات الموجودة في القسم else . حيث نجد التعليمة التالية:

PORTB_OUTSET = PIN0_bm;

وهي عملية وضع القيمة PIN0_bm والتي تساوي 0x01 في المسجل PORTB_OUTSET.
وهذا يجعل حالة الطرف PB0 من البوابة B في حالة 1 منطقي ويكون الجهد على هذا الطرف مرتفع ويساوي قيمة جهد التغذية تقريباً . مما يجعل الضوء الأحمر يتوهج .

بعدها تنتهي حلقة while . ويعود المعالج لتنفيذ تعليمات الحلقة من جديد.
وهكذا يستمر عمل المعالج . فإذا تم الضغط على المفتاح يقوم المعالج بإطفاء الضوء الأحمر. وعند تحرير المفتاح يعود الضوء للإضائة من جديد.

هكذا نكون قد انتهينا من هذا المثال وتعلمنا كيفية التعامل مع بوابات المعالج والتحكم بالأطراف الخارجية للمعالج.