Помогите с дешифровкой ИК сигнала

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Задача у меня такая:

Есть некий ИК пульт, работает по NONAME протоколу, 100% ни один из распространенных(Код повтора 5 и 3 мс, ни где такого не нашел).

Естественно при использовании  irrecv.decode я получаю набор белиберды.

Есть ли возможность записать на ардуинке паттерн сигнала? На выходе хочу получить следующее(как бы это выглядело с протоколом NEC):

9000,  -2250, 560, -560.... и тп

 

b707
Offline
Зарегистрирован: 26.05.2017

Так посмотрите в исходнике библиотеки, кае описан протокол NEC и напишите так же

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

"Паттерн сигнала" удобнее всего записать логическим анализатором.

b707
Offline
Зарегистрирован: 26.05.2017

А, видимо я не понял вопроса

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

В данный момент на руках только ардуинка и комп, через микрофон уже пробовал снимать разными анализаторами сигнал, выглядит так:

https://ibb.co/Dp7j91r

Но я думаю что вижу не полную картину ибо слушаю сигнал на 22кгц. По этому хотел задействовать ардуино.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Повторюсь: удобнее всего - логическим анализатором. Логический анализатор на частоты до примерно 100 кГц можно сделать и из Ардуино Уно/Нано/Мини.

rkit
Offline
Зарегистрирован: 23.11.2016

500кГц, может? Один канал-то.

rkit
Offline
Зарегистрирован: 23.11.2016

хотя не, 500 многовато

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Пока нашел только SVmonitor для ардуино, но опрос настолько медленный, что максимум, что можно корректно записать это нажатие кнопки... Можете посоветовать анализатор под ардуино годный для моей задачи?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

andriano пишет:

Повторюсь: удобнее всего - логическим анализатором. Логический анализатор на частоты до примерно 100 кГц можно сделать и из Ардуино Уно/Нано/Мини.

Да по его картинке все видно и так. Увеличить и линейкой измерить. Я так уже примерно понял, что это. Только его картинка перевернута. И, кстати,  это почти NEC. ;))

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Линейкой мерил и с виду весь сигнал удается повторить, но приемник на него не реагирует. Думаю дело в начале пакета, первые 5 мс есть небольшие провалы и это не шум, скорее всего это не 5000, -3000... а больше похоже на 1600, -100, 1600, -100, 1600, -3000. Но эти провалы очень размыты и их длину я померить могу только приблизительно. Пробовал кстати оба варианта, все равно ноль реакции, в общем устал уже мерить линейкой, хочу просто записать.

Похоже что этот код то, что мне нужно:



/* Raw IR decoder sketch!
 
 This sketch/program uses the Arduno and a PNA4602 to 
 decode IR received. This can be used to make a IR receiver
 (by looking for a particular code)
 or transmitter (by pulsing an IR LED at ~38KHz for the
 durations detected 
 
 Code is public domain, check out www.ladyada.net and adafruit.com
 for more tutorials! 
 */

// We need to use the 'raw' pin reading methods
// because timing is very important here and the digitalRead()
// procedure is slower!
//uint8_t IRpin = 2;
// Digital pin #2 is the same as Pin D2 see
// http://arduino.cc/en/Hacking/PinMapping168 for the 'raw' pin mapping
#define IRpin_PIN      PIND
#define IRpin          2

// the maximum pulse we'll listen for - 65 milliseconds is a long time
#define MAXPULSE 65000

// what our timing resolution should be, larger is better
// as its more 'precise' - but too large and you wont get
// accurate timing
#define RESOLUTION 20 

// we will store up to 100 pulse pairs (this is -a lot-)
uint16_t pulses[100][2];  // pair is high and low pulse 
uint8_t currentpulse = 0; // index for pulses we're storing

void setup(void) {
  Serial.begin(9600);
  Serial.println("Ready to decode IR!");
}

