nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore: синий экран смерти!

Мы продолжаем идти по прямой линии: в каждой точке перегиб!

Чтобы убрать всевозможную рябь с оцифрованного изображения, надо отключить цветность в самой камере. Она управлялась небольшим "джойстиком" на проводе, как это работает, я описывал здесь, и делал "имитацию джойстика" на ПЛИС с помощью 5-битной ШИМ-модуляции и RC-цепочки :)

Сейчас такой "виртуальный джойстик" выведен на 5 кнопок отладочной платы ПЛИС. Увы, за те 8 секунд, что уходит на передачу на компьютер полноценного изображения, меню уже может исчезнуть, поэтому нужно быстро шевелиться!

Поэтому мне хотелось теперь вывести этот "виртуальный джойстик" вовсе на компьютер, чтобы при нажатии кнопок мышкой посылался символ по UART, один из 10: U,D,L,R,E для "вверх, вниз, влево, вправо, энтер" и ещё u,d,l,r,e - то же самое, но ДОЛГОЕ (5 секунд) нажатие, чтобы переключиться в один из режимов, CVBS, AHD, TVI или CVI.

JoystickControl.png

Задача не казалась такой уж сложной, но возникла неожиданная проблема - как только я пытался воспользоваться этим "джойстиком", штуковина начала НАГЛУХО ВИСНУТЬ на получении изображения! Бодался с этим делом полдня, пока не сообразил, в чём же дело...


Для начала я хотел попробовать просто нажать кнопку на отладочной плате, чтобы запустить меню "старым способом" - и попробовать получить изображение. Прошиваю ПЛИС, получаю изображение до нажатия кнопок - всё в порядке. Нажимаю "Enter" (центральную кнопку из 5), снова запрашиваю изображение - и ничего не происходит. Программа на компьютере мучительно ждёт картинку, 10 секунд ждёт, 20 секунд - нифига!

Чтобы понять, в чём тут дело - вывел на светодиодики значение PC (Program Counter), чтобы хоть знать, на какой строке кода мы застряли? Как оказалось, застряли мы здесь:

        @@UsefulLoop: ACQ WholeRow
                      ACQ HSync
                      NOP GPUH
                      SUB 1


на строке SUB 1, но зная, что PC "бежит впереди паровоза", выходит что по SrcAddr выполнялась команда GPUH (Graphic Processing Unit - Horizontal/High byte - получить X-координату самого яркого пикселя на текущем отрезке, для команды ACQ, или старшее слово суммы всех пикселей этого отрезка, если команда была TRK), а по DestAddr выполнялась команда ACQ (ACQuire - дать задание на "обнаружение" ярких точек).

Подумал: может возвратился плохой контакт на плате, когда я начал её шевелить, жать кнопку, придерживая плату другой рукой? Опять расчехлил осциллограф, тыкаю злополучный контакт - там 3,3 вольта, то бишь АЦП отключена! Тыкаю соответствующий контакт на плате ПЛИС - и там тоже 3,3 вольта, т.е в этот раз это всё-таки "логика" виновата!

Следующая мысль: кнопки у меня ещё за что-то отвечают, не только за джойстик. Тот самый Enter мне когда-то заменял Reset для QuatCore, потом я его, правда, убрал. Покопался как следует, поискал "именованные провода" (если проводу даёшь имя - он соединяется со всеми другими проводами того же имени, даже если на схеме этого не обозначено). Нифига: как будто всё в порядке!

Решил хоть какие-то "неизвестные" убрать - и уже запустить этот "джойстик" с компьютера, чтобы плату лишний раз не трогать. Тогда, если нажатие мышкой кнопки "получить изображение" будет давать изображение, а по кнопке "Enter" приводить к зависанию - мы хотя бы сможем исключить шевеление чего-то на плате. Заодно и "не туда присоединённые кнопки" исключим!

Когда-то я уже написал модуль, который получает байтик из приёмника UART и управляет 5-битным ШИМ для вывода сигнала вместо "джойстика" осталось только пыль сдуть:

//we receive symbol by UART:
//"L" = Left
//"R" = Right
//"U" = Up
//"D" = Down
//"E" = Enter
//we should convert it to 5-bit number for PWM,
//and put it for long enough duration so camera react properly
//after some experimentation, we found out: 
//63 ms is enough!
//32 ms is too fast: it doesn't work correctly

//also:
//"l" = left for 5 sec = CVBS
//"r" = right for 5 sec = TVI
//"u" = up for 5 sec = AHD
//"d" = down for 5 sec = CVI

module JoystickControlByUART ( input clk, input ce, input [7:0] D, input Byte_ce,
                                output qe, output reg [4:0] Q = 1'b0);

reg OneOfOurBytes = 1'b0;
reg OneOfOtherOurBytes = 1'b0; //for long presses
reg Byte_ce_z = 1'b0;

wire ena0, ena1;

        DelayLine DL ( .clk (clk),
                        .start (OneOfOurBytes & Byte_ce_z),
                        .ce (ce),
                        .sclr (1'b0),
                        .working (ena0),
                        .finished ());
        defparam
                DL.Duration = 32768;
                
        DelayLine longDL (.clk (clk),
                          .start (OneOfOtherOurBytes & Byte_ce_z),
                          .ce (ce),
                          .sclr (1'b0),
                          .working (ena1),
                          .finished ());
        defparam
                longDL.Duration = 4194304; // 2^22 to get 5 seconds! 
                
        assign qe = ena0 | ena1;

always @(posedge clk) begin
        OneOfOurBytes <= (D == "L") | (D == "R") | (D == "U") | (D == "D") | (D == "E");
        OneOfOtherOurBytes <= (D == "l") | (D == "r") | (D == "u") | (D == "d");
        Byte_ce_z <= Byte_ce;
        if (Byte_ce)
                Q <= ((D == "L") | (D == "l"))? 5'b10100 :
                        ((D == "R") | (D == "r"))? 5'b11011 :
                        ((D == "U") | (D == "u"))? 5'b11000 :
                        ((D == "D") | (D == "d"))? 5'b11111 :
                                                        5'b10000 ;
                                                        
end


endmodule


Что куда присоединять - более-менее понятно, и даже комментарии какие-то есть. Единственное, что не было указано - откуда нужно брать ce? Очевидно, он здесь использовался, чтобы хоть чуть-чуть уменьшить ширину делителей частоты внутри этого модуля. Но необходимые интервалы, по счастью, приведены, и можно догадаться, что присоединить ce нужно к выходу ceo модуля PWM (ШИМ) - там входная частота, идущая на ШИМ (поскольку его ce посажен на лог "1", это выходит ТАКТОВАЯ ЧАСТОТА, 25 МГц), делёная на 32.

В общем, как-то так:


Модуль, работавший от кнопок, не стал окончательно удалять - просто "отвёл в сторонку". Не стал "прогонять сигнал" через QuatCore - просто она параллельно с QuatCore получает любой символ, пришедший по UART, и реагирует на него.

При синтезе выскочили предупреждения, что кнопки SA2, SA4, SA5 и SA6 никакой логикой не управляют. SA2 - это как раз злополучный Enter, который я нажимал. SA3 по-прежнему ещё управляет подсветкой ЖК-экранчика, ну и пускай. Хорошо, одной версией меньше.

Отсинтезировал, прошил, попробовал получить картинку - получаю, как и прежде, никаких проблем. Потом "нажимаю на кнопку" - и ничего!

Снова расчехлил осциллограф, посмотреть, что вообще творится на той ножке, что включает-выключает АЦП? Да, она включается, но не успевает и половины изображения получить - как выключается:



Правда, учитывая что включается АЦП в произвольный момент времени, и мы начинаем с ожидания кадрового импульса, эта длительность мало что значит, и она от раза к разу меняется.

Чтобы понять, что происходит, решил добавить "отладочную строку" в программу, в то самое место, где зависание:

                        ;начинаются полезные строки
                        Acc UsefulRows
        @@UsefulLoop:   ACQ WholeRow
                        ACQ HSync
                        OUT '1'
                        NOP GPUH
                        SUB 1
                        JGE @@UsefulLoop


То есть, когда мы успешно отправили задание на обработку строки, мы будем выдавать "единичку" по UART. И тогда на терминале можно будет проследить, сколько же строк пришло до того, как всё это зависнет?

Откомпилировал, отсинтезировал, запускаю, открываю терминал, отправляю тупо пустую строку (к ней терминал добавляет перенос строки) - и начинает выдаваться прорва кода! Его так много (почти мегабайт), что терминал "рушится" - да, не может этот Termite с большими объёмами справиться почему-то. Ну да ладно, раз дело дошло до выдачи, значит зависания не было.

Снова запустил терминал, в этот раз ввёл E (Enter). И снова выскочила огромнейшая простыня! Ещё один Гейзенбаг: добавление отладочной строки приводит к исчезновению бага!

Ну хорошо, тогда можно попробовать получить картинку после "нажатия джойстика". Результат виден на "картинке для привлечения внимания" в начале поста. Да, меню появилось, но на том пункте меню, на котором стоит курсор, НАФИГ УБИТА СИНХРОНИЗАЦИЯ!

То что потом картинка смещена - не должно нас смущать, поскольку мы не корректируем указатель в памяти после каждой строки. Один сбой - и сдвинуто ВСЁ. в конце концов, наша задача с этим изображением - не сделать офигительное помехоустойчивое устройство видеозахвата, какой-нибудь видеорегистратор с записью на SD, а как раз-таки УЗНАТЬ, ЧТО ТАМ ТВОРИТСЯ, И УСТРАНИТЬ ВСЕ ОШИБКИ И ПОМЕХИ, ЕСЛИ ТАКОВЫЕ ЕСТЬ!

Итак, "курсор" убивает синхронизацию. Осталось вспомнить, а как этот курсор выглядит?

И тут меня в очередной раз выручило, что "все ходы записаны" в ЖЖ. Когда-то я снял видео, как работает "джойстик", и в нём "курсор" отчётливо виден:



Это сцуко СИНИЙ!

Да это же всё объясняет!

Именно насыщенный синий цвет способен сбить мне синхронизацию... Дело в том, что когда три компоненты цвета (красный-зелёный-синий) превращают в яркость, синему дают самый маленький вес, что-то типа 0,1. Поэтому постоянная составляющая на этой сплошной заливке синим очень мала, вблизи чёрного. И к ней примешивается цветовая поднесущая с ОФИГИТЕЛЬНОЙ АМПЛИТУДОЙ, поскольку синяя цветоразностная компонента, напротив, велика!

И таким образом, на нижних полуволнах цветовой поднесущей сигнал с лёгкостью уходит в область синхроимпульсов. И наш видеопроцессор "выпадает в осадок", когда посреди строки вдруг приходит синхроимпульс. Очевидно, сбрасывается координата X, ещё и включается back_porch, которая запрещает запись в память, так что огромные куски выпадают. Но видимо самое вредное, что может произойти - это обнуление выходного буфера, из-за чего и возможно полное зависание. Ведь процессор продолжает ждать результатов обработки, тогда как видеопроцессор исполнит все задания и уходя погасит АЦП - приехали.

К счастью, вся эта история с синхроимпульсами и АЦП "на борт" не пойдёт - там взамен будет фотоприёмная матрица 1205ХВ014, она сама всё это выдаст!


Что ж, баг мы нашли, исправить его дело техники. Если повезёт, завтра прихлопну сразу двоих "одним ударом". А именно, поставлю БИХ-фильтр на вход детектора синхроимпульсов...
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Нахождение двух самых отдалённых точек

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

  • Слишком общительный счётчик

    Вчера я чуть поторопился отсинтезировать проект,параметры не поменял: RomWidth = 8 вместо 7, RamWidth = 9 вместо 8, и ещё EnableByteAccess=1, чтобы…

  • Балансируем конвейер QuatCore

    В пятницу у нас всё замечательно сработало на симуляции, первые 16 миллисекунд полёт нормальный. А вот прошить весь проект на ПЛИС и попробовать "в…

  • Ковыряемся с сантехникой

    Наконец-то закрыл сколько-нибудь пристойно трубы, подводящие к смесителю, в квартире в Москве: А в воскресенье побывал на даче, там очередная…

  • Мартовское велосипедное

    Продолжаю кататься на работу и с работы на велосипеде, а также в РКК Энергию и на дачу. Хотя на две недели случился перерыв, очередная поломка,…

  • Обнаружение на новом GPU - первые 16 мс

    Закончилась симуляция. UFLO и OFLO ни разу не возникли, что не может не радовать. За это время мы дошли до строки 0x10F = 271. Поглядим дамп памяти:…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 5 comments