[Из песочницы] Подключение дисплея DVD плеера к микроконтроллеру

6b6d43195c50451f824e807d5a5e7a06.JPG Начну с предыстории, зачем же мне все это нужно. Я задался целью сделать себе HTPC компьютер на базе корпуса от DVD плеера Daewoo DV-500, внешне он мне нравится, и свободного места в нем достаточно для установки необходимого железа внутрь. Но помимо всего, я задумал оставить родной индикатор и задействовать его для отображения различной информации. О том, как я подключал дисплей к микроконтроллеру и расшифровывал протокол обмена этого дисплея, пойдет в этой статье.Такую работу надо начинать с поиска информации, в первую очередь я нашел схему плеера, проблем с этим, на мое удивление, не возникло. По схеме я смог понять с чем предстоит работать, но найти описания на контроллер дисплея сразу не получилось, т.к. на схеме он подписан неправильно. К сожалению, документацию на контроллер я нашел в последний момент, случайно, но к этому времени я практически полностью разобрал протокол.

Итак, что же мы имеем:

8eca33587255445b947acc1a102b6868.png Мы имеем контроллер VFD дисплея NEC D16312 (обратите внимание как подписан контроллер на схеме, не удивительно что я сразу не смог найти о нем инфу), с подключенными к нему VFD дисплеем, клавиатурной матрицей и светодиодом. С «внешним миром» контроллер подсоединятся через последовательный интерфейс посредством разъема CN1.

Для перехвата сообщений от контроллера основной платы в контроллер дисплея я буду использовать логический анализатор. Я, например, пользуюсь китайским клоном USBee AX PRO, свои ф-ции он выполняет на отлично и стоит недорого.

3661cc080fcd422d8edd7fcf8263284b.png Подключаем выводы Data, Clk и Cs к любым трем выводам логического анализатора, не забываем также соединить землю. Далее, соединяем анализатор по USB с компьютером и запускаем USBee Suite (я опускаю процесс установки драйверов и программного обеспечения для логического анализатора, это выходит за рамки статьи). В настройках Speed and Samples ставим следущие параметры: Sample Rate — 2Msps, Buffer Size — 10M samples. Этого будет достаточно чтоб захватить кадр обмена размером в 5 секунд.Дальнейшие мои действия таковы, делаю единичный захват (кнопка Capture Once) и сразу-же включаю плеер. Как только появилась первая информация на экране — выключаю захват. После проделывания таких действий для различных вариантов отображаемого текста, я стал анализировать полученную информацию.

Итак, какие же имеются закономерности во всех посылках команд. Во-первых, что бросилось в глаза сразу, это периодичность посылки команды с кодом 0×42 и тремя пустыми байтами после нее. Команда имеет строгую периодичность появления даже когда видеоплеер в состоянии покоя. Я предположил, что это команда опроса клавиатуры, проверить теорию очень легко, зажимаю любую кнопку на плеере и делаю захват кадра в программе.

ebb974af8ce2443498cf4d6e9ee4705b.png Сразу же после байта 0×42 появился не нулевой байт, а раньше был 0! Таким образом 0×42 — команда опроса клавиатуры, которая отсылается контроллером основной платы в контроллер дисплея и в ответ на эту команду контроллер дисплея отвечает кодом нажатой клавиши (или 0 если не нажата ни одна).

Следующей на очереди стала команда с кодом 0×40, она появлялась только когда на экран выводилась информация. После нее всегда идут два байта, первый байт всегда начинается с 0xCX, где X изменяющееся значение. Третий байт имеет произвольные значения, но главное что при первом включении он всегда равен 0

b035bea906b449688fd65288e9d4ed11.png На этом скриншоте видно что второй байт растет с каждой посылкой группы 0×40, скорее всего он задает адрес символа и третьим байтом записывается значение символа. В данном случае 0 очищает символ. Всегда после отсылки всех групп с кодом 0×40 последует два байта 0×02 0×8F.

05ce98f9ac0844bcbefb06d65ecbf7ff.png Ну и последней группой данных является команда, начинающаяся с кодом 0×41 и последующим нулевым байтом после нее. Команда появляется только при первом включении видеоплеера.

Анализ данных вырисовал более-менее понятную картинку работы контроллера, осталось только проверить все на практике. Я использовал отладочную платку LPCXpresso, полученною мною «нахаляву» в рамках какого-то конкурса от NXP. Платка оснащена простеньким 32х битным контроллером LPC1114. Т.к. контроллер питается напряжением 3.3В, а контроллер дисплея D16312 от 5ти вольт, то соединять их выводы напрямую я не решился. Возможно, выводы микроконтроллера толерантны к напряжению 5В, но у меня в наличии имелась платка согласования уровней 3.3–5В и я использовал ее.

43c09de038244a8cab04435847b1cffa.jpg Итак, из разъема, идущего к основной плате, извлекаем 3 провода, отвечающие за пины Data, Cs и Clk соответственно пины 1, 2 и 3. К плате контроллера дислея припаиваемся проводами к точкам соединения Data, Clk и Cs, а также нам понядобятся выводы GND и +5V VCC. На первое воемя, я присоединил извлеченные от основной платы три контакта с логическим анализатором, чтобы дебажить передаваемую информацию.

962eb481d54b44e08507a0a44e2e3a9a.JPG Вот так у меня выглядит содениение с отладочной платкой

