بسم الله الرحمن الرحيم
الحمد لله على نعمه التي لا تعد ولا تحصى ... ومن نعم الله الكبيرة علينا أن أنعم علينا بنعمة الإسلام وجعلنا إخوة متحابين نتفانى لمساعدة بعضنا البعض وأمرنا بالتواضع والتعاون المستمر بين أفراد المسلمين .. فالمسلم للمسلم .. وكما أخبرنا الرسول عليه الصلاة والسلام أن أحب الأعمال إلى الله سرور تدخله على مسلم ومن نعم الله علينا نعمة العلم فالعلم كله بيد الله يرزقه من يشاء ويقسمه كيف شاء فاللهم ارزقنا العلم النافع وبارك لنا فيه ووفقنا لنصرة الإسلام والمسلمين ...
لماذا هذه المقدمة ؟؟ لأنه بعون الله ستكون هذه المشاركة هي نهاية حل المشكلة لهذا الموضوع .. والذي كان فيه تعاون كبير بينك وبين أستاذنا الفاضل زكي شاكر الذي يمد يد العون للجميع وبين العبد الفقير محدثك . . فمن الجميل جدا أن تتم تبادل الخبرات و المعلومات وكذلك التعاون المستمر والمنظم تنظيما جيدا ..
ومشاركتك يا أخي الكريم مشاركة جميلة جداً تذكرني بمشاركة كانت بيني وبين أستاذي الفاضل زكي شاكر حول موضوع عن التحكم في درجة الحرارة بواسطة البك (بالاسمبلي) حيث استفدت كثيرا من أستاذي الفاضل زكي شاكر وكانت النتيجة النهائية لهذا الموضوع هو حل المشكلة بعدة طرق .... أكرمك الله يا أستاذ زكي وبارك في علمك ورزقك مزيدا منه وجعلك دائما عونا لإخوانك المسلمين في كل مكان . ( وقد كانت لدي فكرة معينة حول ذلك الموضوع وسأحاول وضعها بإذن الله كما وعدت الاستاذ زكي ولكني احتاج بعض الوقت لتكون هناك مشاركة قوية حول هذه الفكرة التي لدي )
وحتى لا أكثر عليك من الكلام هيا بنا لنختم هذه المشاركة بهذه المعلومات :
أولا : أخي m_mansour2005 لو كان لدينا دالة مقاطعة interrupt هذه الدالة وظيفتها مثلا إضاءة الليد ثم إطفاءه باستمرار عند الضغط على السويتش الواصل بـ b0 وفي حالة عدم الضغط على السويتش يظل الليد منطفئ دائما فسنكتب هذا البرنامج مثلا (علما بأن السويتش موصل بـ b0 والليد موصل بـ c0 )
كود:
void interrupt()
{
while(portb.f0==0){}
portc=0;
delay_ms(1000);
portc=0xff;
delay_ms(1000);
}
void main()
{
intcon.f7=1;intcon.INTE=1;adcon1=0x06;trisb=0x01; trisc=0;
m:
portc=0;
goto m;
}
هل تعلم ماذا سيحدث عند تطبيق هذا البرنامج ؟؟
عند تشغيل الدائرة لن يضيء الليد وعند الضغط على السويتش يضيء الليد ثم ينطفئ وهكذا باستمرار ... باستمرار . . كيف ذلك ؟؟ مع أنه لا يوجد loop داخل دالة المقاطعة .
نعم إذا كتبنا دالة الانتربت بهذا الشكل وعند تنفيذ البرنامج .. سيحدث ما يلي يظل البك ينفذ اللوب الذي في الدالة الرئيسية وعند الضغط على السويتش تحدث المقاطعة فينفذ البك الأوامر التي في هذه الدالة وعندها لن يجد أمر يجعله يرجع للدالة الرئيسية فينفذ الأوامر التي في دالة المقاطعة مرة أخرى وهكذا باستمرار . معنى هذا أنه إذا لم تكتب داخل دالة المقاطعة أمر يجعل البك يعود للدالة الرئيسية فإنه في تلك الحالة أي أوامر ستكتبها داخل الدالة ستكرر باستمرار كأنها داخل لووب.
ومعنى هذا أن البرنامج السابق لن يفرق أبدا عن البرنامج التالي
كود:
void interrupt()
{
qq:
while(portb.f0==0){}
portc=0;
delay_ms(1000);
portc=0xff;
delay_ms(1000);
goto qq;
}
void main()
{
intcon.f7=1;intcon.INTE=1;adcon1=0x06;trisb=0x01; trisc=0;
m:
portc=0;
goto m;
}
هذه أول ملحوظة .. الأمر الثاني هو كيف سأعود ( كيف سأأمر البك بالعودة ) من دالة المقاطعة . هل سأكتب return في نهاية دالة المقاطعة .. إذا كان الأمر return يجعل البك يرجع فمن المفترض عند تجريب الدائرة وعند الضغط على السويتش سيضيء الليد مرة واحدة فقط ثم يعود للدالة الرئيسية وبما أن المقاطعة هذه حدثت أثناء تنفيذ البك للوب m فسيرجع البك لـ m وبالتالي سينطفئ الليد ...
ولكن هذا لن يحدث لماذا ؟؟ ..
لأن الأمر return لن يرجع البك من دالة المقاطعة .. وهذه ملحوظة يجب التنبه إليها . ...
ولكن كيف أعود من دالة المقاطعة .. ببساطة بدل من أن تكتب return اكتب الأمر التالي
وعند كتابة هذا الأمر في نهاية دالة المقاطعة فإن البك سوف ينفذ الأوامر التي في دالة المقاطعة ثم يرجع للدالة الرئيسية في المكان الذي حدثت في المقاطعة أي في اللوب m
كما في الكود التالي :
كود:
void interrupt()
{
while(portb.f0==0){}
portc=0;
delay_ms(1000);
portc=0xff;
delay_ms(1000);
intcon.intf=0;
}
void main()
{
intcon.f7=1;intcon.INTE=1;adcon1=0x06;trisb=0x01; trisc=0;
m:
portc=0;
goto m;
}
وبهذا لن تتكرر عملية الإضاءة والإطفاء باستمرار. وسيرجع البك من الانتربت
أرجو أن تكون فهمت ما أقصد ..
والآن في مشروعك وفي دالة المقاطعة يوجد اختبار للسويتشات هذا الإختبار يجب أن يكون باستمرار وعند الضغط على السويتش b0 مرة أخرى يرجع البك للدالة الرئيسية في المكان الذي حدثت فيه المقاطعة (اللوب) . لذلك في دالة المقاطعة سأجعل هناك لووب معين وليكن اسمه LL داخل هذا اللوب يتم اختبار السويتشات عن طريق استخدام جملة if مثلا ومن هذه السويتشات السويتش b0 الذي من المفترض أن يجعلنا نخرج من دالة الانتربت (المقاطعة) ولهذا يمكنني أن أكتب هذا الكود بالشكل التالي
كود:
void interrupt()
{
while(portb.f0==1){}
scan:
if(porta.f0){while(porta.f0){}increment();show();}
else if(porta.f1){while(porta.f1){}decrement();show();}
else {while(portb.f0){}goto RR;}
goto scan;
RR:
INTCON.INTF=0;
}
فعند الضغط على السويتش b0 يخرج البك من الانتربت عن طريق خروجه من اللوب scan والذهاب للعناون RR ..
==============================================
أهم نقطة أريد إخبارك بها
هي أنك داخل دالة الانتربت لايمكنك أن تستخدم الدوال الخاصة بـ eeprom .. أي أنه لا يمكنك استخدام المكتبة الجاهزة للـ EEPROM داخل دالة المقاطعة (الانتربت) ...
ولكن لماذا ؟؟ لأن هذه الدالة تجعل البت GIE الخاص بـ INTCON بصفر أي أنها تلغي عملية المقاطعة ..
هل تخيلت ما يحدث عند استخدامك لهذه المكتبة داخل دالة المقاطعة .. ستلغي هي المقاطعة ولكن كيف تلغي المقاطعة وهي نفسها ستنفذ داخل المقاطعة ( لأنك وضعت أوامر استخدام هذه المكتبة داخل دالة المقاطعة )
إذن فلا تستغرب حينها أن تجد بعض السويتشات لا تعمل ولا تستغرب إذا وجدت كثيرا من التخاريف تظهر أمامك ...
ولتتأكد من كلامي اقرأ الهيلب الخاص باللغة الجزء الخاص بهذه المكتبة وستجد هذا الكلام فيها
|
Be aware that all interrupts will be disabled during execution of Eeprom_Write routine (GIE bit of INTCON register will be cleared). Routine will restore previous state of this bit on exit.
|
إذن يجب عليك عدم الكتابة على EEPROM داخل دالة الإنتربت بشكل مباشر.
ولكن ما هو الحل ؟؟؟ الحل هو أن تستخدم متغير إضافي هذا المتغير تكون قيمته صفر مثلا . وعند حدوث المقاطعة تصبح قيمته بواحد .. وفي الدالة الرئيسية تجعل البك في أول أمر داخل اللوب التي تجعل البك ينقص من القيمة باستمرار اجعل هناك جملة اختبار تختبر هل هذا المتغير بواحد إذا كان بواحد أي أن المقاطعة حدثت ومن ثم يستدعي دالة معينة تحتوي على استخدام EEPROM وتجعل هذه الدالة ترجع قيمة هذا المتغير للصفر مرة أخرى .
وإليك الآن الكود الذي كتبته ليحل هذه المشكلة ويعتبر مجرد مثال يمكنك التعديل عليه كما تشاء ( الغرض هو معرفة الفكرة فقط )
كود:
unsigned char B[10]={0xfc,0x60,0xda,0xf2,0x66,0xb6,0xbe,0xe0,0xfe,0xf6};
unsigned char ones,tens,m=0;
void show() { portb=B[tens]; portc=B[ones]; }
void increment(){if (ones==9 && tens==9) {tens=0;ones=0;show();}
else if (ones==9){ones=0; tens++; show();}
else ones++;
}
void decrement(){if(tens==0 && ones==0) {tens=1;ones=5;show;}
else if(ones==0) {tens--; ones=9 ;show;}
else ones--;}
void eee()
{eeprom_write(0x00,8);
delay_ms(50);
ones=eeprom_read(0x00);
m=0;
}
void interrupt()
{ //while(portb.f0==1){}
char x;
while(portb.f0==1){}
for(x=0;x<255;x++){
if(porta.f0){while(porta.f0){}increment();show();}
else if(porta.f1){while(porta.f1){}decrement();show();}
else {while(portb.f0){delay_ms(1000); x=254;}}
delay_ms(100);
}
m=1;
INTCON.INTF=0;
}
void main()
{
intcon.f7=1;intcon.INTE=1;adcon1=0x06;trisa=0x03;trisb=0x01; trisc=0; // initaliztion
tens=1; ones=5;
test:
if(m==1)eee();
show();
delay_ms(1000);
decrement();
goto test;
}
هذا مجرد مثال تجريبي فليست هذه الوظيفة المطلوبة .. افهم الفكرة فقط .
ملحوظة أأكد عليها : عند العودة من دالة الانربت سيقوم البك بالعودة للوب TEST وسينفذ الأمر الذي يختبر هل حدثت مقاطعة أم لا عن طريق اختباره للمتغير m .
لذلك إذا أردت أن يقرأ البك القيمة الموجودة في مكان ما في EEPROM التي كان السبب فيها دالة المقاطة فيجب عليك أن تكتب أمر القراءة هذا داخل اللوب test وليس خارجه .... أرجو أن تكون فهمت كلامي .. وفهمت قصدي جيدا .
هذا والحمد لله رب العالمين
==================================
تمهل جيدا واقرأ هذه المشاركة وحاول فهمها جيدا ... ثم اكتب البرنامج بشكله النهائي ثم ضعه هنا ....... تمنياتي لك بالتوفيق .
==================================
وفقنا الله جميعا لما يحب ويرضى ورزقنا وإياكم العلم النافع
والسلام عليكم ورحمة الله وبركاته