Перейти к содержанию
    

Подмешивание в видеосигнал

Разбираюсь с АТмегой16, на этот раз экспериментирую с подмешиванием данных (хотя бы палочек-полосок) в композитный видео-сигнал.

 

Перерыл все возможные гуглы, нашел несколько реализаций, нашел описание передачи видео-сигнала. Читал, смотрел, опять читал. С железом более-менее разобрался, принцип работы понятен.

А вот с софтом не очень, не хватает познаний. Так что все рассуждения основываются на познаниях из мануалов "для чайников" :rolleyes:

 

Итак, купил LM1881, кварц 16 мгц, конденсаторы, резисторы, пару диодов. Подключил все это к меге16 - сигнал горизонтальной синхронизации на вход внешних прерываний INT0, вертикальной на INT1. Получается что прерывание по INT0 возникает в начале прорисовки каждой строки, а INT1 в начале каждого кадра (или полукадра, т.к. рисуется через строчку). Ногу PD7 сделал выходом и подключил обратно к центральной жиле видеокабеля через диод и сопротивление.

 

Сделал счетчик строк, который прибавляет 1 при каждом INT0 и обнуляет при INT1.

Посидел, подумал, написал... в прерывании INT0 ждем нужной строки, после чего ждем определенное время, включаем "питание" на PD7, опять ждем, выключаем питание. Получается горизонтальная линия определенной длины и с определенным смещением от левого края.

 

Нарисуем крест. В обработчике INT0 условие - если строка не та, которая нам нужна, то ждем, включаем PD7 и сразу же выключаем (чтобы получить штрих с наименьшим размахом). Т.к. задержка в каждой строке одинаковая, должна получится ровная вертикальная линия. Но не получается : :wacko: Линия гуляет как бы лесенкой на всем протяжении. По логике получается, что временной интервал от начала прерывания до начала включения PD7 при одинаковых значениях получается разным :(

Фото стоп-кадра экрана прикрепил.

 

Вот кусок кода с обработкой прерываний:

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
          if (vert==150) {       
                i=10;
                while(i--) { #asm("nop"); }
                PORTD.7=1;
                i=30;
                while(i--) { #asm("nop"); }
                PORTD.7=0;  
          } 
          else {
               i=20;
                while(i--) { #asm("nop"); }
                PORTD.7=1;
                PORTD.7=0;  
          }  

        vert++; 
}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here  
        vert=0;  
}

 

 

 

Обучите, пожалуйста, чайника основным принципам правильного построения изображения на этой железной связке (LM1881 + atmega16). Для начала хотя бы прямых линий :rolleyes:

post-33459-1198179833_thumb.jpg

Изменено пользователем idono

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прерывание не обрабатывается мгновенно, всегда происходит завершение выполнения текущей команды фоновой программы, а это обычно от одного до нескольких тактов, плюс время входа в прерывание. Почитайте раздел "Interrupt Response Time" из datasheet. При тактовой 16 МГц это даст переменную задержку не менее 62.5 нс, что составляет больше 1% от ширины экрана, т.к. видимая часть меньше полной длительности строки 64 мкс.

Можно пытаться бороться с неопределенностью момента старта используя сигнал прерывания для выхода из состояния SLEEP, но остается проблема несинхронности тактового генератора процессора и синхроселектора. Или видел вариант применения строчного синхроимпульса в качестве сигнала "сброс" с одновременным разрешением работы тактового RC-генератора процессора. Это позволяет привязать генератор к фазе синхросигнала.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

на 16 мгц совершенно достаточно засыпать потом просыпаться , на глаз не заметно. дополнительной синхронизации не требуется. у меня строчки от начала кадрового импульса (титры выводятся внизу экрана) считаются таймером, во время свободной части кадра проц занят полезным делом :)

 

также поищите поиском avr-vga там есть проект текстового вывода на экран.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

на 16 мгц совершенно достаточно засыпать потом просыпаться , на глаз не заметно. дополнительной синхронизации не требуется. у меня строчки от начала кадрового импульса (титры выводятся внизу экрана) считаются таймером, во время свободной части кадра проц занят полезным делом :)

А если засинхронизировать таймер счётчик по синхроимпульсам строк, то можно попробовать использовать выходы compare для аппаратного рисования. Напримет compare1 есть вкл compare2 есть выкл, и сразу в них новые значения. ИМХО не пробовал, просто подумалось.