void loop(void) {
  uint16_t highpulse, lowpulse;  // temporary storage timing
  highpulse = lowpulse = 0; // start out with no pulse length
  
  
//  while (digitalRead(IRpin)) { // this is too slow!
    while (IRpin_PIN & (1 << IRpin)) {
     // pin is still HIGH

     // count off another few microseconds
     highpulse++;
     delayMicroseconds(RESOLUTION);

     // If the pulse is too long, we 'timed out' - either nothing
     // was received or the code is finished, so print what
     // we've grabbed so far, and then reset
     if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {
       printpulses();
       currentpulse=0;
       return;
     }
  }
  // we didn't time out so lets stash the reading
  pulses[currentpulse][0] = highpulse;
  
  // same as above
  while (! (IRpin_PIN & _BV(IRpin))) {
     // pin is still LOW
     lowpulse++;
     delayMicroseconds(RESOLUTION);
     if ((lowpulse >= MAXPULSE)  && (currentpulse != 0)) {
       printpulses();
       currentpulse=0;
       return;
     }
  }
  pulses[currentpulse][1] = lowpulse;

  // we read one high-low pulse successfully, continue!
  currentpulse++;
}

void printpulses(void) {
  Serial.println("\n\r\n\rReceived: \n\rOFF \tON");
  for (uint8_t i = 0; i < currentpulse; i++) {
    Serial.print(pulses[i][0] * RESOLUTION, DEC);
    Serial.print(" usec, ");
    Serial.print(pulses[i][1] * RESOLUTION, DEC);
    Serial.println(" usec");
  }
  
  // print it in a 'array' format
  Serial.println("int IRsignal[] = {");
  Serial.println("// ON, OFF ");
  for (uint8_t i = 0; i < currentpulse-1; i++) {
    //Serial.print("\t"); // tab
    Serial.print("pulseIR(");
    Serial.print(pulses[i][1] * RESOLUTION , DEC);
    Serial.print(");");
    Serial.println("");
    //Serial.print("\t");
    Serial.print("delayMicroseconds(");
    Serial.print(pulses[i+1][0] * RESOLUTION , DEC);
    Serial.println(");");

  }
  //Serial.print("\t"); // tab
  Serial.print("pulseIR(");
  Serial.print(pulses[currentpulse-1][1] * RESOLUTION, DEC);
  Serial.print(");");

}

 

Однако он нормально измеряет только если пальцами водить по дорожкам, на пульт он не реагирует почему то, хотя:

#include<IRremote.h>
IRrecv  irrecv(12);
decode_results results;

Отлично реагирует на пульт, правда выдает бессмыслицу. Такое ощущение что IRrecv намного чаще опрашивает вход чем digitalRead.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Разобрался почему не работало, нужно было Resolution уменьшить. Однако получаю слишком плавающие тайминги:

Received:

OFF     ON
57958 usec, 8330 usec
985 usec, 50740 usec
35 usec, 17020 usec
53700 usec, 8835 usec
480 usec, 59125 usec
35 usec, 9035 usec
53305 usec, 7440 usec
1870 usec, 65175 usec
0 usec, 2665 usec
53660 usec, 8035 usec
1275 usec, 7315 usec
0 usec, 44635 usec
0 usec, 17270 usec
52275 usec, 8525 usec
790 usec, 59120 usec
40 usec, 9585 usec
52755 usec, 8255 usec
1060 usec, 45930 usec
0 usec, 21675 usec
53895 usec, 8530 usec
785 usec, 25500 usec
0 usec, 28850 usec
0 usec, 14545 usec
52595 usec, 9135 usec
180 usec, 10935 usec
0 usec, 16960 usec
0 usec, 41630 usec
51970 usec, 7710 usec
1605 usec, 20660 usec
0 usec, 7235 usec
0 usec, 40285 usec
53315 usec, 8080 usec
1235 usec, 49545 usec
5 usec, 19260 usec
52685 usec, 29975 usec
0 usec, 49360 usec
51480 usec, 8345 usec
965 usec, 43535 usec
0 usec, 20425 usec
0 usec, 4755 usec
52790 usec, 8190 usec
1125 usec, 20650 usec
0 usec, 33700 usec
0 usec, 7195 usec
0 usec, 6890 usec
53055 usec, 46820 usec
0 usec, 32465 usec
51530 usec, 8325 usec
985 usec, 30300 usec
0 usec, 13225 usec
0 usec, 10825 usec
0 usec, 13900 usec
53245 usec, 8690 usec
625 usec, 37500 usec
0 usec, 6055 usec
0 usec, 24565 usec
53370 usec, 9260 usec
55 usec, 20660 usec
0 usec, 48965 usec
51875 usec, 8870 usec
440 usec, 51955 usec
0 usec, 17025 usec
52520 usec, 8230 usec
1090 usec, 3029 usec
52935 usec, 9000 usec
315 usec, 4804 usec
51160 usec, 8525 usec
790 usec, 61515 usec
45 usec, 7215 usec
52725 usec, 8030 usec
1280 usec, 23095 usec
0 usec, 2405 usec
0 usec, 16800 usec
0 usec, 25970 usec
53225 usec, 11745 usec
0 usec, 849 usec
52685 usec, 9025 usec
285 usec, 8535 usec
0 usec, 60135 usec
52830 usec, 7830 usec
1485 usec, 3650 usec
0 usec, 8500 usec
0 usec, 4840 usec
0 usec, 6100 usec
0 usec, 45095 usec
53310 usec, 9225 usec
85 usec, 2994 usec
52975 usec, 8040 usec
1270 usec, 30300 usec
0 usec, 33660 usec
0 usec, 5275 usec

