nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

Управление "джойстиком" аналоговой камеры с помощью ПЛИС

На аналоговой камере наблюдения на проводе висит такой вот джойстик:



С его помощью можно вызвать экранное меню и настроить много чего: режим цвета (PAL или NTSC), день/ночь (в цвете с включённым ИК фильтром, ч/б с выключенным фильтром или "авто", по датчику освещённости), яркость/контрастность/насыщенность, режимы экспозиции (есть какие-то пресеты, позволяющие выдерживать, когда "светят в морду") и много чего ещё.

Кроме того, если повернуть этот рычажок в одно из четырёх направлений и в течение 5 секунд удерживать, будет выбран один из аналоговых форматов: CVBS (если "влево"), AHD ("вверх"), TVI ("вправо") или CVI ("вниз").

Мне когда-то захотелось избавиться от этого провода и научиться его "эмулировать" с ПЛИС, а потом уже управлять как захочется - либо с кнопочек макетки, а может вообще через UART с компьютера. Оказалось, что для этого достаточно резистора 360 Ом и конденсатора 0,1 мкФ, и 5-битного ШИМ-контроллера, реализованного уже в ПЛИС.


От платы камеры на кабель идёт 5 проводов: видеосигнал со своей "землёй" (выводится на оплётку коаксиального кабеля), питание со своим "общим проводом", и тот самый провод для джойстика.

Если отсоединить кабель вместе с джойстиком и начать "прозванивать", мы выясним следующее:
- когда джойстик не нажат, пятый провод "висит в воздухе",
- при нажатии "вниз", пятый провод закорачивается на землю,
- при нажатии "вверх", провод соединяется на землю через резистор 12 кОм,
- при нажатии "влево" - через резистор 19 кОм,
- при нажатии "вправо" - через резистор 6,2 кОм,
- при нажатии "Enter" - через резистор 27 кОм.

При включении камеры, на пятом проводе появляется +3,3 вольта. Ток короткого замыкания (если соединить его на общий через щупы тестера) составляет 65 мкА, что соответствует "внутреннему сопротивлению" в 51 кОм.

Далее, решил посмотреть напряжение на этом проводе при подключённом джойстике:
- при нажатии "вниз" получаем ноль вольт,
- при нажатии "вверх": 0,748 В,
- при нажатии "влево": 1,147 В,
- "вправо": 0,381 В,
- "Enter": 1,559 В.

Эти значения не до конца "бьются" с моделью, где на стороне камеры провод подключён на 3,3 вольта через резистор 51 кОм. Больше похоже на источник тока, но не суть важно.

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

Мне хотелось выставить номинал R как можно выше, чтобы меньше нагружать выход ПЛИС и чтобы не понадобился большой конденсатор. Но если поставить резистор более 1 кОм, то даже при подаче лог "0" на выход ПЛИС команда "вниз" (закорачивание) не проходит. В выборе резистора 360 Ом я уже не очень уверен. Я размышлял так: подошёл резистор в 820 Ом, а 1 кОм не подходил, это при комнатной температуре и при нормальном питающем напряжении. Решил: поставлю вдвое меньше, "с запасом", но резисторов 430 Ом у меня совсем немного, а вот 360 Ом девать некуда! Действительно, работает, но чуть на перегрузе. Да, камера отжирает всего 50 мкА по этому выводу, крохи. Но "туда-сюда" перекачиваются приличные токи. Сначала с +3,3 на конденсатор, затем с конденсатора на землю. По наихудшему случаю, около 9 мА (в импульсе) через ножку ПЛИС выходит, многовато. Предельно-допустимое значение: 4 мА, предельное: 24 мА. С другой стороны, СРЕДНЕЕ значение тока что через верхний, что через нижний ключ как раз порядка 4 мА, так что более-менее проскрипим. По счастью, для ЭТОЙ ЦЕПИ мне уж точно не нужно будет рисовать карту рабочих режимов :)

Конденсатор я выбрал тоже по-простому: 0,1 мкФ. У меня этих керамических 0,1 мкФ - ну просто завались! Характеристики у них специфические - "никакой" ТКЕ, а иногда и пьезоэффект зверский, но для этой задачи каких-то суперпараметров и не требуется!

В итоге, получаем постоянную времени 36 мкс, что соответствует граничной частоте в 4,4 кГц. Чтобы получить хоть сколько-нибудь постоянное напряжение, надо, чтобы частота ШИМ была ещё раз в 10 выше, то есть 44 кГц. Опытным путём было установлено, что нам здесь достаточно 5-битной ШИМ, и тогда при самой простой реализации она делит тактовую частоту в 32 раза. Если мы используем 4 МГц - то получаем 125 кГц - неплохо. А при работе от 25 МГц выйдет 781 кГц - вообще дофига.

Понятное дело, ШИМ - не единственный способ получить после сглаживания регулируемое напряжение, есть сигма-дельта модуляция, которая позволяет с тех же 4 МГц получить куда более "чистенький" сигнал, но здесь это из пушки по воробьям, тут и ШИМ за глаза.

Приведём код модуля:

//keeps Q disconnected while qen = 0,
//form PWM on it when qen = 1
// D = 0 is largest output ("1" everywhere except 1 low pulse),
// D = 2^(PWMwidth ) - 1 is lowest output (just 0).

//also, we divide input frequency (set by ce pulses) by 2^PWMwidth and output this on ceo.
//(why waste so good counter :))