Изменено пользователем Т.Достоевский

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Прерывание не обрабатывается мгновенно, всегда происходит завершение выполнения текущей команды фоновой программы, а это обычно от одного до нескольких тактов, плюс время входа в прерывание. Почитайте раздел "Interrupt Response Time" из datasheet. При тактовой 16 МГц это даст переменную задержку не менее 62.5 нс, что составляет больше 1% от ширины экрана, т.к. видимая часть меньше полной длительности строки 64 мкс.

 

Нет, чуть больше 0.1% - ведь разница три порядка, а не два. А 0.1% не очень заметны. Но по-хорошему, надо бы принимать дополнительные меры к синхронизации.

 

Можно пытаться бороться с неопределенностью момента старта используя сигнал прерывания для выхода из состояния SLEEP, но остается проблема несинхронности тактового генератора процессора и синхроселектора. Или видел вариант применения строчного синхроимпульса в качестве сигнала "сброс" с одновременным разрешением работы тактового RC-генератора процессора. Это позволяет привязать генератор к фазе синхросигнала.

Для RC-генератора выход из sleep даст необходимую синхронизацию. Альтернатива - PLL для тактового генератора.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вставил бесконечный цикл с idle(); в главной функции - расстояния между изломами вертикальной линии стани больше. Померял линейкой ширину "перелома" (эксперементирую на проекторе, поэтому померять вполне реально) - получилось действительно около 0.2% погрешности.

 

Выходит, что мои волнения были напрасны, для небольшого экрана такая погрешность будет вполне допустима :beer:

 

Следующим шагом попробую выводить осмысленные буковки.

