-
Notifications
You must be signed in to change notification settings - Fork 30
/
Ra8876_Lite.cpp
4346 lines (3937 loc) · 150 KB
/
Ra8876_Lite.cpp
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
/**
* @file Ra8876_Lite.cpp
* @author RAiO Application Team .
* @license BSD license, all text above and below must be included in any redistribution
*
*
* @section HISTORY
*
* Date 29-12-2015 v1.0 - First release<br>
* Date 14-07-2017 Modify by John Leung @ TechToys for CEA861-D monitors compatible<br>
*
* @note Pinout summary::<br>
* Arduino 101, Arduino M0/M0 PRO, Arduino Due(via adapter), Arduino Mega2560(via adapter)<br>
* RA8876-SCK/SD-SCK <--- D13<br>
* RA8876-MISO/SD-MISO ---> D12<br>
* RA8876-MOSI/SD-MOSI <--- D11<br>
* RA8876-SS <--- D10<br>
* RA8876-RESET <--- D8<br>
* RA8876-XnINTR ---> D3<br>
* SD-CS <--- D4<br>
*
* @note Teensy 3.2/3.5 (via adapter)<br>
* RA8876-SCK/SD-SCK <--- D14<br>
* RA8876-MISO/SD-MISO ---> D12<br>
* RA8876-MOSI/SD-MOSI <--- D7<br>
* RA8876-SS <--- D20<br>
* RA8876-RESET <--- D8<br>
* RA8876-XnINTR ---> D15<br>
* SD-CS <--- D10<br>
*
*/
/**
* @note Add support for ESP8266 with hardware SPI <br>
* keyword ESP8266<br>
* Pinout <br>
* RA8876_XNSCS <--- D15<br>
* RA8876_XNRESET <--- D16<br>
* RA8876-MOSI/SD-MOSI <--- D13<br>
* RA8876-MISO/SD-MISO ---> D12<br>
* RA8876-SCK/SD-SCK <--- D14<br>
* RA8876-XnINTR ---> D0<br>
* SD-CS <--- D2<br>
*/
/**
* @note Add support for ESP32 (ESP32-PICO-D4) with hardware SPI
* VSPI for RA8876<br>
* ===============<br>
* RA8876-XNSCS <--- GPIO5<br>
* RA8876-XNRESET <--- GPIO10<br>
* RA8876-MOSI <--- GPIO23<br>
* RA8876-MISO ---> GPIO19<br>
* RA8876-SCK <--- GPIO18<br>
* RA8876-XnINTR ---> GPIO35 //it is an input pin, good for interrupt pin here<br>
* HSPI for SDCARD<br>
* ===============<br>
* SD-CS <--- GPIO15<br>
* SDCARD-MOSI <--- GPIO13<br>
* SDCARD-MISO ---> GPIO4 //Cannot use IO12 somehow...<br>
* SDCARD-SCK <--- GPIO14<br>
*/
/**
* Date 13-10-2017 modify bte_DestinationMemoryStartAddr bug for
* lcdRegDataWrite(RA8876_DT_STR3, addr>>16), it should be lcdRegDataWrite(RA8876_DT_STR2, addr>>16).
*/
/**
* Date 17-10-2017 Note on using Vsync interrupt from XnINTR pin.
* (1) Leave REG[03h] bit7=0 for XnINTR pin active low
* (2) Enable Vsync time base interrupt by writing 0x10 to REG[0Bh]
* (3) wire an MCU pin to XnINTR, set external interrupt trigger with high->low transition
* (4) On an ISR mark a Vsync event
* (5) Process Vsync event, finally write 0x10 to REG[0Ch] to clear the interrupt flag to start all over
*
* *** Vsync frequency varies with framerate. At 60Hz, Vsync frequency is around 17ms. ***
* DSO waveforms of XnINTR pin and Vsync pin look like figures below (w/ 800x480 60Hz generated).
* Top trace is XnINTR, down trace is Vsync (-ve active)
------
|
|
|______________________________
------ -----------------------
| |
| |
|______|
*/
/**
* Add support for board version 2, for Serial Flash 128Mbit. Directive is : #define BOARD_VERSION_2
* Date: 2019-08-10
*/
#include "Ra8876_Lite.h"
/**
* @note Some remarks on SPI settings:
* (1) Although it is stated that SPI mode 3 is required for accessing RA8876 on datasheet,
* PI mode 0 is working fine too from experience. The difference is an idle clock state.
* SPI mode 3 uses a high SCK when idle whereas SPI mode 0 uses a low SCK when idle.
* Both use the same rising SCK edge for sampling. We are using Mode 0 here because SD card onboard is sharing
* the same SPI bus except for the chip select pin. SD card with SPI is using mode 0. Better compatibility
* is acheived with SPI_MODE0.<br>
* (2) RA8876 can handle max 50MHz SPI clock but this clock is limited by individual Arduino platform leading
* to different SPI clock speeds.
*/
#if defined (_VARIANT_ARDUINO_DUE_X_)
SPISettings _param(42000000, MSBFIRST, SPI_MODE0); //Due
#elif defined (_VARIANT_ARDUINO_101_X_)
SPISettings _param(16000000, MSBFIRST, SPI_MODE0); //Genuino 101 setup parameters
#elif defined (_VARIANT_ARDUINO_ZERO_)
#include "wiring_private.h"
SPISettings _param(12000000, MSBFIRST, SPI_MODE0); //Arduino M0/M0 PRO setup parameters
#elif defined (TEENSYDUINO)
SPISettings _param(50000000, MSBFIRST, SPI_MODE0); //Arduino Teensy 3.1/3.2/3.5/3.6
#elif defined (ESP8266)
SPISettings _param(48000000, MSBFIRST, SPI_MODE0); //ESP8266
#else
SPISettings _param(4000000, MSBFIRST, SPI_MODE0); //Others
#endif
/**
* @brief HAL level board support package setup for SPI and GPIO
*/
void Ra8876_Lite::hal_bsp_init(void)
{
pinMode(_xnreset, OUTPUT); digitalWrite(_xnreset, LOW); //RA8876 in reset mode by default
#if defined (_VARIANT_ARDUINO_DUE_X_)
_SPI = &SPI; //legacy SPI port in use with adapter
_SPI->begin(_xnscs);
#elif defined (TEENSYDUINO)
pinMode(_xnscs, OUTPUT); digitalWrite(_xnscs, HIGH); //RA8876 deselect by default
_SPI = &SPI;
//need to use a SPI port separate from I2S pinout
//initialize the bus for Teensy
_SPI->setMOSI(_mosi);
_SPI->setMISO(_miso);
_SPI->setSCK(_sck);
_SPI->begin();
#elif defined (_VARIANT_ARDUINO_ZERO_)
pinMode(_xnscs, OUTPUT); digitalWrite(_xnscs, HIGH); //RA8876 deselect by default
//need to map SPI to pins D11-D13, declare raSPI static in global space for its a hardware resource
static SPIClass raSPI(&sercom1, _miso, _sck, _mosi, SPI_PAD_0_SCK_1, SERCOM_RX_PAD_3);
_SPI = &raSPI;
_SPI->begin();
pinPeripheral(_mosi, PIO_SERCOM);
pinPeripheral(_miso, PIO_SERCOM);
pinPeripheral(_sck, PIO_SERCOM);
#elif defined (ESP8266)
//With ESP8266 it is possible to use Hw CS for RA8876 SPI access; however, it won't be possible
//to use SD Card anymore because the RA8876 & SD card are sharing the same SPI with a different CS line.
//ESP8266 fail to connect SD card when IO15 is set as the hardware CS with _SPI->setCs(true)
pinMode(_xnscs, OUTPUT); digitalWrite(_xnscs, HIGH); //ESP8266 uses Sw CS for SPI
_SPI = &SPI; //default SPI pinout from ESP-12S
//_SPI->setHwCs(true);//using IO15 as SS pin for RA8876
_SPI->begin();
#elif defined (ESP32)
_SPI = &SPI;
_SPI->begin(RA8876_SCK,RA8876_MISO,RA8876_MOSI,RA8876_XNSCS);
pinMode(_xnscs, OUTPUT); digitalWrite(_xnscs, HIGH); //RA8876 deselect by default
//_SPI->setHwCs(true);
//Only 26MHz with IOMUX
_SPI->setFrequency(24000000);
#else
//default initialization for Arduino 101
pinMode(_xnscs, OUTPUT); digitalWrite(_xnscs, HIGH);
_SPI = &SPI;
_SPI->begin();
#endif
}
/**
* @brief HAL level GPIO write. The pin to write should be set an output in hal_bsp_init().
* @param pin is the pin number
* @param level is '1' or '0'
*/
void Ra8876_Lite::hal_gpio_write(uint8_t pin, bool level)
{
digitalWrite(pin, level);
}
/**
* @brief HAL level delay function
* @param ms is the amount of delay (uint32_t) in milliseconds
*/
void Ra8876_Lite::hal_delayMs(uint32_t ms)
{
delay(ms);
}
/**
* @brief HAL level SPI write function in 16-bit
* @param val is a 16-bit value to write
*/
inline uint16_t Ra8876_Lite::hal_spi_write16(uint16_t val)
{
uint16_t d;
#if defined (_VARIANT_ARDUINO_DUE_X_)
_SPI->beginTransaction(_xnscs, _param);
d = _SPI->transfer16(_xnscs,val);
_SPI->endTransaction();
#elif defined (ESP8266)
uint16_t msb, lsb;
_SPI->beginTransaction(_param);
digitalWrite(_xnscs, LOW); //comment this line if _SPI->setCs(true) is used in hal_bsp_init()
_SPI->write16(val);
msb=SPI1W0&0xff;
lsb = (SPI1W0>>8);
d = ((uint16_t)msb<<8) | lsb;
digitalWrite(_xnscs, HIGH);//comment this line if _SPI->setCs(true) is used in hal_bsp_init()
// _SPI->endTransaction(); there is no need to put endTransaction() here for ESP8266. This fcn doing nothing in ESPClass::SPI.cpp
#elif defined (ESP32)
digitalWrite(_xnscs, LOW);
d = _SPI->transfer16(val);
digitalWrite(_xnscs, HIGH);
#else
_SPI->beginTransaction(_param);
digitalWrite(_xnscs, LOW);
d = _SPI->transfer16(val);
digitalWrite(_xnscs, HIGH);
_SPI->endTransaction();
#endif
return d;
}
/**
* @brief HAL level burst data write
* @param *buf points to 8-bit data buffer
* @param byte_count is the number of data in byte
*/
inline void Ra8876_Lite::hal_spi_write(const uint8_t *buf, uint32_t byte_count)
{
if(!byte_count) return;
#if defined (_VARIANT_ARDUINO_DUE_X_)
_SPI->beginTransaction(_xnscs, _param);
_SPI->transfer(_xnscs,RA8876_SPI_DATAWRITE, SPI_CONTINUE);
byte_count=byte_count-1;
while(byte_count--){
_SPI->transfer(_xnscs, *buf++, SPI_CONTINUE);
}
_SPI->transfer(_xnscs, *buf, SPI_LAST);
_SPI->endTransaction();
#elif defined (ESP8266)
_SPI->beginTransaction(_param);
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
_SPI->transferBytes((uint8_t *)buf, NULL, byte_count);
digitalWrite(_xnscs, HIGH);
#elif defined (ESP32)
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
_SPI->writeBytes((uint8_t *)buf, byte_count);
digitalWrite(_xnscs, HIGH);
#else
_SPI->beginTransaction(_param);//set _param should execute above digitalWrite(_xnscs, LOW) for some platform e.g. Arduino M0
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
while(byte_count--){
_SPI->transfer(*buf++);
}
digitalWrite(_xnscs, HIGH);
_SPI->endTransaction(); //endTransaction() should execute after digitalWrite(_xnscs, HIGH) for some platform e.g. Arduino M0
#endif
}
/**
* @brief HAL level burst data read
* @param *buf points to 8-bit data buffer storing data return
* @param byte_count is the byte count
* @note This function is never called after a comparison on the stability with lcdDataRead().
* Running this function sometimes return false values.
*/
inline void Ra8876_Lite::hal_spi_read(uint8_t *buf, uint32_t byte_count)
{
if(!byte_count) return;
#if defined (_VARIANT_ARDUINO_DUE_X_)
_SPI->beginTransaction(_xnscs, _param);
_SPI->transfer(_xnscs,RA8876_SPI_DATAREAD, SPI_CONTINUE);
byte_count=byte_count-1;
while(byte_count--){
*buf++ = _SPI->transfer(_xnscs, 0x00, SPI_CONTINUE);
}
*buf = _SPI->transfer(_xnscs, 0x00, SPI_LAST);
_SPI->endTransaction();
#elif defined (ESP8266)
_SPI->beginTransaction(_param);
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAREAD);
_SPI->transferBytes(NULL, buf, byte_count);
digitalWrite(_xnscs, HIGH);
#elif defined (ESP32)
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAREAD);
_SPI->transferBytes(NULL, buf, byte_count);
digitalWrite(_xnscs, HIGH);
#else
_SPI->beginTransaction(_param);//set _param should execute above digitalWrite(_xnscs, LOW) for some platform e.g. Arduino M0
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAREAD);
while(byte_count--){
*buf++ = _SPI->transfer(0x00);
}
digitalWrite(_xnscs, HIGH);
_SPI->endTransaction(); //endTransaction() should execute after digitalWrite(_xnscs, HIGH) for some platform e.g. Arduino M0
#endif
}
/**
* @brief HAL level burst data write
* @param *buf points to 16-bit data buffer
* @param word_count is the data count in uint16_t
*/
inline void Ra8876_Lite::hal_spi_write(const uint16_t *buf, uint32_t word_count)
{
if(!word_count) return;
#if defined (_VARIANT_ARDUINO_DUE_X_)
_SPI->beginTransaction(_xnscs, _param);
_SPI->transfer(_xnscs,RA8876_SPI_DATAWRITE, SPI_CONTINUE);
word_count=word_count-1;
while(word_count--){
_SPI->transfer(_xnscs, (uint8_t)(*buf), SPI_CONTINUE);
_SPI->transfer(_xnscs, (uint8_t)(*buf>>8), SPI_CONTINUE);
buf++;
}
_SPI->transfer(_xnscs, (uint8_t)(*buf), SPI_CONTINUE);
_SPI->transfer(_xnscs, (uint8_t)(*buf>>8), SPI_LAST);
_SPI->endTransaction();
#elif defined (ESP8266)
_SPI->beginTransaction(_param);
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
while(word_count--)
{
_SPI->write((uint8_t)(*buf));
_SPI->write((uint8_t)(*buf>>8));
buf++;
}
digitalWrite(_xnscs, HIGH);
#elif defined (ESP32)
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
_SPI->writePixels(buf, word_count<<1); //word_count<<1 chg to byte count for SPI.writePixels()
digitalWrite(_xnscs, HIGH);
#else
_SPI->beginTransaction(_param); //set _param should execute above digitalWrite(_xnscs, LOW) for some platform e.g. Arduino M0
digitalWrite(_xnscs, LOW);
_SPI->transfer(RA8876_SPI_DATAWRITE);
while(word_count--){
_SPI->transfer((uint8_t)(*buf));
_SPI->transfer((uint8_t)(*buf>>8));
buf++;
}
digitalWrite(_xnscs, HIGH);
_SPI->endTransaction(); //endTransaction() should execute after digitalWrite(_xnscs, HIGH) for some platform e.g. Arduino M0
#endif
}
/**
* @brief HAL level disable global interrupts
*/
inline void Ra8876_Lite::hal_di(void)
{
noInterrupts();
}
/**
* @brief HAL level enable global interrupts
*/
inline void Ra8876_Lite::hal_ei(void)
{
interrupts();
}
/**
* @brief Class constructor
* @param xnscs is the CS pin for SPI
* @param xnreset is hardware reset pin
* @param mosi is the MOSI pin for SPI
* @param miso is the MOSI pin for SPI
* @param sck is the serial clock pin for SPI
*/
Ra8876_Lite::Ra8876_Lite(uint8_t xnscs, uint8_t xnreset, uint8_t mosi, uint8_t miso, uint8_t sck):
_xnscs(xnscs), _xnreset(xnreset), _mosi(mosi), _miso(miso), _sck(sck){}
/**
* @brief Initialize RA8876 with either predefined LCD timing parameters(manual) or EDID information(automatic).
* @param *timing points to a LCDParam structure when no EDID information is available from the monitor.<br>
* Manual timing parameters default to 720P for 9904 Boot ROM
* Some common LCD parameters defined in LcdParam.h header file.
* @param *edid points to an EDID structure when it is available from the monitor.<br>
* EDID is a data structure to allow a display to pass its identity and display parameters to the host.<br>
* In CH703x, EDID is read from the monitor through DDC_SCL & DDC_SD pins (I2C) into a buffer
* which can be accessed by the host.
* @param automatic is a boolean flag indicating if EDID is available. It defaults to false.<br>
* @return true if successful.<br>
* false if failed.<br>
* @note Example to use
* Ra8876_Lite ra8876lite;
* ra8876lite.begin(); //assume no EDID is required, manually setup RA8876 for default 720P HDTV resolution
*/
bool Ra8876_Lite::begin(const LCDParam *timing, MonitorInfo *edid, bool automatic)
{
_initialised = false;
#ifdef DEBUG_LLD_RA8876
printf("Running ra8876_Lite::begin(args)...\n");
#endif
hal_bsp_init();
//Hard reset RA8876
hal_gpio_write(_xnreset, 1);
hal_delayMs(1);
hal_gpio_write(_xnreset, 0);
hal_delayMs(1);
hal_gpio_write(_xnreset, 1);
hal_delayMs(10);
if(!checkIcReady(10))
{return _initialised;}
//read ID code must disable pll, 01h bit7 set 0
lcdRegDataWrite(0x01,0x00);
hal_delayMs(1);
if ((lcdRegDataRead(0xff) != 0x76)&&(lcdRegDataRead(0xff) != 0x77))
{
#ifdef DEBUG_LLD_RA8876
printf("RA8876 not found!\n");
#endif
return _initialised;
}
else
{
#ifdef DEBUG_LLD_RA8876
printf("RA8876 connect pass!\n");
#endif
}
if(automatic) //EDID information from monitor
{
lcd =
{
edid->dsc_product_name,
(uint16_t)edid->detailed_timings[0].h_addr, (uint16_t)edid->detailed_timings[0].v_addr, //width, height
(uint16_t)edid->detailed_timings[0].h_blank, //horizontal blanking
(uint16_t)edid->detailed_timings[0].h_front_porch, //horizontal front porch
(uint16_t)edid->detailed_timings[0].h_sync, //horizontal pulse width
(uint16_t)edid->detailed_timings[0].v_blank, //vertical blanking
(uint16_t)edid->detailed_timings[0].v_front_porch, //vertical front porch
(uint16_t)edid->detailed_timings[0].v_sync, //vertical pulse width
(uint32_t)edid->detailed_timings[0].pixel_clock/1000000UL, //pixel clock
!(edid->detailed_timings[0].digital.negative_vsync), //vsync polarity
!(edid->detailed_timings[0].digital.negative_hsync), //hsync polarity
0, //rising edge pclk
0, //+ve de
};
}
else //manual LCD timing parameters
{
lcd=
{
timing->name,
timing->width, timing->height,
timing->hblank,
timing->hfporch,
timing->hpulse,
timing->vblank,
timing->vfporch,
timing->vpulse,
timing->pclk,
timing->vsyncPolarity,
timing->hsyncPolarity,
timing->pclkPolarity,
timing->dePolarity,
};
}
if(!ra8876PllInitial(lcd.pclk))
{
#ifdef DEBUG_LLD_RA8876
printf("PLL init failed!\n");
#endif
return _initialised;
}
if(!ra8876SdramInitial())
{
#ifdef DEBUG_LLD_RA8876
printf("SDRAM init failed!\n");
#endif
return _initialised;
}
//REG[01h]
lcdRegDataWrite(RA8876_CCR,RA8876_PLL_ENABLE<<7|RA8876_WAIT_NO_MASK<<6|RA8876_KEY_SCAN_DISABLE<<5|RA8876_TFT_OUTPUT24<<3
|RA8876_I2C_MASTER_DISABLE<<2|RA8876_SERIAL_IF_ENABLE<<1|RA8876_HOST_DATA_BUS_SERIAL);
//REG[02h]
lcdRegDataWrite(RA8876_MACR,RA8876_DIRECT_WRITE<<6|RA8876_READ_MEMORY_LRTB<<4|RA8876_WRITE_MEMORY_LRTB<<1);
//REG[03h]
lcdRegDataWrite(RA8876_ICR,RA8876_GRAPHIC_MODE<<2|RA8876_MEMORY_SELECT_IMAGE);
lcdRegDataWrite(RA8876_DPCR,(lcd.pclkPolarity<<7)|(RA8876_DISPLAY_OFF<<6)|(RA8876_OUTPUT_RGB)); //REG[12h]
//edid->detailed_timings[0].digital.negative_vsync = 1 if polarity is negative
//REG[13h] bit7 XHSYNC polarity 0: Low active, 1: High active
//edid->detailed_timings[0].digital.negative_hsync = 1 if polarity is negative
//REG[13h] bit6 XVSYNC polarity 0: Low active, 1: High active
//All CEA-861-D video signals are DE high active
lcdRegDataWrite(RA8876_PCSR, lcd.hsyncPolarity<<7|lcd.vsyncPolarity<<6|lcd.dePolarity<<5); //REG[13h]
lcdHorizontalWidthVerticalHeight(lcd.width, lcd.height);
lcdHorizontalBackPorch(lcd.hblank-lcd.hfporch-lcd.hpulse);
lcdHsyncStartPosition(lcd.hfporch);
lcdHsyncPulseWidth(lcd.hpulse);
lcdVerticalBackPorch(lcd.vblank-lcd.vfporch-lcd.vpulse);
lcdVsyncStartPosition(lcd.vfporch);
lcdVsyncPulseWidth(lcd.vpulse);
//REG[B9h]: enable XnSFCS1 (pin 38) as chip select for Serial Flash W25Q256FV and enter 4-Byte mode for it
setSerialFlash();
#ifdef DEBUG_LLD_RA8876
printf("Name of LCD: %s\n", lcd.name);
printf("LCD width: %d\n", lcd.width);
printf("LCD height: %d\n", lcd.height);
printf("H blanking: %d\n", lcd.hblank);
printf("H front porch: %d\n", lcd.hfporch);
printf("H pulse width: %d\n", lcd.hpulse);
printf("V blanking: %d\n", lcd.vblank);
printf("V front porch: %d\n", lcd.vfporch);
printf("V pulse width: %d\n", lcd.vpulse);
printf("Pixel clock: %ld\n", lcd.pclk);
printf("Pclk Polarity: %d\n", lcd.pclkPolarity);
printf("Vsync Polarity: %d\n", lcd.vsyncPolarity);
printf("Hsync Polarity: %d\n", lcd.hsyncPolarity);
printf("DE Polarity: %d\n", lcd.dePolarity);
#endif
canvasImageWidth(lcd.width, lcd.height);
activeWindowWH(lcd.width,lcd.height);
_initialised = true;
return _initialised;
}
/**
* @brief Register write function.
* @param reg is the register to write.
*/
void Ra8876_Lite::lcdRegWrite(uint8_t reg)
{
uint16_t _data = ((uint16_t)RA8876_SPI_CMDWRITE<<8 | reg);
hal_spi_write16((uint16_t)_data);
}
/**
* @brief Data write function.
* @param data is the value to write in 8-bit width.
*/
void Ra8876_Lite::lcdDataWrite(uint8_t data)
{
uint16_t _data = ((uint16_t)RA8876_SPI_DATAWRITE<<8 | data);
hal_spi_write16((uint16_t)_data);
}
/**
* @brief Data read function with register location predefined.
* @return data return
*/
uint8_t Ra8876_Lite::lcdDataRead(void)
{
uint8_t vret;
uint16_t _data = ((uint16_t)RA8876_SPI_DATAREAD<<8 | 0xFF);
vret = (uint8_t)hal_spi_write16((uint16_t)_data);
return (vret);
}
/**
* @brief Status read function from the STATUS register.
* @return value returned from the STATUS register
*/
uint8_t Ra8876_Lite::lcdStatusRead(void)
{
uint8_t sret;
uint16_t _data = ((uint16_t)RA8876_SPI_STATUSREAD<<8 | 0xFF);
sret = (uint8_t)hal_spi_write16((uint16_t)_data);
return (sret);
}
/**
* @brief Write 16-bit data to RA8876.
* @note This is to support SPI interface to write 16bpp data after Regwrite 04h
* @param data is the value to write, often used in pixel write in 5-6-5 color mode
*/
void Ra8876_Lite::lcdDataWrite16bpp(uint16_t data)
{
uint8_t buf[2];
buf[0] = (uint8_t)data;
buf[1] = (uint8_t)(data>>8);
hal_spi_write((const uint8_t *)buf, 2);
}
/**
* @brief Make function call to lcdRegDataWrite(), lcdRegDataRead(), & lcdStatusRead() from a serial terminal.
* @param *msg is a pointer to incoming message in ASCII characters.
* @note Prerequisite: printf(args) should be available<br>
* Example to use:<br>
* In Arduino IDE, open Serial Monitor and type in following commands and click <send><br>
* spi,w,0x12,0x60 //SPI write to RA8876 at register 0x12 a value of 0x60 to turn on color bar test.<br>
* spi,w,0x12,0x40 //SPI write to return to normal operation.<br>
* spi,r,0x12 //SPI read from register 0x12.<br>
* spi,r,status //SPI status read from the STATUS register. <br>
*/
void Ra8876_Lite::parser(char *msg)
{
char *token=NULL;
bool isWriteCommand = false;
const char delimiters[] = ", ";
uint8_t reg, val;
token = strtok(msg, delimiters);
if(!strcmp(token,"SPI") || !strcmp(token, "spi"))
{
token = strtok(NULL, delimiters); //get r or w
if(!strcmp(token,"w") || !strcmp(token, "W")){
isWriteCommand=true;
}
else if (!strcmp(token,"r") || !strcmp(token, "R")){
isWriteCommand=false;
}
else{
printf("It is not a valid operation.\n");
return;
}
}
if(isWriteCommand)
{
token = strtok(NULL, delimiters); //get the register address to write
reg = strtol(token, NULL, 16); //convert char to hex
token = strtok(NULL, delimiters); //get the value to write
val = strtol(token, NULL, 16);
lcdRegDataWrite(reg, val);
printf("Write to RA8876@%2Xh, val=%2Xh OK.\n", reg, val);
}
else
{
token = strtok(NULL, delimiters); //get the next token to see if it is a "status"
if(!strncmp(token,"status",6) || !strncmp(token,"STATUS",6))
{
val = lcdStatusRead();
printf("Status read from RA8876=%2Xh OK.\n", val);
}
else
{
reg = strtol(token, NULL, 16);
val = lcdRegDataRead(reg);
printf("Value read from RA8876@%2Xh, val=%2Xh\n", reg, val);
}
}
}
/**
* @brief Return color mode
* @return COLOR_MODE enum<br>
enum {
COLOR_8BPP_RGB332=1, //8BPP in R[3]G[3]B[2], input data format=R[7:5]G[7:5]B[7:6]
COLOR_16BPP_RGB565=2, //16BPP in R[5]G[6]B[5], input data format=G[4:2]B[7:3], [7:3]G[7:5]
COLOR_24BPP_RGB888=3, //24BPP in B[7]G[7]R[7], input data format=B[7:0]G[7:0]R[7:0]
COLOR_6BPP_ARGB2222=4, //Index display with opacity (aRGB 2:2:2:2)
COLOR_12BPP_ARGB4444=5 //12BPP with ARGB of 4bits each, input data format=G[7:4]B[7:4], A[3:0]R[7:4]
} COLOR_MODE;
*/
COLOR_MODE Ra8876_Lite::getColorMode(void)
{
return _colorMode;
}
/**
* @brief Return color depth in bit-per-pixel (bpp)
* @return Color depth in bpp = 1 for RGB332/ARGB2222, bpp=2 for RGB565/ARGB4444, bpp=3 for RGB888, bpp=0 for error
*/
uint8_t Ra8876_Lite::getColorDepth(void)
{
COLOR_MODE _mode = getColorMode();
uint8_t bpp = 2;
switch(_mode)
{
case COLOR_8BPP_RGB332:
case COLOR_6BPP_ARGB2222:
bpp = 1;
break;
case COLOR_16BPP_RGB565:
case COLOR_12BPP_ARGB4444:
bpp = 2;
break;
case COLOR_24BPP_RGB888:
bpp = 3;
break;
}
return bpp;
}
/**
* @brief This function write 8-bit data to a register
* @param reg is the register address to write
* @param data is the data to write
*/
void Ra8876_Lite::lcdRegDataWrite(uint8_t reg,uint8_t data)
{
lcdRegWrite(reg);
lcdDataWrite(data);
}
/**
* @brief This function read a 8-bit value from a register
* @param reg is the register address to read from
* @return content at the register
*/
uint8_t Ra8876_Lite::lcdRegDataRead(uint8_t reg)
{
lcdRegWrite(reg);
return (lcdDataRead());
}
/**
* @brief Polling until memory write FIFO buffer is not full [Status Register] bit7
*/
void Ra8876_Lite::checkWriteFifoNotFull(void)
{
uint16_t i;
for(i=0;i<10000;i++) //Please according to your usage to modify i value.
{
if( (lcdStatusRead()&RA8876_STSR_WR_FIFO_FULL)==0 ){break;}
}
}
/**
* @brief Polling for memory write FIFO buffer full [Status Register] bit7
* @note Only when Memory Write FIFO is not full, MPU may write another one pixel
* @param timeout is the number of read status function to run before a timeout error
* @return true Memory Write FIFO is not full<br>
* false Memory Write FIFO is full<br>
*/
bool Ra8876_Lite::checkWriteFifoNotFull(uint32_t timeout)
{
while(timeout--)
{
if( (lcdStatusRead()&RA8876_STSR_WR_FIFO_FULL)==0 )
return true;
}
#ifdef DEBUG_LLD_RA8876
printf("checkWriteFifoNotFull() timeout error, Write FIFO is full!\n");
#endif
return false;
}
/**
* @brief This function polls until memory write FIFO is empty
*/
void Ra8876_Lite::checkWriteFifoEmpty(void)
{
uint16_t i;
for(i=0;i<10000;i++)
{
if( (lcdStatusRead()&RA8876_STSR_WR_FIFO_EMPTY)==RA8876_STSR_WR_FIFO_EMPTY ){break;}
}
}
/**
* @brief This function polls for Memory Write FIFO empty [Status Register] bit6
* @param timeout measured in number of status read cycle to run
* @return false Memory Write FIFO is not empty.
* true Memory Write FIFO is empty.
*/
bool Ra8876_Lite::checkWriteFifoEmpty(uint32_t timeout)
{
while(timeout--)
{
if( (lcdStatusRead()&RA8876_STSR_WR_FIFO_EMPTY)==RA8876_STSR_WR_FIFO_EMPTY )
return true;
}
#ifdef DEBUG_LLD_RA8876
printf("checkWriteFifoEmpty() timeout error, Memory Write FIFO is not empty!\n");
#endif
return false;
}
/**
* @brief This function polls until Memory Read FIFO not full
* @note [Status Register] bit5
* [Status Register] bit5 =0: Memory Read FIFO is not full<br>
* [Status Register] bit5 =1: Memory Read FIFO is full<br>
*/
void Ra8876_Lite::checkReadFifoNotFull(void)
{
uint16_t i;
for(i=0;i<10000;i++) //Please according to your usage to modify i value.
{if( (lcdStatusRead()&RA8876_STSR_RD_FIFO_FULL)==0x00 ){break;}}
}
/**
* @brief This function polls until Memory Read FIFO not empty
* @note [Status Register] bit4
* [Status Register] bit4=0: Memory Read FIFO is not empty<br>
* [Status Register] bit4=1: Memory Read FIFO is empty.
*/
void Ra8876_Lite::checkReadFifoNotEmpty(void)
{
uint16_t i;
for(i=0;i<10000;i++)// //Please according to your usage to modify i value.
{if( (lcdStatusRead()&RA8876_STSR_RD_FIFO_EMPTY)==0x00 ){break;}}
}
/**
* @brief Polling for core task status until it is not busy [Status Register] bit3
* @note Tasks incl. BTE, Geometry engine, Serial flash DMA, Text write or Graphic write
*/
void Ra8876_Lite::check2dBusy(void)
{
uint32_t i;
for(i=0;i<1000000;i++) //Please according to your usage to modify i value.
{
if( (lcdStatusRead()&RA8876_STSR_CORE_BUSY)==0x00 )
{break;}
}
}
/**
* @brief Polling for core task status [Status Register] bit3
* @note Tasks incl. BTE, Geometry engine, Serial flash DMA, Text write or Graphic write
* @param timeout measured in number of status read cycle to run
* @return true if core is still busy with a timeout error
* false if core is not busy
*/
bool Ra8876_Lite::check2dBusy(uint32_t timeout)
{
while(timeout--)
{
if((lcdStatusRead()&RA8876_STSR_CORE_BUSY)==0x00)
return false;
}
#ifdef DEBUG_LLD_RA8876
printf("check2dBusy() timeout error, core is still busy!\n");
#endif
return true;
}
/**
* @brief Polling for [Status Register] bit2 for SDRAM status
* @param timeout in number of status read cycle
* @return true if SDRAM is ready for access<br>
* false if SDRAM is not ready for access<br>
*/
bool Ra8876_Lite::checkSdramReady(uint32_t timeout)
{
while(timeout--)
{
if((lcdStatusRead()&RA8876_STSR_SDRAM_READY)==RA8876_STSR_SDRAM_READY)
return true;
}
#ifdef DEBUG_LLD_RA8876
printf("checkSdramReady() timeout error!\n");
#endif
return false;
}
/**
* @brief Polling for RA8876 operation status [Status Register]:bit1
* @note Inhibit operation state means internal reset event keep running or
* initial display still running or chip enter power saving state.
* @param timeout in number of status read cycle
* @return true: Normal operation state<br>
* false: Inhibit operation state<br>
*/
bool Ra8876_Lite::checkIcReady(uint32_t timeout)
{
while(timeout--)
{
if( (lcdStatusRead()&RA8876_STSR_OP_MODE_INHIBIT)==0x00 )
{return true;}
}
#ifdef DEBUG_LLD_RA8876
printf("checkIcReady() timeout error!\n");
#endif
return false;
}
/**
* @brief PLL initialization function.
* @note PLL output is calculated from this formula<br>
* PLL = OSC_FREQ*(PLLDIVN+1)/2^PLLDIVM, with conditions 100MHz<=PLL<=600MHz with 10MHz<=OSC_FREQ/2^PLLDIVM<=40MHz<br>
* Clock generated (e.g. Pclk) = PLL/2^PLLDIVK<br>
* OSC_FREQ is the crystal frequency onboard (12MHz)<br>
* PLLDIVM = 0 ~ 1<br>
* PLLDIVK = 0 ~ 3<br>
* PLLDIVN = 1 ~ 63<br>
* Example, we want to output a pixel clock of 74250000Hz,<br>
* set PLLDIVM = 0, PLLDIVN=49, PLL = 12*(49+1)/(2^0) = 600<br>
* set PLLDIVK = 3, PCLK = 600/2^3 = 75MHz (~74.25MHz)<br>
* @param pclk is the pixel clock with unit in MHz<br>
* @return true if successful<br>
* false if not successful. Reason can be a too high pclk frequency<br>
*
*/
bool Ra8876_Lite::ra8876PllInitial(uint16_t pclk)
{
#ifdef DEBUG_LLD_RA8876
printf("Ra8876_Lite::ra8876PllInitial(pclk)...\n");
#endif
uint8_t xPLLDIVN;
uint8_t xPLLDIVK;
//disable PLL prior to parameter changes
uint8_t CCR = lcdRegDataRead(RA8876_CCR);
lcdRegDataWrite(RA8876_CCR, CCR&0x7F); //PLL_EN @ bit[7]
if ((pclk>54)&&(pclk<=SPLL_FREQ_MAX))
{
//e.g. VID code 35,36 2880x480 @59.940Hz pclk=108MHz
//xPLLDIVM=0, xPLLDIVK=2
xPLLDIVK = 2;
//to round off to nearest 0.5, pclk*4/OSC_FREQ-1+0.5 become pclk*4/OSC_FREQ-0.5.
//this round 48.5 to 49 for a higher frame rate.
//xPLLDIVN = round((float)(pclk*4/OSC_FREQ)-0.5);
xPLLDIVN = (pclk*4/OSC_FREQ)-1;
}
else if ((pclk<=54) || (pclk>SPLL_FREQ_MAX)) //pixel clock > 148MHz is not supported. Bound any frequency > 148 to 74MHz
{
//e.g. VID code 4, 1280x720 @60Hz pclk=74.250MHz (round off to 74MHz)
// VID code 31, 1920x1080 @50Hz pclk=148.5MHz (round off to 148MHz)
//xPLLDIVM=0, xPLLDIVK=3
if(pclk>SPLL_FREQ_MAX) {pclk=74;} //bound any pixel clock higher than 108MHz to 74MHz for low field rate
xPLLDIVK = 3;
//xPLLDIVN = round((float)(pclk*8/OSC_FREQ)-0.5);
xPLLDIVN = (pclk*8/OSC_FREQ)-1;
}
/*
#ifdef DEBUG_LLD_RA8876
printf("Setting pixel clock with xPLLDIVN=%d, xPLLDIVK=%d\n", xPLLDIVN, xPLLDIVK);
#endif
*/
if((xPLLDIVN>63)||(xPLLDIVN==0))
{
#ifdef DEBUG_LLD_RA8876
printf("xPLLDIVN out of range. Check video source.\n");
#endif
return false;
}
lcdRegDataWrite(0x05, xPLLDIVK<<1); //set SCLK PLL control register 1
lcdRegDataWrite(0x06, xPLLDIVN); //set SCLK PLL control register 2
lcdRegDataWrite(0x07,0x02); //PLL Divided by 2
lcdRegDataWrite(0x08,(DRAM_FREQ*2/OSC_FREQ)-1); //DRAM_FREQ*2/OSC_FREQ=166*2/12=27
lcdRegDataWrite(0x09,0x02); //PLL Divided by 2
lcdRegDataWrite(0x0A,(CORE_FREQ*2/OSC_FREQ)-1); //CORE_FREQ*2/OSC_FREQ=144*2/12=24
lcdRegDataWrite(RA8876_CCR, RA8876_PLL_ENABLE<<7); //enable PLL
hal_delayMs(20); //wait PLL stable
if((lcdRegDataRead(RA8876_CCR)&0x80)==0x80)
{
#ifdef DEBUG_LLD_RA8876
printf("PLL initialization successful\n");
#endif
return true;
}