![]() |
#1
|
||||
|
||||
![]()
كتاب ودورة جديدة Embedded C Programming PIC والمترجم CCS PIC C
![]() عام : الميكروكونترولر المدمج هو كومبيوتر دقيق والذى يحتوى على معظم أجهزته المحيطة والذاكرة المطلوبة بداخل دائرة متكاملة واحدة مع وحدة المعالجة المركزية CPU . فهو بالفعل "كومبيوتر دقيق فى شريحة" . |
#2
|
||||
|
||||
![]()
الفصل الأول :
لغة السى المدمجة Embedded C Language : 1-1 الهدف: عند نهاية هذا الفصل , يجب أن تكون قادرا على ما يلى : تعريف , ووصف , ووتحديد أنواع المتغير variable والثابت , ومجالها واستخداماتها . بناء إعلانات المتغير والثابت لجميع حجوم البيانات العددية numeric والسلاسل النصية strings. تطبيق قوائم السرد الإحصائية enumeration على إعلانات المتغير . تخصيص assign قيم للمتغيرات والثوابت عن طريق "عامل التخصيص" . تقييم نتائج جميع "العوامل" المستخدمة فى لغة السى . شرح النتائج التى تمتلكها "عبارات التحكم" عند سريان البرنامج . إنشاء "دوال" functions والتى تشتمل على متغيرات , وعوامل , وعبارات تحكم لمهام كاملة . تطبيق "المؤشرات" pointers , و"المصفوفات" arrays و "الهياكل" structures و "الاتحادات" unions كمتغيرات للدالة . إنشاء برامج بلغة السى لمهام كاملة باستخدام مفاهيم هذا الفصل . 1-2 مقدمة : هذا الفصل يغطى الدورة الأساسية فى البرمجة بلفة السى عند تطبيقها على تطبيقات الميكروكونترولر المدمج. سوف تتقدم من المفاهيم الأولية وحتى كتابة البرامج الكاملة , مع أمثلة يمكن تنفيذها على الميكروكونترولر . يتم تقديم المعلومات بالترتيب الذى يحتاجه المبرمج : إعلان declaring المتغيرات والثوابت . المداخل / المخارج I/O البسيطة , بحيث يمكن عمل برامج للمنافذ المتوازية للميكروكونترولر . تخصيص قيم إلى المتغيرات والثوابت وعمل عمليات حسابية مع المتغيرات . عبارات البناء والتحكم بلغة السى لتشكيل برامج كاملة بلغة السى . الأقسام الأخيرة تغطى المواضيع المتقدمة مثل المؤشرات , والمصفوفات , والهياكل , والاتحادات , واستخدامها فى برامج لغة السى . المفاهيم المتقدمة مثل برمجة "الزمن – الحقيقى" real time والمقاطعات تكمل الفصل . |
#3
|
||||
|
||||
![]()
1-3 المفاهيم الأساسية :
كتابة برنامج بلغة السى يشبه بناء منزل من الطوب البلوك : يتم وضع الأساس , يستخدم الرمل والأسمنت لعمل الطوب , يتم ترتيب هذا الطوب لعمل صف ( مدماك – طبقة) من البلوكات ثم يتم تكديس الصفوف لإنشاء البناء . فى برامج السى المدمجة , يتم وضع مجموعات من التعليمات معا لتشكيل "دوال" functions , يتم معاملة تلك الدوال كعمليات " مستوى – مرتفع " , والتى بعد ذلك يتم تجميعها لتشكيل البرنامج . كل برنامج بلغة السى يجب أن يمتلك دالة واحدة على الأقل , هذه الدالة تسمى "الدالة الرئيسية" main() . الدالة main() هى "الأساس" لبرنامج لغة السى , وهى نقطة البداية عند تنفيذ كود البرنامج . جميع الدوالت يتم إطلاقها عن طريق الدالة الرئيسية main() سواء مباشرة أو غير مباشرة . على الرغم من أن الدوال يمكن أن تكون كاملة وخاصة بذاتها , إلا أن المتغيرات والبارامترات يمكن أن تستخدم لريط تلك الدوال مع بعضعا البعض . الدالة الرئيسية main() تعتبر مهمة "المستوى – المنخفض" , لأنها الدالة الأولى التى تستدعى من نظام بدء البرنامج . فى الكثير من الحالات , سوف تحتوى الدالة الرئيسية main() على قليل من العبارات فقط والتى لا تعمل شىء سوى أعداد وقيادة عمل البرنامج من أحد الدوال إلى الأخرى . برنامج لغة السى المدمجة فى أبسط شكل له يبدو كما يلى : كود:
void main( ) { While(1) //do forever…. ; } البرنامج الموضع بأعلاه سوف يتم ترجمته ويعمل على نحو تام , ولكنك لن تتأكد من ذلك لعدم وجود بيان لنشاط من أى نوع . كود:
#include <stdio.h> void main( 0 { printf("HELLO WORLD"); /* the classic C test program.. */ while(1) //do forever.. ; } هذا البرنامج سوف يطبع الكلمات "HELLO WORLD" إلى الخرج القياسى , وهو فى الغالب المنفذ التسلسلى . الميكروكونترولر سوف يجلس وينتظر إلى الأبد أو حتى يتم إعادتة reset . وهذا يعرض واحد من أهم الاختلافات الرئيسية بين برنامج الكومبيوتر الشخصى والبرنامج المصمم للميكروكونترولر المدمج , وهو , أن تطبيقات الميكروكونترولر المدمج تحتوى على حلقة لا نهائية .الكومبيوتر الشخصى يمتلك نظام تشغيل , ونجرد تنفيذ البرنامج , يعود التحكم إلى نظام التشغيل . الميكروكونترولر المدمج لا يمتلك نظام تشغيل لذلك لا يمكن السماح بالخروج من البرنامج فى أى وقت . لذلك فإن كل تطبيق للميكروكونترولر المدمج يمتلك حلقة لا نهائية فى بنائه الداخلى فى مكان ما , مثل while(1) فى المثال أعلاه . هذا يمنع البرنامج من الهروب من الأشياء التى يقوم بفعلها ويفعل أشياء عشوائية والتى قد تكون غير مرغوب فيها . سوف يت شرح بناء while فيما بعد . يحتوى برنامج المثال أيضا على أول "موجه" شائع الاستخدام للمترجم . الموجه #include يخبر المترجم بضم ملف يسمى stdio.h ليكون كجزء من هذا البرنامج . الدالة printf() متوفرة من أجل المكتبة الخارجية وتكون متاحة لنا لأن تعريفها يقع بالملف stdio . هناك بعض العناصر التى يجب تعريفها فى المثال السابق : ; فاصلة بنقطة تستخدم لبيان نهاية التعبير . التعبير فى أبسط أشكاله هو فاصلة بنقطة منفردة . { } تستخم هذه الأقواس لاحتواء (تطويق) بداية ونهاية محتويات الدالة . تستخدم هذه الأقواس أيضا عندما يتم معالجة سلسلة من العبارات كمجموعة (بلوك) واحدة . "text" تستخدم علامات الاقتباس المزدوجة كعلامة على بداية ونهاية سلسلة نصية . // أو /*….*/ تستخدم شرطة – شرطة أو شرطة – نجمة ... نجمة – شرطة للتعليقات . التعليقات هى مجرد ملاحظات المبرمج . التعليقات مهمة للغاية للقدرة على قراءة وفهم البرنامج . هذه حقيقة سواء عند قراءة البرنامج بالمبرمج نفسه فى وقت لاحق أو بشخص آخر . التعليقات المبينة بهذا النص تستخدم لشرح وظيفة كل سطر فى البرنامج . يستخدم الرمز /*….*/ لإنشاء مجموعة (بلوك) تعليقات . بمجرد أن يجد المترجم (/*) فسوف يتجاهل النص الذى يليه , حتى إذا كانت مجموعة التعليقات فى سطور عديدة , حتى يجد (*/) . عندما يجد المترجم الرمز (//) فسوف يتجاهل نص التعليق حتى يصل نهاية السطر فقط . |
#4
|
||||
|
||||
![]()
بعض التعاريف :
"معرف أو محدد الهوية" identifier : هو اسم لمتغير أولدالة يتكون من حرف أو الشرطة التحتية ( _ ) , متبوعا بتسلسل من الحروف و /أو الأرقام و / أو الشرط التحتية . معرفات الهوية عامة تكون حساسة للحالة . يستخدم الموجه #case للتحكم فى حساسية الحالة فى المترجم CCS-PICC . يمكن أن تكون معرفات الهوية بأى طول , لكن بعض المترجمات قد يتعرف على عدد محدود من الحروف . الكلمات المحجوزة : يوجد كلمات معينة لها معنى خاص للمترجم وتعتبر "كلمات محجوزة" . هذه الكلمات المحجوزة يجب إدخالها فى الحالة الصغيرة ويجب عدم استخدامها بتاتا كمعرفات للهوية . الجدول التالى يبين قائمة بالكلمات المحجوزة . ![]() المسافة البيضاء : لأن لغة السى لغة " حرة الشكل" فإن "المسافة البيضاء spaceيتم تجاهلها ما لم يتم تطويقها بأقواس . وهذ يشمل الفراغات , والتبويبات tab والسطر الجديد . |
#5
|
||||
|
||||
![]()
1-4 المتغيرات والثوابت :
حان وقت البحث فى البيانات المخزنة فى شكل متغيرات وثوابت . المتغيرات variables , كما فى الجبر, هى "قيم يمكن أن تتغير" . الثوابت constants ثابتة القيمة . تأتى المتغيرات والثوابت فى أشكال وأحجام كثيرة , ويتم تخزينها قى ذاكرة البرنامج فى أشكال مختلفة والتى سوف نتعرف عليها تباعا . 1-4-1 أنواع المتغير variable types : يتم إعلات (تعريف) المتغير عن طريق "الكلمة المحجوزة الدالة على نوعه وحجمه" يتبعها معرف الهوية (الاسم) : كود:
unsigned char Peabody; int dogs , cats ; long int total_dogs_and_cats ; يتم تخزين المتغيرات والثوابت فى الذاكرة المحدودة للميكروكونترولر , ويحتاج المترجم معرفة مقدار الذاكرة التى يحددها لكل متغير دون استهلاك حيز الذاكرة دون داع . وبالتالى يجب على المبرمج إعلان المتغيرات , وتحديد كل من حجم المتغير ونوع المتغير . من المهم فهم حجم كل نوع من البيانات للمترجم الذى تستخدمه لأنه ليست كل المترجمات سواء . الخطأ الرياضى سوف يقل لأدنى حد طالما أنك تفهم حجم وإشارة الذى تعلن عنه . الجدول التالى يبين قائمة بأنواع المتغير القياسية وحجمها المناظر . ![]() الأنواع الافتراضية من أجل المترجم CCS-PICC للأحجام المعطاة , مدونة بالعامود "Size" بالجدول . الأنواع int1,int8,int16,int32 مخصصة للمترجم CCS-PICC . |
#6
|
||||
|
||||
![]()
1-4-2 مجال المتغير :
لعلك لاحظت أنه يجب إعلان الثوابت والمتغيرات قبل استخدامها . "مجال" المتغير هو مدى القدرة على الوصول إليه داخل البرنامج . يمكن إعلان المتغير بحيث يكون مجاله إما "محلى" local أو "عمومى" global . المتغيرات المحلية Local Variables : المتغيرات المحلية هى مواقع بالذاكرة مخصصة عن طريق "دالة" . هذه المتغيرات لا يمكن الوصول إليها من أى دالة أخرى , بمعنى أن مجالها محدد بالدوال التى أعلنتها . يمكن استخدام إعلان متغير محلى فى دوال عديدة بدون تعارض لأن المترجم يرى كل من هذه المتغيرات كما لو كانت جزء من من هذه الدالة فقط . المتغيرات العمومية Global Variables : المتغير العمومى أو الخارجى هو موقع بالذاكرة مخصص عن طريق المترجم ويمكن الوصول إليه عن طريق جميع دوال البرنامج ( مجال غير محدود) . يمكن تعديل المتغير العمومى عن طريق أى دالة وسوف يحتفظ بقيمته لكى يستخدمها عن طريق الدوال الأخرى . عادة يتم مسح ( تحديدها بصفر) المتغيرات العمومية عند بدء الدالة الرئيسية main( ) . هذه العملية غالبا يتم تنفيذها عن طريق كود البداية المتولد بواسطة المترجم ولا يراه المبرمج . قطعة الكود التالية تبين مجال المتغيرات : كود:
unsigned char globey ; // a global variable void function_z (void) //this is a function called from main( ) { unsigned int tween; //a local variable tween = 12 ; //OK because tween is local globey = 47 ; // OK because globey is global main_loc =12 ; //.This line will generate an error //because main_loc is local to main } void main( ) { unsigned char main_loc; // a variable local to main( ) globey = 34 ; // OK because globey is global tween = 12 ; // will cause an error – tween is locat to function function_z while(1)//do forever.. { ; } } عندما يتم استخدام متغيرات داخل دالة , إذا كان متغير محلى يمتلك نفس اسم المتغير العمومى , فإن المتغير المحلى سوف يستخدم عن طريق الدالة . قيمة المتغير العمومى , فى هذه الحالة , سوف لا يسمح بالوصول إليها من الدالة وسوف تظل على ما هى عليه ولن تمس . 1-4-3 الثوابت Constants : |
#7
|
||||
|
||||
![]()
جزاك الله خيرا استاذنا العزيز
فعلا انا بدأت بتعلم PICC بدلا من مايكروسى حيث ان برنامج Protues يدعم اومره ويمكن مشاهدتها و هى تنفذ سطر سطر داخل البروتوس |
#8
|
||||
|
||||
![]() اقتباس:
شكرا جزيلا لك بارك الله فيك مع تمنياتى بدوام التوفيق |
#9
|
||||
|
||||
![]()
1-4-3 الثوابت Constants :
الثوابت هى "قيم ثابتة" والتى قد لا يتم تعديلها أثناء تنفيذ البرنامج . فى كثير من الحالات تكون الثوابت جزء من البرنامج المترجم نفسه , موضوعة فى ذاكرة القراءة فقط ROM , بدلا من ذاكرة الوصول العشوائى RAM القابلة للتغيير . فى التخصيص التالى : x = 3 + y ; العدد "3" هو "ثابت" وسوف يتم تشفيره مباشرة فى عملية "جمع" عن طريق المترجم . يمكن أيضا أن تكون الثوابت فى شكل حروف أو سلسلة نصية كما يلى : كود:
printf("hello world"); //The text hello world is placed in program memory and never changes. x = 'B' ; //The letter 'B' is permanently set in program memory. يمكنك أيضا إعلان الثابت عن طريق "الكلمة المحجوزة" const ثم الإشارة إلى نوعه وحجمه . لكى يكون الإعلان تام مطلوب "معرف الهوية" أو الاسم و "القيمة" كما يلى : كود:
cons char c = 57 ; تعريف متغير كثابت سوف يؤدى إلى أن هذا المتغير يخزن فى حيز ذاكرة البرنامج بدلا من حيز ذاكرة المعطيات RAM المتغيرة المحدودة . هذا يساعد على الحفاظ على حيز ذاكرة RAM المحدود . الثوابت العددية Numeric Constants : يمكن إعلان الثوابت العددية بعدة طرق عن طريق الإشارة إلى "الأساس العددى" وجعل البرنامج أكثر قدرة للقراءة . يمكن كتابة ثوابت "الأعداد الصحيحة" integer و "الأعداد الصحيحة الطويلة" long integer بالأشكال التالية : · الشكل "العشرى" decimal بدون "سابقة" (مثل (1234 . · الشكل "الثنائى" binary مع السابقة "0b" (مثل 0b101001 ) . · الشكل "السداسى عشر" hexadecimal مع السابقة "0x" (مثل 0xff ). · الشكل "الثمانى" octal مع السابقة "0" ( مثل 0777 ) . يوجد أيضا "معدلات" modifiers للتعريف بشكل أفضل للحجم المراد وتستخدم مع الثوابت كما يلى : · ثوابت "الأعداد الصحيحة بدون إشارة" Unsigned integer يمكن أن تمتلك "اللاحقة" U ( مثل 10000U ) . · ثوابت "الأعداد الصحيحة الطويلة" Long integer يمكن أن تمتلك اللاحقة L (مثل 99L ). · ثوابت "الأعداد الصحيحة الطويلة بدون إشارة" Unsigned long integer يمكن أن تمتلك اللاحقة UL ( مثل 99UL ) . · ثوابت "الأعداد الحقيقية" Floating point يمكن أن تمتلك اللاحقة F (مثل 1.234F ) . · ثوابت "الحروف" Character يجب أن تحاط بعلامات "اقتباس مفردة" ( مثل 'a' و 'A' ). ثوابت الحرف Character constants : ثوابت الحرف يمكن أن تكون "قابلة للطباعة" ( مثل 0 – 9 و A – Z ) أو حروف "غير قابلة للطباعة" ( مثل "سطر جديد" new line أو "عودة العربة" carriage return أو "التبويب" tab ) . ثوابت الحرف القابلة للطباعة قد يتم إحاطتها بعلامتى اقتباس مفردة ( مثل 'a' ) . علامتى الاقتباس المفرد التى تحيط "الشرطة المائلة" backslash المتبوعة بقيمة ثمانية أو سداسية عشر يمكن أيضا أن تمثل ثوابت الحرف كما يلى : 't' يمكن تمثيلها عن طريق '\164' (ثمانى) . 't' يمكن تمثيلها عن طريق 'x74' (سداسى عشر) . الجدول التالى يبين الحروف الغير قابلة للطباعة والتى يمكن التعرف عليها من خلال لغة السى : ![]() قوائم السرد Enumerations والتعريفات Definitions : القدرة على قراءة برامج لغة السى هام للغاية . قوائم السرد والتعريفات تمكن المبرمج من استبدال الأعداد بأسماء أو عبارات أخرى تكون أكثر معنى . "قوائم السرد" هى قائمة من الثوابت . تستخدم "الكلمة المحجوزة" enum لتخصيص قيم أعداد صحيحة ثابتة متتابعة لقائمة من المعرفات "المسميات" كما يلى : كود:
int num_val ; //declare an integer variable //declare an enumeration enum { zero_val , one_val , two_val , three_val ) ; num_val = two_val ; // the same as : num_val = 2 ; الاسم zero_val مخصص له القيمة "0" والاسم one_val مخصص له القيمة "1" والاسم two_val مخصص له القيمة "2" وهكذا . القيمة الابتدائية قد تجبر على التحديد كما فى : كود:
enum { start=10 , next1 , next2 , end_val } ; والتى تؤدى إلى أن تكون القيمة الابتدائية بالقيمة "10" وبعد ذلك بالتتابع سوف يتزايد الاسم بواحد . قيمة next1 تكون "11" وقيمة next2 تكون "12" والقيمة النهائية end_val تكون "13" . تستخدم قوائم السرد لاستبدال الأعداد الصرفة , والتى سوف يبحث فيها المبرمج , بكلمات أو عبارات تساعد فى وصف استخدام الأعداد . تستخدم التعريفات definitions بطريقة مشابه إلى حد ما بقوائم السرد , فى أنها سوف تسمح "باستبدال سلسلة نصية بأخرى" . لاحظ المثال التالى : كود:
… … enum { red_led_on = 1 , green_led_on, both_leds_on } ; #define leds PORTB … … PORTB = 0x1 ; // means turn the red LED on leds = red_led_on ; // means the same thing السطر #define leds PORTB يتسبب فى قيام المترجم باستبدال (التعويض عن) المسمى PORTB أينما وجد بالكلمة led . لاحظ أن سطر #define لا ينتهى بالفاصلة المنقوطة وفقط قد يكون به تعليق محاط بالرموز /*….*/ . قائمة السرد تحدد red_led_on بالقيمة "1" وتحدد green_led_on بالقيمة "2" وتحدد كلاهما both_leds_on بالقيمة "3" . هذه القيم قد تستخدم فى برنامج للتحكم فى ليد أحمر وليد أخضر , حيث إخراج القيمة "1" تضىء الليد الأحمر والقيمة "2" تضىء الليد الأخضر وهكذا . التقطة هى أن " leds = red_led_on" أسهل بكثير فى فهم سياق البرنامج عن "PORTB=0x1" . الموجة #define ليس جزء فعلى من بناء جمل لغة السى . التوجيه منفصل عن عملية ترجمة البرنامج حيث يحدث قبل بداية الترجمة الفعلية . 1-5 عمليات الدخل / الخرج I /O Operations : |
#10
|
||||
|
||||
![]()
1-5 عمليات الدخل / الخرج I /O Operations :
المتحكمات الدقيقة المدمجة يجب أن تتفاعل مباشرة مع الأجهزة hardware الأخرى . لذلك , فإن الكثير من عمليات الدخل والخرج تتم عن طريق استخدام "المنافذ المتوازية" المدمجة بها . معظم مترجمات لغة السى توفر طريقة مناسبة للتفاعل مع المنافذ المتوازية من خلال "مكتبة" library أو "ملف رأس" header file . المترجم CCS-PICC يستخدم الأمر #byte لتخصيص مسميات labels لكل منفذ متوازى بالإضافة إلى أجهزة الدخل / الخرج الأخرى . هذا الأمر سوف يتم مناقشته فيما بعد , لكن المثال التالى سوف يساعد على استعراض استخدام المنافذ المتوازية : كود:
#include <16F877.h> //register definition header file for #fuses HS,NOWDT //a Microchip PIC16F877 and fuses unsigned char z ; // declare z void main (void) { set_tris_d(0x00); //set all bits of port D for output set_tris_b(0Xff); //set port B for all input while(1) { z = input_b( ) ; // read the binary value on the //port B pins ( i.e., input from port B) output_d(z+1) ; // write the binary value read from B // plus 1 to port D } } هذا المثال يبين كل من طرق القراءة والكتابة لمنفذ المتوازى . الحرف "z" تم إعلانه كمتغير نوع "بدون إشارة" unsigned وحجم "حرف" char لأن المنفذ هو منفذ 8 بتات 8-bit port . والمتغير من هذا النوع سوف يخزن بيانات بطول 8-bit . تستخدم السجلات TRISx لمعالج الميكروكونترولر PIC لتحديد أى بتات المنفذ ( A,B,… تبعا للمعالج) يتم استخدامها كمخارج . عند الإعادة reset , جميع منافذ الدخل / الخرج تكون فى الحالة الافتراضية كمداخل عن طريق كتابة الميكروكونترولر القيمة "1" على جميع بتات السجلات TRISx . لذلك على المبرمج تحديد بتات TRISx تبعا لأى من البتات تستخدم كمخارج . على سبيل المثال : كود:
set_tris_b(0xc3); //set the upper 2 and lower 2 bits of //port B for input , the rest for output //0xc3=0b11000011 هذا المثال يهىء البتين العلويين والبتين السفليين للاستخدام كبتات دخل والبقية كمخارج . |
![]() |
مواقع النشر (المفضلة) |
أدوات الموضوع | |
انواع عرض الموضوع | |
|
|