Кстати, те проекты avr-vga и телеметрию для воздушного шара я нашел и изучил в первую очередь. По коду почти ничего не понял :(

 

Буду постить свои попытки изучить систему в этой теме, если вы не против :rolleyes: Может быть еще кому-то из начинающих она поможет в будущем.

 

Если я правильно понял принцип построения текста на экране - есть некий двухмерный массив с битами 0 1, как бы матрица для построения цифры по слоям, в котором цифра, например, 1 выглядит так (беру цифру с разрешением 8х12, как в проекте avr-vga):

 

11111111

11111111

10000111

11100111

11100111

11100111

11100111

11100111

11100111

11100111

11111111

11111111

 

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

 

// пошел пробовать :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Смотри на avrfreaks в последних проектах "AVGA demo".

там что-то совсем жестко, я и элементарные проекты разобрать не могу :(

 

написал сам как смог прорисовку одного символа:

 

int start_line=100;    // начинаем вывод с линии номер 100
int cur_line=0;         // переменная для отсчета линий с момента начала прорисовки символа 

// сам тестовый символ (нарисован 0 единичками)
char symbol[8]={0b01111111,
                0b01000001,
                0b01000001,
                0b01000001,
                0b01000001,
                0b01000001,
                0b01000001,
                0b01111111};

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{
         //если находимся в области прорисовки:
          if (vert>start_line && vert<start_line+9)  {
                i=30;
                while(i--) { #asm("nop"); }    // задержка перед выводом (для отступа слева)
  
                PORTD.7=symbol[cur_line] & (1 << 0); // присваиваем порту вывода 1, если в нулевом бите symbol единица (т.е. включаем белый пиксел)
                PORTD.7=symbol[cur_line] & (1 << 1); // тоже, но проверяем первый бит
                PORTD.7=symbol[cur_line] & (1 << 2); // второй бит
                PORTD.7=symbol[cur_line] & (1 << 3); // и так далее
                PORTD.7=symbol[cur_line] & (1 << 4);
                PORTD.7=symbol[cur_line] & (1 << 5);
                PORTD.7=symbol[cur_line] & (1 << 6);
                PORTD.7=symbol[cur_line] & (1 << 7);                 
                           
                cur_line++; // переходим на опрос слдующего элемента массива
                
          } else {               
                cur_line=0; // сбрасываем позицию на ноль, если находимся вне области прорисовки
          }

        vert++; // увеличиваем счетчик горизонтальных линий
}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
        vert=0;  //сбрасываем счетчик линий при начале прорисовки нового кадра
}

 

В результате получается такой широченный символ :)

Как с максимальной скоростью вывести на PORTD.7 сразу все восемь бит переменной? То есть если в переменной "0b01011100", то на порт PD7 быстренько по-очереди выведутся значения 0 1 0 1 1 1 0 0

Циклом for() получается еще дольше, чем я в ряд записал, видимо процессор тратит много тактов на такие операции

post-33459-1198227264_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

видимо процессор тратит много тактов на такие операции

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

как вариант №1 осмелюсь предложить перед операцией собственно вывода сделать чтение нужного байта из массива в какую то переменную, а потом сдвигать виты только из этой переменной, примерно так:

       
                tmp =  symbol[cur_line]        
                PORTD.7=tmp & (1 << 0); // присваиваем порту вывода 1, если в нулевом бите symbol единица (т.е. включаем белый пиксел)
                PORTD.7=tmp & (1 << 1); // тоже, но проверяем первый бит
                PORTD.7=tmp & (1 << 2); // второй бит
                PORTD.7=tmp & (1 << 3); // и так далее
                PORTD.7=tmp & (1 << 4);
                PORTD.7=tmp & (1 << 5);
                PORTD.7=tmp & (1 << 6);
                PORTD.7=tmp & (1 << 7);

возможно того же эффекта можно добиться используя оптимизацию по скорости компилятора.

 

вариант №2, сделать аппаратный вывод битов, например через внешний регистр с параллельной загрузкой и последосательным выходом, можно попробовать для этой цели приспособить аппаратный SPI, который есть на МК. А еще, если есть свободный порт, например то же PORTD, то можно сделать так:

PORTD = symbol[cur_line];
PORTD >>= 1; //и так 8 раз

при этом все линии порта кроме младшей должны быть свободны а на выводе PORTD.0 мы получим последовательность битов, начиная от младшего, либо можно получить биты, начиная со старшего , если применить сдвиг в другую сторону, тогда последовательность будет выходить с PORTD.7

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Чтение байта из массива перед выводом помогло, ширина символа сократилась примерно в полтора раза. Опимизация компилятора по скорости уменьшила символ еще на столько же! Спасибо за советы, GDI

 

Теперь символ 8х12 выглядит вполне хорошо. Теперь попробую привязать готовый массив с шрифтом, дабы букву можно было оттуда выбирать и делать вывод

post-33459-1198230900_thumb.jpg

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

нарисовал из нулей массив цифр, написал функцию для прерывания. Старался делать все без массивов и циклов - при срабатывании прерывания, перед выводом делаю все вычисления, байты сравниваю и тп, потом в ряд идет вывод. Да условия перед этим выводом такой сложности, что вывод поспевает уже к концу отрисовки строки, только только 3 цифры успеваю вывести.

 

Для теста я пробовал вывести на одну из строк показания с датчика температуры - впритык к максимальной загрузке меги, но получилось (результат прикрепил).

 

Вот код (только основная проблемная часть)

 

char symbol[13][12]={
                {
                0b00011000, // 0
                0b00100100,
                0b01000010,
                0b01000010,
                0b01000010,
                0b01000010,
                0b01000010,
                0b01000010,
                0b01000010,
                0b01000010,
                0b00100100,
                0b00011000
                },
                ..... и так далее
}

// External Interrupt 0 service routine
interrupt [EXT_INT0] void ext_int0_isr(void)
{              
      //если текущая строка попадает в заданую область для 1го ряда цифр
        if (vert>start_line && vert<start_line+13)  { 
           drawing1=number1; //присваиваем переменным прорисовки цифр drawing значения каких-то параметров
           drawing2=number2; //в тесте параметров нет, поэтому значения просто присвоены выше
           drawing3=number3; 
           state1=1; // флаг нахождения в нужном диапазоне для обработчика ниже
        } else {
           state1=0;
        }  
        
        // случай со вторым рядом цифр
        if (vert>start_line2 && vert<start_line2+13)  { 
           drawing1=number5;
           drawing2=number6;
           drawing3=number7; 
           state2=1;
        } else {
           state2=0;
        }

         // случай с третьим рядом цифр
        if (vert>start_line3 && vert<start_line3+13)  { 
           drawing1=number9;
           drawing2=number10;
           drawing3=number11;
           state3=1;
        } else {
           state3=0;
        }

     
          //если строка попадает в область прорисовки одного из 3х значений, то запускаем обработчик
          if (state1 || state2 || state3)  {
               
                        //вычисляем значение (0 или 1) каждого пиксела для всех 3х цифр в данной строке (тип переменной tmp - bit)
                        tmp00 =  symbol[drawing1][cur_line]& (0b10000000 >> 0); 
                        tmp01 =  symbol[drawing1][cur_line]& (0b10000000 >> 1); 
                        tmp02 =  symbol[drawing1][cur_line]& (0b10000000 >> 2); 
                        tmp03 =  symbol[drawing1][cur_line]& (0b10000000 >> 3); 
                        tmp04 =  symbol[drawing1][cur_line]& (0b10000000 >> 4); 
                        tmp05 =  symbol[drawing1][cur_line]& (0b10000000 >> 5); 
                        tmp06 =  symbol[drawing1][cur_line]& (0b10000000 >> 6); 
                        tmp07 =  symbol[drawing1][cur_line]& (0b10000000 >> 7);  

                        tmp10 =  symbol[drawing2][cur_line]& (0b10000000 >> 0); 
                        tmp11 =  symbol[drawing2][cur_line]& (0b10000000 >> 1); 
                        tmp12 =  symbol[drawing2][cur_line]& (0b10000000 >> 2); 
                        tmp13 =  symbol[drawing2][cur_line]& (0b10000000 >> 3); 
                        tmp14 =  symbol[drawing2][cur_line]& (0b10000000 >> 4); 
                        tmp15 =  symbol[drawing2][cur_line]& (0b10000000 >> 5); 
                        tmp16 =  symbol[drawing2][cur_line]& (0b10000000 >> 6); 
                        tmp17 =  symbol[drawing2][cur_line]& (0b10000000 >> 7);  
  
                        tmp20 =  symbol[drawing3][cur_line]& (0b10000000 >> 0); 
                        tmp21 =  symbol[drawing3][cur_line]& (0b10000000 >> 1); 
                        tmp22 =  symbol[drawing3][cur_line]& (0b10000000 >> 2); 
                        tmp23 =  symbol[drawing3][cur_line]& (0b10000000 >> 3); 
                        tmp24 =  symbol[drawing3][cur_line]& (0b10000000 >> 4); 
                        tmp25 =  symbol[drawing3][cur_line]& (0b10000000 >> 5); 
                        tmp26 =  symbol[drawing3][cur_line]& (0b10000000 >> 6); 
                        tmp27 =  symbol[drawing3][cur_line]& (0b10000000 >> 7);    
                        
                        // и быстро выводим получившиеся значения с небольшой задержкой (промежутки между цифрами)
                        PORTD.7=tmp00;
                        PORTD.7=tmp01;
                        PORTD.7=tmp02;
                        PORTD.7=tmp03;
                        PORTD.7=tmp04;
                        PORTD.7=tmp05;
                        PORTD.7=tmp06;
                        PORTD.7=tmp07; 
                        
                        delay_us(5);   
  
                        PORTD.7=tmp10;
                        PORTD.7=tmp11;
                        PORTD.7=tmp12;
                        PORTD.7=tmp13;
                        PORTD.7=tmp14;
                        PORTD.7=tmp15;
                        PORTD.7=tmp16;
                        PORTD.7=tmp17;
                        
                        delay_us(5);
                        
                        PORTD.7=tmp20;
                        PORTD.7=tmp21;
                        PORTD.7=tmp22;
                        PORTD.7=tmp23;
                        PORTD.7=tmp24;
                        PORTD.7=tmp25;
                        PORTD.7=tmp26;
                        PORTD.7=tmp27; 
                                          
                cur_line++;
                
          } else {
                cur_line=0;    
          } 
          

        vert++;  

}

// External Interrupt 1 service routine
interrupt [EXT_INT1] void ext_int1_isr(void)
{
// Place your code here  
        vert=0;  
}

 

Кажется все эти просчеты &, сдвиги битов и выборы из 3х-мерного массива в количестве 24 штуки сильно грузят процессор. Так, что 3й символ успевает выскочить уже почти в конце строки. Просчеты для 4го символа вставить конечно уже некуда :(

Само собой разумеется, что алгоритм всех этих просчетов неверный. Хочется побольше информации на изображение наложить.

 

Подскажите более правильный алгоритм побитового вывода байта из массива в порт, пожалуйста :crying:

post-33459-1198254729_thumb.jpg

Изменено пользователем idono

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ИМХО это большой героизм советского толка писать такое на С. Лучше на ассемблере - там эмулятор точно считает время, можно всё учесть и пользоваться всякими хитростями. Сам интересуюсь подобной штукой, поэтому интересно, что у Вас получится. Видел проект, где пикселы строки заносятся во все 32 регистра, а потом тупо сдвигаются. Для сдвига, подумалось мне, можно использовать один из последовательных интерфейсов, кажется TWI может подойти. Или использовать внешний регистр сдвига как external RAM. Насчёт дрожания. PLL к строчной синхронизации конечно желателен, но у нас есть разработка с пиксельной частотой 20 МГц, буквы не дрожат.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для сдвига, подумалось мне, можно использовать один из последовательных интерфейсов, кажется TWI может подойти. Или использовать внешний регистр сдвига как external RAM.

TWI - не лучший вариант. SPI - тоже. А вот в новых мегах UART умеет работать как SPI, причем с буферизацией - это, наверное, как раз будет то, что нужно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...