دمج الوحدات · الدرس 3.9

إنشاء مؤقت

العودة إلى الدروس

وظائف المؤقت

ستتعلم إنشاء مؤقت لمواقف متعددة، مع مقاطعات المتحكم وإزالة ارتداد الأزرار والمسح الديناميكي للشاشات الرقمية.

المكونات المطلوبة

الأزرار الثلاثة

زر الدقائق يزيد دقيقة مع كل ضغطة، والاستمرار في الضغط يسرّع الزيادة. زر الثواني يزيد ثانية، والاستمرار يسرّعها أيضًا.

زر الوظيفة يبدأ العد التنازلي بعد ضبط الوقت. الضغط عليه أثناء العد يمسح الوقت. عند انتهاء العد يصدر الطنان صوتًا ويومض LED. الضغط عليه بعد الإنذار يوقف الصوت والوميض ويمسح الوقت.

توصيل المؤقت

بسبب كثرة الأجهزة قد لا تكفي قدرة قيادة منافذ IO. تُشغَّل دائرة LED المتصلة بالمنفذ A0 بالمستوى المرتفع، مع مقاومة 10kΩ في دائرة التحكم. يُستخدم ترانزستور (NPN Transistor 8050) عادةً لقيادة الأحمال التي تعمل بجهد نحو 5V.

توصيل المؤقت مع LED والطنان
توصيل المؤقت مع LED والطنان
توصيل شاشة المؤقت والأزرار
توصيل شاشة المؤقت والأزرار

توصيل أمثلة التدريب

توصيل مثال الأزرار والمقاطعات
توصيل مثال الأزرار والمقاطعات
توصيل مثال المؤقت والطنان
توصيل مثال المؤقت والطنان

مؤقت المراقبة وارتداد المفاتيح

Watchdog

رغم أن المؤقت النهائي لا يستخدمه، يشرح الدرس مؤقت المراقبة لتفادي التعطل أو الحلقات غير المنتهية؛ إذا لم يعد البرنامج يستجيب يعيد ضبط المتحكم.

يعرض المثال avr/wdt.h و wdt_enable(WDTO_1S)، أي ثانية واحدة، بينما يوجد delay(2000). لذلك قد يعمل Watchdog ويعيد الضبط. ضع // في بداية سطر wdt_enable وسطر رسالة watchDog_interrupt قبل تشغيل التجربة المعتادة. تستخدم wdt_reset() لتحديث المراقبة ومنع إعادة الضبط.

رسائل تشغيل المثال
رسائل تشغيل المثال
رسائل مقاطعة زر المثال
رسائل مقاطعة زر المثال

الجزء الثاني: إزالة الارتداد

عند فتح أو إغلاق تماس ميكانيكي، تؤدي مرونته إلى اهتزاز قصير بدل انتقال مستقر. يحدث ذلك في الضغط والتحرير. قد تسبب ضغطة واحدة ثلاث مقاطعات بدل واحدة؛ وسيعالج الدرس ذلك.

رسم ارتداد التماس عند الفتح والإغلاق
رسم ارتداد التماس عند الفتح والإغلاق

الجزء الأول: المقاطعات

تعمل التعليمات عادة بالتتابع داخل loop(). إذا وقع حدث عاجل نستخدم مقاطعة لتنفيذه ثم نعود إلى البرنامج. قبل تجربة البرامج، تأكد من تثبيت مكتبتي PinChangeInt و MsTimer2 في بيئة Arduino IDE. إذا ظهرت رسالة تشير إلى مكتبة مفقودة، فاتبع خطوات التثبيت في قسم «ما هي المكتبات؟» وطرق تثبيتها في الدرس 1.5 «وميض LED وإضافة المكتبات».

افتح ملف البرنامج demo1.ino من ملفات الدرس في بيئة Arduino IDE. بعد إتمام التوصيلات، وصّل لوحة أردوينو (UNO R3 Board) بالحاسوب بكابل توصيل (USB Cable)، ثم اضغط زر Upload (رفع) لنقل البرنامج إلى اللوحة وتشغيله عليها.

مقاطعة Timer2

يحدد MsTimer2:: set(4000, timer2_interrupt) فترة المقاطعة، وتبدأ MsTimer2:: start() المؤقت. تقطع المقاطعة تنفيذ loop() لتشغيل timer2_interrupt() وطباعة رسالة ثم العودة.

رسائل مقاطعة Timer2 في المثال
رسائل مقاطعة Timer2 في المثال

مقاطعة تغير المنفذ

تتشابه مع مقاطعة المؤقت، لكن المشغّل تغير الإشارة بدل الزمن. يوضح الدرس HIGH و LOW و RISING، أي انتقال منخفض إلى مرتفع، و FALLING، أي مرتفع إلى منخفض، و CHANGE.

