forked from adafruit/Adafruit-GFX-Library
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Adafruit_SPITFT.cpp
1281 lines (1213 loc) · 45.8 KB
/
Adafruit_SPITFT.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 Adafruit_SPITFT.cpp
*
* @mainpage Adafruit SPI TFT Displays (and some others)
*
* @section intro_sec Introduction
*
* Part of Adafruit's GFX graphics library. Originally this class was
* written to handle a range of color TFT displays connected via SPI,
* but over time this library and some display-specific subclasses have
* mutated to include some color OLEDs as well as parallel-interfaced
* displays. The name's been kept for the sake of older code.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
* @section dependencies Dependencies
*
* This library depends on <a href="https://github.com/adafruit/Adafruit_GFX">
* Adafruit_GFX</a> being present on your system. Please make sure you have
* installed the latest version before using this library.
*
* @section author Author
*
* Written by Limor "ladyada" Fried for Adafruit Industries,
* with contributions from the open source community.
*
* @section license License
*
* BSD license, all text here must be included in any redistribution.
*/
#include "Adafruit_SPITFT.h"
#if defined(USE_SPI_DMA)
// TODO: Implement DMA
#endif // end USE_SPI_DMA
// Possible values for Adafruit_SPITFT.connection:
#define TFT_HARD_SPI 0 ///< Display interface = hardware SPI
#define TFT_SOFT_SPI 1 ///< Display interface = software SPI
// #define TFT_PARALLEL 2 ///< Display interface = 8- or 16-bit parallel
uint8_t *spi_buffer;
const int SPI_BUFFER_SIZE = 1024;
// CONSTRUCTORS ------------------------------------------------------------
/*!
@brief Adafruit_SPITFT constructor for software (bitbang) SPI.
@param w Display width in pixels at default rotation setting (0).
@param h Display height in pixels at default rotation setting (0).
@param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
@param dc Arduino pin # for data/command select (required).
@param mosi Arduino pin # for bitbang SPI MOSI signal (required).
@param sck Arduino pin # for bitbang SPI SCK signal (required).
@param rst Arduino pin # for display reset (optional, display reset
can be tied to MCU reset, default of -1 means unused).
@param miso Arduino pin # for bitbang SPI MISO signal (optional,
-1 default, many displays don't support SPI read).
@return Adafruit_SPITFT object.
@note Output pins are not initialized; application typically will
need to call subclass' begin() function, which in turn calls
this library's initSPI() function to initialize pins.
*/
Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, PinName cs, PinName dc, PinName mosi, PinName sck, PinName rst, PinName miso)
: Adafruit_GFX(w, h), connection(TFT_SOFT_SPI), _w(w), _h(h), _rst(rst), _cs(cs), _dc(dc)
{
swspi._sck = sck;
swspi._mosi = mosi;
swspi._miso = miso;
}
/*!
@brief Adafruit_SPITFT constructor for hardware SPI using the board's
default SPI peripheral.
@param w Display width in pixels at default rotation setting (0).
@param h Display height in pixels at default rotation setting (0).
@param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
@param dc Arduino pin # for data/command select (required).
@param rst Arduino pin # for display reset (optional, display reset
can be tied to MCU reset, default of -1 means unused).
@return Adafruit_SPITFT object.
@note Output pins are not initialized; application typically will
need to call subclass' begin() function, which in turn calls
this library's initSPI() function to initialize pins.
*/
Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, PinName cs, PinName dc, PinName rst)
: Adafruit_GFX(w, h), connection(TFT_HARD_SPI), _w(w), _h(h), _rst(rst), _cs(cs), _dc(dc)
{
hwspi._spi = new SPI(SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS);
}
/*!
@brief Adafruit_SPITFT constructor for hardware SPI using a specific
SPI peripheral.
@param w Display width in pixels at default rotation (0).
@param h Display height in pixels at default rotation (0).
@param spi Pointer to SPI type.
@param cs Arduino pin # for chip-select (-1 if unused, tie CS low).
@param dc Arduino pin # for data/command select (required).
@param rst Arduino pin # for display reset (optional, display reset
can be tied to MCU reset, default of -1 means unused).
@param bits SPI Bits (4 - 16, default: 8)
@param mode SPI mode (default: 0)
@param freq SPI frequency (optional, pass 0 if unused)
@return Adafruit_SPITFT object.
@note Output pins are not initialized; application typically will
need to call subclass' begin() function, which in turn calls
this library's initSPI() function to initialize pins.
*/
Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, SPI &spi, PinName cs, PinName dc, PinName rst, int bits, int mode, uint32_t freq)
: Adafruit_GFX(w, h), connection(TFT_HARD_SPI), _w(w), _h(h), _rst(rst), _cs(cs), _dc(dc), _freq(freq)
{
hwspi._spi = &spi;
hwspi._bits = bits;
hwspi._mode = mode;
hwspi._freq = freq;
}
/*!
@brief Adafruit_SPITFT constructor for parallel display connection.
@param w Display width in pixels at default rotation (0).
@param h Display height in pixels at default rotation (0).
@param busWidth If tft16 (enumeration in header file), is a 16-bit
parallel connection, else 8-bit.
16-bit isn't fully implemented or tested yet so
applications should pass "tft8" for now...needed to
stick a required enum argument in there to
disambiguate this constructor from the soft-SPI case.
Argument is ignored on 8-bit architectures (no 'wide'
support there since PORTs are 8 bits anyway).
@param d0 Arduino pin # for data bit 0 (1+ are extrapolated).
The 8 (or 16) data bits MUST be contiguous and byte-
aligned (or word-aligned for wide interface) within
the same PORT register (might not correspond to
Arduino pin sequence).
@param wr Arduino pin # for write strobe (required).
@param dc Arduino pin # for data/command select (required).
@param cs Arduino pin # for chip-select (optional, -1 if unused,
tie CS low).
@param rst Arduino pin # for display reset (optional, display reset
can be tied to MCU reset, default of -1 means unused).
@param rd Arduino pin # for read strobe (optional, -1 if unused).
@return Adafruit_SPITFT object.
@note Output pins are not initialized; application typically will need
to call subclass' begin() function, which in turn calls this
library's initSPI() function to initialize pins.
Yes, the name is a misnomer...this library originally handled
only SPI displays, parallel being a recent addition (but not
wanting to break existing code).
*/
/*
// TODO: Implement Parallel
Adafruit_SPITFT::Adafruit_SPITFT(uint16_t w, uint16_t h, tftBusWidth busWidth, PinName d0, PinName wr, PinName dc, PinName cs, PinName rst, PinName rd)
: Adafruit_GFX(w, h), connection(TFT_PARALLEL), _w(w), _h(h), _rst(rst), _cs(cs), _dc(dc)
{
tft8._d0 = d0;
tft8._wr = wr;
tft8._rd = rd;
tft8.wide = (busWidth == tft16);
}
*/
// end constructors -------
// CLASS MEMBER FUNCTIONS --------------------------------------------------
// begin() and setAddrWindow() MUST be declared by any subclass.
/*!
@brief Configure microcontroller pins for TFT interfacing. Typically
called by a subclass' begin() function.
@note Another anachronistically-named function; this is called even
when the display connection is parallel (not SPI). Also, this
could probably be made private...quite a few class functions
were generously put in the public section.
*/
void Adafruit_SPITFT::initSPI(void)
{
// Init basic control pins common to all connection types
if (_cs >= 0)
{
pinMode(_cs, OUTPUT);
digitalWrite(_cs, HIGH); // Deselect
}
pinMode(_dc, OUTPUT);
digitalWrite(_dc, HIGH); // Data mode
if (connection == TFT_HARD_SPI)
{
/*
*/
// Setup spi buffer
if ((spi_buffer = (uint8_t *)malloc(SPI_BUFFER_SIZE)))
memset(spi_buffer, 0, SPI_BUFFER_SIZE);
// Setup Hardware SPI
hwspi._spi->format(hwspi._bits, hwspi._mode);
hwspi._spi->frequency(hwspi._freq);
//hwspi._spi->begin();
}
else if (connection == TFT_SOFT_SPI)
{
pinMode(swspi._mosi, OUTPUT);
digitalWrite(swspi._mosi, LOW);
pinMode(swspi._sck, OUTPUT);
digitalWrite(swspi._sck, LOW);
if (swspi._miso >= 0)
{
pinMode(swspi._miso, INPUT);
}
}
/*
// TODO: Implement Parallel
else
{ // TFT_PARALLEL
// Initialize data pins. We were only passed d0, so scan
// the pin description list looking for the other pins.
// They'll be on the same PORT, and within the next 7 (or 15) bits
// (because we need to write to a contiguous PORT byte or word).
pinMode(tft8._wr, OUTPUT);
digitalWrite(tft8._wr, HIGH);
if (tft8._rd >= 0)
{
pinMode(tft8._rd, OUTPUT);
digitalWrite(tft8._rd, HIGH);
}
}
*/
if (_rst >= 0)
{
// Toggle _rst low to reset
pinMode(_rst, OUTPUT);
digitalWrite(_rst, HIGH);
wait_ms(100);
digitalWrite(_rst, LOW);
wait_ms(100);
digitalWrite(_rst, HIGH);
wait_ms(200);
}
#if defined(USE_SPI_DMA)
// TODO: Implement DMA
#endif // end USE_SPI_DMA
}
/*!
@brief Call before issuing command(s) or data to display. Performs
chip-select (if required) and starts an SPI transaction (if
using hardware SPI and transactions are supported). Required
for all display types; not an SPI-specific function.
*/
void Adafruit_SPITFT::startWrite(void)
{
SPI_BEGIN_TRANSACTION();
if (_cs >= 0)
SPI_CS_LOW();
}
/*!
@brief Call after issuing command(s) or data to display. Performs
chip-deselect (if required) and ends an SPI transaction (if
using hardware SPI and transactions are supported). Required
for all display types; not an SPI-specific function.
*/
void Adafruit_SPITFT::endWrite(void)
{
if (_cs >= 0)
SPI_CS_HIGH();
SPI_END_TRANSACTION();
}
// -------------------------------------------------------------------------
// Lower-level graphics operations. These functions require a chip-select
// and/or SPI transaction around them (via startWrite(), endWrite() above).
// Higher-level graphics primitives might start a single transaction and
// then make multiple calls to these functions (e.g. circle or text
// rendering might make repeated lines or rects) before ending the
// transaction. It's more efficient than starting a transaction every time.
/*!
@brief Draw a single pixel to the display at requested coordinates.
Not self-contained; should follow a startWrite() call.
@param x Horizontal position (0 = left).
@param y Vertical position (0 = top).
@param color 16-bit pixel color in '565' RGB format.
*/
void Adafruit_SPITFT::writePixel(int16_t x, int16_t y, uint16_t color)
{
if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height))
{
setAddrWindow(x, y, 1, 1);
SPI_WRITE16(color);
}
}
/*!
@brief Issue a series of pixels from memory to the display. Not self-
contained; should follow startWrite() and setAddrWindow() calls.
@param colors Pointer to array of 16-bit pixel values in '565' RGB
format.
@param len Number of elements in 'colors' array.
@param block If true (default case if unspecified), function blocks
until DMA transfer is complete. This is simply IGNORED
if DMA is not enabled. If false, the function returns
immediately after the last DMA transfer is started,
and one should use the dmaWait() function before
doing ANY other display-related activities (or even
any SPI-related activities, if using an SPI display
that shares the bus with other devices).
@param bigEndian If using DMA, and if set true, bitmap in memory is in
big-endian order (most significant byte first). By
default this is false, as most microcontrollers seem
to be little-endian and 16-bit pixel values must be
byte-swapped before issuing to the display (which tend
to be big-endian when using SPI or 8-bit parallel).
If an application can optimize around this -- for
example, a bitmap in a uint16_t array having the byte
values already reordered big-endian, this can save
some processing time here, ESPECIALLY if using this
function's non-blocking DMA mode. Not all cases are
covered...this is really here only for SAMD DMA and
much forethought on the application side.
*/
void Adafruit_SPITFT::writePixels(uint16_t *colors, uint32_t len, bool block, bool bigEndian)
{
if (!len)
return; // Avoid 0-byte transfers
#if defined(USE_SPI_DMA)
// TODO: Implement DMA
#endif // end USE_SPI_DMA
if (connection == TFT_HARD_SPI)
{
uint32_t i = 0;
for (int remaining_bytes = 2 * len; remaining_bytes > 0; remaining_bytes -= SPI_BUFFER_SIZE)
{
// Fill array of colors
for (uint8_t *ptr = (uint8_t *)spi_buffer; (ptr < (uint8_t *)(spi_buffer + SPI_BUFFER_SIZE)) && (i < len); ptr++)
{
*ptr++ = highByte(colors[i]);
*ptr = lowByte(colors[i++]);
}
// Write array of bytes to SPI
hwspi._spi->write((char *)spi_buffer, std::min(remaining_bytes, SPI_BUFFER_SIZE), (char *)NULL, 0);
}
}
else
{
// All other cases (bitbang SPI or non-DMA hard SPI or parallel),
// use a loop with the normal 16-bit data write function:
while (len--)
SPI_WRITE16(*colors++);
}
}
/*!
@brief Wait for the last DMA transfer in a prior non-blocking
writePixels() call to complete. This does nothing if DMA
is not enabled, and is not needed if blocking writePixels()
was used (as is the default case).
*/
void Adafruit_SPITFT::dmaWait(void)
{
#if defined(USE_SPI_DMA)
// TODO: Implement DMA
#endif // end USE_SPI_DMA
}
/*!
@brief Issue a series of pixels, all the same color. Not self-
contained; should follow startWrite() and setAddrWindow() calls.
@param color 16-bit pixel color in '565' RGB format.
@param len Number of pixels to draw.
*/
void Adafruit_SPITFT::writeColor(uint16_t color, uint32_t len)
{
if (!len)
return; // Avoid 0-byte transfers
#if defined(USE_SPI_DMA)
// TODO: Implement DMA
#endif // end USE_SPI_DMA
// All other cases (non-DMA hard SPI, bitbang SPI, parallel)...
if (connection == TFT_HARD_SPI)
{
// Fill array of colors
uint8_t hi = highByte(color), lo = lowByte(color);
if (hi == lo) // if hi byte equals low byte, set all bytes using memset
{
memset(spi_buffer, hi, SPI_BUFFER_SIZE);
}
else // otherwise, loop through buffer setting pairs of bytes
{
uint16_t data = SWAP_BYTES(color);
for (uint16_t *ptr = (uint16_t *)spi_buffer; ptr < (uint16_t *)(spi_buffer + SPI_BUFFER_SIZE); ptr++)
*ptr = data;
}
// Write array of bytes to SPI
for (int remaining_bytes = 2 * len; remaining_bytes > 0; remaining_bytes -= SPI_BUFFER_SIZE)
hwspi._spi->write((char *)spi_buffer, std::min(remaining_bytes, SPI_BUFFER_SIZE), (char *)NULL, 0);
/*
// Write each byte
while (len--)
{
hwspi._spi->write(hi);
hwspi._spi->write(lo);
}
*/
}
else if (connection == TFT_SOFT_SPI)
{
while (len--)
{
// Bit-Bang the data out
for (uint16_t bit = 0, x = color; bit < 16; bit++)
{
if (x & 0x8000)
SPI_MOSI_HIGH();
else
SPI_MOSI_LOW();
SPI_SCK_HIGH();
x <<= 1;
SPI_SCK_LOW();
}
}
}
/*
// TODO: Implement Parallel
else
{ // TFT_PARALLEL
if (hi == lo)
{
}
else
{
while (len--)
{
TFT_WR_STROBE();
}
}
}
*/
}
/*!
@brief Draw a filled rectangle to the display. Not self-contained;
should follow startWrite(). Typically used by higher-level
graphics primitives; user code shouldn't need to call this and
is likely to use the self-contained fillRect() instead.
writeFillRect() performs its own edge clipping and rejection;
see writeFillRectPreclipped() for a more 'raw' implementation.
@param x Horizontal position of first corner.
@param y Vertical position of first corner.
@param w Rectangle width in pixels (positive = right of first
corner, negative = left of first corner).
@param h Rectangle height in pixels (positive = below first
corner, negative = above first corner).
@param color 16-bit fill color in '565' RGB format.
@note Written in this deep-nested way because C by definition will
optimize for the 'if' case, not the 'else' -- avoids branches
and rejects clipped rectangles at the least-work possibility.
*/
void Adafruit_SPITFT::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
if (w && h)
{ // Nonzero width and height?
if (w < 0)
{ // If negative width...
x += w + 1; // Move X to left edge
w = -w; // Use positive width
}
if (x < _width)
{ // Not off right
if (h < 0)
{ // If negative height...
y += h + 1; // Move Y to top edge
h = -h; // Use positive height
}
if (y < _height)
{ // Not off bottom
int16_t x2 = x + w - 1;
if (x2 >= 0)
{ // Not off left
int16_t y2 = y + h - 1;
if (y2 >= 0)
{ // Not off top
// Rectangle partly or fully overlaps screen
if (x < 0)
{
x = 0;
w = x2 + 1;
} // Clip left
if (y < 0)
{
y = 0;
h = y2 + 1;
} // Clip top
if (x2 >= _width)
{
w = _width - x;
} // Clip right
if (y2 >= _height)
{
h = _height - y;
} // Clip bottom
writeFillRectPreclipped(x, y, w, h, color);
}
}
}
}
}
}
/*!
@brief Draw a horizontal line on the display. Performs edge clipping
and rejection. Not self-contained; should follow startWrite().
Typically used by higher-level graphics primitives; user code
shouldn't need to call this and is likely to use the self-
contained drawFastHLine() instead.
@param x Horizontal position of first point.
@param y Vertical position of first point.
@param w Line width in pixels (positive = right of first point,
negative = point of first corner).
@param color 16-bit line color in '565' RGB format.
*/
void inline Adafruit_SPITFT::writeFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
if ((y >= 0) && (y < _height) && w)
{ // Y on screen, nonzero width
if (w < 0)
{ // If negative width...
x += w + 1; // Move X to left edge
w = -w; // Use positive width
}
if (x < _width)
{ // Not off right
int16_t x2 = x + w - 1;
if (x2 >= 0)
{ // Not off left
// Line partly or fully overlaps screen
if (x < 0)
{
x = 0;
w = x2 + 1;
} // Clip left
if (x2 >= _width)
{
w = _width - x;
} // Clip right
writeFillRectPreclipped(x, y, w, 1, color);
}
}
}
}
/*!
@brief Draw a vertical line on the display. Performs edge clipping and
rejection. Not self-contained; should follow startWrite().
Typically used by higher-level graphics primitives; user code
shouldn't need to call this and is likely to use the self-
contained drawFastVLine() instead.
@param x Horizontal position of first point.
@param y Vertical position of first point.
@param h Line height in pixels (positive = below first point,
negative = above first point).
@param color 16-bit line color in '565' RGB format.
*/
void inline Adafruit_SPITFT::writeFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
if ((x >= 0) && (x < _width) && h)
{ // X on screen, nonzero height
if (h < 0)
{ // If negative height...
y += h + 1; // Move Y to top edge
h = -h; // Use positive height
}
if (y < _height)
{ // Not off bottom
int16_t y2 = y + h - 1;
if (y2 >= 0)
{ // Not off top
// Line partly or fully overlaps screen
if (y < 0)
{
y = 0;
h = y2 + 1;
} // Clip top
if (y2 >= _height)
{
h = _height - y;
} // Clip bottom
writeFillRectPreclipped(x, y, 1, h, color);
}
}
}
}
/*!
@brief A lower-level version of writeFillRect(). This version requires
all inputs are in-bounds, that width and height are positive,
and no part extends offscreen. NO EDGE CLIPPING OR REJECTION IS
PERFORMED. If higher-level graphics primitives are written to
handle their own clipping earlier in the drawing process, this
can avoid unnecessary function calls and repeated clipping
operations in the lower-level functions.
@param x Horizontal position of first corner. MUST BE WITHIN
SCREEN BOUNDS.
@param y Vertical position of first corner. MUST BE WITHIN SCREEN
BOUNDS.
@param w Rectangle width in pixels. MUST BE POSITIVE AND NOT
EXTEND OFF SCREEN.
@param h Rectangle height in pixels. MUST BE POSITIVE AND NOT
EXTEND OFF SCREEN.
@param color 16-bit fill color in '565' RGB format.
@note This is a new function, no graphics primitives besides rects
and horizontal/vertical lines are written to best use this yet.
*/
inline void Adafruit_SPITFT::writeFillRectPreclipped(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
setAddrWindow(x, y, w, h);
writeColor(color, (uint32_t)w * h);
}
// -------------------------------------------------------------------------
// Ever-so-slightly higher-level graphics operations. Similar to the 'write'
// functions above, but these contain their own chip-select and SPI
// transactions as needed (via startWrite(), endWrite()). They're typically
// used solo -- as graphics primitives in themselves, not invoked by higher-
// level primitives (which should use the functions above for better
// performance).
/*!
@brief Draw a single pixel to the display at requested coordinates.
Self-contained and provides its own transaction as needed
(see writePixel(x,y,color) for a lower-level variant).
Edge clipping is performed here.
@param x Horizontal position (0 = left).
@param y Vertical position (0 = top).
@param color 16-bit pixel color in '565' RGB format.
*/
void Adafruit_SPITFT::drawPixel(int16_t x, int16_t y, uint16_t color)
{
// Clip first...
if ((x >= 0) && (x < _width) && (y >= 0) && (y < _height))
{
// THEN set up transaction (if needed) and draw...
startWrite();
setAddrWindow(x, y, 1, 1);
SPI_WRITE16(color);
endWrite();
}
}
/*!
@brief Draw a filled rectangle to the display. Self-contained and
provides its own transaction as needed (see writeFillRect() or
writeFillRectPreclipped() for lower-level variants). Edge
clipping and rejection is performed here.
@param x Horizontal position of first corner.
@param y Vertical position of first corner.
@param w Rectangle width in pixels (positive = right of first
corner, negative = left of first corner).
@param h Rectangle height in pixels (positive = below first
corner, negative = above first corner).
@param color 16-bit fill color in '565' RGB format.
@note This repeats the writeFillRect() function almost in its entirety,
with the addition of a transaction start/end. It's done this way
(rather than starting the transaction and calling writeFillRect()
to handle clipping and so forth) so that the transaction isn't
performed at all if the rectangle is rejected. It's really not
that much code.
*/
void Adafruit_SPITFT::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
{
if (w && h)
{ // Nonzero width and height?
if (w < 0)
{ // If negative width...
x += w + 1; // Move X to left edge
w = -w; // Use positive width
}
if (x < _width)
{ // Not off right
if (h < 0)
{ // If negative height...
y += h + 1; // Move Y to top edge
h = -h; // Use positive height
}
if (y < _height)
{ // Not off bottom
int16_t x2 = x + w - 1;
if (x2 >= 0)
{ // Not off left
int16_t y2 = y + h - 1;
if (y2 >= 0)
{ // Not off top
// Rectangle partly or fully overlaps screen
if (x < 0)
{
x = 0;
w = x2 + 1;
} // Clip left
if (y < 0)
{
y = 0;
h = y2 + 1;
} // Clip top
if (x2 >= _width)
{
w = _width - x;
} // Clip right
if (y2 >= _height)
{
h = _height - y;
} // Clip bottom
startWrite();
writeFillRectPreclipped(x, y, w, h, color);
endWrite();
}
}
}
}
}
}
/*!
@brief Draw a horizontal line on the display. Self-contained and
provides its own transaction as needed (see writeFastHLine() for
a lower-level variant). Edge clipping and rejection is performed
here.
@param x Horizontal position of first point.
@param y Vertical position of first point.
@param w Line width in pixels (positive = right of first point,
negative = point of first corner).
@param color 16-bit line color in '565' RGB format.
@note This repeats the writeFastHLine() function almost in its
entirety, with the addition of a transaction start/end. It's
done this way (rather than starting the transaction and calling
writeFastHLine() to handle clipping and so forth) so that the
transaction isn't performed at all if the line is rejected.
*/
void Adafruit_SPITFT::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
{
if ((y >= 0) && (y < _height) && w)
{ // Y on screen, nonzero width
if (w < 0)
{ // If negative width...
x += w + 1; // Move X to left edge
w = -w; // Use positive width
}
if (x < _width)
{ // Not off right
int16_t x2 = x + w - 1;
if (x2 >= 0)
{ // Not off left
// Line partly or fully overlaps screen
if (x < 0)
{
x = 0;
w = x2 + 1;
} // Clip left
if (x2 >= _width)
{
w = _width - x;
} // Clip right
startWrite();
writeFillRectPreclipped(x, y, w, 1, color);
endWrite();
}
}
}
}
/*!
@brief Draw a vertical line on the display. Self-contained and provides
its own transaction as needed (see writeFastHLine() for a lower-
level variant). Edge clipping and rejection is performed here.
@param x Horizontal position of first point.
@param y Vertical position of first point.
@param h Line height in pixels (positive = below first point,
negative = above first point).
@param color 16-bit line color in '565' RGB format.
@note This repeats the writeFastVLine() function almost in its
entirety, with the addition of a transaction start/end. It's
done this way (rather than starting the transaction and calling
writeFastVLine() to handle clipping and so forth) so that the
transaction isn't performed at all if the line is rejected.
*/
void Adafruit_SPITFT::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
{
if ((x >= 0) && (x < _width) && h)
{ // X on screen, nonzero height
if (h < 0)
{ // If negative height...
y += h + 1; // Move Y to top edge
h = -h; // Use positive height
}
if (y < _height)
{ // Not off bottom
int16_t y2 = y + h - 1;
if (y2 >= 0)
{ // Not off top
// Line partly or fully overlaps screen
if (y < 0)
{
y = 0;
h = y2 + 1;
} // Clip top
if (y2 >= _height)
{
h = _height - y;
} // Clip bottom
startWrite();
writeFillRectPreclipped(x, y, 1, h, color);
endWrite();
}
}
}
}
/*!
@brief Essentially writePixel() with a transaction around it. I don't
think this is in use by any of our code anymore (believe it was
for some older BMP-reading examples), but is kept here in case
any user code relies on it. Consider it DEPRECATED.
@param color 16-bit pixel color in '565' RGB format.
*/
void Adafruit_SPITFT::pushColor(uint16_t color)
{
startWrite();
SPI_WRITE16(color);
endWrite();
}
/*!
@brief Draw a 16-bit image (565 RGB) at the specified (x,y) position.
For 16-bit display devices; no color reduction performed.
Adapted from https://github.com/PaulStoffregen/ILI9341_t3
by Marc MERLIN. See examples/pictureEmbed to use this.
5/6/2017: function name and arguments have changed for
compatibility with current GFX library and to avoid naming
problems in prior implementation. Formerly drawBitmap() with
arguments in different order. Handles its own transaction and
edge clipping/rejection.
@param x Top left corner horizontal coordinate.
@param y Top left corner vertical coordinate.
@param pcolors Pointer to 16-bit array of pixel values.
@param w Width of bitmap in pixels.
@param h Height of bitmap in pixels.
*/
void Adafruit_SPITFT::drawRGBBitmap(int16_t x, int16_t y, uint16_t *pcolors, int16_t w, int16_t h)
{
int16_t x2, y2; // Lower-right coord
if ((x >= _width) || // Off-edge right
(y >= _height) || // " top
((x2 = (x + w - 1)) < 0) || // " left
((y2 = (y + h - 1)) < 0)) // " bottom
return;
int16_t bx1 = 0, by1 = 0, // Clipped top-left within bitmap
saveW = w; // Save original bitmap width value
if (x < 0)
{ // Clip left
w += x;
bx1 = -x;
x = 0;
}
if (y < 0)
{ // Clip top
h += y;
by1 = -y;
y = 0;
}
if (x2 >= _width)
w = _width - x; // Clip right
if (y2 >= _height)
h = _height - y; // Clip bottom
pcolors += by1 * saveW + bx1; // Offset bitmap ptr to clipped top-left
startWrite();
setAddrWindow(x, y, w, h); // Clipped area
if (saveW == w)
{
// Draw all-at-once
writePixels(pcolors, saveW * h); // Push all rows
}
else
{
// Draw line-by-line
while (h--)
{ // For each (clipped) scanline...
writePixels(pcolors, w); // Push one (clipped) row
pcolors += saveW; // Advance pointer by one full (unclipped) line
}
}
endWrite();
}
/**************************************************************************/
/*!
@brief Draw PROGMEM-resident XBitMap Files (*.xbm), exported from GIMP.
Usage: Export from GIMP to *.xbm, rename *.xbm to *.c and open in editor.
C Array can be directly used with this function.
There is no RAM-resident version of this function; if generating bitmaps
in RAM, use the format defined by drawBitmap() and call that instead.
@param x Top left corner x coordinate
@param y Top left corner y coordinate
@param bitmap byte array with monochrome bitmap
@param w Width of bitmap in pixels
@param h Hieght of bitmap in pixels
@param color 16-bit 5-6-5 Color to draw pixels with
*/
/**************************************************************************/
void Adafruit_SPITFT::drawXBitmap(int16_t x, int16_t y, const uint8_t bitmap[], int16_t w, int16_t h, uint16_t fg_color, uint16_t bg_color)
{
int16_t byteWidth = (w + 7) / 8; // Bitmap scanline pad = whole byte
uint8_t byte = 0;
// Nearly identical to drawBitmap(), only the bit order
// is reversed here (left-to-right = LSB to MSB):
uint16_t fg_color_data = SWAP_BYTES(fg_color);
uint16_t bg_color_data = SWAP_BYTES(bg_color);
startWrite();
for (int16_t j = 0; j < h; j++, y++)
{
for (int16_t i = 0; i < w; i++)
{
if (i & 7)
byte >>= 1;
else
byte = pgm_read_byte(&bitmap[j * byteWidth + i / 8]);
// if (byte & 0x01)
// writePixel(x + i, y, fg_color);
if (byte & 0x01)
*(((uint16_t *)spi_buffer) + i) = fg_color_data;
else
*(((uint16_t *)spi_buffer) + i) = bg_color_data;
}
setAddrWindow(x, y, w, 1); // Clipped area
hwspi._spi->write((char *)spi_buffer, 2 * w, (char *)NULL, 0);
}
endWrite();
}
// -------------------------------------------------------------------------
// Miscellaneous class member functions that don't draw anything.
/*!
@brief Invert the colors of the display (if supported by hardware).
Self-contained, no transaction setup required.
@param i true = inverted display, false = normal display.
*/
void Adafruit_SPITFT::invertDisplay(bool i)
{
startWrite();
writeCommand(i ? invertOnCommand : invertOffCommand);
endWrite();
}
/*!
@brief Given 8-bit red, green and blue values, return a 'packed'
16-bit color value in '565' RGB format (5 bits red, 6 bits
green, 5 bits blue). This is just a mathematical operation,
no hardware is touched.
@param red 8-bit red brightnesss (0 = off, 255 = max).
@param green 8-bit green brightnesss (0 = off, 255 = max).
@param blue 8-bit blue brightnesss (0 = off, 255 = max).
@return 'Packed' 16-bit color value (565 format).
*/
uint16_t Adafruit_SPITFT::color565(uint8_t red, uint8_t green, uint8_t blue)
{
return ((red & 0xF8) << 8) | ((green & 0xFC) << 3) | (blue >> 3);
}
// -------------------------------------------------------------------------
// Lowest-level hardware-interfacing functions. Many of these are inline and
// compile to different things based on #defines -- typically just a few
// instructions. Others, not so much, those are not inlined.
/*!
@brief Start an SPI transaction if using the hardware SPI interface to
the display. If using an earlier version of the Arduino platform
(before the addition of SPI transactions), this instead attempts
to set up the SPI clock and mode. No action is taken if the
connection is not hardware SPI-based. This does NOT include a
chip-select operation -- see startWrite() for a function that
encapsulated both actions.
*/
inline void Adafruit_SPITFT::SPI_BEGIN_TRANSACTION(void)