![]() |
#31
|
|||
|
|||
![]()
اعتبارات عملية فى تنفيذ مثل هذه ألبوردات:
الكثير يعانى من مشكلة أن الأمور تسير على ما يرام و عندما توضع الريلايات تختل الأمور و يعيد المتحكم البدء من جديد ولا يستقر إطلاقا الكثير يقترح استخدام أوبتوكبلر و حلول أخرى كثيرة لكن المشكلة أن الأحمال ألحثية تكون فى لحظة توصيلها صفر أوم أى قصر و حتى يبنى المجال و يقل التيار تدريجا ، تتسبب فى نبضة قاسية على خطوط التغذية. المعروف من مفكوك فورير أن النبضة الحادة لها مركبات حتى ترددات ما لانهاية وهذا يوفر قدرا كبيرا من الترددات العالية. عند ترددات مثل 100 ميجا و لا أفترض الأعلى، ستجد أن وصلات التغذية تشكل ملفات و مكثفات و دوائر رنين غير مرئية و غير محسوبة رجاء للفحص و الدراسة وليس التطبيق حاول تصفح مرسل إذاعة FM ، ستجد كم المشاكل حول أنه لم يعمل كثيرة رغم أننى نفذت أكثر من واحد – المشكلة فقط "أنه كان متاحا لى راسم ذبذبات يرسم و يحسب هذا التردد" و من ثم علمت كم هو بعيد عن التردد المطلوب. الآن ماذا يهمك و أنت تدرس متحكم؟ رجاء راجع حجم الملف المستخدم ستجده ثلاث إلى 5 لفات على قلم رصاص رفيع أى طول السلك لا يزيد عن10 سم. كم هو طول خطوط التغذية لديك؟؟ تغليظ المسارات يقلل من الأثر لكنه ليس بالقدر المطلوب، وفى هذا المجال التوصيل على اليسار أكثر أمنا و أقل تأثيرا عن التوصيل على اليمين وهذا يعنى أن مسارات تغذية الريلايات من المنبع تكون منفصلة تماما عن باقى المكونات الإلكترونية ولا تجعل الرجوع مشتركا إلا فى نقطة البدء أيضا وضع مكثف بجوار كل ريلاى أو فى مدخل تغذيتهم سيجعل نبضات الريلاى مأخوذة منه ولا تعبره لباقى الدوائر لو تستخدم وسائل خارجية مثل الموتورات و السولينويدات أى صمامات المياه الكهربية فكلها قد تعمل من220 متردد و الريلاى سيتحكم فيها. حسنا ضوضاء هذه المعدات ستشكل مشكلة أيضا لذا عادة ما تفصل مساراتها عن مسارات وحدة التغذية. استخدام وحدات تغذية مثبتة الجهد و تقطيعية تقلل كثيرا من أثر هذه الضوضاء لأنها تعتمد التقطيع ثم التخلص من أثره. و استخدام مرشح دخول للبوردة يقلل كثيرا من أثر هذه التداخلات. استخدام مرشح تغذية يفيد جدا فى التخلص من أثر هذه التداخلات Line Filter و ممكن أن يكون على البوردة لو تغذى من مصدر متردد سواء 220 أو 12 فولت أو يكون قبل محول الدخول. أيضا يجب الاهتمام جيدا بتغذية المتحكم ذاته حتى لا تدخل له نبضات من طرفى التغذية. كما أن الكثير لا يعبأ بطرف MCLR أو RESET فإما تلغيه و تهيء الطرف كمدخل / مخرج وتعتمد على الريسيت الذاتى مع انخفاض الفولت و تضع على طرفى التغذية مكثفات كافية أو تجعله طرف بدء تشغيل و توصله بالمقاومة و المكثف المناسبين أيضا لا تنسى أن وضعك مكثف 100 ميكروفاراد لا يغنى عن واحد 0.1 ميكرو على ألتوازى معه و الأفضل أن يكون قرص سيراميك لأن لف طبقات المكثف يخلق ملف داخلى يعوق الاستجابة للترددات العالية. قبل البدء قى تحويل البرنامج باللغات العالية سنبدأ فى المرة القادمة فهم ما هى اللغات العالية وهو موضوعنا القادم إن شاء الله |
#32
|
|||
|
|||
![]()
هيكل اللغات العالية و مم تتكون:
اللغات العالية تشترك فى مجموعة من الصفات مع اختلافات فى نص كتابتها و قد تتعجب أنها تتكون من 10 أوامر فقط تنقسم لثلاثة أقسام هى الشروط Conditions و الدورات Iteration ثم القفز أو الانتقال Jump قبل دراسة تفاصيلها ندرس الهيكل العام لأى برنامج الهيكل العام: تبدأ بالتسميات حيث تعطى لأى مكون اسم مريح من واقع الدائرة لتذكرك ما دور كل اسم سواء كان منفذ كامل أو طرف من منفذ أو مكون داخلى كمسجل أو علم Flag أو قيمة ثابتة أو متغيرة الخ. التسميات لا تأخذ أى ذاكرة لأنها تنتهى عند بدء الترجمة حيث يستبدل المترجم كل اسم بما يساويه سواء أكان مكون أو حتى معادلة رياضيه (كثير من المترجمات تسمح بهذا وليست الكل) . هذه التسميات تكون بالتعليمه Alias أو Symbol حسب المترجم باسكوم أو ميكروإلكترونيكا و يفضل غالبا و يحتم أحيانا كثيرة أن يكون فى أول البرنامج لحاجة المترجم لها لترجمة باقى الأسطر . يلى ذلك التعريفات حيث تعرف المتغيرات و الثوابت اسم كل منها و حجمه (كم بايت) وهل هو موجب فقط أم موجب و سالب كما سبق الشرح و هل هى رقم عشرى Float يلى ذلك تعريف ألدوال و الوظائف حيث يمكن تعريفها وكتابة الكود اللازم لها أو نكتفى بالتعريف فقط و نضع الكود لها لاحقا. فى اللغات العالية يتكون البرنامج من مجموعة من الوظائف و قد تحتاج ألدوال أيضا و الوظيفة هى ببساطة مجموعة أوامر متتابعة تؤدى مهمة معينه. مثلا اكتب على الشاشة كذا الخ ولا ناتج لها سوى العمل المطلوب. أما ألدوال فهى مشابهة إلا أنها تعطى نتيجة مثلا جيب الزاوية فهى دالة أعطيها 30 تعطينى نصف لأن جا 30 = 0.5 هذه الأسماء عامة أى تشمل البرنامج كاملا بكافة أقسامه. أغلب المترجمات تسمح بالتعريف داخل الدالة أو الوظيفة و يكون ظهور هذه القيمة داخل الدالة فقط مثلا لو عرفت دالة باسم "Send" و عرفت داخلها قيمة "Count" فقيمة Count هذه لا تستطيع أى تعليمه فى البرنامج خارج "Send" أن تصل إليها و تتعامل معها. تعريف الوظيفة: هى مجموعة تعليمات بين بداية و نهاية يحددان ما يخص هذه الوظيفة ففى البيزك نجد باسكوم تستخدم فى قسم التعريفات كود:
Declare Sub InitLCD Declare Sub Testvar(b As Byte , I As Integer , W As Word ) لاحقا فى البرنامج و حيث تريد لهذه الوظيفة أن توضع فى الذاكرة بين باقى البرنامج تكتب ما يلى كود:
Sub InitLCD . . End Sub Sub Testvar(b As Byte , I As Integer , W As Word ) . . . End Sub كود:
SUB PROCEDURE DoThisThing () . . . End Sub كود:
SUB PROCEDURE WriteLCD (Line2Print as string) .كود . . End Sub و لكى تستخدم أى منهم نتبع الصيغة البسيطة كأى تعليمه أخرى مثلا كود:
InitLCD Testvar(5 , 20 , 1245 ) DoThisThing WriteLCD (“ Hello World “) أما الدالة فتكون بطريقة باسكوم كود:
Declare Function Myfunction(byval I As Integer , S As String) As Integer لاحقا وحيث تريد أن تضعها تكتب الكود لها هكذا كود:
Function Myfunction(byval I As Integer , S As String) As Integer . .كود .كود MyFunction = 2*I/y End Function و تطلبها كما تطلب أى دالة عادية فستحتاج مثلا أن تعرف بعض المتغيرات منها CurrentVal مثلا و تريد أن تضع فيه جا 30 ستكتب ببساطة كود:
CurrentVal = Sin(30) CurrentVal = Myfunction(5,”Day”) أما بطريقة ميكروإلكترونيكا فيمكن تعريفها مسبقا بكتابة sub function First(dim a as word, dim b as word) as word forward البداية هى كلمة Sub اختصار Subroutine ثم كلمة function للدلالة على أنها تعيد نتيجة ثم يلى ذلك اسمها وهو ما عبرت عنه بكلمة First ثم قوسين () بينهما الأسماء و أحجامها و بعد ذلك كلمتى as word لتفيد أنها ستعيد قيمة بحجم Word و أخيرا كلمة forward للدلالة على أن الكود سيأتى لاحقا كود:
لاحقا تضع الكود كما يلى SUB function First(dim a as word, dim b as word) as word .كود .كود .Result = 5*a/b End Sub ذكرنا سابقا أن التعريفات ذات مدى عام أو شامل تغطى كل الوظائف و ألدوال أى يمكن لأى منها أن تقرأ هذه التعريفات أو تغيرها بصرف النظر عن كونها دالة أو وظيفة لكن لو فى داخل أى دالة أو وظيفة أعلنت متغير جديد سيكون مجاله محليا فقط أى بداخل الدالة أو الوظيفة ولا يمكن التعامل معه خارجها. لاحظ أن كل البرنامج مجموعة من الوظائف لذا على الأقل سيحتوى على وظيفة واحدة فقط و تسمى Main ولا تعلن بأى من الوظائف السابقة Declare , SUB و لذلك تنتهى بكلمة END فقط بدون أى إضافات . فى لغة C نجد الأمر مختلف قليلا فلا يوجد وظيفة أو دالة ولكن كلها subroutine فإن أعاد شيئا فهو دالة وإلا فيكون وظيفة و الإعلان عنها كما يلى كود:
int max(int x, int y) { return (x>=y) ? x : y; } القيمة التى سترد بها هى بعد return و طبعا نفس الشروط السابقة و هى هنا تقول ما بين القوسين x أكبر من أو تساوى y . أما بعدها نجد ؟ وهذه تعنى "هل ما بين القوسين صحيح؟ لو نعم صحيح أعد أو قيمة تالية وهى هنا X و إن لم تكن أعد القيمة التى تليها أى Y . عبقرية لكن حاول أن تتذكرها عندما تحتاجها أو تستنتجها ما لم أكن ذكرتها لك ولو خطأ ما فى النص حاول اكتشافه ، شخصيا أفضل كثيرا الصيغة IF X>=Y THEN RESULT=X ELSE RESULT =Y بسيطة و مفهومة بلا شرح و منطقية. و أخيرا القوس المعوج الغالق . هواة هذه اللغة يعيبون على لغة البيزك كلمة مثل End Sub , End Function. حسنا كل الأوامر العشرة و كل وظيفة تنتهى بذات القوس “{” و المترجم يربطه بأقرب واحد مفتوح “}” ونادرا ما تكون دالة أو وظيفة لا تحتوى عدد من الأوامر المتداخلة فعندما ترى { لا تعلم أى شيء يخص هذا القوس و تبذل جهدا كبيرا لتتبع الأقواس عبر الصفحات أما لو وجدت END IF فقد علمت أنك أغلقت دورة IF ولم تغلق غيرها ولن تذهب END IF لأقرب شيء مفتوح مثل دورة For لأنه لا يخصها، لذلك أستطيع أن اركز اهتمامى على البرمجة و منطقها عوضا عن فوازير الأقواس التى لا تربح جوائز - معذرة هذا رأيى المتواضع و لكل رأى يسعد به أخيرا لاحظنا كلمة ByVal ولم نقل عنها شيئا، هى اختصار By Value و مقابلها ByRef أى By Reference. لو لدينا متغير ما مثل Angle و لديك وظيفة باسم ChK_A فيها ستختبر قيمة Angle و بناء عليها تفعل خيار من خيارات كثيرة قد تحتاج لتعليمه مثل If sin(Angle) =.5 Then فماذا سيفعل المترجم؟؟ سابقا هو خصص مثلا الخانة 10 من الذاكرة لحفظ قيمة Angle و فى هذه اللحظة Angle=45 و الآن تطلب منه استخدام دالة من داخل دالتك ChK_A لحساب جيب الزاوية، إذن هل سيرسل للدالة قيمة 10 حيث تقبع قيمة Angle أم يرسل المحتوى 45؟ و ما الفرق !! كلاهما واحد!! كلا الفرق شاسع فلو أرسل القيمة By_Val فالتغيير داخل الدالة سيكون على القيمة 45 فقط أما لو أرسلتها By_ref فالتغيير سيكون على محتوى الخانة. وهذا هو الوضع الافتراضى أى أن أى تغيير داخل الدالة سيطال المتغير الأصلى. مهلا سبق أن قلت أن الدالة لا تعيد شيئا وهنا تقول أنها تغير من قيمة المتغيرات أى أنها تعيد شيئا!! أجل وهذه ميزة يمكنك استخدامها و تسمى الأثر الجانبى أو الغير مباشر Side Effect المرة القادمة إن شاء الله نتكلم عن الأوامر أو الأكواد التى تستخدم لبناء البرنامج |
#33
|
|||
|
|||
![]()
الأكواد العشرة
تنقسم الأكواد العشر لثلاث مجموعات هى الشروط و الدورات و الانتقال الشروط: الأمر IF الأول هو ما سبق شرحه IF و نعيد شرحه هنا أولا بالأسيمبلى كان فى صورة JB أو JNB وهما القفز لو بت =1 أو = صفر ولكن عموما تحتاج مقارنة رقمين لذا نستخدم معها الأمر CJNE وهو اختصار Compare and Jump if Not Equal وهى تقارن بين المراكم A و قيمة مباشرة أو محتوى ذاكرة مع المراكم أو محتوى مسجل مع المراكم أو باعتبار مسجل من R0,R1 حامل عنوان فيقارن المراكم بالذاكرة التى يشير إليها هذا المسجل أو مسجل مع قيمة مباشرة مثلا سنقارن محتوى المسجل R7 بالقيمة المباشرة 60 هيكسا و ننتقل لو مختلفين إلى Not_Eq. ما يحدث حقا هو طرح القيمة 60 هيكسا من محتوى R7 و بالتالى لو 60 أكبر ستسبب Carry =1 و إلا سيكون Carry = Zero و يمكن التحقق بالأمر JC أى قفز لو Carry =1 كود:
CJNE R7, # 60H, NOT_EQ ; . . . . . . . . ;R7 = 60H. Code if equal sjmp Finish NOT_EQ: JC REQ_LOW ; IF R7 < 60H. Some Code for R7 > 60H. ; . . . . . . . . ;R7 > 60H. sjmp Finish REQ_LOW: code for R7 < 60H. Finish: نفس الشيء بالبيزك و مثال للمقارنة على التساوى فقط فى التالى أو يمكنك التعديل بلا يساوى أو اكبر من أو أصغر من أو ما تشاء من شروط متعددة كود:
If Sw1 = 1 then Goto SW2test Else LEDs = 254 End If كود:
If PORT1 = 1 then ; Level 1 Goto SW2test Else IF PORT2 = 1 THEN ; Level 2 LEDs = 254 ELSE ; Level 2 LEDs= 255 Else ; Level 1 LEDs=255 End If نقطة هامة أخرى، أين تضع End If يعتمد على المترجم فالمترجم باسكوم يعتمد Elseif و يعتبرها امتداد للأولى (وهذا من سمات لغة البيزك المرئى الأصلية للحاسبات) و من ثم تفاضل بين عديد من الخيارات و متى تحقق أحدها خرجت من الدورة ولذا تحتاج End if واحدة فقط فى آخرها و يمكنك وضع IF أخرى متداخلة حيث تشاء لتتمكن من فحص خيارات أخرى متى شئت و هذا يصعب فى ميكروإلكترونيكا فمثلا كود:
If a = b then '…...1 Led0 = 1 elseif c=d then '…....2 Led1=1 elseif porta = 5 then '…......3 if a= 8 then Print Porta = 5 and a=8 elseif g=7 then Motor1 = 1 else Motor 1 =0 end if else if Portc =0 '….....4 Led3=1 else ' ….....5 Led4 = 0 end if (a= 8) AND (porta = 5) إلا أننا وفرنا تنفيذ أمر AND فى كل تساؤل. ميكروإلكترونيكا لا تعتمد elseif و تعتمد بدلا عنه else if بمسافة تفصل بينهما فتعتبر if التالية جديدة و تتطلب End If خاصة نفس فكرة أقواس السى وهذا يجعل الأمور معقدة و مربكة . مما سبق أيضا نستنتج أن ترتيب الأوامر يعطى أولوية الاختبار فقد يسبب تساؤل عن أمر ما أقل أهمية فى عدم فحص أمر آخر أكثر أهمية . و لنكتبه بلغة C كود:
if (PORT1=1) { goto SW2test ; } else if (PORT2) { LEDs = 254 ; } else { LEDs=255; } الأمر Select case أو switch وهو الثانى ، فكما رأينا الحاجة أم الاختراع فقد وضع هذا الأمر لمقارنة قيمة واحدة مقابل عدد كبير من الاحتمالات، لذا فهذا الأمر بالأسيمبلى سيكون ذات التعليمات السابقة و ستكرر الأمر CJNE عدة مرات كل مرة بقيمة اختبار أخرى و لكن لن تستخدم JC إلا لو احتجت لها و ستضع القيمة التى تختبرها مرة واحدة فقط فى المراكم وهى فى المثال التالى قيمة reg و بعد ذلك تكرر CJNE حسب ما تريد، أى ستوفر أمر MOV مع كل مقارنة. و فى البيزك / باسكوم كود:
select case reg case 0 opmode = 0 case 1,2,6 opmode = 1 case 5 TO 7 opmode = 2 Case > 9 : Print “Too High” Case else : PRINT “NOT Valid” end select المهم أنها رقم صحيح (قيد بسبب الميكرو لكن فى الحاسبات الشخصية تكون أى شيء) أو قد يكون نصا أو حرف. هذا المترجم يسمح من لغة البيزك الأصلية بكلمة TO لتحديد مدى "من إلى" كما يسمح بتعدد القيم 1,2,6 و بالعلاقات النسبية أكبر من أو أصغر من أو اكبر من و يساوى أو أصغر من و يساوى لاحظ أنه لو تطابق المتغير مع أول شرط ستنفذ الأوامر المرتبطة به بكاملها (واحد أو أكثر) ثم يخرج آليا من الشرط ولا تختبر باقى الحالات. لو لم يتطابق فسيقارن التالى وهكذا ميكروإلكترونيكا تستخدم الصيغة ذاتها مع تعديل طفيف كود:
select case reg case 0 opmode = 0 case 1,2,3,4 opmode = 1 case 5,6,7 opmode = 2 end select بلغة السى نجدها كود:
switch (phase) { case 0: Lo(); break; case 1: Mid(); break; case 2: Hi(); break; default: Message("Invalid state!"); } لاحظ أيضا أنك لا تستطيع استخدام مدى من .. إلى .. ولا أكثر من قيمة 1,2,3,4 ولا نسب أكبر و أصغر الخ. رقم و فقط و بسبب ذلك قد تضطر لاستخدام أمر IF بدلا منها !!!!! أو تفضل البيزك مثلى المرة القادمة إن شاء الله ندرس الدورات |
#34
|
|||
|
|||
![]()
الدورات Iteration
For Loop الثالث و اشهرها إطلاقا هى دورة For أى بينما المتغير "ص" من كذا إلى كذا افعل هذا ثم عدل قيمة "ص" وهى بالأسيمبلى تكون هكذا كود:
MOV R7,#50 Loop5: DoThis DJNZ R7,Loop5 هذا الأمر بالبيزك لكلا المترجمين تأخذ الصورة For المتغير = البداية TO النهاية بخطوة عدد موجب أو سالب كود:
Dim A As Byte , B1 As Byte , C As Integer For A = 1 To 10 Step 2 Print "This is A " ; A Next A * Print "Now lets count down" For C = 10 To -5 Step -1 Print "This is C " ; C Next كود:
For c = 1 To 10 … For D = 2 to 8 … Next D …. …. Next c أما بلغة C فلأمر كما هو معروف مختلف حيث تكون كود:
for ( i = 0; i < n; i++ ) s += a[i] * b[i]; لو لديك أكثر من أمر للتنفيذ عندها تضعهم بين قوسين معوجين {…..} و كل سطر ينتهى بالمقدسة ";” هذه اللغة تتيح لك استخدام أكثر من متغير معا Do Loop الرابع و بذات كود الأسيمبلى السابق يمكنك أن تهئ هذه الدورة، يمكنك تحقيقها بالأمر DJNZ أو CJNE السابقين أما بالبيزك فالصيغة المشتركة هى كود:
A = 1 'assign a var Do 'begin a do..loop Print A 'print var Incr A 'increase by one Loop Until A = 10 'do until a=10 كما لاحظت أننا نقول دوما ذات الكود، فعلا هى دورة تكرارية تنتهى بمقارنة ثم القفز للبداية أو القفز خارجها فلم نتوقع كود مختلف؟ هى فقط تعبيرات مختلفة لتسهيل الوصول للهدف لو حذفت جملة Until A = 10 ستتحول إلى دورة غير منتهية فلا شرط ينهيها و هى بذلك تتحول إلى أمر GOTO وهى مطابقة للأمر AJMP أو LJMP بالأسيمبلى. نلاحظ هنا أن الشرط Until A = 10 أو ما يسمى بالاختبار يكون فى آخر الدورة مما يضمن تنفيذها مرة واحدة على الأقل لذا سيكون لدينا أمر آخر لضمان عدم التنفيذ. أما بلغة C كود:
do { s += a[i] * b[i]; i++; } while ( i < n ); كما ذكرنا لو نريد عدم التنفيذ إطلاقا لو الشرط غير محقق نلجأ للأمر الخامس While / While-WEND الاختلاف الوحيد فى الأمر الخامس هو وضع الشرط و اختباره فى البداية و لهذا فإن لم يتحقق لا تنفذ باقى الأوامر و بالأسيمبلى فغنى عن التعريف أنها مسألة إعادة تنسيق للأوامر فقط بالبيزك تكون كود:
A = 1 'assign var While A < 10 'test expression * Print A 'print var * Incr A 'increase by one Wend 'continue loop بلغة C كود:
while (i < n) { s += a[i] * b[i]; i++; } |
#35
|
|||
|
|||
![]()
الانتقال أو التفرع Branching
الحقيقة هنا بعض الأوامر مختلفة بين اللغات لذلك فالعدد هنا يعتمد على اللغة لكن أشهرها و اعمها هى الأمر السادس GOTO و صيغته ببساطة Goto ثم العنوان المراد الانتقال إليه وهو غير مشروط ولا خلاف عليه فى اللغات وهو يسبب الانتقال المباشر من مكان هذا الأمر للعنوان المذكور بدون قيد أو شرط ماذا يحدث لو دخلت فى دورة مثل For و تأكد لك أنها انتهت مبكرا لتحقق شرط ما أو اصبح الاستمرار فيها يسبب مشكلة ما؟ حسنا توافر لك الأمر السابع وهو Exit Exit Break Continue الأمر السابع هو أمر للخروج الطارئ وفى باسكوم لديك أربعة صور هى حيث Exit Sub و Exit Function هما ذات الشيء EXIT FOR EXIT DO EXIT WHILE EXIT SUB EXIT FUNCTION و تكتب كما هى أى لا تكتفى بكلمة Exit ولكن تتبعها بالدالة وهى للخروج الطارئ عند حدوث خطأ ما أو جدت أنه لا داعى للاستمرار . و سبب ضرورة ذكر اسم الدالة لا يخفى على أحد فغالبا ما تكون مثلا دورة For داخل وظيفة Function أو داخل دورة أخرى مثل Do لذا وجب تحديد مما تخرج بالضبط. فى ميكروإلكترونيكا و فى كلا المترجمين البيزك و C قصرت على SUB أو Function وتكتب وحدها فقط أى لو لديك وظيفة أو دالة ثم فى خطوة ما وجدت أنه لا داعى للاستمرار أوحدث خطأ ما و يجب الخروج تكتبها فتعود لاستكمال الكود الأصلى. أما باقى ألدوال For,DO,WHILE فقد استخدمت كلمة Break للخروج و نذكر أننا تعرضنا لها فى أمر Switch السابق و أضافت أمر آخر هو Continue لتفويت أو تخطى هذه الدورة فقط دون الخروج و استكمال باقى الدورات الأمر الثامن هو العودة Return Return قبل مناقشته يجب أن نعود للأسيمبلى للارتباط الوثيق بينهما. كلنا نعلم ما هو Subroutine و لمن لا يعلم هو قطعة من الكود ستتكرر كثيرا فى البرنامج ولو أعدنا كتابتها قد لا نجد ذاكرة تكفى، تخيل أنك لو كل مرة تريد كتابة رقم أو حرف على شاشة ستكتب لها كود، كم مليون مرة ستحتاج لكل تحديث للمعروض ؟ ، لذا يكتب هذا الكود فى عنوان فى مكان ما من الذاكرة أى يبدأ بهذا العنوان وهذا الكود يسمى Subroutine أى إجراء فرعى وهو فى اللغات العالية تحول إلى دالة أو وظيفة أما كونه يعيد قيمة ما فببساطة يضعها فى خانات من الذاكرة محددة ليستغلها الكود الطالب لاحقا. حسنا كيف ستنهيه إذن لتكمل باقى البرنامج أو حتى تكتب كود Subroutine آخر؟ أجل الأمر GOTO لكن مهلا نذهب لأين و أنا أصلا لا أعلم من أين استدعيت؟ حسنا يجب إذن أن نضيف لها أمر نهاية خاص بكود مختلف وهو أمر RET اختصار Return و أمر الانتقال إليه لا يكون بالأمر GOTO ولكن أيضا بأمر مختلف GOSUB إذن المتحكم عندما يجد الأمر GOSUB متبوعا بعنوان ، يعلم أنه عائد للخطوة التالية لذا سيحفظ عنوان العودة (عنوان الخطوة التالية) فى الرصة Stack و يذهب لعنوان Subroutine المذكور فى الأمر و ينفذه و عندما ينتهى سيجد الأمر RET وهذا يجعله يستعيد العنوان من الرصة ليستأنف العمل حيث كان قبل طلب Subroutine حسنا هذا جميل لكن مهلا هناك موضوع آخر على قدر كبير من الأهمية وهو المقاطعة ، عندما تحدث سيفعل المتحكم ذات الشيء من حفظ الخطوة التالية ثم الانتقال لعنوان خدمة المقاطعة و يوقف كافة المقاطعات التالية و يجب عندما يعود من خدمه المقاطعة أن يتيح المقاطعة أيضا إضافة لاسترجاع العنوان ، لذا فكود المقاطعة ينتهى بأمر مختلف هو RETI اختصار Return from Interrupt أى أن كلاهما سيعيد المتحكم للعنوان الصحيح لكن المقاطعة ستختلف و الخطأ فى استخدامها قد يسبب أن المتحكم يستجيب لأول مقاطعة ثم يمتنع بعد ذلك وقد تظن أنه لا يستجيب إطلاقا لو كانت أول مقاطعة من مصدر سريع مثل 50 هرتز الخ. هذا فضلا عن أن فى كثير من المتحكمات يتم حفظ بعض المسجلات آليا و استعادتها آليا لضرورة ما بها من بيانات و أهميتها. هذا التمهيد يتيح لنا فهم الخلافات ففى ميكروإلكترونيكا جعلت الأمر Return واحدا وهو العودة للخطوة الطالبة أى ستولد RET أما لو كان الأمر يخص مقاطعة فإعلان كود المقاطعة هو ألذى سيحدد أن العودة ستكون RETI . أما فى باسكوم فالأمر مختلف ، لو فى داخل شرط ستعطى RET ولو بدون شرط فأول واحدة فقط تعطى RETI وهذا فى رأى المتواضع تعقيد لا جدوى منه و سيفرض استخدام الأمر GOTO لتذهب حيث الأمر ألذى يعطى RETI الأمر التاسع هو GOSUB GOSUB هو أمر للانتقال إلى Subroutine سواء فى الكود أو داخل وظيفة أو دالة قد ترى أن الأمر مكرر ولا جدوى من هذا الأمر لكن معذرة أخى الكريم فللضرورة أحكام و السبب ببساطة أنك لا تستطيع أن تعلن عن وظيفة أو دالة من داخل أخرى لذا فإن احتجت لتكرار كود داخل دالة فإما تجعله فى دالة منفصلة و هذا يزيد عددهم بلا داعى أو لو هذا التكرار لا يطلب إلا فى هذه الدالة أو وظيفة فقط فلتسهيل تتبع الكود يمكنك أن تجعلها داخلية بهذا الأمر وهى ستطال كافة المتغيرات أى لا حاجة لتفريقها كدالة أو وظيفة فهى فقط Subroutine الأمر بسيط وهو GOSUB متبوعا بالعنوان وهو فى البيزك لكلا المترجمين لكن للأسف C لا يدعمه الأمر العاشر و الأخير هو On Value استخدمت باسكوم أمرا من أوامر البيزك المرئى للحاسب مع المتحكمات و هو أن تكتب ON متبوعة بمتغير ثم تختار GOTO أو GOSUB ثم مجموعة من العناوين كود:
ON var [GOTO] [GOSUB] label1 [, label2 ] [,CHECK] إذن ما هذه الضجة حول اللغات العالية و المترجمات؟ هى يا عزيزى تسمى المكتبات وهى مجموعة أوامر إضافية تتبناها كل شركة لخدمة عملائها مثل كتابة الشاشة LCD و قراءة أزرار Keypad و التعامل مع المنفذ التسلسلى أو I2C الخ. لذا فهى تختلف من مترجم لآخر و ليست من لغة لأخرى فقط. ولا حل أمامنا سوى ملفات المساعدة المرة القادمة إن شاء الله نعيد البرنامج بالبيزك ثم إن شاء الله بعده بلغة C |
#36
|
|||
|
|||
![]()
الغسالة باستخدام C51 و لغة البيزك:
من الأفضل أن نعيد هنا ذات الدائرة حتى نرجع إليها كما فعلنا سابقا نشرح المطلوب. نهيء المتحكم ونجهز المقاطعات ثم نفحص الأزرار و نضئ ألليد المناظر. إن كان زر التشغيل مع الباب مغلق إذن لو دورة بارد ننتقل للتشغيل و إلا نشغل السخان و إن كان مطلوب دافئ ننتظر حساس دافئ و إلا ننتظر الساخن نحدد الزمن طبقا للدورة ثم نبدأ الفترة الأولى ثم نفتح الصرف و نغلقه و نعيد ملئ الخزان و نشغل دورة شطف ثم نكرر الشطف ثم أخيرا دورة عصر ثم توقف بيزك باسكوم و نبدأ بالبيزك ، باسكوم ثم سنستخدم ميكروإلكترونيكا لتبيان الفرق ثم C . عند بدء مشروع جديد سيفتح لك صفحة خالية و من القائمة Option\Compiler\chip تحدد اسم المتحكم و تردد الكريستال الخ. ثم تنقر زر Add Code ثم تعود للصفحة و إن شئت تكتب فى أول سطر تعليق أنها تعريفات Definition فلا بأس يلى ذلك التعريفات السابق استخدامها. بما أننا نستخدم ذات الدائرة فللتسهيل يمكنك نسخ الكود بالأسيمبلى كاملا من بروتس و لصقه هنا ثم من قائمة Edit اختار Replace أو انقر CTRL+R و اكتب فى الخانة الأولى كلمة EQU و فى التالى كلمة Alias واختار Replace all و بهذا انتهت أول خطوة إن شئت يمكنك تجميع كل ثلاثة أوامر فى سطر واحد باستخدام ":” و إن شئت فلا ولكنى سأفعلها حتى لا تكون الصفحة طويلة خاصة فى المنتديات- لاحظ أن وجود كود الأسيمبلى لن يضير المترجم و ستصبح هكذا التعريفات كود:
' DEFINITIONS Leds Alias P0 : Relays Alias P1 : Switches Alias P2 : Controls Alias P3 Fullled Alias Leds.0 : Halfled Alias Leds.1 : Smallled Alias Leds.2 Hotled Alias Leds.3 : Mildled Alias Leds.4 : Coldled Alias Leds.5 Wateron Alias Relays.0 : Drainon Alias Relays.1 : Heateron Alias Relays.2 Motoron Alias Relays.3 : Run_nspin Alias Relays.4 : Waterfull Alias Relays.5 Waterempty Alias Relays.6 : Dooropen Alias Relays.7 : Midtemp Alias Switches.0 Hitemp Alias Switches.1 : Cold Alias Switches.2 : Mild Alias Switches.3 Hot Alias Switches.4 : Small Alias Switches.5 : Half Alias Switches.6 Full Alias Switches.7 : Run Alias Controls.1 ' VARIABLES Dim Runtime As Word Jobpending Alias PSW.5 Emergency: Goto Begin Counttime: Decr Runtime If Runtime = 0 Then Reset Jobpending Stop Timer0 End If Return هنا ملاحظة أننا عندما ضغطنا على زر "صح" سيقوم المترجم بفحص الكود فإن لم يجد أخطاء سيكتب رسالة خضراء أن ألأمر تم بنجاح أما لو هناك خطأ ما ، فسيتوقف على أول سطر به خطا و يضع عليه شريط أحمر و على اليسار دائرة بها حرف E . يوجد أسفل يسار الواجهة زر مكتوب عليه Errors انقر عليه لتقرأ قائمة الأخطاء و السطر المتصل بها و نصيحة حاول دوما تصحيح الخطأ الأول فقط ثم أعد النقر على الزر "صح" فكثيرا ما يكون الخطأ فى تحليل المترجم لهذا الخطأ تسبب فى الظن الخاطئ فى كثير مما يليه، لذا فالأول بالأول. ثم بعد ذلك سنجد عنوان INT0 (IE0)Emergency وهو خانة 3 فى الذاكرة ولكن المترجم سيتولى ذلك فكيف نعرفه ما نريد أى أن هذا الكود لمقاطعة و أيها هى؟ حسنا نضع عنوان فى أى مكان و ليكن Emergency: و نكتب تحته ما فعلناه فى البرنامج السابق و كان sjmp Start لكن بمجرد كتابة الكلمة Start ستظهر بجوار مؤشر الكتابة مربع به ثلاث خيارات للمؤقتات الثلاثة ، إذن كلمة Start من الكلمات المحجوزة لتشغيل و إيقاف المؤقتات و لن تصلح كعنوان فنستبدله بكلمة Begin و التى لا تسبب شيء فنضع بعدها مباشرة ":” لتصبح عنوان ولا تكتب شيء بعده على السطر . لاحظ أنك تفصل بين الأوامر بالحرف " : “ و الفارق هو مسافة قبله و بعده لذا لو وضعتها ستحصل على رسالة خطأ أن الأمر Emergency غير معروف فى السطر رقم كذا ، الآن sjmp Start ستصبح Goto Begin وهذا فقط ما يفعله لكن لم يعلم بعد أنه لمقاطعة سنكتب عنوان جديد للمقاطعة الثانية الخاصة بالتايمر صفر وكان لحساب الزمن فكنا ننقص الثوانى و من ثم الدقائق فإن أصبحت صفرا نوقف العداد و ننزل العلم أى نصفر F0 و هنا دمجناهما معا فى متغير واحد اسمه Runtime و لذا ننقصه ثم نختبره بالأمر IF فإن تحقق نستخدم الأمر RESET لجعل أى بت = صفر أى أنه صالح لبت من منفذ أو من متغير بأى حجم و عكسها Set و تكتب RESET x حيث x اسم البت أو RESET y.x حيث y هو المتغير و x رقم البت فيه وبذلك جعلنا العلم = صفر و الأمر Stop Timer0 لإيقاف التايمر و أخيرا أمر العودة من المقاطعة Return و أيضا هذا هو كود المقاطعة لكنه لم يعلم بعد أنها كذلك هنا ستسأل، لماذا لم تكتب فى الأولى أيضا Return ؟ معك حق لكنها لن تنفذ لأن قبلها أمر انتقال للبداية وهناك يجب أن نتأكد أن كل الأمور مهيأة على الوجه الكامل. أرجو أن تمحو كل الأوامر بالأسيمبلى التى استبدلناها بالبيزك. ألآن سيكون لدينا البداية Begin و أول سطر كان كود:
MOV IE,#10000011b ' Enable T0 & INT0 external MOV TMOD,#00000110b ; T0 8bit counter auto reload لم نعد بحاجة إليه ولا للسطر الخاص TMOD فسنستخدم بدلا منهما الأمر CONFIG TIMERx = COUNTER/TIMER , GATE=INTERNAL/EXTERNAL , MODE=0/3 مجرد أن تكتب CONFIG ستظهر قائمة تختار منها بالأسهم أول بند هو TIMER0 ثم اضغط مسافة أو Enter أو إدخال فتضاف للكود ثم تكتب علامة = فيظهر خيارين COUNTER و TIMER و بالأسهم اختار COUNTER ثم مسافة أو Enter أو إدخال ثم تكتب فاصلة , فتظهر لك أربع خيارات MODE=x و تختار منها 2= MODE و اضغط مسافة و فاصلة لتختار بين INTERNAL/EXTERNAL و طبعا EXTERNAL لأنه سيعد نبضات من الخارج و بعد ذلك نضيف القيم 205 بالأمر = بدلا من الأمر mov ثم قيمة TCON لتجعل INT0 قدح بالحافة . التهيئة تجهز العداد لكن يبقى متوقف لذا نكتب Enable فتفتح قائمة نختار آخرها كلمة Interrupts لإتاحة الكل ثم نكرر ونختار Int0 لإتاحة المقاطعة. الأمر MOV SP أيضا سيتولاه المترجم ولم نعد بحاجة إليه هنا نلحظ فرق بين الأسيمبلى و اللغة العالية أننا فى الأسيمبلى بأمر واحد وضعنا فى IE إتاحة ما تشاء من المقاطعات بينمت هنا خطوة لكل شيء تتيحه. طبعا يمكنك استخدام الأمر IE=xx كما سبق لكن ستشرح ما هو المتاح و الغير متاح لتتذكره لاحقا مقابل الاختصار. المترجم الذكى يدرك سلسلة الأوامر المتتالية للإتاحة و يجمعها فى أمر واحد أيضا. كود:
Begin: Config Timer0 = Counter , Mode = 2 , Gate = Exteral TH0 = 205 ; Auto reload TL0 = 205 TCON = 1 ; Clr All interrupts, Set INT0 Edge trigger Enable Interrupts Enable Int0 Leds = 11011011b ' Reset All PORTS! select Small cold cycle Relays = 255 Switches = 255 Controls = 255 On Int0 Emergency , Nosave On Timer0 CountTime Bitwait Run , Set ' Run button release LoopAgain: On Int0 Emergency , Nosave On Timer0 CountTime والأول يعنى "عند حدوث مقاطعة من" Int0 انتقل للعنوان Emergency ولا تحفظ أى من قيم المسجلات فى الرصة و الثانى يعنى "عند حدوث مقاطعة من" Timer0 انتقل للعنوان CountTime مع حفظ أى من قيم المسجلات فى الرصة القيمة الفرضية عندما لا تكتب كلمة "Nosave” أن يحفظ البرنامج المسجلات التى تستخدم فى خطوات برنامج خدمة المقاطعة وهو أسلم بالطبع وبهاتين الخطوتين عرفنا المتحكم ماذا يفعل عند كل مقاطعة . و أخيرا كلمة Loop من كلمات اللغة لذا نستبدلها بكلمة LoopAgain و هكذا انهينا ألتهيئة ولو راجعتها الآن ستجدها أسهل من الأسيمبلى فأمر يغنيك عن كثير لكن أحيانا تجد أنك أقل حرية من الأسيمبلى. المرة القادمة إن شاء الله نكتب باقى البرنامج. |
#37
|
|||
|
|||
![]()
أول البرنامج
بعد التهيئة السابقة وصلنا لأول البرنامج حيث العنوان التكرارىLoopAgain بعد ذلك الاختبار If Full = 0 Then أى لو زرار Full مضغوط إذن نفعل السابق وهو ORL ثم LEDs,#00000111b ، الوظيفة "أو" OR وهى بالأسيمبلى ORL وفى البيزك OR لأنه هنا لا يعتمد النوع الآخر Boolean من OR و تتحول الوظيفة لهذه الصيغة Leds = Leds Or &B00000111 أى أن اجعل LEDs تساوى = LEDs OR 00000111 و هنا الرمز &B ليعنى أن الرقم بالنظام ألثنائى Binary قبل أن نترك هذه الخطوة نعلم أن هناك أربعة وظائف هى OR,AND,NOT, EXOR وهى أو – مع – عاكس – أو المطلقة وهى كما هى هكذا لا تحتاج لتفكير ولا تذكر ولا تستخدم رموز أو إشارات – لغة انجليزية بسيطة و مفهومة كان من الممكن أن نكمل بطريقتين، الأولى كود:
If Full=0 Then Leds = Leds Or &B00000111 : Reset FulLed End if If Half = 0 Then Leds = Leds Or &B00000111 : Reset HalfLed End If if Small = 0 Then Leds = Leds Or &B00000111 : Reset SmallLed End If البرنامج أصبح كود:
LoopAgain: If Full = 0 Then Leds = Leds Or &B00000111 : Reset FulLed Elseif Half = 0 Then Leds = Leds Or &B00000111 : Reset HalfLed Elseif Small = 0 Then Leds = Leds Or &B00000111 : Reset SmallLed End If If Hot = 0 Then Leds = Leds Or &B00111000 : Reset Hotled Elseif Mild = 0 Then Leds = Leds Or &B00111000 : Reset Mildled Elseif Cold = 0 Then Leds = Leds Or &B00111000 : Reset Coldled End If If Run = 1 Then Goto Loop ' Wait Run Press Bitwait Dooropen , Reset ' Wait Door to close Reset Wateron ' Fill with water Bitwait Waterfull , Reset ' Wait full Tank Set Wateron ' Close water Valve فى الأمر التالى استخدمنا فى الأسيمبلى الأمر JNB ColdLED,SetCycle ; Cold cycle No Heat needed لنختبر إذا زرار البارد قد اختير ننتقل و إلا نشغل السخان و تترجم كود:
If Coldled = 1 Then Reset Heateron ' No Cold then Heater On كود:
JNB MildLED,HOTTemp jb HiTemp,$ ; wait mild sensor sjmp SetCycle HOTTemp: jb MidTemp,$ ; wait HOT sensor و تترجم هكذا If Coldled = 1 Then ' not Cold then Reset Heateron ' Heater on If Mildled = 0 Then Bitwait Midtemp , Reset Else Bitwait Hitemp , Reset Set Heateron ' Turn off heater End If ' Now Start Washing كود:
SetCycle: ; Now Start Washing JNB FullLED,HalfCycle MOV Minutes,#20 sjmp RunMotor HalfCycle: JNB HalfLED,SmallCycle MOV Minutes,#8 sjmp RunMotor SmallCycle: MOV Minutes,#5 RunMotor: و تترجم هكذا If Fullled = 0 Then Runtime = 20 * 60 Elseif Halfled = 0 Then Runtime = 480 Else Runtime = 300 End If باقى الدورات موضوعنا القادم بإذن الله |
#38
|
|||
|
|||
![]()
باقى الدورات
ألان ندير الموتور الفترة الزمنية المحددة ، ولكى نعرف الزمن يجب أن نقيم JobPending أى نجعله = 1 و ندير الموتور و ننتظر JobPending أن يحدد نهاية المدة و كانت هكذا كود:
RunMotor: MOV Seconds,#59 SETB JobPending CLR MotorOn SETB TR0 ; Start Timer/Counter JB JobPending,$ ; Wait to Finish SETB MotorON ; stop motor كود:
Set Jobpending ' Timing flag Reset Motoron ' Start Motor Start Timer0 ' Start Timer/Counter Bitwait Jobpending , Reset ' Wait to Finish Set Motoron كود:
CLR DrainON ; Open Drain JB WaterEmpty,$ ;Wait Drain Setb DrainON ; Close Drain CLR WaterON ; Fill again JB WaterFull,$ SETB WaterON ; Close Water MOV Minutes,#5 ; 1St Rinse MOV Seconds,#59 SETB JobPending CLR MotorOn ; Start Rinse SETB TR0 ; Start Timer JB JobPending,$ ; Wait to Finish Setb MotorOn ; Stop Rinse CLR DrainON ; Open Drain second cycle JB WaterEmpty,$ ;Wait Drain Setb DrainON ; Close Drain CLR WaterON ; Fill again JB WaterFull,$ SETB WaterON ; Close Water MOV Minutes,#5 ; 2nd Rinse MOV Seconds,#59 SETB JobPending SETB TR0 ; Start Timer/Counter CLR MotorOn ; Start Rinse SETB TR0 ; Start Timer JB JobPending,$ ; Wait to Finish Setb MotorOn ; Stop Rinse كود:
Mov Rinse,#2 RinseLoop: CLR DrainON ; Open Drain JB WaterEmpty,$ ;Wait Drain Setb DrainON ; Close Drain CLR WaterON ; Fill again JB WaterFull,$ SETB WaterON ; Close Water MOV Minutes,#5 ; 1St Rinse MOV Seconds,#59 SETB JobPending CLR MotorOn ; Start Rinse SETB TR0 ; Start Timer JB JobPending,$ ; Wait to Finish Setb MotorOn ; Stop Rinse Djnz Rinse, RinseLoop ألان نترجم هذا بلغة البيزك نضيف فى التعريفات Dim Rinse As Byte , cntr as byte و هنا تترجم التعليمات هكذا . كود:
For cntr = 1 To Rinse Reset Drainon ' Open Drain Bitwait Waterempty , Reset ' Wait Drain Set Drainon ' Close Drain Reset Wateron ' Fill again Bitwait Waterfull , Reset Set Wateron ' Close Water Runtime = 300 ' Rinse Time Set Jobpending Reset Motoron ' Start Rinse Start Timer0 ' Start Timer Bitwait Jobpending , Reset ' Wait to Finish Set Motoron ' Stop Rinse Next ' Loop Back كود:
CLR DrainON ; Open Drain JB WaterEmpty,$ ;Wait Drain MOV Minutes,#3 ; SPIN MOV Seconds,#59 SETB JobPending CLR MotorOn ; Start Rinse CLR Run_Nspin ; SPIN SETB TR0 ; Start Timer/Counter JB JobPending,$ ; Wait to Finish MOV Relays,#255 ; ALL OFF, INITIAL State MOV Controls,#255 jmp Loop ;==================================== END وتترجم هكذا Reset Drainon ' Open Drain second cycle Bitwait Waterempty , Reset ' Wait Drain Runtime = 180 ' SPIN Set Jobpending Reset Motoron ' Start Rinse Reset Run_nspin ' SPIN Start Timer0 ' Start Timer/Counter Bitwait Jobpending , Reset ' Wait to Finish Relays = 255 ' ALL OFF, INITIAL State Controls = 255 Goto Loopِagain |
#39
|
|||
|
|||
![]()
البرنامج بمترجم بيزك من ميكروإلكترونيكا
ها هو البرنامج كاملا لمترجم باسكوم و سنعدل ما نحتاج فقط كود:
' DEFINITIONS Leds Alias P0 : Relays Alias P1 : Switches Alias P2 : Controls Alias P3 Fullled Alias Leds.0 : Halfled Alias Leds.1 : Smallled Alias Leds.2 Hotled Alias Leds.3 : Mildled Alias Leds.4 : Coldled Alias Leds.5 Wateron Alias Relays.0 : Drainon Alias Relays.1 : Heateron Alias Relays.2 Motoron Alias Relays.3 : Run_nspin Alias Relays.4 : Waterfull Alias Relays.5 Waterempty Alias Relays.6 : Dooropen Alias Relays.7 : Midtemp Alias Switches.0 Hitemp Alias Switches.1 : Cold Alias Switches.2 : Mild Alias Switches.3 Hot Alias Switches.4 : Small Alias Switches.5 : Half Alias Switches.6 Full Alias Switches.7 : Run Alias Controls.1 ' VARIABLES Dim Runtime As Word Jobpending Alias Psw.5 Dim Rinse As Byte , cntr as byte Emergency: Goto Begin Counttime: Decr Runtime If Runtime = 0 Then Reset Jobpending Stop Timer0 End If Return نظرا لعدم قبوله أكثر من أمر لكل سطر فسنضطر إلى التعريف هكذا program WM ' Declarations section symbol FullLED = LEDs.0 symbol HalfLED = LEDS.1 symbol SmallLED = LEDs.2 symbol HOTLED = LEDs.3 symbol MildLED = LEDs.4 symbol ColdLED = LEDs.5 symbol WaterOn = Relays.0 symbol DrainOn = Relays.1 symbol HeaterOn = Relays.2 symbol MotorOn = Relays.3 symbol Run_Nspin = Relays.4 symbol WaterFull = Relays.5 symbol WaterEmpty = Relays.6 symbol DoorOpen = Relays.7 symbol MidTemp = Switches.0 symbol HiTemp = Switches.1 symbol Cold = Switches.2 symbol Mild = Switches.3 symbol Hot = Switches.4 symbol SmallLoad = Switches.5 symbol Half = Switches.6 symbol Full = Switches.7 symbol RUN = Controls.1 Dim Leds as byte at P0 dim Relays as byte at P1 Dim Switches as byte at P2 Dim Controls as byte at P3 Dim JobPending as sbit at PSW.F0 dim RunTime as word dim Rinse as byte dim cntr as byte sub procedure INT0_Interrupt() org IVT_ADDR_EX0 asm sjmp 0 end asm end sub sub procedure Timer0_Interrupt() org IVT_ADDR_ET0 Dec (RunTime) If RunTime = 0 then JobPending = 0 TR0_bit = 0 ' Stop Timer1 end if end sub للمقاطعة يستخدم هذا المترجم تعريف مختلف sub procedure INT0_Interrupt() org IVT_ADDR_EX0 حيث Sub كلمة يستخدمها دوما كما فى فكر لغة C وهى زائدة كما بينا فى باسكوم فهو يتبعها بكلمة procedure أو كلمة Function وواحدة تغنى ثم اسم المقاطعة وهو هنا INT0_Interrupt() ثم كلمة org أو IV لتخبره أنها مقاطعة Interrupt Vector و أخيرا العنوان IVT_ADDR_EX0 فى المقاطعة الخاصة بالطوارئ كنا سنستخدم انتقال للبداية GOTO Begin لكنها لا تقبل رقم و تطلب عنوان "سبق تعريفه" لذا إما ننقل كل الكود لآخر البرنامج أو لنتحايل و نبقى على التتابع استخدمنا الأسيمبلى بالتعريف asm ثم القفز ثم end asm. المقاطعة الثانية نستخدم نفس الأوامر. يلى ذلك أوامر التهيئة ما بين Begin , LoopAgain كود:
Begin: Config Timer0 = Counter , Mode = 2 , Gate = Exteral Th0 = 205 ' Auto reload Tl0 = 205 Tcon = 1 Enable Interrupts Enable Int0 Leds = &B11011011 ' Reset All PORTS! select Small cold cycle Relays = 255 Switches = 255 Controls = 255 Rinse = 2 On Int0 Emergency , Nosave On Timer0 Counttime الأسطر الحمراء لا يدعمها هذا المترجم ولذلك سنضطر لتهيئة التايمر يدويا كما سبق فى الأسيمبلى كما أن نتيح المقاطعة و نبدأ و نوقف العداد بالأمر النصى بدلا من Enable فتصبح IE=%10000011 ' Enable T0 & INT0 external TH0 = 205 ' Auto reload TL0 = 205 ' Start Value TMOD = %00000110 TCON = 1 ' Clear All interrupts, Set INT0 Edge trigger SP = 70 ' Move stack away Leds = %11011011 ' Reset All PORTS! select Small cold cycle Relays = 255 Switches = 255 Controls = 255 Rinse = 2 الشرط لو Full=0 سيتطلب الأمرين التاليين و نظرا لحدود المترجم سنحتاج سطرين منفصلين الأول كالسابق مع تغيير &B واستخدام الحرف % ثم السطر الثانى لا يدعم المترجم الأمر Reset و لكنه يوفر أمر SetBit(P0, 2) ' Set P0.2 و الأمر ClearBit(P0, 7) ' Clear P0.7 والمشكلة هنا أنهما لا يقبلا سوى الأمر الصريح كما بالمثالين السابقين أى لا نستطيع الاستفادة بالتسميات ولو أردنا التعديل لاحقا سنفحص كل سطر بحثا عنها لذا سنستخدم الأمر الصريح أيضا . أيضا يجب ملاحظة أن هذا المترجم لا يدعم الأمر Elseif لذا يجب أن نكتب Else و فى داخلها نضع If جديدة لها Else وهكذا و نضع End If بعدد If المستخدمة عكس باسكوم End If واحدة للدورة كلها مما يجعل باسكوم أكثر وضوحا. الكود يصبح كود:
do ' Wait Run =1 unpressed loop until Run = 1 ' Run button release Loopagain: If Full = 0 Then Leds = Leds Or %00000111 FullLED = 0 Else if Half = 0 Then Leds = Leds Or %00000111 Halfled = 0 Else if SmallLoad = 0 Then Leds = Leds Or %00000111 Smallled = 0 end if end if End If If Hot = 0 Then Leds = Leds Or %00111000 Hotled = 0 Else if Mild = 0 Then Leds = Leds Or %00111000 Mildled = 0 Else if Cold = 0 Then Leds = Leds Or %00111000 Coldled = 0 end if end if End If If Run = 1 Then Goto LoopAgain ' Wait Run Press كود:
do loop until Run = 1 ' Run button release do If Full = 0 Then Leds = Leds Or %00000111 FullLED = 0 Else if Half = 0 Then Leds = Leds Or %00000111 Halfled = 0 Else if SmallLoad = 0 Then Leds = Leds Or %00000111 Smallled = 0 end if end if End If If Hot = 0 Then Leds = Leds Or %00111000 Hotled = 0 Else if Mild = 0 Then Leds = Leds Or %00111000 Mildled = 0 Else if Cold = 0 Then Leds = Leds Or %00111000 Coldled = 0 end if end if End If loop until RUN =0 |
#40
|
|||
|
|||
![]()
تكملة البرنامج
بعد ذلك غلق الباب و تحديد الفترة الزمنية حسب الخيارات و كانت كود:
Bitwait Dooropen , Reset ' Wait Door to close Reset Wateron ' Fill with water Bitwait Waterfull , Reset ' Wait full Tank Set Wateron ' Close water Valve If Coldled = 1 Then ' not Cold then Reset Heateron ' Heater on If Mildled = 0 Then Bitwait Midtemp , Reset Else Bitwait Hitemp , Reset Set Heateron ' Turn off heater End If ' Now Start Washing If Fullled = 0 Then Runtime = 20 * 60 Elseif Halfled = 0 Then Runtime = 480 Else Runtime = 300 End If و كما سبق التعديل ستصبح بعد استبدال Waitbit و Elseif do ' Wait Door to close loop until Dooropen = 1 Wateron = 0 ' Fill with water do loop until Waterfull = 1 ' Wait full Tank Wateron = 1 ' Close water Valve If Coldled = 1 Then ' not Cold then Heateron = 0 ' Heater on If Mildled = 0 Then do loop until Midtemp = 0 Else do loop until Hitemp = 0 end if Heateron = 1 ' Turn off heater End If ' Now Start Washing If Fullled = 0 Then Runtime = 20 * 60 Else if Halfled = 0 Then Runtime = 480 Else Runtime = 300 end if End If ' Runmotor: كود:
Runmotor: Set Jobpending ' Timing flag Reset Motoron ' Start Motor Start Timer0 ' Start Timer/Counter Bitwait Jobpending , Reset ' Wait to Finish Set Motoron ' stop motor For cntr = 1 To rinse Reset Drainon ' Open Drain Bitwait Waterempty , Reset ' Wait Drain Set Drainon ' Close Drain Reset Wateron ' Fill again Bitwait Waterfull , Reset Set Wateron ' Close Water Runtime = 300 ' 1St Rinse Set Jobpending Reset Motoron ' Start Rinse Start Timer0 ' Start Timer Bitwait Jobpending , Reset ' Wait to Finish Set Motoron ' Stop Rinse Next Reset Drainon ' Open Drain second cycle Bitwait Waterempty , Reset ' Wait Drain Runtime = 180 ' SPIN Set Jobpending Reset Motoron ' Start Rinse Reset Run_nspin ' SPIN Start Timer0 ' Start Timer/Counter Bitwait Jobpending , Reset ' Wait to Finish Relays = 255 ' ALL OFF, INITIAL State Controls = 255 Goto Loopagain End كود:
' Runmotor: Jobpending = 1 ' Timing flag Motoron = 0 ' Start Motor TR0_bit = 1 ' Start Timer/Counter do loop until Jobpending =0 ' Wait to Finish Motoron = 1 ' stop motor For cntr = 0 To Rinse Drainon = 0 ' Open Drain do loop until Waterempty = 0 ' Wait Drain Drainon = 1 ' Close Drain Wateron = 0 ' Fill again do loop until Waterfull = 0 Wateron = 1 ' Close Water Runtime = 300 ' 1St Rinse Jobpending = 1 Motoron = 1 ' Start Rinse TR0_bit = 1 ' Start Timer do loop until Jobpending = 0 ' Wait to Finish Motoron = 1 ' Stop Rinse Next cntr Drainon = 0 ' Open Drain second cycle do loop until Waterempty = 0 ' Wait Drain Runtime = 180 ' SPIN Jobpending = 1 Motoron = 0 ' Start Rinse Run_nspin = 0 ' SPIN TR0_bit = 1 ' Start Timer/Counter do loop until Jobpending = 0 ' Wait to Finish Relays = 255 ' ALL OFF, INITIAL State Controls = 255 Goto Loopagain end. While TRUE ثم تستبدل Goto Loopagain WEND و استبقيتهم للآخر حتى لا يبدو البرنامج معقد و به كثير من التنبؤات ولكن يمكن استبدال شيء بأخر المرة القادمة إن شاء الله سنستخدم ذات البرنامج بلغة C |
![]() |
مواقع النشر (المفضلة) |
أدوات الموضوع | |
انواع عرض الموضوع | |
|
|