[Из песочницы] Подключение дисплея DVD плеера к микроконтроллеру
Начну с предыстории, зачем же мне все это нужно. Я задался целью сделать себе HTPC компьютер на базе корпуса от DVD плеера Daewoo DV-500, внешне он мне нравится, и свободного места в нем достаточно для установки необходимого железа внутрь. Но помимо всего, я задумал оставить родной индикатор и задействовать его для отображения различной информации. О том, как я подключал дисплей к микроконтроллеру и расшифровывал протокол обмена этого дисплея, пойдет в этой статье.Такую работу надо начинать с поиска информации, в первую очередь я нашел схему плеера, проблем с этим, на мое удивление, не возникло. По схеме я смог понять с чем предстоит работать, но найти описания на контроллер дисплея сразу не получилось, т.к. на схеме он подписан неправильно. К сожалению, документацию на контроллер я нашел в последний момент, случайно, но к этому времени я практически полностью разобрал протокол.
Итак, что же мы имеем:
Мы имеем контроллер VFD дисплея NEC D16312 (обратите внимание как подписан контроллер на схеме, не удивительно что я сразу не смог найти о нем инфу), с подключенными к нему VFD дисплеем, клавиатурной матрицей и светодиодом. С «внешним миром» контроллер подсоединятся через последовательный интерфейс посредством разъема CN1.
Для перехвата сообщений от контроллера основной платы в контроллер дисплея я буду использовать логический анализатор. Я, например, пользуюсь китайским клоном USBee AX PRO, свои ф-ции он выполняет на отлично и стоит недорого.
Подключаем выводы Data, Clk и Cs к любым трем выводам логического анализатора, не забываем также соединить землю. Далее, соединяем анализатор по USB с компьютером и запускаем USBee Suite (я опускаю процесс установки драйверов и программного обеспечения для логического анализатора, это выходит за рамки статьи). В настройках Speed and Samples ставим следущие параметры: Sample Rate — 2Msps, Buffer Size — 10M samples. Этого будет достаточно чтоб захватить кадр обмена размером в 5 секунд.Дальнейшие мои действия таковы, делаю единичный захват (кнопка Capture Once) и сразу-же включаю плеер. Как только появилась первая информация на экране — выключаю захват. После проделывания таких действий для различных вариантов отображаемого текста, я стал анализировать полученную информацию.
Итак, какие же имеются закономерности во всех посылках команд. Во-первых, что бросилось в глаза сразу, это периодичность посылки команды с кодом 0×42 и тремя пустыми байтами после нее. Команда имеет строгую периодичность появления даже когда видеоплеер в состоянии покоя. Я предположил, что это команда опроса клавиатуры, проверить теорию очень легко, зажимаю любую кнопку на плеере и делаю захват кадра в программе.
Сразу же после байта 0×42 появился не нулевой байт, а раньше был 0! Таким образом 0×42 — команда опроса клавиатуры, которая отсылается контроллером основной платы в контроллер дисплея и в ответ на эту команду контроллер дисплея отвечает кодом нажатой клавиши (или 0 если не нажата ни одна).
Следующей на очереди стала команда с кодом 0×40, она появлялась только когда на экран выводилась информация. После нее всегда идут два байта, первый байт всегда начинается с 0xCX, где X изменяющееся значение. Третий байт имеет произвольные значения, но главное что при первом включении он всегда равен 0
На этом скриншоте видно что второй байт растет с каждой посылкой группы 0×40, скорее всего он задает адрес символа и третьим байтом записывается значение символа. В данном случае 0 очищает символ. Всегда после отсылки всех групп с кодом 0×40 последует два байта 0×02 0×8F.
Ну и последней группой данных является команда, начинающаяся с кодом 0×41 и последующим нулевым байтом после нее. Команда появляется только при первом включении видеоплеера.
Анализ данных вырисовал более-менее понятную картинку работы контроллера, осталось только проверить все на практике. Я использовал отладочную платку LPCXpresso, полученною мною «нахаляву» в рамках какого-то конкурса от NXP. Платка оснащена простеньким 32х битным контроллером LPC1114. Т.к. контроллер питается напряжением 3.3В, а контроллер дисплея D16312 от 5ти вольт, то соединять их выводы напрямую я не решился. Возможно, выводы микроконтроллера толерантны к напряжению 5В, но у меня в наличии имелась платка согласования уровней 3.3–5В и я использовал ее.
Итак, из разъема, идущего к основной плате, извлекаем 3 провода, отвечающие за пины Data, Cs и Clk соответственно пины 1, 2 и 3. К плате контроллера дислея припаиваемся проводами к точкам соединения Data, Clk и Cs, а также нам понядобятся выводы GND и +5V VCC. На первое воемя, я присоединил извлеченные от основной платы три контакта с логическим анализатором, чтобы дебажить передаваемую информацию.
Вот так у меня выглядит содениение с отладочной платкой
Вся железная часть готова, пора приступать к софту. Для начала напишем программу, которая будет повторять полученные в ходе анализа, данные. Для 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 отсылает команду в порт, у этой ф-ции имеется второй интересный параметр. Если присмотреться на графики сигналов выше, то можно увидеть что сигнал стробирования, выставляется в активный (низкий) уровень, перед посылкой данных, и меняется между двумя независимыми командами. Но не переключается, если после отправки команды должны передаваться данные
Так вот, второй параметр говорит что команда является атомарной если isSingle == 1. Для случая неатомарных команд предназначена ф-ция EndCommand, которую следует вызывать после отсылки данных.
Отсылка же данных выполняется в ф-ции WriteData, по очереди, бит-за-битом, начиная с младшего, передаем в пин Data информацию. Каждое выставление данных сопровождается сигналом синхронизации, ф-ция Clock генерирует его.
В ф-ции main сначала инициализируем работу с входами ввода/вывода, далее выставляем пины Data, Clk и Cs на вывод. В бесконечном цикле имитируем команды, которые были получены ранее на этапе анализа данных. Т.о. мы отправили в контроллер дисплея последовательность данных 0×40 0xC0 0xFF 0×02 0×8F, а контроллер ответил выводом _8
Т.о. было установлено что команда: — 0×42 — отвечает за обработку клавиатуры. После ее отправки в контроллер дисплея нужно выставить порт Data на вход и прочитать с него данные, предварительно синхронизировав сигналом Clk— 0×40 — команда записи данных в дисплей, после нее должна записываться команда указания адреса символа, а после указания адреса записываются данные— 0×41 — это команда для управления светодиодом, после нее идет байт с данными какой светодиод должен быть включен или нетБолее подробную информацию вы можете найти в руководстве по данному контроллеру дисплея D16312, там расписаны как должны формироваться команды. Ну, а я оформил всю работу в небольшую библиотечку, которая лежит на гитхабе. Библиотека позволяет отображать текст, управлять спецсимволами, заполнять символ диска в процентном соотношении, читать клавиатуру, менять яркость отображения и управлять светодиодом. На этом все, надеюсь кому-то это чтиво будет полезным и интересным. А на закуску я оставлю видео, с демонстрацией работы библиотеки
[embedded content]