Summary of Mike’s Electric Stuff Blog Pages

Ref. Mike’s Electric Stuff who did all the hard work. The display is a LH154Q01 (datasheet attached for posterity).

Connector

The connector is a Molex 503548-1220 (DigiKey WM4662). The connector on the display is a Molex 503552-1220.

Pinout

(picture courtesy Mike’s page)

Pin Description Notes
1 CLK+ MIPI Clock
2 VDD 3.3V display power
3 CLK- MIPI Clock
4 VEE 1.8V display power
5 GND
6 VSYNC# Vertical sync from display. 1.8V. 530us active low.
7 D+ MIPI Data
8 RST# 1.8V signal
9 D- MIPI Data
10 LED+ Backlight power
11 GND
12 LED- Backlight power

The backlight is 4 LEDs in series. Mike mentions a Vf of 15V 16mA typical, with a range of 15-17V with current 10-25mA over that range.

Interface Hardware

Mike used a MachXO2 to drive the MIPI lines. He uses this connection:

Quoting him:

This is the hardware configuration I used to drive the display from a Lattice MACHXO2 FPGA.. The 1K pullup on DN was a last-minute fix when I found some displays didn’t work - not looked in detail at the exact effect - could just have been ground shift pulling the line slightly negative without it.
Note I didn’t use the clock output from the DDR IO cell, as this shares tristate control with the data line, so you can’t tristate the data bus without also tristating the clock.

The sequence to enter high speed mode on the data lines (Clock is same as data) is as follows :
LVDS tristated,DN bias and DP bias high
DP bias low
DN bias low
LVDS output enable, output low (DP=0,DN=1)

Initialization Sequence

Mike doesn’t post the display datasheet and I can’t find it, so I’m just copying the fragment he posted.

From his page:

I reduced the 250ms initial delay to 15ms with no obvious problems. I also tried not delaying the 3.0V supply turn-on, which also appeared to work OK. The delay between reset and clock start sequence (see below) IS needed. I tested some shorter timings, and problems started occurring if the delay between the reset rising edge and clock start went below about 64us.

So the minimum initialization would seem to be: +3V and +1.8V on, reset low, wait >100us, reset high, wait >100us, clock start sequence.

The clock start sequence looks like this:

I think he’s drawing attention to the fact that you need to drive both differential lines low for about 100ns to signal to the display that it should wake up/enable termination, and after that you can start driving the differential clock as you’d expect.

Data Transmission

The display uses the 1-lane MIPI DSI protocol. The specification isn’t available to the public but some judicious googling found it. Attached here for posterity.

Entry Into High-Speed Mode:

Start of Command Sequence

End of Command Sequence

Mike mentions that a data line transition is required at the end of a packet. The last byte is a CRC but it is ignored, so a simple way to create this transition is to set the second CRC byte to 0x00 and add an extra 0xff byte (which is not part of the command byte, so it does not count as part of the payload when calculating packet length).

Commands

I’ve attached a copy of the DSC spec here as well. Normal text is the DSI command, bold is the DSC command, and italics are the ECC byte.

Command Description
15 11 00 25 Exit sleep mode. datasheet reccommends waiting >120mS after this command. I suspect this is just for display voltages to settle, to avoid visible effects
15 29 00 0F Display on
15 3A 55 02 Set 16 bits per pixel mode
15 3A 77 1F Set 24 bits per pixel mode
15 36 80 12 RGB order (for 16bpp), display bottom to top (same as AVI)
15 36 88 2A BGR order (for 24bpp AVI), display bottom to top (as AVI)
Other bits for command 36:
0: Flip vertical (seems to have no effect)
1: Flip horizontal (seems to have no effect)
2: Vertical line data latch order (seems to have no effect)
3: 0: RGB, 1: BGR
4: Display refresh order, 1 = bottom to top
5: rotate 90 deg (affects order of next display data, not current display)
6: Flip column (affects order of next display data, not current display)
7: flip row (affects order of next display data, not current display)
Bits 0 to 2 may affect the display refresh order, and so could be relevant for video applications to avoid tearing effects
Command Description
39 E1 01 0B ss <0x1E0 bytes> 00 00 Write line of 16bpp display data (480 bytes)
39 D1 02 07 ss <0x2D0 bytes> 00 00 Write line of 24bpp display data (720 bytes)
ss=0x2C for first line, 0x3C for subsequent lines
00 00 is dummy CRC - this is not checked
See this list for command strings with & ECC bytes to send multiple lines

