المكتبة Syra uOS للمعالجات التحكمية – AVR XMega

في هذه المرحلة سوف نتعرف على كيفية استخدام هذه المكتبة مع معالجات XMEGA التي هي معالجات 8 بت من نوع AVR . من إنتاج شركة ATMEL سابقاً والتي أصبحت ملكاً لشركة Microchip .
تعرفنا في السابق على كيفية إنشاء مشروع جديد باستخدام المكتبة Syra uF  . والتي هي نظام غير متزامن تعتمد على مبدأ الأحداث . وهي مصممة لبناء تطبيقات قابلة للتوسع للمعالجات التحكمية.
سنختار عائلة المعالجات XMega لهذه الدورة . ويمكن تحميل نسخة من هذه المكتبة من الرابط التالي :
Syra uF AVR XMega Release v1.4.0
وللإطلاع على الأجزاء السابقة من هذه الدورة , نجدها في الروابط التالية :
المكتبة Syra uF للمعالجات التحكمية – المقدمة
المكتبة Syra uF للمعالجات التحكمية – الأساسيات

في هذا الجزء سوف نتعلم كيفية كتابة المهام الجديدة . وإضافتها للمشروع . وتسجيلها في النظام .
سنأخذ مثالاً عن كتابة مهمتين لتشغيل أضواء LED موصولة مع أطراف المعالج .

سنكمل المثال الذي بدأناه في الجزء السابق . الذي تم إنشائه لمعالح من نوع XMega . ويمكن للمبرمج أن يطبق نفس الخطوات مع أي نوع من المعالجات التي تدعمها هذه المكتبة .
لنفرض أننا نريد كتابة مهمتين منفصلتين عن بعض . تقوم كل واحدة منها بتشغيل ضوء مرة كل فترة زمنية معينة.
لنأخذ المخطط التالي لتوصيل الأضواء مع أطراف المعالج :

إضافة المهام للمشروع :
أولا نقوم بإضافة  ملف جديد للمشروع  لنكتب فيه الكود الخاص بهذه المهام وذلك كما يلي :

  • من نافذة Solution Explorer  نضع المؤشر على إسم المشروع  ونضغط بالزر اليمين .
  • تظهر قائمة نختار منها Add  لإضافة عنصر جديد ، فتظهر قائمة جديدة صغيرة .
  • نختار منها New Item  كما في الشكل التالي :

بعدها تظهر نافذة جديدة  نختار فيها نوع الملف الذي نريد إضافته ،  ونكتب اسم الملف  الذي نريده .
في مثالنا هذا نختار نوع الملف C File   لاننا نريد إضافة ملف لكتابة كود بلغة  C   .
ونكتب اسم الملف tasks.c لأننا سنكتب فيه جميع المهام ، وهذا لسهولة الشرح .
لكن بشكل عام يفضل كتابة كل مهمة في ملف منفصل وذلك لتمييز الملفات في المشروع عن بعضها .
ثم نضغط على Add  لإضافة الملف للمشروع .

  • نعيد الخطوات السابقة وذلك لإضافة  ملف ثاني  من نوع  Include File  ، وهذا من متطلبات لغة البرمجة C  .
    أيضا نختار الاسم نفسه  للملف الجديد tasks.h ، ثم نضغط  Add  .

هكذا يصبح لدينا ملفين جديدين في المشروع كما في الشكل :

في البداية تكون الملفات الجديدة فارغة . وقبل البدء بكتابة الأوامر لدينا قاعدة هامة :
كل ملف ينتهي باللاحقة c مثل الملف tasks.c وأي ملف نضيفه للمشروع يجب أن نكتب في بدايته تعليمة تضمين الملف inc.h :

#include “inc.h”

وكذلك كل ملف ينتهي باللاحقة h مثل tasks.h يجب أن يتم تضمينه في ملف الربط inc.h . وذلك لتصبح هذه الملفات متوافقة مع نظام عمل المكتبة . فيصبح محتوى الملف inc.h بالشكل التالي :