يهيئ المثال A3 بـ INPUT_PULLUP ويستخدم attachPinChangeInterrupt(A3, button_interrupt, FALLING). عند ضغط الزر قد ترى ثلاث رسائل مقاطعة من ضغطة واحدة؛ السبب ارتداد المفتاح الذي ناقشته الصفحة السابقة.

إزالة الارتداد والمسح وبناء المؤقت

افتح ملف البرنامج demo2.ino من ملفات الدرس في بيئة Arduino IDE. بعد إتمام التوصيلات، وصّل لوحة أردوينو (UNO R3 Board) بالحاسوب بكابل توصيل (USB Cable)، ثم اضغط زر Upload (رفع) لنقل البرنامج إلى اللوحة وتشغيله عليها. قد يفيد تأخير قصير لإزالة الارتداد، لكن التأخير الطويل يهدر عمل المتحكم. لذلك تستخدم قيمة الزمن بالملي ثانية للتمييز بين ضغطة حقيقية وارتداد. الفرق الأكبر من 200ms يعد ضغطة مستقلة.

مقتطف من الشرح
if (200 < abs(time_buttonApin - last_time_buttonApin)) {

  Serial.println("buttonApin have been pressed");

  last_time_buttonApin = time_buttonApin;

}

الجزء الثالث: المسح الديناميكي

يرسل رمز الشكل واختيار الخانة لكل شاشة بالتتابع. تعمل خانة واحدة لحظيًا، لكن سرعة المسح وبقاء أثر الرؤية يجعلان جميع الأرقام ظاهرة.

افتح ملف البرنامج demo3.ino من ملفات الدرس في بيئة Arduino IDE. بعد إتمام التوصيلات، وصّل لوحة أردوينو (UNO R3 Board) بالحاسوب بكابل توصيل (USB Cable)، ثم اضغط زر Upload (رفع) لنقل البرنامج إلى اللوحة وتشغيله عليها.

الجزء الرابع: المؤقت متعدد الوظائف

يعرض الدرس مخططًا لتجميع أفكار المقاطعات وقراءة الأزرار وعرض الوقت.

افتح ملف البرنامج MyTimer.ino من ملفات الدرس في بيئة Arduino IDE. بعد إتمام التوصيلات، وصّل لوحة أردوينو (UNO R3 Board) بالحاسوب بكابل توصيل (USB Cable)، ثم اضغط زر Upload (رفع) لنقل البرنامج إلى اللوحة وتشغيله عليها. ينفذ هذا البرنامج المؤقت الكامل.

مخطط تجميع وظائف المؤقت
مخطط تجميع وظائف المؤقت

الكود

demo1/demo1.ino

#include "PinChangelnt.h"
#include "MsTimer2.h"          
#include "avr/wdt.h"

int number = 0;
void setup() 
{
  Serial.begin(9600);
  
  // 4000ms triggered once  "timer2_interrupt" is the function name:you can set your favourite name  
  MsTimer2::set(4000, timer2_interrupt); 
  MsTimer2::start(); //timer2 interrupt begin

  pinMode(A3, INPUT_PULLUP); 
  attachPinChangeInterrupt(A3, button_interrupt, FALLING );

  wdt_enable(WDTO_1S);
  Serial.println("watchDog_interrupt triggered");
}

/*the function is the thing you want to do in timer2_interrupt,
and the function name is the favourite name you set in "MsTimer2::set(4000, timer2_interrupt); "*/
void timer2_interrupt()
{
  Serial.println("timer2_interrupt triggered");
}

void button_interrupt()
{
  Serial.println("button_interrupt triggered");
}

void loop() 
{
 Serial.print("we begin to count:");
 Serial.println(number++);
 delay(2000);
 wdt_reset();//we should feed the dog avoiding it reseted.
}

demo2/demo2.ino

#include "PinChangelnt.h"

int buttonApin = A1;
int time_buttonApin = 0;
int last_time_buttonApin = 0;
int i;
void setup() 
{ 
  Serial.begin(9600);
  pinMode(buttonApin, INPUT_PULLUP);
  attachPinChangeInterrupt(buttonApin,buttonApin_interrupt, FALLING );
}


void buttonApin_interrupt()
{
  time_buttonApin=millis();
 
  if (200 < (abs(time_buttonApin - last_time_buttonApin)))
  {
    Serial.print("buttonApin have been pressed");
    last_time_buttonApin = time_buttonApin;  //update the time you press
    Serial.println("              i:");
    Serial.println(i++);
  }   
}

void loop() 
{
}

demo3/demo3.ino

#include "MsTimer2.h"
#define COM4 5
#define COM3 4
#define COM2 3
#define COM1 2

