Работа с прерываниями

Тема в разделе "Arduino & Shields", создана пользователем postal2201, 20 янв 2017.

  1. postal2201

    postal2201 Нерд

    Здравствуйте! Есть некая функция NTSC_fix()
    Код (C++):
    // Pins
    int lid = 2;      
    int biosA18 = 3;
    int biosD2 = 4;  
    int delay_ntsc = 2400;
    void setup()
    {
      NTSC_fix();
    }

    void NTSC_fix()
    {
      //Make sure all pins are inputs
      DDRB = 0x00;

      //Wait until just before the pulse on biosA18 arrives
      delay(delay_ntsc);

      //...And wait here until it actually happened
      while(!(PINB == B00001000))
      {
        ;  //Wait
      }
      delayMicroseconds(12);
      PORTB = B00000000;
      DDRB = B00010000; // Switch PB4
      delayMicroseconds(5);
      DDRB = 0x00;
    }
    void loop()
    {
      if(lid == 0)
      {
        while(lid != 1);      //Wait until the lid is closed again (after being opened) to initiate a new  cycle
       NTSC_fix();
      }
    }
    Функция выполняется один раз при включении, а также при открытии.закрытии крышки. Но в зависимости от разных моделей, лог 1 на biosA18 может не прийти никогда. Из-за этого программа зависает на одном месте. Чтобы этого не произошло, я так понимаю нужно использовать прерывание. Но прерывание должно будет выполниться только 1 раз! Среагировать только на 1 импульс от biosA18, остальные не учитывать, до цикла открытия/закрытия крышки.
    Я представляю это так: void setup() инициализирует некую функцию Test которая инициализирует прерывание attachInterrupt(0, fix, RISING), далее функция fix() при поступлении лога 1 выполняет необходимое переключение пинов (как в NTSC_fix()), и выполняет detachInterrupt(interrupt), для игнорирования последующих изменений лога на biosA18. Также вызов функции Test помещаем в цикл ожидания открытия/закрытия крышки. Это возможно осуществить, или все это бред и нужен другой способ?
     
    Последнее редактирование: 20 янв 2017
  2. rkit

    rkit Гуру

    У вас в коде вообще никакие значения не считываются.
     
  3. postal2201

    postal2201 Нерд

    Вот, разве это не ожидание импульса?
    Код (C++):
    while(!(PINB & B00001000))
      {
        ;  //Wait
      }
     
  4. rkit

    rkit Гуру

    А как программа до этого места доберется?
     
  5. postal2201

    postal2201 Нерд

    А почему не доберется? NTSC_fix() вызывается в void setup()
     
  6. rkit

    rkit Гуру

    И дальше что? Я не буду строчку за строчкой с вами всё разбирать.
     
  7. postal2201

    postal2201 Нерд

    while(!(PINB == B00001000)), в этом была проблема?
    Проверил на эмуляторе, все работает.
     
    Последнее редактирование: 20 янв 2017
  8. sslobodyan

    sslobodyan Гик

    Ваша задача не совсем понятна (вернее скорость обработки). Но в общем случае такие задачи решаются через флаг состояния либо прерывания.
    Если делаете с помощью прерываний, то в какой-то момент скетч решает, что нужно контролировать вход. Значит скетч "взводит" прерывание через attachInterrupt и дальше себе работает. Как только прерывание сработало, то в процедуре обработки отключается дальнейшая реакция на это прерывание detachInterrupt и обязательно очищается флаг, вызвавший это прерывание. Если флаг не очистить, то возможно повторное прерывание даже после detachInterrupt, а вам нужно гарантировано одно срабатывание. Потом скетч опять может разрешить прерывание.
    Можно и без прерываний делать через конечный автомат или просто сканировать в главном лупе. Но и в таком случае применяем флаг состояния. Ставим сначала этот флаг в сетапе 1. Пока этот флаг в 1, крутим скетч дальше. Обнаружили нужное состояние на пине - вызвали обслуживающую процедуру, в которой внутри сбросили этот флаг в 0. Все, больше скетч туда не зайдет, пока снова флаг не станет 1.
    Как-то так.
     
    arkadyf нравится это.
  9. postal2201

    postal2201 Нерд

    sslobodyan, спасибо за пояснения! Думаю флаг это то что мне нужно.
     
  10. postal2201

    postal2201 Нерд

    Друзья, а при использовании флага состояния программа приостанавливается или начинается заново с петли?
     
  11. sslobodyan

    sslobodyan Гик

    Программа в микроконтроллерах никогда не останавливается, за исключением перевода контроллера в спящий режим. И сама собой тоже ни в какие петли не влазит.
     
  12. mcureenab

    mcureenab Гуру

    Функция loop в цикле запускается.

    Что делать с флагом состояния решает только сама программа. Может игнорировать его в том числе.
     
  13. postal2201

    postal2201 Нерд

    Добрый день! Собрал итоговую прошивку. Но как то странно срабатывает прерывание. Serial.println вместо NTSC_fixed! выводит только 2 первые буквы NT. Почему?
    Код (C++):
    //--------------------------------------------------
    //                  Pinouts!
    //--------------------------------------------------
    //FOR ARDUINO UNO (WITH ATMEGA328):
    // - Arduino pin 8  = data    = ATMega pin 14
    // - Arduino pin 9  = gate    = ATMega pin 15
    // - Arduino pin 10 = lid     = ATMega pin 16
    // - Arduino pin 2 = biosA18  = ATMega pin 2
    // - Arduino pin 12 = biosD2  = ATMega pin 18

    //FOR ATTINY13/25/45/85:
    // - PB0 = biosA18  = ATTiny pin 5
    // - PB1 = gate     = ATTiny pin 6
    // - PB2 = lid      = ATTiny pin 7
    // - PB3 = data     = ATTiny pin 2
    // - PB4 = biosD2   = ATTiny pin 3


    //--------------------------------------------------
    //               Chip selection!
    //--------------------------------------------------
    #define ARDUINO_UNO        //Make that "#define ARDUINO_UNO" if you want to compile for Arduino Uno instead of ATTiny25/45/85

    #ifdef ARDUINO_UNO
    //Pins
    unsigned char data = 8;         //The pin that outputs the SCEE SCEA SCEI string
    unsigned char gate = 9;         //The pin that outputs the SCEE SCEA SCEI string
    unsigned char lid = 10;         //The pin that gets connected to the internal CD lid signal; active high
    unsigned char biosA18 = 2;      //Only used in SCPH-102 PAL mode
    unsigned char biosD2 = 12;      //Only used in SCPH-102 PAL mode
    boolean flagNTSC = 0;
    boolean injectFlag = 0;
    unsigned char delay_between_bits = 4;
    unsigned char delay_between_injections = 74;
    #endif

    #ifdef ATTINY
    //Pins
    unsigned char data = 3;        //The pin that outputs the SCEE SCEA SCEI string
    unsigned char gate = 1;
    unsigned char lid = 2;         //The pin that gets connected to the internal CD lid signal; active high
    unsigned char biosA18 = 0;     //Only used in SCPH-102 PAL mode
    unsigned char biosD2 = 4;      //Only used in SCPH-102 PAL mode
    boolean flagNTSC = 0;
    boolean injectFlag = 0;
    unsigned char delay_between_bits = 4;
    unsigned char delay_between_injections = 68;
    #endif

    bool readBit(int index, const unsigned char *ByteSet)
    {
      int byte_index = index >> 3;
      byte bits = pgm_read_byte(&(ByteSet[byte_index]));
      int bit_index = index & 0x7; // same as (index - byte_index<<3) or (index%8)
      byte mask = 1 << bit_index;
      return (0 != (bits & mask));
    }
    void NTSC_fix()
    {
      detachInterrupt(0);
      flagNTSC = 1;
      delayMicroseconds(12);
      PORTB = 0b00000000;
      DDRB = 0b00010000;
      delayMicroseconds(5);
      DDRB = 0x00;
      Serial.println("NTSC_fixed!");
      delay(60000);
    }

    void inject_SCEE()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010};
        Serial.println("inject_SCEE");
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          Serial.print(0);
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
          Serial.print(1);
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }

    void inject_SCEA()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010};
      Serial.println("inject_SCEA");
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          Serial.print(0);
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
          Serial.print(1);
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }

    void inject_SCEI()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11011010, 0b00000010};
      Serial.println("inject_SCEI");
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          Serial.print(0);
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
          Serial.print(1);
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }


    void inject_SCEx()
    {
      unsigned char loop_counter;

      pinMode(gate, OUTPUT);
      digitalWrite(gate, 0);

      for (loop_counter = 0; loop_counter < 40; loop_counter = loop_counter + 1)
      {
        inject_SCEE();
        inject_SCEA();
        inject_SCEI();
      }

      pinMode(gate, INPUT);
      pinMode(data, INPUT);
      injectFlag =1;
      Serial.println("inject_DONE!");
     
    }


    void setup()
    {
      Serial.begin(9600); // инициализация послед. порта
      pinMode(data, INPUT);
      pinMode(gate, INPUT);
      pinMode(lid, INPUT);
      pinMode(biosA18, INPUT);
      attachInterrupt(0, NTSC_fix, RISING);
      delay(1200);
      inject_SCEx();
    }


    void loop()
    {

      if (lid == 0)
      {
        while (lid != 1);     //Wait until the lid is closed again (after being opened) to initiate a new injection cycle
        flagNTSC = 0;
        injectFlag = 0;
        attachInterrupt(0, NTSC_fix, RISING);
        delay(1200);
        inject_SCEx();
      }
      if ((injectFlag == 0) && (flagNTSC == 1))
      {
        inject_SCEx();
      }

    }
     
  14. mcureenab

    mcureenab Гуру

    Пока выполняется обработчик прерывания другие прерывания не выполняются.
    Выносите этот код из обработчика прерывания.

    Код (C++):
      Serial.println("NTSC_fixed!");
      delay(60000);
     
     
  15. postal2201

    postal2201 Нерд

    Получается Serial.println обрабатывается тоже прерыванием? Если так, то все отлично, Serial.println мне нужен только для отладки. А delayMicroseconds работают в обработчике прерывания?
     
  16. mcureenab

    mcureenab Гуру

    delayMicroseconds это обычный холостой цикл.

    Это обязательно в обработчике прерывания делать:


    Код (C++):
      delayMicroseconds(12);
      PORTB = 0b00000000;
      DDRB = 0b00010000;
      delayMicroseconds(5);
      DDRB = 0x00;
     
    в прерывании желательно оставить только

    Код (C++):
    volitile boolean flagINT = 0;
    Код (C++):
    void NTSC_fix()
    {
      detachInterrupt(0);
      flagINT = true;
    }
     
    а в loop поместить

    Код (C++):
    if(flagINT) {
      flagNTSC = 1;
      delayMicroseconds(12);
      PORTB = 0b00000000;
      DDRB = 0b00010000;
      delayMicroseconds(5);
      DDRB = 0x00;
      Serial.println("NTSC_fixed!");
      delay(60000);
      flagINT = false;
    }
     
     
  17. sslobodyan

    sslobodyan Гик

    Не надо ничего переносить - там же жесткие тайминги.
     
  18. mcureenab

    mcureenab Гуру

    Вот именно, что тайминги. Пока функция отрабатывает в режиме прерывания другие прерывания, в том числе по таймеру не обрабатываются. Нарушается работа функций millis и deleay.
     
  19. sslobodyan

    sslobodyan Гик

    Да, но у спросившего микросекунды, а они работают только по счетчику без прерываний. И на сколько я понял, это работа с тв-экраном, там просто так менять или переносить задержки не стОит.
     
  20. postal2201

    postal2201 Нерд

    Друзья, Всем спасибо. Поправил последнюю функцию, следящую за открытием.закрытием крышки. Та что была изначально не работала. Практически весь исходный авторский код получился переписан. Теперь осталось определить задержки для Attiny13, так как ардуино и 25-85 аттини для этого проекта работают на 8Мгц, а аттини13 изначально работает на 9.6Мгц.
    P.S. Я знаю что можно перестроить внутренний генератор на другую частоту записью необходимого значения в OSCCAL. Но как это правильно сделать не знаю. Если кто знает напишите пожалуйста. Судя по даташиту на Attiny13a, 8Мгц соответствует значение OSCCAL 74.
    Код (C++):
    //    ATTYNee!!! Universal mod for PS1 & PSOne.
    //--------------------------------------------------
    //                  Pinouts!
    //--------------------------------------------------
    //FOR ARDUINO UNO (WITH ATMEGA328):
    // - Arduino pin 8  = data    = ATMega pin 14
    // - Arduino pin 9  = gate    = ATMega pin 15
    // - Arduino pin 10 = lid     = ATMega pin 16
    // - Arduino pin 2 = biosA18  = ATMega pin 2
    // - Arduino pin 12 = biosD2  = ATMega pin 18

    //FOR ATTINY13/25/45/85:
    // - PB0 = biosA18  = ATTiny pin 5
    // - PB1 = gate     = ATTiny pin 6
    // - PB2 = lid      = ATTiny pin 7
    // - PB3 = data     = ATTiny pin 2
    // - PB4 = biosD2   = ATTiny pin 3


    //--------------------------------------------------
    //               Chip selection!
    //--------------------------------------------------
    #define ARDUINO_UNO        //Make that "#define ARDUINO_UNO" if you want to compile for Arduino Uno instead of ATTiny25/45/85

    #ifdef ARDUINO_UNO
    //Pins
    unsigned char data = 8;         //The pin that outputs the SCEE SCEA SCEI string
    unsigned char gate = 9;         //The pin that outputs the SCEE SCEA SCEI string
    unsigned char lid = 10;         //The pin that gets connected to the internal CD lid signal; active high
    unsigned char biosA18 = 2;      //Only used in SCPH-102 PAL mode
    unsigned char biosD2 = 12;      //Only used in SCPH-102 PAL mode
    boolean flagNTSC = 0;
    boolean injectFlag = 0;
    boolean tray = 0;
    unsigned char delay_between_bits = 4;
    unsigned char delay_between_injections = 74;
    #endif

    #ifdef ATTINY
    //Pins
    unsigned char data = 3;        //The pin that outputs the SCEE SCEA SCEI string
    unsigned char gate = 1;
    unsigned char lid = 2;         //The pin that gets connected to the internal CD lid signal; active high
    unsigned char biosA18 = 0;     //Only used in SCPH-102 PAL mode
    unsigned char biosD2 = 4;      //Only used in SCPH-102 PAL mode
    boolean flagNTSC = 0;
    boolean injectFlag = 0;
    boolean tray = 0;
    unsigned char delay_between_bits = 4;
    unsigned char delay_between_injections = 68;
    #endif

    bool readBit(int index, const unsigned char *ByteSet)
    {
      int byte_index = index >> 3;
      byte bits = pgm_read_byte(&(ByteSet[byte_index]));
      int bit_index = index & 0x7; // same as (index - byte_index<<3) or (index%8)
      byte mask = 1 << bit_index;
      return (0 != (bits & mask));
    }
    void NTSC_fix()
    {
      detachInterrupt(0);
      flagNTSC = 1;
      delayMicroseconds(12);
      PORTB = 0b00000000;
      DDRB = 0b00010000;
      delayMicroseconds(5);
      DDRB = 0x00;
    // Serial.println("NTSC_fixed!");  //Debug only
    }

    void inject_SCEE()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11101010, 0b00000010};
      //Serial.println("inject_SCEE");  //Debug only
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          //Serial.print(0);  //Debug only
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
          //Serial.print(1);  //Debug only
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }

    void inject_SCEA()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11111010, 0b00000010};
      //Serial.println("inject_SCEA");  //Debug only
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          //Serial.print(0);  //Debug only
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
          //Serial.print(1);  //Debug only
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }

    void inject_SCEI()
    {
      static const unsigned char ByteSet[] PROGMEM = {0b01011001, 0b11001001, 0b01001011, 0b01011101, 0b11011010, 0b00000010};
      //Serial.println("inject_SCEI");  //Debug only
      for (unsigned char i = 0; i < 44; i++) {
        if (readBit(i, ByteSet) == 0)
        {
          //Serial.print(0);  //Debug only
          pinMode(data, OUTPUT);
          digitalWrite(data, 0);
          delay(delay_between_bits);
        }
        else
        {
         //Serial.print(1);  //Debug only
          pinMode(data, INPUT);
          delay(delay_between_bits);
        }
      }

      pinMode(data, OUTPUT);
      digitalWrite(data, 0);
      delay(delay_between_injections);
    }


    void inject_SCEx()
    {
      unsigned char loop_counter;

      pinMode(gate, OUTPUT);
      digitalWrite(gate, 0);

      for (loop_counter = 0; loop_counter < 40; loop_counter = loop_counter + 1)
      {
        inject_SCEE();
        inject_SCEA();
        inject_SCEI();
      }

      pinMode(gate, INPUT);
      pinMode(data, INPUT);
      injectFlag = 1;
      //Serial.println("inject_DONE!"); //Debug only

    }


    void setup()
    {
      //Serial.begin(9600); //Debug only
      pinMode(data, INPUT);
      pinMode(gate, INPUT);
      pinMode(lid, INPUT);
      pinMode(biosA18, INPUT);
      attachInterrupt(0, NTSC_fix, RISING);
      delay(1200);
      inject_SCEx();

    }

    void loop()
    {

      if (digitalRead(lid) == LOW && tray == 0)
      {
       // Serial.println("Open"); //Debug only
        tray = 1;
      }
      if (digitalRead(lid) == HIGH && tray == 1)
      {
       // Serial.println("Closed"); Debug only
        tray = 0;
        flagNTSC = 0;
        injectFlag = 0;
        attachInterrupt(0, NTSC_fix, RISING);
        delay(1200);
        inject_SCEx();
      }
      if (injectFlag == 0 && flagNTSC == 1)
      {
        inject_SCEx();
      }
    }