The iPod sends these commands before each frame, but it doesn’t appear necessary:

Command Description
29 05 00 25 2A 00 00 00 EF 48 03 Set column address. First byte pair (MS first) is the start column, second pair the end column (0 to 239 here). 48 03 is the CRC
This command should be useable to write smaller windows of the display, and would be handy for font rendering by setting the window size to the font size
I couldn’t get this command to work for sub-fullscreen sizes... either it is not implemented, or it checks the CRC
29 05 00 25 2b 00 00 00 EF 0C 08 Set line address, as above - also couldn’t get it to work
05 2C 00 25 Write memory start. I think this is duplicated by the 2C in the first line of the image data

Multiple Line Commands

List of commands with ECC for different numbers of lines (in 16bpp mode). Lines (bytes = lines*480+1) : write command including ECC

01 : 39 E1 01 0B 33 : 39 E1 3D 37 65 : 39 E1 79 34 97 : 39 E1 B5 34
02 : 39 C1 03 10 34 : 39 C1 3F 2C 66 : 39 C1 7B 2F 98 : 39 C1 B7 2F
03 : 39 A1 05 15 35 : 39 A1 41 16 67 : 39 A1 7D 2A 99 : 39 A1 B9 12
04 : 39 81 07 0E 36 : 39 81 43 0D 68 : 39 81 7F 31 100 : 39 81 BB 09
05 : 39 61 09 1F 37 : 39 61 45 24 69 : 39 61 81 1C 101 : 39 61 BD 20
06 : 39 41 0B 04 38 : 39 41 47 3F 70 : 39 41 83 07 102 : 39 41 BF 3B
07 : 39 21 0D 01 39 : 39 21 49 02 71 : 39 21 85 02 103 : 39 21 C1 01
08 : 39 01 0F 1A 40 : 39 01 4B 19 72 : 39 01 87 19 104 : 39 01 C3 1A
09 : 39 E1 10 25 41 : 39 E1 4C 01 73 : 39 E1 88 39 105 : 39 E1 C4 02
10 : 39 C1 12 3E 42 : 39 C1 4E 1A 74 : 39 C1 8A 22 106 : 39 C1 C6 19
11 : 39 A1 14 3B 43 : 39 A1 50 38 75 : 39 A1 8C 27 107 : 39 A1 C8 24
12 : 39 81 16 20 44 : 39 81 52 23 76 : 39 81 8E 3C 108 : 39 81 CA 3F
13 : 39 61 18 31 45 : 39 61 54 0A 77 : 39 61 90 32 109 : 39 61 CC 16
14 : 39 41 1A 2A 46 : 39 41 56 11 78 : 39 41 92 29 110 : 39 41 CE 0D
15 : 39 21 1C 2F 47 : 39 21 58 2C 79 : 39 21 94 2C 111 : 39 21 D0 2F
16 : 39 01 1E 34 48 : 39 01 5A 37 80 : 39 01 96 37 112 : 39 01 D2 34
17 : 39 E1 1F 2A 49 : 39 E1 5B 29 81 : 39 E1 97 29 113 : 39 E1 D3 2A
18 : 39 C1 21 0D 50 : 39 C1 5D 06 82 : 39 C1 99 3E 114 : 39 C1 D5 05
19 : 39 A1 23 3C 51 : 39 A1 5F 37 83 : 39 A1 9B 0F 115 : 39 A1 D7 34
20 : 39 81 25 13 52 : 39 81 61 10 84 : 39 81 9D 20 116 : 39 81 D9 23
21 : 39 61 27 0E 53 : 39 61 63 0D 85 : 39 61 9F 3D 117 : 39 61 DB 3E
22 : 39 41 29 19 54 : 39 41 65 22 86 : 39 41 A1 1A 118 : 39 41 DD 11
23 : 39 21 2B 28 55 : 39 21 67 13 87 : 39 21 A3 2B 119 : 39 21 DF 20
24 : 39 01 2D 07 56 : 39 01 69 04 88 : 39 01 A5 04 120 : 39 01 E1 07
25 : 39 E1 2E 2B 57 : 39 E1 6A 28 89 : 39 E1 A6 28 121 : 39 E1 E2 2B
26 : 39 C1 30 23 58 : 39 C1 6C 07 90 : 39 C1 A8 3F 122 : 39 C1 E4 04
27 : 39 A1 32 12 59 : 39 A1 6E 36 91 : 39 A1 AA 0E 123 : 39 A1 E6 35
28 : 39 81 34 3D 60 : 39 81 70 3E 92 : 39 81 AC 21 124 : 39 81 E8 22
29 : 39 61 36 20 61 : 39 61 72 23 93 : 39 61 AE 3C 125 : 39 61 EA 3F
30 : 39 41 38 37 62 : 39 41 74 0C 94 : 39 41 B0 34 126 : 39 41 EC 10
31 : 39 21 3A 06 63 : 39 21 76 3D 95 : 39 21 B2 05 127 : 39 21 EE 21
32 : 39 01 3C 29 64 : 39 01 78 2A 96 : 39 01 B4 2A 128 : 39 01 F0 29