/*	
// inc.h ملف الربط	
*/

#include "SMF/core.h"

#include "main.h"

//--------------------------
// في هذا المكان نضيف التعريف
#include "tasks.h"

والآن نفتح الملف tasks.c   لكتابة المهمة الأولى . حيث قلنا أن المهمة وظيفتها تشغيل وإطفاء ضوء بشكل متكرر .
بفرض الضوء الذي لونه أخضر موصول مع الطرف  PIN0 من البوابة A .

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

ونكتب بداخله  أوامر تعريف الطرف المطلوب على أنه خرج كما يلي :

void tasks_Setup()
{
	//  تحديد الطرف الموصول مع الضوءالأخضر على أنه خرج
	PORTA_DIRSET = PIN0_bm;
        //  قد نحتاج تحديد قيمة أولية للطرف
        // هنا مثلا نختار أن يكون الضوء منطفئ
	PORTA_OUTCLR = PIN0_bm;
	
	
	//  تحديد الطرف الموصول مع الضوءالأحمر على أنه خرج
	PORTA_DIRSET = PIN1_bm;
        //  أيضا يمكن تحديد قيمة أولية للطرف
        // وأيضا نختار أن يكون الضوء منطفئ
	PORTA_OUTCLR = PIN1_bm;
}

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

ثانيا نكتب تابع جديد  ليكون هو المهمة التي نريد كتابة  الجزء الرئيسي من الكود بداخله .
ونختار إسم للتابع أو للمهمة وليكن   Green_Task
ونكتب بداخل هذا التابع الجزء الخاص بتشغيل و إطفاء الضوء الأخضر ، لأن هذا هو الكود الذي نريد أن يتم تنفيذه بشكل متكرر .
وفي حالتنا هذه نحتاج الأمر التالي الذي يقوم بتغيير حالة الطرف PIN0 :

PORTA_OUTTGL = PIN0_bm ;

ثم الانتظار لمدة ثانية او 1000 ميلي ثانية ، من خلال الأمر التالي :

TaskWait(1000);

فيصبح التابع بالشكل :

//  مهمة الضوء الأخضر
void Green_Task()
{
	//  هذا الأمر لتغيير حالة الطرف الموصول مع الضوء الأخضر
	 PORTA_OUTTGL = PIN0_bm;
	 
	 //  هذا الأمر لتحديد زمن الانتظار قبل إعادة تنفيذ المهمة
	 TaskWait(1000);
}

نكرر نفس العملية السابقة لإضافة مهمة ثانية خاصة بالضوء الأحمر ، مع مراعاة أن الضوء الأحمر موصول مع الطرف الثاني من البوابة PORTA . وأن هذه المهمة سيتم تنفيذها كل 500 ميلي ثانية.
وهنا تصبح المهمة بالشكل التالي :

//  مهمة الضوء الأحمر 
void Red_Task()
{
	//  هذا الأمر لتغيير حالة الطرف الموصول مع الضوء الأحمر
	PORTA_OUTTGL = PIN1_bm;
	
	//  هذا الأمر لتحديد زمن الانتظار قبل إعادة تنفيذ المهمة
	TaskWait(500);
}

هكذا نكون قد انتهينا من كتابة توابع المهام المطلوبة ،  وكذلك  تابع التهيئة .
وبذلك يصبح محتوى الملف tasks.c بالكامل كالتالي :

/*
// tasks.c محتوى الملف
*/

// تضمين ملف الربط مع النظام
#include "inc.h"

////////////////////////////
// تابع التهيئة للمهام
void tasks_Setup()
{
	//  تحديد الطرف الموصول مع الضوءالأخضر على أنه خرج
	PORTA_DIRSET = PIN0_bm;
        //  قد نحتاج تحديد قيمة أولية للطرف
        // هنا مثلا نختار أن يكون الضوء منطفئ
	PORTA_OUTCLR = PIN0_bm;
	
	
	//  تحديد الطرف الموصول مع الضوءالأحمر على أنه خرج
	PORTA_DIRSET = PIN1_bm;
        //  أيضا يمكن تحديد قيمة أولية للطرف
        // وأيضا نختار أن يكون الضوء منطفئ
	PORTA_OUTCLR = PIN1_bm;
}

////////////////////////////
//  مهمة الضوء الأخضر
void Green_Task()
{
	//  هذا الأمر لتغيير حالة الطرف الموصول مع الضوء الأخضر
	 PORTA_OUTTGL = PIN0_bm;
	 
	 //  هذا الأمر لتحديد زمن الانتظار قبل إعادة تنفيذ المهمة
	 TaskWait(1000);
}

////////////////////////////
//  مهمة الضوء الأحمر 
void Red_Task()
{
	//  هذا الأمر لتغيير حالة الطرف الموصول مع الضوء الأحمر
	PORTA_OUTTGL = PIN1_bm;
	
	//  هذا الأمر لتحديد زمن الانتظار قبل إعادة تنفيذ المهمة
	TaskWait(500);
}

لكن حتى يفهم النظام أن هذه التوابع هي مهام يجب عليه تنفيذها في حلقة المهام ، لابد من تسجيلها في النظام .
وأن تابع التهيئة هو من الأوامر التي يجب أن تعمل مرة واحدة عند الإقلاع يجب تعريف ذلك في النظام ليعمل البرنامج بالشكل الصحيح .

تسجيل المهام في النظام :

أولا بحسب قواعد البرمجة بلغة C . يجب إضافة تعريف التوابع التي كتبناها سابقة جميعها في الملف tasks.h , وذلك لتصبح مرئية لباقي أجزاء المشروع.

/*
 * tasks.h  الملف
 */ 

void tasks_Setup();

void Green_Task();
void Red_Task();

ثم ننتقل للجزء الأهم وهو تسجيل المهام في النظام .
قلنا أن التابع OnBoot الموجود في الملف main.c . خاص لكتابة الأوامر التي نريد تنفيذها في مرحلة الإقلاع .
 لذلك نقوم باستدعاء تابع التهيئة في هذا القسم , ومن ثم  تسجيل المهام .

بالنسبة لاستدعاء تابع التهيئة ، الموضوع بسيط جدا ، فقط نكتب إسم التابع .
أما تسجيل المهمة فيتم بالاستعانة بالأمر  Sys_CreateTask   وهو من الاوامر الخاصة بالمكتبة لتسجيل المهام .
حيث نستدعي هذا الأمر . ونمرر إسم المهمة لهذا التابع . مع الإنتباه لعدم كتابة إسم المهمة مع الأقواس .
لأننا نريد تمرير إسم المهمة . وليس استدعاء المهمة . الشكل التالي يوضح ذلك :

/*
// main.c
*/

void OnBoot()
{
	
	// استدعاء تابع التهيئة للمهام
	tasks_Setup();
	
	
	// تسجيل المهمة الأولى في النظام
	Sys_CreateTask(Green_Task);
	
	// تسجيل المهمة الثانية في النظام
	Sys_CreateTask(Red_Task);
	
	// الآن كل شيئ جاهز للعمل
	
}
 

هكذا نكون قد انتهينا من كتابة المهام المطلوبة ، وتابع التهيئة ، وقمنا بتسجيل جميع المهام في النظام بشكل جيد .

نقوم ببناء المشروع من خلال الأمر Build   في  بيئة البرمجة .
وبعدها  ننقل الملف التنفيذي الناتج للمعالج عبر المبرمجة للتجريب العملي .

وهكذا نكون قد انتهينا من كتابة برنامج للمعالج التحكمي يحتوي أكثر من مهمة . بالاعتماد على المكتبة  Syra uF .