Особенно смущают значения 0 usec. Почему так могут плавать значения? Ардуинка все таки не может так быстро опрашивать вход?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

rkit пишет:

500кГц, может? Один канал-то.

А какая разница, сколько каналов?

Что, прочитать один бит из порта быстрее, чем порт целиком?

Upper
Offline
Зарегистрирован: 23.06.2020

andriano пишет:

А какая разница, сколько каналов?

Что, прочитать один бит из порта быстрее, чем порт целиком?

Смотря что понимать под логическим анализатором. Если просто отправка в SERIAL на компьютер и там анализировать, то один канал  требует в восемь раз меньшей скорости SERIAL При частоте опроса 500 КГц требует скорость примерно 500000*10/8 = 625000 baud. Ближайшая частота для кварца 16МГц равна 666667. Восемь каналов с частотой опроса 500 КГц уже не передать.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Помогите пожалуйста доработать код. Попробовал через аналоговый вход считать и все выглядит довольно убедительно, однако в неудобном исполнении. Я же хочу вывести пройденное время при смене 0\1:

Сейчас на выходе получаю это:

One: 9304636 ms.
Zero: 9305356 ms.
One: 9306120 ms.
Zero: 9306836 ms.
Zero: 9307596 ms.
Zero: 9308356 ms.
One: 9309116 ms.
Zero: 9309836 ms.
Zero: 9310596 ms.
Zero: 9311360 ms.
Zero: 9312116 ms.
Zero: 9312876 ms.
Zero: 9313636 ms.
Zero: 9314396 ms.
One: 9315156 ms.
Zero: 9315876 ms.
One: 9316636 ms.
Zero: 9317356 ms.
Zero: 9318116 ms.
Zero: 9318876 ms.
One: 9319636 ms.
Zero: 9320356 ms.
Zero: 9321116 ms.
Zero: 9321876 ms.

И если посчитать на калькуляторе, тайминги ровные. Я с ардуино познакомился совсем недавно, не могу понять почему при таком коде, мне выдает актуальное время а не разницу между предыдущим результатом и актуальным временем, код такой:

void loop()
{
int val_1 = analogRead(A0); 
int val_2 = 100;
unsigned long myTime;
unsigned long LastTime;
unsigned long ResultTime;
      myTime = micros();
      ResultTime = myTime - LastTime;
  if (val_1 > val_2) {
      LastTime = myTime;
      Serial.print("Zero: "); 
      Serial.print(ResultTime);
      Serial.println(" ms.");
} else {
     LastTime = myTime;
     Serial.print("One: ");
     Serial.print(ResultTime);
     Serial.println(" ms.");
  }}

 

 

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Итак, через три дня я смог написать простую программу для записи паттернов, на основе прерываний:

  volatile boolean flag; // Флаг прерывания
  volatile unsigned long timer; // Рассчитанное время между прерываниями
  volatile unsigned long timer2; // Таймер последнего значение прошлого прерывания