module PWM (input clk, input ce, input qen, input [PWMwidth - 1 : 0] D, output ceo, inout Q);

	parameter PWMwidth = 5;
	
	wire [PWMwidth - 1 : 0] cnt;
	wire TC; //Terminal Count
	
	assign ceo = TC & ce;
	
	reg sQ = 1'b0;
	
	assign Q = qen? sQ : 1'bz; //puts into Z state if qen = 0
	
	lpm_counter counter (
				.clock (clk),
				.cnt_en (ce),
				.q (cnt),
				.cout (TC) );
	defparam
		counter.lpm_direction = "UP",
		counter.lpm_port_updown = "PORT_UNUSED",
		counter.lpm_type = "LPM_COUNTER",
		counter.lpm_width = PWMwidth;
		
	always @(posedge clk) if (ce)
		sQ <= TC? 1'b0 : (D == cnt)? 1'b1 : sQ;
endmodule


Здесь мы объявили выход Q как двунаправленный, чтобы подчеркнуть, что он может переходить в Z-состояние (с высоким выходным сопротивлением) - так сделано, чтобы и "старый" джойстик мог сохранять функциональность, если надо.

Далее, у всех ШИМ есть интересная особенность. Будучи n-битными, они могли бы изобразить 2n+1 различных состояний. К примеру, 1-битный (!!!) ШИМ мог бы изобразить 3 напряжения: ноль, +3,3 В (лог "1") и +1,55 В (квадратный меандр). По идее, можно ввести одним битом больше, чтобы можно было закодировать все эти напряжения.

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

В нашем случае, лог "0" очень нужен, чтобы работала кнопка "вниз", а лог "1" не нужен вообще, вместо него у нас переход в высокое выходное сопротивление. А даже если оно нам не нужно, если на выходе будет 31/32 от 3,3 вольт, камера вполне воспримет такое как не нажатые кнопки (там огромная дистанция между 3,3 вольтами и 1,56 вольтами, обозначающими Enter).

Как можно видеть, весь этот ШИМ-контроллер состоит из 5-битного счётчика, который считает "вверх", от 0 до 31 и снова переходит в ноль, и так далее.

Выход по достижении 31 сбрасывается в ноль, а в единицу он переключается при равенстве значения счётчика тому числу, что подано на вход. Важно, что у переключения в ноль имеется приоритет! Поэтому, если мы подадим на вход 31, то выход так и будет всегда оставаться в нуле. Подадим 30 - выход будет оставаться в нуле, включаться на 1 такт - и снова возращаться в ноль, и так далее.

Вход в итоге получился "инвертированным", когда самое большое значение на нём (31) соответствует самому низкому напряжению (0 вольт). В данной ситуации - вообще не страшно, ведь нам только нужно для каждой кнопки задать свою константу. А так, можно заменить счётчик "вверх" на счётчик "вниз", у которого выход cout=1 соответствует всем "нулям". Этот модуль я писал ещё в начале лета 2019 года, совсем был "зелёным", к счёту "вниз" относился с большим недоверием :)

Всего этот модуль ШИМ занимает 11 ЛЭ для 5 бит, это ещё с учётом выхода ceo (clock enable - output) - я использовал это деление тактовой частоты в 32 раза для других вещей :) Без него получится и того меньше.

Теперь нужен ещё один модуль, который в зависимости от нажатой кнопки на макетке будет подавать на ШИМ-контроллер подходящее число. Вот он:

//all the inputs are '1' when not pressed, '0' when pressed.
module JoystickForAnalogCamera (input up, input down, input left, input right, input enter, output Qen, output [4:0] Q);

	//priority is not relevant
/*
	assign Q = 	~up? 	5'b11000:
			 //or   5'b11001
			~down? 	5'b11111:
			~left?	5'b10100: //not this...
			//or     10010,
			//or     10011,
			//or     10011,
			//or     10101 //and not this
			~right?	5'b11011:
				5'b10000;
*/
	//always 1
	assign Q[4] = 1'b1;
	//0 only if ~left or ~enter
	assign Q[3] = left & enter;
	//1 only if ~down
	assign Q[2] = ~down;
	//1 only if ~down or ~left or ~right
	assign Q[1] = (~down) | (~left) | (~right);
	//1 only if ~down or ~right
	assign Q[0] = (~down) | (~right);
	//enable output if at least one button is pressed
	assign Qen = Q[1] | ~(up & enter);
endmodule


Как показано в закомментированном коде, константы можно слегка варьировать - будет нормально работать. А потом, смеху ради, я провёл ручную оптимизацию - выбрал такие константы, которые легко (с минимальным числом входов) кодируются. В итоге сэкономил аж 2 ЛЭ - их число с 6 упало до 4 :) А в совокупности с ШИМ, могло и того меньше выйти, не проверял.

Посмотрим, что получается на осциллограммах. Когда ни одна кнопка не нажата. Кто бы сомневался: 3,3 вольта как на ножке ПЛИС, так и на входе в камеру:
IMG20200308135052.jpg

Если нажать "вниз", картина тоже довольно скучная:
IMG20200308135052.jpg

Другие чуть интереснее. "Влево". Классический ШИМ и частичное сглаживание RC-цепочкой:
IMG20200308135052.jpg

"Вправо":
IMG20200308135052.jpg

"Вверх":


"Enter":



Вот так оно работает:
Tags: ПЛИС, работа, странные девайсы
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments