-
Notifications
You must be signed in to change notification settings - Fork 0
/
lvds.c
1726 lines (1538 loc) · 65.3 KB
/
lvds.c
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
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
This is an old note from main() upon init...
And, actually, the Timer0 stuff may not be related to the comment.
I have no idea how it functioned, it seems the rest was deleted.
//For synchronizing timer1 settings (to avoid glitches)
// count the number of CPU cycles...
// depending on how many cycles it takes to start this and whatnot,
// there may be an offset. But the jist is we know every 7 cycles
// the timer1 reset is synchronized with the CPU instructions...
// OCR0A = 6;
// timer_setWGM(0, WGM_CLR_ON_COMPARE);
// timer_selectDivisor(0, CLKDIV1);
*/
//For Red and Green (NOT Blue) This enables four shades, instead of three
// (including black)
// Doing so increases pixel-processing time, thus the pixel-widths
// (thus decreasing resolution)
// each color takes 9 cycles to process in three-shade mode
// or 12 cycles for red and green, plus 9 for blue in four-shade mode
// a/o v59: I don't think this does anything in ROW_SEG_BUFFER
#define FOUR_SHADES TRUE
//a/o v60
//I can't find this anywhere else... Might not be looking hard enough
#define NUM_COLORS 48
//a/o v60: These are old notes, as well...
// As I recall, there was some issue with DC signals on the LVDS lines.
// I think this was when I was attempting to use AC-Coupling (long before
// the 74LS86 was deemed functional)
// and probably has no relevence now.
//DC_DE is only used in drawPix...
//#define DC_DE_DISABLE TRUE
//This should remove DC from Vsync, etc...
// Currently only implemented with FULL_INIT_TESTS below...
//#define REMOVE_DC TRUE
//There are two methods for configuring the PWM channels to output
// pseudo-serial data for changes between timing modes...
// (e.g. Horizontal Front Porch -> Hsync -> Back Porch -> Data Enabled)
// * Write all the necessary/related registers upon each transition
// Leads to redundancy, as many of the registers are already properly
// configured from the last transition.
// * Write only the changed registers
// (As I recall, it was possible to have only a single register-change
// on most, if not all, transitions...)
//
// The benefit of the latter being: Since the pixel-clock is generally
// faster than the processor-clock, or at least not-aligned, reducing the
// number of instructions to *one* would allow for glitchless transitions
// in the actual PWM signal... guaranteeing that at least the data being
// transmitted would conform to FPD-Link compatible signals...
// (no glitches that might, e.g. cause a couple serial-frames to send both
// Data Enable *and* VSync active at the same time. Or, e.g. to send a
// serial-frame which indicates an H-sync while transitioning between
// the H-Back-Porch and Data-Enable)
// Since the only display I've gotten working via LVDS, so far, is
// DE-Only, these tests and the importance of these glitches will
// probably have to be revisited when working with a display that pays
// attention to the Hsync and Vsync signals.
// FULL_INIT_TESTS uses the first option, which guarantees that if I missed
// some register-transition (while coding), that at least it *should* send
// the proper signals (after the transition).
// (The transition-time may, of course, lead to incompatible signals)
// So, it's mostly for testing, and should be removed if possible.
//#define FULL_INIT_TESTS TRUE
//This should just reduce the number of instructions which actually get
// executed, since Hsync doesn't need to be sent, etc.
// Actually, I'm working with a DE-Only display right now, and this is NOT
// set to TRUE... I haven't tested it with this TRUE
//#define DE_ONLY_DISPLAY TRUE
//PWM Timing:
// (ATTiny861, Timer1, FastPWM, PWM1X;
// a/o LCDdirectLVDS1_5_PWMtimingTests):
//
// PWM output on OC1A
// if OCR1A = 0, OC1A remains constant High
// OCR1A = 1, OC1A is low for 2 counts
// OCR1A = 2, OC1A is low for 3 counts
// OCR1A = 3, OC1A is low for 4 counts
// OCR1A = OCR1C, OC1A remains constant Low
//
// THUS:
// compare-match occurs when TCNT changes AWAY from match
// (assuming TCNT starts at 0 for one pulse, 1 for one pulse...)
// TOP (OCR1C) is included in the count...
// There is no single-count pulse-width
// (Though, it seems dead-time could simulate it...)
// (set a deadtime of 1 on the BOTTOM edge and OCR1A)
// (but then there's no high for only one clock)
// Rather than *possibly* mess with timing (on other displays?)
// Also for testing...
//#define DT0_BLUES_ONLY TRUE
//A/O v13: Using the Samsung LTN display, instead of the IDTech IAXG
// LTN appears to be content with my pseudo-LVDS scheme.
// IAXG has never unblanked
// Even though suitable timings were found with SwitchResX
// Maybe due to psuedo-LVDS
// slight timing issues // It probably doesn't work with all cases...
// Definitely with drawPix/Images...(RC oscillator variances?)
// glitches when switching LVDS states
// (thought I had that figured out, originally)
// The fact the LTN appears to recognize the signalling suggests the
// IAXG would be worth further exploration...
// Unfortunately, the CCFT blew out my inverter
// And I must have put it back together incorrectly
// (backlight filters out of order or flipped?)
// (which actually makes for some very interesting
// visuals, but hard to develop with)
// IAXG: uses DE, V, and H
// At low pixel-clock DE is active for fewer pixels...
// Last Tested: 680 was full-screen
// Nice because it increases the frame-rate!
// LTN: uses DE only
// The idea is to use FastPWM with the PLL to implement 64-85Mbits/sec
// (the PLL on the Tiny861 supposedly maxes out at 85MHz)
// (Though I am currently running with OSCAL set to the highest frequency
// and the PLL seems to be syncing at about 128MHz)
// Wiring:
// Many iterations of AVR->Differential "LVDS" circuitry resulted in the
// simplest of all:
//
// Believe it or not, the XOR is a standard TTL LS-series XOR: 74LS86
// Specifically: TI SN74LS86N from 1980 (the only XOR in my collection)
// It's spec'd to run from 4.5-5.5V, and its propagatio delays and slew
// rates aren't really spec'd to be good enough for 128MHz pixel clock
// yet it's working...
// Further, the output voltages are right in the LVDS range,
// IIRC (last I 'scoped) around 1.5V High and 1.0V Low
// (Don't forget the LCD has a 100ohm resistor between
// RXinN/clk+ and RXinN/clk-)
// Most signals are connected as shown (RXin0, RXin2, RXclk)
// Green is the only one which has RXin1- and RXin1+ swapped
// since it is on the /OC1B (inverted) output
// Green may benefit from a pull-up resistor on /OC1B
// there was some weird noise appearing like a floating-input
// when the full frame was not filled with pixels
// (but it should've waited to disable /OC1B until *after* the delays,
// etc. So I'm not sure what it was)
//
// It's probably best to use two XORs from the same chip for a single
// LVDS channel, since different chips may have slightly different
// characteristics.
//
// The entire circuit, thus, requires TWO 74LS86's
// (four XORs apiece, two per LVDS channel, 8-total)
//
//
// VCC3V3 VCC3V3
// | |
// +---\ \¯¯¯-_
// | | ¯-
// | | XOR >------> RXinN/clk-
// AVR | | _-
// OC1x >----+------/ /___-¯
// output |
// |
// |
// `------\ \¯¯¯-_
// | | ¯-
// | | XOR >------> RXinN/clk+
// | | _-
// +---/ /___-¯
// | |
// GND GND
//
//
// Also used: The TTL 74AHC series...
// I found some one-gang 74AHC1G32 and 74AHC1G86's on an old iBook
// motherboard. (An OR and an XOR, respectively). These are spec'd for
// 3.3V operation, and faster. The output voltages appear OK for LVDS
// (with a 100ohm load in the LCD)
//
// NOTE: Since I only had enough of these 1-gang devices for two LVDS
// channels, I had to implement Red and Green with the LS
// before switching all channels to the LS... Using different chips
// (specifically, different TYPES of chips) for different channels
// caused timing issues: Since the AHC is faster, the clock and Blue
// signals are synchronized, but the red and green signals were shifted
// a bit or two (resulting in "Black" appearing green, of course
// true black isn't really possible with my timing scheme... see below)
//
// For other circuits attempted, see oldNotes.txt
// (and boy there were many, involving voltage dividers, AC coupling,
// reference voltages, BJT differential amplifiers... I doubt I
// documented them all, or even most. Whoda thunk the simplest,
// especially under- AND over-spec'd--timing, supply voltage, and
// output voltage--would be the one...?)
//
// LVDS/FPD-Link timing:
// |<--- (LCDdirectLVDS: "pixel") --->|
// Timer1: |<-- One Timer1 Cycle (OCR1C=6) -->|
// TCNT: | 0 1 2 3 4 5 6 | 0 1 2 3
// |____.____.____.____ |____.____.____.____
// RXclk+: / | \ . . / | \ //
// | | ¯¯¯¯ ¯¯¯¯ ¯¯¯¯| |
// One Pixel: | |<--- One FPD-Link Pixel Cycle --->|
// | |
// "Blue/DVH" |____ ____v____ ____ ____v____ ____|____ ____
// RXin2: X B3 X B2 X DE X /V X /H X B5 X B4 X B3 X B2 X ...
// |¯¯¯¯ ¯¯¯¯^¯¯¯¯ ¯¯¯¯ ¯¯¯¯^¯¯¯¯ ¯¯¯¯|¯¯¯¯ ¯¯¯¯
// | |<--Not Blue-->| |
// | |
// "Green" |____ ____v____ ____v____ ____ ____|____ ____
// RXin1: X G2 X G1 X B1 X B0 X G5 X G4 X G3 X G2 X G1 X ...
// |¯¯¯¯ ¯¯¯¯^¯¯¯¯ ¯¯¯¯^¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯ ¯¯¯¯
// | |<------->|-Not Green |
//
// "Red" |____ ____v____v____ ____ ____ ____|____ ____
// RXin0: X R1 X R0 X G0 X R5 X R4 X R3 X R2 X R1 X R0 X ...
// |¯¯¯¯ ¯¯¯¯^¯¯¯¯^¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
// | |<-->|-Not Red
//
// Of course: The Not Green/Red bits above are low-bits and
// basically have little/no visible effect
//
//
// Ponderings on using /OC1x's and OC1D for other colors...
// /OC1D could easily be used for another color, unaffected by others
// since DT1(L) and OCR1D are unused
//
// DE: Blue Values
// -----------
// OCR1A=4,5,6 (OCR1A=6, Full-Blue would force /OC1A low,
// DT1(H)=0,1,2 e.g. Green=Full-Green or Off)
// Also: OCR1A=4,5 would affect Green
// (DT1(L) is from this edge...)
// DT1(H) also affects complementary OC1D
//
// CLK: OCR1B=3 CLOCK can NOT be complementary-output mode
// (/OC1B unusuable)
// otherwise, DT1 would affect clock
//
//
// CLOCK_INSENSITIVITY_TESTING:
// (a/o 56-36-3ish, no longer testable)
// Testing of DE/Blue's DeadTime values on Clocking...
// Image-shift (not sure why, more calculations? Not *that* many!)
// Blue now has only two shades besides black
// ~66% and 100%
// Now each blue pixel (the ones appearing black)
// is bordered by a blue line...
// All doable. Would have preferred 3 shades besides black,
// but two isn't bad.
//
// New Idea: Since CLOCK can be used with DT1H (during DE)
// DE/Blue DT values are 0, 1, or 2 (D0, D1, D2)
// Dn corresponds to the low-to-high dead-timer value
// (aka counter-reset delay, on OCR1x)
// dn corresponds to the high-to-low dead-timer value
// (aka OCR match delay, on /OCR1x)
// Cn corresponds to OCR1x=n
//
// This was easier to comprehend in v<26... now it's more informative
// but harder to view...
// OCR1B = 3
// TCNT: | 0 1 2 3 | 4 5 6
//
// CLKideal: /¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\______________/
//
// |____ ____ ____.____v |
// CLK: D0>/ D1>/ D2>/ \ . . /
// OC1B |¯¯¯¯ ¯¯¯¯ |¯¯¯¯ ¯¯¯¯ ¯¯¯¯|
// | |
// | G2 G1 B1 B0 | G5 G4 G3
// Green: | |____ ____ ____
// /OC1B \ . . . d0>/ d1>/ d2>/ d3?\ //
// |¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
// |
// | R1 R0 G0 R5 R4 R3 R2
// Red: |____ ____ ____ ____ ____ ____ ____
// OC1D D0>/ D1>/ D2>/ C2>\ C3>\ C4>\ C5>\ C6...
// ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
// ____ ____ ____ ____ ____ ____ ____
// /OC1D \ ^OCR1D>=6^ L>/ M>/ N>/ O>/ P?>...
// ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
//
// B3 B2 DE /V /H B5 B4
// "Blue/DVH" |____ ____ ____.____.____ ____ ____
// OC1A: D0>/ D1>/ D2>/ C4>\ C5>\ C6...
// |¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
// ____ ____ ____ ____ ____ ____ ____
// /OC1A: \ ^^^--OCR1A>=6--^^^ X>/ Y>/ Z?>\ //
// (usable?) ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯
// X: OCR1A=4, DTL=0
// Y: OCR1A=4, DTL=1; OCR1A=5, DTL=0
// Z: OCR1A=4, DTL=2; OCR1A=5, DTL=1; OCR1A>=6
//I've looked it over extensively, albiet exhaustedly, and it seems
// there's only one way to do this. Unfortunately, DTH=1,2 *does* affect
// the clocking. Everything displays properly, but the lighter shades of
// blue enabled by DTH=1,2 don't display at all (or dang-near black)
// So there's really no benefit to using DTH=1,2 during DE for blues...
// That gives two (similar) shades of bright blue 63/63 and 47/63
// *'d are arbitrarily chosen for implementation
// F'd are selected when FOUR_SHADES is TRUE
// (experimentation might show other choices are better)
//
// Red is connected to OC1D, Complementary output is not necessary
// OCR1D determines its brightness:
// (+OC1D => RX0+)
// *Off (0/63): OCR1D = 0 \ //
// 3/63: OCR1D = 1 > These three appear identical in glColorTest
// 3/63: OCR1D = 2 / (G0 Active, from here down)
// *35/63: OCR1D = 3
// F51/63: OCR1D = 4
// 59/63: OCR1D = 5 \ These two appear identical in glColorTest
// *63/63: OCR1D >= 6 /
// (+OC1D => RX0-)
// Not really worth pursuing, next step from 60/63 is 28/63 then 12
//
// Green is connected to /OC1B (the complementary channel to CLK)
// Its polarity is reversed (but that's easy since we have a single-ended
// to differential converter, and its outputs can just be swapped)
// DTL1 determines its brightness (G0 affected by Red):
// (/OC1B => RX1+)
// Off (0/63): DTL1 = 3 (is this possible?)
// 8-9/63: DTL1 = 2
// 24-25/63: DTL1 = 1
// 56-57/63: DTL1 = 0
// (/OC1B => RX1-) (B1,0 Active, as well as G2,1)
// *Off (6/63): DTL1 = 0
// *38-39/63: DTL1 = 1
// F54-55/63: DTL1 = 2
// *62-63/63: DTL1 = 3
//
// Blue, as in previous versions, is connected to OC1A, DTH=0 during DE
// so as not to interfere with the clock
// OCR1A determines the brightness (B1,0 active, per Green->RX1-):
// (+OC1A => RX2+) (B3,2 Active from here down)
// *Off (15/63): OCR1A=4
// *47/63: OCR1A=5
// *63/63: OCR1A=6
//
// The clock is single-ended (complementary-mode disabled) during NON-DE
// because the DE/V/H signals (except in DE mode) require DT1H to vary.
// When DE is active the clock channel (OC1B) is switched to
// complementary-output-mode to enable the Green PWM output
//
// For easier viewing:
// Red: (+OC1D => RX0+)
// Off (0/63): OCR1D = 0
// 35/63: OCR1D = 3
// 51/63: OCR1D = 4 (FOUR_SHADES only)
// 63/63: OCR1D >= 6
// Green: (/OC1B => RX1-) (B1,0 Active, as well as G2,1)
// Off (6/63): DTL1 = 0
// 38-39/63: DTL1 = 1
// 54-55/63: DTL1 = 2 (FOUR_SHADES only)
// 62-63/63: DTL1 = 3
// Blue: (+OC1A => RX2+) (B3,2 Active from here down)
// Off (15/63): OCR1A=4
// 47/63: OCR1A=5
// 63/63: OCR1A=6
//
// Toward creating a GIMP palette... v54.5
// Probably absurd, but this was brown/orange on GIMP and it's yellow here
//
// Red: (+OC1D => RX0+)
// Off (0/63): OCR1D = 0 r=0
// 35/63: OCR1D = 3 r=142
// 51/63: OCR1D = 4 r=206
// 63/63: OCR1D >= 6 r=255
// Green: (/OC1B => RX1-) (B1,0 Active, as well as G2,1)
// Off (6/63): DTL1 = 0 g=24
// 38-39/63: DTL1 = 1 g=154
// 54-55/63: DTL1 = 2 g=218
// 62-63/63: DTL1 = 3 g=251
// Blue: (+OC1A => RX2+) (B3,2 Active from here down)
// Off (15/63): OCR1A=4 b=61
// 47/63: OCR1A=5 b=190
// 63/63: OCR1A=6 b=255
//
// These numbers don't look entirely accurate...
// they vary depending on the other colors... and why is blue so high
// even when it's off?
// Maybe I'm looking at old notes...?
// SEE lvdsColorExperiments.c Now Here.
// Implementations/prospects:
// * rowBuffer.c (more like row-settings-buffer)
// calculate a row's worth of pixels before drawing that row
// (uses packed color settings in a single byte per drawable pixel)
// 64 drawable pixels across, regardless of LVDS speed
// * For faster pixels: these settings values could be stored
// in individual bytes. Gives about 1/3 more pixels at 3x the memory
// (not implemented)
// * rowSegBuffer.c
// also calculates an entire row before drawing it
// instead of storing pixels, store "segments"
// i.e. each segment is defined by a color value and a length
// the color-value is stored as DT/OCR values (not RGB values)
// Number of segments is limited only by memory...
// e.g. 64 segments per row (max) is 64*3Bytes
// Three bytes for color, and *really simple* packing for seg-length
// BUT: at slow LVDS speeds, the resolution of these segments could be
// as high as one LCD pixel.
// possibly: at *really* low LVDS speeds we could be 64*2Bytes
// (with packing)
// Actually: Using GB_COMBINED gives 2Bytes per segment
// only adds two clock cycles to each "pixel", so probably worth it
// In Any Case: There's not enough RAM for a full frame
// 64 pixels across * 64 pixels down is 4096 bytes
// So whatever method, we need to precalculate each row before
// displaying it
// Could be as simple as loading direct from program memory
//
// Since low-bits are barely visible, their effect is neglected.
// Thus: Green is affected only by the Compare-Match deadTimer (DT1L)
// Red is affected only by OCR1D, and could be single-ended
// OTOH:
// The Visible blue values (with DT affecting clock)
// are only the high-two values, which are affected only by OCR1A
// So counter-reset dead-time (DT1H) needn't be varied
// And, then, the clock won't be affected at all
// (assuming we set it to single-ended during DE disabled, for V/H)
//
// NEW CONSIDERATION: "The dead timer delays the waveform by a minimum of
// "of one count, when DT=0..."
// So changing the clock from single-ended to complementary
// might actually cause the clock output to be shifted!
// DE/V/H Timing (LCDdirectLVDS):
//
//
// | 0 1 2 3 4 5 6 All: set @ 0
// |____.____.____.____ OCR1C = 6
// Clock: / \ . . / Complementary-
// ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ Output Mode
// required for DT
// Signal: B3 B2 DE /V /H B5 B4 | B3
// ____ ____ ____ ____ ____ ____ ____|____
// DE: X B3 X B2 / . . \ B5 X B4 X B3 X
// state2 ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯
// DE_BLUE: >¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯| DT=X, OCR=0xff
// Watch the transition!! -------^
// DE_NORM: >_________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯\_________| DT=2, OCR=4
// DC_DISABLED:
// maxBlue: >¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯\____| DT=0, OCR=5
// See below for more blue settings...
//
// ____ ____ ____ ____ ____|____
// H (only): X xx X xx \ / \ / xx X xx X xx X
// state1 ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯
// >______________/¯¯¯¯\______________| DT=3, OCR=3
// DC_DISABLED:
// Not much can be done...
//
// ____ ____ ____ ____ ____|____
// V w/o H: X xx X xx \ . / \ xx X xx X xx X
// state3 ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯
// >___________________/¯¯¯¯\_________| DT=4, OCR=4 (+?)
// DC_DISABLED:
// >___________________/¯¯¯¯¯¯¯¯¯\____| DT=4, OCR=5
// ____ ____ ____ ____|____
// V w/ H: X xx X xx \ . . / xx X xx X xx X
// state4 ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯
// >__________________________________| DT=X, OCR=0
// TransitionWatch!!! -------^
// Shouldn't matter... DT from no-edge
// DC_DISABLED:
// >¯¯¯¯¯¯¯¯¯\________________________| DT=0, OCR=1
// ____ ____ ____ ____ ____ ____|____
// Nada: X xx X xx \ / . \ xx X xx X xx X
// state0 ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯ ¯¯¯¯|¯¯¯¯
// >______________/¯¯¯¯¯¯¯¯¯\_________| DT=3, OCR=4 (+?)
// DC_DISABLED:
// >______________/¯¯¯¯¯¯¯¯¯¯¯¯¯¯\____| DT=3, OCR=5
// Transitions: OLD!!!!! WRONG!!!!
// (are they? There're several additional transitions, now, for one.)
// The idea is to reduce the number of instructions between each
// LVDS "state."
// These are implemented below in the case:
// "#else //NOT FULL_INIT_TESTS"
// Since each instruction takes *longer than* a single pixel
// two instructions would *at best* occur on two consecutive pixels
// Thus, there's likely to be a transition-glitch
// (e.g. old OCR value with new DT value)
// Note that the new DT values are implemented at the next corresponding
// edge after the DT-write instruction completes
// (for +OC1A/DT1H, when TCNT is reset to 0)
// (for -OC1A/DT1L, when TCNT passes OCR1A)
// New OCR values are delayed until the next TCNT reset to 0
//
// Initial:
// Nada DT=2 NADA_OCR = (3<=OCR<(=?)6)
// Nada -> H OCR=2
// H -> Nada OCR=NADA_OCR
//
// Nada -> V DT=3
// V -> V+H OCR=0
// V+H -> V OCR=NADA_OCR
// V -> Nada DT=2
//
// Nada -> DE DT=1
// DE -> Nada DT=2
#if (defined(REMOVE_DC) && REMOVE_DC)
#error "REMOVE_DC hasn't been tested since lcdStuff, or long prior"
#define Nada_init() { DT1=(3<<4); OCR1A=5; }
//Unused, normally...
#define Vsync_init() { DT1=(4<<4); OCR1A=5; }
#define VplusH_init() { DT1=0; OCR1A=1; }
#define Hsync_init() { DT1=(3<<4); OCR1A=3; }
#define DEonly_init() { DT1=(2<<4); OCR1A=4; }
#define DEblue_init() { DT1=0; OCR1A=5; }
#else //!REMOVE_DC
//Nada init
#define Nada_init() { DT1=(3<<4); OCR1A=4; }
//Unused, normally...
#define Vsync_init() { DT1=(4<<4); OCR1A=4; }
#define VplusH_init() { DT1=0; OCR1A=0; }
#define Hsync_init() { DT1=(3<<4); OCR1A=3; }
// #define DE_init() { DT1=(2<<4); OCR1A=4; } //...SHOULD BE
#define DEonly_init() { DT1=(2<<4); OCR1A=4; }
#define DEblue_init() { DT1=0; OCR1A=6; }
//#define DE_init() { DT1=(1<<4); OCR1A=2; } //Works with DE_ONLY
//#define DE_init() { DT1=0; OCR1A=0xff; } //DE_BLUE
#endif //REMOVE_DC
//Display is DE-Only (doesn't use H/Vsync)
// Shouldn't be necessary to select this if it is,
// since DE timing is the same either way
// but I want to test whether a single-bit is being detected
// (e.g. maybe the rise/fall-times of the output aren't fast enough for a
// single bit, which might explain why the other display didn't work)
#if (defined(DE_ONLY_DISPLAY) && DE_ONLY_DISPLAY)
#define Vsync_fromNada() Nada_init()
#define VplusH_fromVsync() Nada_init()
#define Vsync_fromVplusH() Nada_init()
#define Nada_fromVsync() Nada_init()
#define Hsync_fromNada() Nada_init()
#define Nada_fromHsync() Nada_init()
#define DEonly_fromNada() DE_init()
#define DEblue_fromDEonly() DE_init()
#define Nada_fromDEblue() Nada_init()
#if(DE_BLUE)
#warning "DE_BLUE is true, but not implemented with DE_ONLY_DISPLAY"
#warning "...The display should be blank"
#endif
//Use full initialization of each LVDS state
// (might not be good during transitions, but should guarantee
// steady-state, in case my transitions aren't correct...)
#elif (defined(FULL_INIT_TESTS) && FULL_INIT_TESTS)
#define Vsync_fromNada() Vsync_init()
#define VplusH_fromVsync() VplusH_init()
#define Vsync_fromVplusH() Vsync_init()
#define Nada_fromVsync() Nada_init()
#define Hsync_fromNada() Hsync_init()
#define Nada_fromHsync() Nada_init()
#define DEonly_fromNada() DEonly_init()
#define DEblue_fromDEonly() DEblue_init()
#define Nada_fromDEonly() Nada_init()
#define Nada_fromDEblue() Nada_init()
#else //NOT FULL_INIT_TESTS
//#define Vsync_fromNada() { DT1=(4<<4); }
#define Vsync_fromNada() { DT1=(4<<4); OCR1A=5; } //Leave two bits high
//for easy-scoping
//Three would be nicer, but I'm pretty sure OCR=TOP=ON
#define VplusH_fromVsync() { OCR1A=0; }
//#define Vsync_fromVplusH() { OCR1A=4; }
#define Vsync_fromVplusH() { OCR1A=5; } //Extra bits for scoping
//#define Nada_fromVsync() { DT1=(3<<4); }
#define Nada_fromVsync() { DT1=(3<<4); OCR1A=4; } //scoping...
#define Hsync_fromNada() { OCR1A=3; }
#define Nada_fromHsync() { OCR1A=4; }
#define Nada_fromDEblue() { DT1=(3<<4); OCR1A=4; }
#define Nada_fromDEonly() { DT1=(3<<4); }
#define DEonly_fromNada() { DT1=(2<<4); }
#define DEblue_fromNada() { OCR1A=0xff; }
#define DEblue_fromDEonly() { OCR1A=0xff; }
/*
#if (!defined(DE_BLUE) || !DE_BLUE)
//Nada -> DE
//#define DE_fromNada() { DT1=(1<<4); } //DT1 = (1<<4);
#define DE_fromNada() { DT1=(2<<4); }
//DE -> Nada
//#define Nada_fromDE() { DT1=(2<<4); } //DT1 = (2<<4);
#define Nada_fromDE() { DT1=(3<<4); }
#else
//Nada -> DE
//#define DE_fromNada() { OCR1A=0xff; } //DT1 = (1<<4);
#define DE_fromNada() { OCR1A=0xff; }
//DE -> Nada
//#define Nada_fromDE() { OCR1A=3; } //DT1 = (2<<4);
#define Nada_fromDE() { OCR1A=4; }
#endif
*/
#endif //End Of FULL_INIT_TESTS
// NOTES:
// DeadTimer requires
// COM1x1:0 = 01 ("Complementary Compare Output Mode" ?)
// Complementary Compare Output Mode:
// OCW1A: cleared on match, set at BOTTOM
// If PWM1X (pwm inversion) is used, OC1A = !OCW1A
// (Does not affect DT)
//
// CLOCK:
// Can NOT use differential mode with:
// OC1B pin is -differential input (through a resistor)
// /OC1B pin is +differential input (through a resistor)
// BECAUSE: DeadTimer affects ALL PWM channels
//
//
//
// ISSUES:
// Using Dead-Timer does not allow for use of complementary outputs
// as complementary LVDS inputs...
// DeadTimer affects ALL PWM channels in complementary mode
// (e.g. RXclkin+ on OC1B and RXclkin- on /OC1B is not an option)
//
// The typical patterns look like this (not at all to scale):
// ----______------------------------------------------___----- V
// --_--_--_--_--_--_--_--_--_--_--_--_--_--_ H
// __________-__-__-__-__-__-__-__-__-__- DE
//
// ^^^^\ //blah
// 1234 5?
// Pixels are sent during DE High (basically all the CPU will be used here)
//Ideally,
// there won't be any glitches when changing from one state to another
//
// NOTES: PWM1X inversion affects all PWM channels!
//
// Unchangeables:
// FastPWM
// inverted with PWM1X
//
// Init (pre 1):
// DeadTimerRising=1
//
// The states are:
// (Not necessarily accurate, just looking into necessary changes)
// (from Vsync L->H)
//
// 1 NothingActive (long, No DE, VporchFrontTimes)
// * 1<=DeadTimerRising<=5
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// * OCR1A == 6
//
// 2 Hsync
// * DeadTimer _OFF_ -> Horiz
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// (OCR1A == 6
// 3 NothingActive (short)
// * 1<=DeadTimerRising<=5
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// (OCR1A == 6
// 4 DE
// (1<=DeadTimerRising<(=?)5 (>1 for blue pixels?)
// (==5 cancelled by OCR1A match?)
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// * OCR1A = 5 (or 1<=OCR1A<5 for blue pixels?)
// 5 NothingActive(?)
// (1<=DeadTimerRising<=5
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// * OCR1A == 6
//
// 7 Repeat 2-5 for each row
//
// 8 NothingActive (long, No DE, VporchBackTimes)
// ? 1<=DeadTimerRising<=5
// (OCW1A: set @ BOTTOM, cleared @ OCR1A
// (OCR1A == 6
//
// 9 V w/o H
// ? OFF <= DeadTimer <= (5 - OCR1A)
// ** OCW1A: cleared at BOTTOM, set at OCR1A
// ??? What is the effect of changing this while running?
// * 1 <= OCR1A <= 5
// 10 V w/ H
// ? DeadTimer OFF (H -> Low)
// * OCW1A: set @ BOTTOM, cleared @ OCR1A
// * OCR1A > (=?) OCR1C
//
// 11 Repeat 9-10 for Vsync time...
//
// Here's how it worked pre-lvds:
// HSYNC, VSYNC, and DE refer to the actual pins
// In the LVDS setup, there're modes corresponding to each pin-combination
//
// Timer Interrupt:
// loadData:
// HSYNC active
// HSYNC Low delay
// HSYNC inactive
// if(dataEnable)
// DE active
// Send row data
// DE inactive
// //Prep for next interrupt
// switch(hsyncCount++)
// //Vsync H->L (active)
// 1:
// dataEnable=FALSE
// VSYNC active
// //Vsync L->H (inactive)
// T_Vlow:
// VSYNC inactive
// //Start of frame
// T_VD +(T_Vlow):
// dataEnable=TRUE
// //All rows have been displayed
// V_COUNT +(T_VD+T_Vlow):
// dataEnable=FALSE
// //Frame Complete
// T_DV +(V_COUNT+T_VD+T_Vlow):
// hsyncCount=0
// if(dataEnable)
// Use the remaining time to load the next row to memory
// So:
//Interrupt0 End
// | | Interrupt1
// v v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___________________________________________________ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯|_____________________________________ ...
// | ||
// DETIME^--------^|
// |
// VSYNC active
// dataEnable=FALSE (not necessary?)
//
//InterruptT_Vlow End
// | |
// v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___________________________________________________ ...
// V _____________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// | ||
// DETIME^--------^|
// |
// VSYNC inactive
//
//
//InterruptT_VD End InterruptT_VD+1
// | | | End
// v v v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ____________________|¯¯¯¯¯¯¯¯|_______|¯¯¯¯¯¯¯¯|____ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// | || | |
// DETIME^--------^| ^--------^
// |
// dataEnable=TRUE
//
//InterruptV_COUNT End
// | |
// v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___|¯¯¯¯¯¯¯¯|______________________________________ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// | || | |
// DETIME^--------^| ^--------^
// |
// dataEnable=FALSE
//
//
// This isn't really a state, it's basically just:
// if(hsyncCount == NUM_HYSYNCS_PER_FRAME)
// hsyncCount = 0;
// .....................................
// . .
//InterruptT_DV End .Interrupt0 .
// | | .| .
// v v .v .
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ .
// DE ___________________________________________________ .
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|____________________ .
// | || . | | .
// DETIME^--------^| . ^--------^ .
// | . .
// hsyncCount=0 (repeat from Interrupt0) .
// . .
// .....................................
//
// NOW to compare with LVDS states:
//
//Interrupt0 End
// | | Interrupt1
// v v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___________________________________________________ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯|_____________________________________ ...
// ^ ^ ^ ^ ^
// | | | | |
// | | | | +--- V w/o H \ These two toggle until
// | | | +----- V w/ H / next LCD state...
// | | |
// | | +--------- V w/o H > Intermediate change of state
// | |
// | +----- NothingActive \ From Previous State (?)
// +------- H_Only /
//InterruptT_Vlow End
// | |
// v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___________________________________________________ ...
// V _____________|¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// ^ ^ ^ ^ ^
// | | | | |
// | | | | +--- NothingActive \ These two toggle until
// | | | +----- H_Only / next LCD state...
// | | |
// | | +--------- NothingActive > Intermediate change of state
// | |
// | +----- V w/o H \ From previous state
// +------- V w/ H /
//InterruptT_VD End InterruptT_VD+1
// | | | End
// v v v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ____________________|¯¯¯¯¯¯¯¯|_______|¯¯¯¯¯¯¯¯|____ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// ^ ^ ^ ^^ ^
// | | | || |
// | | | || +--- NothingActive \ These four
// | | | |+------------ DE | cycle until
// | | | +------------- NothingActive | next LCD state...
// | | +--------------- H_Only ^ /
// | | |
// | +----- NothingActive \ From previous |
// +------- H_Only / state +-This intermediate state
// may not be necessary
// (IAXG01 shows 160 dots!)
//InterruptV_COUNT End
// | |
// v v
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// DE ___|¯¯¯¯¯¯¯¯|______________________________________ ...
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ ...
// ^ ^^ ^ ^ ^
// | || | | |
// | || | | +--- NothingActive \ These two toggle
// | || | +----- H_Only / until next LCD state
// | || |
// | || +---- NothingActive \ //blah
// | |+------------- DE | From previous state
// | +-------------- NothingActive |
// +---------------- H_Only /
// This isn't really a state, it's essentially just:
// if(hsyncCount == NUM_HSYNCS_PER_FRAME)
// hsyncCount = 0;
// .....................................
// . .
//InterruptT_DV End .Interrupt0 .
// | | .| .
// v v .v .
// H |_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯|_|¯¯¯¯¯¯¯¯¯¯¯¯¯¯ .
// DE ___________________________________________________ .
// V ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯|____________________ .
// ^ ^ . .
// | | .....................................
// | +--- NothingActive \ //blah
// +----- H_Only / From previous state...
// LVDS State transitions:
// 1 H_Only
// 2 NothingActive
//
// 3 V w/o H
// 4 V w/ H
//
// (repeat 3,4)
//
// 5 V w/o H
//
// 6 NothingActive
// 7 H_Only
//
// (repeat 6,7)
//
// 8 NothingActive
//
// 9 H_Only
// 10 NothingActive
// 11 DE
// 12 NothingActive
//
// (repeat 9-12)
//
// 13 H_Only
// 14 NothingActive
//
// (repeat 13-14)
//
// These should be reviewed to determine which changes are necessary
// between each state...
// (the fewer changes, the less likely we'll glitch...?)
// This'll probably be better rearranged...
// For now, though, OC1A is in all the notes above, for the D/V/H signal
// so OC1B is for the clock...
// CHANGING THESE does NOT change channel association.
#define DVH_OCR OCR1A
#define CLOCK_OCR OCR1B
void lvds_timerInit(void)
{
//Timer1 is used for LVDS (in PLL clocking mode)
//pll_enable();
//We want it to count 7 bits, 0-6 and reset at 7
OCR1C = 6;
//We want the clock to go low at TCNT=0 and high at TCNT=4
CLOCK_OCR = 3; //2; //3;
// My 'scope is only 20MHz, and I'd rather be able to use the digital mode
// which is even slower...
//Overridden when SLOW_EVERYTHING_TEST is true...
//#define TOOFAST_TEST TRUE
#warning "HERE AND BELOW, doesn't OSCCAL have special write requirements?"
//OSCCAL = 0x00;
#if (defined(SLOW_EVERYTHING_TEST) && SLOW_EVERYTHING_TEST)
//FOR TESTING. This should slow the clock...
// Gives roughly 4MHz...
OSCCAL = 0x00; //0x80;
// This should divide the system clock by 256
// does this affect the PLL? NO.
// The PLL is clocked only by the RC Oscillator
// OSCCAL does affect it.
// OPTIONS FOR SIMULATING FULL FUNCTIONALITY AT LOW SPEED:
// Run PLL as normal
// Prescale Timer1 and DeadTimer equally