لغة مجمع الميكرومراقب
يشرح الدرس لغة المجمع للميكرومراقب PIC16F84A: بنية السطر وأنظمة العدّ، صيغة التعليمة وترميزها في 14 بتاً، التعليمات الـ35 بعائلاتها ودوراتها، الأعلام Z وDC وC، التوجيهات LIST و#include و__CONFIG وEQU وORG وEND، ويُختم بتمارين محلولة بنمط البكالوريا.
يتكوّن الدرس من 7 أجزاء: 1. لغة المجمع: من البرنامج المصدر إلى الشفرة الآلية، 2. صيغة التعليمة وترميزها وزمن تنفيذها، 3. سجل الحالة STATUS والأعلام وتعليمات الحساب، 4. تعليمات النقل والمنطق والتدوير، 5. تعليمات البت والقفز والتحكم في سير البرنامج، 6. التوجيهات وبنية الملف المصدر، 7. تمارين محلولة بنمط البكالوريا.
1. لغة المجمع: من البرنامج المصدر إلى الشفرة الآلية
لماذا لغة المجمع؟
الميكرومراقب (microcontrôleur) PIC16F84A لا يفهم إلا الشفرة الآلية (code machine): كلمات ثنائية طول كل منها 14 بتاً، مخزّنة في ذاكرة البرنامج FLASH التي تتسع لـ1024 كلمة (من العنوان 000h إلى 3FFh). فالكلمة $11\,0000\,0000\,1010$ مثلاً تعني «ضع القيمة 0Ah في سجل العمل W». كتابة برنامج كامل بهذه الطريقة مرهقة وكثيرة الأخطاء، لذلك نكتبه بـلغة المجمع (langage assembleur): نعوّض كل كلمة آلية بـرمز تذكيري (mnémonique) من بضعة أحرف مشتقة من الإنجليزية، مثل MOVLW اختصار Move Literal to W، أي «انقل الثابت إلى W».
> لكل تعليمة بلغة المجمع كلمة آلية واحدة من 14 بتاً، والعكس صحيح: لغة المجمع هي الشفرة الآلية نفسها مكتوبة برموز يقرؤها الإنسان.
يملك PIC16F84A 35 تعليمة فقط، لأنه من بنية RISC (مجموعة تعليمات مختصرة): تعليمات قليلة وبسيطة، أغلبها يُنفَّذ في دورة آلة واحدة.
من الملف المصدر إلى الميكرومراقب

يبيّن الشكل المراحل: نكتب البرنامج المصدر (programme source) في ملف نصي امتداده asm، ثم يترجمه المجمِّع (assembleur) MPASM المدمج في بيئة MPLAB إلى ملف امتداده hex يحوي الكلمات الآلية، ثم ينقلها المبرمِج (programmateur) إلى ذاكرة FLASH للميكرومراقب.
- يتحقق المجمِّع من صحة الكتابة: رمز تذكيري مجهول، أو وسم غير معرَّف، أو قيمة خارج المجال، كلها تظهر في ملف الأخطاء err.
- ملف القائمة lst (listing) يعرض لكل سطر عنوانه في الذاكرة وشفرته الآلية بالست عشري، وهو مفيد لفهم الترميز (الجزء 2).
- المحاكاة (Proteus أو محاكي MPLAB) تسمح بتجربة البرنامج قبل برمجة الدارة الحقيقية.
بنية سطر في البرنامج المصدر
يتكون كل سطر من أربعة حقول (champs) على الأكثر، تفصل بينها مسافات أو جدولات:

يُظهر الشكل سطرين: الأول يبدأ بوسم في العمود الأول، والثاني بلا وسم فيبدأ بمسافات قبل الرمز التذكيري.
| الحقل | الدور | قواعد الكتابة | |---|---|---| | الوسم (étiquette) | اسم يدل على عنوان التعليمة، تقصده GOTO أو CALL | يبدأ في العمود الأول بحرف أو بالرمز _، ويفرّق المجمِّع فيه بين الحروف الكبيرة والصغيرة | | الرمز التذكيري | اسم التعليمة أو التوجيه | لا يُكتب في العمود الأول | | المعاملات (opérandes) | ما تعمل عليه التعليمة | تفصل بينها فاصلة: `CPT,F` | | التعليق (commentaire) | شرح للقارئ | يبدأ بالفاصلة المنقوطة ; ويتجاهله المجمِّع تماماً |
الرموز التذكيرية لا تتأثر بحالة الحروف (`movlw` و`MOVLW` سواء)، أما أسماء السجلات المعرّفة في الملف p16F84A.inc فمكتوبة بحروف كبيرة: نكتب STATUS وPORTB.
رموز المعاملات في جدول التعليمات
يستعمل جدول التعليمات في وثيقة الصانع (ويُرفق مقتطف منه عادة بموضوع البكالوريا) الرموز الآتية:
| الرمز | المعنى | المجال | |---|---|---| | f | عنوان سجل (file register) في الذاكرة RAM | 7 بتات: من 00h إلى 7Fh | | W | سجل العمل (registre de travail) | 8 بتات | | b | رقم البت داخل السجل | من 0 إلى 7 | | k | ثابت (littéral) أو عنوان في ذاكرة البرنامج | 8 بتات للثابت، و11 بتاً لعنوان CALL وGOTO | | d | وجهة النتيجة (destination) | $d = 0$ أو W: النتيجة في W، و$d = 1$ أو F: النتيجة في f | | PC | عداد البرنامج (compteur ordinal) | 13 بتاً | | TOS | قمة المكدس (Top Of Stack) | آخر عنوان رجوع محفوظ |
الكتابة (f) تعني «محتوى السجل f»، والسهم $\rightarrow$ يعني «يُنقل إلى». فالعملية $(W) + (f) \rightarrow d$ تُقرأ: «اجمع محتوى W ومحتوى f وضع النتيجة في الوجهة d».
> الوجهة d: التعليمة `ADDWF CPT,W` تضع النتيجة في W ويبقى CPT على حاله، والتعليمة `ADDWF CPT,F` تضعها في CPT. الرمزان W وF معرّفان في الملف p16F84A.inc بالقيمتين 0 و1، فـ`ADDWF CPT,0` هي نفسها `ADDWF CPT,W`.
إذا نُسي المعامل d افترض المجمِّع أن الوجهة هي f مع رسالة تنبيه، فاكتبه دائماً.
دور سجل العمل W
W سجل من 8 بتات خارج خريطة الذاكرة RAM، وتمر عبره المعطيات: لا توجد في PIC16F84A تعليمة تنقل محتوى سجل إلى سجل آخر مباشرة، فنمرّ بـW في تعليمتين. مثال: نسخ محتوى السجل 0Ch في السجل 0Dh:
أنظمة العدّ في المجمِّع MPASM
يقبل MPASM كتابة الأعداد بعدة أسس، والأفضل أن نكتب البادئة دائماً:
| الأساس | الكتابة | مثال بالعدد 63 | |---|---|---| | ست عشري (hexadécimal) | `0x3F` أو `H'3F'` أو `3Fh` | `MOVLW 0x3F` | | عشري (décimal) | `D'63'` أو `.63` | `MOVLW D'63'` | | ثنائي (binaire) | `B'00111111'` | `MOVLW B'00111111'` | | ثماني (octal) | `O'77'` | نادر الاستعمال | | رمز ASCII | `'A'` أي 41h | لإرسال حرف |
> الأساس الافتراضي في MPASM هو الست عشري: التعليمة `MOVLW 10` تضع في W القيمة 10h أي 16، لا 10. لتجنّب الخطأ نكتب `D'10'` أو `0x0A`.
قاعدة أخرى: العدد الست عشري المكتوب باللاحقة h يجب أن يبدأ برقم، فنكتب 0A5h لا A5h، لأن A5h يُقرأ اسماً لا عدداً.
مثال محلول
نريد شحن عداد بالقيمة العشرية 200، ثم كتابة القيمة الثنائية $1011\,0100$ في المنفذ B.
- التحويل: $200 = 12\times16 + 8$، والرقم 12 يُكتب C بالست عشري، إذن العدد 200 يُكتب C8h. نكتب `MOVLW D'200'` أو `MOVLW 0xC8`.
- القيمة $1011\,0100$: نقسمها إلى نصفي بايت (quartets): 1011 يقابله B، و0100 يقابله 4، فهي B4h، وبالعشري $11\times16 + 4 = 180$. نكتب `MOVLW B'10110100'` ثم `MOVWF PORTB`.
- لو كتبنا `MOVLW 200` لقرأها المجمِّع 200h أي 512، وهي لا تتسع في 8 بتات: ينبّه المجمِّع ويحتفظ بالبتات الثمانية الدنيا فقط، أي 00h، فيبدأ العداد من 0 لا من 200.
ملاحظة للامتحان
- اكتب الصفر لا الحرف O: `0x1F` و`TMR0` و`T0IF`. الكتابة OX1F أو TMRO يرفضها المجمِّع.
- الثابت k في تعليمات القيمة الفورية من 8 بتات: أكبر قيمة له 255 أي FFh، فالقيمة 300 مثلاً لا تُشحن في سجل واحد.
- التعليق لا يأخذ أي مكان في ذاكرة البرنامج: اكتب تعليقاتك بلا تردد، فهي تُحسب لك في الإجابة.
2. صيغة التعليمة وترميزها وزمن تنفيذها
التعليمة كلمة من 14 بتاً
كل تعليمة كلمة واحدة من 14 بتاً تحمل شفرة العملية (opcode) ثم المعاملات. تقسّم وثيقة الصانع التعليمات الـ35 إلى ثلاث عائلات حسب ما تعمل عليه:
| العائلة | العدد | أمثلة | المعاملات | |---|---|---|---| | تعليمات موجهة نحو البايت (byte-oriented) | 18 | ADDWF، MOVF، DECFSZ | f وd | | تعليمات موجهة نحو البت (bit-oriented) | 4 | BCF، BSF، BTFSC، BTFSS | f وb | | تعليمات الثوابت والتحكم (literal and control) | 13 | MOVLW، GOTO، CALL، RETURN | k أو لا شيء |
المجموع $18 + 4 + 13 = 35$ تعليمة.
الصيغ الأربع للكلمة الآلية