MIPI DSI Crash Course

MIPI has short and long packets.

Short Packet Format

SoT is start of data transmission shown above + the sync byte.
EoT is end of transmission back to low power mode.

The ECC is calculated as follows:

b7 = 0 
b6 = 0
b5 = D10 ^ D11 ^ D12 ^ D13 ^ D14 ^ D15 ^ D16 ^ D17 ^ D18 ^ D19 ^ D21 ^ D22 ^ D23 
b4 = D4 ^ D5 ^ D6 ^ D7 ^ D8 ^ D9 ^ D16 ^ D17 ^ D18 ^ D19 ^ D20 ^ D22 ^ D23 
b3 = D1 ^ D2 ^ D3 ^ D7 ^ D8 ^ D9 ^ D13 ^ D14 ^ D15 ^ D19 ^ D20 ^ D21 ^ D23 
b2 = D0 ^ D2 ^ D3 ^ D5 ^ D6 ^ D9 ^ D11 ^ D12 ^ D15 ^ D18 ^ D20 ^ D21 ^ D22 
b1 = D0 ^ D1 ^ D3 ^ D4 ^ D6 ^ D8 ^ D10 ^ D12 ^ D14 ^ D17 ^ D20 ^ D21 ^ D22 ^ D23
b0 = D0 ^ D1 ^ D2 ^ D4 ^ D5 ^ D7 ^ D10 ^ D11 ^ D13 ^ D16 ^ D20 ^ D21 ^ D22 ^ D23

Here’s some C code from Mike’s page to generate it and send commands:

// calc XOR parity bit of all '1's in 24 bit value
uint8_t parity(uint32_t val)
{
    uint32_t i, p;
    for (i=0,p=0; i != 24; i++) {
        p ^=val;
        val >>= 1;
    }

    return p & 1;
}