#define LATCH 12  
#define CLOCK 13 
#define DATA 8 
unsigned int number = 9999;
const static uint8_t num_buf[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

void setup() 
{
  latch_init(LATCH,CLOCK,DATA); //74hC595 IC
  
  com_init(COM1,COM2,COM3,COM4); 
  
  //  com4   com3  com2  com1 
  // _________________________
  // |  9  |  9  |  9  |  9  |    ←Four Digital Seven Segment
  //  ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄ ̄

  MsTimer2::set(100, timer2_interrupt); // 100ms period
  MsTimer2::start();
}

void timer2_interrupt()
{
  number--;
}

void loop() 
{
  display_num(number);
}

demo3/MySegDispaly.ino

void latch_init(const uint8_t latch,const uint8_t clock,const uint8_t data)
{
  pinMode(latch,OUTPUT);
  pinMode(clock,OUTPUT);
  pinMode(data,OUTPUT);
 }

void com_init(const uint8_t com1 , const uint8_t com2 ,const uint8_t com3 ,const uint8_t com4 )
{
  pinMode(com1,OUTPUT);
  pinMode(com2,OUTPUT);
  pinMode(com3,OUTPUT);
  pinMode(com4,OUTPUT);
}

void display_num(uint16_t num)
{
  uint8_t com=0;
  uint8_t polling=0;
  uint8_t bit_num=0;
  uint8_t num_place[4]={0};
  
  if(num>=1000&&num<=9999)
  {
   polling=4;
   bit_num=3;
   num_place[3]=num/1000;
   num_place[2]=num/100%10;
   num_place[1]=num/10%10;
   num_place[0]=num%10;
     for(int i=0;i<4;i++)
     {
      choose_com(polling);
      polling--;
      display(num_place[bit_num--]);
      delay(5);
     }
   }
   else if(num>=100&&num<1000)
   {
    polling=3;
    bit_num=2;
    num_place[2]=num/100;
    num_place[1]=num/10%10;
    num_place[0]=num%10;
      for(int i=0;i<3;i++)
     {
      choose_com(polling);
      polling--;
      display(num_place[bit_num--]);
      delay(5);
     }
    }
    else if(num>=10&&num<100)
   {
    polling=2;
    bit_num=1;
    num_place[1]=num/10;
    num_place[0]=num%10;
       for(int i=0;i<2;i++)
       {
        choose_com(polling);
        polling--;
        display(num_place[bit_num--]);
        delay(5);
       }
    }
    else if(num>=0 && num<=9)
    { 
        num_place[0]=num%10;
        choose_com(1);
        display(num_place[0]);
        delay(5);
     }
    else
     {num=9999;}
 
  }


void display(uint8_t num)
{
  digitalWrite(LATCH,LOW);
  shiftOut(DATA,CLOCK,MSBFIRST,num_buf[num]);
  digitalWrite(LATCH,HIGH);
}

void choose_com(uint8_t com)
 {
  switch(com)
  {
    case 1:choose_com1();
            
            break;
    case 2:choose_com2();

            break;
   case 3:choose_com3();

            break;
    case 4:choose_com4();
            
            break;         
    default :break;
    }
  }

void choose_com1()
{
    digitalWrite(COM1,LOW);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,HIGH);   
 }

 void choose_com2()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,LOW);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,HIGH);  
 }
 void choose_com3()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,LOW);
    digitalWrite(COM4,HIGH);  
 }
 void choose_com4()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,LOW);    
 }

 void clear_display()
{
  for(int i=0;i<4;i++)
 {
    choose_com(i);
    digitalWrite(LATCH,LOW);
    shiftOut(DATA,CLOCK,MSBFIRST,0);
    digitalWrite(LATCH,HIGH);
    digitalWrite(LATCH,LOW);
    delay(5);
  }
}

MyTimer/button.ino

void button_init(const uint8_t button_s,const uint8_t button_ms,const uint8_t button_choose)
{
  pinMode(button_s, INPUT_PULLUP);  
  pinMode(button_ms, INPUT_PULLUP); 
  pinMode(button_choose, INPUT_PULLUP);
}


void button_interrupt_init(const uint8_t button_s,const uint8_t button_ms,const uint8_t button_choose)
{
  attachPinChangeInterrupt(button_s, button_s_interrupt, FALLING );
  attachPinChangeInterrupt(button_ms, button_ms_interrupt, FALLING);
  attachPinChangeInterrupt(button_choose, button_choose_interrupt, FALLING );
}


void button_s_interrupt()
{
  time_button_s=millis();
  
  if (200 < (abs(time_button_s - last_time_button_s)))
  {
    flag_begin=0; 
    
    number +=100;
    
    last_time_button_s = time_button_s;
    
    if (number >= 9900)
        number = 9999;
  }   
}

  void button_ms_interrupt()
{
  time_button_ms=millis();
  
  if (200 < (abs(time_button_ms - last_time_button_ms)))
  {
     flag_begin=0;
     
     number +=1;
     
     if(number%100 == 60 && number != 60)
       number = number/100*100+100;
       
     if(number==60)
        {number=100;}

     if(number >= 9900)
        number = 9999;

      last_time_button_ms = time_button_ms;
  }    
}

