Считыватель таймкода SMPTE с ЖК-дисплеем, вопрос кодирования

Мне удалось адаптировать некоторый код с форумов Arduino для отображения тайм-кода на ЖК-дисплее.

В чем я хотел бы получить дополнительную помощь, так это в том, есть ли способ определить one_time_max и т. Д. По-разному в зависимости от высокого низкого состояния контакта? Таким образом, я мог бы использовать переключатель для изменения времени, чтобы он мог переключаться между NTSC и PAL.

Во-вторых, не будет ли кто-нибудь любезен объяснить, что происходит в средней части этого кода? Я сделал несколько заметок, но любая помощь была бы очень признательна.

Вот картина того, как это происходит до сих пор. Я буду держать вас в курсе событий по мере продолжения проекта :)

// Code from forum post Dec 12, 2007

// включить код библиотеки:
#include <LiquidCrystal.h>

// инициализировать библиотеку номерами контактов интерфейса
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define one_time_max 600 // эти значения настроены для видео PA
#define one_time_min 400
// Это сочетание единицы и нуля с небольшим количеством места для ошибок.
#define zero_time_max 1050 //
#define zero_time_min 950 //


#define icpPin 8 // ICP входной контакт на arduino
//#define one_time_max 475 // эти значения настроены для NTSC-видео
//#define one_time_min 300 // PAL будет около 1000 для 0 и 500 для 1
//#define zero_time_max 875 // 80 бит умножить на 29,97 кадра в секунду
//#define zero_time_min 700 // равно 833 (разделить на 8 тактовых импульсов)

#define end_data_position 63
#define end_sync_position 77
#define end_smpte_position 80

volatile unsigned int pin = 13;
volatile unsigned int bit_time;
// volatile инструктирует переменную для хранения в оперативной
volatile boolean valid_tc_word;
// boolean может иметь одно из двух значений true или false, но по умолчанию false
volatile boolean ones_bit_count;
// boolean может иметь одно из двух значений true или false, но по умолчанию false
volatile boolean tc_sync;
// boolean может иметь одно из двух значений true или false, но по умолчанию false
volatile boolean write_tc_out;
// boolean может иметь одно из двух значений true или false, но по умолчанию false
volatile boolean drop_frame_flag;
// boolean может иметь одно из двух значений true или false, но по умолчанию false
volatile byte total_bits; //здесь хранится 8-битное беззнаковое число
volatile byte current_bit; //здесь хранится 8-битное беззнаковое число
volatile byte sync_count; //здесь хранится 8-битное беззнаковое число

volatile byte tc[8]; //здесь хранится 8-битное беззнаковое число
volatile char timeCode[11]; //здесь хранится 8-битное беззнаковое число

/* ICR interrupt vector */
ISR(TIMER1_CAPT_vect) {
  //ISR=Подпрограмма обслуживания прерываний и событие захвата timer1
  //toggleCaptureEdge
  TCCR1B ^= _BV(ICES1);
  //переключает ребро, которое запускает обработчик, так что измеряется длительность как высоких, так и низких импульсов.
  bit_time = ICR1; //это значение генерирует таймер
  //resetTimer1
  TCNT1 = 0;
  // Игнорировать изменения фазы < время для 1 бита или > время для нулевого бита (время изменения фазы нулевого бита вдвое больше, чем 1 бит)
  if ((bit_time < one_time_min) || (bit_time > zero_time_max)) {
    // это избавляет от всего, что не то, что мы ищем
    total_bits = 0;
  } else {
    // Если бит, который мы читаем, равен 1, то проигнорируйте первое изменение фазы
    if (ones_bit_count == true)
      // считайте только вторые импульсы
      ones_bit_count = false;
    else {
      // Мы уже проверили внешнее время для 1 и нулевых битов, поэтому не видим, равно ли внутреннее время > нулю мин
      if (bit_time > zero_time_min) {
        // У нас есть нулевой бит
        current_bit = 0;
        sync_count = 0; // Не 1 бит, поэтому не может быть частью 12-битной синхронизации.
      } else {
        // Это должен быть 1 бит, тогда
        ones_bit_count = true; // Флаг, чтобы мы не читали следующий край 1 бита
        current_bit = 1;
        sync_count++; // Увеличить количество битов синхронизации
        if (sync_count == 12) {
          // часть последних двух байтов слова таймкода
          // У нас есть 12 1 в строке, которые могут быть только частью синхронизации
          sync_count = 0;
          tc_sync = true;
          total_bits = end_sync_position;
        }
      }

      if (total_bits <= end_data_position) {
        // timecode работает меньше всего, поэтому нам нужно
        // чтобы изменить ситуацию
        tc[0] = tc[0] >> 1;
        for(int n=1;n<8;n++) {
          //создает тс[1-8]
          if(tc[n] & 1) tc[n-1] |= 0x80;
          tc[n] = tc[n] >> 1;
        }
        if(current_bit == 1) tc[7] |= 0x80;
      }
      total_bits++;
    }
    if (total_bits == end_smpte_position) {
      // у нас есть 80-й бит
      total_bits = 0;
      if (tc_sync) {
        tc_sync = false;
        valid_tc_word = true;
      }
    }
    if (total_bits <= end_data_position) {
      // timecode работает меньше всего, поэтому нам нужно
      // чтобы изменить ситуацию
      tc[0] = tc[0] >> 1;
      for(int n=1;n<8;n++) {
        //создает тс[1-8]
        if(tc[n] & 1) tc[n-1] |= 0x80;
        tc[n] = tc[n] >> 1;
      }
      if(current_bit == 1) tc[7] |= 0x80;
    }
    total_bits++;
  }

  if (total_bits == end_smpte_position) {
    // у нас есть 80-й бит
    total_bits = 0;
    if (tc_sync) {
      tc_sync = false;
      valid_tc_word = true;
    }
  }

  if (valid_tc_word) {
    valid_tc_word = false;
    timeCode[10] = (tc[0]&0x0F)+0x30;
    // фреймы это преобразование из двоичного в десятичное дает нам последнюю цифру
    timeCode[9] = (tc[1]&0x03)+0x30;
    // 10 кадров это преобразование из двоичного в десятичное дает нам первую цифру
    timeCode[8] =  ':';
    timeCode[7] = (tc[2]&0x0F)+0x30; // секунды
    timeCode[6] = (tc[3]&0x07)+0x30; // 10 секунд
    timeCode[5] =  ':';
    timeCode[4] = (tc[4]&0x0F)+0x30; // минуты
    timeCode[3] = (tc[5]&0x07)+0x30; // 10 минут
    timeCode[2] = ':';
    timeCode[1] = (tc[6]&0x0F)+0x30; // часы
    timeCode[0] = (tc[7]&0x03)+0x30; // 10 часов
    drop_frame_flag = bit_is_set(tc[1], 2);
    //определяет, есть ли бит отбрасываемого кадра.
    write_tc_out = true;
  }
}

