Connecting the DVD player display to the microcontroller
I'll start with the background, why do I need all this. I set out to make myself an HTPC computer based on the case from a Daewoo DV-500 DVD player, I like it outwardly, and there is enough free space in it to install the necessary iron inside. But besides everything, I decided to leave my own indicator and use it to display various information. This article will discuss how I connected the display to the microcontroller and decrypted the exchange protocol for this display.
Such work should begin with a search for information, first of all I found the player’s scheme, there were no problems with this, to my surprise. According to the scheme, I was able to understand what to work with, but I could not immediately find the descriptions on the display controller, because it is not correctly signed on the diagram. Unfortunately, I found the documentation for the controller at the last moment, by accident, but by this time I had almost completely disassembled the protocol.
So what do we have:
We have a NEC D16312 VFD display controller (note how the controller is signed on the diagram, it is not surprising that I could not immediately find the info about it), with a VFD display, keyboard matrix and LED connected to it. With the "outside world", the controller is connected via a serial interface via connector CN1.
To intercept messages from the mainboard controller to the display controller, I will use a logic analyzer. For example, I use the Chinese clone USBee AX PRO, it performs its functions perfectly and is inexpensive.
We connect the findings of Data, Clk and Cs to any three pins of the logic analyzer, do not forget to also connect the ground. Next, connect the analyzer via USB to the computer and launch USBee Suite (I omit the process of installing drivers and software for the logic analyzer, this is beyond the scope of the article). In the settings for Speed and Samples, set the following parameters: Sample Rate - 2Msps, Buffer Size - 10M samples. This will be enough to capture an exchange frame of 5 seconds.
My further actions are as follows: I do a single capture (Capture Once button) and immediately turn on the player. As soon as the first information appears on the screen, I turn off the capture. After doing such actions for various options of the displayed text, I began to analyze the information received.
So, what are the patterns in all the premises of the teams. Firstly, what immediately caught my eye was the frequency of sending a command with code 0x42 and three empty bytes after it. The team has a strict frequency of occurrence even when the video player is at rest. I suggested that this is a keyboard polling command, it is very easy to check the theory, I hold down any button on the player and capture the frame in the program.
Immediately after byte 0x42 there was not a zero byte, but before it was 0! Thus, 0x42 is a keyboard polling command that is sent by the main board controller to the display controller and, in response to this command, the display controller responds with the key code pressed (or 0 if none are pressed).
The next in line was the team with the code 0x40, it appeared only when information was displayed on the screen. Two bytes always follow it, the first byte always starts with 0xCX, where X is the changing value. The third byte has arbitrary values, but the main thing is that the first time it is turned on, it is always 0
In this screenshot you can see that the second byte grows with each sending of the 0x40 group, most likely it sets the address of the character and the value of the character is written in the third byte. In this case, 0 clears the character. Always after sending all groups with the code 0x40, two bytes 0x02 0x8F will follow.
Well, the last group of data is a command starting with code 0x41 and the subsequent zero byte after it. The command appears only the first time you turn on the video player.
Data analysis has drawn a more or less clear picture of the controller, it remains only to check everything in practice. I used the LPCXpresso debugging handkerchief, which I received "free" in the framework of some competition from NXP. The scarf is equipped with a simple 32-bit controller LPC1114. Because Since the controller is powered by a voltage of 3.3V, and the display controller D16312 is from 5 volts, I did not dare to connect their outputs directly. It is possible that the microcontroller outputs are tolerant to 5V, but I had a 3.3-5V matching board and I used it.
So, from the connector that goes to the main board, we remove 3 wires that are responsible for the pins Data, Cs and Clk, respectively, pins 1, 2 and 3. We solder the wires to the connection points of Data, Clk and Cs to the board of the display controller, and we also need conclusions GND and + 5V VCC. For the first time, I connected the three pins extracted from the main board with a logic analyzer to debug the transmitted information.
This is what my debugging scarf looks like
All the iron part is ready, it's time to start the software. To begin with, we will write a program that will repeat the data obtained during the analysis. LPCXpresso uses the eponymous LPCXpresso IDE based on Eclipse. We start, specify the path to our new workspace and import 2 standard libraries CMSIS_CORE_LPC11xx and LPC11xx_cmsis2_Lib into it, we will need them for development. Next, create a new project File-> New-> C / C ++ -> LPCXpresso C Project then LPC11 / LPC12 -> LPC11xx / ... -> C Project. We set the project name and in the next window select the target controller, in my case it is LPC1114 / 302. At the next step, the library CMSIS_CORE_LPC11xx should already appear in the list, because we imported it earlier. We do not change anything in the DSP library window and in the next step leave everything by default, click Finish.
Add the following code to the generated file <project name> .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(0x40, 1);
BeginCommand(0xC0, 0);
WriteData(0xFF);
EndCommand();
BeginCommand(0x02, 1);
BeginCommand(0x8F, 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;
}
}
Here we see several functions for working with a serial port. At the very top of the file, port and pin settings for data, synchronization and strobe are set. In my case, everything hangs on port 3, pin 2 is responsible for the Data signal, 0 - synchronization and 1 - gating.
The BeginCommand function sends a command to the port, this function has a second interesting parameter. If you look closely at the graphs of the signals above, you can see that the gating signal is set to the active (low) level, before sending data, and changes between two independent commands. But it does not switch if data should be transmitted after sending the command
So, the second parameter says that the command is atomic if isSingle == 1. For the case of non-atomic commands, the EndCommand function is intended, which should be called after sending the data.
Sending data is performed in the WriteData function, in turn, bit by bit, starting from the lowest, we transmit information to the Data pin. Each data setting is accompanied by a synchronization signal, Clock function generates it.
In the main function, we first initialize the work with I / O inputs, then set the Data, Clk, and Cs pins to the output. In an endless loop, we simulate the commands that were received earlier at the data analysis stage. T.O. we sent a sequence of data 0x40 0xC0 0xFF 0x02 0x8F to the display controller, and the controller responded with _8
T.O. it was found that the command:
- 0x42 - is responsible for processing the keyboard. After sending it to the display controller, you need to set the Data port to the input and read the data from it, previously synchronizing with the signal Clk
- 0x40 - the command to write data to the display, after it the command for specifying the address of the symbol should be written, and after specifying the address the data
is written - 0x41 - this a command for controlling the LED, after it there is a byte with the data which LED should be turned on or not
. You can find more detailed information in the manual for this D16312 display controller, it describes how they should be formed to Omands. Well, I designed all the work in a small library that lies on the github. The library allows you to display text, control special characters, fill in the drive symbol in percentage terms, read the keyboard, change the display brightness and control the LED. That's all, I hope someone reading this will be useful and interesting. And for starters I will leave a video demonstrating the library