void setup() {
  Serial.begin(500000); // Запускаем порт
  attachInterrupt(0, up, CHANGE); // Задаем функцию обработки внешнего прерывания
}
void loop() {   
if (flag) {  // Если флаг прерывания взведен, выводим время
Serial.print(timer); //Печатаем время
Serial.print(", ");
flag = 0; // Сбрасываем флаг
}}
void up() { // Обрабатываем прерывание
  flag = 1; // Взводим флаг
  timer = micros()- timer2; // Высчитываем пройденое время с прошлого прерывания
  timer2 = micros(); // Запоминаем время последнего прерывания
  } 

На выходе получаем такое:

364892< мусор, 3068, 580, 584, 580, 584, 584, 580, 584, 584, 580, 584, 580, 584, 580, 588, 584, 584, 588, 588, 1604, 1608, 1608, 1612, 1608, 1608, 1612, 1604, 1608, 1612, 1604, 1608, 1608, 1608, 1608, 1608, 1608, 1600, 48400< повтор телеграммы

Однако допустим 584 должен выглядеть как 584,584(логическая единица), а 1608 как 584, 1608(логический ноль)

Если это отредактировать заменой в блокноте, на выходе получаем почти один в один диаграмму, за несколькими но, она немного длиннее оригинала, т.к. то, что считала ардуина 584 на самом деле 530,не знаю с чем это связано, возможно обработка прерывания занимает 54мкс. И начало пакета которое длится 5 мс(единицу) она не замечает в упор, дописывал руками, хотя ноль 3мс отлично видит. Заказал логический анализатор, замучила меня эта ардуина.

Upper
Offline
Зарегистрирован: 23.06.2020

Tier_zNet пишет:

то, что считала ардуина 584 на самом деле 530,не знаю с чем это связано, возможно обработка прерывания занимает 54мкс. И начало пакета которое длится 5 мс(единицу) она не замечает в упор, дописывал руками, хотя ноль 3мс отлично видит. Заказал логический анализатор, замучила меня эта ардуина.

А как узнали, что оно на самом деле 530?
По логике, длительность обработки прерывания не должна влиять на показания, т.к. она вносит постоянную задержку вызова, и на разность между вызовами влиять не должна.
Если не рассматривать задержки за счет выполнения других прерываний, то данный код наоборот МОЖЕТ занижать значение длительности (но незначительно), т.к. micros() вызывается два раза. С этой точки зрения правильнее написать так

void up() { // Обрабатываем прерывание
  flag = 1; // Взводим флаг
  unsigned long tmp = micros();
  timer = tmp- timer2; // Высчитываем пройденое время с прошлого прерывания
  timer2 = tmp; // Запоминаем время последнего прерывания
  } 
Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Upper пишет:

 как узнали, что оно на самом деле 530?

Просто измеряю рулеткой в анализаторе.

Итак, по непонятным для меня причинам, сегодня заново прогрузил тот же скетч и получил актуальные паттерны (почти), а так же с вашими изменениями, результат одинаков:

3177192(мусор), 8000(5+3 мс старт пакета), 1060(единица), 1056, 1060, 1060, 1060, 1056, 1060, 1056, 1060, 1060, 1060, 1060, 1056, 1060, 1060, 1056, 1060, 1064, 2080(ноль), 2084, 2084, 2084, 2080, 2088, 2080, 2084, 2084, 2084, 2084, 2084, 2084, 2080, 2088, 2080, 2084, 2080, 48800(перерыв между пакетами)

Прилагаю картинку оригинала и моей копии пакета, прошу помочь разобраться с моими вопросами:

https://ibb.co/c17XZvR

1) Как видно по паттерну он отсчитывает логическую единицу(1*) как 1060 (530мс High + 530мс Low), то же самое с нулем(2*) (2080 = 530 High + 1550 Low) и со стартом пакета(3*) (8000 = 5000 High + 3000 Low). Хотя я использую прерывание CHANGE, то есть я должен получать единицу как 530, 530, а не 1060, почему так происходит?

