![]() |
#11
|
|||
|
|||
![]()
أول برامجنا:
عائلةC51 - أتميل لتسهيل الأمور فى أول برنامج، أعلم أن الغالبية تفضل برنامج بروتس حيث ترسم الدائرة و تحاكيها قبل التنفيذ العملى. حسنا لننشئ مشروع جديد كما بالصورة التالية نستخدم فيه AT89c51 متصلة كما بالصورة. سنلاحظ هنا أن الليدات متصله على منفذ صفر PORT0 من خلال مقاومات متصلة بالموجب. السبب أن منافذ هذه العائلة لا تمد بتيار رغم أن الداتاشيت تقول أنها تستطيع ذلك لكن أيضا عندما تسحب منها تيار تحد فورا من تيار الخرج. باختصار لا تصدر تيار يضئ ليد أو يتحكم فى ترانزستور . لكنه يقبل تيار حتى 20 مللى أمبير. توصل المفاتيح كما بالرسم. أعلم أن البعض سيعترض لكن كل أوجه الإعراض ليست لها ضرورة الآن. فى صفحة البرنامج ستجد العديد من الأسطر كتبها لك البرنامج و كلها تبدأ بالفاصلة المنقوطة ";” و السطر بكاملة باللون الأخضر. هذا لأن هذه الفاصلة جعلت السطر بكاملة "تعليق" أى غير وارد فى البرنامج ولكنه للشرح أو التنسيق. ثم يلى ذلك تعريف أن هذا هو الملف ألرئيسى و يمكنك حذفه. كود:
و يليه تعليمه للمترجم بالميكرو المستخدم وهو 8051 و هو ملف يلحق بالبرنامج به تعريفات لكل المسجلات و المنافذ و محتويات الميكرو الداخلية. بدونه لن تستطيع أن تكتب أمرا واحدا . أيضا مسبوق بعلامة $ و فى مترجم غيره قد تكون علامة # و اسم الملف 8051.MCU و كل مترجم سيضع اسم الملف الصحيح له. كود:
يمكنك حذف التالى أيضا ، سنبقى فقط على السطرين السابقين فقط. كود:
org 0000h هذه اختصار Origin بمعنى نقطة أصل أو نقطة بدء لتقول للمترجم "أريد الكود التالى فى هذا العنوان" وهى احتياطا لو تريد برنامج متقدم سيكون لها جدوى أما فى هذا البرنامج البسيط سيكون الكود فعلا فى خانة صفر ولا حاجة لتكرارها و يمكن حذفها. حتى الآن كل ما سبق هو تعليمات للمترجم ولا ينتج عنها أى كود ولو حاولت أن تعكس المسار لتحصل على التعليمات من الكود، فلن تحصل مما سبق على أى كود ليكتب لك بدلا عنه هذه الأسطر. أول أمر هو JMP start أى اقفز حيث العنوان Start لكى تضع عنوان تذهب إليه وقتما شئت يجب أن يكون من كلمة واحدة قد تحتوى _ لزيادة الإيضاح و أن تبدأ بحرف وليس رقم و ألا تكون من كلمات الكود المستخدمة و أن تكون أول ما فى السطر أو قبلها مسافات بيضاء و أن تنتهى بالنقطتين ":” مثلا كود:
كما ذكرنا البرنامج سيبدأ دوما من خانة صفر و بدون استخدام باقى مكونات المتحكم فلا حاجة لها ويمكن حذفها jmp Start ثم الشرح ألذى يقول قطاع الكود و أيضا تعليمه مشابهة لتحديد مكان كتابة باقى البرنامج org 0100h و تعنى العنوان 100 بالنظام الستة عشرى لوجود الحرف h أما لو b سيكون ثنائى ولو بدون سيكون العشرى المعتاد كود:
أى لدينا ثلاثة أسطر فقط وهنا نبدأ أول البرنامج الفاعل وهذا موضوعنا القادم بإذن الله. |
#12
|
|||
|
|||
![]()
ابتكر أحدهم فى شركة إنتيل استخدام كلمة Move وهى تعنى حرك أو انقل لكى تعنى انسخ ولا أدرى لماذا بينما استخدم نظيره فى موتورولا كلمة Load وهى بمعنى "حمًّل" أو "ضع شيء فى" ، وهى أقرب للمنطق لكن هكذا سارت الأمور و تبنت Mov كل من إنتيل و لاحقا أتميل و أيضا ميكرو تشيب
الاسم Mov هو مختصر ودوما بدون حرف e و يليها المنقول إليه أو الهدف ثم فاصلة "," ثم المنقول منه أو المصدر و لا قيود على من سيكون المصدر و من سيكون الهدف ، محتوى خانة ذاكرة أو عنوان الخانة أو مسجل و بهذا. لو كان المصدر حرفيا بمعنى رقم ما تريد نسخه أو وضعه فى مسجل ما نضع قبله علامة شباك "#” و بدون هذه العلامة سواء فى المصدر أو الهدف فهو عنوان لخانة ذاكرة و ما سيتأثر هو محتواها السطر الأول سيكون Mov P0 , #255 Mov p3 , #255 وهذا يعنى ضع فى المنفذ P0 القيمة 255 لوجود علامة "#” ثم السطر الثانى سيعنى ضع فى المنفذ P3 القيمة 255 لوجود "#” أيضا. هذا يعنى جعل كل أطراف هذين المنفذين عليهما +5 فولت و يمكن استخدامهم كمداخل أيضا. ماذا لو لم نضع الشباك؟ أى هكذا MOV P3 , 255 سيترجمها المترجم انك تريد محتوى خانة الذاكرة رقم 255 حسبما يتصادف محتواه. كود:
Loop: jb p3.0 , SW2test mov P0 , #11111110b SW2Test: jb P3.1 , SW3Test mov P0 , #11111100b SW3Test: jb p3.2 , start mov P0 , #11111000b jmp start ;=========================== END الآن انسخ السطور السابقة فى مترجم بروتس وهذا شرح كل منها. Loop: عنوان و لدينا أيضا عنوانين آخرين هما SW2Test و SW3Test وهى ذات فائدة فى تحديد مسار البرنامج يليه الأمر JB وهو اختصار Jump if Bit set و يعنى اقفز لو البت =1 وهو يليه اسم هذه البت ثم فاصلة ثم إزاحة عدد الخطوات التى يجب الانتقال إليها. هذه الخطوات هى فى بايت واحدة لذا ستكون بإجمالي 255 خانة بين للأمام 128 خطوة و للخلف 127 خطوة. سيكون من الصعب عليك حساب عدد الخطوات فستحتاج لمعرفه كم خطوة لكل تعليمه الخ لذا نستخدم عنوان و نترك المهمة الصعبة للمترجم. إذن السطر التالى يعنى لو البت "المنفذ3 الطرف صفر" =1 اقفز حيث العنوان SW2test وهذا يعنى أنني اختبرت المفتاح SW1 فإن لم يفعل سينتقل للعنوان "اختبار المفتاح2” وهذا من فوائد العنوان أن يكون معبرا عن هدف هذه الخطوة لتذكيرك ماذا أنت ذاهب لتفعل أما لو كان مضغوطا على المفتاح1 سيجعل الطرف = صفر و من ثم لن ينتقل و سينفذ ألتعليمه mov P0 , #11111110b أى سيضع صفرا على الطرف المتصل بالليد الأول ليضئ وهنا كان ممكن أن نكتب MOV P0 , 254 لكن قد تتساءل لاحقا ماذا تعنى 254 ووضعها بالصورة الثنائية أوضح لك أن كل الأطراف آحاد ماعدا الأول بصفر ، ثم نكرر عند العنوان SW2Test حيث نجد نفس ألتعليمه إلا أن البت أصبحت P3.1 أى المنفذ 3 الطرف رقم 1 و القفز للعنوان SW3Test إن لم ينتقل أو يقفز سينفذ الأمر mov P0 , #11111100b و به تضيء ألليد الأول و الثانى. و بعد العنوان الأخير سنجد الاختبار الأخير للبت الثالثة إلا أن القفز للبداية فإن لم يقفز سيضيء الليدات الثلاث ثم يقفز للبداية لتكرار الاختبارات مرة أخرى بالأمر Jmp لتحديد متى يتم الضغط على أى الأزرار. و فى الحقيقة أمر JMP ليست من الأوامر رغم أن كل المترجمات تتبناها و الأمر الصحيح هو AJMP أو LJMP و الفرق بينهم بايت واحدة فالأول AJMP من2 بايت و يعمل على 2 ك من حيث كتبت بينما الثانى LJMP من 3 بايت و يغطى المدى كاملا وهو ما تترجم إليه كلمة Jmp عادة. قبل أن نترك الموضوع تعلمنا الأمر JB و هناك الأمر عكسه وهو JNB أظن توقعت Jump if Not Bit set يعنى سيختبر البت و الانتقال سيكون لو = صفرا بدلا من لو =1 آخر سطر وهو END لا معنى له و كثير من المترجمات استغنت عنه ولكنه مستبقى لأسباب تاريخية فقط. قديما عندما كانت البرامج تدخل للحاسب بالبطاقات المثقبة لم يكن هناك وسيلة لتعلم الحاسب أن هذا آخر كارت برنامج وهذا تمام الكود عكس الآن حيث يقوم نظام التشغيل بهذه المهمة و يحدد لك أول و آخر الملف . لذا كان لابد أن يكون آخر كارت عليه هذا الكود END و إلا سيحصل على رسالة أن الملف غير كامل. أما الآن فسيقول لك لم أجد كلمة END، حسنا وماذا لو وجدتها؟؟ أليست آخر كلمة فى الملف؟ ولو انتهى الملف أليس هذا كاف من نظام التشغيل أن يخبرك بتمام الملف؟؟ الآن جرب محاكاة البرنامج و ستجده يعمل و الليدات تضئ و تطفئ طالما الزر مضغوطا عليه. لكن هناك ملاحظات تتطلب التعديل، حاول أن تكتشفها و تعدلها قبل موضوعنا القادم بإذن الله. |
#13
|
|||
|
|||
![]()
لعلك لاحظت أن المحاكاة كشفت أن الليدات الثلاث تطفئ ثم تضيء مرة أخرى وهذا فى برنامجنا لا شيئ لكن فى الواقع لو هذا فى آله و تقوم بذبذبة جزء منها بهذا المعدل قد تتلفها، إذن ما العيب و كيف نصلحه؟؟
العيب أنك فى كل مرة تقفز إلى Start لتعيد الدورة و فيها تطفئ الكل بدلا من أن تعدل فقط حسب المطلوب لذا نضع عنوان آخر هو Loop وهو فيه الدورة التكرارية كود:
Start: Mov P0 , #255 Mov p3 , #255 Loop: jb p3.0 , SW2test mov P0 , #11111110b sjmp Loop SW2Test: jb P3.1 , SW3Test mov P0 , #11111100b sjmp LOOP SW3Test: jb p3.2 , start mov P0 , #11111000b jmp Loop ;=========================== END الآن بعد الاختبار الأول إن لم يتم الانتقال و أضأنا ألليد سنعود لأول الاختبار مباشرة فلا جدوى من تكملة الاختبارات، بل على العكس عندما نختبر الأخير لا دلالة لدينا عن حالة الأول مثلا و من ثم الأفضل إضافة ألتعليمه sjmp loop لنعيد الاختبارات من الأول لنحدد أى الأزرار مضغوط. نفس الأمر فى نهاية الاختبار الثانى حيث لو لم ينتقل نتيجة لأول اختبار سيختبر الثانى فينفذ أو ينتقل إن نفذ فسيعود لبدء الاختبارات مرة أخرى إن لم ينتقل سيذهب للثالث و الآن وصولة للثالث هو حكم بأن الأول و الثانى لم يضغط على أى منهما لكن عدد الخطوات للانتفال قد تغير. من فوائد العنوان انه عندما يتحرك صعودا أو هبوطا ستحسب الخطوات آليا ولو زادت عن المسموح سيعطى المترجم رسالة خطا لمعالجة الموقف. الآن وباختبار الثالث إن كان غير مضغوط فيجب إطفاء الكل بالانتقال للعنوان start و إن كان مضغوط سنضيء الكل بتنفيذ الأمرMov P0 , #11111000b ثم نقفز لتكرار الاختبارات دون إطفاء. جرب المحاكاة الآن لو حاولت تنفيذ هذا عمليا على بوردة تجميع فلن يعمل لعدم توصيل كثير من الأطراف الهامة و التى لا يعيرها ألمحاكي اهتماما، ولكى تنفذها يجب أن تكون الدائرة هكذا. ألمرة القادمة بإذن الله سنكرر نفس البرنامج مع بيك. |
#14
|
|||
|
|||
![]()
ميكروتشيب PIC16F877A
الآن سننفذ ذات الدائرة على بروتس باستخدام PIC16c877A متحكم و معه ذات المكونات و الدائرة كما بالرسم. سنلاحظ اختلاف فى توصيل الليدات و السبب أن منافذ ألبيك قادرة على الإمداد بالتيار عكس أتميل التى تقبل فقط. نلاحظ أيضا أننا لن نتعامل حاليا مع منفذي A,E لارتباطهما بالمحول تماثلى رقمى و يجب تهيئتهما للتعامل كمنفذ رقمى فقط. يمكننا استخدام ألباقى وسنختار المنفذ D . المنفذ B يحتوي مقاومات جذب للأعلى Pull-up وهى خاصية ضرورية لأن المنفذ عندما يهيأ كدخول ستكون له معاوقة دخول عالية و من ثم الجهد عليه غير محدد و عند قراءته كطرف رقمى قد يقرأ 1 ثم يقرأ صفر بلا انتظام ولكنها توضع للأطراف الثمانية معا أو تحذف معا ولا ينصح باستخدامها لو البعض دخول و الباقى خروج و هذا يعالج بوضع المقاومات هذه خارجيا وهى R4,R5,R6 و عادة ما تكون 10 ك أوم , ولذا لم يعد مهما استخدام المنفذ B كدخول أو خروج و سنختار كما هو بالرسم. بمراجعة صفحة الكود سنجد كما سبق كثير من الأسطر لا حاجة لها الآن سوى سطر كود:
RST code 0x0 بعد ذلك نبدأ بتهيئة المنفذ B كخروج و المنفذ D كدخول و هذا بتنفيذ الأمر TRISB=0 و يكتب هكذاgoto Start PGM code كود:
CLRF STATUS ; Bank0 CLRF PORTB ; Initialize PORTB by clearing output data latches CLRF PORTD ; Initialize PORTD by clearing output data latches BSF STATUS, RP0 ; Select Bank1 CLRF TRISB ; All OUTs حيث CLRF تعنى Clear أو إلغاء محتوى المسجل المشار إليه بالحرف f وهو هنا STATUS وهذا لاختيار المجموعة صفر حيث لا يمكن للبيك أن يتعامل مع الذاكرة ككل و يقسمها لمجموعات كل منها تسمى بانك أو مجموعة أو BANK و نختار Bank0 لجعل المخارج أصفار بالأمرين CLRF PORTB ثم CLRF PORTD بعد ذلك نختار بانك رقم 1 بالأمر BSF STATUS, RP0 لتهيئة المنافذ بعد ذلك بالأمر CLRF TRISB لجعل المنفذ B مخارج. لتهيئه المنفذ D كدخول نحتاج لأمر مشابه للسابق أى TRISD=255 و المشكلة أنك لا تملك أمر مماثل لأتميل حيث تكتب ما تشاء حيث تشاء كيف تشاء لكن يمكنك فقط كتابه ما تشاء فى المسجل W ثم تنسخه حيث تشاء بعد ذلك، ولهذا تحتاج لأمرين هما MOVLW 255 MOVWF TRISD حيث الأول Move Literal W أو اكتب حرفيا فى المسجل W و القيمة المطلوب كتابتها هى 255 أى 11111111 ثنائى ثم نسخها للمسجل TRISD بالأمر التالى MOVWF أو انقل محتوى W للمسجل F و المسمى هنا TRISD و أخيرا الأمر CLRF STATUS ; Bank0 لنعود للبانك صفر مرة أخرى لنكتب فى المنافذ. نبدأ البرنامج بالعنوان كود:
Loop: BTFSC PORTD , 0 GOTO Sw1 MOVLW 1 MOVWF PORTB GOTO Loop BTFSS f, b Bit Test f, Skip if Set و هى تعنى اختبر البت f و لو =1 لا تنفذ الأمر التالى مباشرة و عكسها BTFSC f, b Bit Test f, Skip if Clear و هى تعنى اختبر البت f و لو =0 لا تنفذ الأمر التالى مباشرة وهذا الأمر التالى طبعا يجب أن يكون أمر انتقال لمكان ما GOTO xxx وهكذا يصبح اختبر المنفذ PORTD الطرف صفر فإن كان =1 انتقل لعنوان Sw1 و إلا فالزر مضغوط ولذا نكتب 1 فى المسجل W ثم ننسخه للمنفذ B و نعود للعنوان Loop لنكرر الاختبار. أما لو انتقلنا إلى Sw1 سنجد كود:
Sw1: BTFSC PORTD , 1 GOTO Sw2 MOVLW 3 MOVWF PORTB GOTO Loop كود:
Sw2: BTFSC PORTD, 2 GOTO SW3 MOVLW 7 MOVWF PORTB GOTO Loop كود:
SW3: CLRF PORTB GOTO Loop ;=========== END من الواضح أن كم التعليمات أكثر بكثير مما استخدم مع السابق C51 وهذا راجع لقله الأوامر المتاحة فضلا عن كثير من التعليمات لا تستنتجها وحدك لبدء التهيئة و التشغيل. لذا فألبيك يفضل دوما برمجته باستخدام اللغات العالية مثل البيزك أو C. فهذا البرنامج البسيط تطلب العديد من التعليمات ولو كان البرنامج أكثر تطورا ربما أصبح من الصعب تنفيذه بالأسيمبلى بألبيك . يجب أن نتذكر هنا أننا باستخدام لغة ما غير الأسيمبلى قد تضيف كثير من التعليمات التى كان من الممكن اختصارها بالأسيمبلى.، لكن هناك أمر لا يسهل التغاضي عنه وهو استخدام مكتبات الأرقام العشرية و أنا شخصيا لو احتاج لعدد ثابت من الأرقام العشرية استخدم الحساب بدلا من المكتبات الجاهزة لاستهلاكها لذاكرة البرنامج فمثلا لو احتاج لرقمين صحيحين و كسرين عشريين أعتبر الحسابات ستكون على رقم أعلاه 9999 صحيح و استخدم له 2 بايت (حتى 65000) أو بالإشارة +/-32000 ثم فى عرض الرقم أضيق علامة عشرية بين الثانى و الثالث. من المثال السابق نستخلص منهجا لما سيلى وهو أننا يمكننا مناقشة الأسيمبلى لمنتجات أتميل C51 فقط بينما AVR , ميكروتشيب سنكتفي باللغات العالية وذلك لكون الأسماء قد تغيرت و أيضا ذاكرة البرنامج زادت حتى سهل استخدام الحساب بالأرقام العشرية و من ثم اقتصرت البرمجة بالأسيمبلى على السرعة أولا و تقليل الحجم إن اضطر الأمر . المرة القادمة إن شاء الله سنحاول كتابة هذا البرنامج باللغات العالية |
#15
|
|||
|
|||
![]()
تمهيد للغات العالية:
التعريفات: قبل أن نتناول اللغات العالية يجب أن نتعرض لما تركناه فى الشرح السابق و هو يخص البرمجة بشكل عام سواء بالأسيمبلى أو أى من اللغات العالية وهى الأجزاء الثلاث التالية كود:
;================================== ; DEFINITIONS ;================================== أغلب المترجمات تفترض أنك تضع هذا فى أول البرنامج دوما لأنه سيحتاج هذه المعلومات لاحقا و القلة فقط تدعم الإعلان لاحقا فهى ستحتاج لمسح الملف كاملا مرة لتحديد كل التعريفات ثم تبدأ فى التحليل . لنعد للبرنامج السهل الأول سنجد كود:
Start: Mov P0 , #255 Mov p3 , #255 Loop: jb p3.0 , SW2test mov P0 , #11111110b sjmp Loop SW2Test: jb P3.1 , SW3Test mov P0 , #11111100b sjmp LOOP SW3Test: jb p3.2 , start mov P0 , #11111000b jmp Loop أجل هذا القسم هو لهذا الغرض فيمكنك مثلا كتابة كود:
Switches EQU P0 LEDs EQU P3 SW1 EQU P0.0 SW2 EQU P0.1 SW3 EQU P0.2 Switches Alias P0 هكذا تعطى اسم للمنفذ ككل و اسم لكل طرف إن شئت، بل لو عرفت المنفذ أولا يمكنك استخدام اسمه لاحقا للتسهيل أكثر هكذا SW1 EQU Switches.0 فى ميكروإلكترونيكا Symbol Sw1 = Switches فى لغةC نجد تعليمه للمترجم هى كود:
#define My_Name System_Name كود:
#define Switches PORT0 هناك نقطة أخرى بالغة الأهمية أيضا، فماذا لو بعد الانتهاء من البرنامج و بدأت فى رسم البوردة وجدت تعارض فى المسارات ووجدت أنه من الأفضل استخدام منفذ آخر أو طرف آخر. من الأفضل أن تتحمل الخطأ فلو حاولت مراجعة البرنامج كاملا لتغير هذا الطرف بالتأكيد ستنسى و تعانى الأمرين حتى تجعله يعمل مرة أخرى. باستخدام هذه المسميات سيكفى أن تغير التعريف فى أول البرنامج وهو سطر واحد فقط ليعمل البرنامج كما يجب على الوضع الجديد بالأطراف الجديدة. أليست هذه نعمة؟! الآن الخبر الجيد أن هذه المسميات تكون للمترجم فقط ولا ينتج عنها أى كود لذا لا تبخل فى جعل الاسم مناسبا لا هو قصير جدا ويصعب تذكره مثل X,C فلن تذكر لاحقا X يعنى ماذا ولاC أيضا، ، ولا تجعل الاسم طويلا فيصعب عليك كتابته كل مرة داخل البرنامج مثل Liquid_Crystal_Data_Port و يكفى مثلا LCD_Data أيضا هذه الطريقة لا تشترط على ماذا تطبق فيمكنك تطبيقها على ثوابت مثل NoOfLoops=25 أو حتى على معادلة أو كود مثلا كود:
PressTime Alias delay (25) بلغة ميكرو سى نستخدم الصورة التالية ولاحظ أن ميكروإلكترونيكا هى الشركة الوحيدة التى تستخدم تعريفين bit , sbit , فى كل منتجاتها وتنصح باستخدام sbit دوما. كود:
sbit SW1 at RC5_bit; char Switches at PORT0 ; البعض يقول تحديد نهاية السطر، حسنا هذا كان ضروريا وقت ما كنا نقرأ من الكروت المثقوبة لنحدد بها أى ";” نهاية السطر أما الآن فهناك فى الويندوز 2 بايت فى نهاية كل سطر هما CR,LF وهما كود 10 و كود 13 أما فى أبل أو يونكس / لينيكس و ملفات الأردوينو فهى إما الأولى أو الثانية فقط حيث CR اختصار Carriage Return أى رجوع العربة (الحاملة للورقة) أى الرجوع لأول السطر و LF اختصار Line Feed أى تحرك لسطر جديد ... أى أن هناك نهاية سطر مزدوجة فى كل سطر ولا حجة لهذا العذر و لكن لها ميزة عظيمة فى إرباك المبرمج خاصة أنها أحيانا لو وضعت تسبب إرباك المترجم ولا يعطى رسالة خطأ حتى ، فقط يعطيك برنامج كما تخيله هو ، ولو نسيتها فكثيرا ما يكتشفها لكن ليست دوما. السطر الأول يعنى المفتاح SW1 عند الطرف رقم 5 من المنفذ C من المسجل PORT السطر الثانى يعنى تخصيص اسم Switches للمنفذ0 الثوابت: نفس مفهوم الأسماء يمكن امتداده للقيم الثابتة رغم عدم ظهورها فى المثال السابق إلا أنك قد تحتاج للتعامل مع 25 مدخلا و تحتاج دورة فيها 25 للقراءة ثم أخرى ربما للحساب و ثالثة للمقارنة ورابعة للعرض على الشاشة الخ فستجد نفسك فى عديد من الدورات كلها 25 مرة و مثال آخر أنك تحتاج تكرار أمر ما عدة مرات لتحصل على نتيجة ثابته أو تتأكد من ثبات القيمة فعادة ما نضع رقم لكن بنفس القياس لو احتجت لتغيير الدورات فى المثال الأول بالنقصان أو الزيادة سيكون ذلك كابوسا لكن لو استخدمت التعريف السابق فى إعطاء اسم للرقم فكل ما عليك هو تعديله فى أول البرنامج مرة واحدة كل مترجم له طريقته فى هذا فالبعض يستخدم طريقة EQU السابقة أو Alias أو يجعلها واضحة باستخدام اللفظ "ثابت" أو Constant و اختصارا Const هكذا Const Loops = 25 Const repeat = 50 نفس الخبر الجيد السابق ينطبق هنا فهذه الثوابت لا تأخذ من ذاكرة المتحكم لأنها لا تحفظ فى البرنامج و يقوم المترجم باستبدال كل كلمة Loops بالعدد 25 و هكذا و فى النهاية يقوم بالترجمة باستخدام الأعداد و ليس الأسماء و سنوضح الفروق بالأمثلة على لغتى البيزك و السي.. لكن هل من الممكن أن نعدل هذه القيمة بحسب الحاجة؟ هذا يسمى "المتغيرات" لكن لتنوعها سنجعلها المرة القادمة بإذن الله |
#16
|
|||
|
|||
![]()
المتغيرات
نأتى الآن للجزء التالى من التعليقات التى سبق حذفها وهى باسم المتغيرات كود:
كن حذرا هنا فما لم تكن تتعامل مع الأسيمبلى فكل تخصيص لمتغير أنه بت واحدة ، سيحجز المترجم مقابلها بايت كاملة !!. يجب أن تعلن صراحة عن المتغيرات التى ستستخدمها و إلا ستلقى رسالة خطأ و يتوقف عن الترجمة. فى الأسيمبلى تستخدم كلمه EQU السابقة أيضا، ستقول كيف؟ سأقول كما سبق أن ذكرنا هى تعنى هذا يساوى أو يستبدل بذلك فلو تصف خانة ذاكرة إذن هى متغير لأن محتواها يأخذ أى قيمة من صفر إلى 255 و أذكر أننا قلنا فى ألتعليمه Mov P0 , #255 ` ذكرنا أن "#” تضع 255 على المنفذ لكن لو حذفناها سيأخذ محتوى الذاكرة 255 حسبما كان هذا و يضعه على المنفذ ، بهذا لو كتبت ألتعليمه Days EQU 50 فهذا يعرف المترجم أن عليه استبدال كلمة Days بالقيمة 50 فى الأمرين التاليين Mov P0 , #Days Mov P0 , Days ومن ثم فى السطر الأول سيضع حرفيا على المنفذ صفر قيمة 50 أما فى السطر الثانى سيأخذ محتوى خانة الذاكرة رقم 50 و يضعها على المنفذ. لو تريد تخصيص Word وهى من 2 بايت سيخصص المترجم المكان الأول الذى أشرت إليه فى الأمر و التالى له آليا. لا يوجد فى الأسيمبلى أكبر من 2 بايت إلا بتخليقك أنت لما تحتاج أو تستخدم مكتبة خارجية. فى لغة البيزك تستخدم كلمة Dim و هى اختصار Dimension بمعنى "يصنع وفقا لأبعاد محددة" وهى بلغة الحرفيين كلمة "يواصف" أى يعطيه المواصفات و أكثر المترجمات تسمح بتسمية عدد من المتغيرات المتساوية الحجم مثلا Dim Loop , Cycle , X , T as byte وهى هنا تقول أصنع متغيرات واحد باسم Loop وآخر باسم Cycle وآخر باسم X وآخر باسم T كل منها بايت واحدة و لتسمى آخر بحجم 2 بايت يجب أن تعلن من جديد باستخدام Dim فى لغة C تضع النوع أولا هكذا كود:
أصغر متغير هو البت الواحدة وفى عائلة C51 يوجد منها 2 بت للمستخدم و للأسف لا يوجد مثلها فى أى من المتحكمات الأخرى. داخل كل متحكم يوجد ما يسمى مسجل حالة البرنامج أو Program Status Word و اختصارا PSW وهذا اسمه أى لو تريد الكتابة فيه أو القراءة منه تستخدم الاسم PSW.1 مثلا لقراءة البت رقم 1 الصورة لعائلة C51 حيث نجد فيها 2 بت للمستخدم الأولى باللون الأصفر و اسمها F0 أى يمكنك أن تستخدم هذا الاسم مباشرة هكذا JB F0, Next_Action أو تعيد تسميتها بالتعريفات السابق شرحها و الثانية باللون البرتقالى وهى بلا اسم لذا يمكنك استخدام PSW.1 أو تعريفها بأي اسم مناسب . باقى الخانات سيلى شرحها لاحتوائه على كثير من الأمور الهامة. لو اخترت أى بت أخرى سيخصص المترجم بايت كاملة لها. لو ستستخدم مترجمات ميكرو الكترونيكا تذكر أنها تسمى sbit الوحدة الثانية من المتغيرات هى البايت وهى 8 بت وهى تعبر عن حرف من كلمة أى حرف هجائى أو عدد من صفر وحتى 255. بعض المترجمات خاصة فى اللغات العالية مثل ميكرو الكترونيكا تسمى الحرف Chr اختصار Character و تسمى أيضا Short اختصار Short-Integer و من كل تضع بلا إشارة UN-signed Character أو بالإشارة signed Character و تصبح أعلى بت فيها رقم 7 تمثل إشارة الرقم. لو صفر يكون العدد موجبا و لو 1 يصير سالبا و بهذا تعبر عن +127 إلى -128 فقط بدلا من صفر إلى 255 أيضا Signed short int أو Un-signed short int لنفس المدى أما مترجم BASCOM فيستخدم Byte من صفر إلى 255 التعديل الأخير تم بواسطة ماجد عباس محمد ; 06-17-2018 الساعة 10:04 AM |
#17
|
|||
|
|||
![]()
الثالث هو 2 بايت أى 16 بن و يسمى فى ميكرو الكترونيكا Int اختصار Integer و منهsigned Integer
من -32768 إلى +32767 ومنه Un-signed Integer من صفر و حتى 65535 أما مترجم BASCOM فيستخدم Integer من -32768 إلى +32767 و Word من صفر و حتى 65535 وهذه من التعبيرات المستخدمة فى الميكرو بروسيسور وقبلها عند أول تطبيقات الحاسبات الرابع من 4 بايت و يسمى فى ميكرو الكترونيكا Long وهو من -2147483648 إلى +2147483647 و أيضا Un-signed Long وهو من صفر إلى 4294967295 أما مترجم BASCOM فيستخدم Long وهو من -2147483648 إلى +2147483647 و أضاف لنسخة AVR الكلمة DWORD وهو من صفر إلى 4294967295 الخامس الأرقام العشرية و يسمى فى ميكرو الكترونيكا Float أو Double أو Long Double و كلهم ذات الشيء من 4 بايت للمدى من 1.5x10^–45 إلى 3.4x10^38 أما مترجم BASCOM فيستخدم Single من 4 بايت أو 32 بت للمدى من 1.5x10^–45 إلى 3.4x10^38 و أضاف لنسخة AVR الكلمة Double من 8 بايت أو 64 بت للمدى 5.0x10^–324 إلى 1.7x10^308 وسادسا النصوص وهى حتى 254 حرف باسم String لذا يجب أن تراجع ملف المساعدة للمترجم الخاص بك. بقى آخر جزء وهو المقاطعة و سنشرحه تفصيلا فى حينه المقاطعة و البدء: الكل له مكان محدد للبدء حيث يجب أن تضع أين يبدأ برنامجك وهى خانة صفر فى أول الذاكرة، لذا قد تكون بداية برنامجك فعلا أو انتقال لحيث تضع برنامجك فى الذاكرة. أما فى المقاطعة فكما ذكرنا سابقا أن أتميل لها عنوان خاص بكل مقاطعة بينما ميكروتشيب لها عنوان واحد فقط تضع فيه عنوان برنامج معالجة المقاطعة تكون فى خانة 4 حيث تضع عنوان معالجة المقاطعة تتعرف من أين أتت ثم تقوم بالمطلوب أما فى أتميل ستجد لكل مقاطعة خانة حيث تضع عنوان معالجة المقاطعة الخاصة بهذا المصدر مما يوفر عليك تحديد مصدرها. كود:
;================================== ; RESET and INTERRUPT VECTORS ;================================== كود:
; Interrupt vectors: (0000H): ; Reset system الرقم بين قوسين هو بديل للتعليمه السابق شرحها Org (0000) H أوتعنى ضع الكود التالى فى هذا العنوان وهو صفر و حرف H يعنى أنه هيكسا ولو لم يذكر سيترجم عشرى. لو لم تستخدم أى من المقاطعات التالية فيمكنك وضع أول كود برنامجك هنا (فى خانة الصفر) لكن لو ستستخدم أى من المقاطعات التالية يستحسن وضع أمر انتقال لبداية البرنامج مثل Ljmp Start غنى عن التعريف أن Start هو عنوان لاحق فى البرنامج أول مقاطعة من الطرف الخارجى INT0 بعد 3 خانات من الريسيت وهى كافية لأمر القفز لكن كل عنوان مقاطعة بعد ذلك يبعد 8 خانات عن السابق له ، لذلك لو ما تحتاجه لخدمة المقاطعة يمكن أن تكفيه 8 خانات مثل قراءة منفذ تسلسلى أو إعادة تحميل عداد الخ ،يمكنك الاكتفاء بهذه الخانات و إلا ستحتاج لأمر القفز لعنوان آخر. كود:
(0003h) : ; Adress of INT0 (IE0) Ljmp Int0Code (000Bh): ; Adress of Interrupt Timer 0 (TF0) Ljmp T0Code (0013H) ; External 1 (IE1) Ljmp Ext1Code (001BH) ; Timer 1 (TF1) Ljmp T1Code (0023h): ; Serial Interrupt Ljmp SiCode (002Bh): ; Timer2 (TF2 or EXF2) ;Timer2 code if used...... يمكنك وضع كود تايمر2 هنا مباشرة ولا حاجة لأمر انتقال إلا للضرورة . . Start: . . T0Code: . . Int0Code: . . SiCode: . . T1Code: عندما تحدث مقاطعة يحفظ المتحكم عنوان الخطوة التالية فى الرصة Stack ثم ينتقل للعنوان المذكور و ينفذ ما به و إن وجد به أمر قفز للعنوان الخاص بهذا الطالب للخدمة و هناك ، أنت تعلم ما ستستخدم من مسجلات ، لذا لو تريد فى برنامج المقاطعة استخدام أى من المسجلات فمن الحكمة أن تحفظها أيضا خاصة مسجل حالة البرنامج PSW لأنه يحتوى مؤشرات ناتج الحسابات و تحفظ هذه المسجلات فى الذاكرة لكن الأسلم و الأسرع بأمر "دفع فى الرصة" Push و عكسه استرداد من الرصة POP فهو يحفظ الترتيب خاصة مع المسجلات 16 بت كود:
Push direct ; direct is any register or memory POP direct ; '' '' فى ميكرو تشيب الأمر أكثر تعقيدا لأن لديك مكان واحد تنتقل إليه و هناك عليك أن تبحث أى المصادر سبب هذه المقاطعة و مما يزيد الأمور سوءاً أن البت الخاصة بالمقاطعة ستكون 1 بصرف النظر عن أن هذه المقاطعة متاحة أصلا أم لا ولا أن المقاطعة كلها أصلا متاحة أم لا لذلك من الأفضل تركها للغات العالية حيث تولد الكود اللازم للتحقق. الآن بعد أن عرفنا التعريفات و الثوابت و المتغيرات يمكننا أن نعيد صياغة برنامجنا السابق باللغات العالية، بالبيزك مرة و C مرة وهذا موضوعنا القادم بإذن الله. |
#18
|
|||
|
|||
![]()
اللغات العالية :
ربما نسينا برنامجنا السابق لذا ها هو مجددا و بعد إضافة المسميات كود:
Switches EQU P0 LEDs EQU P3 SW1 EQU P3.0 SW2 EQU P0.1 SW3 EQU P0.2 Start: Mov Switches , #255 Mov LEDs , #255 Loop: jb SW1 , SW2test mov LEDs , #11111110b ; = 254 sjmp Loop SW2Test: jb SW2 , SW3Test mov LEDs , #11111100b ; = 252 sjmp LOOP SW3Test: jb SW3 , start mov LEDs , #11111000b ; = 248 jmp Loop و أظن أنه أصبح مفهوما بصورة أوضح. هل يمكننا وضعه فى كلمات لشرح خطواته؟ نعطى منفذ الليدات اسم LEDs ونعطى السويتشات اسم Switches و السويتش الأول اسم SW1 وهكذا ضع على منفذ السويتشات 255 و على منفذ الليدات 255 العنوان دورة: لو المفتاح Sw1 =1 عندها انتقل للعنوان SW2test و إلا ضع على الليدات 254 ثم عد للدورة العنوان SW2test: لو المفتاح Sw2 =1 عندها انتقل للعنوان SW3test و إلا ضع على الليدات 252 ثم عد للدورة العنوان SW3test: لو المفتاح Sw3 =1 عندها انتقل للعنوان start و إلا ضع على الليدات 248 ثم عد للدورة هل نترجمه للإنجليزية؟؟ لو = IF ، و إلا = Else و عندها = Then و إعطاء اسم هى Dimension لآنها تعنى اسم و حجم معا أو حيز أو صنع شيء طبقا لمواصفة محددة أما استخدام اسم بديل فهو Alias . كثير من المترجمات تفضل البدء بكلمة Main بمعنى أساسى ، لا بأس فهى تعرفك أنت أين يبدأ البرنامج و دوما ما تحتاج عنوان فى بداية البرنامج لتكراره. كود:
Main: Switches = 255 LEDs = 255 Loop: If Sw1 = 1 then Goto SW2test Else LEDs = 254 Goto loop End If حيثما تريد كتابة أكثر من تعليمه فى أى من الجزأين فإما تكتب كل منهما فى سطر مستقل كما سبق أو تفصل بينهما بالحرف ": “ كما فى المثال التالى أو ربما المترجم يتبنى حرف آخر، فلنستخدمه إذن. طبعا هنا ستقول أن كلمة "عندها أو Then “ زائدة لكن ستجد ضرورتها لو كتبتها فى سطر واحد هكذا If Sw1 = 1 then Goto SW2test Else LEDs = 254 : Goto loop فهى تفصل بين كود الشرط و ألذى قد يكون معادلة طويلة و بين الأوامر الواجب تنفيذها عند تحقق الشرط كما Else تفصل بين كود التحقق و كود عدم التحقق – كل كلمة لها مكانها و ضرورتها و مفهومة إن نسيت بمقدورك أن تستنتجها و تركز فى البرنامج بدلا من التركيز على ما لا جدوى منه من أقواس و فواصل منقوطة وهل الفاصلة قبل القوس أم بعده.. وفى نمط السطر الواحد سنستغنى عن حد الختام وهو End If لأنها تفصل بين كود Else و باقى البرنامج. هكذا يمكننا أن نكتب باقى البرنامج بالسطر الواحد هكذا كود:
SW2test: If Sw2 = 1 then Goto SW3test Else LEDs = 252 : Goto loop SW3test: If Sw3 = 1 then Goto Start Else LEDs = 248 : Goto loop طبعا لو عكسنا المنطق سنوفر قفزة من الاثنتين (الأولى) بحكم أنها للمكان التالى مباشرة فيمكننا القول كود:
If Sw1 = 0 then LEDs = 254 : Goto loop If Sw2 = 0 then LEDs = 252 : Goto loop If Sw3 = 0 then LEDs = 248 : Goto loop Goto Main لاحظ أن البرنامج الأفضل هو ألذى يقوم بمهام أكثر بكود أقل فى زمن أسرع. و لنكتبه بلغة C أولا يجب أن تلتزم بالأحرف المكتوبة بمعنى لو كتبت Loop ثم لاحقا goto loop سيعترض أن العنوان غير موجود. هذا من بقايا نشأة اللغة فالفرق بين الحرف الكبير و الصغير هو البت رقم 5 فمثلا حرف A = 0100 0001 بينما حرف a=0110 0001 وهكذا فى باقى الأحرف و لتوحيد المقاس يلزم تنفيذ أمر على كل الحروف مثل OR x,#0010 0000 لجعل الحروف صغيرة أو AND x,1101 1111 لجعلها كبيرة وهذا كان رفاهية و إسراف عندما كانت ذاكرة الحاسبات تحسب بالبايت و تشترى بالبت و حتى لاحقا عندما حسبت بالكيلو بايت. أيضا كل الأوامر تكتب بالأحرف الصغيرة ولو كتبت أى حرف كبير لن يتعرف عليها المترجم. هذا قد يوقعك فى مشكلة لو أخطأت فى حجم حرف فى اسم متغير فى برنامج من عدة صفحات حسب المترجم المستخدم. سنجد أن البداية بالتعريفات كما سبق الشرح ثم بكلمة Void وهى تعنى "خلاء" أو فراغ أو مساحة و كلمة main ضرورية للبدء ثم قوسين لتحديد أنك لا تضع بينهما أى بيانات مطلوب معالجتها ثم القوس المعوج ألذى يحدد البداية و مثله للنهاية كود:
void main() { TRISA = 0xFF; // PORTA is input TRISC = 0; // PORTC is output Loop: if (Sw1=1) { Goto SW2test ; } else { LEDs = 254 ; Goto loop ; } كود:
SW2test: if (Sw1 = 0 ) { LEDs = 254 ; Goto loop ; } if (Sw2 = 0) { LEDs = 252 ; Goto loop ; } if (Sw3 = 0) { LEDs = 248 ; Goto loop ; Goto Start ; } أيضا التعليمات بالبيزك و السى أعطت ذات الكود أى لا توجد لغة أقرب للمكونات من الأخرى. |
#19
|
|||
|
|||
![]()
صورة أخرى من ألتعليمه IF
بعض الشركات تحبط استخدام الأمر GOTO و تفضل عدم استخدامه و مثال لذلك تقترح الصورة المتداخلة من الأمر IF وتسمى Nested IF وهى أنك بعد الجزء ELSE تضع الشرط التالى مباشرة وهى قد تبدو منطقية و أسهل فهما و لنجرب ذلك فى المثال السابق حيث كان البرنامج بعد التعريفات كود:
Start: Switches = 255 LEDs = 255 Loop: If Sw1 = 0 then LEDs = 254 : Goto loop If Sw2 = 0 then LEDs = 252 : Goto loop If Sw3 = 0 then LEDs = 248 : Goto loop Goto Start باستخدام الطريقة المتداخلة تصبح كود:
Start: Switches = 255 LEDs = 255 Loop: If Sw1 = 0 then LEDs = 254 Else If Sw2 = 0 then LEDs = 252 Else If Sw3 = 0 then LEDs = 248 Else LEDs = 255 ; السطر الأخير End IF Goto Start سينشئ المترجم آليا أمر انتقال لنهاية الدورة حيث تكتب END IF أو تضع القوس المعوج فى لغة C وهذا سيضيف المقابل للسطر الأخير أمر وضع 255 على المنفذ و انتقال لأخر الدورة بدلا من Start و قبل أن نطور برنامجنا السابق و نتعمق أكثر نرى كيف نطبق هذا باستخدام النوعين الآخرين وهذا موضوعنا القادم بإذن الله |
#20
|
|||
|
|||
![]()
البرنامج باستخدام AVR
باستخدام الأسيمبلى سنحاج لتعديل البرنامج كله فقد تغير الأمر MOV ليصبح LDI اختصار LOAD Immediate أى تضع قيمة ما مباشرة فى مسجل و LDS لنسخ ذاكرة إلى مسجل و عكسها STS Store Data to space فضلا عن باقى التعليمات أيضا كما أن المنافذ هنا قد تغيرت و أصبح لكل منفذ 3 مسجلات واحد للخرج و يسمى PORTx و الثانى للاتجاه دخول/خروج و يسمى DDRx و الثالث لحالة الطرف و يسمى PINx كما سبق الشرح ، لذا سيكون أصعب بقليل كتابة هذا البرنامج لكنه ممكن ، و نظرا لكبر الذاكرة المتاحة و الرغبة فى التعامل مع الأرقام العشرية سيكون من الأفضل الاكتفاء باللغات العالية كما علمنا سابقا أن منافذ AVR أصبحت تعطى تيار أو تقبل تيار مشابه لمنافذ ألبيك و عكس C51 لذا يمكن أن نتبنى الأسلوبين أى المطابق للسابق أو مشابه للبيك هكذا مع مراعاة تغيير أوامر إضاءة ألليد من صفر إلى 1 وأذكر هذا تمهيدا للتعامل مع الريلايات لاحقا أيضا لم أوصل طرف RESET لأنه يمكن الاستغناء عنه باختيار خاصية Brown out detect مع فولت 2.7 فعندما يهبط جهد التغذية لهذه القيمة يحدث ريست داخلى آليا ولا حاجة للتوصيل الخارجى لكن إن شئت فليكن، أيضا لم أوصل الكريستال لأنه يمكن استخدام المولد الداخلى 1 ميجا أو 2 أو 4 أو 8 ميجا و إن كان دقيق فى حدود 1% وهو فى نظرى كاف لكثير من التطبيقات ما عدا الوقت الدقيق ألذي يحتاج أكثر من هذه الدقة أو لو تستخدم التواصل التسلسلى. باستخدام البيزك و مراعاة المسجلات الثلاث السابقة سيصبح البرنامج كود:
$regfile = "m16def.dat" ' تحديد الميكرو $Crystal=4000000 ' تردد الكريستال $hwstack=40 ' تحديد المرصوصة $swstack=16 ' تحديد المرصوصة $framesize=32 ' ذاكرة كمسودة لبعض ألدوال الأول تحديد ملف التعريف بالميكرو Atmega16A الثانى تردد الكريستال وهو لا يهم إلا فى حساب الكود اللازم للمنفذ التسلسلى و حساب التأخير المطلوب فى بعض الوظائف الثالث تخصيص مرصوصة الانتقال و المقاطعة الخ وتسمى Hardware أو مرصوصة المكونات الرابع تخصيص مرصوصة تسمى Software أو مرصوصة البرامج وهى مطلوبة للمترجم لبعض العناوين الخامس كمسودة لحسابات يحتاجها المترجم لبعض ألدوال كتنسيق النصوص الخ كود:
Leds Alias PORTA : Leds_dcn Alias DDRA ' تسميه المنافذ و المفاتيح و مسجل الاتجاه Switches Alias PORTD : Switch_dcn Alias DDRD Sw1 Alias PIND.0 : Sw2 Alias PIND.1 : Sw3 Alias PIND.2 ' تسميه المفاتيح لاحظ استخدام PIND بدلا من PORTD Main: Leds_dcn = 0 : Switch_dcn = 255 Start: Switches = 255 Leds = 255 ' enable Pullup Loop: If Sw1 = 0 then LEDs = 254 : Goto loop If Sw2 = 0 then LEDs = 252 : Goto loop If Sw3 = 0 then LEDs = 248 : Goto loop Goto Start نلاحظ أن البرنامج لم يختلف كثيرا لدرجة أننا لم نحتاج لإعادة رسم الدائرة فيمكننا أن نستخدم ذات الأطراف فقط نراعى أن أطراف التغذية و الكريستال و الريسيت قد تغيرت أرقامها. المرة القادمة إن شاء الله نعيده بالبيك 16 و 18 |
![]() |
مواقع النشر (المفضلة) |
أدوات الموضوع | |
انواع عرض الموضوع | |
|
|