https://www.electricstuff.co.uk/nanohack.html
Tour : click picture Back to Mike's Click picture below for
below for previous Electric Stuff next
---------------------------------------------------------------------
img_5034.jpg (583324 bytes)
Ipod Nano V6 LCD hacking
See these videos for full info
Updated May 6 2013 - added FPGA circuitry, fixed typos.
June 29 - fixed wrong CLKP/CLKN labels in clock startup sequence
Links to MIPI documents : D-PHY (Physical low-level info) DSI
specification (Command Protocol) DCS specification (LCD commands)
Connector pinout. Mating connector is Molex 503548-1220. (Connector
on display is 503552-1220)
View looking at rear of mated connector
wpe71.jpg (6768 bytes)
+-------------------------------------------------------------------+
|Pin |Name |Direction|Function |
|------+-------+---------+------------------------------------------|
|1 |CLKP |To |Positive clock |
| | |display | |
|------+-------+---------+------------------------------------------|
| | | |+3.0V (2.9 to 3.1V, abs max 4.0) Current |
| | | |draw 3.5mA. Slightly content dependent. |
| | | |Appears to work OK at 3.3v. Display starts|
|2 |Vdd |Power |washing out below 2.6v. Voltage has no |
| | | |obvious effect on brightness above 3.0v. I|
| | | |ran it up to 4.1V with no increase in |
| | | |current draw. |
|------+-------+---------+------------------------------------------|
|3 |CLKN |To |Negative clock |
| | |display | |
|------+-------+---------+------------------------------------------|
| | | |+1.8v (1.7 to 1.9V, abs max 4.0) |
|4 |Vee |Power |1.8mA 48MHz clock, idle. 2mA updating at |
| | | |15fps. Likely to be fairly clock-rate |
| | | |dependent. |
|------+-------+---------+------------------------------------------|
|5 |GND | |0V |
|------+-------+---------+------------------------------------------|
|6 |SYNC |From |Vertical sync.Goes low for 530uS at 60Hz. |
| | |display |1.8V level |
|------+-------+---------+------------------------------------------|
|7 |DP |To |Positive data |
| | |display | |
|------+-------+---------+------------------------------------------|
|8 |Reset |To |Active low reset, 1.8v level. No internal |
| | |display |pullup. |
|------+-------+---------+------------------------------------------|
|9 |DN |To |Negative data |
| | |display | |
|------+-------+---------+------------------------------------------|
| | | |Backlight +ve. Backlight is 4 LEDs in |
| | | |series. Typical Vf 14.8V at 16mA |
| | | |Backlight current is specified at typ |
|10 |LED+ |Power |10.5mA, max 25mA, Vf 15.5-17.0 max over |
| | | |this range. |
| | | |Derate 25 to 5mA over 40 to 80 deg.C |
| | | |ambient temp. |
|------+-------+---------+------------------------------------------|
|11 |GND | |0V |
|------+-------+---------+------------------------------------------|
|12 |LED- | |Backlight -ve. |
|------+-------+---------+------------------------------------------|
|Shield| | |The four corner mechanical pads on the |
| | | |connector are connected to GND |
+-------------------------------------------------------------------+
This is the initialisation sequence from the datasheet.
wpe5D.jpg (9552 bytes)
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 mimimum intialiation would seem to be : +3v and +1.8v on,
reset low, Wait >100uS, Reset High, Wait >100uS, Clock start
sequence
I couldn't easily test any constraints on powerup to reset-high time
due to FPGA configuration time.
Clock startup sequence
At startup, the following sequence is needed to enable the clock
receiver. This is the same as the sequence for the data bus.
On the ipod, the clock runs continuously - I don't know if this is
necessary - it may be possible to disable the clock between commands,
or even during a command between btyes - to be tested.
Non-continuous clock is an optional feature of the DSI spec. However
it did work when the clock period varied from cycle to cycle when I
was testing frequency range, so clock gaps between bytes. I've tested
at clock rates down to 2MHz. Issues below that may have been due to
my test setup.
In the ipod, the clock runs at 110MHz.
The framerate is NOT derived from this clock, but from an internal
oscillator.
The absolute polarity of the LVDS clock does matter - the sync byte
will not correct for wrong clock phasing.
wpe7A.jpg (71399 bytes)
Data packet
A packet can contain one of more commands, Only one start byte (0xB8)
is required to start the packet ( i.e. not between commands). I don't
know if gaps are allowed between commands within a packet - if they
are, they may need a new start byte.
The data lines must stay below 0.5V during high speed (LVDS) mode.
LVDS swing is typically +/-100mV relative to a +200mV baseline.
I have found that a small positive bias was needed on the DN line on
some displays - not investigated fully.
From MIPI D-PHY spec :
dphy.png (30348 bytes)
Data start - entry to high speed mode
datstart.png (18837 bytes)
Start byte sequence
datastart.png (22996 bytes)
End of data packet, exit from high-speed mode
A data line transition is needed at the end of the last byte. As the
last byte will be CRC, which is ignored, a simple way to achive this
would be to set the second CRC byte to 0x00 and send an extra 0xFF
byte (not included in the payload byte count) to generate this
transition.
datend.png (29439 bytes)
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)
wpe5E.jpg (93998 bytes)
Using non-differential signals
I have done a quick test to confirm that it does work with
non-differential signals, i.e. holding one of the DP/DN and CLKN/CLKP
lines to a fixed level. I haven't done any optimisation on component
values yet.
Command protocol
Two commands are required to initialise the display - exit sleep and
display on. They can be in either order. After this, sync pulses will
start and the display will show random garbage unless previously sent
a frame of image data.
+-------------------------------------------------------------------+
| |Italics are the ECC byte. First byte is DSI |
|Command. Hex. |command, listed in DSI spec |
| |Values in bold are DCS commands interpreted by |
| |the LCD controller, liste in the DCS spec. |
|-------------------+-----------------------------------------------|
| |Exit sleep mode. datasheet reccommends waiting |
|15 11 00 25 |>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 : |
| |Bit 0 : Flip vertical (seems to have no effect)|
| |Bit 1 : Flip horizontal (seems to have no |
| |effect) |
| |Bit 2 : Vertical line data latch order (seems |
| |to have no effect) |
| |Bit 3 : 0: RGB,. 1: BGR |
| |Bit 4 : Display refresh order, 1 = bottom to |
| |top |
| |Bit 5 : rotate 90 deg (affects order of next |
| |display data, not current display) |
| |Bit 6 : Flip column (affects order of next |
| |display data, not current display) |
| |Bit 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. |
|-------------------+-----------------------------------------------|
| |Write line of 16bpp display data (480 decimal |
| |bytes). |
| |ss=0x2C for first line, 0x3C for subsequent |
|39 E1 01 0B ss < |lines |
|1E0 bytes > 00 00 |00 00 is dummy CRC - this is not checked |
| | |
| |See this list for command strings with & ECC |
| |bytes to send multiple lines |
|-------------------+-----------------------------------------------|
| |Write line of 24bpp display data (720 decimal |
|39 D1 02 07 ss < |bytes). |
|2D0 bytes > 00 00 |ss=0x2C for first line, 0x3C for subsequent |
| |lines |
|-------------------+-----------------------------------------------|
| | |
|-------------------------------------------------------------------|
|The ipod sends the following commands prior to each frame, but they|
|don't appear to be necessary |
|-------------------------------------------------------------------|
| |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|
|29 05 00 25 2A 00 |windows of the display, and would be handy for |
|00 00 EF 48 03 |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 |Set line address, as above - also couldn't get |
|00 00 EF 0C 08 |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 |
+-------------------------------------------------------------------+
The initialisation data can be merged with display data, which is
useful if data is coming from a preformatted source where commands
can be embedded along with teh display data, e.g. FPGA playing back
from SPI flash.
So the full sequence to initialise and send a frame of 16bpp data
would be :
+-----------------------------------------------------------------+
|for y = 0 to 239 |
| |
| |
| |
|send B8 15 29 00 0F 15 11 00 25 15 36 80 12 15 3A 55 02 39 E1 01 |
|0B |
|; Start| DispOn |Exitsleep | Row order | 16bpp | Write |
|data |
| |
|if y=0 then send 2C else send 3C |
| |
|Send 1E0 (=480 dec) bytes of pixel data |
|Send 00 00 FF ; dummy checksum plus data-end transition |
| |
| |
| |
|next y |
+-----------------------------------------------------------------+
It is possible to send more than one line per command (tested with 2
lines, coudn'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 . *To be tested*
Packet formats from MIPI DSI spec
SoT is the entry to high-speed mode, plue the 0xB8 start byte. EoT is
a transition after the last bit, then exit from high-speed mode.
Short packet
shortpkt.png (51851 bytes)
Long packet
The checksum (actually a CRC) is ignored by the display so does not
need to be calculated.
longpkt.png (99337 bytes)
ECC calculation
ECC values are listed for the basic comamnds in the table above. For
others, they can be calculated as follows
The ECC byte comprises six parity bits calculated from various bits
in the header as follows :
P7=0
P6=0
P5=D10^D11^D12^D13^D14^D15^D16^D17^D18^D19^D21^D22^D23
P4=D4^D5^D6^D7^D8^D9^D16^D17^D18^D19^D20^D22^D23
P3=D1^D2^D3^D7^D8^D9^D13^D14^D15^D19^D20^D21^D23
P2=D0^D2^D3^D5^D6^D9^D11^D12^D15^D18^D20^D21^D22
P1=D0^D1^D3^D4^D6^D8^D10^D12^D14^D17^D20^D21^D22^D23
P0=D0^D1^D2^D4^D5^D7^D10^D11^D13^D16^D20^D21^D22^D23
Here is some Visual Basic code (sorry!) to calculate the ECC.
It takes a 3 byte string of binary values for the packet header, and
returns a 4 byte string including the ECC
+-------------------------------------------------------------+
|Dim eccreg As Long |
| |
|Private Function add_ecc(s As String) As String |
|Dim p0 As Byte |
|eccreg = Asc(Mid$(s, 1, 1)) Or (CLng(Asc(Mid$(s, 2, 1))) * |
|256) Or (CLng(Asc(Mid$(s, 3, 1))) * 65536) |
|p0 = 0 |
|If ((gb(10) + gb(11) + gb(12) + gb(13) + gb(14) + gb(15) + gb|
|(16) + gb(17) + gb(18) + gb(19) + gb(21) + gb(22) + gb(23)) |
|And 1) = 1 Then p0 = p0 Or 32 |
|If ((gb(4) + gb(5) + gb(6) + gb(7) + gb(8) + gb(9) + gb(16) +|
|gb(17) + gb(18) + gb(19) + gb(20) + gb(22) + gb(23)) And 1) =|
|1 Then p0 = p0 Or 16 |
|If ((gb(1) + gb(2) + gb(3) + gb(7) + gb(8) + gb(9) + gb(13) +|
|gb(14) + gb(15) + gb(19) + gb(20) + gb(21) + gb(23)) And 1) =|
|1 Then p0 = p0 Or 8 |
|If ((gb(0) + gb(2) + gb(3) + gb(5) + gb(6) + gb(9) + gb(11) +|
|gb(12) + gb(15) + gb(18) + gb(20) + gb(21) + gb(22)) And 1) =|
|1 Then p0 = p0 Or 4 |
|If ((gb(0) + gb(1) + gb(3) + gb(4) + gb(6) + gb(8) + gb(10) +|
|gb(12) + gb(14) + gb(17) + gb(20) + gb(21) + gb(22) + gb(23))|
|And 1) = 1 Then p0 = p0 Or 2 |
|If ((gb(0) + gb(1) + gb(2) + gb(4) + gb(5) + gb(7) + gb(10) +|
|gb(11) + gb(13) + gb(16) + gb(20) + gb(21) + gb(22) + gb(23))|
|And 1) = 1 Then p0 = p0 Or 1 |
|add_ecc = s + Chr$(p0) |
|End Function |
| |
|Function gb(bit As Long) |
|gb = (eccreg \ (2 ^ bit)) And 1 |
|End Function |
+-------------------------------------------------------------+
Here is some C code
+-------------------------------------------------------------------+
|char parity (unsigned long val) |
|{ // calc XOR parity bit of all '1's in 24 bit value |
|unsigned long i,p; |
|p=0;for(i=0;i!=24;i++) {p^=val;val>>=1;} |
|return(p&1); |
|} |
| |
|unsigned long add_ecc(unsigned long cmd) |
|{// add ecc byte to b29-24 of 24 bit command packet (lsbyte first) |
|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); |
|} |
| |
| |
|void sendlcdcmd(unsigned long l) |
|{ |
|// send command, sends MSByte first for readability |
|// e.g. exit sleep 0x151100 |
|l=((l&0xff)<<16) | (l&0xff00) | ((l&0xff0000)>>16); // reverse |
|bytes to lsbyte first order |
|l=add_ecc(l); |
|sendpmp(l);sendpmp(l>>8);sendpmp(l>>16);sendpmp(l>>24); |
|} |
+-------------------------------------------------------------------+
Content creation
I highly reccommend Virtualdub for creating uncompressed AVIs for
playback. It supports oddball output resolutions, and nonstandard
formats like 1bppp mono and 16 bit RGB.
Virtualdub's 16 bit 565 format matches the input format for this LCD.
24 bit RGB matches the LCD if the BGR order bit is set to 1.
To improve the quality of images displayed as 16bpp, I have written
this filter for virtualdub, which uses dithering to improve
greyscaling, avoiding banding on colour gradients etc.
To format an arbitary video file :
Load file into virtualdub (accepts AVI,MPEG and bitmap sequences)
Video->filters->Add resize, set to 240x240 pixels.
Video->Colour Depth->16 bit RGB (565)
Audio -> No audio
Video->Compression->No compression
Set in and out markers if you only want a section of the file
File->Save as AVI
Parsing AVI files
Uncompressed AVIs are fairly easy to handle - after parsing the
header, you get packed frames of data, with an 8 byte header between
each frame. The frames are in a format suitable for sending directly
to the display with no further processing.
Here is some sample code in C and VB6
Dimensions
wpe79.jpg (12014 bytes)
List of commands with ECC for different numbers of lines (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 |
+-------------------------------------------------------------------------------------------------------------------------+
img_5034.jpg (583324 bytes)
---------------------------------------------------------------------
Click picture above for Back to Mike's Click picture above
Previous Electric Stuff for next