idono 0 20 декабря, 2007 Опубликовано 20 декабря, 2007 (изменено) · Жалоба Разбираюсь с АТмегой16, на этот раз экспериментирую с подмешиванием данных (хотя бы палочек-полосок) в композитный видео-сигнал. Перерыл все возможные гуглы, нашел несколько реализаций, нашел описание передачи видео-сигнала. Читал, смотрел, опять читал. С железом более-менее разобрался, принцип работы понятен. А вот с софтом не очень, не хватает познаний. Так что все рассуждения основываются на познаниях из мануалов "для чайников" :rolleyes: Итак, купил LM1881, кварц 16 мгц, конденсаторы, резисторы, пару диодов. Подключил все это к меге16 - сигнал горизонтальной синхронизации на вход внешних прерываний INT0, вертикальной на INT1. Получается что прерывание по INT0 возникает в начале прорисовки каждой строки, а INT1 в начале каждого кадра (или полукадра, т.к. рисуется через строчку). Ногу PD7 сделал выходом и подключил обратно к центральной жиле видеокабеля через диод и сопротивление. Сделал счетчик строк, который прибавляет 1 при каждом INT0 и обнуляет при INT1. Посидел, подумал, написал... в прерывании INT0 ждем нужной строки, после чего ждем определенное время, включаем "питание" на PD7, опять ждем, выключаем питание. Получается горизонтальная линия определенной длины и с определенным смещением от левого края. Нарисуем крест. В обработчике INT0 условие - если строка не та, которая нам нужна, то ждем, включаем PD7 и сразу же выключаем (чтобы получить штрих с наименьшим размахом). Т.к. задержка в каждой строке одинаковая, должна получится ровная вертикальная линия. Но не получается : Линия гуляет как бы лесенкой на всем протяжении. По логике получается, что временной интервал от начала прерывания до начала включения 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: Изменено 20 декабря, 2007 пользователем idono Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
PSP 0 20 декабря, 2007 Опубликовано 20 декабря, 2007 · Жалоба Прерывание не обрабатывается мгновенно, всегда происходит завершение выполнения текущей команды фоновой программы, а это обычно от одного до нескольких тактов, плюс время входа в прерывание. Почитайте раздел "Interrupt Response Time" из datasheet. При тактовой 16 МГц это даст переменную задержку не менее 62.5 нс, что составляет больше 1% от ширины экрана, т.к. видимая часть меньше полной длительности строки 64 мкс. Можно пытаться бороться с неопределенностью момента старта используя сигнал прерывания для выхода из состояния SLEEP, но остается проблема несинхронности тактового генератора процессора и синхроселектора. Или видел вариант применения строчного синхроимпульса в качестве сигнала "сброс" с одновременным разрешением работы тактового RC-генератора процессора. Это позволяет привязать генератор к фазе синхросигнала. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Serj78 0 20 декабря, 2007 Опубликовано 20 декабря, 2007 · Жалоба на 16 мгц совершенно достаточно засыпать потом просыпаться , на глаз не заметно. дополнительной синхронизации не требуется. у меня строчки от начала кадрового импульса (титры выводятся внизу экрана) считаются таймером, во время свободной части кадра проц занят полезным делом :) также поищите поиском avr-vga там есть проект текстового вывода на экран. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
fmdost 0 20 декабря, 2007 Опубликовано 20 декабря, 2007 (изменено) · Жалоба на 16 мгц совершенно достаточно засыпать потом просыпаться , на глаз не заметно. дополнительной синхронизации не требуется. у меня строчки от начала кадрового импульса (титры выводятся внизу экрана) считаются таймером, во время свободной части кадра проц занят полезным делом :) А если засинхронизировать таймер счётчик по синхроимпульсам строк, то можно попробовать использовать выходы compare для аппаратного рисования. Напримет compare1 есть вкл compare2 есть выкл, и сразу в них новые значения. ИМХО не пробовал, просто подумалось. Изменено 20 декабря, 2007 пользователем Т.Достоевский Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Гость =VRA= 20 декабря, 2007 Опубликовано 20 декабря, 2007 · Жалоба Все это на раз делается даже без LM1881 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Прерывание не обрабатывается мгновенно, всегда происходит завершение выполнения текущей команды фоновой программы, а это обычно от одного до нескольких тактов, плюс время входа в прерывание. Почитайте раздел "Interrupt Response Time" из datasheet. При тактовой 16 МГц это даст переменную задержку не менее 62.5 нс, что составляет больше 1% от ширины экрана, т.к. видимая часть меньше полной длительности строки 64 мкс. Нет, чуть больше 0.1% - ведь разница три порядка, а не два. А 0.1% не очень заметны. Но по-хорошему, надо бы принимать дополнительные меры к синхронизации. Можно пытаться бороться с неопределенностью момента старта используя сигнал прерывания для выхода из состояния SLEEP, но остается проблема несинхронности тактового генератора процессора и синхроселектора. Или видел вариант применения строчного синхроимпульса в качестве сигнала "сброс" с одновременным разрешением работы тактового RC-генератора процессора. Это позволяет привязать генератор к фазе синхросигнала. Для RC-генератора выход из sleep даст необходимую синхронизацию. Альтернатива - PLL для тактового генератора. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
idono 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Вставил бесконечный цикл с 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 Имея массив с текстом для вывода и счетчик текущей строки, выводим чисто построчно, перемигиваясь в каждой строке нулями и единицами. Ну и конечно надо учитывать, что изображение строится через строку. // пошел пробовать :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
alux 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Смотри на avrfreaks в последних проектах "AVGA demo". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
idono 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Смотри на 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() получается еще дольше, чем я в ряд записал, видимо процессор тратит много тактов на такие операции Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
GDI 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба видимо процессор тратит много тактов на такие операции А вы посмотрите что у вас в асм листиге получилось, там же, небось еще и выборка из массива каждый раз делается, как вариант №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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
idono 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Чтение байта из массива перед выводом помогло, ширина символа сократилась примерно в полтора раза. Опимизация компилятора по скорости уменьшила символ еще на столько же! Спасибо за советы, GDI Теперь символ 8х12 выглядит вполне хорошо. Теперь попробую привязать готовый массив с шрифтом, дабы букву можно было оттуда выбирать и делать вывод Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
idono 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 (изменено) · Жалоба нарисовал из нулей массив цифр, написал функцию для прерывания. Старался делать все без массивов и циклов - при срабатывании прерывания, перед выводом делаю все вычисления, байты сравниваю и тп, потом в ряд идет вывод. Да условия перед этим выводом такой сложности, что вывод поспевает уже к концу отрисовки строки, только только 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: Изменено 21 декабря, 2007 пользователем idono Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
maik-vs 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба ИМХО это большой героизм советского толка писать такое на С. Лучше на ассемблере - там эмулятор точно считает время, можно всё учесть и пользоваться всякими хитростями. Сам интересуюсь подобной штукой, поэтому интересно, что у Вас получится. Видел проект, где пикселы строки заносятся во все 32 регистра, а потом тупо сдвигаются. Для сдвига, подумалось мне, можно использовать один из последовательных интерфейсов, кажется TWI может подойти. Или использовать внешний регистр сдвига как external RAM. Насчёт дрожания. PLL к строчной синхронизации конечно желателен, но у нас есть разработка с пиксельной частотой 20 МГц, буквы не дрожат. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rezident 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Чтобы уменьшить джиттер и величину запаздываний вывода символов, вывод нужно разместить как можно ближе к точке входа в прерывание. Выводите сначала заранее подготовленный буфер и только потом уже делайте проверки и готовьте для вывода следующий буфер. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
rx3apf 0 21 декабря, 2007 Опубликовано 21 декабря, 2007 · Жалоба Для сдвига, подумалось мне, можно использовать один из последовательных интерфейсов, кажется TWI может подойти. Или использовать внешний регистр сдвига как external RAM. TWI - не лучший вариант. SPI - тоже. А вот в новых мегах UART умеет работать как SPI, причем с буферизацией - это, наверное, как раз будет то, что нужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться