Считыватель таймкода 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();
}
}
@user3316519, 👍4
Обсуждение3 ответа
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
Я нахожусь в процессе реализации точно такой же вещи. Самый простой способ обойти эту проблему-просто избавиться от переменных one_time_max и one_time_min и просто реализовать значение средней точки, проверяя bit_time на соответствие этому значению для единицы и нуля. Просто проверяя значение средней точки, он позволяет подпрограмме настраивать себя на целочисленные / нецелочисленные значения таймкода от 24fps до 30fps.
Я реализую аналогичный проект, но мой считыватель таймкода встроен в саму хлопушку.
https://improduction.ru/tags/imslate-1/
Дело в том, что длина бита зависит от частоты кадров в секунду. Я просто "прослушал" сигнал с моего рекордера Zoom F8, а затем запустил эталонные данные. Получая данные с кабеля, я просто сравниваю их и определяю частоту. Вот и все.
https://improduction.ru/all/elektronnaya-hlopushka-imslate-1-pro-chtenie-taymkoda/
Я вижу, что у вас есть похожий проект. У вас хорошие образы. Однако я понятия не имею, как это пытается решить этот вопрос., @timemage
Я написал, как в общих чертах решил эту проблему, и показал, как выглядит мой дисплей. Я определяю частоты и DF / NF в режиме реального времени. Но я не буду показывать код, @EndyVelvet
- Печать string and integer LCD
- Отправка значения с одного Arduino на другой
- ЖК-дисплей I2C отображает странные символы
- Экран LCD 16*02 I2C показывает только первый напечатанный символ
- Запрограммируйте Arduino Uno как цифровые часы
- ЖК-дисплей странные символы
- 16*2 1602A LCD дисплей не отображает никаких символов или контрастности, несмотря на правильное подключение
- Ошибка: "'lcd' does not name a type" при использовании библиотеки LiquidCrystal.
Итак, если бы пин-код был высоким или низким, какие переменные вы бы изменили? И на что бы вы их поменяли?, @hichris123
Привет, значения, которые я хотел бы изменить, - это one_time_min и т. Д. И я хочу изменить их на значения, необходимые для декодирования NTSC, а не PAL, @user3316519