// add ecc byte to b29-24 of 24 bit command packet (lsbyte first)
uint32_t add_ecc(uint32_t cmd)
{
    cmd &= 0x00ffffff; 

    if (parity(cmd & 0b111100010010110010110111)) cmd |= 0x01000000; 
    if (parity(cmd & 0b111100100101010101011011)) cmd |= 0x02000000;
    if (parity(cmd & 0b011101001001101001101101)) cmd |= 0x04000000;
    if (parity(cmd & 0b101110001110001110001110)) cmd |= 0x08000000;
    if (parity(cmd & 0b110111110000001111110000)) cmd |= 0x10000000;
    if (parity(cmd & 0b111011111111110000000000)) cmd |= 0x20000000;

    return cmd;
}

Long Packet Format

The 16-bit checksum is actually a CRC and this display ignores it anyway, so we don’t have to calculate it. The ECC calculation is the same as in the short packet.

Sending Data

The init data can be sent with the line data. Some pseudocode:

uint8_t cmds[] = {
    0xB8,                        /* sync */
    0x15, 0x29, 0x00, 0x0f,            /* display on */
    0x15, 0x11, 0x00, 0x25,            /* exit sleep */
    0x15, 0x36, 0x80, 0x12,            /* row ordering */
    0x15, 0x3a, 0x55, 0x02,            /* 16bpp */
    0x39, 0xe1, 0x01, 0x0B };        /* write data */
uint8_t crc[] = { 0x00, 0x00 };            /* checksum (ignored anyway) */

for (y=0; y<240; y++) {
    write(cmds, sizeof(cmds));
    write( (y == 0) ? 0x2c : 0x3c, 1);    /* ss */
    write(linedata, 480);        /* 1 line of 16bpp is 480 bytes */
    write(checksum, 2);
    write(0xff, 1);            /* end of data transition */
    }

From Mike’s page:

It is possible to send more than one line per command (tested with 2 lines, couldn’t do more due to FPGA buffer size limit), but there is a 64K limit imposed by the 16 bit payload length field. As multiple commands can be sent in one packet, it should be possible to send a whole frame in one packet, i.e. only entering high-speed mode at the start and exiting at the end. It may also be possible to just stay in high speed mode all the time, which would be useful if the clock doesn’t mind being stopped between bytes, as you wouldn’t need to do the enter/exit high-speed mode every time.

Content Creation

Mike has some excellent information on this. I’ve made local copies of his VirtuaDub filter and AVI parser, but he’s got more information on his site.

picture359-1.png (71.3 KB) Andrew Kohlsmith, 02/15/2014 11:46 AM

picture804-1.png (36.2 KB) Andrew Kohlsmith, 02/15/2014 11:52 AM

picture252-1.png (254 KB) Andrew Kohlsmith, 02/15/2014 11:59 AM

picture146-1.png (419 KB) Andrew Kohlsmith, 02/15/2014 12:03 PM

picture146-2.png (58.9 KB) Andrew Kohlsmith, 02/15/2014 12:03 PM

picture146-3.png (66.2 KB) Andrew Kohlsmith, 02/15/2014 12:03 PM

picture171-1.png (147 KB) Andrew Kohlsmith, 02/15/2014 12:07 PM

picture347-1.png (58.4 KB) Andrew Kohlsmith, 02/15/2014 01:01 PM

picture347-2.png (189 KB) Andrew Kohlsmith, 02/15/2014 01:01 PM

ErrDiffFilter.vdf - error diffusion filter for VirtuaDub (71.5 KB) Andrew Kohlsmith, 02/15/2014 01:11 PM

aviparse.c Magnifier - Microchip C AVI file parser source (2.74 KB) Andrew Kohlsmith, 02/15/2014 01:23 PM

MIPI_DSI_Specification_v1b_8320061508.pdf (1.26 MB) Andrew Kohlsmith, 02/15/2014 01:29 PM

MIPI_DCS_Specification__v1.01.00.pdf (952 KB) Andrew Kohlsmith, 02/15/2014 02:20 PM

LH154Q01-TD01.pdf (983 KB) Andrew Kohlsmith, 02/15/2014 03:26 PM

Add picture from clipboard (Maximum size: 1 GB)