void button_choose_interrupt ()
{
   time_button_choose=millis();

   if (200 < (abs(time_button_choose - last_time_button_choose)))
    { 
      if(flag_begin)
        {
          number=0;
         }
         
      flag_begin=!flag_begin;
      
      last_time_button_choose = time_button_choose;
    }
}

MyTimer/buzzer.ino

void buzzer_init(const uint8_t buzzer)
{
  pinMode(buzzer,OUTPUT);//initialize the buzzer pin as an output
 }


void time_out()
{
  while(flag_begin && number==0)
   {
     delay(100);
     digitalWrite(BUZZER,HIGH); 
     digitalWrite(LED,LOW);
     delay(100);
     digitalWrite(BUZZER,LOW);
     delay(100);
     digitalWrite(LED,HIGH);
   }    
}

MyTimer/MCU_timer.ino

void MCU_timer_interrupt_init(unsigned long time1 ,unsigned long time2)
{
  MsTimer2::set(time2, timer2_interrupt); // 100ms period
  MsTimer2::start();

  MsTimer1::set(time1, timer1_interrupt); // 100ms period
  MsTimer1::start();
}


void timer1_interrupt()
{
    time_timer=millis();
    
   if( !digitalRead(BUTTON_MS) &&  1000 < (abs(time_button_ms-time_timer)) )
   {
       number +=1;
       if(number%100 == 60 && number != 60)
          number = number/100*100+100;
       
       if(number==60)
          number=100;  
    }

    if( !digitalRead(BUTTON_S) &&  1000 < (abs(time_button_s-time_timer))  )
   {
       number +=100;
   }
}


void timer2_interrupt()
{
  if(flag_begin)
 {    
   if (number > 9999)
      number = 9999;
  
    if(number%100 > 60)
       number = number/100*100+59;

    if(number>0)   
        number--;

    if (number > 9999)
      number = 9999;
  
    if(number%100 > 60)
       number = number/100*100+59;
  }  
}

MyTimer/MySegDispaly.ino

#define TIME 2

void latch_init(const uint8_t latch,const uint8_t clock,const uint8_t data)
{
  pinMode(latch,OUTPUT);
  pinMode(clock,OUTPUT);
  pinMode(data,OUTPUT);
 }

void com_init(const uint8_t com1 , const uint8_t com2 ,const uint8_t com3 ,const uint8_t com4 )
{
  pinMode(com1,OUTPUT);
  pinMode(com2,OUTPUT);
  pinMode(com3,OUTPUT);
  pinMode(com4,OUTPUT);
}

void display_num(uint16_t num)
{
  uint8_t com=0;
  uint8_t polling=0;
  uint8_t bit_num=0;
  uint8_t num_place[4]={0};
  
  if(num>=1000&&num<=9999)
  {
   polling=4;
   bit_num=3;
   num_place[3]=num/1000;
   num_place[2]=num/100%10;
   num_place[1]=num/10%10;
   num_place[0]=num%10;
     for(int i=0;i<4;i++)
     {clear_bit_display();
      choose_com(polling);
      polling--;
      display(num_place[bit_num--]);
      delay(TIME);
     }
   }
   else if(num>=100&&num<1000)
   {
    polling=3;
    bit_num=2;
    num_place[2]=num/100;
    num_place[1]=num/10%10;
    num_place[0]=num%10;
      for(int i=0;i<3;i++)
     {clear_bit_display();
      choose_com(polling);
      polling--;
      display(num_place[bit_num--]);
      delay(TIME);
     }
    }
    else if(num>=10&&num<100)
   {clear_bit_display();
    polling=2;
    bit_num=1;
    num_place[1]=num/10;
    num_place[0]=num%10;
       for(int i=0;i<2;i++)
       {
        choose_com(polling);
        polling--;
        display(num_place[bit_num--]);
        delay(TIME);
       }
    }
    else if(num>=0 && num<=9)
    { clear_bit_display();
        num_place[0]=num%10;
        choose_com(1);
        display(num_place[0]);
        delay(TIME);
     }
    else
     {num=9999;}
 
  }


void led_init(const uint8_t led)
{
   digitalWrite(LED,HIGH);
}


void display(uint8_t num)
{
  digitalWrite(LATCH,LOW);
  shiftOut(DATA,CLOCK,MSBFIRST,num_buf[num]);
  digitalWrite(LATCH,HIGH);
}

void choose_com(uint8_t com)
 {
  switch(com)
  {
    case 1:choose_com1();
            
            break;
    case 2:choose_com2();

            break;
   case 3:choose_com3();

            break;
    case 4:choose_com4();
            
            break;         
    default :break;
    }
  }

void choose_com1()
{
    digitalWrite(COM1,LOW);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,HIGH);   
 }

 void choose_com2()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,LOW);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,HIGH);  
 }
 void choose_com3()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,LOW);
    digitalWrite(COM4,HIGH);  
 }
 void choose_com4()
{
    digitalWrite(COM1,HIGH);
    digitalWrite(COM2,HIGH);
    digitalWrite(COM3,HIGH);
    digitalWrite(COM4,LOW);    
 }