2) Если с первым вопросом я могу корректировать полученные значения вручную, то вот, что делать со стартом пакета не знаю, на картинке видно, что начало телеграммы(3*) у моей копии это ровная планка 5мс, в то время как у оригинала есть два небольших спада, которые не видит ни ардуино, ни я не могу померить четко их анализатором. Это не помеха, начало пакета это точно не 5000, как я писал выше это похоже на 1600,100,1600,100,1600, но гарантированно это измерить я не могу. Почему ардуина эти спады не замечает, слишком короткие?

Upper
Offline
Зарегистрирован: 23.06.2020

Я не разбираюсь в том как работают ИК пульты, и не могу сказать как должно быть и как не должно быть.

В добавок в вашем вопросе картинка не совпадает с описанием. 1* 2* и 3* в тексте и 1 2 3 выделения на картинке. 

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Спасибо, поправил.

nik182
Offline
Зарегистрирован: 04.05.2015

Чуть не так это делается. Нужно завести массив и складывать в него полученные длительности после получения старт пакета. После получения перерыва выстааить флаг и выводить все значения в сериал из loop. Скорость сериала поставить 115200.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

nik182 пишет:
Чуть не так это делается. Нужно завести массив и складывать в него полученные длительности после получения старт пакета. После получения перерыва выстааить флаг и выводить все значения в сериал из loop. Скорость сериала поставить 115200.

Что это даст? Ардуина рассмотрит структуру моего проблемного участка вначале пакета (5мс)? Или просто для красоты? Я вчера мучился со сборкой этого массива, исплевался и бросил, просто задрал скорость порта.

nik182
Offline
Зарегистрирован: 04.05.2015

На таких таймингах - около миллисекунды можно и с задранной скоростью обойтись. Вывод одного символа через сериал на 115200 занимает 87 мкс, строки из 4 символов и cr lf - 486 мкс, что меньше Ваших 1056 мкс и значить буфер сериала чистится своевременно. Если наоборот, то при длинных последовательностях возможно переполнение буфера сериала и потери символов.  Вот здесь получали данные с таймингами 20 мкс. Справились только с помощью массива.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

nik182 пишет:

На таких таймингах - около миллисекунды можно и с задранной скоростью обойтись. Вывод одного символа через сериал на 115200 занимает 87 мкс, строки из 4 символов и cr lf - 486 мкс, что меньше Ваших 1056 мкс и значить буфер сериала чистится своевременно. Если наоборот, то при длинных последовательностях возможно переполнение буфера сериала и потери символов.  Вот здесь получали данные с таймингами 20 мкс. Справились только с помощью массива.

Помогите понять, что я делаю не так:

  volatile boolean flag; // Флаг прерывания
  volatile unsigned long timer; // Рассчитанное время между прерываниями
  volatile unsigned long timer2; // Таймер последнего значение прошлого прерывания
  volatile int massiv[500];
 
  void setup() {
  Serial.begin(500000); // Запускаем порт
  attachInterrupt(0, up, CHANGE); // Задаем функцию обработки внешнего прерывания
}

void loop() {
  if (flag){ // На выходе всего этого получаю бред
  for (int il = 0; il < 500; il++) {
  Serial.print(massiv[il]);
  Serial.print(", ");
  flag = 0;
}}}
void up() { // Обрабатываем прерывание
  unsigned long tmp = micros();
  timer = tmp - timer2; // Высчитываем пройденое время с прошлого прерывания
  timer2 = tmp; // Запоминаем время последнего прерывания
  for (int i = 0; i<500; i++){  // Надеюсь что сдвигаю массив
  massiv[i] = timer; // Надеюсь, что пишу время в массив
  if (timer<30000){ // Т.к. перерыв между пакетами 48800, при значении в 30к считаю что пакет окончен
  flag = 1;} // Взвожу по такому поводу флаг
 }}

 

nik182
Offline
Зарегистрирован: 04.05.2015

