-
Notifications
You must be signed in to change notification settings - Fork 0
/
UseThatOldTFT.txt
643 lines (549 loc) · 35.6 KB
/
UseThatOldTFT.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
Use that oldish LCD!
NOTE: This was last updated over a month before I posted it here. It's
mostly just a brain-dump of LCD concepts and vague ideas as to how to make
use of them with a cheap 8-bit microcontroller. It's probably not well
organized, nor complete. It also goes way too deep into possibilities as
opposed to how I actually implemented it. I guess this document is just to
inspire experimentation. Much of the useful implementation-information
(schematics, timing diagrams) is well-documented in my code, which I
eventually plan to make available online, along with video of my setup in
action. If you find this "document" useful, and wish for more information,
I'll be happy to hear it and be more likely to post the rest of my material
sooner.
The main point of this particular document is this: You (generally) don't
have to match, or even come close to, the documented timing specifications
for a particular TFT... From experience, it's definitely possible to put an
image on a 1024x768 laptop display directly from an 8-bit microcontroller
running at 16MHz (though maybe not *every* display matching these specs,
best to have a couple to experiment with). Experiment!
CopyRote Notice: This is my work. This knowledge came from a LOT of rote
head-banging and a bit of research. It was written from my own head, except
where otherwise quoted and linked. If you wish to reproduce any part of it
PLEASE ask permission first. This is just common human decency. I shouldn't
have to make it more legally-clear than that. At the very least, it would
be nice to know someone found it worthwhile.
Who's my intended audience? I don't really know... I hope someone can make
use of it. I'm hoping my work won't be stolen verbatim for a school
project, but we all understand that there's no such thing as any project
that isn't based on others' work, and, in this era, everyone's work can be
made available to everyone... I hope this doesn't contain too much jargon
to make it difficult for anyone with a basic understanding of digital
electronics and microcontrollers (e.g. Arduino).
These days, old laptop LCDs are pretty easy to come by. Buy one off ebay
for $10-$15, or find one in an old laptop which's been, quite literally,
pho'd.
Was a time replacing that display would cost upwards of $200, even for a
used one, but these days, I guess, there're a lot of pho'd laptops. Also,
we have the benefit that they've standardized things, and improved the
technology, quite a bit since the last time I tried to use an old (386)
laptop display in a project.
I won't go into too much history here, except to say that life is
significantly easier now that TFT's are on the junk pile. Or at least, it's
significantly easier to give new life to that old TFT than it would be for
a 486-era display.
A few things, before I begin with the technicalities: I have experimented
with exactly *3* old TFT displays. Two worked with my methods described
here, one didn't. I still have some hope for that display, but not as much
drive. (Maybe someone here can figure it out?). I've pieced together this
"document" from experimentation, lots of experimentation, as well as
piecing together quite a bit from data-sheets and other specifications as a
starting-point. I have no training, nor professional experience, with LCDs,
so take *everything* said here with that in mind. I will also throw in this
IMPORTANT BIT: Apparently most of these displays are SIGNIFICANTLY more
flexible than their documentation claims. One additional thing: If you just
want to use your old laptop display as an additional DVI or VGA display for
your desktop computer, then you're in a tremendous amount of luck... these
days that conversion circuitry can be purchased off ebay for less than $50
(and you needn't read on).
Notes on Types of LCDs:
I'm not an expert, so don't quote me on these details...
TFT (Thin-Film-Transistor)
displays are pretty much the only ones discussed here, they are pretty much
the standard for many years now. Each pixel is backed by a transistor (and
capacitor?) that holds its value until the next refresh. Most of these
displays have very similar timing requirements, signal inputs, and by now
even similar pinouts.
[D]STN ([Double] Super-Twist-Som'n)
displays are the old style (grayscale and color displays from before and
during the 486 era). They couldn't hold an image for long. So, to prevent
noticeable flicker as best they could by increasing refresh-rate, they
often split the screen in half and wrote to the top half and the bottom
half at the same time. Realistically, I think they use roughly the same
signals as TFT, and very similar timing (besides the split-screen thing),
but they're not particularly easy to interface in any standard way. Pinouts
were as varying as the devices themselves. Compatibility between laptops of
different models was almost ziltch.
The rest refers to TFT displays, specifically:
Signals:
Pixel Clock - usually a constant frequency, never disabled even when not
actively "drawing." Generally all the other signals are loaded/latched once
per pixel clock. See notes re: pixel clock, below.
Hsync - "Horizontal Sync" Tells the display it's time to start a new row.
Usually a very brief pulse (on the order of 20 pixel clocks) immediately
before pixel data is drawn
Vsync - "Vertical Sync" Tells the display it's time to start a new frame.
Usually several hsyncs long (thus 1000's of pixel clocks).
DE - "Data Enable" When this is active, pixel data is drawn to the display
one (or two) pixels per pixel clock. Thus, if the screen is 1024x768, this
will be active for *exactly* 1024 pixel clocks. (See notes re DE below)
Color data - usually 6bits per color (Red, Green, and Blue). This data is
completely neglected if DE is inactive. If all those wires are too daunting
just tie them all together on a single uC pin! You'll get black-and-white.
Or break them apart by color and tie all of the same color bits together
and you'll only need three pins and get 8 colors.
Notes Re Pixel Clock:
Some displays may actually require this to be constantly-running as long as
the display is powered-up. Some displays (especially LVDS) may not actually
look at the pixel clock itself for drawing pixels... This should be a
different note.
NOTE RE DE: Different displays seem to handle extended DEs differently
Also, the same display might handle them differently depending on the
other timing signals.
e.g. sometimes DEs that are active longer than the number of horizontal
pixels appear to start a new row. Sometimes they seem to be gobbled up by
the nonexistant pixels on the right side of the display (which is certainly
easier to work with, since *exact* timing when dealing with CPU cycles is
difficult).
DEs shorter than the number of horizontal pixels also have interesting
effects
If it's not too short, it often seems to just repeat whatever last pixel
was drawn until reaching the edge of the screen. If it's too short,
sometimes it won't sync at all. There're probably lots of interesting
things that could be done here with experimentation...
Electrical Interfaces:
These are described more later, but briefly:
TTL - uses a single wire for each signal. Significantly easier to
interface, but pretty daunting when looking at 30+ wires.
LVDS - each pair of wires transmits 7 bits of data per pixel clock, pretty
much the standard for laptop displays (in fact it's more appropriately
called FPD-Link). This data is usually transmitted at speeds nowhere near a
microcontroller's reach, but it can be fudged, and most of my experiments
have been with an LVDS display.
Notes re: Stretching...
Some displays seem to stretch their pixel data horizontally over multiple
pixels if driven with a slow pixel-clock. They may be *really smart* and
implement scaling for lower resolutions internally, but I'm not convinced.
Especially since the cases where I've seen this still have the same
vertical resolution.
I'm not sure this stretching is LVDS-specific, but it might make sense.
This is all hypothesizing here:
LVDS uses a special scheme to "recover" the bit-clocking information. There
is a dedicated "clock" signal, but its purpose is specifically for the
pixel-clock, which only transitions twice per 7 bits. In order to "recover"
the bit-clock, the receiving end generally has a "Phase-Locked-Loop" (PLL)
which in this case is basically a frequency-multiplier. The receiver looks
at the pixel clock signal and multiplies its frequency by 7 in order to
sample each data bit before separating them back into individual wires.
These PLLs have certain operating-ranges. Sometimes they may run at a lower
frequency than 7*the input (if the input frequency is too high) or
sometimes they may run at a higher frequency (if the input frequency is
really low). Depending on how it's implemented, it may well sync up
perfectly for a few cycles and drop out completely for a few more. It's a
stretch (haha) to think that it could sync for seven cycles, then lose
sync, while yet another PLL has been tied to the actual pixel clock running
as a repeater, of sorts... in which case, data would be received
sporadically... nah, this is all quite a stretch. Or there are two PLLs,
one for the bits and one for the pixel-clock, the bit-PLL syncs up fine,
but the clock PLL runs faster than the actual clock (it's running at its
minimum frequency). Yeah, that makes some sense. Then the LCD itself is
sampling (with the "repeated" (haha, again) pixel-clock) data that the
bit-PLL latches at a slightly slower rate, thus doubling some pixels...
Dual-Pixel
(This is not the same as DSTN)
displays use one pixel-clock to display two horizontally-adjacent pixels at
a time. Why? Because it's fun to make things complicated, of course! No, it
increases frame-rate, makes it possible to send twice as much data at the
same bit-frequency, at the cost of twice as many wires. It's becoming
increasingly common as display resolutions increase. Generally, if you have
a laptop display larger than 1024x768 it's most likely Dual-Pixel (this is
a rough estimate). DVI also has a dual-pixel mode, but that doesn't
necessarily mean the display itself is as well, and vice-versa. For
instance, I was surprised to discover that an old 1024x768 VGA screen had a
dual-pixel interface at the LCD. That must have been fun for the VGA-to-TFT
circuit designers.
What's it mean for hackers? Well, actually, it's kind of nice. With a
microcontroller it's darn-near impossible to use the full resolution. My
latest is roughly 300 drawable pixels scaled across to 1024 physical. If
this display was dual-pixel, it would equate to 300 drawable pixels scaled
across 512, which would double the frame-rate... err... that's not right,
because the row-rate is determined by the processing time devoted to the
number of pixels... Anyways, the only additional work it would require is a
little soldering, and could in fact make your project easier.
Yeah, it's a LOT of wires, and looks very daunting, but those "odd" pixels
and "even" pixels could easily be wired directly together, in which case
it's literally no different to physically interface than a single-pixel
display.
DE-Only
displays don't pay attention to Hsync or Vsync signals. They determine that
information by the amount of time between active DE signals. I'm not
certain, but they seem to be easier to work with. Though that seems
backwards considering they'd have to do a lot more computation on the
display itself... So it would seem their timing requirements would be a lot
more sensitive to whatever computation methods they use (and don't exactly
document). All of my functional experiments with an LVDS display are with a
DE-ONLY LVDS display, and it seems astoundingly tolerant. However, I don't
know that this has anything to do with its being DE-Only.
non-DE-Only (most common?)
Uses Hsync and Vsync signals, as described.
IMPORTANT SEE: NOTE RE LVDS DISPLAYS WHICH ARE NOT DE-ONLY (most?).
Now put it to use!
The main thing is this, *as I understand it* (I won't keep saying that
after this, so keep it in mind): TFT displays, inherently, have built-in
memory. It's the whole point of the "Thin-film transistors" to be able to
store each pixel's state until the next refresh. There's a transistor at
every pixel, used to hold its state. Thus, why TFT displays look so much
better than the older non-TFT displays (which many of you may have never
encountered), which literally flicker on when written and fade to off until
they're written again. For the most part, those non-TFT flickery-days are
long-past. (Though, you'll find out here that flickering TFT's can be used
to our advantage).
Maybe, at this point, I've already given all the information necessary for
you to get one working from an arduino or similarly under-powered device!
If not, I'll continue.
This needn't be mentioned so early in the document, but eventually it's a
consideration. Also, it's not necessary to pay too much attention to my
hypothesizing... You can skip to the next paragraph.
Take that main-point just a little further: LCDs of all sorts have this
inherent issue... When the liquid crystals (pixels) are driven with a
constant (DC) voltage they cannot retain their visible state forever.
Gradually, they will revert to their original state (either black or clear,
depending on the technology used in the particular display). At one time it
was made very clear that driving a liquid crystal (segment, or pixel) with
DC will eventually "ruin" that pixel. I have yet to encounter this effect,
but it's been made very clear in numerous places over the years. I don't
know what method they use for driving pixels at fractional-intensities
(gray) without using DC. Maybe they read the pixel's TFT during each
refresh and write a new temporary DC value based on that...? I *highly*
doubt they have an *additional* frame-buffer for the sake of remembering
the previous DC value of each pixel and calculating a new one. I also
*highly* doubt they refresh all the pixels *separately* from (and faster
than) the regular refresh rate driven by a video-card, (otherwise there
wouldn't be the effect described below?). It could well be that I
completely misunderstood those warnings I read over and over again all
those years ago. It could also be that nowadays DC *won't* damage the
crystal, and it could be that they're actually using DC values to drive
these pixels to fractional-values. IF that's the case, it's likely those DC
values are stored in a (leaky) capacitor which drives the TFT which holds
the pixel state. Whatever the case, we have another issue causing the main
point of this paragraph...
The point of the above diatribe is that, whatever the cause, the displays
I've worked with *do* eventually fade if not refreshed regularly. However,
nowhere near as regularly as stated in the documentation for these
displays. An example: The LTN121X1 display I acquired from an old iBook
specs the minimum refresh rate at ~50Hz. My current code refreshes at
~1/5Hz. There is visible fade between each refresh, and you also have the
joy of *watching* as each pixel is redrawn (a horizontal black bar scrolls
down the screen for about 5 seconds), BUT, this means that we can stretch
our refresh rate down to *once per five seconds,* well within the abilities
of a lowly 8-bit microcontroller.
So, if you have a relatively stationary image you wish to display, and
don't mind a visible update on the screen, we can stretch the screen's
capabilities quite a bit, and even drive it from an arduino-like device
with only a couple TTL chips.
On to the capabilities (not all-inclusive, and likely mutually-exlusive).
These are my accomplishments so far on two different 1024x768 TFT displays,
using an Atmel AVR microcontroller (like those used in Arduinos) running at
~16MHz:
*30Hz refresh, 21pixels by 21pixels (stretched across the screen), 8 colors
*1/5Hz refresh, 300ish (stretched horizontally) pixels by 768pixels, 48
colors
*5Hz refresh, 64pixels by 48pixels (stretched), 48 colors.
and more.
Using these specification-stretching techniques we can do quite a bit. With
a higher-power 8-bit microcontroller (high-end arduino), low expectations
for number of displayed colors, low pixel-count, etc. We can get 30Hz
refresh, no problem. For most, the flicker at 30Hz isn't even visible (old
tube TV's run at 30Hz, or 33?). And, with some clever coding, this could
quite probably be an image that changes slightly in *every* frame, just
like a low-resolution movie. The particular display I implemented this on
was not an old laptop display, but an old desktop display with its innards
removed, an ATmega644 connected directly to its Pixel-clock, Hsync, Vsync,
Data-Enable inputs. The display itself was a "dual-pixel" display, which
means for 1024x768 it required 512 pixel clocks per row, and had two sets
of color inputs, one for each of the two pixels that were displayed. It's
quite daunting to look at 40+ wires, at first, but when realizing only four
of them are necessary for timing, (the same four signals used by most
displays), the rest can all be tied together to a few outputs to create an
image. I initially chose to tie all the blues together, all the reds, and
all the greens, then using a single output port I could write each "pixel"
in a single "out" instruction (e.g. PORTA = 0x07; // Display white,
PORTA=0x01; //Red)
Some other considerations for this particular set-up:
I chose to implement this using an ATmega644 running at 15MHz with a
clock-prescaler of 2 from an external crystal running at 30MHz (also used
to directly drive the pixel-clock). Thus, each processor instruction was
running 2 pixels wide, but each pixel-clock was displaying two pixels
(dual-pixel display). Between loading from the frame-buffer and writing
that value to the output port, I had control over 21 (stretched) pixels in
each horizontal scan. Since I was working with a frame-buffer, this equated
to each row being repeated numerous times. Though, later, I was able to
improve this significantly. Also, repeating of rows isn't necessary, so 21
(really wide) pixels by 768 pixels was entirely possible.
There is *plenty* of room for improvement on this design, as it was my
first TFT experiment... Other considerations: Don't use a prescaler for the
ATmega's clock... 30MHz is way out of specs, but maybe it'd work? Use
assembly instead of C for the pixel-loading routines, those are just a few,
and not even considering Other Effects...
And, since I've mentioned "Other effects" let's talk about them... For one
thing, though it seemed like a hassle while coding what I was intending, if
timing is not quite right, there are some interesting effects. For
instance, sometimes Hsync timing issues would cause *rows* to be repeated.
No joke. I have no idea how it works on a fundamental level, but it did.
That likely could be taken advantage of if explored. (e.g. If it could be
determined how to intentionally cause it to repeat rows numerous times,
then the frame-rate could be increased that much more, assuming those rows
would have been duplicated anyhow via software... i.e. to create square
pixels). Another timing effect that I haven't yet discovered how to
harness: somehow it's possible to cause only partial refreshes... e.g. a
partial refresh starts from the top and only refreshes the top portion of
the screen, it could then be followed by a full refresh, to effectively
double the refresh-rate in the top portion of the screen. (Things which
change seldomly would be at the bottom portion). Another timing-related
oddity is that some displays appear to do stretching on their own...
For instance, I did some heavy-testing of a display connected to my laptop,
using "SwitchResX." I was bumping down its pixel clock as low as I possibly
could, just to see if it was even plausible to run it off my AVR. What I
found boggled my mind, as I bumped down its pixel clock, the image started
to stretch horizontally... When I finally found a rate that *should* work
with my AVR setup, my 1024x768 display was down to 680 (stretched) by 768.
The lower horizontal resolution meant I could bump the frame-rate up with
lower pixel-clocks, which was a blessing I looked forward to taking
advantage of. But, ultimately, this was the display I haven't yet been able
to drive via the AVR... This particular display had a bold note stating
that it would display black if an input signal did not match the timing
specifications required. While the timing I'd gotten to work via SwitchResX
was *way* outside the specified range, it never displayed anything but
black when connected to my circuit. Ponderings as to why the image was
stretched...? Maybe the pixel clock isn't used directly... maybe the PLL
that extracts the pixel clock saturates at a lower limit which is higher
than the pixel clock I was supplying... Regardless, the settings which I
was able to mimick with the AVR were *not* capapble of driving this
display. I'm guessing it's due to a poor LVDS implementation.
On that note Signal Inputs:
Flat-Panel-Display-Link (aka "FPD-Link", aka, poorly, "LVDS"):
There are two common interfaces, that I've run into, at the LCD panel's
connector, LVDS or (low-voltage) TTL. If you've got a desktop LCD display
or LCD TV, there's most likely a circuit which converts from VGA, HDMI, or
DVI, or any number of other input signals to either LVDS or TTL which
connects to the panel itself. (Interestingly, I've worked on a Plasma TV,
which uses LVDS... the plasma display itself was busted, but I was lucky
enough to be able to rewire it to connect *directly* to an LCD panel, it
was even the right resolution, though I believe some of the bits were
reversed, as high-brightness colors showed up as different colors... e.g.
bright red appearing as bright blue).
So, at the connector of your LCD panel, you most likely have either TTL or
LVDS. I'll start with TTL, it's simpler to understand. The terms TTL and
LVDS have nothing to do with LCDs, specifically, and mostly just define the
hardware-level interface.
TTL:
Has separate wires for each timing signal... Horizontal-Sync,
Vertical-Sync, Data-Enable, and Pixel-Clock. It also has separate wires for
each color bit used for each pixel.
e.g. a simple 640x480 TFT display running at 18bits-per-pixel will have 4
timing wires, and 6 wires for each color (red, green, blue), or 22 wires.
It will also have a few wires for the power supply (+3.3V and GND). If
you're lucky, the wires will be colored based on their purpose. In most
cases, those signals will be at or close to 3.3V = High (a '1' bit) and 0V
= Low (a '0' bit). Each wire is "sampled" at either the falling
(high-to-low) or rising (low-to-high) edge of the pixel-clock. (The
pixel-clock is running constantly, regardless of whether actual pixels are
being transmitted, for instance an H-Sync may be indicated by the H-Sync
pin being held low for 20 pixel-clocks).
The simplest way to connect this to a microcontroller (AVR, Arduino, PIC,
whatever) is to just wire each of the LCD pins to a microcontroller output,
and run that microcontroller at 3.3V... from there it's all software.
FPD-Link/LVDS:
Uses fewer wires, transmitting those very same signals in serial-form. In
(the most common form of) FPD-Link, each wire carries seven bits of
information for each pixel-clock. Thus, those six wires per color, in the
example above, can easily be combined into three wires (well, actually,
pairs of wires, more on that later), with a few bits to spare. Brilliantly,
there are three bits remaining, which are used for the timing bits
(excluding the pixel clock). The pixel-clock is on its own wire, but still
sent in 7-bit serial form.
There's a lot of detailed information out there as to how that data is
formatted (and a lot in my code). For now I'll say it's as simple as this:
during one pixel-clock on one wire, each other wire sends seven bits of
color (or timing) data. Thus, the bit-transmission-rate of a single wire is
seven times faster than the pixel-clock. We're talking some fast data-rates
here, especially for a lowly 8-bit microcontroller running at, say, 16MHz.
And as an aside, the data is NOT formatted such that each wire corresponds
to a single color. Though, in most cases I will refer to them as the red
signal, the green signal, and the blue/timing signal, since the low bits
are so dim they can hardly be seen. This is much better visualized in
graphic form, but I'm feeling wordy. See the code, it's got lots of ASCII
graphics). In fact, the six bits of red are on signal 0, along with the
first (least significant) bit of green. The remaining five of green are on
signal 1, along with the first two of blue. The remaining four bits of blue
are on signal 2, along with the Data Enable, Vertical Sync, and Horizontal
Sync bits.
To make things a little more complicated, LVDS means "Low-Voltage
Differential Signaling." The low-voltage part means it's lower than 3.3V
(TTL-levels). The "differential" part means that each signal is sent on two
wires. When one wire is high, the other is low, and vice-versa. There're a
lot of great reasons for this, but I'll leave them out for now.
Now, how do we make this work with our microcontroller?
Well, if your microcontroller (rare) has four differential serial ports
that can run at full speed and in 7-bit mode, then it's just a matter of a
few resistors (and those might not actually be necessary, more on that
later). If you have four serial ports that *aren't* differential, we can
simulate that as described later.
Regardless, using serial ports means loading those serial buffers
*constantly*. Also consider that a LOT of the data being sent to an LCD
repeats numerous times. During a twenty-pixel HSync active period, that
means sending the same data set down all the serial lines twenty times.
During a V-Sync active it means sending the same data 1024 times (H=Off,
V=On, DataEnable=Off, no color data) AND the 7-bit-encoded pixel clock,
which never varies. But, most serial ports don't repeat the same data, so
that means reloading those serial buffers repeatedly, which takes a lot of
processing power. Further, since most serial ports max out at the CPU
frequency, that'd limit the pixel-clock to 1/7th of the CPU frequency!
There are plenty of intriguing possibilities, with even just one serial
port, anyhow. Consider, for instance, that the pixel-clock (three-bits-on,
four-bits-off, as I recall) could be handled via a PWM pin. And
Red/Green/Blue could all be combined on a single serial port for, roughly,
grayscale.
It *may* even be possible to actually bit-bang the FPD-Link signals... My
1/5Hz refresh-rate experiments, running at a pixel-clock of ~2MHz suggest
that it's worth exploring stretching these things to their limits.
USE PWM outputs instead of Serial!
Anyways, believe it or not, my setup uses Pulse-Width-Modulation outputs to
simulate serial data streams. The ATTiny861 has three separate PWM outputs
which can run up to 8 times faster than the CPU clock. Thus, the "bit rate"
for my pseudo-serial signals is up to 128Mbps(!), or a pixel-clock
8/7ths(?) faster than the CPU frequency. There's a lot to be discussed
here, and it's well-documented in my code. As far as limitations: No, of
course we can't use a PWM signal to send *any* serial data stream, but by
changing the width of the pulse and shifting it left or right (using the
Dead-Time-Generator), we can send some pretty useful data, including all
timing signals. In the end, I've gotten up to 48 colors pretty well
stretched across the spectrum from black to white: four shades of red and
green, and three of blue (including black). I also started exploring other
timing configurations, which would allow hundreds of colors, but in strange
palettes. Have even considered options for switching those palettes on the
fly, by inverting the polarity of the LVDS signals, etc.
Ultimately, though, since my latest project uses 1/5Hz refresh, a
specialized high-speed "Phase-Locked-Loop" pulse-width-modulator is
unnecessary, and this could likely be implemented on an Arduino, directly.
LVDS signals from a microcontroller pin:
So, before I get into the details of my configuration, I'll throw this out
there... I spent *quite a bit* of time experimenting with various methods
to create electrical signals that the LVDS receiver in the display would
interpret correctly. Though there's plenty of room for improvement and
experimentation with other methods, ultimately the simplest worked the
best...
All it takes is a couple TTL-level XOR chips. This, too, is well-documented
in my code, but I'll summarize here (actually, the documentation might be
easier to understand than this explanation):
At the microcontroller-side, each of the four to-be-LVDSicized signals is
output on a single pin. That pin, as described before, is at TTL levels; it
will ideally be 0-3.3v depending on its value (low or high). Feed that
signal into two separate XORs, and configure them such that the output on
one will be high whenever the other is low, and vice-versa. (e.g. tie the
unused input on one XOR high, and the other unused input on the other XOR
low, we've made a buffer and an inverter with TTL-level outputs). Now just
tie these directly to the differential LVDS signals connected to the LCD.
(I'll add a note that this is based entirely on experiment, if your chips
don't match, I'd highly recommend doing some additional testing *before*
connecting directly to your LCD and possibly killing it. I'll try to
explain how, later.)
But wait! This TTL chip is driving TTL levels, not low-voltage! And Why do
we need a buffer, since the output of the microcontroller is already at the
same levels it'll output? And, further, my TTL chip isn't rated for those
speeds, nor supply voltages! Or, what about using separate Buffer and
Inverter chips?
Well, This is based *entirely* on my (limited) experiments, but here goes:
The currently-working and heavily experimented-with setup consists of Texas
Instruments' SN74LS86, from 1980. They're under-speced in nearly every way.
The LS series is supposed to be run from 5V, no less than 4.5V. Yes, I am
running them off 3.3V. Yes, I have used them at the highest bit-frequency
possible from my AVR (~128MHz, *way* faster than the '86, or even the AVR,
is rated).
I have a dozen of these ancient XOR chips, and knew just by looking at them
that they weren't spec'd for speed or output voltage, not even current, nor
even the power-supply voltage of 3.3V. I put off experimenting with them
for a *long* time because of this. I even desoldered a couple more
properly-spec'd chips from an old motherboard to do my initial experiments.
I found *exactly* enough XORs to try out two channels; the Clock, and the
Blue/Timing channel. These were AHC-series; higher-speed, rated for 3.3V,
better, if not ideal, for these experiments.
First, a little more about LVDS:
At the LCD-side, the signal-pairs are "terminated" with 100ohm resistors. I
could go into all my vague understanding for the reasoning behind this (and
it's useful stuff, signal-bounce reduction, noise-immunity, etc.), but for
these purposes I just care about how it makes this system work...
So, basically, when one of my AHC (TTL) outputs is driving high (and the
other low) it's almost like driving 3.3V into a 100ohm resistor, to
ground... but the TTL outputs aren't strong enough to drive a 100ohm load
at 3.3V, so the output voltage sags, quite a bit actually, less than 2.5V
if I recall correctly. And, when looking at the other side, it's kind of
like driving 0V into a 100ohm resistor up to 3.3V. But the TTL outputs
aren't strong enough for that either, so instead of low being 0V, it's
closer to 1V.
Now here's the part that I find cool... these voltages are *damn-near
exactly* the values spec'd by LVDS. It's quite plausible it was designed
based on devices with similar characteristics being used way outside their
specifications.
That said, there's no necessity, even, for additional resistors on our
driver (XOR) outputs.
How did I get away with the 'LS86, when it requires 4.5-5.5V supply? I
dunno. It could well have been that they were designed such that the output
drivers *require* a certain voltage to even function... (e.g. to overcome
the "dead-band" in a transistor voltage-follower). But, thankfully, it
wasn't the case with this particular batch of 'LS86's from 1980. It's a
curiosity, but for now I'm just glad it worked. (One of the nice things
about datasheets of devices from that era is the "equivalent circuit"
drawings, which might be a great place to look into the hows and whys of
their stated vs. real limitations... I'm not that smart that I can just
look at a diagram of transistors and know how it works, especially at this
analog level).
One note, here: At full-speed (128MHz bit-rate) it *is* somewhat important
to use matching TTL chips. When I tried the AHC's for clock and Blue/Timing
and the LS's for Green and Red, there was a noticeable bit-shift. E.G.
Displaying black would come through as a dark shade of green. Replacing the
faster AHC's with *all* LS's cleared that up quite a bit. LIKEWISE, at
these high-speeds, and *especially* when using under-spec'd chips like the
LS's, I recommend using pairs of XORs from a single XOR chip for each
differential pair.
Some alternatives (not recommended unless you enjoy experimenting, and
really, isn't that why we're here?) would be using separate buffer and
inverter chips, or even forgoing the buffers altogether. It's likely these
will work fine at low speeds, but as you increase the bit-rate it's quite
likely that the signals feeding into the LCD will be shifted by as much as
an entire bit, maybe more. When these delayed signals are feeding off each
other (they are connected to each other through a low-value resistor,
remember) who knows what kind of mess could be received. Worst-case
voltages could swing for nearly a bit-period outside the maximum rating for
LVDS, up to 3.3V, or even worse with some signal bounce. It's best to keep
those "propagation delays" as equal as possible, not only by using the same
*types* of chips, but even those from the same date and location of
manufacture. This is why I used XORs instead of separate buffers and
inverters. (Yes, most manufacturers have different locations around the
world, and their processes may be slightly different, may even vary from
day-to-day... at least, this is what I've been told. At least keep it in
mind when using whatever chips you have lying around.) All that said, my
first functioning experiment at full speed used four separate one-gang
(single-gate) AHC chips (desoldered); one each of an OR and an XOR for each
channel (clock and blue/timing).
NOTE RE LVDS DISPLAYS WHICH ARE NOT DE-ONLY:
I have one LVDS-based non-DE-Only display which has yet to display anything
but black. Its documentation explicitly states that if a signal is received
that doesn't match its timing requirements it will display black, so it's
possible it was just a coding error. HOWEVER: I did *match* the timing (as
best I could with early-code experiments and no feedback) to a timing I
tried with it *in my computer.* If that timing was programmed into the
microcontroller correctly, and the LVDS was working properly, it should
have worked. Which leads me to this: It's *quite likely* the reason I
haven't gotten this one working is due to my flakey LVDS simulation
method... maybe the bits aren't aligned appropriately with the pixel-clock;
the DE signal is active-high while the others are active-low, thus DE may
be readable as true as long any bits surrounding DE are also high... Also:
With my earlier attempts at LVDS it was found that skinny-bits (e.g. H-sync
only = low, the rest of the bits are high) aren't necessarily active
long-enough for the bit to cross the thresholds... (In other words, it the
skinny bit may be lost, which is *pretty important* for Hsync). The
circuitry has changed since then, and another quick test didn't work, but I
haven't looked into the skinny-bit-issue since the latest circuitry. There
are *many* other theories regarding the LVDS implementation... (e.g. at
high-speed with the LS's, bits may not be aligning correctly, some of the
colors are slightly off, dark red shows as slightly green, etc.)