forked from sepp89117/ILI9486_RPi-t4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathILI9486_t3n.h
1104 lines (968 loc) · 38.8 KB
/
ILI9486_t3n.h
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 library is for Waveshare 4inch RPi LCD (C) with ILI9486 and Teensy 4.x usage only
// Forked from:
// https://github.com/kurte/ILI9341_t3n
#ifndef _ILI9486_t3NH_
#define _ILI9486_t3NH_
#ifdef _SPIN_H_INCLUDED
#warning "Spin library is no longer required"
#endif
#define _SPIN_H_INCLUDED // try to avoid spin library from loading.
#define ILI9486_USE_DMAMEM
// Allow us to enable or disable capabilities, particully Frame Buffer and Clipping for speed and size
#ifndef DISABLE_ILI9486_FRAMEBUFFER
#if defined(__MK66FX1M0__) // T3.6
#define ENABLE_ILI9486_FRAMEBUFFER
#define SCREEN_DMA_NUM_SETTINGS 3 // see if making it a constant value makes difference...
#elif defined(__MK64FX512__) // T3.5
#define ENABLE_ILI9486_FRAMEBUFFER
#define SCREEN_DMA_NUM_SETTINGS 4 // see if making it a constant value makes difference...
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#define ENABLE_ILI9486_FRAMEBUFFER
#define SCREEN_DMA_NUM_SETTINGS 3 // see if making it a constant value makes difference...
#endif
#endif
// Allow way to override using SPI
#ifdef __cplusplus
#include "Arduino.h"
#include <SPI.h>
#include <DMAChannel.h>
#endif
#include <stdint.h>
#include "ILI9486_fonts.h"
#define ILI9486_TFTWIDTH 320
#define ILI9486_TFTHEIGHT 480
#define ILI9486_NOP 0x00
#define ILI9486_SWRESET 0x01
#define ILI9486_RDDID 0x04
#define ILI9486_RDDST 0x09
#define ILI9486_SLPIN 0x10
#define ILI9486_SLPOUT 0x11
#define ILI9486_PTLON 0x12
#define ILI9486_NORON 0x13
#define ILI9486_RDMODE 0x0A
#define ILI9486_RDMADCTL 0x0B
#define ILI9486_RDPIXFMT 0x0C
#define ILI9486_RDIMGFMT 0x0D
#define ILI9486_RDSELFDIAG 0x0F
#define ILI9486_INVOFF 0x20
#define ILI9486_INVON 0x21
#define ILI9486_GAMMASET 0x26
#define ILI9486_DISPOFF 0x28
#define ILI9486_DISPON 0x29
#define ILI9486_CASET 0x2A
#define ILI9486_PASET 0x2B
#define ILI9486_RAMWR 0x2C
#define ILI9486_RAMRD 0x2E
#define ILI9486_PTLAR 0x30
#define ILI9486_MADCTL 0x36
#define ILI9486_VSCRSADD 0x37
#define ILI9486_PIXFMT 0x3A
#define ILI9486_FRMCTR1 0xB1
#define ILI9486_FRMCTR2 0xB2
#define ILI9486_FRMCTR3 0xB3
#define ILI9486_INVCTR 0xB4
#define ILI9486_DFUNCTR 0xB6
#define ILI9486_PWCTR1 0xC0
#define ILI9486_PWCTR2 0xC1
#define ILI9486_PWCTR3 0xC2
#define ILI9486_PWCTR4 0xC3
#define ILI9486_PWCTR5 0xC4
#define ILI9486_VMCTR1 0xC5
#define ILI9486_VMCTR2 0xC7
#define ILI9486_RDID1 0xDA
#define ILI9486_RDID2 0xDB
#define ILI9486_RDID3 0xDC
#define ILI9486_RDID4 0xDD
#define ILI9486_GMCTRP1 0xE0
#define ILI9486_GMCTRN1 0xE1
/*
#define ILI9486_PWCTR6 0xFC
*/
// Color definitions
#define ILI9486_BLACK 0x0000 /* 0, 0, 0 */
#define ILI9486_NAVY 0x000F /* 0, 0, 128 */
#define ILI9486_DARKGREEN 0x03E0 /* 0, 128, 0 */
#define ILI9486_DARKCYAN 0x03EF /* 0, 128, 128 */
#define ILI9486_MAROON 0x7800 /* 128, 0, 0 */
#define ILI9486_PURPLE 0x780F /* 128, 0, 128 */
#define ILI9486_OLIVE 0x7BE0 /* 128, 128, 0 */
#define ILI9486_LIGHTGREY 0xC618 /* 192, 192, 192 */
#define ILI9486_DARKGREY 0x7BEF /* 128, 128, 128 */
#define ILI9486_BLUE 0x001F /* 0, 0, 255 */
#define ILI9486_GREEN 0x07E0 /* 0, 255, 0 */
#define ILI9486_CYAN 0x07FF /* 0, 255, 255 */
#define ILI9486_RED 0xF800 /* 255, 0, 0 */
#define ILI9486_MAGENTA 0xF81F /* 255, 0, 255 */
#define ILI9486_YELLOW 0xFFE0 /* 255, 255, 0 */
#define ILI9486_WHITE 0xFFFF /* 255, 255, 255 */
#define ILI9486_ORANGE 0xFD20 /* 255, 165, 0 */
#define ILI9486_GREENYELLOW 0xAFE5 /* 173, 255, 47 */
#define ILI9486_PINK 0xF81F
#define ILI9486_GREY 0x2104 // Dark grey 16 bit colour
#define CL(_r,_g,_b) ((((_r)&0xF8)<<8)|(((_g)&0xFC)<<3)|((_b)>>3))
#ifndef ILI9486_swap
#define ILI9486_swap(a, b) { typeof(a) t = a; a = b; b = t; }
#endif
#define sint16_t int16_t
// Lets see about supporting Adafruit fonts as well?
#ifndef _GFXFONT_H_
#define _GFXFONT_H_
/// Font data stored PER GLYPH
typedef struct {
uint16_t bitmapOffset; ///< Pointer into GFXfont->bitmap
uint8_t width; ///< Bitmap dimensions in pixels
uint8_t height; ///< Bitmap dimensions in pixels
uint8_t xAdvance; ///< Distance to advance cursor (x axis)
int8_t xOffset; ///< X dist from cursor pos to UL corner
int8_t yOffset; ///< Y dist from cursor pos to UL corner
} GFXglyph;
/// Data stored for FONT AS A WHOLE
typedef struct {
uint8_t *bitmap; ///< Glyph bitmaps, concatenated
GFXglyph *glyph; ///< Glyph array
uint8_t first; ///< ASCII extents (first char)
uint8_t last; ///< ASCII extents (last char)
uint8_t yAdvance; ///< Newline distance (y axis)
} GFXfont;
#endif // _GFXFONT_H_
//These enumerate the text plotting alignment (reference datum point)
#define TL_DATUM 0 // Top left (default)
#define TC_DATUM 1 // Top centre
#define TR_DATUM 2 // Top right
#define ML_DATUM 3 // Middle left
#define CL_DATUM 3 // Centre left, same as above
#define MC_DATUM 4 // Middle centre
#define CC_DATUM 4 // Centre centre, same as above
#define MR_DATUM 5 // Middle right
#define CR_DATUM 5 // Centre right, same as above
#define BL_DATUM 6 // Bottom left
#define BC_DATUM 7 // Bottom centre
#define BR_DATUM 8 // Bottom right
//#define L_BASELINE 9 // Left character baseline (Line the 'A' character would sit on)
//#define C_BASELINE 10 // Centre character baseline
//#define R_BASELINE 11 // Right character baseline
#ifdef __cplusplus
// At all other speeds, _pspi->beginTransaction() will use the fastest available clock
#define ILI9486_SPICLOCK 80e6 //valid speeds are (120mhz, 80mhz, 60mhz, 48, 40, 34.29, 30mhz, 26.67, 24mhz
#define ILI9486_SPICLOCK_READ 2000000
class ILI9486_t3n : public Print
{
public:
ILI9486_t3n(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255,
uint8_t _MOSI=11, uint8_t _SCLK=13, uint8_t _MISO=12);
void begin(uint32_t spi_clock=ILI9486_SPICLOCK, uint32_t spi_clock_read=ILI9486_SPICLOCK_READ);
void sleep(bool enable);
void pushColor(uint16_t color);
void fillScreen(uint16_t color);
inline void fillWindow(uint16_t color) {fillScreen(color);}
void drawPixel(int16_t x, int16_t y, uint16_t color);
void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
void fillRectHGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color1, uint16_t color2);
void fillRectVGradient(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color1, uint16_t color2);
void fillScreenVGradient(uint16_t color1, uint16_t color2);
void fillScreenHGradient(uint16_t color1, uint16_t color2);
void setRotation(uint8_t r);
void setScroll(uint16_t offset);
void invertDisplay(boolean i);
void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
// Pass 8-bit (each) R,G,B, get back 16-bit packed color
static uint16_t color565(uint8_t r, uint8_t g, uint8_t b) {
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
//color565toRGB - converts 565 format 16 bit color to RGB
static void color565toRGB(uint16_t color, uint8_t &r, uint8_t &g, uint8_t &b) {
r = (color>>8)&0x00F8;
g = (color>>3)&0x00FC;
b = (color<<3)&0x00F8;
}
//color565toRGB14 - converts 16 bit 565 format color to 14 bit RGB (2 bits clear for math and sign)
//returns 00rrrrr000000000,00gggggg00000000,00bbbbb000000000
//thus not overloading sign, and allowing up to double for additions for fixed point delta
static void color565toRGB14(uint16_t color, int16_t &r, int16_t &g, int16_t &b) {
r = (color>>2)&0x3E00;
g = (color<<3)&0x3F00;
b = (color<<9)&0x3E00;
}
//RGB14tocolor565 - converts 14 bit RGB back to 16 bit 565 format color
static uint16_t RGB14tocolor565(int16_t r, int16_t g, int16_t b)
{
return (((r & 0x3E00) << 2) | ((g & 0x3F00) >>3) | ((b & 0x3E00) >> 9));
}
//uint8_t readdata(void);
uint8_t readcommand8(uint8_t reg, uint8_t index = 0);
// Added functions to read pixel data...
uint16_t readPixel(int16_t x, int16_t y);
void readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors);
void writeRect(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors);
// writeRect8BPP - write 8 bit per pixel paletted bitmap
// bitmap data in array at pixels, one byte per pixel
// color palette data in array at palette
void writeRect8BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette );
// writeRect4BPP - write 4 bit per pixel paletted bitmap
// bitmap data in array at pixels, 4 bits per pixel
// color palette data in array at palette
// width must be at least 2 pixels
void writeRect4BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette );
// writeRect2BPP - write 2 bit per pixel paletted bitmap
// bitmap data in array at pixels, 4 bits per pixel
// color palette data in array at palette
// width must be at least 4 pixels
void writeRect2BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette );
// writeRect1BPP - write 1 bit per pixel paletted bitmap
// bitmap data in array at pixels, 4 bits per pixel
// color palette data in array at palette
// width must be at least 8 pixels
void writeRect1BPP(int16_t x, int16_t y, int16_t w, int16_t h, const uint8_t *pixels, const uint16_t * palette );
// writeRectNBPP - write N(1, 2, 4, 8) bit per pixel paletted bitmap
// bitmap data in array at pixels
// Currently writeRect1BPP, writeRect2BPP, writeRect4BPP use this to do all of the work.
//
void writeRectNBPP(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t bits_per_pixel,
const uint8_t *pixels, const uint16_t * palette );
// from Adafruit_GFX.h
void drawCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, uint16_t color);
void fillCircle(int16_t x0, int16_t y0, int16_t r, uint16_t color);
void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, uint16_t color);
void drawTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void fillTriangle(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint16_t color);
void drawRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color);
void fillRoundRect(int16_t x0, int16_t y0, int16_t w, int16_t h, int16_t radius, uint16_t color);
void drawBitmap(int16_t x, int16_t y, const uint8_t *bitmap, int16_t w, int16_t h, uint16_t color);
//Balzers code
void drawTransparentBgPicRotated(int x, int y, int w, int h, const uint16_t* pic, const uint16_t backColor, int centerX, int centerY, int rotAngle);
void drawValRing(int x, int y, int w, int h, const uint16_t* pic, int Angle);
void drawPicBrightness(int x, int y, int w, int h, const uint16_t* pic, int brightness);
void fadeInScreen(int factor);
void drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size_x, uint8_t size_y);
void inline drawChar(int16_t x, int16_t y, unsigned char c, uint16_t color, uint16_t bg, uint8_t size)
{ drawChar(x, y, c, color, bg, size);}
static const int16_t CENTER = 9998;
void setCursor(int16_t x, int16_t y, bool autoCenter=false);
void getCursor(int16_t *x, int16_t *y);
void setTextColor(uint16_t c);
void setTextColor(uint16_t c, uint16_t bg);
void setTextSize(uint8_t sx, uint8_t sy);
void inline setTextSize(uint8_t s) { setTextSize(s,s); }
uint8_t getTextSizeX();
uint8_t getTextSizeY();
uint8_t getTextSize();
void setTextWrap(boolean w);
boolean getTextWrap();
// setOrigin sets an offset in display pixels where drawing to (0,0) will appear
// for example: setOrigin(10,10); drawPixel(5,5); will cause a pixel to be drawn at hardware pixel (15,15)
void setOrigin(int16_t x = 0, int16_t y = 0) {
_originx = x; _originy = y;
//if (Serial) Serial.printf("Set Origin %d %d\n", x, y);
updateDisplayClip();
}
void getOrigin(int16_t* x, int16_t* y) { *x = _originx; *y = _originy; }
// setClipRect() sets a clipping rectangle (relative to any set origin) for drawing to be limited to.
// Drawing is also restricted to the bounds of the display
void setClipRect(int16_t x1, int16_t y1, int16_t w, int16_t h)
{ _clipx1 = x1; _clipy1 = y1; _clipx2 = x1+w; _clipy2 = y1+h;
//if (Serial) Serial.printf("Set clip Rect %d %d %d %d\n", x1, y1, w, h);
updateDisplayClip();
}
void setClipRect() {
_clipx1 = 0; _clipy1 = 0; _clipx2 = _width; _clipy2 = _height;
//if (Serial) Serial.printf("clear clip Rect\n");
updateDisplayClip();
}
// overwrite print functions:
virtual size_t write(uint8_t);
virtual size_t write(const uint8_t *buffer, size_t size);
int16_t width(void) { return _width; }
int16_t height(void) { return _height; }
uint8_t getRotation(void);
void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint16_t color);
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
int16_t getCursorX(void) const { return cursor_x; }
int16_t getCursorY(void) const { return cursor_y; }
void setFont(const ILI9486_t3_font_t &f);
void setFont(const GFXfont *f = NULL);
void setFontAdafruit(void) { setFont(); }
void drawFontChar(unsigned int c);
void drawGFXFontChar(unsigned int c);
void getTextBounds(const uint8_t *buffer, uint16_t len, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
void getTextBounds(const char *string, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
void getTextBounds(const String &str, int16_t x, int16_t y,
int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h);
int16_t strPixelLen(const char * str);
// added support for drawing strings/numbers/floats with centering
// modified from tft_ili9486_ESP github library
// Handle numbers
int16_t drawNumber(long long_num,int poX, int poY);
int16_t drawFloat(float floatNumber,int decimal,int poX, int poY);
// Handle char arrays
int16_t drawString(const String& string, int poX, int poY);
int16_t drawString1(char string[], int16_t len, int poX, int poY);
void setTextDatum(uint8_t datum);
// added support for scrolling text area
// https://github.com/vitormhenrique/ILI9486_t3
// Discussion regarding this optimized version:
//http://forum.pjrc.com/threads/26305-Highly-optimized-ILI9486-%28320x240-TFT-color-display%29-library
//
void setScrollTextArea(int16_t x, int16_t y, int16_t w, int16_t h);
void setScrollBackgroundColor(uint16_t color);
void enableScroll(void);
void disableScroll(void);
void scrollTextArea(uint8_t scrollSize);
void resetScrollBackgroundColor(uint16_t color);
// added support to use optional Frame buffer
enum {ILI9486_DMA_INIT=0x01, ILI9486_DMA_CONT=0x02, ILI9486_DMA_FINISH=0x04,ILI9486_DMA_ACTIVE=0x80};
void setFrameBuffer(uint16_t *frame_buffer);
uint8_t useFrameBuffer(boolean b); // use the frame buffer? First call will allocate
void freeFrameBuffer(void); // explicit call to release the buffer
void updateScreen(void); // call to say update the screen now.
bool updateScreenAsync(bool update_cont = false); // call to say update the screen optinoally turn into continuous mode.
void waitUpdateAsyncComplete(void);
void endUpdateAsync(); // Turn of the continueous mode fla
void dumpDMASettings();
#ifdef ENABLE_ILI9486_FRAMEBUFFER
uint16_t *getFrameBuffer() {return _pfbtft;}
uint32_t frameCount() {return _dma_frame_count; }
uint16_t subFrameCount() {return _dma_sub_frame_count; }
boolean asyncUpdateActive(void) {return (_dma_state & ILI9486_DMA_ACTIVE);}
void initDMASettings(void);
void setFrameCompleteCB(void (*pcb)(), bool fCallAlsoHalfDone=false);
#else
uint32_t frameCount() {return 0; }
uint16_t subFrameCount() {return 0; }
uint16_t *getFrameBuffer() {return NULL;}
boolean asyncUpdateActive(void) {return false;}
#endif
protected:
SPIClass *_pspi = nullptr;
SPIClass::SPI_Hardware_t *_spi_hardware;
bool pieMask[18326]; //18326 = result of image width * height
void drawMaskLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) {
int x;
int y;
int w = abs(x1 - x0);
w++;
int h = abs(y1 - y0);
h++;
if (w > h) {
//x is longer
for (int i = 0; i < w; i++) {
if (x1 - x0 < 0) x = x0 + (-1 * i);
else
x = x0 + i;
if (y1 - y0 < 0) y = y0 + (-1 * i * (float)h / (float)w);
else y = y0 + (i * (float)h / (float)w);
int index = y*154+x; //154 = image width
pieMask[index] = 1;
index = y*154+x+1; //154 = image width
pieMask[index] = 1;
}
} else {
//y is longer
for (int i = 0; i < h; i++) {
if (x1 - x0 < 0) x = x0 + (-1 * i * (float)w / (float)h);
else x = x0 + (i * (float)w / (float)h);
if (y1 - y0 < 0) y = y0 + (-1 * i);
else y = y0 + i;
int index = y*154+x;
pieMask[index] = 1;
index = y*154+x+1;
pieMask[index] = 1;
}
}
}
void fillPie(int x, int y, int r, int startAngle, int endAngle){
memset(pieMask,0,18326); //set global bool array pieMask to zero
startAngle -= 180;
endAngle -= 180;
if (startAngle>endAngle) startAngle -= 360;
float steps=0.2;
int x1,y1,x2,y2;
x1=x + cos(((float)startAngle*PI)/180.0f) * (float)r;
y1=y + sin(((float)startAngle*PI)/180.0f) * (float)r;
for (float i=0+steps;i<endAngle-startAngle-steps;i+=steps)
{
x2=x + cos((((float)startAngle+i)*PI)/180.0f) * (float)r;
y2=y + sin((((float)startAngle+i)*PI)/180.0f) * (float)r;
if ((x1!=x2) or (y1!=y2)){
drawMaskLine(x,y,x2,y2);
}
x1=x2;
y1=y2;
}
}
bool checkPieBounds(int x, int y){
int index = y*154+x; //154 = image width
return pieMask[index];
}
uint8_t _spi_num; // Which buss is this spi on?
uint32_t _SPI_CLOCK; // #define ILI9486_SPICLOCK 30000000
uint32_t _SPI_CLOCK_READ; //#define ILI9486_SPICLOCK_READ 2000000
#if defined(KINETISK)
KINETISK_SPI_t *_pkinetisk_spi;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
IMXRT_LPSPI_t *_pimxrt_spi;
#elif defined(KINETISL)
KINETISL_SPI_t *_pkinetisl_spi;
#endif
int16_t _width, _height; // Display w/h as modified by current rotation
int16_t cursor_x, cursor_y;
bool _center_x_text = false;
bool _center_y_text = false;
int16_t _clipx1, _clipy1, _clipx2, _clipy2;
int16_t _originx, _originy;
int16_t _displayclipx1, _displayclipy1, _displayclipx2, _displayclipy2;
bool _invisible = false;
bool _standard = true; // no bounding rectangle or origin set.
inline void updateDisplayClip() {
_displayclipx1 = max(0,min(_clipx1+_originx,width()));
_displayclipx2 = max(0,min(_clipx2+_originx,width()));
_displayclipy1 = max(0,min(_clipy1+_originy,height()));
_displayclipy2 = max(0,min(_clipy2+_originy,height()));
_invisible = (_displayclipx1 == _displayclipx2 || _displayclipy1 == _displayclipy2);
_standard = (_displayclipx1 == 0) && (_displayclipx2 == _width) && (_displayclipy1 == 0) && (_displayclipy2 == _height);
if (Serial) {
//Serial.printf("UDC (%d %d)-(%d %d) %d %d\n", _displayclipx1, _displayclipy1, _displayclipx2,
// _displayclipy2, _invisible, _standard);
}
}
int16_t scroll_x, scroll_y, scroll_width, scroll_height;
boolean scrollEnable,isWritingScrollArea; // If set, 'wrap' text at right edge of display
uint16_t textcolor, textbgcolor, scrollbgcolor;
uint32_t textcolorPrexpanded, textbgcolorPrexpanded;
uint8_t textsize_x, textsize_y, rotation, textdatum;
boolean wrap; // If set, 'wrap' text at right edge of display
const ILI9486_t3_font_t *font;
// Anti-aliased font support
uint8_t fontbpp = 1;
uint8_t fontbppindex = 0;
uint8_t fontbppmask = 1;
uint8_t fontppb = 8;
uint8_t* fontalphalut;
float fontalphamx = 1;
uint32_t padX;
// GFX Font support
const GFXfont *gfxFont = nullptr;
int8_t _gfxFont_min_yOffset = 0;
// Opaque font chracter overlap?
unsigned int _gfx_c_last;
int16_t _gfx_last_cursor_x, _gfx_last_cursor_y;
int16_t _gfx_last_char_x_write = 0;
uint16_t _gfx_last_char_textcolor;
uint16_t _gfx_last_char_textbgcolor;
bool gfxFontLastCharPosFG(int16_t x, int16_t y);
uint8_t _rst;
uint8_t _cs, _dc;
uint8_t pcs_data, pcs_command;
uint8_t _miso, _mosi, _sclk;
///////////////////////////////
// BUGBUG:: reorganize this area better!
#if defined(KINETISK)
//inline uint8_t sizeFIFO() {return _fifo_size; }
uint32_t _fifo_full_test;
void waitFifoNotFull(void);
void waitFifoEmpty(void);
void waitTransmitComplete(void) ;
void waitTransmitComplete(uint32_t mcr);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
uint8_t pending_rx_count = 0; // hack ...
void waitFifoNotFull(void);
void waitFifoEmpty(void);
void waitTransmitComplete(void) ;
void waitTransmitComplete(uint32_t mcr);
#elif defined(KINETISL)
#endif
//////////////////////////////
// add support to allow only one hardware CS (used for dc)
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
uint32_t _cspinmask;
volatile uint32_t *_csport;
uint32_t _spi_tcr_current;
uint32_t _dcpinmask;
uint32_t _tcr_dc_assert;
uint32_t _tcr_dc_not_assert;
volatile uint32_t *_dcport;
#else
uint8_t _cspinmask;
volatile uint8_t *_csport;
#endif
#ifdef KINETISL
volatile uint8_t *_dcport;
uint8_t _dcpinmask;
#endif
#ifdef ENABLE_ILI9486_FRAMEBUFFER
// Add support for optional frame buffer
uint16_t *_pfbtft; // Optional Frame buffer
uint8_t _use_fbtft; // Are we in frame buffer mode?
uint16_t *_we_allocated_buffer; // We allocated the buffer;
int16_t _changed_min_x, _changed_max_x, _changed_min_y, _changed_max_y;
bool _updateChangedAreasOnly = true; // current default off,
void (*_frame_complete_callback)() = nullptr;
bool _frame_callback_on_HalfDone = false;
// Add DMA support.
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
uint16_t *_pfbtft_async; // pointer to async buffer at start of async operation...
static ILI9486_t3n *_dmaActiveDisplay[3]; // Use pointer to this as a way to get back to object...
#else
static ILI9486_t3n *_dmaActiveDisplay; // Use pointer to this as a way to get back to object...
#endif
volatile uint8_t _dma_state = 0; // DMA status
volatile uint32_t _dma_frame_count = 0; // Can return a frame count...
volatile uint16_t _dma_sub_frame_count = 0; // Can return a frame count...
#if defined(__MK66FX1M0__)
// T3.6 use Scatter/gather with chain to do transfer
static DMASetting _dmasettings[4];
static DMAChannel _dmatx;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
// try work around DMA memory cached. So have a couple of buffers we copy frame buffer into
// as to move it out of the memory that is cached...
static const uint32_t _count_pixels = ILI9486_TFTWIDTH * ILI9486_TFTHEIGHT;
DMASetting _dmasettings[3];
DMAChannel _dmatx;
volatile uint32_t _dma_pixel_index = 0;
uint16_t _dma_buffer_size; // the actual size we are using <= DMA_BUFFER_SIZE;
uint16_t _dma_cnt_sub_frames_per_frame;
uint32_t _spi_fcr_save; // save away previous FCR register value
static void dmaInterrupt1(void);
static void dmaInterrupt2(void);
#elif defined(__MK64FX512__)
// T3.5 - had issues scatter/gather so do just use channels/interrupts
// and update and continue
static DMAChannel _dmatx;
static DMAChannel _dmarx;
static uint16_t _dma_count_remaining;
static uint16_t _dma_write_size_words;
#endif
static void dmaInterrupt(void);
void process_dma_interrupt(void);
#endif
void charBounds(char c, int16_t *x, int16_t *y,
int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy);
uint16_t _previous_addr_y0 = 0xffff;
uint16_t _previous_addr_y1 = 0xffff;
void setAddr(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
__attribute__((always_inline)) {
writecommand_cont(ILI9486_CASET); // Column addr set
writedata8_cont((uint8_t)(x0 >> 8)); // XSTART
writedata8_cont((uint8_t)(x0 & 0xFF)); // XSTART
writedata8_cont((uint8_t)(x1 >> 8)); // XEND
writedata8_cont((uint8_t)(x1 & 0xFF)); // XEND
if ((y0 != _previous_addr_y0) || (y1 != _previous_addr_y1)) {
writecommand_cont(ILI9486_PASET); // Row addr set
writedata8_cont((uint8_t)(y0 >> 8)); // YSTART
writedata8_cont((uint8_t)(y0 & 0xFF)); // YSTART
writedata8_cont((uint8_t)(y1 >> 8)); // YEND
writedata8_cont((uint8_t)(y1 & 0xFF)); // YEND
_previous_addr_y0 = y0;
_previous_addr_y1 = y1;
}
}
//. From Onewire utility files
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
void DIRECT_WRITE_LOW(volatile uint32_t * base, uint32_t mask) __attribute__((always_inline)) {
*(base+34) = mask;
}
void DIRECT_WRITE_HIGH(volatile uint32_t * base, uint32_t mask) __attribute__((always_inline)) {
*(base+33) = mask;
}
#endif
void beginSPITransaction(uint32_t clock) __attribute__((always_inline)) {
_pspi->beginTransaction(SPISettings(clock, MSBFIRST, SPI_MODE0));
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
if (!_dcport) _spi_tcr_current = _pimxrt_spi->TCR; // Only if DC is on hardware CS
#endif
if (_csport) {
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
DIRECT_WRITE_LOW(_csport, _cspinmask);
#else
*_csport &= ~_cspinmask;
#endif
}
}
void endSPITransaction() __attribute__((always_inline)) {
if (_csport) {
#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
DIRECT_WRITE_HIGH(_csport, _cspinmask);
#else
*_csport |= _cspinmask;
#endif
}
_pspi->endTransaction();
}
#if defined(KINETISK)
void writecommand_cont(uint8_t c) __attribute__((always_inline)) {
_pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writedata8_cont(uint8_t c) __attribute__((always_inline)) {
_pkinetisk_spi->PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writedata16_cont(uint16_t d) __attribute__((always_inline)) {
_pkinetisk_spi->PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_CONT;
waitFifoNotFull();
}
void writecommand_last(uint8_t c) __attribute__((always_inline)) {
uint32_t mcr = _pkinetisk_spi->MCR;
_pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
waitTransmitComplete(mcr);
}
void writedata8_last(uint8_t c) __attribute__((always_inline)) {
uint32_t mcr = _pkinetisk_spi->MCR;
_pkinetisk_spi->PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
waitTransmitComplete(mcr);
}
void writedata16_last(uint16_t d) __attribute__((always_inline)) {
uint32_t mcr = _pkinetisk_spi->MCR;
_pkinetisk_spi->PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ;
waitTransmitComplete(mcr);
}
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x
#define TCR_MASK (LPSPI_TCR_PCS(3) | LPSPI_TCR_FRAMESZ(31) | LPSPI_TCR_CONT | LPSPI_TCR_RXMSK )
void maybeUpdateTCR(uint32_t requested_tcr_state) /*__attribute__((always_inline)) */ {
if ((_spi_tcr_current & TCR_MASK) != requested_tcr_state) {
bool dc_state_change = (_spi_tcr_current & LPSPI_TCR_PCS(3)) != (requested_tcr_state & LPSPI_TCR_PCS(3));
_spi_tcr_current = (_spi_tcr_current & ~TCR_MASK) | requested_tcr_state ;
// only output when Transfer queue is empty.
if (!dc_state_change || !_dcpinmask) {
while ((_pimxrt_spi->FSR & 0x1f) ) ;
_pimxrt_spi->TCR = _spi_tcr_current; // update the TCR
} else {
waitTransmitComplete();
if (requested_tcr_state & LPSPI_TCR_PCS(3)) DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
else DIRECT_WRITE_LOW(_dcport, _dcpinmask);
_pimxrt_spi->TCR = _spi_tcr_current & ~(LPSPI_TCR_PCS(3) | LPSPI_TCR_CONT); // go ahead and update TCR anyway?
}
}
}
// BUGBUG:: currently assumming we only have CS_0 as valid CS
void spiTransfer_cont(uint8_t data, int tcr_state) __attribute__((always_inline)) {
maybeUpdateTCR(LPSPI_TCR_PCS(tcr_state) | LPSPI_TCR_FRAMESZ(7) | LPSPI_TCR_CONT);
_pimxrt_spi->TDR = data;
pending_rx_count++;
waitFifoNotFull();
}
void spiTransfer_last(uint8_t data, int tcr_state) __attribute__((always_inline)) {
maybeUpdateTCR(LPSPI_TCR_PCS(tcr_state) | LPSPI_TCR_FRAMESZ(7) | LPSPI_TCR_CONT);
_pimxrt_spi->TDR = data;
pending_rx_count++;
waitTransmitComplete();
}
void writecommand_cont(uint8_t c) __attribute__((always_inline)) {
spiTransfer_cont(0x00, 0);
spiTransfer_cont(c, 0);
}
void writedata8_cont(uint8_t d) __attribute__((always_inline)) {
spiTransfer_cont(0x00, 1);
spiTransfer_cont(d, 1);
}
void writedata16_cont(uint16_t d) __attribute__((always_inline)) {
spiTransfer_cont(uint8_t(d >> 8), 1);
spiTransfer_cont(uint8_t(d & 0xff), 1);
}
void writecommand_last(uint8_t c) __attribute__((always_inline)) {
spiTransfer_cont(0x00, 0);
spiTransfer_last(c, 0);
}
void writedata8_last(uint8_t d) __attribute__((always_inline)) {
spiTransfer_cont(0x00, 1);
spiTransfer_last(d, 1);
}
void writedata16_last(uint16_t d) __attribute__((always_inline)) {
spiTransfer_cont(uint8_t(d >> 8), 1);
spiTransfer_last(uint8_t(d & 0xff), 1);
}
#elif defined (KINETISL)
// Lets see how hard to make it work OK with T-LC
uint8_t _dcpinAsserted;
uint8_t _data_sent_not_completed;
void waitTransmitComplete() {
while (_data_sent_not_completed) {
uint16_t timeout_count = 0xff; // hopefully enough
while (!(_pkinetisl_spi->S & SPI_S_SPRF) && timeout_count--) ; // wait
uint8_t d __attribute__((unused));
d = _pkinetisl_spi->DL;
d = _pkinetisl_spi->DH;
_data_sent_not_completed--; // We hopefully received our data...
}
}
uint16_t waitTransmitCompleteReturnLast() {
uint16_t d = 0;
while (_data_sent_not_completed) {
uint16_t timeout_count = 0xff; // hopefully enough
while (!(_pkinetisl_spi->S & SPI_S_SPRF) && timeout_count--) ; // wait
d = (_pkinetisl_spi->DH << 8) | _pkinetisl_spi->DL;
_data_sent_not_completed--; // We hopefully received our data...
}
return d;
}
void setCommandMode() __attribute__((always_inline)) {
if (!_dcpinAsserted) {
waitTransmitComplete();
*_dcport &= ~_dcpinmask;
_dcpinAsserted = 1;
}
}
void setDataMode() __attribute__((always_inline)) {
if (_dcpinAsserted) {
waitTransmitComplete();
*_dcport |= _dcpinmask;
_dcpinAsserted = 0;
}
}
void outputToSPI(uint8_t c) {
if (_pkinetisl_spi->C2 & SPI_C2_SPIMODE) {
// Wait to change modes until any pending output has been done.
waitTransmitComplete();
_pkinetisl_spi->C2 = 0; // make sure 8 bit mode.
}
while (!(_pkinetisl_spi->S & SPI_S_SPTEF)) ; // wait if output buffer busy.
// Clear out buffer if there is something there...
if ((_pkinetisl_spi->S & SPI_S_SPRF)) {
uint8_t d __attribute__((unused));
d = _pkinetisl_spi->DL;
_data_sent_not_completed--;
}
_pkinetisl_spi->DL = c; // output byte
_data_sent_not_completed++; // let system know we sent something
}
void outputToSPI16(uint16_t data) {
if (!(_pkinetisl_spi->C2 & SPI_C2_SPIMODE)) {
// Wait to change modes until any pending output has been done.
waitTransmitComplete();
_pkinetisl_spi->C2 = SPI_C2_SPIMODE; // make sure 8 bit mode.
}
uint8_t s;
do {
s = _pkinetisl_spi->S;
// wait if output buffer busy.
// Clear out buffer if there is something there...
if ((s & SPI_S_SPRF)) {
uint8_t d __attribute__((unused));
d = _pkinetisl_spi->DL;
d = _pkinetisl_spi->DH;
_data_sent_not_completed--; // let system know we sent something
}
} while (!(s & SPI_S_SPTEF) || (s & SPI_S_SPRF));
_pkinetisl_spi->DL = data; // output low byte
_pkinetisl_spi->DH = data >> 8; // output high byte
_data_sent_not_completed++; // let system know we sent something
}
void writecommand_cont(uint8_t c) {
setCommandMode();
outputToSPI(c);
}
void writedata8_cont(uint8_t c) {
setDataMode();
outputToSPI(c);
}
void writedata16_cont(uint16_t c) {
setDataMode();
outputToSPI16(c);
}
void writecommand_last(uint8_t c) {
setCommandMode();
outputToSPI(c);
waitTransmitComplete();
}
void writedata8_last(uint8_t c) {
setDataMode();
outputToSPI(c);
waitTransmitComplete();
}
void writedata16_last(uint16_t c) {
setDataMode();
outputToSPI16(c);
waitTransmitComplete();
}
#endif
#ifdef ENABLE_ILI9486_FRAMEBUFFER
void clearChangedRange() {
_changed_min_x = 0x7fff;
_changed_max_x = -1;
_changed_min_y = 0x7fff;
_changed_max_y = -1;
}
void updateChangedRange(int16_t x, int16_t y, int16_t w, int16_t h)
__attribute__((always_inline)) {
if (x < _changed_min_x) _changed_min_x = x;
if (y < _changed_min_y) _changed_min_y = y;
x += w - 1;
y += h - 1;
if (x > _changed_max_x) _changed_max_x = x;
if (y > _changed_max_y) _changed_max_y = y;
}
//could combine with above, but avoids the +-...
void updateChangedRange(int16_t x, int16_t y)
__attribute__((always_inline)) {
if (x < _changed_min_x) _changed_min_x = x;
if (y < _changed_min_y) _changed_min_y = y;
if (x > _changed_max_x) _changed_max_x = x;
if (y > _changed_max_y) _changed_max_y = y;
}
#endif
void updateChangedAreasOnly(bool updateChangedOnly) {
#ifdef ENABLE_ILI9486_FRAMEBUFFER
_updateChangedAreasOnly = updateChangedOnly;
#endif
}
void HLine(int16_t x, int16_t y, int16_t w, uint16_t color)
__attribute__((always_inline)) {
#ifdef ENABLE_ILI9486_FRAMEBUFFER
if (_use_fbtft) {
drawFastHLine(x, y, w, color);
return;
}
#endif
x+=_originx;
y+=_originy;
// Rectangular clipping
if((y < _displayclipy1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
if(x<_displayclipx1) { w = w - (_displayclipx1 - x); x = _displayclipx1; }
if((x+w-1) >= _displayclipx2) w = _displayclipx2-x;
if (w<1) return;
setAddr(x, y, x+w-1, y);
writecommand_cont(ILI9486_RAMWR);
do { writedata16_cont(color); } while (--w > 0);
}
void VLine(int16_t x, int16_t y, int16_t h, uint16_t color)
__attribute__((always_inline)) {
#ifdef ENABLE_ILI9486_FRAMEBUFFER
if (_use_fbtft) {
drawFastVLine(x, y, h, color);
return;
}
#endif
x+=_originx;
y+=_originy;
// Rectangular clipping
if((x < _displayclipx1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
if(y < _displayclipy1) { h = h - (_displayclipy1 - y); y = _displayclipy1;}
if((y+h-1) >= _displayclipy2) h = _displayclipy2-y;
if(h<1) return;
setAddr(x, y, x, y+h-1);
writecommand_cont(ILI9486_RAMWR);
do { writedata16_cont(color); } while (--h > 0);
}
/**
* Found in a pull request for the Adafruit framebuffer library. Clever!
* https://github.com/tricorderproject/arducordermini/pull/1/files#diff-d22a481ade4dbb4e41acc4d7c77f683d
* Converts 0000000000000000rrrrrggggggbbbbb
* into 00000gggggg00000rrrrr000000bbbbb
* with mask 00000111111000001111100000011111
* This is useful because it makes space for a parallel fixed-point multiply
* This implements the linear interpolation formula: result = bg * (1.0 - alpha) + fg * alpha
* This can be factorized into: result = bg + (fg - bg) * alpha
* alpha is in Q1.5 format, so 0.0 is represented by 0, and 1.0 is represented by 32
* @param fg Color to draw in RGB565 (16bit)
* @param bg Color to draw over in RGB565 (16bit)
* @param alpha Alpha in range 0-255
**/
uint16_t alphaBlendRGB565( uint32_t fg, uint32_t bg, uint8_t alpha )
__attribute__((always_inline)) {
alpha = ( alpha + 4 ) >> 3; // from 0-255 to 0-31
bg = (bg | (bg << 16)) & 0b00000111111000001111100000011111;
fg = (fg | (fg << 16)) & 0b00000111111000001111100000011111;
uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111;
return (uint16_t)((result >> 16) | result); // contract result
}
/**
* Same as above, but fg and bg are premultiplied, and alpah is already in range 0-31
*/
uint16_t alphaBlendRGB565Premultiplied( uint32_t fg, uint32_t bg, uint8_t alpha )
__attribute__((always_inline)) {
uint32_t result = ((((fg - bg) * alpha) >> 5) + bg) & 0b00000111111000001111100000011111;