التجربةالسابعه:- المؤقتات Timers والعدادات Counters

الوصف(Description):-

الميكروكونترولر PIC  مجهز بنظام أو أكثر للتوقيت الدقيق يعرف باسم المؤقتات Timers والتى يمكن استخدامها لتنفيذ مجموعة متنوعة من وظائف التوقيت الدقيقة مثل توليد أحداث في أوقات محددة ، وقياس مدة (فترة دوام) duration حدث وحفظ تسجيل التاريخ والوقت وعد الأحداث counting وما إلى ذلك.العنصر الرئيسي فى وحدة (موديول) المؤقت هو عداد ثنائى حر يتزايد عند كل نبضة ترد إليه . ونظرا لأنه يعمل بشكل مستقل فإنه يمكن أن يعد النبضات فى وقت واحد (بالتزامن) مع تنفيذ البرنامج الرئيسي.يمتلك الميكروكونترولر  PIC16F88 ثلاثة من المؤقتات كأجهزة hardware مدمجة به وتسمى :Timer0 و Timer1و Timer2 وسوف نتناول فى هذا التدريب وحدة (موديول) المؤقت    Timer0.

المعلومات النظرية المطلوبة  ( Required Theory)


معلومات عن وحدة (موديول) المؤقت Timer 0
وحدة (موديول) المؤقت / العداد timer/counter module ببساطة هى عداد ثنائى binary counter  مستقل والذى يمكن تهيئته (إعداده) لكى يقوم بعد نبضات ساعة دورات التعليمات الداخلية أو عد نبضات ساعة خارجية .
المؤقت Timer 0 هو عداد متزامن بسعة 8 خانات  8-bit والذى يقوم بحفظ (تخزين) قيمة العداد فى سجل وظائف خاصة يسمى TMR0 . هذا السجل قابل للقراءة وللكتابة فى أى وقت عن طريق البرنامج . إذا كتبت قيمة عليه فسوف يبدأ العداد التزايد من هذه القيمة .عندما يتم تشغيل الموديول Timer 0 بنبضات ساعة تعليمات المعالج فيقال أنه يعمل كمؤقت timer  لأنه يتزايد بمعدل ثابت ( يتحدد بنبضات ساعة المعالج) , وإذا علمت عدد النبضات التى قام بعدها يمكنك إشتقاق (إستنتاج) الوقت المنقضى .
يمكن للمؤقت Timer 0 أيضا عد النبضات الخارجية الواصلة إلى الطرف RA4(pin3)  واسمه البديل هو : T0CKI (Timer Zero Clock Input) " مدخل نبضات الساعة للمؤقت صفر " . عندما يقوم الموديول بعد النبضات الخارجية يقال أنه يعمل كعداد counter . سوف نناقش نظامى العمل كل على حدة .
نظام المؤقت Timer mode
·         يتم اختيار نظام المؤقت بمسح clear الخانة TOCS (OPTION register, bit 5) .
·         فى نظام المؤقت يتزايد السجل TMR0 عند كل دورة تعليمة . ولأن هذا السجل ذو سعة 8-bit فيمكنه العد من الصفر 00 إلى  FF (255) , وعندما يصل إلى أقصى قيمة له وهى FF (255) ويتقدم فى التزايد فيلف العد عائدا إلى الصفر 00 وتعرف هذه الحالة بالطفحان overflow (تجاوز الحد الأقصى) ويتم تسجيلها بواسطة الخانة  T0IF (Timer0 Interrupt Flag) بالسجل INTCON (Interrupt Control) بجعلها بالقيمة set  أى 1 , حيث يمكن لهذه الحالة بدء (توليد) مقاطعة والتى تعرف باسم " المقاطعة بالمؤقت صفر " Timer0 Interrupt إذا ما كانت ممكنة لعمل مقاطعة .
·         يتم تمكين المقاطعة بالمؤقت Timer 0 بجعل الخانة T0IE (Timer0 Interrupt Enable) " تمكين المقاطعة بالمؤقت صفر " بسجل التحكم فى المقاطعة INTCON بواحد أى set بالأضافة إلى خانة التمكين العام GIE . هذه المقاطعة هى التى يجب أن تبين أن الوقت إنتهى وتحدث عند حدوث طفحان بالسجل TMR0 .
·         يجب مسح clear الخانة TOIF " علم المقاطعة بالمؤقت صفر " بواسطة روتين خدمة المقاطعة بحيث يمكن حدوث مقاطعة بالمؤقت من جديد .
·         نفترض أن الميكروكونترولر PIC يعمل على مذبذب ساعة بتردد Fosc =4 MHz فإن تردد نبضات ساعة التعليمات سوف تكون Fosc /4 = 1 MHz . نتيجة لذلك يقوم العداد بالتزايد كل واحد ميكروثانية 1 μs بالضبط .وهذا يعنى أن المؤقت Timer 0 سوف يستغرق 256 μs لكى يعد من الصفر إلى الصفر التالى . عن طريق تحميل سجل المؤقت TMR0 بقيمة مسبقة مناسبة يمكن اختيار فترات زمنية أصغر .على سبيل المثال إذا تم تحميل السجل TMR0 مسبقا بالقيمة  206 فإن المؤقت Timer 0 سوف يحدث له طفحان بعد 50 μs فقط والسؤال ماذا اذا اردنا زمن كبر من 256 ميكرو ثانيه الاجابه في النقطه التاليه
·          علاوة على ذلك يوجد قاسم تردد مبرمج بسعة 8-bit يعرف باسم  prescaler "المقياس المسبق " أو " معامل القسمة " وهو متاح لقياس الفترات الزمنية الطويلة . والمقياس المسبق هو دائرة قسمة تقع بين مصدر نبضات الساعة والمؤقت وتقوم بقسمة التردد الداخل على قيمة من ثمانى قيم ثنائية تقع ما بين القيمة 2 والقيمة 256 .إذا كان تردد نبضات ساعة التعليمات 1MHz فإن أقصى فترة زمنية سوف تكون
256 x 256 μs = 65.536 ms
وهى التى تناظر معمل قسمة 256 . قيم معامل القسمة يتم اختيارها برمجيا بواسطة الخانات PS0, PS1,PS2 " خانات اختيار معامل القسمة " بالسجل OPTION .

من أجل تخصيص استخدام معامل القسمة مع موديول المؤقت Timer 0 يجب مسح clear = 0  الخانة  PSA " تخصيص معامل القسمة " . إما إذا كانت الخانة PSA بواحد set = 1  فلا يتم تخصيص أى معامل قسمة لموديول المؤقت .
نظام العداد Counter Mode
·         يتم اختيار نظام العداد بجعل الخانة  TOCSبالسجل OPTION بواحد set = 1  .
·         فى هذا النظام يقوم موديول المؤقت Timer 0 بعد نبضات الساعة الخارجية الموجودة على الطرف RA4/T0CKI.
·         سوف يتزايد المؤقت سواء عند الحافة الصاعدة أو عند الحافة الهابطة لنبضات الساعة والتى يتم اختيار ذلك برمجيا بواسطة الخانة T0SE (Timer0 Source Edge) " حافة مصدر المؤقت صفر " بالسجل OPTION .فإذا كانت هذه الخانة بواحد set فسوف يتزايد المؤقت عند الحافة الهابطة لكل نبضة من نبضات الساعة التى تصل إلى الطرف  RA4/T0CKI . مرة أخرى يمكن توسعة أو تمديد مدى العداد باستخدام prescaler .

مخطط الدائرة    (Circuit Diagram)


البرنامج (Software)

sbit LED at RB0_bit;
unsigned short Num;
void interrupt() {
  Num ++;          
  if(Num == 18) {
   LED = ~LED;                                // Toggle LED every sec
   Num = 0;
  }

  TMR0 = 39;       
  INTCON.TMR0IF = 0
}
void main() {
 CMCON = 0x07 ;                         // Disbale comparators
TRISB = 0b00000000;                  // PORTB All Outputs
TRISA = 0b00100000;                 // PORTA All Outputs, Except RA5=MCLR
 LED = 0;
 Num = 0;
 OPTION_REG = 0x07;             // Prescaler (1:256) is assigned to the timer TMR0
 TMR0 = 39;                              // Timer T0 counts from 39 to 255
 INTCON = 0xA0;                    // Enable interrupt TMR0 and Global Interrupts
 do {
  } while(1);  }

شرح البرنامج