void setup() {
  lcd.begin (16, 2);
  pinMode(icpPin, INPUT); // ICP pin (digital pin 8 on arduino) as input
  bit_time = 0;
  valid_tc_word = false;
  ones_bit_count = false;
  tc_sync = false;
  write_tc_out = false;
  drop_frame_flag = false;
  total_bits =  0;
  current_bit =  0;
  sync_count =  0;
  lcd.print("FINISHED SETUP");
  delay (1000);
  TCCR1A = B00000000; // очистить все
  TCCR1B = B11000010; // ICNC1 шумоподавление + ICES1 старт на восходящем крае + CS11 деление на 8
  TCCR1C = B00000000; // очистить все
  TIMSK1 = B00100000; // ICIE1 включить icp
  TCNT1 = 0; // очистить таймер1
}

void loop() {
  if (write_tc_out) {
    write_tc_out = false;
    if (drop_frame_flag)
      lcd.print("TC-[df] ");
    else
      lcd.print("TC-NO DROP FRAME");
    lcd.setCursor(0, 1);
    lcd.print((char*)timeCode);
    lcd.print("\r");
    lcd.setCursor(11, 1);
    lcd.print("......");
    delay (30);
    lcd.clear();
  }
}

, 👍4

Обсуждение

Итак, если бы пин-код был высоким или низким, какие переменные вы бы изменили? И на что бы вы их поменяли?, @hichris123

Привет, значения, которые я хотел бы изменить, - это one_time_min и т. Д. И я хочу изменить их на значения, необходимые для декодирования NTSC, а не PAL, @user3316519


3 ответа


1

one_time_max, one_time_min, zero_time_max и zero_time_min-все это определения в верхней части файла. Это позволит вставить их как постоянные значения во время компиляции. Это делает изменение кода для работы с ними немного неудобным, но не невозможным.

Как ни странно, значение one_time_max не используется, но остальные используются. Чтобы вы могли переключаться между PAL и NTSC, я бы сделал следующее (это немного лениво, но быстро):

  • измените one_time_max на ONE_TIME_MAX_NTSC и ONE_TIME_MAX_PAL, чтобы у вас было две определенные константы для каждой части. Caps-это соглашение для определений.
  • создайте новую глобальную переменную, логическое значение, которое является флагом, определяющим, следует ли декодировать PAL или NTSC.
  • каждый раз, когда используется одна из констант, используйте условный оператор, основанный на глобальной переменной, чтобы решить, какие константы используются.
  • используйте ввод с помощью кнопки, чтобы определить значение глобальной переменной.
,

Спасибо! я попробую это сделать и посмотрю, что произойдет. Я не думаю, что вы могли бы объяснить, как добавление 0x0F + 0x30 преобразует его из предположительно шестнадцатеричного значения в десятичное?, @user3316519

&ing кажется излишним в этой ситуации. 0x30 - это ASCII для 0, поэтому, если вы представляете 0 как 0x00 и 9 как 0x9, то добавление 0x30 преобразуется в ASCII., @Cybergibbons


1

Я нахожусь в процессе реализации точно такой же вещи. Самый простой способ обойти эту проблему-просто избавиться от переменных one_time_max и one_time_min и просто реализовать значение средней точки, проверяя bit_time на соответствие этому значению для единицы и нуля. Просто проверяя значение средней точки, он позволяет подпрограмме настраивать себя на целочисленные / нецелочисленные значения таймкода от 24fps до 30fps.

,

0

Я реализую аналогичный проект, но мой считыватель таймкода встроен в саму хлопушку.

https://improduction.ru/tags/imslate-1/

Дело в том, что длина бита зависит от частоты кадров в секунду. Я просто "прослушал" сигнал с моего рекордера Zoom F8, а затем запустил эталонные данные. Получая данные с кабеля, я просто сравниваю их и определяю частоту. Вот и все.

https://improduction.ru/all/elektronnaya-hlopushka-imslate-1-pro-chtenie-taymkoda/

,

Я вижу, что у вас есть похожий проект. У вас хорошие образы. Однако я понятия не имею, как это пытается решить этот вопрос., @timemage

Я написал, как в общих чертах решил эту проблему, и показал, как выглядит мой дисплей. Я определяю частоты и DF / NF в режиме реального времени. Но я не буду показывать код, @EndyVelvet