92182571bf164fbf956f4eaf588a441b.JPG Вся железная часть готова, пора приступать к софту. Для начала напишем программу, которая будет повторять полученные в ходе анализа, данные. Для LPCXpresso используется одноименная LPCXpresso IDE на базе Eclipse. Запускаем, указываем путь к нашему новому рабочему пространству и импортим в него 2 стандартных библиотеки CMSIS_CORE_LPC11xx и LPC11xx_cmsis2_Lib, они нам понадобятся для разработки. Далее создадим новый проект File→New→C/C++→LPCXpresso C Project далее LPC11 / LPC12 → LPC11xx /… →C Project. Задаем имя проекта и в следующем окне выбираем целевой контроллер, в моем случае это LPC1114/302. На следующем шаге в списке уже должна появиться библиотека CMSIS_CORE_LPC11xx, т.к. мы ее импортировали ранее. В окне задания DSP библиотеки ничего не меняем и на следующем шаге оставляем все по умолчанию, жмем Finish.

Добавляем следующий код в сгенеренный файл <имя проекта>.c

#ifdef __USE_CMSIS #include «LPC11xx.h» #include «clkconfig.h» #include «gpio.h» #endif

#include

void Delay (int32_t ticks); void BeginCommand (uint8_t cmd, uint8_t isSingle); void EndCommand (); void Clock (); void WriteData (uint8_t data);

#define PORT 3 #define DATA_BIT 2 #define CLK_BIT 0 #define CS_BIT 1

int main (void) { GPIOInit ();

// Set pins to output GPIOSetDir (PORT, CLK_BIT, 1); GPIOSetDir (PORT, CS_BIT, 1); GPIOSetDir (PORT, DATA_BIT, 1);

while (1) { BeginCommand (0×40, 1); BeginCommand (0xC0, 0); WriteData (0xFF); EndCommand ();

BeginCommand (0×02, 1); BeginCommand (0×8F, 1);

Delay (3000000); } return 0; }

void Delay (int32_t ticks) { volatile int32_t i = ticks; while (i--); }

void BeginCommand (uint8_t cmd, uint8_t isSingle) { GPIOSetValue (PORT, CLK_BIT, 1); GPIOSetValue (PORT, CS_BIT, 0); Delay (10);

WriteData (cmd);

if (isSingle) { EndCommand (); } }

void EndCommand () { GPIOSetValue (PORT, CLK_BIT, 1); GPIOSetValue (PORT, CS_BIT, 1); Delay (10); }

void Clock () { Delay (10); GPIOSetValue (PORT, CLK_BIT, 0); Delay (10); GPIOSetValue (PORT, CLK_BIT, 1); }

void WriteData (uint8_t data) { GPIOSetDir (PORT, DATA_BIT, 1);

uint8_t i = 0; for (i = 0; i < 8; ++i) { uint8_t isSet = (data & 0x01); GPIOSetValue(PORT, DATA_BIT, isSet);

Clock ();

data = data >> 1; } } Здесь видим несколько ф-ций для работы с последовательным портом. В самом верху файла задаются настройки порта и пинов для данных, синхронизации и строба. В моем случае все висит на порту 3, пин 2 отвечает за сигнал Data, 0 — синхронизацию и 1 — стробирование.Ф-ция BeginCommand отсылает команду в порт, у этой ф-ции имеется второй интересный параметр. Если присмотреться на графики сигналов выше, то можно увидеть что сигнал стробирования, выставляется в активный (низкий) уровень, перед посылкой данных, и меняется между двумя независимыми командами. Но не переключается, если после отправки команды должны передаваться данные

eccc348e4a664fa8b717c7df97c11e38.png Так вот, второй параметр говорит что команда является атомарной если isSingle == 1. Для случая неатомарных команд предназначена ф-ция EndCommand, которую следует вызывать после отсылки данных.

Отсылка же данных выполняется в ф-ции WriteData, по очереди, бит-за-битом, начиная с младшего, передаем в пин Data информацию. Каждое выставление данных сопровождается сигналом синхронизации, ф-ция Clock генерирует его.

В ф-ции main сначала инициализируем работу с входами ввода/вывода, далее выставляем пины Data, Clk и Cs на вывод. В бесконечном цикле имитируем команды, которые были получены ранее на этапе анализа данных. Т.о. мы отправили в контроллер дисплея последовательность данных 0×40 0xC0 0xFF 0×02 0×8F, а контроллер ответил выводом _8

008a7a7d217c4f3983d2440b04c39a0f.JPG Т.о. было установлено что команда: — 0×42 — отвечает за обработку клавиатуры. После ее отправки в контроллер дисплея нужно выставить порт Data на вход и прочитать с него данные, предварительно синхронизировав сигналом Clk— 0×40 — команда записи данных в дисплей, после нее должна записываться команда указания адреса символа, а после указания адреса записываются данные— 0×41 — это команда для управления светодиодом, после нее идет байт с данными какой светодиод должен быть включен или нетБолее подробную информацию вы можете найти в руководстве по данному контроллеру дисплея D16312, там расписаны как должны формироваться команды. Ну, а я оформил всю работу в небольшую библиотечку, которая лежит на гитхабе. Библиотека позволяет отображать текст, управлять спецсимволами, заполнять символ диска в процентном соотношении, читать клавиатуру, менять яркость отображения и управлять светодиодом. На этом все, надеюсь кому-то это чтиво будет полезным и интересным. А на закуску я оставлю видео, с демонстрацией работы библиотеки

[embedded content]

© Habrahabr.ru