void clear_bit_display()
{
  digitalWrite(LATCH, LOW);
  shiftOut(DATA, CLOCK, MSBFIRST, 0);
  digitalWrite(LATCH, HIGH);
  digitalWrite(LATCH, LOW);
  delay(TIME);
}


 void clear_display()
{
  for(int i=0;i<4;i++)
 {
    choose_com(i);
    digitalWrite(LATCH,LOW);
    shiftOut(DATA,CLOCK,MSBFIRST,0);
    digitalWrite(LATCH,HIGH);
    digitalWrite(LATCH,LOW);
    delay(TIME);
  }
}

MyTimer/MyTimer.h

#ifndef MYTIMER_H
#define MYTIMER_H

void time_out();

static int time_button_ms=0;
static int time_button_s=0;
static int time_button_choose=0;

static int time_timer=0;

static int last_time_button_ms=0;
static int last_time_button_s=0;
static int last_time_button_choose=0;

static uint16_t flag_begin=0;

static uint16_t number=0;

const static uint8_t num_buf[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};

#define BUTTON_S A3
#define BUTTON_MS 6    
#define BUTTON_CHOOSE A1

#define COM4 5
#define COM3 4
#define COM2 3
#define COM1 2

#define LATCH 12  
#define CLOCK 13 
#define DATA 8   

#define LED A0
#define BUZZER A2

#endif

MyTimer/MyTimer.ino

#include "PinChangelnt.h"   // ↑↑↑↑↑↑ all the define in there
#include "MsTimer2.h"
#include "MsTimer1.h"
#include "MyTimer.h"

/***********************************************************************************************************************
 *                  My Timer                                                                      *
 *        Copyright (c)     Elegoo                                                                                  
 *        author:           wee                                                               *                                                                                 *
 *        Version number :  1.00                                                                                       *
 *        Date :            2019.8.22                                                                                   *
 ***********************************************************************************************************************/


void setup() 
{
  latch_init(LATCH,CLOCK,DATA); //74hC595 IC
  
  com_init(COM1,COM2,COM3,COM4); 

  button_init(BUTTON_S,BUTTON_MS,BUTTON_CHOOSE);

  button_interrupt_init(BUTTON_S,BUTTON_MS,BUTTON_CHOOSE);

  MCU_timer_interrupt_init(150 ,1060); //set timer1 0.15s  timer2 1s 
  
  buzzer_init(BUZZER);

  led_init(LED);
}

void loop() 
{
 display_num(number);

 time_out();
}

MyTimer/PinChangelnt.h

#define PCINT_VERSION 2402
#define  detachPinChangeInterrupt(pin)       PCintPort::detachInterrupt(pin)
#define attachPinChangeInterrupt(pin,userFunc,mode) PCintPort::attachInterrupt(pin, &userFunc,mode)
#define getInterruptedPin()             PCintPort::getArduinoPin()
#ifndef PinChangeInt_h
#define PinChangeInt_h
#include "stddef.h"
#include <Arduino.h>
#include <new.h>
#include <wiring_private.h>
#undef DEBUG
#undef  INLINE_PCINT
#define INLINE_PCINT
#if defined __AVR_ATmega2560__ || defined __AVR_ATmega1280__ || defined __AVR_ATmega1281__ || defined __AVR_ATmega2561__ || defined __AVR_ATmega640__
  #define __USE_PORT_JK
  #define NO_PORTA_PINCHANGES
  #define NO_PORTC_PINCHANGES
  #define NO_PORTD_PINCHANGES
  #if ((defined(NO_PORTB_PINCHANGES) && defined(NO_PORTJ_PINCHANGES)) || \
      (defined(NO_PORTJ_PINCHANGES) && defined(NO_PORTK_PINCHANGES)) || \
      (defined(NO_PORTK_PINCHANGES) && defined(NO_PORTB_PINCHANGES)))
    #define INLINE_PCINT inline
  #endif
#else
  #define NO_PORTJ_PINCHANGES
  #define NO_PORTK_PINCHANGES
  #if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
    #ifndef NO_PORTA_PINCHANGES
      #define __USE_PORT_A
    #endif
  #else
    #define NO_PORTA_PINCHANGES
  #endif
  #if (   (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTB_PINCHANGES) && defined(NO_PORTC_PINCHANGES)) || \
      (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTB_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) || \
      (defined(NO_PORTA_PINCHANGES) && defined(NO_PORTC_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) || \
      (defined(NO_PORTB_PINCHANGES) && defined(NO_PORTC_PINCHANGES) && defined(NO_PORTD_PINCHANGES)) )
    #define INLINE_PCINT inline
  #endif