فى البداية سوف ننشأ زمن تأخير بقيمة واحد ثانية تقريبا باستخدام موديول المؤقت Timer 0 كمؤقت . الميكروكونترولر يعمل على تردد ساعة المذبذب Fosc = 4MHz لذلك يكون الزمن الدورى للتعليمة هو 1 μs . لتوليد فترة تأخير زمنى قدرها ثانية يجب أن يعد المؤقت مليون 1000000 دورة تعليمة . فإذا استخدمنا معامل القسمة prescaler  بالقيمة 256 فإن العدد المطلوب يقل من مليون إلى ) (1000000/256 = 3906  بمعني اخر اي انه كل مليون نبضه تعليمه سوف يخرج prescaler حوالي 3906 نبضه . والآن إذا حملنا السجل TMR0 بالقيمة المسبقة 39 فإن طفحانه سوف يحدث بعد   256-39 = 217 counts   مع ملاحظه ان هذا الطفحان سوف يحدث بعد 217 نبضه من نبضات prescaler وهكذا تكون عدد مرات الطفحان المطلوبة  3906/217 = 18 . بهذه الإعدادات فإنه بعد 18  مرة طفحان للسجل TMR0 (وفى كل مرة يتم تحميله مسبقا بالقيمة 39 ) ينقضى فترة زمنية قدرها واحد ثانية تقريبا .
البرنامج يوظف هذا التأخير فى عمل وميض لليد .

تطوير البرنامج(1)

نريد ان نطور البرنامج بحيث اننا يمكننا قياس الثواني والدقائق حيث اننا سوف نستخدم عدد 2 ليد ضوئي الاول يتغير حالته كل ثانيه اما الاخر فيتغير حالته كل دقيقه ولعمل هذا التطوير يجب اولا عم مجموعهمن الحسابات لحساب كيف يمكن للتيمر ان يعد الدقائق كما يلي مما سبق حيث اننا لتوليد فتره زمنيه مقدارها ثانيه كنا نحتا ج الي مليون دوره تعليمهاما هنا فنحتاج الي دقيقه فيتم ضرب المليون دوره تعليمه في 60 ينتج لنا 60 مليون دوره تعليمه لكي نولد فتره زمنيه مقدارها دقيقه اذا يمكن حساب معامل القسمه prescaler كما يلي
Prescaler  =  256    / 60000000 =   234375
اي كل ستين مليون دوره تعليمه يخرج من Prescaler حوالي 234375نبضه بقسمه هذا الرقم علي 75 نتج رقم صحيح وهو3125 والرقم 75 نحن فرضناه فقط ليكون ناتج القسمه رقم صحيح هذا الرقم الذي نتج هو عدد مرات المقاطعه للأنتاج الوقت المطلوب وهوالدقيقه اما الرقم الذ سوف يوضع داخل التيمر ليبدأالتيمر زيرو من العد من عنده فينتج من طرح الرقم 75 من 256 فينتج لنا الرقم 181 هذا الرقم هو الذ سوف نضعه في التايمر ليعد من عنده وبذلك يعد التايمر من 181 ويحدث المقاطعه عند طفحان التايمر حوالي 3125 لينتج لنا الدقيقه اما لحساب الثانيه فالوضع  هنا سوف يكون مختلف حيث ان التايمر زيرو سوف يعد من الرقم 181 فالسؤال هنا كم مره سوف تحدث المقاطعه لينتج لنا الثانيه النتيجه ببساطه ان حيث ان كل مليون نبضه تعليمه سوف يخرج prescaler حوالي 3906 نبضه   نقوم بقسمه هذا الرقم عليالرقم 75 سوف ينتج لنا 52 اي بعد 52 مقاطعه سوف يبنج ثانيه والبرنامج التالي تطورناه بحيث يضئ ليد كل ثانيه اما الاخر فيضئ دقيقه وينطفئ دقيقه مع ملاحظه هنا شئ مهم جدا اننا غيرنا في نوع المتغيرات التي تعد الدقائق لأحتوائها علي عدد كبير وهو المتغير mn جعلناه من نوع unsigned int وذلك لانه يحتوي علي رقم كبير نوعا ما  


البرنامج


sbit LED1 at RB0_bit;
sbit LED2 at RB1_bit;
unsigned short  sec;
unsigned int mn;
void interrupt() {
  sec ++;
    mn++;
  if(sec == 52) { LED1 = ~LED1; sec=0;}
  if(mn == 3125) { LED2 = ~LED2;mn=0; }                             // Toggle LED every sec
  TMR0 = 181;                                                                         // TMR0 returns to its initial value
  INTCON.TMR0IF = 0;                                                         // Bit T0IF is cleared so that the interrupt could reoccur
}
void main() {
CMCON = 0x07 ;                                                               // Disbale comparators
TRISB = 0b00000000;                                                    // PORTB All Outputs
TRISA = 0b00100000;                                                  // PORTA All Outputs, Except RA5=MCLR
 LED1 = 0;  LED2 = 0;
 sec = 0;  mn = 0;
 OPTION_REG = 0x07;                                                 // Prescaler (1:256) is assigned to the timer TMR0
 TMR0 = 181;                                                                 // Timer T0 counts from 39 to 255
 INTCON = 0xA0;                                                         // Enable interrupt TMR0 and Global Interrupts
 do {
  } while(1);                                                                     // infinite loop
}

مخطط الدائرة الجديده

تطويرالبرنامج(2)

سوف نطور البرنامج السابق وذلك لعرض عدد مرات المقاطاعات وايضا الثواني وكذلك الدقائق وعرض ذلك 


علي شاشه LCD

مخطط الدائرة الجديده


البرنامج (Software)
 sbit LCD_RS at Rb4_bit;
 sbit LCD_EN at Rb5_bit;
 sbit LCD_D4 at Rb0_bit;
 sbit LCD_D5 at Rb1_bit;
 sbit LCD_D6 at Rb2_bit;
 sbit LCD_D7 at Rb3_bit;
 sbit LCD_RS_Direction at TRISb4_bit;
 sbit LCD_EN_Direction at TRISb5_bit;
 sbit LCD_D4_Direction at TRISb0_bit;
 sbit LCD_D5_Direction at TRISb1_bit;
 sbit LCD_D6_Direction at TRISb2_bit;
 sbit LCD_D7_Direction at TRISb3_bit;
  char message0[] = "inter";
  char message1[] = " sec";
  char message2[] = " min";
  char *inter_uput = "00";
  char *sec = "00";
  char *minut = "00";
unsigned int  count_rput,count_sec,count_minut,sss;
unsigned int mn;
void interrupt() {
  count_rput ++;  mn++;
  if(count_rput == 52) {  count_sec++; count_rput=0;}
   if(mn == 3125) {count_minut++; count_rput=0; count_sec=0;mn=0; }
  TMR0 = 181;
  INTCON.TMR0IF = 0;
}
void main() {
CMCON = 0x07 ;
TRISB = 0b00000000;
TRISA = 0b00100000;
OPTION_REG = 0x07;
TMR0 = 181;
INTCON = 0xA0;
count_rput=0;
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF);
Lcd_Out(1,1,message0);
Lcd_Out(1,7,message1);
Lcd_Out(1,12,message2);
  do {
inter_uput[0] =  (count_rput/10)%10 + 48;
inter_uput[1] =  count_rput%10 + 48;

sec[0] =  (count_sec/10)%10 + 48;
sec[1] =  count_sec%10 + 48;

minut[0] =  (count_minut/10)%10 + 48;
minut[1] =  count_minut%10 + 48;

Lcd_Out(2,2,inter_uput);
Lcd_Out(2,8,sec);
Lcd_Out(2,14,minut);

  } while(1);
 }


شرح البرنامج 

البرنامج سوف يقوم بعمل عداد ليعد عدد مرات المقاطعات ولكن نصفر هذا العداد كل ثانيه والمتغير المسئول هن هذا العد هو count_rput وبعد ان يتخطي هذا العداد العدد 52 فسوف  يزيد المتغير count_secالمسئول عن الثواني بمقدار واحد  اي الثواني سوف تزيد بمقدار وحد بعد 52 مقاطعه ويزيد المتغير count_minut المسئول عن الدقائق بمقدار واحد بعد عدد3125 مقاطعه يتم حسابها عن طريق المتغيرmn حيث يزيد بقدار واحد عند كل مقاطعه مع ملاحظه اننا نصفر هذا المتغير عندما يحدث 3125مقاطعه  اي يمر دقيقه بالضبط ويتم عرض كلا من العداد المقاطعات والثواني والدقائق من خلال البرنامج 




فيديو التجربه


هناك تعليق واحد :