nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Обнаружение точек "в железе"

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

После нескольких часов отладки получилось то что надо:


Это было весело...



Вот программа, которую мы прошивали, а точнее её основной файл, videoprocessing.asm:

;громко думаем, как организовать видеоообработку
;наилучшее качество получим с сигнала CVI, т.к у него длительность видимой строки максимальная, 43 мкс, и 10,3 мкс на обратный ход
;у нас есть не более 308 тактов для подготовки к следующей строке!

;в 1205ХВ014 логика будет существенно отличаться: сканирование "в два луча", всего 8 тактов на обратный ход
;но давайте решать проблемы по мере их поступления. Потренируемся на кошках, то бишь на аналоговых камерах высокой чёткости...

%include "QuatCoreConsts.inc"
%include "Win1251.inc"
.rodata
	; VSync			EQU	0x8000	;ожидание кадрового синхроимпульса, значение для регистра произвольное
	; HSync			EQU	0x4000	;ожидание строчного синхроимпульса, а затем ещё интервал front porch (между импульсом и началом картинки)
	; WholeRow		EQU	1023		;для обработки всей строки целиком	
	; ImgHeight		EQU	720		;720p пока что 
	VSync			EQU	0x8000	;ожидание кадрового синхроимпульса, значение для регистра произвольное
	HSync			EQU	0x40EE	;ожидание строчного синхроимпульса, а затем ещё интервал back porch (между импульсом и началом картинки) в 240 тактов (239+1 такт на сброс счётчика + 2 такта задержки на ПЗУ)
	WholeRow		EQU	31		;для проверки на симуляторе
	ImgHeight		EQU	32		;вообще, 32, но вводим дополнительные строки, чтобы перенести все точки из ActivePoints в AllPoints
	ImgHeightP1		EQU	33		;не обучили свой компилятор обрабатывать "constexp"
	Threshold		EQU	0xDFF		;минимальная яркость пятен, сейчас поставили 1/8 от "насыщения" (4096), но с инверсией, так надо!
	Nil			EQU	-32768	;традиционно это 0, но у нас нет флага нуля! Поэтому куда удобнее гигантское отриц. значение, всяко у нас 65535 слов памяти не наберётся, надеемся что 256..512 слов вполне хватит
.code	
	main proc
	
		%include "SetClock.asm"
			
				SP		Stack	;инициализация стека.
		
		%include "ProcessFrame.asm"
		
				;давайте передадим параметры обнаруженных точек по UART
				;пока тупо по младшему байту
				SIO		UART
.rodata
	PointsStr	db	'Обнаруженные точки:',0x0D,0x0A,0x00
.code
				X		PointsStr
				CALL		print
				
				Z		AllPoints	;В списке AllPoints нет фиктивных точек. Но может вообще никаких не быть!
				;Хотя сам AllPoints - это "заголовок", с него надо двинуться вперёд, если получится
				i		0		;номер точки (для отображения в терминале, поэтому нумерация с 1)
				JMP		@@moveOn	;перемещение на очередную точку
				;к началу цикла мы переместились на одну из найденных
		@@outLoop:	X		NumSign		
				Acc		i
				CALL		CallThemAll
				;назвали номер точки, теперь выдаём её параметры
.rodata
	NumSign		db	'№',0x00
	XStr		db	'X:',0x00
	YStr		db	'Y:',0x00
	LumStr		db	'Яркость:',0x00
	SizeStr		db	'Диаметр:',0x00		
	OutOfMemStr	db	'Out of heap memory',0x0D,0x0A,0x00	
.code	
;в [Z+1] лежит радиус точки,
;в [Z+2j+k]=[Z+2] - X-координата
;в [Z+2j+1]=[Z+3] - Y-координата
;в [Z+4j+k]=[Z+4] - яркость
				X		XStr
				Acc		[Z+2j+k]	;X-координата
				CALL 		CallThemAll
				
				X		YStr
				Acc		[Z+2j+1]	;Y-координата
				CALL 		CallThemAll
				
				X		LumStr
				Acc		0xFFF		;инверсия 12 младших бит через вычитание
				SUB		[Z+4j+k]	;Яркость
				CALL 		CallThemAll
				
				X		SizeStr
				Acc		[Z+1]	;размер точки
				CALL 		CallThemAll				
				
				
				;продвигаемся на одну позицию, если только указатель не nil
		@@moveOn:	ZAcc		RoundZero
				SUB		[Z+k]
				Z		[Z+k]
				i++		0
				JL		@@outLoop


				JMP		@@endless
	OutOfMemory:		X		OutOfMemStr
				CALL		print
	@@endless: 		JMP 		@@endless
	main endp	

		
	;после 3 вызовов этой процедуры начинается экономия :)
	CallThemAll	proc
		CALL	print
		CALL	BetterBin2Bcd
		OUT 	13
		OUT 	10
		JMP 	[--SP]
	CallThemAll endp
	
	%include "Print.asm"
	%include "Bin2Bcd.asm"
	