Не надо for в прерывании. Перед началом работы очищаем волатильную глобальную переменную, которая будет содержать указатель на текущее положение массива, и   в прерывании, после записи в массив данных, делаем инкремент этой переменной. Проверяем получившееся значение и если превышает заданное количество выставляем флаг что можно выводить. В цикле loop проверяем флаг, если выставлен то очищаем флаг и переменную и в цикле for выводим в сериал заданное количество. Если повезло и до начала вывода не случилось прерывания, то получим нормальные данные. Как по мне, то 500 мкс должно хватить. По ссылке в предыдущем посте можно посмотреть как это реализовано.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

nik182 пишет:

Не надо for в прерывании. Перед началом работы очищаем волатильную глобальную переменную, которая будет содержать указатель на текущее положение массива, и   в прерывании, после записи в массив данных, делаем инкремент этой переменной. Проверяем получившееся значение и если превышает заданное количество выставляем флаг что можно выводить. В цикле loop проверяем флаг, если выставлен то очищаем флаг и переменную и в цикле for выводим в сериал заданное количество. Если повезло и до начала вывода не случилось прерывания, то получим нормальные данные. Как по мне, то 500 мкс должно хватить. По ссылке в предыдущем посте можно посмотреть как это реализовано.

Случилось, вышел вот такой код:

  volatile boolean flag = 0; // Флаг прерывания
  volatile unsigned long timer; // Рассчитанное время между прерываниями
  volatile unsigned long timer2; // Таймер последнего значение прошлого прерывания
  volatile int massiv[600];
  volatile int counter =0;
  volatile int counter2 =0;
 
  void setup() {
  Serial.begin(500000); // Запускаем порт
  attachInterrupt(0, up, CHANGE); // Задаем функцию обработки внешнего прерывания
}

void loop() {
    if (counter > 500){ // После 500 записей взводим флаг
    flag = 1;
    }
if (flag){
    Serial.print(massiv[counter2]); // Печатаем массив
    Serial.print(", ");
    counter2 = counter2 + 1; // Смещаемся на следующую строку
}
if (counter2 > 500){ // Отвечатав 500 строк сбрасываем цикл
    flag = 0;
    counter = 0;
    counter2 = 0;
  }}
void up() { // Обрабатываем прерывание
  unsigned long tmp = micros();
  timer = tmp - timer2; // Высчитываем пройденое время с прошлого прерывания
  timer2 = tmp; // Запоминаем время последнего прерывания
  massiv[counter] = timer; // Пишу время в массив
  counter = counter + 1; // Сдвигаю массив
 }

Но на выходе я получил совершенно те же данные, только пачками по 500 штук. Все таки порт успевал.

Upper
Offline
Зарегистрирован: 23.06.2020

Если у вас несколько ардуин, сделайте из одной генератор сигналов - вроде это и есть ваша конечная цель, отправляйте с нее известные сигналы на "логический анализатор" и смотрите, что он правильно измеряет, что нет. Какие минимальные импульсы может зарегистрировать. Хорошо бы знать из теории - какая может быть минимальная длительность импульса, с какой точностью надо воспроизводить импульсы. Тогда будет понятно, хватает возможностей самоделки, или надо ждать купленного.

Tier_zNet
Offline
Зарегистрирован: 25.09.2021

Мало ардуин, мало, мало ардуин (голосом Бузовой). Я пришел к выводу, что ардуина это игрушка и я в нее наигрался, поэтому, подожду логический анализатор китайский на 24 мгц :)

nik182
Offline
Зарегистрирован: 04.05.2015

Я ж писал выше про тайминги, что при 115200 Ваша программа будет успевать получать все данные. Массив в прерывании нужен, если время отправки одной строки больше времени между импульсами. Нужно помнить, что через миллис точность получения данных 4 мкс. Если нужна большая точность, то надо использовать свободный таймер для подсчёта интервалов времени. Зарядить его на нужную точность.   

Upper
Offline
Зарегистрирован: 23.06.2020

Там (#15) программа так написана, что минимально отрабатываемое время очень большое. В том числе за счет того, что флаг сбрасывается не сразу, а после обработки и поэтому, если за это время пришло и обработалось прерывание, то флаг обработки этого прерывания будет утерян. 

 Если перенести 

12     flag = 0; // Сбрасываем флаг

наверх, сразу после if (flag), то станет намного лучше. Но все равно с массивом надежнее.