#endif
#define PCdetachInterrupt(pin)  PCintPort::detachInterrupt(pin)
#define PCattachInterrupt(pin,userFunc,mode) PCintPort::attachInterrupt(pin, userFunc,mode)
#define PCgetArduinoPin() PCintPort::getArduinoPin()
typedef void (*PCIntvoidFuncPtr)(void);
class PCintPort {
public:
  PCintPort(int index,int pcindex, volatile uint8_t& maskReg) :
  portInputReg(*portInputRegister(index)),
  portPCMask(maskReg),
  PCICRbit(1 << pcindex),
  portRisingPins(0),
  portFallingPins(0),
  firstPin(NULL)
#ifdef PINMODE
  ,intrCount(0)
#endif
  {
    #ifdef FLASH
    ledsetup();
    #endif
  }
  volatile  uint8_t&    portInputReg;
  static    int8_t attachInterrupt(uint8_t pin, PCIntvoidFuncPtr userFunc, int mode);
  static    void detachInterrupt(uint8_t pin);
  INLINE_PCINT void PCint();
  static volatile uint8_t curr;
  #ifndef NO_PIN_NUMBER
  static  volatile uint8_t  arduinoPin;
  #endif
  #ifndef NO_PIN_STATE
  static volatile uint8_t pinState;
  #endif
  #ifdef PINMODE
  static volatile uint8_t pinmode;
  static volatile uint8_t s_portRisingPins;
  static volatile uint8_t s_portFallingPins;
  static volatile uint8_t s_lastPinView;
  static volatile uint8_t s_pmask;
  static volatile char s_PORT;
  static volatile uint8_t s_changedPins;
  static volatile uint8_t s_portRisingPins_nCurr;
  static volatile uint8_t s_portFallingPins_nNCurr;
  static volatile uint8_t s_currXORlastPinView;
  volatile uint8_t intrCount;
  static volatile uint8_t s_count;
  static volatile uint8_t pcint_multi;
  static volatile uint8_t PCIFRbug;
  #endif
  #ifdef FLASH
  static void ledsetup(void);
  #endif
protected:
  class PCintPin {
  public:
    PCintPin() :
    PCintFunc((PCIntvoidFuncPtr)NULL),
    mode(0) {}
    PCIntvoidFuncPtr PCintFunc;
    uint8_t   mode;
    uint8_t   mask;
    uint8_t arduinoPin;
    PCintPin* next;
  };
  void    enable(PCintPin* pin, PCIntvoidFuncPtr userFunc, uint8_t mode);
  int8_t    addPin(uint8_t arduinoPin,PCIntvoidFuncPtr userFunc, uint8_t mode);
  volatile  uint8_t&    portPCMask;
  const   uint8_t     PCICRbit;
  volatile  uint8_t     portRisingPins;
  volatile  uint8_t     portFallingPins;
  volatile uint8_t    lastPinView;
  PCintPin* firstPin;
};
#ifndef LIBCALL_PINCHANGEINT
volatile uint8_t PCintPort::curr=0;
#ifndef NO_PIN_NUMBER
volatile uint8_t PCintPort::arduinoPin=0;
#endif
#ifndef NO_PIN_STATE
volatile uint8_t PCintPort::pinState=0;
#endif
#ifdef PINMODE
volatile uint8_t PCintPort::pinmode=0;
volatile uint8_t PCintPort::s_portRisingPins=0;
volatile uint8_t PCintPort::s_portFallingPins=0;
volatile uint8_t PCintPort::s_lastPinView=0;
volatile uint8_t PCintPort::s_pmask=0;
volatile char  PCintPort::s_PORT='x';
volatile uint8_t PCintPort::s_changedPins=0;
volatile uint8_t PCintPort::s_portRisingPins_nCurr=0;
volatile uint8_t PCintPort::s_portFallingPins_nNCurr=0;
volatile uint8_t PCintPort::s_currXORlastPinView=0;
volatile uint8_t PCintPort::s_count=0;
volatile uint8_t PCintPort::pcint_multi=0;
volatile uint8_t PCintPort::PCIFRbug=0;
#endif
#ifdef FLASH
#define PINLED 13
volatile uint8_t *led_port;
uint8_t led_mask;
uint8_t not_led_mask;
boolean ledsetup_run=false;
void PCintPort::ledsetup(void) {
  if (! ledsetup_run) {
    led_port=portOutputRegister(digitalPinToPort(PINLED));
    led_mask=digitalPinToBitMask(PINLED);
    not_led_mask=led_mask^0xFF;
    pinMode(PINLED, OUTPUT); digitalWrite(PINLED, LOW);
    ledsetup_run=true;
  }
};
#endif
#if defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__) 
#ifndef NO_PORTA_PINCHANGES
PCintPort portA=PCintPort(1, 0,PCMSK0);
#endif
#ifndef NO_PORTB_PINCHANGES
PCintPort portB=PCintPort(2, 1,PCMSK1);
#endif
#ifndef NO_PORTC_PINCHANGES
PCintPort portC=PCintPort(3, 2,PCMSK2);
#endif
#ifndef NO_PORTD_PINCHANGES
PCintPort portD=PCintPort(4, 3,PCMSK3);
#endif
#else
#ifndef NO_PORTB_PINCHANGES
PCintPort portB=PCintPort(2, 0,PCMSK0);
#endif
#ifndef NO_PORTC_PINCHANGES
PCintPort portC=PCintPort(3, 1,PCMSK1);
#endif
#ifndef NO_PORTD_PINCHANGES
PCintPort portD=PCintPort(4, 2,PCMSK2);
#endif
#endif
#ifdef __USE_PORT_JK
#ifndef NO_PORTJ_PINCHANGES
PCintPort portJ=PCintPort(10,1,PCMSK1);
#endif
#ifndef NO_PORTK_PINCHANGES
PCintPort portK=PCintPort(11,2,PCMSK2);
#endif
#endif
static PCintPort *lookupPortNumToPort( int portNum ) {
    PCintPort *port = NULL;
  switch (portNum) {
#ifndef NO_PORTA_PINCHANGES
  case 1:
    port=&portA;
    break;
#endif
#ifndef NO_PORTB_PINCHANGES
  case 2:
    port=&portB;
    break;
#endif
#ifndef NO_PORTC_PINCHANGES
  case 3:
    port=&portC;
    break;
#endif
#ifndef NO_PORTD_PINCHANGES
  case 4:
    port=&portD;
    break;
#endif
#ifdef __USE_PORT_JK
#ifndef NO_PORTJ_PINCHANGES
  case 10:
    port=&portJ;
    break;
#endif
#ifndef NO_PORTK_PINCHANGES
  case 11:
    port=&portK;
    break;
#endif
#endif
    }
    return port;
}
void PCintPort::enable(PCintPin* p, PCIntvoidFuncPtr userFunc, uint8_t mode) {
  p->mode=mode;
  p->PCintFunc=userFunc;
#ifndef NO_PORTJ_PINCHANGES
  if ((p->arduinoPin == 14) || (p->arduinoPin == 15)) {
    portPCMask |= (p->mask << 1);
  }
  else {
    portPCMask |= p->mask;
  }
#else
    portPCMask |= p->mask;
#endif
  if ((p->mode == RISING) || (p->mode == CHANGE)) portRisingPins |= p->mask;
  if ((p->mode == FALLING) || (p->mode == CHANGE)) portFallingPins |= p->mask;
  PCICR |= PCICRbit;
}
int8_t PCintPort::addPin(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, uint8_t mode)
{
  PCintPin* tmp;
  tmp=firstPin;
  if (firstPin != NULL) {
    do {
      if (tmp->arduinoPin == arduinoPin) { enable(tmp, userFunc, mode); return(0); }
      if (tmp->next == NULL) break;
      tmp=tmp->next;
    } while (true);
  }
  PCintPin* p=new PCintPin;
  if (p == NULL) return(-1);
  p->arduinoPin=arduinoPin;
  p->mode = mode;
  p->next=NULL;
  p->mask = digitalPinToBitMask(arduinoPin);
  if (firstPin == NULL) firstPin=p;
  else tmp->next=p;
#ifdef DEBUG
  Serial.print("addPin. pin given: "); Serial.print(arduinoPin, DEC);
  int addr = (int) p;
  Serial.print(" instance addr: "); Serial.println(addr, HEX);
  Serial.print("userFunc addr: "); Serial.println((int)p->PCintFunc, HEX);
#endif
  enable(p, userFunc, mode);
#ifdef DEBUG
  Serial.print("addPin. pin given: "); Serial.print(arduinoPin, DEC), Serial.print (" pin stored: ");
  int addr = (int) p;
  Serial.print(" instance addr: "); Serial.println(addr, HEX);
#endif
  return(1);
}
int8_t PCintPort::attachInterrupt(uint8_t arduinoPin, PCIntvoidFuncPtr userFunc, int mode)
{
  PCintPort *port;
  uint8_t portNum = digitalPinToPort(arduinoPin);
  if ((portNum == NOT_A_PORT) || (userFunc == NULL)) return(-1);
  port=lookupPortNumToPort(portNum);
  port->lastPinView=port->portInputReg;
#ifdef DEBUG
  Serial.print("attachInterrupt- pin: "); Serial.println(arduinoPin, DEC);
#endif
  return(port->addPin(arduinoPin,userFunc,mode));
}
void PCintPort::detachInterrupt(uint8_t arduinoPin)
{
  PCintPort *port;
  PCintPin* current;
  uint8_t mask;
  uint8_t portNum = digitalPinToPort(arduinoPin);
  if (portNum == NOT_A_PORT) return;
  port=lookupPortNumToPort(portNum);
  mask=digitalPinToBitMask(arduinoPin);
  current=port->firstPin;
  while (current) {
    if (current->mask == mask) {
      uint8_t oldSREG = SREG;
      cli();
#ifndef NO_PORTJ_PINCHANGES
      if ((arduinoPin == 14) || (arduinoPin == 15)) {
        port->portPCMask &= ~(mask << 1);
      }
      else {
        port->portPCMask &= ~mask;
      }
#else
      port->portPCMask &= ~mask;
#endif
      if (port->portPCMask == 0) PCICR &= ~(port->PCICRbit);
      port->portRisingPins &= ~current->mask; port->portFallingPins &= ~current->mask;
      SREG = oldSREG; 
      return;
    }
    current=current->next;
  }
}
void PCintPort::PCint() {
  #ifdef FLASH
  if (*led_port & led_mask) *led_port&=not_led_mask;
  else *led_port|=led_mask;
    #endif
  #ifndef DISABLE_PCINT_MULTI_SERVICE
  uint8_t pcifr;
  while (true) {
  #endif
    #ifdef PINMODE
    PCintPort::s_lastPinView=lastPinView;
    intrCount++;
    PCintPort::s_count=intrCount;
    #endif
    uint8_t changedPins = (PCintPort::curr ^ lastPinView) &
                ((portRisingPins & PCintPort::curr ) | ( portFallingPins & ~PCintPort::curr ));
    #ifdef PINMODE
    PCintPort::s_currXORlastPinView=PCintPort::curr ^ lastPinView;
    PCintPort::s_portRisingPins_nCurr=portRisingPins & PCintPort::curr;
    PCintPort::s_portFallingPins_nNCurr=portFallingPins & ~PCintPort::curr;
    #endif
    lastPinView = PCintPort::curr;
    PCintPin* p = firstPin;
    while (p) {
      if (p->mask & changedPins) {
        #ifndef NO_PIN_STATE
        PCintPort::pinState=PCintPort::curr & p->mask ? HIGH : LOW;
        #endif
        #ifndef NO_PIN_NUMBER
        PCintPort::arduinoPin=p->arduinoPin;
        #endif
        #ifdef PINMODE
        PCintPort::pinmode=p->mode;
        PCintPort::s_portRisingPins=portRisingPins;
        PCintPort::s_portFallingPins=portFallingPins;
        PCintPort::s_pmask=p->mask;
        PCintPort::s_changedPins=changedPins;
        #endif
        p->PCintFunc();
      }
      p=p->next;
    }
  #ifndef DISABLE_PCINT_MULTI_SERVICE
    pcifr = PCIFR & PCICRbit;
    if (pcifr == 0) break;
    PCIFR |= PCICRbit;
    #ifdef PINMODE
    PCintPort::pcint_multi++;
    if (PCIFR & PCICRbit) PCintPort::PCIFRbug=1;
    #endif
    PCintPort::curr=portInputReg;
  }
  #endif
}
#ifndef NO_PORTA_PINCHANGES
ISR(PCINT0_vect) {
  #ifdef PINMODE
  PCintPort::s_PORT='A';
  #endif
  PCintPort::curr = portA.portInputReg;
  portA.PCint();
}
#define PORTBVECT PCINT1_vect
#define PORTCVECT PCINT2_vect
#define PORTDVECT PCINT3_vect
#else
#define PORTBVECT PCINT0_vect
#define PORTCVECT PCINT1_vect
#define PORTDVECT PCINT2_vect
#endif
#ifndef NO_PORTB_PINCHANGES
ISR(PORTBVECT) {
  #ifdef PINMODE
  PCintPort::s_PORT='B';
  #endif
  PCintPort::curr = portB.portInputReg;
  portB.PCint();
}
#endif
#ifndef NO_PORTC_PINCHANGES
ISR(PORTCVECT) {
  #ifdef PINMODE
  PCintPort::s_PORT='C';
  #endif
  PCintPort::curr = portC.portInputReg;
  portC.PCint();
}
#endif
#ifndef NO_PORTD_PINCHANGES
ISR(PORTDVECT){
  #ifdef PINMODE
  PCintPort::s_PORT='D';
  #endif
  PCintPort::curr = portD.portInputReg;
  portD.PCint();
}
#endif
#ifdef __USE_PORT_JK
#ifndef NO_PORTJ_PINCHANGES
ISR(PCINT1_vect) {
  #ifdef PINMODE
  PCintPort::s_PORT='J';
  #endif
  PCintPort::curr = portJ.portInputReg;
  portJ.PCint();
}
#endif
#ifndef NO_PORTK_PINCHANGES
ISR(PCINT2_vect){
  #ifdef PINMODE
  PCintPort::s_PORT='K';
  #endif
  PCintPort::curr = portK.portInputReg;
  portK.PCint();
}
#endif
#endif
#ifdef GET_PCINT_VERSION
uint16_t getPCIntVersion () {
  return ((uint16_t) PCINT_VERSION);
}
#endif
#endif
#endif

ملفات الدرس والمكتبات

المكتبات المرفقة

ملفات الدرس

100%

عرض الصورة

استخدم + و− للتكبير والتصغير، أو اضغط Esc للإغلاق.