.data
	;расчётный диаметр пятна. Вообще, должен грубо устанавливаться исходя из экспозиции кадра, затем корректироваться по вычисленной дальности
	;но пока, для отладки, сразу зададим 3
	D1		dw	6
	;ДИВАН
	;в дальнейшем будет ещё R2 - радиус МДД, когда работаем в ближней зоне (они больше не используются для измерения, но нужно проверять их наличие)

	ActivePoints	dw	APHeadDummyX
	AllPoints	dw	Nil	
	APHeadDummyX	dw	Nil	;когда будем сверять с X-координатой ПРЕДЫДУЩЕЙ ТОЧКИ, это позволит не вводить лишних проверок!
	Heap		dw	Elem0	
	APTailDummyX	dw	32767
	;возможно, и оставшиеся значения Y, Lum, Path могут пригодится, а пока пущай будут чем угодно
	
	;область для списков точек, для начала она вся объединена в список Heap
	Elem0:
	Elem0Next	dw	Elem1
	Elem0X		dw	?
	Elem0Y		dw	?
	Elem0L		dw	?
	Elem0P		dw	?
	Elem1		dw	Elem2,?,?,?,?
	Elem2		dw	Elem3,?,?,?,?
	Elem3		dw	Elem4,?,?,?,?
	Elem4		dw	Elem5,?,?,?,?
	Elem5		dw	Elem6,?,?,?,?
	Elem6		dw	Elem7,?,?,?,?
	Elem7		dw	Elem8,?,?,?,?
	Elem8		dw	Elem9,?,?,?,?
	Elem9		dw	ElemA,?,?,?,?
	ElemA		dw	ElemB,?,?,?,?
	ElemB		dw	ElemC,?,?,?,?
	ElemC		dw	ElemD,?,?,?,?
	ElemD		dw	ElemE,?,?,?,?
	ElemE		dw	ElemF,?,?,?,?
	ElemF		dw	Elem10,?,?,?,?
	Elem10		dw	Elem11,?,?,?,?
	Elem11		dw	Elem12,?,?,?,?
	Elem12		dw	Elem13,?,?,?,?
	Elem13		dw	Elem14,?,?,?,?
	Elem14		dw	Elem15,?,?,?,?
	Elem15		dw	Elem16,?,?,?,?
	Elem16		dw	Elem17,?,?,?,?
	Elem17		dw	Elem18,?,?,?,?
	Elem18		dw	Elem19,?,?,?,?
	Elem19		dw	Elem1A,?,?,?,?
	Elem1A		dw	Elem1B,?,?,?,?
	Elem1B		dw	Elem1C,?,?,?,?
	Elem1C		dw	Elem1D,?,?,?,?
	Elem1D		dw	Elem1E,?,?,?,?
	Elem1E		dw	Elem1F,?,?,?,?
	Elem1F		dw	-32768,?,?,?,?
	
	Stack		dw	?,?,?,?


А к ней подключается ещё 4 кусочка: SetClock.asm (установить тактовую частоту 25 МГц), ProcessFrame.asm (тот самый зверский алгоритм обнаружения точек), Print.asm (вывод строк) и Bin2Bcd.asm (вывод целых чисел в десятичной форме).

Решил попробовать "с нахрапу" - откомпилить и тут же прошить в ПЛИС. И получил довольно интересный результат:



С виду прямо то что надо, но присматриваешься - и находишь несоответствия!

Во-первых, откуда взялась "Яркость:" на второй строке? Её там быть не должно!
Во-вторых, странная нумерация точек: то они все номер ноль, а потом сразу номер 5, да так и остаются.
И точек слишком много, их должно быть 4, а здесь их 7. Причём, что интересно, внизу 4 корректные точки (не считая нумерации), а наверху какая-то фигня с максимально возможной яркостью и при этом с нулевыми координатами и нулевым диаметром.

Одну вещь я сразу заподозрил: Процедура Bin2Bcd затирает регистры! Посмотрел подробнее: она может поменять значения регистров i,k,Y,Acc. При этом k к концу выполнения всегда нулевой, а это нам и надо, Y мы тоже благоразумно использовать не стали, а вот регистр i как раз показывал номер текущей точки. По окончании работы Bin2Bcd в i лежала последняя выведенная цифра минус 1, а если там ноль - так и будет ноль. Смотрим - всё совпадает. Добавляем push i в начале (у нас он записывается как [SP++] i) и pop i в конце (i [--SP]) и пробуем ещё разок:



Действительно, нумерация восстановлена, идёт от 1 до 7. Но остальные проблемы никуда не делись.