يبيّن الشكل توزيع البتات الـ14 (من البت 13 إلى البت 0) في كل صيغة، وتحتها مثال مرمّز:
- الموجهة نحو البايت: شفرة العملية في 6 بتات (13 إلى 8)، ثم بت الوجهة d (البت 7)، ثم عنوان السجل f في 7 بتات (6 إلى 0).
- الموجهة نحو البت: شفرة العملية في 4 بتات (13 إلى 10)، ثم رقم البت b في 3 بتات (9 إلى 7)، ثم f في 7 بتات.
- الثوابت: شفرة العملية في 6 بتات، ثم الثابت k في 8 بتات (7 إلى 0).
- CALL وGOTO: شفرة العملية في 3 بتات، ثم العنوان k في 11 بتاً (10 إلى 0).
نتائج مهمة لهذه الصيغ
- العنوان f من 7 بتات: لا يُرمَّز إلا 128 عنواناً (من 00h إلى 7Fh)، أي بنك واحد. عنوان TRISB هو 86h أي $1000\,0110$: بته الثامن لا مكان له في التعليمة، فتحتفظ بـ$000\,0110$ أي 06h، وهو عنوان PORTB نفسه. الذي يحدد السجل المقصود هو البت RP0 من السجل STATUS: إذا كان $RP0 = 0$ فهو PORTB (البنك 0)، وإذا كان $RP0 = 1$ فهو TRISB (البنك 1). لذلك نكتب `BSF STATUS,RP0` قبل الكتابة في TRISA وTRISB وOPTION_REG. يعرض MPASM عندها الرسالة 302 «Register in operand not in bank 0» للتذكير فقط، ولا يضيف تبديل البنك بدلاً منك.
- الثابت k من 8 بتات: قيمته من 0 إلى 255.
- عنوان CALL وGOTO من 11 بتاً: يصل إلى 2048 كلمة، وذاكرة PIC16F84A فيها 1024 كلمة فقط، فكل عناوينها في المتناول.
- رقم البت b من 3 بتات: من 0 إلى 7.
مثال محلول: ترميز تعليمتين وفك ترميز ثالثة
نأخذ شفرة كل تعليمة من جدول التعليمات أسفله.
أ) الترميز الأول: `BSF STATUS,RP0`. شفرة BSF هي $01\,01$، والبت $b = 5$ أي 101، والسجل STATUS عنوانه 03h أي $000\,0011$.
| الحقل | OPCODE | b | f | |---|---|---|---| | البتات | 0101 | 101 | 0000011 |
نجمع البتات الـ14 ونقسمها إلى مجموعات من 4 بدءاً من اليمين: $01\,0110\,1000\,0011$، أي 1683h.
ب) الترميز الثاني: `MOVWF PORTB`. شفرة MOVWF هي $00\,0000\,1$، ثم f أي $000\,0110$، فالكلمة $00\,0000\,1000\,0110$ أي 0086h. والتعليمة `MOVWF TRISB` تعطي الشفرة نفسها 0086h كما رأينا.
ج) فك ترميز الكلمة 1283h: بالثنائي $01\,0010\,1000\,0011$.
- البتان 13 و12 يساويان 01: تعليمة موجهة نحو البت.
- البتان 11 و10 يساويان 00: هي BCF.
- البتات 9 إلى 7 تساوي 101: البت رقم 5.
- البتات 6 إلى 0 تساوي $000\,0011$: السجل 03h أي STATUS.
النتيجة `BCF STATUS,5`، أي الرجوع إلى البنك 0.
جدول التعليمات الـ35
في عمود الشفرة: d بت الوجهة، وf بتات العنوان، وb بتات رقم البت، وk بتات الثابت، وx بت لا يهم (يضعه المجمِّع 0).
التعليمات الموجهة نحو البايت (18)
| التعليمة | العملية | الدورات | الأعلام | الشفرة | |---|---|---|---|---| | `ADDWF f,d` | $(W) + (f) \rightarrow d$ | 1 | C، DC، Z | `00 0111 dfff ffff` | | `ANDWF f,d` | $(W)\ \mathrm{AND}\ (f) \rightarrow d$ | 1 | Z | `00 0101 dfff ffff` | | `CLRF f` | $0 \rightarrow f$ | 1 | Z | `00 0001 1fff ffff` | | `CLRW` | $0 \rightarrow W$ | 1 | Z | `00 0001 0xxx xxxx` | | `COMF f,d` | $\overline{(f)} \rightarrow d$ | 1 | Z | `00 1001 dfff ffff` | | `DECF f,d` | $(f) - 1 \rightarrow d$ | 1 | Z | `00 0011 dfff ffff` | | `DECFSZ f,d` | $(f) - 1 \rightarrow d$، قفز إن كانت النتيجة 0 | 1 (2) | لا شيء | `00 1011 dfff ffff` | | `INCF f,d` | $(f) + 1 \rightarrow d$ | 1 | Z | `00 1010 dfff ffff` | | `INCFSZ f,d` | $(f) + 1 \rightarrow d$، قفز إن كانت النتيجة 0 | 1 (2) | لا شيء | `00 1111 dfff ffff` | | `IORWF f,d` | $(W)\ \mathrm{OR}\ (f) \rightarrow d$ | 1 | Z | `00 0100 dfff ffff` | | `MOVF f,d` | $(f) \rightarrow d$ | 1 | Z | `00 1000 dfff ffff` | | `MOVWF f` | $(W) \rightarrow f$ | 1 | لا شيء | `00 0000 1fff ffff` | | `NOP` | لا عملية | 1 | لا شيء | `00 0000 0xx0 0000` | | `RLF f,d` | تدوير إلى اليسار عبر C | 1 | C | `00 1101 dfff ffff` | | `RRF f,d` | تدوير إلى اليمين عبر C | 1 | C | `00 1100 dfff ffff` | | `SUBWF f,d` | $(f) - (W) \rightarrow d$ | 1 | C، DC، Z | `00 0010 dfff ffff` | | `SWAPF f,d` | تبادل نصفي البايت | 1 | لا شيء | `00 1110 dfff ffff` | | `XORWF f,d` | $(W)\ \mathrm{XOR}\ (f) \rightarrow d$ | 1 | Z | `00 0110 dfff ffff` |
التعليمات الموجهة نحو البت (4)
| التعليمة | العملية | الدورات | الأعلام | الشفرة | |---|---|---|---|---| | `BCF f,b` | $0 \rightarrow f\langle b \rangle$ | 1 | لا شيء | `01 00bb bfff ffff` | | `BSF f,b` | $1 \rightarrow f\langle b \rangle$ | 1 | لا شيء | `01 01bb bfff ffff` | | `BTFSC f,b` | قفز إن كان البت b يساوي 0 | 1 (2) | لا شيء | `01 10bb bfff ffff` | | `BTFSS f,b` | قفز إن كان البت b يساوي 1 | 1 (2) | لا شيء | `01 11bb bfff ffff` |
تعليمات الثوابت والتحكم (13)
| التعليمة | العملية | الدورات | الأعلام | الشفرة | |---|---|---|---|---| | `ADDLW k` | $(W) + k \rightarrow W$ | 1 | C، DC، Z | `11 111x kkkk kkkk` | | `ANDLW k` | $(W)\ \mathrm{AND}\ k \rightarrow W$ | 1 | Z | `11 1001 kkkk kkkk` | | `CALL k` | $PC + 1 \rightarrow TOS$ ثم $k \rightarrow PC$ | 2 | لا شيء | `10 0kkk kkkk kkkk` | | `CLRWDT` | تصفير مؤقت الحراسة WDT | 1 | $\overline{TO}$، $\overline{PD}$ | `00 0000 0110 0100` | | `GOTO k` | $k \rightarrow PC$ | 2 | لا شيء | `10 1kkk kkkk kkkk` | | `IORLW k` | $(W)\ \mathrm{OR}\ k \rightarrow W$ | 1 | Z | `11 1000 kkkk kkkk` | | `MOVLW k` | $k \rightarrow W$ | 1 | لا شيء | `11 00xx kkkk kkkk` | | `RETFIE` | $TOS \rightarrow PC$ ثم $1 \rightarrow GIE$ | 2 | لا شيء | `00 0000 0000 1001` | | `RETLW k` | $k \rightarrow W$ ثم $TOS \rightarrow PC$ | 2 | لا شيء | `11 01xx kkkk kkkk` | | `RETURN` | $TOS \rightarrow PC$ | 2 | لا شيء | `00 0000 0000 1000` | | `SLEEP` | الدخول في نمط السبات | 1 | $\overline{TO}$، $\overline{PD}$ | `00 0000 0110 0011` | | `SUBLW k` | $k - (W) \rightarrow W$ | 1 | C، DC، Z | `11 110x kkkk kkkk` | | `XORLW k` | $(W)\ \mathrm{XOR}\ k \rightarrow W$ | 1 | Z | `11 1010 kkkk kkkk` |
زمن تنفيذ التعليمة: دورة الآلة
تدوم دورة الآلة (cycle machine) أربع دورات للمذبذب:
$T_{cy} = 4\,T_{osc} = \frac{4}{f_{osc}}$
مع بلور 4 MHz: $T_{osc} = 0{,}25\ \mu s$ و$T_{cy} = 1\ \mu s$. ومع 20 MHz: $T_{cy} = 0{,}2\ \mu s$.
يعمل الميكرومراقب بمبدأ الأنبوب (pipeline): أثناء تنفيذ تعليمة يجلب التعليمة التي تليها في الذاكرة، فتأخذ كل تعليمة دورة واحدة فعلياً. أما التعليمة التي تغيّر عداد البرنامج PC (GOTO وCALL وRETURN وRETLW وRETFIE، أو كتابة في PCL) فتجعل التعليمة المجلوبة مسبقاً بلا فائدة: تُنفَّذ مكانها NOP، فتدوم دورتين. وتعليمات الاختبار والقفز (BTFSC وBTFSS وDECFSZ وINCFSZ) تدوم دورة واحدة إن لم يتحقق القفز، ودورتين إن تحقق، وهذا معنى الكتابة «1 (2)».
مثال محلول
نحسب مدة المقطع الآتي مع بلور 4 MHz ثم مع بلور 20 MHz:
- عدد الدورات: $1 + 1 + 1 + 1 + 2 = 6$ دورات.
- مع 4 MHz: $t = 6 \times 1\ \mu s = 6\ \mu s$.
- مع 20 MHz: $t = 6 \times 0{,}2\ \mu s = 1{,}2\ \mu s$.
ملاحظة للامتحان
- يعطيك الموضوع عادة مقتطفاً من هذا الجدول في ملحق «وثائق الصانع»: لا تحفظ الشفرات، لكن اعرف كيف تقرأ الصيغة والدورات والأعلام.
- لا تخلط بين دور المذبذب $T_{osc}$ ودورة الآلة $T_{cy}$: مع 4 MHz الأول 0,25 µs والثاني 1 µs.
- التعليمتان `MOVWF PORTB` و`MOVWF TRISB` لهما الشفرة نفسها: البت RP0 وحده يفرّق بينهما عند التنفيذ.
3. سجل الحالة STATUS والأعلام وتعليمات الحساب
سجل الحالة STATUS
سجل الحالة (registre d'état) STATUS سجل من 8 بتات عنوانه 03h في البنك 0، ويظهر أيضاً في العنوان 83h من البنك 1: هو السجل نفسه، فيمكن قراءته وكتابته مهما كان البنك المختار، وهذا ما يسمح بتبديل البنك في أي لحظة.

يُظهر الشكل البتات الثمانية: بتات اختيار البنك IRP وRP1 وRP0 في الأعلى، ثم بتا الحالة $\overline{TO}$ و$\overline{PD}$، ثم الأعلام الثلاثة Z وDC وC التي تكتبها الوحدة الحسابية والمنطقية UAL (ALU).
| البت | الاسم | الدور | |---|---|---| | 7 | IRP | غير مستعمل في PIC16F84A، يُترك 0 | | 6 | RP1 | غير مستعمل في PIC16F84A، يُترك 0 | | 5 | RP0 | اختيار البنك: 0 للبنك 0 (00h إلى 7Fh)، و1 للبنك 1 (80h إلى FFh) | | 4 | $\overline{TO}$ | يصير 0 إذا انقضى مؤقت الحراسة WDT، ويصير 1 بعد التغذية وبالتعليمتين CLRWDT وSLEEP | | 3 | $\overline{PD}$ | يصير 0 بتنفيذ SLEEP، ويصير 1 بعد التغذية وبالتعليمة CLRWDT | | 2 | Z | علم الصفر (zéro) | | 1 | DC | علم حمل نصف البايت (digit carry) | | 0 | C | علم الحمل (carry) |
البتان $\overline{TO}$ و$\overline{PD}$ للقراءة فقط. أما Z وDC وC فتكتبها العمليات، ويمكن أيضاً كتابتها بالتعليمتين BCF وBSF، مثل `BCF STATUS,C` قبل تدوير.
معنى الأعلام الثلاثة
- Z: يأخذ 1 إذا كانت نتيجة العملية صفراً (00h)، و0 في غير ذلك.
- C في الجمع: يأخذ 1 إذا تجاوزت النتيجة FFh، أي ظهر حمل خارج البت 7.
- DC في الجمع: يأخذ 1 إذا ظهر حمل من البت 3 إلى البت 4، أي تجاوز مجموع نصفي البايت الأدنيين 0Fh.
- في الطرح القطبية معكوسة: $C = 1$ يعني «لا استلاف» (النتيجة موجبة أو معدومة)، و$C = 0$ يعني «استلاف» (النتيجة سالبة). والأمر نفسه لـDC على نصف البايت الأدنى.
سبب هذه القطبية: الوحدة الحسابية لا تطرح، بل تجمع المتمّم إلى اثنين:
$(f) - (W) = (f) + \overline{(W)} + 1$
فالحمل الناتج عن هذا الجمع هو C: يظهر الحمل عندما لا يكون هناك استلاف.
التعليمات التي تغيّر الأعلام
| الأعلام المتأثرة | التعليمات | |---|---| | C وDC وZ | ADDWF، ADDLW، SUBWF، SUBLW | | C وحده | RLF، RRF | | Z وحده | ANDWF، ANDLW، IORWF، IORLW، XORWF، XORLW، COMF، INCF، DECF، MOVF، CLRF، CLRW | | $\overline{TO}$ و$\overline{PD}$ | CLRWDT، SLEEP | | لا شيء | MOVLW، MOVWF، SWAPF، BCF، BSF، BTFSC، BTFSS، DECFSZ، INCFSZ، GOTO، CALL، RETURN، RETLW، RETFIE، NOP |
> INCF وDECF لا تغيّران C، وDECFSZ وINCFSZ لا تغيّران أي علم، وMOVLW لا تغيّر Z: لاختبار صفرية سجل نستعمل `MOVF f,F` التي تعيد كتابة السجل في نفسه وتضبط Z.
تعليمات الحساب
| التعليمة | العملية | ملاحظة | |---|---|---| | `ADDWF f,d` | $(W) + (f) \rightarrow d$ | الجمع على 8 بتات، والحمل في C | | `ADDLW k` | $(W) + k \rightarrow W$ | جمع ثابت | | `SUBWF f,d` | $(f) - (W) \rightarrow d$ | السجل ناقص W، لا العكس | | `SUBLW k` | $k - (W) \rightarrow W$ | الثابت ناقص W، لا العكس | | `INCF f,d` | $(f) + 1 \rightarrow d$ | FFh تصير 00h و$Z = 1$، ولا يتغير C | | `DECF f,d` | $(f) - 1 \rightarrow d$ | 00h تصير FFh |
أمثلة محلولة

يبيّن الجزء (a) من الشكل الحمول بين البتات عند الجمع، والجزء (b) الطرح بجمع المتمم.
مثال 1، الجمع: $W = 9Ch$ و$f = 7Ah$، ثم `ADDWF f,W`.
- $9Ch + 7Ah = 156 + 122 = 278 = 116h$: لا يبقى في W إلا البايت الأدنى، $W = 16h$.
- خرج حمل من البت 7، إذن $C = 1$.
- نصفا البايت الأدنيان: $Ch + Ah = 12 + 10 = 22$، وهو أكبر من 15، إذن $DC = 1$.
- النتيجة ليست صفراً، إذن $Z = 0$.
مثال 2، الجمع مع Z وC معاً: $W = F0h$ ثم `ADDLW 0x10`. $F0h + 10h = 100h$، فيبقى $W = 00h$ مع $C = 1$ و$Z = 1$، و$DC = 0$ لأن $0 + 0 = 0$ في نصف البايت الأدنى.
مثال 3، طرح بنتيجة سالبة: $W = 05h$ ثم `SUBLW 0x03`. العملية $3 - 5 = -2$، وتحسبها الوحدة $03h + FAh + 1 = FEh$ دون حمل خارج البت 7. إذن $W = FEh$ (المتمم إلى اثنين للعدد 2) و$C = 0$ (استلاف)، و$DC = 0$ لأن $3 - 5$ يحتاج استلافاً في نصف البايت الأدنى، و$Z = 0$.
مثال 4، طرح بنتيجة موجبة: السجل $f = 20h$ و$W = 12h$، ثم `SUBWF f,F`. النتيجة $20h - 12h = 0Eh$ في f، و$C = 1$ (لا استلاف)، و$DC = 0$ لأن نصف البايت الأدنى $0 - 2$ يحتاج استلافاً، و$Z = 0$.
مقارنة عددين بالطرح
بعد `SUBWF f,W` نقرأ النتيجة من العلمين C وZ دون الحاجة إلى قيمة W:
| الحالة | C | Z | |---|---|---| | $(f) > (W)$ | 1 | 0 | | $(f) = (W)$ | 1 | 1 | | $(f) < (W)$ | 0 | 0 |
تطبيق: عتبة. نريد معرفة هل العدد N المقروء على المنفذ B أصغر من 100 أو يساويه:
التعليمة SUBLW تحسب $100 - N$: لا استلاف (أي $C = 1$) إذا وفقط إذا كان $N \le 100$. مثلاً $N = 80$ يعطي $W = 20 = 14h$ و$C = 1$، و$N = 120$ يعطي $100 - 120 = -20$ أي $W = ECh$ و$C = 0$.
أخطاء شائعة
- قلب ترتيب الطرح: SUBLW تحسب $k - W$، وSUBWF تحسب $f - W$.
- قراءة $C = 1$ في الطرح على أنه استلاف: العكس هو الصحيح.
- انتظار تغيّر C بعد INCF أو DECF: هاتان التعليمتان لا تغيّران إلا Z.
- نسيان أن نتيجة الجمع تُقتطع إلى 8 بتات: $F0h + 10h$ تعطي 00h في W، والحمل وحده يحفظ البت التاسع.
4. تعليمات النقل والمنطق والتدوير
تعليمات النقل والتصفير
| التعليمة | العملية | الأعلام | الاستعمال | |---|---|---|---| | `MOVLW k` | $k \rightarrow W$ | لا شيء | شحن ثابت في W | | `MOVWF f` | $(W) \rightarrow f$ | لا شيء | كتابة W في سجل أو منفذ | | `MOVF f,d` | $(f) \rightarrow d$ | Z | قراءة سجل أو منفذ في W | | `CLRF f` | $0 \rightarrow f$ | $Z = 1$ | تصفير سجل أو منفذ | | `CLRW` | $0 \rightarrow W$ | $Z = 1$ | تصفير W |
شحن ثابت في سجل يتطلب دائماً تعليمتين، لأن MOVLW لا تكتب إلا في W:
> التعليمة `MOVF f,F` تنقل السجل إلى نفسه: لا يتغير محتواه، لكنها تضبط Z. هي الطريقة المعتادة لاختبار «هل السجل صفر؟».
التعليمات المنطقية وفكرة القناع
التعليمات المنطقية تعمل بتاً ببت بين W وثابت k أو سجل f، وكلها لا تغيّر إلا Z:
| التعليمة | العملية | الأثر على البت حسب بت القناع | |---|---|---| | `ANDLW k` و`ANDWF f,d` | AND | بت القناع 0 يفرض 0، وبت القناع 1 يُبقي البت | | `IORLW k` و`IORWF f,d` | OR | بت القناع 1 يفرض 1، وبت القناع 0 يُبقي البت | | `XORLW k` و`XORWF f,d` | XOR | بت القناع 1 يعكس البت، وبت القناع 0 يُبقيه | | `COMF f,d` | NOT | يعكس كل البتات الثمانية |
القناع (masque) قيمة نختار بتاتها لنؤثر في بتات محددة دون غيرها.
مثال محلول: عمليات متتالية على بايت
نقرأ المنفذ B فنجد $1011\,0110$ (B6h)، ثم ننفذ:
| التعليمة | W بالثنائي | W بالست عشري | Z | |---|---|---|---| | `MOVF PORTB,W` | $1011\,0110$ | B6h | 0 | | `ANDLW 0x0F` | $0000\,0110$ | 06h | 0 | | `IORLW B'10000000'` | $1000\,0110$ | 86h | 0 | | `XORLW 0xFF` | $0111\,1001$ | 79h | 0 |
- ANDLW 0x0F حذف النصف الأعلى واحتفظ بالبتات 3 إلى 0.
- IORLW فرض البت 7 على 1 دون لمس الباقي.
- XORLW 0xFF عكس كل البتات، كما تفعل COMF لكن على W.
اختبار المساواة بـXOR
نتيجة XOR بين عددين تساوي صفراً إذا وفقط إذا كانا متساويين. لمعرفة هل وصل العداد CPT إلى 12:
هذه الطريقة لا تغيّر CPT، ولا تعتمد على C كما في المقارنة بالطرح.
عكس مخرج واحد
لعكس حالة الثنائية الضوئية على RB2 وحدها:
أما `COMF PORTB,F` فتعكس المخارج الثمانية كلها، وهو خطأ متكرر في البرامج المنقولة.
> كل تعليمة تعدّل منفذاً (BSF وBCF وXORWF وCOMF على PORTB مثلاً) تقرأ أولاً حالة الأرجل ثم تكتب: هذا مبدأ قراءة-تعديل-كتابة (read-modify-write). لا يطرح مشكلاً مع مخارج تغذي مقاحل أو ثنائيات عبر مقاومات.
تبادل نصفي البايت SWAPF
`SWAPF f,d` تتبادل نصف البايت الأعلى (البتات 7 إلى 4) والأدنى (3 إلى 0): السجل الذي يحتوي 3Ah يعطي A3h. لا تغيّر أي علم، وتُستعمل مثلاً لوضع رقم BCD في النصف الأعلى قبل إرساله.
التدوير عبر الحمل RLF وRRF

يبيّن الشكل أن التدوير يمرّ عبر C، فهو تدوير على 9 بتات:
- `RLF f,d`: كل بت ينتقل إلى اليسار، البت 7 يدخل C، وC القديم يدخل البت 0. مثال الشكل: 96h مع $C = 0$ تعطي 2Ch و$C = 1$.
- `RRF f,d`: كل بت ينتقل إلى اليمين، البت 0 يدخل C، وC القديم يدخل البت 7. مثال الشكل: 96h مع $C = 0$ تعطي 4Bh و$C = 0$.
الضرب والقسمة على 2
إزاحة عدد إلى اليسار بمرتبة تضربه في 2، وإلى اليمين تقسمه على 2، بشرط تصفير C قبل التدوير حتى لا يدخل بت غريب:
مثال: $X = 15h$ أي 21. بعد BCF ثم RLF يصبح $X = 2Ah$ أي 42. ولو كان $C = 1$ قبل RLF لأصبح 2Bh أي 43، وهذا خطأ.
تطبيق: ضوء متنقل (chenillard)
نريد إشعال ثنائيات المنفذ B واحدة بعد الأخرى. نستعمل سجلاً وسيطاً motif حتى لا نقرأ الأرجل:
بعد 8 تدويرات يخرج البت 1 من البت 7 إلى C فتنطفئ كل الثنائيات مرة واحدة، ثم يعود في التدوير التاسع إلى البت 0: الدورة 9 خطوات بسبب C (على شرط ألا يغيّر البرنامج الفرعي tempo العلم C، وهو حال حلقات DECFSZ وGOTO). لتجنب الخطوة الفارغة نعيد شحن motif بـ01h عندما يصير صفراً.
ملاحظة للامتحان
- `CLRF PORTB` يصفّر مخارج المنفذ B، ولا يجعل الأرجل مداخل أو مخارج: الاتجاه يحدده TRISB وحده.
- MOVLW لا تغيّر Z، فلا تختبر Z بعدها.
- لا تنسَ `BCF STATUS,C` قبل RLF أو RRF إذا كنت تضرب أو تقسم.
5. تعليمات البت والقفز والتحكم في سير البرنامج
تعليمات البت: BCF وBSF
`BCF f,b` تضع 0 في البت b من السجل f، و`BSF f,b` تضع فيه 1، دون لمس البتات الأخرى ودون تغيير أي علم. هي أكثر التعليمات استعمالاً في التحكم:
| المثال | المعنى | |---|---| | `BSF STATUS,RP0` | اختيار البنك 1 | | `BCF STATUS,RP0` | الرجوع إلى البنك 0 | | `BSF PORTB,0` | جعل المخرج RB0 في الحالة 1 (تشغيل المرحل مثلاً) | | `BCF PORTB,0` | جعل المخرج RB0 في الحالة 0 | | `BCF INTCON,T0IF` | تصفير علم فيضان المؤقت TMR0 |
تعليمات الاختبار والقفز: BTFSC وBTFSS
- `BTFSC f,b` (Bit Test f, Skip if Clear): إذا كان البت b يساوي 0 قُفزت التعليمة الموالية، وإلا نُفِّذت.
- `BTFSS f,b` (Bit Test f, Skip if Set): إذا كان البت b يساوي 1 قُفزت التعليمة الموالية، وإلا نُفِّذت.
«القفز» هنا قفز فوق تعليمة واحدة فقط، لذلك تكون التعليمة الموالية غالباً GOTO. تدوم تعليمة الاختبار دورة واحدة إذا لم يحدث القفز، ودورتين إذا حدث.
ترجمة الشروط
| الشرط في الخوارزمية | الكتابة | |---|---| | انتظر حتى يصير $RA0 = 1$ | `att BTFSS PORTA,0` ثم `GOTO att` | | انتظر حتى يصير $RA0 = 0$ | `att BTFSC PORTA,0` ثم `GOTO att` | | إذا كان $RB4 = 1$ اذهب إلى suite | `BTFSC PORTB,4` ثم `GOTO suite` | | إذا كانت نتيجة العملية صفراً اذهب إلى nul | `BTFSC STATUS,Z` ثم `GOTO nul` |
مثال: تحكم بمدخل ومخرج
الملتقط S على RA0 والمرحل KA1 على RB0. المطلوب: يعمل KA1 ما دام $S = 1$ ويتوقف عندما يصير $S = 0$.
إذا كان $S = 1$ تقفز BTFSS فوق `GOTO arret` فتُنفَّذ BSF. وإذا كان $S = 0$ تُنفَّذ `GOTO arret`.
حلقات العدّ: DECFSZ وINCFSZ
`DECFSZ f,d` تنقص 1 من f، فإذا صارت النتيجة صفراً قُفزت التعليمة الموالية. و`INCFSZ f,d` تزيد 1 وتقفز إذا صارت النتيجة صفراً (أي عند الانتقال من FFh إلى 00h). لا تغيّر أي منهما الأعلام.

يبيّن الشكل تتبع (trace) الحلقة الآتية بقيمة $N = 3$، عنواناً بعنوان، مع قيمة CPT وعدد الدورات والزمن التراكمي عند 4 MHz:
- في المرورين الأولين لا تصل CPT إلى 0: DECFSZ دورة واحدة ثم GOTO دورتان.
- في المرور الثالث تصير $CPT = 0$: تقفز DECFSZ فوق GOTO وتدوم دورتين، ويستمر البرنامج في 014h.
عدد دورات الحلقة وحدها (DECFSZ وGOTO) بقيمة ابتدائية N:
$n = 3(N - 1) + 2 = 3N - 1$
ومع تعليمتي الشحن MOVLW وMOVWF يصير $3N + 1$، أي 10 دورات بقيمة $N = 3$ كما في الشكل. وإذا كانت الحلقة برنامجاً فرعياً نضيف CALL وRETURN (دورتان لكل منهما): $3N + 5$.
> إذا شُحنت CPT بالقيمة 0 فإن أول DECFSZ تعطي FFh، فتدور الحلقة 256 مرة: هي أطول حلقة ممكنة بسجل واحد.
القفز والبرامج الفرعية: GOTO وCALL وRETURN
- `GOTO k`: قفز غير مشروط إلى العنوان k (دورتان)، ولا يحفظ أي عنوان.
- `CALL k`: يحفظ عنوان التعليمة الموالية $PC + 1$ في المكدس (pile)، ثم يقفز إلى البرنامج الفرعي k (دورتان).
- `RETURN`: يسترجع العنوان من قمة المكدس TOS ويرجع إليه (دورتان).
- `RETLW k`: مثل RETURN، مع شحن الثابت k في W قبل الرجوع.
- `RETFIE`: رجوع من برنامج المقاطعة، ويضع $GIE = 1$ لإعادة السماح بالمقاطعات.

يُظهر الشكل أن CALL في العنوان 021h تحفظ العنوان 022h في المكدس ثم تقفز إلى tempo في 040h، وأن RETURN تعيد البرنامج إلى 022h، أي إلى التعليمة التي تلي CALL.
المكدس في PIC16F84A فيه 8 مستويات فقط، ولا توجد تعليمات PUSH وPOP: لا يكتب فيه إلا CALL وقبول مقاطعة (يحفظ عنوان الرجوع قبل القفز إلى 0004h)، ولا يقرأ منه إلا RETURN وRETLW وRETFIE. إذا تداخلت أكثر من 8 استدعاءات كتب الاستدعاء التاسع فوق العنوان الأول (مكدس دائري) دون أي إنذار، فيضيع البرنامج عند الرجوع.
جدول قيم بالتعليمة RETLW
نريد تحويل رقم من 0 إلى 9 إلى شفرة الرقم ذي 7 قطع (مهبط مشترك، القطعة a على RB0 حتى g على RB6):
عند تنفيذ `ADDWF PCL,F` يكون PC قد أشار إلى التعليمة الموالية، فإضافة W تقفز إلى RETLW رقم W. مثلاً $W = 4$: نصل إلى `RETLW 0x66` فيرجع البرنامج و$W = 66h$ أي $0110\,0110$: القطع b وc وf وg، وهي شكل الرقم 4. يشترط أن يكون الجدول كله في أول 256 كلمة من الذاكرة ما دام PCLATH يساوي 0. وكتابة PCL تغيّر PC، فـ`ADDWF PCL,F` تدوم دورتين.
تعليمات أخرى
- `NOP`: لا تفعل شيئاً سوى استهلاك دورة واحدة، تُستعمل لضبط زمن التأجيل.
- `CLRWDT`: تصفّر مؤقت الحراسة WDT (إذا كان مفعّلاً) حتى لا يعيد تشغيل الميكرومراقب.
- `SLEEP`: تُدخل الميكرومراقب في نمط السبات (استهلاك ضعيف) حتى توقظه مقاطعة أو المؤقت WDT أو إعادة التهيئة بالرجل $\overline{MCLR}$.
أخطاء شائعة
- الخلط بين BTFSC وBTFSS: الحرف الأخير يقول متى يحدث القفز، C للبت 0 (Clear) وS للبت 1 (Set).
- الظن أن القفز يتجاوز أكثر من تعليمة واحدة.
- الخروج من برنامج فرعي بـGOTO بدل RETURN: يبقى العنوان في المكدس، وبعد 8 مرات يفسد المكدس.
- عدّ DECFSZ دورة واحدة في المرور الأخير: هي دورتان لأن القفز يحدث.
6. التوجيهات وبنية الملف المصدر
التعليمة والتوجيه
التعليمة (instruction) أمر للميكرومراقب: يترجمها المجمِّع إلى كلمة آلية يُنفذها PIC. أما التوجيه (directive) فأمر للمجمِّع نفسه: يخبره بنوع الميكرومراقب، أو بمكان وضع الشفرة، أو باسم قيمة، ولا يولّد في الغالب أي تعليمة. الاستثناء المهم هو `CONFIG` الذي يكتب كلمة الإعداد في العنوان 2007h.
| التوجيه | الدور | مثال | |---|---|---| | `LIST` | يحدد الميكرومراقب، وأساس العد إن أردنا | `LIST p=16F84A` | | `#include` | يُدرج ملف التعريفات (أسماء السجلات وبتاتها) | `#include ` | | `CONFIG` | يكتب كلمة الإعداد (configuration word) | `CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC` | | `EQU` | يعطي اسماً لقيمة (عنوان أو ثابت) | `CPT EQU 0x0C` | | `#DEFINE` | يعوّض نصاً بنص قبل الترجمة | `#DEFINE LED PORTB,0` | | `CBLOCK` و`ENDC` | يعطي عناوين متتالية لعدة متغيرات | `CBLOCK 0x0C` ثم الأسماء ثم `ENDC` | | `ORG` | يحدد العنوان الذي تبدأ منه الشفرة الموالية | `ORG 0x000` | | `END` | نهاية الملف المصدر، وما بعده يُهمل | `END` |
ماذا يحتوي الملف p16F84A.inc؟
ملف نصي يرافق MPLAB، يعرّف أسماء السجلات وبتاتها بالتوجيه EQU، ومنه هذا المقتطف:
بفضله نكتب `BSF STATUS,RP0` بدل `BSF 0x03,5`، والمعنى واحد. ولولا `#include` لرفض المجمِّع هذه الأسماء.
EQU و#DEFINE وCBLOCK
- `CPT EQU 0x0C` يجعل الاسم CPT مرادفاً للعدد 0Ch. نستعمله عنواناً لمتغير في السجلات العامة (من 0Ch إلى 4Fh): `DECFSZ CPT,F`. EQU لا يحجز شيئاً ولا يضع أي قيمة في الذاكرة.
- `#DEFINE LED PORTB,0` يعوّض الكلمة LED بالنص «PORTB,0»: فـ`BSF LED` تصير `BSF PORTB,0`.
- `CBLOCK 0x0C` ثم سطر `CPT1` وسطر `CPT2` ثم `ENDC` يعطي CPT1 العنوان 0Ch وCPT2 العنوان 0Dh.
> فخّ شائع: بعد `CPT EQU 0x0C` تشحن التعليمة `MOVLW CPT` في W العدد 0Ch، أي العنوان، لا محتوى المتغير. لقراءة المحتوى نكتب `MOVF CPT,W`.
كلمة الإعداد __CONFIG
كل رمز من الملف p16F84A.inc قيمته 3FFFh مع تصفير البت أو البتات التي يخصها إذا كان خياره يتطلب 0 (فـ`_CP_OFF` لا يصفّر شيئاً)، والتوجيه يجمعها بالعملية AND (الرمز &):
| الرمز | القيمة | البتات المعنية | |---|---|---| | `_CP_OFF` | 3FFFh | حماية الشفرة معطلة | | `_WDT_OFF` | 3FFBh | البت 2 يساوي 0: مؤقت الحراسة معطل | | `_PWRTE_ON` | 3FF7h | البت 3 يساوي 0: مؤقت التشغيل مفعّل | | `_XT_OSC` | 3FFDh | البتان 1 و0 يساويان 01: مذبذب XT |
النصف الأدنى: F وB و7 وD أي 1111 و1011 و0111 و1101، وحاصل AND بينها 0001، فالكلمة 3FF1h. التوجيه يُكتب بشرطتين سفليتين، ويأتي بعد `#include` لأن الرموز معرّفة في الملف المُدرج. تفاصيل بتات هذه الكلمة في درس هيكلة الميكرومراقب.
ORG وأشعة البداية
عند إعادة التهيئة يبدأ التنفيذ من العنوان 0000h (شعاع إعادة التهيئة)، والعنوان 0004h محجوز لشعاع المقاطعة. لذلك نكتب في 0000h قفزة إلى البرنامج، ونبدأ البرنامج بعد 0004h:
برنامج كامل وملف قائمته
المطلوب: نسخ حالة أربع قاطعات موصولة بـRA0 إلى RA3 على أربع ثنائيات موصولة بـRB0 إلى RB3، بلا توقف. الأرجل غير المستعملة تُبرمج مداخل.
يعطي المجمِّع ملف القائمة الآتي (العنوان والشفرة الآلية لكل تعليمة):
| العنوان | الشفرة | التعليمة | |---|---|---| | 0000 | 2805 | `GOTO debut` | | 0005 | 0186 | `CLRF PORTB` | | 0006 | 1683 | `BSF STATUS,RP0` | | 0007 | 301F | `MOVLW B'00011111'` | | 0008 | 0085 | `MOVWF TRISA` | | 0009 | 30F0 | `MOVLW B'11110000'` | | 000A | 0086 | `MOVWF TRISB` | | 000B | 1283 | `BCF STATUS,RP0` | | 000C | 0805 | `MOVF PORTA,W` | | 000D | 390F | `ANDLW 0x0F` | | 000E | 0086 | `MOVWF PORTB` | | 000F | 280C | `GOTO boucle` |
ملاحظات على القائمة:
- التوجيهات LIST و#include وORG وEND لم تولّد أي شفرة، و`CONFIG` كتب كلمته 3FF1h في العنوان 2007h خارج ذاكرة البرنامج فلا تظهر في هذا الجدول، والوسم debut أخذ العنوان 0005h والوسم boucle العنوان 000Ch.
- `MOVWF TRISB` في 000Ah و`MOVWF PORTB` في 000Eh لهما الشفرة نفسها 0086h: الفرق كله في البت RP0 لحظة التنفيذ.
- مدة الحلقة boucle: $1 + 1 + 1 + 2 = 5$ دورات، أي 5 µs مع بلور 4 MHz: تُنسخ القاطعات على الثنائيات كل 5 µs.
أخطاء المجمِّع الأكثر تكراراً
| الخطأ | النتيجة | التصحيح | |---|---|---| | `_CONFIG` بشرطة واحدة | تعليمة مجهولة | `CONFIG` بشرطتين | | `CONFIG` قبل `#include` | رموز غير معرّفة | ضع `#include` أولاً | | `TMRO` أو `OX1F` بالحرف O | اسم أو عدد مجهول | `TMR0` و`0x1F` بالصفر | | `MOVLW 15` بقصد خمسة عشر | تُشحن 15h أي 21 | `MOVLW D'15'` | | رمز تذكيري في العمود الأول | تحذير من المجمِّع | ابدأ السطر بمسافة | | وسم بعد العمود الأول | تحذير | اكتب الوسم في العمود الأول | | نسيان `BSF STATUS,RP0` قبل TRISB | تُكتب القيمة في PORTB | بدّل البنك ثم ارجع إلى البنك 0 | | نسيان `END` | خطأ نهاية الملف | `END` في آخر سطر |
الرسالة 302 التي تظهر عند كل كتابة في TRISA أو TRISB ليست خطأ: هي تذكير بأن العنوان في البنك 1، فتحقق فقط من وجود `BSF STATUS,RP0` قبلها.
7. تمارين محلولة بنمط البكالوريا
التمرين 1: عدّاد القطع على بساط ناقل
دفتر الشروط: بساط ناقل يحمل قطعاً نحو صندوق. الضغط على الزر dcy يشغّل البساط (المحرك M يتحكم فيه المرحل KA1). يعدّ الملتقط الضوئي S القطع المارة: يعطي 1 ما دامت قطعة أمامه. بعد مرور 6 قطع يتوقف البساط وتشتعل اللمبة H (عبر المرحل KA2) لطلب تبديل الصندوق. الضغط من جديد على dcy يطفئ H ويعيد الدورة.

يبيّن الشكل الميكرومراقب مع أرقام أرجله: الملتقط S على RA0 (الرجل 17) والزر dcy على RA1 (الرجل 18) مع مقاومتي السحب R1 وR2 نحو الأرض، والمرحلان KA1 وKA2 على RB0 وRB1 (الرجلان 6 و7) عبر المقحلين T1 وT2، وبلور 4 MHz مع المكثفين C1 وC2.
البرنامج:
الأسئلة
- ما دور كل من R1 وR3 والثنائية D1؟
- أعط محتوى TRISA وTRISB بالثنائي وبالست عشري (الأرجل غير المستعملة مداخل)، وتحقق منه في البرنامج.
- أكمل التعليقات من `attente` إلى آخر البرنامج.
- لماذا يحتوي البرنامج على حلقتين piece وlibre لكل قطعة؟
- ما قيمة CPT بعد مرور 4 قطع؟ وأيّ علم تغيّره DECFSZ؟
- رمّز التعليمتين `BSF PORTB,0` و`DECFSZ CPT,F`.
- احسب مدة مقطع التهيئة من `init` إلى `attente` (دون تعليمات attente).
الحل
- R1 مقاومة سحب نحو الأرض (pull-down): تفرض $RA0 = 0$ ما دام تماس S مفتوحاً، فلا يبقى المدخل عائماً. R3 تُبقي $\overline{MCLR}$ في الحالة 1 فلا يُعاد تشغيل الميكرومراقب. D1 ثنائية العجلة الحرة (roue libre): عند قطع T1 تمرّر تيار الوشيعة KA1 وتحمي المقحل من التوتر الزائد.
- المداخل RA0 وRA1، وRA2 إلى RA4 غير مستعملة فهي مداخل: TRISA يساوي $\text{-{}-{}-}1\,1111$، أي 1Fh. المخرجان RB0 وRB1 (0)، والباقي مداخل (1): TRISB يساوي $1111\,1100$، أي FCh. وهذا ما تشحنه التعليمتان `MOVLW 0x1F` و`MOVLW 0xFC`.
- التعليقات:
| التعليمة | التعليق | |---|---| | `attente BTFSS PORTA,1` | هل dcy مضغوط؟ إن كان $RA1 = 1$ اقفز | | `GOTO attente` | لا: واصل الانتظار | | `BCF PORTB,1` | إطفاء اللمبة H | | `MOVLW D'6'` ثم `MOVWF CPT` | شحن العداد بعدد القطع 6 | | `BSF PORTB,0` | تشغيل البساط ($KA1 = 1$) | | `piece BTFSS PORTA,0` ثم `GOTO piece` | انتظار وصول قطعة ($S = 1$) | | `libre BTFSC PORTA,0` ثم `GOTO libre` | انتظار مرورها ($S = 0$) | | `DECFSZ CPT,F` ثم `GOTO piece` | قطعة إضافية، وإذا لم تكتمل الست ننتظر الموالية | | `BCF PORTB,0` | إيقاف البساط | | `BSF PORTB,1` | إشعال H: الصندوق ممتلئ | | `GOTO attente` | انتظار ضغطة جديدة على dcy |
- القطعة تبقى أمام الملتقط عدة ميلي ثوانٍ، بينما تدوم دورة الحلقة بضع ميكروثوانٍ. لو اختبرنا $S = 1$ فقط لعُدّت القطعة الواحدة آلاف المرات. الحلقة piece تكشف وصول القطعة، والحلقة libre تنتظر خروجها، فتُعدّ كل قطعة مرة واحدة (كشف الجبهة الصاعدة ثم النازلة).
- CPT تبدأ من 6 وتنقص 1 مع كل قطعة: بعد 4 قطع $CPT = 2$. التعليمة DECFSZ لا تغيّر أي علم، وقرار التوقف يأتي من القفز لا من Z.
- `BSF PORTB,0`: الشفرة $01\,01$، و$b = 0$ أي 000، و$f = 06h$ أي $000\,0110$: الكلمة $01\,0100\,0000\,0110$، أي 1406h. و`DECFSZ CPT,F`: الشفرة $00\,1011$، و$d = 1$، و$f = 0Ch$ أي $000\,1100$: الكلمة $00\,1011\,1000\,1100$، أي 0B8Ch.
- سبع تعليمات من دورة واحدة: $n = 7$ دورات، و$T_{cy} = 4/f_{osc} = 1\ \mu s$، فالمدة $7\ \mu s$.
التمرين 2: منظم حرارة بسيط
المعطيات: محوّل تماثلي رقمي يعطي على المنفذ B عدداً N من 8 بتات مرتبطاً بدرجة الحرارة θ بالعلاقة $N = 2\,\theta$ (θ بالدرجة المئوية). مرحل التسخين KA موصول بـRA1 عبر مقحل، والأرجل الأخرى للمنفذ A غير مستعملة. البلور 4 MHz. البرنامج الرئيسي:
الأسئلة
- أعط TRISA وTRISB بالست عشري (الأرجل غير المستعملة مداخل).
- أكمل الجدول: محتوى W والعلمين C وZ بعد SUBLW، وحالة التسخين، من أجل $N = 80$ و$N = 100$ و$N = 120$.
- استنتج درجة الحرارة التي يتوقف عندها التسخين.
- ما التعديل اللازم لتصير العتبة 45 °C؟
- احسب مدة دورة واحدة للحلقة عندما يكون التسخين شغالاً، ثم عندما يكون متوقفاً.
الحل
- RA1 مخرج والباقي مداخل: TRISA يساوي $\text{-{}-{}-}1\,1101$، أي 1Dh. المنفذ B كله مداخل: TRISB يساوي FFh.
- SUBLW تحسب $100 - N$، و$C = 1$ إذا وفقط إذا كان $N \le 100$:
| N | $100 - N$ | W | C | Z | التسخين | |---|---|---|---|---|---| | 80 | 20 | 14h | 1 | 0 | يعمل | | 100 | 0 | 00h | 1 | 1 | يعمل | | 120 | $-20$ | ECh | 0 | 0 | متوقف |
مع $N = 120$: النتيجة السالبة $-20$ تُكتب $256 - 20 = 236$ أي ECh، والاستلاف يعطي $C = 0$، فلا تقفز BTFSS وتُنفَّذ `GOTO chaud`.
- التسخين يعمل ما دام $N \le 100$، أي $2\,\theta \le 100$ أو $\theta \le 50\ °C$، ويتوقف فوق 50 °C.
- العتبة 45 °C توافق $N = 2 \times 45 = 90$: نكتب `SUBLW D'90'`.
- التسخين شغال ($C = 1$): MOVF (1) وSUBLW (1) وBTFSS مع قفز (2) وBSF (1) وGOTO (2)، أي $n = 7$ دورات و$7\ \mu s$. التسخين متوقف ($C = 0$): MOVF (1) وSUBLW (1) وBTFSS دون قفز (1) وGOTO chaud (2) وBCF (1) وGOTO lire (2)، أي $n = 8$ دورات و$8\ \mu s$.
> ملاحظة: بعتبة واحدة يتأرجح المرحل بسرعة حين تقترب الحرارة من 50 °C. في نظام حقيقي نستعمل عتبتين (تشغيل تحت 48 °C وإيقاف فوق 52 °C مثلاً).
التمرين 3: قراءة ملف القائمة وكلمة الإعداد
المعطيات: ضاع الملف المصدر لبرنامج، وبقي ملف القائمة (العنوان والشفرة) وكلمة الإعداد 3FF1h:
| العنوان | الشفرة | |---|---| | 0000 | 2805 | | 0005 | 1683 | | 0006 | 3003 | | 0007 | 0085 | | 0008 | 0186 | | 0009 | 1283 | | 000A | 1805 | | 000B | 280A | | 000C | 1586 | | 000D | 280D |
الأسئلة
- أعد كتابة البرنامج بلغة المجمع مستعيناً بجدول التعليمات.
- حدد المداخل والمخارج التي يبرمجها.
- ماذا يفعل البرنامج؟ وما الاحتياط الناقص قبل جعل المنفذ B مخرجاً؟
- أعد كتابة سطر `CONFIG` بالرموز.
الحل
- فك الترميز سطراً سطراً:
| العنوان | الشفرة | التعليمة | السبب | |---|---|---|---| | 0000 | 2805 | `GOTO 0x005` | $10\,1$ ثم العنوان 005h | | 0005 | 1683 | `BSF STATUS,RP0` | $01\,01$، البت 5، السجل 03h | | 0006 | 3003 | `MOVLW 0x03` | $11\,00$ ثم الثابت 03h | | 0007 | 0085 | `MOVWF TRISA` | $00\,0000\,1$ ثم 05h، مع $RP0 = 1$ | | 0008 | 0186 | `CLRF TRISB` | $00\,0001\,1$ ثم 06h، مع $RP0 = 1$ | | 0009 | 1283 | `BCF STATUS,RP0` | $01\,00$، البت 5، السجل 03h | | 000A | 1805 | `BTFSC PORTA,0` | $01\,10$، البت 0، السجل 05h | | 000B | 280A | `GOTO 0x00A` | قفز إلى 00Ah | | 000C | 1586 | `BSF PORTB,3` | $01\,01$، البت 3، السجل 06h | | 000D | 280D | `GOTO 0x00D` | قفز إلى نفسه |
مثال التفصيل: 1586h تساوي $01\,0101\,1000\,0110$؛ البتات 13 إلى 10 تساوي 0101 (BSF)، والبتات 9 إلى 7 تساوي 011 أي 3، والبتات 6 إلى 0 تساوي $000\,0110$ أي 06h (PORTB).
- TRISA يساوي 03h أي $\text{-{}-{}-}0\,0011$: RA0 وRA1 مداخل، وRA2 وRA3 وRA4 مخارج. وTRISB يساوي 00h: المنفذ B كله مخارج. التعليمتان 0085h و0186h تصلان إلى TRISA وTRISB لأن البنك 1 مختار ($RP0 = 1$) لحظة تنفيذهما.
- البرنامج ينتظر ما دام $RA0 = 1$ (الحلقة 00Ah و00Bh)، فإذا صار $RA0 = 0$ قفزت BTFSC فوق GOTO فأشعل المخرج RB3، ثم يبقى في الحلقة 000Dh إلى الأبد. الاحتياط الناقص: لم يُصفَّر المنفذ B قبل جعله مخرجاً، ومحتواه عند التغذية غير محدد، فقد تُشغَّل بعض المخارج لحظة التهيئة. نضيف `CLRF PORTB` في البنك 0 قبل `BSF STATUS,RP0` (و`CLRF PORTA` للسبب نفسه، لأن RA2 إلى RA4 صارت مخارج أيضاً).
- 3FF1h تساوي $11\,1111\,1111\,0001$: البتات 13 إلى 4 كلها 1 (حماية الشفرة معطلة)، والبت 3 يساوي 0 (مؤقت التشغيل مفعّل)، والبت 2 يساوي 0 (مؤقت الحراسة معطل)، والبتان 1 و0 يساويان 01 (مذبذب XT). السطر: `CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC`.
ملاحظة للامتحان
- في سؤال TRIS ابدأ بجدول: الرجل، مدخل أو مخرج، البت. ثم اكتب البايت من البت 7 إلى البت 0، ثم حوّله إلى الست عشري.
- في سؤال «أكمل التعليقات» اكتب المعنى في النظام (تشغيل البساط، انتظار القطعة) لا مجرد ترجمة الرمز التذكيري.
- في حساب المدة عدّ الدورات تعليمة بتعليمة: GOTO وCALL وRETURN دورتان، وتعليمة الاختبار دورتان عند القفز.
الجزء الأول من الدرس مفتوح للجميع، وبقيته مع الاشتراك.