Со словом "Яркость:" скоро тоже стало понятно, оно как раз шло сразу за строкой "Обнаруженные точки:" (с переводом строки), и в листинге памяти куда-то пропал нолик, которым должна завершаться строка! Это вообще старая болячка моего компилятора, что в перечислении через запятую значений нельзя ставить после запятой ставить пробел - он и пробел, и запятую считает разделителем, и находит пустое значение между ними, и найдя его считает что строка закончена. Надо будет это подправить, проблема элементарная.

А пока просто убрал пробел - и после компиляции получил такое:


Да, лишняя "Яркость:" исчезла, и вместе с ней ДВЕ ЛИШНИЕ ТОЧКИ. Осталась всего одна лишняя точка! Причём почему это вышло - я не мог понять очень долго. Громко смотрел на все эти листинги и пытался придумать хоть одно объяснение, как добавление этого нолика могло повлиять на выводимые точки.

В конце концов сдался - и запустил симуляцию на компьютере. Посмотрел содержание оперативной памяти после выполнения:


Как оказалось, в списке AllPoints действительно лежит лишняя точка. Она правильно внесена в этот список: AllPoints указывает на неё, а она указывает на последнюю "нормальную" точку. Явно это было произведено "намеренно". Причём, Y-координата 36, выходящая за границы изображения 32х32, намекает, что занесена она была когда видеопроцессор стал "сыпать нулевыми результатами" на каждом такте, с единственной целью поскорее перенести все оставшиеся точки из ActivePoints в AllPoints. К нам должны были приходить исключительно точки с нулевой яркостью, поэтому мы их не вносим в список - и всё хорошо.

После изучения "осциллограммы", я наконец-то нашёл, где возникает ошибка:



Если мы взглянём на строчку Sum, то увидим один кусочек, где она с 0xFFF (нулевая координата и нулевая яркость в дополнении до единиц) вдруг перескакивает в нули, причём происходит это когда UFLO=1 (Underflow, опустошение входного буфера).

FACEPALM!
Опять не смог на один шаг вперёд подумать. Ведь проговорил, что наш особо жадный буфер FIFO считает нормальным, будучи полностью пустым, заносить в себя любые данные с шины!

Вот только до конца эту мысль не смог довести. С шины могут прийти не просто разные режимы синхронизации (на это плевать) или разные координаты (мы их всё равно игнорируем). Может затесаться команда TRK (Tracking) вместо знакомой нам ACQ (Acquire), а она как раз-таки устанавливает SUM во все нули, вместо 0xFFF. Собственно, именно это и произошло.

А такое значение яркости, 0x000, в коде дополнения до единиц означает максимально возможную яркость, 0xFFF=4095. Разумеется, она превышает порог - и мы заносим эту точку в список.

Также объясняется, как добавление нолика в строку могло поменять поведение: поменялись адреса, особенно с помощью нашей дорогой QuatCoreImmTable (которая любит пихать произвольные значения в старшие биты, когда это позволительно). В итоге, опустошённый буфер получал другие значения, и "застревал" на кадровом синхроимпульсе в разных местах (пока мы сами не подадим на него команду, которая заведомо затрёт "пустое" значение в буфере), а потому видеопроцессор "нарывался" на случайно возникшую команду TRK разное количество раз - сначала это было 3 раза, а снизилось до 1.

Также становится понятно, почему диаметр этих точек нулевой. Они всё время приходят "по нескольку штук", и мы сначала обнаруживаем одну точку с координатой 0. Затем, на этой же самой строки ищем точку за ней - и находим ещё одну с координатой 0. Он понимает, что они расположены слишком близко друг к другу, и нужно их "слить" в одну. И на данный момент это означает лишь одно: обновить диаметр точки. Предполагается, что расстояние между ними должно было дать сколько-нибудь адекватную оценку (хотя думаю, надо его ещё на 2 умножить). Но в данном "вырожденном" случае расстояние оказывается нулевым - вот и выходит нулевой диаметр.

Так мы, сами того не желая, наконец-то довели "покрытие кода" ProcessFrame до 100%. Хотя здесь требуется более сложное поведение, нынешнее опять же "не заглядывает на шаг вперёд"...

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

И после всех этих изменений мы наконец-то получили правильный результаты, причём всё это работало на своих штатных 25 МГц.


Забавно, как наложились ошибки из всех "слоёв" данного проекта: одна была "в железе", вторая в компиляторе, третья в программе.

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

И пора наконец-то переходить к реальной камере! Первым делом получить с неё изображение и убедиться, что оно сколько-нибудь качественное, между делом изготовить хоть что-то напоминающее мишень (4 катафота на заданных расстояниях друг от друга) - и начать "натурные испытания".
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 1 comment