PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

4207 lines
129KB

  1. /***************************************************
  2. This is a library for the Adafruit 1.8" SPI display.
  3. This library works with the Adafruit 1.8" TFT Breakout w/SD card
  4. ----> http://www.adafruit.com/products/358
  5. as well as Adafruit raw 1.8" TFT display
  6. ----> http://www.adafruit.com/products/618
  7. Check out the links above for our tutorials and wiring diagrams
  8. These displays use SPI to communicate, 4 or 5 pins are required to
  9. interface (RST is optional)
  10. Adafruit invests time and resources providing this open source code,
  11. please support Adafruit and open-source hardware by purchasing
  12. products from Adafruit!
  13. Written by Limor Fried/Ladyada for Adafruit Industries.
  14. MIT license, all text above must be included in any redistribution
  15. ****************************************************/
  16. #include "ST7735_t3.h"
  17. #include "ST7789_t3.h"
  18. #include <limits.h>
  19. #include "pins_arduino.h"
  20. #include "wiring_private.h"
  21. #include <SPI.h>
  22. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  23. //#define DEBUG_ASYNC_UPDATE
  24. //#define DEBUG_ASYNC_LEDS
  25. #ifdef DEBUG_ASYNC_LEDS
  26. #define DEBUG_PIN_1 0
  27. #define DEBUG_PIN_2 1
  28. #define DEBUG_PIN_3 2
  29. #endif
  30. volatile short _dma_dummy_rx;
  31. ST7735_t3 *ST7735_t3::_dmaActiveDisplay[3] = {0, 0, 0};
  32. #if defined(__MK66FX1M0__)
  33. DMASetting ST7735_t3::_dmasettings[3][4];
  34. #endif
  35. #if defined(__IMXRT1062__) // Teensy 4.x
  36. // On T4 Setup the buffers to be used one per SPI buss...
  37. // This way we make sure it is hopefully in uncached memory
  38. ST7735DMA_Data ST7735_t3::_dma_data[3]; // one structure for each SPI buss...
  39. #endif
  40. #endif
  41. // Constructor when using software SPI. All output pins are configurable.
  42. ST7735_t3::ST7735_t3(uint8_t cs, uint8_t rs, uint8_t sid, uint8_t sclk, uint8_t rst)
  43. {
  44. _cs = cs;
  45. _rs = rs;
  46. _sid = sid;
  47. _sclk = sclk;
  48. _rst = rst;
  49. _rot = 0xff;
  50. hwSPI = false;
  51. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  52. _pfbtft = NULL;
  53. _use_fbtft = 0; // Are we in frame buffer mode?
  54. _we_allocated_buffer = NULL;
  55. _dma_state = 0;
  56. #endif
  57. _screenHeight = ST7735_TFTHEIGHT_160;
  58. _screenWidth = ST7735_TFTWIDTH;
  59. _width = _screenWidth;
  60. _height = _screenHeight;
  61. cursor_y = cursor_x = 0;
  62. textsize_x = 1;
  63. textsize_y = 1;
  64. textcolor = textbgcolor = 0xFFFF;
  65. wrap = true;
  66. font = NULL;
  67. gfxFont = NULL;
  68. setClipRect();
  69. setOrigin();
  70. }
  71. // Constructor when using hardware SPI. Faster, but must use SPI pins
  72. // specific to each board type (e.g. 11,13 for Uno, 51,52 for Mega, etc.)
  73. ST7735_t3::ST7735_t3(uint8_t cs, uint8_t rs, uint8_t rst)
  74. {
  75. _cs = cs;
  76. _rs = rs;
  77. _rst = rst;
  78. _rot = 0xff;
  79. hwSPI = true;
  80. _sid = _sclk = (uint8_t)-1;
  81. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  82. _pfbtft = NULL;
  83. _use_fbtft = 0; // Are we in frame buffer mode?
  84. _we_allocated_buffer = NULL;
  85. _dma_state = 0;
  86. #endif
  87. _screenHeight = ST7735_TFTHEIGHT_160;
  88. _screenWidth = ST7735_TFTWIDTH;
  89. cursor_y = cursor_x = 0;
  90. textsize_x = 1;
  91. textsize_y = 1;
  92. textcolor = textbgcolor = 0xFFFF;
  93. wrap = true;
  94. font = NULL;
  95. gfxFont = NULL;
  96. setClipRect();
  97. setOrigin();
  98. }
  99. /***************************************************************/
  100. /* Teensy 3.0, 3.1, 3.2, 3.5, 3.6 */
  101. /***************************************************************/
  102. #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
  103. inline void ST7735_t3::waitTransmitComplete(void) {
  104. uint32_t tmp __attribute__((unused));
  105. while (!(_pkinetisk_spi->SR & SPI_SR_TCF)) ; // wait until final output done
  106. tmp = _pkinetisk_spi->POPR; // drain the final RX FIFO word
  107. }
  108. inline void ST7735_t3::waitTransmitComplete(uint32_t mcr) {
  109. uint32_t tmp __attribute__((unused));
  110. while (1) {
  111. uint32_t sr = _pkinetisk_spi->SR;
  112. if (sr & SPI_SR_EOQF) break; // wait for last transmit
  113. if (sr & 0xF0) tmp = _pkinetisk_spi->POPR;
  114. }
  115. _pkinetisk_spi->SR = SPI_SR_EOQF;
  116. _pkinetisk_spi->MCR = mcr;
  117. while (_pkinetisk_spi->SR & 0xF0) {
  118. tmp = _pkinetisk_spi->POPR;
  119. }
  120. }
  121. inline void ST7735_t3::spiwrite(uint8_t c)
  122. {
  123. // pass 1 if we actually are setup to with MOSI and SCLK on hardware SPI use it...
  124. if (_pspi) {
  125. _pspi->transfer(c);
  126. return;
  127. }
  128. for (uint8_t bit = 0x80; bit; bit >>= 1) {
  129. *datapin = ((c & bit) ? 1 : 0);
  130. *clkpin = 1;
  131. *clkpin = 0;
  132. }
  133. }
  134. inline void ST7735_t3::spiwrite16(uint16_t d)
  135. {
  136. // pass 1 if we actually are setup to with MOSI and SCLK on hardware SPI use it...
  137. if (_pspi) {
  138. _pspi->transfer16(d);
  139. return;
  140. }
  141. spiwrite(d >> 8);
  142. spiwrite(d);
  143. }
  144. void ST7735_t3::writecommand(uint8_t c)
  145. {
  146. if (hwSPI) {
  147. _pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0);
  148. while (((_pkinetisk_spi->SR) & (15 << 12)) > _fifo_full_test) ; // wait if FIFO full
  149. } else {
  150. *rspin = 0;
  151. spiwrite(c);
  152. }
  153. }
  154. void ST7735_t3::writecommand_last(uint8_t c) {
  155. if (hwSPI) {
  156. uint32_t mcr = _pkinetisk_spi->MCR;
  157. _pkinetisk_spi->PUSHR = c | (pcs_command << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
  158. waitTransmitComplete(mcr);
  159. } else {
  160. *rspin = 0;
  161. spiwrite(c);
  162. }
  163. }
  164. void ST7735_t3::writedata(uint8_t c)
  165. {
  166. if (hwSPI) {
  167. _pkinetisk_spi->PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0);
  168. while (((_pkinetisk_spi->SR) & (15 << 12)) > _fifo_full_test) ; // wait if FIFO full
  169. } else {
  170. *rspin = 1;
  171. spiwrite(c);
  172. }
  173. }
  174. void ST7735_t3::writedata_last(uint8_t c)
  175. {
  176. if (hwSPI) {
  177. uint32_t mcr = _pkinetisk_spi->MCR;
  178. _pkinetisk_spi->PUSHR = c | (pcs_data << 16) | SPI_PUSHR_CTAS(0) | SPI_PUSHR_EOQ;
  179. waitTransmitComplete(mcr);
  180. } else {
  181. *rspin = 1;
  182. spiwrite(c);
  183. }
  184. }
  185. void ST7735_t3::writedata16(uint16_t d)
  186. {
  187. if (hwSPI) {
  188. _pkinetisk_spi->PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1);
  189. while (((_pkinetisk_spi->SR) & (15 << 12)) > _fifo_full_test) ; // wait if FIFO full
  190. } else {
  191. *rspin = 1;
  192. spiwrite16(d);
  193. }
  194. }
  195. void ST7735_t3::writedata16_last(uint16_t d)
  196. {
  197. if (hwSPI) {
  198. uint32_t mcr = _pkinetisk_spi->MCR;
  199. _pkinetisk_spi->PUSHR = d | (pcs_data << 16) | SPI_PUSHR_CTAS(1) | SPI_PUSHR_EOQ;
  200. waitTransmitComplete(mcr);
  201. } else {
  202. *rspin = 1;
  203. spiwrite16(d);
  204. }
  205. }
  206. #define CTAR_24MHz (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0) | SPI_CTAR_DBR)
  207. #define CTAR_16MHz (SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0) | SPI_CTAR_DBR)
  208. #define CTAR_12MHz (SPI_CTAR_PBR(0) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0))
  209. #define CTAR_8MHz (SPI_CTAR_PBR(1) | SPI_CTAR_BR(0) | SPI_CTAR_CSSCK(0))
  210. #define CTAR_6MHz (SPI_CTAR_PBR(0) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1))
  211. #define CTAR_4MHz (SPI_CTAR_PBR(1) | SPI_CTAR_BR(1) | SPI_CTAR_CSSCK(1))
  212. void ST7735_t3::setBitrate(uint32_t n)
  213. {
  214. if (n >= 24000000) {
  215. ctar = CTAR_24MHz;
  216. } else if (n >= 16000000) {
  217. ctar = CTAR_16MHz;
  218. } else if (n >= 12000000) {
  219. ctar = CTAR_12MHz;
  220. } else if (n >= 8000000) {
  221. ctar = CTAR_8MHz;
  222. } else if (n >= 6000000) {
  223. ctar = CTAR_6MHz;
  224. } else {
  225. ctar = CTAR_4MHz;
  226. }
  227. SIM_SCGC6 |= SIM_SCGC6_SPI0;
  228. _pkinetisk_spi->MCR = SPI_MCR_MDIS | SPI_MCR_HALT;
  229. _pkinetisk_spi->CTAR0 = ctar | SPI_CTAR_FMSZ(7);
  230. _pkinetisk_spi->CTAR1 = ctar | SPI_CTAR_FMSZ(15);
  231. _pkinetisk_spi->MCR = SPI_MCR_MSTR | SPI_MCR_PCSIS(0x1F) | SPI_MCR_CLR_TXF | SPI_MCR_CLR_RXF;
  232. }
  233. /***************************************************************/
  234. /* Teensy 4. */
  235. /***************************************************************/
  236. #elif defined(__IMXRT1062__) // Teensy 4.x
  237. inline void ST7735_t3::spiwrite(uint8_t c)
  238. {
  239. //Serial.println(c, HEX);
  240. if (_pspi) {
  241. _pspi->transfer(c);
  242. } else {
  243. // Fast SPI bitbang swiped from LPD8806 library
  244. for(uint8_t bit = 0x80; bit; bit >>= 1) {
  245. if(c & bit) DIRECT_WRITE_HIGH(_mosiport, _mosipinmask);
  246. else DIRECT_WRITE_LOW(_mosiport, _mosipinmask);
  247. DIRECT_WRITE_HIGH(_sckport, _sckpinmask);
  248. asm("nop; nop; nop; nop; nop; nop; nop; nop; nop; nop; nop;");
  249. DIRECT_WRITE_LOW(_sckport, _sckpinmask);
  250. }
  251. }
  252. }
  253. void ST7735_t3::writecommand(uint8_t c)
  254. {
  255. if (hwSPI) {
  256. maybeUpdateTCR(_tcr_dc_assert | LPSPI_TCR_FRAMESZ(7) /*| LPSPI_TCR_CONT*/);
  257. _pimxrt_spi->TDR = c;
  258. _pending_rx_count++; //
  259. waitFifoNotFull();
  260. } else {
  261. DIRECT_WRITE_LOW(_dcport, _dcpinmask);
  262. spiwrite(c);
  263. }
  264. }
  265. void ST7735_t3::writecommand_last(uint8_t c)
  266. {
  267. if (hwSPI) {
  268. maybeUpdateTCR(_tcr_dc_assert | LPSPI_TCR_FRAMESZ(7));
  269. _pimxrt_spi->TDR = c;
  270. _pending_rx_count++; //
  271. waitTransmitComplete();
  272. } else {
  273. DIRECT_WRITE_LOW(_dcport, _dcpinmask);
  274. spiwrite(c);
  275. }
  276. }
  277. void ST7735_t3::writedata(uint8_t c)
  278. {
  279. if (hwSPI) {
  280. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(7));
  281. _pimxrt_spi->TDR = c;
  282. _pending_rx_count++; //
  283. waitTransmitComplete();
  284. } else {
  285. DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
  286. spiwrite(c);
  287. }
  288. }
  289. void ST7735_t3::writedata_last(uint8_t c)
  290. {
  291. if (hwSPI) {
  292. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(7));
  293. _pimxrt_spi->TDR = c;
  294. _pending_rx_count++; //
  295. waitTransmitComplete();
  296. } else {
  297. DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
  298. spiwrite(c);
  299. }
  300. }
  301. void ST7735_t3::writedata16(uint16_t d)
  302. {
  303. if (hwSPI) {
  304. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(15) | LPSPI_TCR_CONT);
  305. _pimxrt_spi->TDR = d;
  306. _pending_rx_count++; //
  307. waitFifoNotFull();
  308. } else {
  309. DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
  310. spiwrite(d >> 8);
  311. spiwrite(d);
  312. }
  313. }
  314. void ST7735_t3::writedata16_last(uint16_t d)
  315. {
  316. if (hwSPI) {
  317. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(15));
  318. _pimxrt_spi->TDR = d;
  319. // _pimxrt_spi->SR = LPSPI_SR_WCF | LPSPI_SR_FCF | LPSPI_SR_TCF;
  320. _pending_rx_count++; //
  321. waitTransmitComplete();
  322. } else {
  323. DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
  324. spiwrite(d >> 8);
  325. spiwrite(d);
  326. }
  327. }
  328. void ST7735_t3::setBitrate(uint32_t n)
  329. {
  330. if (n >= 8000000) {
  331. SPI.setClockDivider(SPI_CLOCK_DIV2);
  332. } else if (n >= 4000000) {
  333. SPI.setClockDivider(SPI_CLOCK_DIV4);
  334. } else if (n >= 2000000) {
  335. SPI.setClockDivider(SPI_CLOCK_DIV8);
  336. } else {
  337. SPI.setClockDivider(SPI_CLOCK_DIV16);
  338. }
  339. }
  340. /***************************************************************/
  341. /* Teensy LC */
  342. /***************************************************************/
  343. #elif defined(__MKL26Z64__)
  344. inline void ST7735_t3::spiwrite(uint8_t c)
  345. {
  346. //Serial.println(c, HEX);
  347. if (hwSPI) {
  348. SPI.transfer(c);
  349. } else if (hwSPI1) {
  350. SPI1.transfer(c);
  351. } else {
  352. // Fast SPI bitbang swiped from LPD8806 library
  353. for(uint8_t bit = 0x80; bit; bit >>= 1) {
  354. if(c & bit) *dataport |= datapinmask;
  355. else *dataport &= ~datapinmask;
  356. *clkport |= clkpinmask;
  357. *clkport &= ~clkpinmask;
  358. }
  359. }
  360. }
  361. void ST7735_t3::writecommand(uint8_t c)
  362. {
  363. *rsport &= ~rspinmask;
  364. spiwrite(c);
  365. }
  366. void ST7735_t3::writecommand_last(uint8_t c)
  367. {
  368. *rsport &= ~rspinmask;
  369. spiwrite(c);
  370. }
  371. void ST7735_t3::writedata(uint8_t c)
  372. {
  373. *rsport |= rspinmask;
  374. spiwrite(c);
  375. }
  376. void ST7735_t3::writedata_last(uint8_t c)
  377. {
  378. *rsport |= rspinmask;
  379. spiwrite(c);
  380. }
  381. void ST7735_t3::writedata16(uint16_t d)
  382. {
  383. *rsport |= rspinmask;
  384. spiwrite(d >> 8);
  385. spiwrite(d);
  386. }
  387. void ST7735_t3::writedata16_last(uint16_t d)
  388. {
  389. *rsport |= rspinmask;
  390. spiwrite(d >> 8);
  391. spiwrite(d);
  392. }
  393. void ST7735_t3::setBitrate(uint32_t n)
  394. {
  395. if (n >= 8000000) {
  396. SPI.setClockDivider(SPI_CLOCK_DIV2);
  397. } else if (n >= 4000000) {
  398. SPI.setClockDivider(SPI_CLOCK_DIV4);
  399. } else if (n >= 2000000) {
  400. SPI.setClockDivider(SPI_CLOCK_DIV8);
  401. } else {
  402. SPI.setClockDivider(SPI_CLOCK_DIV16);
  403. }
  404. }
  405. #endif //#if defined(__SAM3X8E__)
  406. // Rather than a bazillion writecommand() and writedata() calls, screen
  407. // initialization commands and arguments are organized in these tables
  408. // stored in PROGMEM. The table may look bulky, but that's mostly the
  409. // formatting -- storage-wise this is hundreds of bytes more compact
  410. // than the equivalent code. Companion function follows.
  411. #define DELAY 0x80
  412. static const uint8_t PROGMEM
  413. Bcmd[] = { // Initialization commands for 7735B screens
  414. 18, // 18 commands in list:
  415. ST7735_SWRESET, DELAY, // 1: Software reset, no args, w/delay
  416. 50, // 50 ms delay
  417. ST7735_SLPOUT , DELAY, // 2: Out of sleep mode, no args, w/delay
  418. 255, // 255 = 500 ms delay
  419. ST7735_COLMOD , 1+DELAY, // 3: Set color mode, 1 arg + delay:
  420. 0x05, // 16-bit color
  421. 10, // 10 ms delay
  422. ST7735_FRMCTR1, 3+DELAY, // 4: Frame rate control, 3 args + delay:
  423. 0x00, // fastest refresh
  424. 0x06, // 6 lines front porch
  425. 0x03, // 3 lines back porch
  426. 10, // 10 ms delay
  427. ST7735_MADCTL , 1 , // 5: Memory access ctrl (directions), 1 arg:
  428. 0x08, // Row addr/col addr, bottom to top refresh
  429. ST7735_DISSET5, 2 , // 6: Display settings #5, 2 args, no delay:
  430. 0x15, // 1 clk cycle nonoverlap, 2 cycle gate
  431. // rise, 3 cycle osc equalize
  432. 0x02, // Fix on VTL
  433. ST7735_INVCTR , 1 , // 7: Display inversion control, 1 arg:
  434. 0x0, // Line inversion
  435. ST7735_PWCTR1 , 2+DELAY, // 8: Power control, 2 args + delay:
  436. 0x02, // GVDD = 4.7V
  437. 0x70, // 1.0uA
  438. 10, // 10 ms delay
  439. ST7735_PWCTR2 , 1 , // 9: Power control, 1 arg, no delay:
  440. 0x05, // VGH = 14.7V, VGL = -7.35V
  441. ST7735_PWCTR3 , 2 , // 10: Power control, 2 args, no delay:
  442. 0x01, // Opamp current small
  443. 0x02, // Boost frequency
  444. ST7735_VMCTR1 , 2+DELAY, // 11: Power control, 2 args + delay:
  445. 0x3C, // VCOMH = 4V
  446. 0x38, // VCOML = -1.1V
  447. 10, // 10 ms delay
  448. ST7735_PWCTR6 , 2 , // 12: Power control, 2 args, no delay:
  449. 0x11, 0x15,
  450. ST7735_GMCTRP1,16 , // 13: Magical unicorn dust, 16 args, no delay:
  451. 0x09, 0x16, 0x09, 0x20, // (seriously though, not sure what
  452. 0x21, 0x1B, 0x13, 0x19, // these config values represent)
  453. 0x17, 0x15, 0x1E, 0x2B,
  454. 0x04, 0x05, 0x02, 0x0E,
  455. ST7735_GMCTRN1,16+DELAY, // 14: Sparkles and rainbows, 16 args + delay:
  456. 0x0B, 0x14, 0x08, 0x1E, // (ditto)
  457. 0x22, 0x1D, 0x18, 0x1E,
  458. 0x1B, 0x1A, 0x24, 0x2B,
  459. 0x06, 0x06, 0x02, 0x0F,
  460. 10, // 10 ms delay
  461. ST7735_CASET , 4 , // 15: Column addr set, 4 args, no delay:
  462. 0x00, 0x02, // XSTART = 2
  463. 0x00, 0x81, // XEND = 129
  464. ST7735_RASET , 4 , // 16: Row addr set, 4 args, no delay:
  465. 0x00, 0x02, // XSTART = 1
  466. 0x00, 0x81, // XEND = 160
  467. ST7735_NORON , DELAY, // 17: Normal display on, no args, w/delay
  468. 10, // 10 ms delay
  469. ST7735_DISPON , DELAY, // 18: Main screen turn on, no args, w/delay
  470. 255 }, // 255 = 500 ms delay
  471. Rcmd1[] = { // Init for 7735R, part 1 (red or green tab)
  472. 15, // 15 commands in list:
  473. ST7735_SWRESET, DELAY, // 1: Software reset, 0 args, w/delay
  474. 150, // 150 ms delay
  475. ST7735_SLPOUT , DELAY, // 2: Out of sleep mode, 0 args, w/delay
  476. 255, // 500 ms delay
  477. ST7735_FRMCTR1, 3 , // 3: Frame rate ctrl - normal mode, 3 args:
  478. 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
  479. ST7735_FRMCTR2, 3 , // 4: Frame rate control - idle mode, 3 args:
  480. 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
  481. ST7735_FRMCTR3, 6 , // 5: Frame rate ctrl - partial mode, 6 args:
  482. 0x01, 0x2C, 0x2D, // Dot inversion mode
  483. 0x01, 0x2C, 0x2D, // Line inversion mode
  484. ST7735_INVCTR , 1 , // 6: Display inversion ctrl, 1 arg, no delay:
  485. 0x07, // No inversion
  486. ST7735_PWCTR1 , 3 , // 7: Power control, 3 args, no delay:
  487. 0xA2,
  488. 0x02, // -4.6V
  489. 0x84, // AUTO mode
  490. ST7735_PWCTR2 , 1 , // 8: Power control, 1 arg, no delay:
  491. 0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
  492. ST7735_PWCTR3 , 2 , // 9: Power control, 2 args, no delay:
  493. 0x0A, // Opamp current small
  494. 0x00, // Boost frequency
  495. ST7735_PWCTR4 , 2 , // 10: Power control, 2 args, no delay:
  496. 0x8A, // BCLK/2, Opamp current small & Medium low
  497. 0x2A,
  498. ST7735_PWCTR5 , 2 , // 11: Power control, 2 args, no delay:
  499. 0x8A, 0xEE,
  500. ST7735_VMCTR1 , 1 , // 12: Power control, 1 arg, no delay:
  501. 0x0E,
  502. ST7735_INVOFF , 0 , // 13: Don't invert display, no args, no delay
  503. ST7735_MADCTL , 1 , // 14: Memory access control (directions), 1 arg:
  504. 0xC8, // row addr/col addr, bottom to top refresh
  505. ST7735_COLMOD , 1 , // 15: set color mode, 1 arg, no delay:
  506. 0x05 }, // 16-bit color
  507. Rcmd2green[] = { // Init for 7735R, part 2 (green tab only)
  508. 2, // 2 commands in list:
  509. ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay:
  510. 0x00, 0x02, // XSTART = 0
  511. 0x00, 0x7F+0x02, // XEND = 127
  512. ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay:
  513. 0x00, 0x01, // XSTART = 0
  514. 0x00, 0x9F+0x01 }, // XEND = 159
  515. Rcmd2red[] = { // Init for 7735R, part 2 (red tab only)
  516. 2, // 2 commands in list:
  517. ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay:
  518. 0x00, 0x00, // XSTART = 0
  519. 0x00, 0x7F, // XEND = 127
  520. ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay:
  521. 0x00, 0x00, // XSTART = 0
  522. 0x00, 0x9F }, // XEND = 159
  523. Rcmd2green144[] = { // Init for 7735R, part 2 (green 1.44 tab)
  524. 2, // 2 commands in list:
  525. ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay:
  526. 0x00, 0x00, // XSTART = 0
  527. 0x00, 0x7F, // XEND = 127
  528. ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay:
  529. 0x00, 0x00, // XSTART = 0
  530. 0x00, 0x7F }, // XEND = 127
  531. Rcmd2green160x80[] = { // 7735R init, part 2 (mini 160x80)
  532. 2, // 2 commands in list:
  533. ST7735_CASET, 4, // 1: Column addr set, 4 args, no delay:
  534. 0x00, 0x00, // XSTART = 0
  535. 0x00, 0x4F, // XEND = 79
  536. ST7735_RASET, 4, // 2: Row addr set, 4 args, no delay:
  537. 0x00, 0x00, // XSTART = 0
  538. 0x00, 0x9F}, // XEND = 159
  539. Rcmd2minist7735s[] = {
  540. 3, // 2 commands in list:
  541. ST7735_CASET , 4 , // 1: Column addr set, 4 args, no delay:
  542. 0x00, 0x00+26, // XSTART = 0
  543. 0x00, 0x7F+26, // XEND = 127
  544. ST7735_RASET , 4 , // 2: Row addr set, 4 args, no delay:
  545. 0x00, 0x00+1, // XSTART = 0
  546. 0x00, 0x4F+1, // XEND = 79
  547. ST7735_INVON, 0}, // these displays need colors inversed
  548. Rcmd3[] = { // Init for 7735R, part 3 (red or green tab)
  549. 4, // 4 commands in list:
  550. ST7735_GMCTRP1, 16 , // 1: Magical unicorn dust, 16 args, no delay:
  551. 0x02, 0x1c, 0x07, 0x12,
  552. 0x37, 0x32, 0x29, 0x2d,
  553. 0x29, 0x25, 0x2B, 0x39,
  554. 0x00, 0x01, 0x03, 0x10,
  555. ST7735_GMCTRN1, 16 , // 2: Sparkles and rainbows, 16 args, no delay:
  556. 0x03, 0x1d, 0x07, 0x06,
  557. 0x2E, 0x2C, 0x29, 0x2D,
  558. 0x2E, 0x2E, 0x37, 0x3F,
  559. 0x00, 0x00, 0x02, 0x10,
  560. ST7735_NORON , DELAY, // 3: Normal display on, no args, w/delay
  561. 10, // 10 ms delay
  562. ST7735_DISPON , DELAY, // 4: Main screen turn on, no args w/delay
  563. 100 }; // 100 ms delay
  564. // Companion code to the above tables. Reads and issues
  565. // a series of LCD commands stored in PROGMEM byte array.
  566. void ST7735_t3::commandList(const uint8_t *addr)
  567. {
  568. uint8_t numCommands, numArgs;
  569. uint16_t ms;
  570. beginSPITransaction();
  571. numCommands = pgm_read_byte(addr++); // Number of commands to follow
  572. //Serial.printf("CommandList: numCmds:%d\n", numCommands); Serial.flush();
  573. while(numCommands--) { // For each command...
  574. writecommand_last(pgm_read_byte(addr++)); // Read, issue command
  575. numArgs = pgm_read_byte(addr++); // Number of args to follow
  576. ms = numArgs & DELAY; // If hibit set, delay follows args
  577. numArgs &= ~DELAY; // Mask out delay bit
  578. while(numArgs > 1) { // For each argument...
  579. writedata(pgm_read_byte(addr++)); // Read, issue argument
  580. numArgs--;
  581. }
  582. if (numArgs) writedata_last(pgm_read_byte(addr++)); // Read, issue argument - wait until this one completes
  583. if(ms) {
  584. ms = pgm_read_byte(addr++); // Read post-command delay time (ms)
  585. if(ms == 255) ms = 500; // If 255, delay for 500 ms
  586. //Serial.printf("delay %d\n", ms); Serial.flush();
  587. endSPITransaction();
  588. delay(ms);
  589. beginSPITransaction();
  590. }
  591. }
  592. endSPITransaction();
  593. }
  594. // Initialization code common to both 'B' and 'R' type displays
  595. void ST7735_t3::commonInit(const uint8_t *cmdList, uint8_t mode)
  596. {
  597. _colstart = _rowstart = 0; // May be overridden in init func
  598. _ystart = _xstart = 0;
  599. #if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
  600. if (_sid == (uint8_t)-1) _sid = 11;
  601. if (_sclk == (uint8_t)-1) _sclk = 13;
  602. // Lets try to handle cases where DC is not hardware, without going all the way down to bit bang!
  603. //if (SPI.pinIsMOSI(_sid) && SPI.pinIsSCK(_sclk) && SPI.pinIsChipSelect(_rs)) {
  604. if (SPI.pinIsMOSI(_sid) && SPI.pinIsSCK(_sclk)) {
  605. _pspi = &SPI;
  606. _spi_num = 0; // Which buss is this spi on?
  607. _pkinetisk_spi = &KINETISK_SPI0; // Could hack our way to grab this from SPI object, but...
  608. _fifo_full_test = (3 << 12);
  609. //Serial.println("ST7735_t3::commonInit SPI");
  610. #if defined(__MK64FX512__) || defined(__MK66FX1M0__)
  611. } else if (SPI1.pinIsMOSI(_sid) && SPI1.pinIsSCK(_sclk)) {
  612. _pspi = &SPI1;
  613. _spi_num = 1; // Which buss is this spi on?
  614. _pkinetisk_spi = &KINETISK_SPI1;
  615. _fifo_full_test = (0 << 12);
  616. //Serial.println("ST7735_t3::commonInit SPI1");
  617. } else if (SPI2.pinIsMOSI(_sid) && SPI2.pinIsSCK(_sclk)) {
  618. _pspi = &SPI2;
  619. _spi_num = 2; // Which buss is this spi on?
  620. _pkinetisk_spi = &KINETISK_SPI2;
  621. _fifo_full_test = (0 << 12);
  622. //Serial.println("ST7735_t3::commonInit SPI2");
  623. #endif
  624. } else _pspi = nullptr;
  625. if (_pspi) {
  626. hwSPI = true;
  627. _pspi->setMOSI(_sid);
  628. _pspi->setSCK(_sclk);
  629. _pspi->begin();
  630. //Serial.println("After SPI begin");
  631. _spiSettings = SPISettings(ST7735_SPICLOCK, MSBFIRST, mode);
  632. // See if both CS and DC are valid CS pins.
  633. if (_pspi->pinIsChipSelect(_rs, _cs)) {
  634. pcs_data = _pspi->setCS(_cs);
  635. pcs_command = pcs_data | _pspi->setCS(_rs);
  636. cspin = 0; // Let know that we are not setting manual
  637. //Serial.println("Both CS and DC are SPI pins");
  638. // See if at least DC is hardware CS...
  639. } else if (_pspi->pinIsChipSelect(_rs)) {
  640. // We already verified that _rs was valid CS pin above.
  641. pcs_data = 0;
  642. pcs_command = pcs_data | _pspi->setCS(_rs);
  643. if (_cs != 0xff) {
  644. pinMode(_cs, OUTPUT);
  645. cspin = portOutputRegister(digitalPinToPort(_cs));
  646. *cspin = 1;
  647. }
  648. } else {
  649. // DC is not, and won't bother to check CS...
  650. pcs_data = 0;
  651. pcs_command = 0;
  652. if (_cs != 0xff) {
  653. pinMode(_cs, OUTPUT);
  654. cspin = portOutputRegister(digitalPinToPort(_cs));
  655. *cspin = 1;
  656. }
  657. pinMode(_rs, OUTPUT);
  658. rspin = portOutputRegister(digitalPinToPort(_rs));
  659. *rspin = 0;
  660. // Pass 1, now lets set hwSPI false
  661. hwSPI = false;
  662. }
  663. // Hack to get hold of the SPI Hardware information...
  664. uint32_t *pa = (uint32_t*)((void*)_pspi);
  665. _spi_hardware = (SPIClass::SPI_Hardware_t*)(void*)pa[1];
  666. } else {
  667. //Serial.println("ST7735_t3::commonInit Software SPI :(");
  668. hwSPI = false;
  669. _pspi = nullptr;
  670. cspin = (_cs != 0xff)? portOutputRegister(digitalPinToPort(_cs)) : 0;
  671. rspin = portOutputRegister(digitalPinToPort(_rs));
  672. clkpin = portOutputRegister(digitalPinToPort(_sclk));
  673. datapin = portOutputRegister(digitalPinToPort(_sid));
  674. *cspin = 1;
  675. *rspin = 0;
  676. *clkpin = 0;
  677. *datapin = 0;
  678. if (_cs != 0xff) pinMode(_cs, OUTPUT);
  679. pinMode(_rs, OUTPUT);
  680. pinMode(_sclk, OUTPUT);
  681. pinMode(_sid, OUTPUT);
  682. }
  683. // Teensy 4
  684. #elif defined(__IMXRT1062__) // Teensy 4.x
  685. if (_sid == (uint8_t)-1) _sid = 11;
  686. if (_sclk == (uint8_t)-1) _sclk = 13;
  687. if (SPI.pinIsMOSI(_sid) && SPI.pinIsSCK(_sclk)) {
  688. _pspi = &SPI;
  689. _spi_num = 0; // Which buss is this spi on?
  690. _pimxrt_spi = &IMXRT_LPSPI4_S; // Could hack our way to grab this from SPI object, but...
  691. } else if (SPI1.pinIsMOSI(_sid) && SPI1.pinIsSCK(_sclk)) {
  692. _pspi = &SPI1;
  693. _spi_num = 1; // Which buss is this spi on?
  694. _pimxrt_spi = &IMXRT_LPSPI3_S;
  695. } else if (SPI2.pinIsMOSI(_sid) && SPI2.pinIsSCK(_sclk)) {
  696. _pspi = &SPI2;
  697. _spi_num = 2; // Which buss is this spi on?
  698. _pimxrt_spi = &IMXRT_LPSPI1_S;
  699. } else _pspi = nullptr;
  700. if (_pspi) {
  701. hwSPI = true;
  702. _pspi->begin();
  703. _pending_rx_count = 0;
  704. _spiSettings = SPISettings(ST7735_SPICLOCK, MSBFIRST, mode);
  705. _pspi->beginTransaction(_spiSettings); // Should have our settings.
  706. _pspi->transfer(0); // hack to see if it will actually change then...
  707. _pspi->endTransaction();
  708. _spi_tcr_current = _pimxrt_spi->TCR; // get the current TCR value
  709. // uint32_t *phack = (uint32_t* )&_spiSettings;
  710. // Serial.printf("SPI Settings: TCR: %x %x (%x %x)\n", _spi_tcr_current, _pimxrt_spi->TCR, phack[0], phack[1]);
  711. // Hack to get hold of the SPI Hardware information...
  712. uint32_t *pa = (uint32_t*)((void*)_pspi);
  713. _spi_hardware = (SPIClass::SPI_Hardware_t*)(void*)pa[1];
  714. } else {
  715. hwSPI = false;
  716. _sckport = portOutputRegister(_sclk);
  717. _sckpinmask = digitalPinToBitMask(_sclk);
  718. pinMode(_sclk, OUTPUT);
  719. DIRECT_WRITE_LOW(_sckport, _sckpinmask);
  720. _mosiport = portOutputRegister(_sid);
  721. _mosipinmask = digitalPinToBitMask(_sid);
  722. pinMode(_sid, OUTPUT);
  723. DIRECT_WRITE_LOW(_mosiport, _mosipinmask);
  724. }
  725. if (_cs != 0xff) {
  726. _csport = portOutputRegister(_cs);
  727. _cspinmask = digitalPinToBitMask(_cs);
  728. pinMode(_cs, OUTPUT);
  729. DIRECT_WRITE_HIGH(_csport, _cspinmask);
  730. } else _csport = 0;
  731. if (_pspi && _pspi->pinIsChipSelect(_rs)) {
  732. uint8_t dc_cs_index = _pspi->setCS(_rs);
  733. _dcport = 0;
  734. _dcpinmask = 0;
  735. dc_cs_index--; // convert to 0 based
  736. _tcr_dc_assert = LPSPI_TCR_PCS(dc_cs_index);
  737. _tcr_dc_not_assert = LPSPI_TCR_PCS(3);
  738. } else {
  739. //Serial.println("ST7735_t3: Error not DC is not valid hardware CS pin");
  740. _dcport = portOutputRegister(_rs);
  741. _dcpinmask = digitalPinToBitMask(_rs);
  742. pinMode(_rs, OUTPUT);
  743. DIRECT_WRITE_HIGH(_dcport, _dcpinmask);
  744. _tcr_dc_assert = LPSPI_TCR_PCS(0);
  745. _tcr_dc_not_assert = LPSPI_TCR_PCS(1);
  746. }
  747. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(7));
  748. // Teensy LC
  749. #elif defined(__MKL26Z64__)
  750. hwSPI1 = false;
  751. if (_sid == (uint8_t)-1) _sid = 11;
  752. if (_sclk == (uint8_t)-1) _sclk = 13;
  753. // See if pins are on standard SPI0
  754. if ((_sid == 7 || _sid == 11) && (_sclk == 13 || _sclk == 14)) {
  755. hwSPI = true;
  756. } else {
  757. hwSPI = false;
  758. if ((_sid == 0 || _sid == 21) && (_sclk == 20 )) {
  759. hwSPI1 = true;
  760. }
  761. }
  762. if (_cs != 0xff) {
  763. pinMode(_cs, OUTPUT);
  764. csport = portOutputRegister(digitalPinToPort(_cs));
  765. cspinmask = digitalPinToBitMask(_cs);
  766. } else csport = 0;
  767. pinMode(_rs, OUTPUT);
  768. rsport = portOutputRegister(digitalPinToPort(_rs));
  769. rspinmask = digitalPinToBitMask(_rs);
  770. _spiSettings = SPISettings(ST7735_SPICLOCK, MSBFIRST, mode);
  771. if(hwSPI) { // Using hardware SPI
  772. if (_sclk == 14) SPI.setSCK(14);
  773. if (_sid == 7) SPI.setMOSI(7);
  774. SPI.begin();
  775. } else if(hwSPI1) { // Using hardware SPI
  776. SPI1.setSCK(_sclk);
  777. SPI1.setMOSI(_sid);
  778. SPI1.begin();
  779. } else {
  780. pinMode(_sclk, OUTPUT);
  781. pinMode(_sid , OUTPUT);
  782. clkport = portOutputRegister(digitalPinToPort(_sclk));
  783. dataport = portOutputRegister(digitalPinToPort(_sid));
  784. clkpinmask = digitalPinToBitMask(_sclk);
  785. datapinmask = digitalPinToBitMask(_sid);
  786. *clkport &= ~clkpinmask;
  787. *dataport &= ~datapinmask;
  788. }
  789. // toggle RST low to reset; CS low so it'll listen to us
  790. *csport &= ~cspinmask;
  791. #endif
  792. // BUGBUG
  793. // digitalWrite(_cs, LOW);
  794. if (_rst != 0xff) {
  795. pinMode(_rst, OUTPUT);
  796. digitalWrite(_rst, HIGH);
  797. delay(100);
  798. digitalWrite(_rst, LOW);
  799. delay(100);
  800. digitalWrite(_rst, HIGH);
  801. delay(200);
  802. }
  803. if(cmdList) commandList(cmdList);
  804. }
  805. // Initialization for ST7735B screens
  806. void ST7735_t3::initB(void)
  807. {
  808. commonInit(Bcmd);
  809. }
  810. // Initialization for ST7735R screens (green or red tabs)
  811. void ST7735_t3::initR(uint8_t options)
  812. {
  813. commonInit(Rcmd1);
  814. if (options == INITR_GREENTAB) {
  815. commandList(Rcmd2green);
  816. _colstart = 2;
  817. _rowstart = 1;
  818. } else if(options == INITR_144GREENTAB) {
  819. _screenHeight = ST7735_TFTHEIGHT_144;
  820. commandList(Rcmd2green144);
  821. _colstart = 2;
  822. _rowstart = 3;
  823. } else if(options == INITR_144GREENTAB_OFFSET) {
  824. _screenHeight = ST7735_TFTHEIGHT_144;
  825. commandList(Rcmd2green144);
  826. _colstart = 0;
  827. _rowstart = 32;
  828. } else if(options == INITR_MINI160x80) {
  829. _screenHeight = ST7735_TFTHEIGHT_160;
  830. _screenWidth = ST7735_TFTWIDTH_80;
  831. commandList(Rcmd2green160x80);
  832. _colstart = 24;
  833. _rowstart = 0;
  834. } else if (options == INITR_MINI160x80_ST7735S) {
  835. _screenHeight = 160;
  836. _screenWidth = 80;
  837. commandList(Rcmd2minist7735s);
  838. _colstart = 26;
  839. _rowstart = 1;
  840. } else {
  841. // _colstart, _rowstart left at default '0' values
  842. commandList(Rcmd2red);
  843. }
  844. commandList(Rcmd3);
  845. // if black or mini, change MADCTL color filter
  846. if ((options == INITR_BLACKTAB) || (options == INITR_MINI160x80)){
  847. writecommand(ST7735_MADCTL);
  848. writedata_last(0xC0);
  849. }
  850. tabcolor = options;
  851. setRotation(0);
  852. }
  853. void ST7735_t3::setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)
  854. {
  855. beginSPITransaction();
  856. setAddr(x0, y0, x1, y1);
  857. writecommand(ST7735_RAMWR); // write to RAM
  858. // The setAddrWindow/pushColor will only work if SPI is kept active during this loop...
  859. endSPITransaction();
  860. }
  861. void ST7735_t3::pushColor(uint16_t color, boolean last_pixel)
  862. {
  863. //beginSPITransaction();
  864. if (last_pixel) {
  865. writedata16_last(color);
  866. endSPITransaction();
  867. } else {
  868. writedata16(color);
  869. }
  870. }
  871. //#include "glcdfont.c"
  872. extern "C" const unsigned char glcdfont[];
  873. void ST7735_t3::drawPixel(int16_t x, int16_t y, uint16_t color)
  874. {
  875. x += _originx;
  876. y += _originy;
  877. if((x < _displayclipx1) ||(x >= _displayclipx2) || (y < _displayclipy1) || (y >= _displayclipy2)) return;
  878. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  879. if (_use_fbtft) {
  880. _pfbtft[y*_width + x] = color;
  881. } else
  882. #endif
  883. {
  884. beginSPITransaction();
  885. setAddr(x,y,x+1,y+1);
  886. writecommand(ST7735_RAMWR);
  887. writedata16_last(color);
  888. endSPITransaction();
  889. }
  890. }
  891. void ST7735_t3::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color)
  892. {
  893. x+=_originx;
  894. y+=_originy;
  895. // Rectangular clipping
  896. if((x < _displayclipx1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
  897. if(y < _displayclipy1) { h = h - (_displayclipy1 - y); y = _displayclipy1;}
  898. if((y+h-1) >= _displayclipy2) h = _displayclipy2-y;
  899. if(h<1) return;
  900. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  901. if (_use_fbtft) {
  902. uint16_t * pfbPixel = &_pfbtft[ y*_width + x];
  903. while (h--) {
  904. *pfbPixel = color;
  905. pfbPixel += _width;
  906. }
  907. } else
  908. #endif
  909. {
  910. beginSPITransaction();
  911. setAddr(x, y, x, y+h-1);
  912. writecommand(ST7735_RAMWR);
  913. while (h-- > 1) {
  914. writedata16(color);
  915. }
  916. writedata16_last(color);
  917. endSPITransaction();
  918. }
  919. }
  920. void ST7735_t3::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color)
  921. {
  922. x+=_originx;
  923. y+=_originy;
  924. // Rectangular clipping
  925. if((y < _displayclipy1) || (x >= _displayclipx2) || (y >= _displayclipy2)) return;
  926. if(x<_displayclipx1) { w = w - (_displayclipx1 - x); x = _displayclipx1; }
  927. if((x+w-1) >= _displayclipx2) w = _displayclipx2-x;
  928. if (w<1) return;
  929. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  930. if (_use_fbtft) {
  931. if ((x&1) || (w&1)) {
  932. uint16_t * pfbPixel = &_pfbtft[ y*_width + x];
  933. while (w--) {
  934. *pfbPixel++ = color;
  935. }
  936. } else {
  937. // X is even and so is w, try 32 bit writes..
  938. uint32_t color32 = (color << 16) | color;
  939. uint32_t * pfbPixel = (uint32_t*)((uint16_t*)&_pfbtft[ y*_width + x]);
  940. while (w) {
  941. *pfbPixel++ = color32;
  942. w -= 2;
  943. }
  944. }
  945. } else
  946. #endif
  947. {
  948. beginSPITransaction();
  949. setAddr(x, y, x+w-1, y);
  950. writecommand(ST7735_RAMWR);
  951. while (w-- > 1) {
  952. writedata16(color);
  953. }
  954. writedata16_last(color);
  955. endSPITransaction();
  956. }
  957. }
  958. void ST7735_t3::fillScreen(uint16_t color)
  959. {
  960. fillRect(0, 0, _width, _height, color);
  961. }
  962. // fill a rectangle
  963. void ST7735_t3::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
  964. {
  965. x+=_originx;
  966. y+=_originy;
  967. // Rectangular clipping (drawChar w/big text requires this)
  968. if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
  969. if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
  970. if(x < _displayclipx1) { w -= (_displayclipx1-x); x = _displayclipx1; }
  971. if(y < _displayclipy1) { h -= (_displayclipy1 - y); y = _displayclipy1; }
  972. if((x + w - 1) >= _displayclipx2) w = _displayclipx2 - x;
  973. if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
  974. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  975. if (_use_fbtft) {
  976. if ((x&1) || (w&1)) {
  977. uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
  978. for (;h>0; h--) {
  979. uint16_t * pfbPixel = pfbPixel_row;
  980. for (int i = 0 ;i < w; i++) {
  981. *pfbPixel++ = color;
  982. }
  983. pfbPixel_row += _width;
  984. }
  985. } else {
  986. // Horizontal is even number so try 32 bit writes instead
  987. uint32_t color32 = (color << 16) | color;
  988. uint32_t * pfbPixel_row = (uint32_t *)((uint16_t*)&_pfbtft[ y*_width + x]);
  989. w = w/2; // only iterate half the times
  990. for (;h>0; h--) {
  991. uint32_t * pfbPixel = pfbPixel_row;
  992. for (int i = 0 ;i < w; i++) {
  993. *pfbPixel++ = color32;
  994. }
  995. pfbPixel_row += (_width/2);
  996. }
  997. }
  998. } else
  999. #endif
  1000. {
  1001. // TODO: this can result in a very long transaction time
  1002. // should break this into multiple transactions, even though
  1003. // it'll cost more overhead, so we don't stall other SPI libs
  1004. beginSPITransaction();
  1005. setAddr(x, y, x+w-1, y+h-1);
  1006. writecommand(ST7735_RAMWR);
  1007. for(y=h; y>0; y--) {
  1008. for(x=w; x>1; x--) {
  1009. writedata16(color);
  1010. }
  1011. writedata16_last(color);
  1012. }
  1013. endSPITransaction();
  1014. }
  1015. }
  1016. #define MADCTL_MY 0x80
  1017. #define MADCTL_MX 0x40
  1018. #define MADCTL_MV 0x20
  1019. #define MADCTL_ML 0x10
  1020. #define MADCTL_RGB 0x00
  1021. #define MADCTL_BGR 0x08
  1022. #define MADCTL_MH 0x04
  1023. void ST7735_t3::setRotation(uint8_t m)
  1024. {
  1025. //Serial.printf("Setting Rotation to %d\n", m);
  1026. beginSPITransaction();
  1027. writecommand(ST7735_MADCTL);
  1028. rotation = m % 4; // can't be higher than 3
  1029. switch (rotation) {
  1030. case 0:
  1031. if ((tabcolor == INITR_BLACKTAB) || (tabcolor == INITR_MINI160x80)) {
  1032. writedata_last(MADCTL_MX | MADCTL_MY | MADCTL_RGB);
  1033. } else {
  1034. writedata_last(MADCTL_MX | MADCTL_MY | MADCTL_BGR);
  1035. }
  1036. _width = _screenWidth;
  1037. _height = _screenHeight;
  1038. _xstart = _colstart;
  1039. _ystart = _rowstart;
  1040. break;
  1041. case 1:
  1042. if ((tabcolor == INITR_BLACKTAB) || (tabcolor == INITR_MINI160x80)) {
  1043. writedata_last(MADCTL_MY | MADCTL_MV | MADCTL_RGB);
  1044. } else {
  1045. writedata_last(MADCTL_MY | MADCTL_MV | MADCTL_BGR);
  1046. }
  1047. _height = _screenWidth;
  1048. _width = _screenHeight;
  1049. _ystart = _colstart;
  1050. _xstart = _rowstart;
  1051. break;
  1052. case 2:
  1053. if ((tabcolor == INITR_BLACKTAB) || (tabcolor == INITR_MINI160x80)) {
  1054. writedata_last(MADCTL_RGB);
  1055. } else {
  1056. writedata_last(MADCTL_BGR);
  1057. }
  1058. _width = _screenWidth;
  1059. _height = _screenHeight;
  1060. _xstart = _colstart;
  1061. // hack to make work on a couple different displays
  1062. _ystart = (_rowstart==0 || _rowstart==32)? 0 : 1;//_rowstart;
  1063. break;
  1064. case 3:
  1065. if ((tabcolor == INITR_BLACKTAB) || (tabcolor == INITR_MINI160x80)) {
  1066. writedata_last(MADCTL_MX | MADCTL_MV | MADCTL_RGB);
  1067. } else {
  1068. writedata_last(MADCTL_MX | MADCTL_MV | MADCTL_BGR);
  1069. }
  1070. _width = _screenHeight;
  1071. _height = _screenWidth;
  1072. _ystart = _colstart;
  1073. // hack to make work on a couple different displays
  1074. _xstart = (_rowstart==0 || _rowstart==32)? 0 : 1;//_rowstart;
  1075. break;
  1076. }
  1077. _rot = rotation; // remember the rotation...
  1078. endSPITransaction();
  1079. //Serial.printf("SetRotation(%d) _xstart=%d _ystart=%d _width=%d, _height=%d\n", _rot, _xstart, _ystart, _width, _height);
  1080. setClipRect();
  1081. setOrigin();
  1082. cursor_x = 0;
  1083. cursor_y = 0;
  1084. }
  1085. void ST7735_t3::setRowColStart(uint16_t x, uint16_t y) {
  1086. _rowstart = x;
  1087. _colstart = y;
  1088. if (_rot != 0xff) setRotation(_rot);
  1089. }
  1090. void ST7735_t3::invertDisplay(boolean i)
  1091. {
  1092. beginSPITransaction();
  1093. writecommand_last(i ? ST7735_INVON : ST7735_INVOFF);
  1094. endSPITransaction();
  1095. }
  1096. /*!
  1097. @brief Adafruit_SPITFT Send Command handles complete sending of commands and const data
  1098. @param commandByte The Command Byte
  1099. @param dataBytes A pointer to the Data bytes to send
  1100. @param numDataBytes The number of bytes we should send
  1101. */
  1102. void ST7735_t3::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
  1103. beginSPITransaction();
  1104. writecommand_last(commandByte); // Send the command byte
  1105. while (numDataBytes > 1) {
  1106. writedata(*dataBytes++); // Send the data bytes
  1107. numDataBytes--;
  1108. }
  1109. if (numDataBytes) writedata_last(*dataBytes);
  1110. endSPITransaction();
  1111. }
  1112. uint16_t ST7735_t3::readPixel(int16_t x, int16_t y)
  1113. {
  1114. uint16_t colors = 0;
  1115. readRect(x, y, 1, 1, &colors);
  1116. return colors;
  1117. }
  1118. // Now lets see if we can read in multiple pixels
  1119. void ST7735_t3::readRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t *pcolors)
  1120. {
  1121. // Use our Origin.
  1122. x+=_originx;
  1123. y+=_originy;
  1124. //BUGBUG:: Should add some validation of X and Y
  1125. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  1126. if (_use_fbtft) {
  1127. uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
  1128. for (;h>0; h--) {
  1129. uint16_t * pfbPixel = pfbPixel_row;
  1130. for (int i = 0 ;i < w; i++) {
  1131. *pcolors++ = *pfbPixel++;
  1132. }
  1133. pfbPixel_row += _width;
  1134. }
  1135. return;
  1136. }
  1137. #endif
  1138. }
  1139. // Now lets see if we can writemultiple pixels
  1140. void ST7735_t3::writeRect(int16_t x, int16_t y, int16_t w, int16_t h, const uint16_t *pcolors)
  1141. {
  1142. x+=_originx;
  1143. y+=_originy;
  1144. uint16_t x_clip_left = 0; // How many entries at start of colors to skip at start of row
  1145. uint16_t x_clip_right = 0; // how many color entries to skip at end of row for clipping
  1146. // Rectangular clipping
  1147. // See if the whole thing out of bounds...
  1148. if((x >= _displayclipx2) || (y >= _displayclipy2)) return;
  1149. if (((x+w) <= _displayclipx1) || ((y+h) <= _displayclipy1)) return;
  1150. // In these cases you can not do simple clipping, as we need to synchronize the colors array with the
  1151. // We can clip the height as when we get to the last visible we don't have to go any farther.
  1152. // also maybe starting y as we will advance the color array.
  1153. if(y < _displayclipy1) {
  1154. int dy = (_displayclipy1 - y);
  1155. h -= dy;
  1156. pcolors += (dy*w); // Advance color array to
  1157. y = _displayclipy1;
  1158. }
  1159. if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
  1160. // For X see how many items in color array to skip at start of row and likewise end of row
  1161. if(x < _displayclipx1) {
  1162. x_clip_left = _displayclipx1-x;
  1163. w -= x_clip_left;
  1164. x = _displayclipx1;
  1165. }
  1166. if((x + w - 1) >= _displayclipx2) {
  1167. x_clip_right = w;
  1168. w = _displayclipx2 - x;
  1169. x_clip_right -= w;
  1170. }
  1171. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  1172. if (_use_fbtft) {
  1173. uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
  1174. for (;h>0; h--) {
  1175. uint16_t * pfbPixel = pfbPixel_row;
  1176. pcolors += x_clip_left;
  1177. for (int i = 0 ;i < w; i++) {
  1178. *pfbPixel++ = *pcolors++;
  1179. }
  1180. pfbPixel_row += _width;
  1181. pcolors += x_clip_right;
  1182. }
  1183. return;
  1184. }
  1185. #endif
  1186. beginSPITransaction();
  1187. setAddr(x, y, x+w-1, y+h-1);
  1188. writecommand(ST7735_RAMWR);
  1189. for(y=h; y>0; y--) {
  1190. pcolors += x_clip_left;
  1191. for(x=w; x>1; x--) {
  1192. writedata16(*pcolors++);
  1193. }
  1194. writedata16_last(*pcolors++);
  1195. pcolors += x_clip_right;
  1196. }
  1197. endSPITransaction();
  1198. }
  1199. ///
  1200. ///
  1201. ///
  1202. // Draw a rectangle
  1203. void ST7735_t3::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color)
  1204. {
  1205. drawFastHLine(x, y, w, color);
  1206. drawFastHLine(x, y+h-1, w, color);
  1207. drawFastVLine(x, y, h, color);
  1208. drawFastVLine(x+w-1, y, h, color);
  1209. }
  1210. // Draw a rounded rectangle
  1211. void ST7735_t3::drawRoundRect(int16_t x, int16_t y, int16_t w,
  1212. int16_t h, int16_t r, uint16_t color) {
  1213. // smarter version
  1214. drawFastHLine(x+r , y , w-2*r, color); // Top
  1215. drawFastHLine(x+r , y+h-1, w-2*r, color); // Bottom
  1216. drawFastVLine(x , y+r , h-2*r, color); // Left
  1217. drawFastVLine(x+w-1, y+r , h-2*r, color); // Right
  1218. // draw four corners
  1219. drawCircleHelper(x+r , y+r , r, 1, color);
  1220. drawCircleHelper(x+w-r-1, y+r , r, 2, color);
  1221. drawCircleHelper(x+w-r-1, y+h-r-1, r, 4, color);
  1222. drawCircleHelper(x+r , y+h-r-1, r, 8, color);
  1223. }
  1224. // Fill a rounded rectangle
  1225. void ST7735_t3::fillRoundRect(int16_t x, int16_t y, int16_t w,
  1226. int16_t h, int16_t r, uint16_t color) {
  1227. // smarter version
  1228. fillRect(x+r, y, w-2*r, h, color);
  1229. // draw four corners
  1230. fillCircleHelper(x+w-r-1, y+r, r, 1, h-2*r-1, color);
  1231. fillCircleHelper(x+r , y+r, r, 2, h-2*r-1, color);
  1232. }
  1233. // Draw a triangle
  1234. void ST7735_t3::drawTriangle(int16_t x0, int16_t y0,
  1235. int16_t x1, int16_t y1,
  1236. int16_t x2, int16_t y2, uint16_t color) {
  1237. drawLine(x0, y0, x1, y1, color);
  1238. drawLine(x1, y1, x2, y2, color);
  1239. drawLine(x2, y2, x0, y0, color);
  1240. }
  1241. // Fill a triangle
  1242. void ST7735_t3::fillTriangle ( int16_t x0, int16_t y0,
  1243. int16_t x1, int16_t y1,
  1244. int16_t x2, int16_t y2, uint16_t color) {
  1245. int16_t a, b, y, last;
  1246. // Sort coordinates by Y order (y2 >= y1 >= y0)
  1247. if (y0 > y1) {
  1248. st7735_swap(y0, y1); st7735_swap(x0, x1);
  1249. }
  1250. if (y1 > y2) {
  1251. st7735_swap(y2, y1); st7735_swap(x2, x1);
  1252. }
  1253. if (y0 > y1) {
  1254. st7735_swap(y0, y1); st7735_swap(x0, x1);
  1255. }
  1256. if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
  1257. a = b = x0;
  1258. if(x1 < a) a = x1;
  1259. else if(x1 > b) b = x1;
  1260. if(x2 < a) a = x2;
  1261. else if(x2 > b) b = x2;
  1262. drawFastHLine(a, y0, b-a+1, color);
  1263. return;
  1264. }
  1265. int32_t
  1266. dx01 = x1 - x0,
  1267. dy01 = y1 - y0,
  1268. dx02 = x2 - x0,
  1269. dy02 = y2 - y0,
  1270. dx12 = x2 - x1,
  1271. dy12 = y2 - y1,
  1272. sa = 0,
  1273. sb = 0;
  1274. // For upper part of triangle, find scanline crossings for segments
  1275. // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
  1276. // is included here (and second loop will be skipped, avoiding a /0
  1277. // error there), otherwise scanline y1 is skipped here and handled
  1278. // in the second loop...which also avoids a /0 error here if y0=y1
  1279. // (flat-topped triangle).
  1280. if(y1 == y2) last = y1; // Include y1 scanline
  1281. else last = y1-1; // Skip it
  1282. for(y=y0; y<=last; y++) {
  1283. a = x0 + sa / dy01;
  1284. b = x0 + sb / dy02;
  1285. sa += dx01;
  1286. sb += dx02;
  1287. /* longhand:
  1288. a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
  1289. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  1290. */
  1291. if(a > b) st7735_swap(a,b);
  1292. drawFastHLine(a, y, b-a+1, color);
  1293. }
  1294. // For lower part of triangle, find scanline crossings for segments
  1295. // 0-2 and 1-2. This loop is skipped if y1=y2.
  1296. sa = dx12 * (y - y1);
  1297. sb = dx02 * (y - y0);
  1298. for(; y<=y2; y++) {
  1299. a = x1 + sa / dy12;
  1300. b = x0 + sb / dy02;
  1301. sa += dx12;
  1302. sb += dx02;
  1303. /* longhand:
  1304. a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
  1305. b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
  1306. */
  1307. if(a > b) st7735_swap(a,b);
  1308. drawFastHLine(a, y, b-a+1, color);
  1309. }
  1310. }
  1311. // Draw a circle outline
  1312. void ST7735_t3::drawCircle(int16_t x0, int16_t y0, int16_t r,
  1313. uint16_t color) {
  1314. int16_t f = 1 - r;
  1315. int16_t ddF_x = 1;
  1316. int16_t ddF_y = -2 * r;
  1317. int16_t x = 0;
  1318. int16_t y = r;
  1319. drawPixel(x0 , y0+r, color);
  1320. drawPixel(x0 , y0-r, color);
  1321. drawPixel(x0+r, y0 , color);
  1322. drawPixel(x0-r, y0 , color);
  1323. while (x<y) {
  1324. if (f >= 0) {
  1325. y--;
  1326. ddF_y += 2;
  1327. f += ddF_y;
  1328. }
  1329. x++;
  1330. ddF_x += 2;
  1331. f += ddF_x;
  1332. drawPixel(x0 + x, y0 + y, color);
  1333. drawPixel(x0 - x, y0 + y, color);
  1334. drawPixel(x0 + x, y0 - y, color);
  1335. drawPixel(x0 - x, y0 - y, color);
  1336. drawPixel(x0 + y, y0 + x, color);
  1337. drawPixel(x0 - y, y0 + x, color);
  1338. drawPixel(x0 + y, y0 - x, color);
  1339. drawPixel(x0 - y, y0 - x, color);
  1340. }
  1341. }
  1342. void ST7735_t3::drawCircleHelper( int16_t x0, int16_t y0,
  1343. int16_t r, uint8_t cornername, uint16_t color) {
  1344. int16_t f = 1 - r;
  1345. int16_t ddF_x = 1;
  1346. int16_t ddF_y = -2 * r;
  1347. int16_t x = 0;
  1348. int16_t y = r;
  1349. while (x<y) {
  1350. if (f >= 0) {
  1351. y--;
  1352. ddF_y += 2;
  1353. f += ddF_y;
  1354. }
  1355. x++;
  1356. ddF_x += 2;
  1357. f += ddF_x;
  1358. if (cornername & 0x4) {
  1359. drawPixel(x0 + x, y0 + y, color);
  1360. drawPixel(x0 + y, y0 + x, color);
  1361. }
  1362. if (cornername & 0x2) {
  1363. drawPixel(x0 + x, y0 - y, color);
  1364. drawPixel(x0 + y, y0 - x, color);
  1365. }
  1366. if (cornername & 0x8) {
  1367. drawPixel(x0 - y, y0 + x, color);
  1368. drawPixel(x0 - x, y0 + y, color);
  1369. }
  1370. if (cornername & 0x1) {
  1371. drawPixel(x0 - y, y0 - x, color);
  1372. drawPixel(x0 - x, y0 - y, color);
  1373. }
  1374. }
  1375. }
  1376. void ST7735_t3::fillCircle(int16_t x0, int16_t y0, int16_t r,
  1377. uint16_t color) {
  1378. drawFastVLine(x0, y0-r, 2*r+1, color);
  1379. fillCircleHelper(x0, y0, r, 3, 0, color);
  1380. }
  1381. // Used to do circles and roundrects
  1382. void ST7735_t3::fillCircleHelper(int16_t x0, int16_t y0, int16_t r,
  1383. uint8_t cornername, int16_t delta, uint16_t color) {
  1384. int16_t f = 1 - r;
  1385. int16_t ddF_x = 1;
  1386. int16_t ddF_y = -2 * r;
  1387. int16_t x = 0;
  1388. int16_t y = r;
  1389. while (x<y) {
  1390. if (f >= 0) {
  1391. y--;
  1392. ddF_y += 2;
  1393. f += ddF_y;
  1394. }
  1395. x++;
  1396. ddF_x += 2;
  1397. f += ddF_x;
  1398. if (cornername & 0x1) {
  1399. drawFastVLine(x0+x, y0-y, 2*y+1+delta, color);
  1400. drawFastVLine(x0+y, y0-x, 2*x+1+delta, color);
  1401. }
  1402. if (cornername & 0x2) {
  1403. drawFastVLine(x0-x, y0-y, 2*y+1+delta, color);
  1404. drawFastVLine(x0-y, y0-x, 2*x+1+delta, color);
  1405. }
  1406. }
  1407. }
  1408. // Bresenham's algorithm - thx wikpedia
  1409. void ST7735_t3::drawLine(int16_t x0, int16_t y0,
  1410. int16_t x1, int16_t y1, uint16_t color)
  1411. {
  1412. if (y0 == y1) {
  1413. if (x1 > x0) {
  1414. drawFastHLine(x0, y0, x1 - x0 + 1, color);
  1415. } else if (x1 < x0) {
  1416. drawFastHLine(x1, y0, x0 - x1 + 1, color);
  1417. } else {
  1418. drawPixel(x0, y0, color);
  1419. }
  1420. return;
  1421. } else if (x0 == x1) {
  1422. if (y1 > y0) {
  1423. drawFastVLine(x0, y0, y1 - y0 + 1, color);
  1424. } else {
  1425. drawFastVLine(x0, y1, y0 - y1 + 1, color);
  1426. }
  1427. return;
  1428. }
  1429. bool steep = abs(y1 - y0) > abs(x1 - x0);
  1430. if (steep) {
  1431. st7735_swap(x0, y0);
  1432. st7735_swap(x1, y1);
  1433. }
  1434. if (x0 > x1) {
  1435. st7735_swap(x0, x1);
  1436. st7735_swap(y0, y1);
  1437. }
  1438. int16_t dx, dy;
  1439. dx = x1 - x0;
  1440. dy = abs(y1 - y0);
  1441. int16_t err = dx / 2;
  1442. int16_t ystep;
  1443. if (y0 < y1) {
  1444. ystep = 1;
  1445. } else {
  1446. ystep = -1;
  1447. }
  1448. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  1449. if (!_use_fbtft) beginSPITransaction();
  1450. #else
  1451. beginSPITransaction();
  1452. #endif
  1453. int16_t xbegin = x0;
  1454. if (steep) {
  1455. for (; x0<=x1; x0++) {
  1456. err -= dy;
  1457. if (err < 0) {
  1458. int16_t len = x0 - xbegin;
  1459. if (len) {
  1460. VLine(y0, xbegin, len + 1, color);
  1461. } else {
  1462. Pixel(y0, x0, color);
  1463. }
  1464. xbegin = x0 + 1;
  1465. y0 += ystep;
  1466. err += dx;
  1467. }
  1468. }
  1469. if (x0 > xbegin + 1) {
  1470. VLine(y0, xbegin, x0 - xbegin, color);
  1471. }
  1472. } else {
  1473. for (; x0<=x1; x0++) {
  1474. err -= dy;
  1475. if (err < 0) {
  1476. int16_t len = x0 - xbegin;
  1477. if (len) {
  1478. HLine(xbegin, y0, len + 1, color);
  1479. } else {
  1480. Pixel(x0, y0, color);
  1481. }
  1482. xbegin = x0 + 1;
  1483. y0 += ystep;
  1484. err += dx;
  1485. }
  1486. }
  1487. if (x0 > xbegin + 1) {
  1488. HLine(xbegin, y0, x0 - xbegin, color);
  1489. }
  1490. }
  1491. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  1492. if (!_use_fbtft) {
  1493. writecommand_last(ST7735_NOP);
  1494. endSPITransaction();
  1495. }
  1496. #else
  1497. writecommand_last(ST7735_NOP);
  1498. endSPITransaction();
  1499. #endif
  1500. }
  1501. void ST7735_t3::drawBitmap(int16_t x, int16_t y,
  1502. const uint8_t *bitmap, int16_t w, int16_t h,
  1503. uint16_t color) {
  1504. int16_t i, j, byteWidth = (w + 7) / 8;
  1505. for(j=0; j<h; j++) {
  1506. for(i=0; i<w; i++ ) {
  1507. if(pgm_read_byte(bitmap + j * byteWidth + i / 8) & (128 >> (i & 7))) {
  1508. drawPixel(x+i, y+j, color);
  1509. }
  1510. }
  1511. }
  1512. }
  1513. void ST7735_t3::setCursor(int16_t x, int16_t y, bool autoCenter) {
  1514. _center_x_text = autoCenter; // remember the state.
  1515. _center_y_text = autoCenter; // remember the state.
  1516. if (x == ST7735_t3::CENTER) {
  1517. _center_x_text = true;
  1518. x = _width/2;
  1519. }
  1520. if (y == ST7735_t3::CENTER) {
  1521. _center_y_text = true;
  1522. y = _height/2;
  1523. }
  1524. if (x < 0) x = 0;
  1525. else if (x >= _width) x = _width - 1;
  1526. cursor_x = x;
  1527. if (y < 0) y = 0;
  1528. else if (y >= _height) y = _height - 1;
  1529. cursor_y = y;
  1530. if(x>=scroll_x && x<=(scroll_x+scroll_width) && y>=scroll_y && y<=(scroll_y+scroll_height)){
  1531. isWritingScrollArea = true;
  1532. } else {
  1533. isWritingScrollArea = false;
  1534. }
  1535. _gfx_last_char_x_write = 0; // Don't use cached data here
  1536. }
  1537. void ST7735_t3::getCursor(int16_t *x, int16_t *y) {
  1538. *x = cursor_x;
  1539. *y = cursor_y;
  1540. }
  1541. void ST7735_t3::setTextSize(uint8_t s_x, uint8_t s_y) {
  1542. textsize_x = (s_x > 0) ? s_x : 1;
  1543. textsize_y = (s_y > 0) ? s_y : 1;
  1544. }
  1545. uint8_t ST7735_t3::getTextSize() {
  1546. return textsize_x; // bug bug 2 values now
  1547. }
  1548. uint8_t ST7735_t3::getTextSizeX() {
  1549. return textsize_x;
  1550. }
  1551. uint8_t ST7735_t3::getTextSizeY() {
  1552. return textsize_y;
  1553. }
  1554. void ST7735_t3::setTextColor(uint16_t c) {
  1555. // For 'transparent' background, we'll set the bg
  1556. // to the same as fg instead of using a flag
  1557. textcolor = textbgcolor = c;
  1558. }
  1559. void ST7735_t3::setTextColor(uint16_t c, uint16_t b) {
  1560. textcolor = c;
  1561. textbgcolor = b;
  1562. // pre-expand colors for fast alpha-blending later
  1563. textcolorPrexpanded = (textcolor | (textcolor << 16)) & 0b00000111111000001111100000011111;
  1564. textbgcolorPrexpanded = (textbgcolor | (textbgcolor << 16)) & 0b00000111111000001111100000011111;
  1565. }
  1566. void ST7735_t3::setTextWrap(boolean w) {
  1567. wrap = w;
  1568. }
  1569. boolean ST7735_t3::getTextWrap()
  1570. {
  1571. return wrap;
  1572. }
  1573. uint8_t ST7735_t3::getRotation(void) {
  1574. return _rot;
  1575. }
  1576. /***************************************************************************************
  1577. ** Function name: setTextDatum
  1578. ** Description: Set the text position reference datum
  1579. ***************************************************************************************/
  1580. void ST7735_t3::setTextDatum(uint8_t d)
  1581. {
  1582. textdatum = d;
  1583. }
  1584. /***************************************************************************************
  1585. ** Function name: drawNumber
  1586. ** Description: draw a long integer
  1587. ***************************************************************************************/
  1588. int16_t ST7735_t3::drawNumber(long long_num, int poX, int poY)
  1589. {
  1590. char str[14];
  1591. ltoa(long_num, str, 10);
  1592. return drawString(str, poX, poY);
  1593. }
  1594. int16_t ST7735_t3::drawFloat(float floatNumber, int dp, int poX, int poY)
  1595. {
  1596. char str[14]; // Array to contain decimal string
  1597. uint8_t ptr = 0; // Initialise pointer for array
  1598. int8_t digits = 1; // Count the digits to avoid array overflow
  1599. float rounding = 0.5; // Round up down delta
  1600. if (dp > 7) dp = 7; // Limit the size of decimal portion
  1601. // Adjust the rounding value
  1602. for (uint8_t i = 0; i < dp; ++i) rounding /= 10.0f;
  1603. if (floatNumber < -rounding) // add sign, avoid adding - sign to 0.0!
  1604. {
  1605. str[ptr++] = '-'; // Negative number
  1606. str[ptr] = 0; // Put a null in the array as a precaution
  1607. digits = 0; // Set digits to 0 to compensate so pointer value can be used later
  1608. floatNumber = -floatNumber; // Make positive
  1609. }
  1610. floatNumber += rounding; // Round up or down
  1611. // For error put ... in string and return (all TFT_ILI9341_ESP library fonts contain . character)
  1612. if (floatNumber >= 2147483647) {
  1613. strcpy(str, "...");
  1614. //return drawString(str, poX, poY);
  1615. }
  1616. // No chance of overflow from here on
  1617. // Get integer part
  1618. unsigned long temp = (unsigned long)floatNumber;
  1619. // Put integer part into array
  1620. ltoa(temp, str + ptr, 10);
  1621. // Find out where the null is to get the digit count loaded
  1622. while ((uint8_t)str[ptr] != 0) ptr++; // Move the pointer along
  1623. digits += ptr; // Count the digits
  1624. str[ptr++] = '.'; // Add decimal point
  1625. str[ptr] = '0'; // Add a dummy zero
  1626. str[ptr + 1] = 0; // Add a null but don't increment pointer so it can be overwritten
  1627. // Get the decimal portion
  1628. floatNumber = floatNumber - temp;
  1629. // Get decimal digits one by one and put in array
  1630. // Limit digit count so we don't get a false sense of resolution
  1631. uint8_t i = 0;
  1632. while ((i < dp) && (digits < 9)) // while (i < dp) for no limit but array size must be increased
  1633. {
  1634. i++;
  1635. floatNumber *= 10; // for the next decimal
  1636. temp = floatNumber; // get the decimal
  1637. ltoa(temp, str + ptr, 10);
  1638. ptr++; digits++; // Increment pointer and digits count
  1639. floatNumber -= temp; // Remove that digit
  1640. }
  1641. // Finally we can plot the string and return pixel length
  1642. return drawString(str, poX, poY);
  1643. }
  1644. /***************************************************************************************
  1645. ** Function name: drawString (with or without user defined font)
  1646. ** Description : draw string with padding if it is defined
  1647. ***************************************************************************************/
  1648. // Without font number, uses font set by setTextFont()
  1649. int16_t ST7735_t3::drawString(const String& string, int poX, int poY)
  1650. {
  1651. int16_t len = string.length() + 2;
  1652. char buffer[len];
  1653. string.toCharArray(buffer, len);
  1654. return drawString1(buffer, len, poX, poY);
  1655. }
  1656. int16_t ST7735_t3::drawString1(char string[], int16_t len, int poX, int poY)
  1657. {
  1658. int16_t sumX = 0;
  1659. uint8_t padding = 1/*, baseline = 0*/;
  1660. uint16_t cwidth = strPixelLen(string); // Find the pixel width of the string in the font
  1661. uint16_t cheight = textsize_y*8;
  1662. if (textdatum || padX)
  1663. {
  1664. switch(textdatum) {
  1665. case TC_DATUM:
  1666. poX -= cwidth/2;
  1667. padding += 1;
  1668. break;
  1669. case TR_DATUM:
  1670. poX -= cwidth;
  1671. padding += 2;
  1672. break;
  1673. case ML_DATUM:
  1674. poY -= cheight/2;
  1675. //padding += 0;
  1676. break;
  1677. case MC_DATUM:
  1678. poX -= cwidth/2;
  1679. poY -= cheight/2;
  1680. padding += 1;
  1681. break;
  1682. case MR_DATUM:
  1683. poX -= cwidth;
  1684. poY -= cheight/2;
  1685. padding += 2;
  1686. break;
  1687. case BL_DATUM:
  1688. poY -= cheight;
  1689. //padding += 0;
  1690. break;
  1691. case BC_DATUM:
  1692. poX -= cwidth/2;
  1693. poY -= cheight;
  1694. padding += 1;
  1695. break;
  1696. case BR_DATUM:
  1697. poX -= cwidth;
  1698. poY -= cheight;
  1699. padding += 2;
  1700. break;
  1701. /*
  1702. case L_BASELINE:
  1703. poY -= baseline;
  1704. //padding += 0;
  1705. break;
  1706. case C_BASELINE:
  1707. poX -= cwidth/2;
  1708. poY -= baseline;
  1709. //padding += 1;
  1710. break;
  1711. case R_BASELINE:
  1712. poX -= cwidth;
  1713. poY -= baseline;
  1714. padding += 2;
  1715. break;
  1716. */
  1717. }
  1718. // Check coordinates are OK, adjust if not
  1719. if (poX < 0) poX = 0;
  1720. if (poX+cwidth > width()) poX = width() - cwidth;
  1721. if (poY < 0) poY = 0;
  1722. //if (poY+cheight-baseline >_height) poY = _height - cheight;
  1723. }
  1724. if(font == NULL){
  1725. for(uint8_t i = 0; i < len-2; i++){
  1726. drawChar((int16_t) (poX+sumX), (int16_t) poY, string[i], textcolor, textbgcolor, textsize_x, textsize_y);
  1727. sumX += cwidth/(len-2) + padding;
  1728. }
  1729. } else {
  1730. setCursor(poX, poY);
  1731. for(uint8_t i = 0; i < len-2; i++){
  1732. drawFontChar(string[i]);
  1733. setCursor(cursor_x, cursor_y);
  1734. }
  1735. }
  1736. return sumX;
  1737. }
  1738. void ST7735_t3::scrollTextArea(uint8_t scrollSize){
  1739. uint16_t awColors[scroll_width];
  1740. for (int y=scroll_y+scrollSize; y < (scroll_y+scroll_height); y++) {
  1741. readRect(scroll_x, y, scroll_width, 1, awColors);
  1742. writeRect(scroll_x, y-scrollSize, scroll_width, 1, awColors);
  1743. }
  1744. fillRect(scroll_x, (scroll_y+scroll_height)-scrollSize, scroll_width, scrollSize, scrollbgcolor);
  1745. }
  1746. void ST7735_t3::setScrollTextArea(int16_t x, int16_t y, int16_t w, int16_t h){
  1747. scroll_x = x;
  1748. scroll_y = y;
  1749. scroll_width = w;
  1750. scroll_height = h;
  1751. }
  1752. void ST7735_t3::setScrollBackgroundColor(uint16_t color){
  1753. scrollbgcolor=color;
  1754. fillRect(scroll_x,scroll_y,scroll_width,scroll_height,scrollbgcolor);
  1755. }
  1756. void ST7735_t3::enableScroll(void){
  1757. scrollEnable = true;
  1758. }
  1759. void ST7735_t3::disableScroll(void){
  1760. scrollEnable = false;
  1761. }
  1762. void ST7735_t3::resetScrollBackgroundColor(uint16_t color){
  1763. scrollbgcolor=color;
  1764. }
  1765. // overwrite functions from class Print:
  1766. size_t ST7735_t3::write(uint8_t c) {
  1767. return write(&c, 1);
  1768. }
  1769. size_t ST7735_t3::write(const uint8_t *buffer, size_t size)
  1770. {
  1771. // Lets try to handle some of the special font centering code that was done for default fonts.
  1772. if (_center_x_text || _center_y_text ) {
  1773. int16_t x, y;
  1774. uint16_t strngWidth, strngHeight;
  1775. getTextBounds(buffer, size, 0, 0, &x, &y, &strngWidth, &strngHeight);
  1776. //Serial.printf("_fontwrite bounds: %d %d %u %u\n", x, y, strngWidth, strngHeight);
  1777. // Note we may want to play with the x ane y returned if they offset some
  1778. if (_center_x_text && strngWidth > 0){//Avoid operations for strngWidth = 0
  1779. cursor_x -= ((x + strngWidth) / 2);
  1780. }
  1781. if (_center_y_text && strngHeight > 0){//Avoid operations for strngWidth = 0
  1782. cursor_y -= ((y + strngHeight) / 2);
  1783. }
  1784. _center_x_text = false;
  1785. _center_y_text = false;
  1786. }
  1787. size_t cb = size;
  1788. while (cb) {
  1789. uint8_t c = *buffer++;
  1790. cb--;
  1791. if (font) {
  1792. if (c == '\n') {
  1793. cursor_y += font->line_space;
  1794. if(scrollEnable && isWritingScrollArea){
  1795. cursor_x = scroll_x;
  1796. }else{
  1797. cursor_x = 0;
  1798. }
  1799. } else {
  1800. drawFontChar(c);
  1801. }
  1802. } else if (gfxFont) {
  1803. if (c == '\n') {
  1804. cursor_y += (int16_t)textsize_y * gfxFont->yAdvance;
  1805. if(scrollEnable && isWritingScrollArea){
  1806. cursor_x = scroll_x;
  1807. }else{
  1808. cursor_x = 0;
  1809. }
  1810. } else {
  1811. drawGFXFontChar(c);
  1812. }
  1813. } else {
  1814. if (c == '\n') {
  1815. cursor_y += textsize_y*8;
  1816. if(scrollEnable && isWritingScrollArea){
  1817. cursor_x = scroll_x;
  1818. }else{
  1819. cursor_x = 0;
  1820. }
  1821. } else if (c == '\r') {
  1822. // skip em
  1823. } else {
  1824. if(scrollEnable && isWritingScrollArea && (cursor_y > (scroll_y+scroll_height - textsize_y*8))){
  1825. scrollTextArea(textsize_y*8);
  1826. cursor_y -= textsize_y*8;
  1827. cursor_x = scroll_x;
  1828. }
  1829. drawChar(cursor_x, cursor_y, c, textcolor, textbgcolor, textsize_x, textsize_y);
  1830. cursor_x += textsize_x*6;
  1831. if(wrap && scrollEnable && isWritingScrollArea && (cursor_x > (scroll_x+scroll_width - textsize_x*6))){
  1832. cursor_y += textsize_y*8;
  1833. cursor_x = scroll_x;
  1834. }
  1835. else if (wrap && (cursor_x > (_width - textsize_x*6))) {
  1836. cursor_y += textsize_y*8;
  1837. cursor_x = 0;
  1838. }
  1839. }
  1840. }
  1841. }
  1842. return size;
  1843. }
  1844. // Draw a character
  1845. void ST7735_t3::drawChar(int16_t x, int16_t y, unsigned char c,
  1846. uint16_t fgcolor, uint16_t bgcolor, uint8_t size_x, uint8_t size_y)
  1847. {
  1848. if((x >= _width) || // Clip right
  1849. (y >= _height) || // Clip bottom
  1850. ((x + 6 * size_x - 1) < 0) || // Clip left TODO: is this correct?
  1851. ((y + 8 * size_y - 1) < 0)) // Clip top TODO: is this correct?
  1852. return;
  1853. if (fgcolor == bgcolor) {
  1854. // This transparent approach is only about 20% faster
  1855. if ((size_x == 1) && (size_y == 1)) {
  1856. uint8_t mask = 0x01;
  1857. int16_t xoff, yoff;
  1858. for (yoff=0; yoff < 8; yoff++) {
  1859. uint8_t line = 0;
  1860. for (xoff=0; xoff < 5; xoff++) {
  1861. if (glcdfont[c * 5 + xoff] & mask) line |= 1;
  1862. line <<= 1;
  1863. }
  1864. line >>= 1;
  1865. xoff = 0;
  1866. while (line) {
  1867. if (line == 0x1F) {
  1868. drawFastHLine(x + xoff, y + yoff, 5, fgcolor);
  1869. break;
  1870. } else if (line == 0x1E) {
  1871. drawFastHLine(x + xoff, y + yoff, 4, fgcolor);
  1872. break;
  1873. } else if ((line & 0x1C) == 0x1C) {
  1874. drawFastHLine(x + xoff, y + yoff, 3, fgcolor);
  1875. line <<= 4;
  1876. xoff += 4;
  1877. } else if ((line & 0x18) == 0x18) {
  1878. drawFastHLine(x + xoff, y + yoff, 2, fgcolor);
  1879. line <<= 3;
  1880. xoff += 3;
  1881. } else if ((line & 0x10) == 0x10) {
  1882. drawPixel(x + xoff, y + yoff, fgcolor);
  1883. line <<= 2;
  1884. xoff += 2;
  1885. } else {
  1886. line <<= 1;
  1887. xoff += 1;
  1888. }
  1889. }
  1890. mask = mask << 1;
  1891. }
  1892. } else {
  1893. uint8_t mask = 0x01;
  1894. int16_t xoff, yoff;
  1895. for (yoff=0; yoff < 8; yoff++) {
  1896. uint8_t line = 0;
  1897. for (xoff=0; xoff < 5; xoff++) {
  1898. if (glcdfont[c * 5 + xoff] & mask) line |= 1;
  1899. line <<= 1;
  1900. }
  1901. line >>= 1;
  1902. xoff = 0;
  1903. while (line) {
  1904. if (line == 0x1F) {
  1905. fillRect(x + xoff * size_x, y + yoff * size_y,
  1906. 5 * size_x, size_y, fgcolor);
  1907. break;
  1908. } else if (line == 0x1E) {
  1909. fillRect(x + xoff * size_x, y + yoff * size_y,
  1910. 4 * size_x, size_y, fgcolor);
  1911. break;
  1912. } else if ((line & 0x1C) == 0x1C) {
  1913. fillRect(x + xoff * size_x, y + yoff * size_y,
  1914. 3 * size_x, size_y, fgcolor);
  1915. line <<= 4;
  1916. xoff += 4;
  1917. } else if ((line & 0x18) == 0x18) {
  1918. fillRect(x + xoff * size_x, y + yoff * size_y,
  1919. 2 * size_x, size_y, fgcolor);
  1920. line <<= 3;
  1921. xoff += 3;
  1922. } else if ((line & 0x10) == 0x10) {
  1923. fillRect(x + xoff * size_x, y + yoff * size_y,
  1924. size_x, size_y, fgcolor);
  1925. line <<= 2;
  1926. xoff += 2;
  1927. } else {
  1928. line <<= 1;
  1929. xoff += 1;
  1930. }
  1931. }
  1932. mask = mask << 1;
  1933. }
  1934. }
  1935. } else {
  1936. // This solid background approach is about 5 time faster
  1937. uint8_t xc, yc;
  1938. uint8_t xr, yr;
  1939. uint8_t mask = 0x01;
  1940. uint16_t color;
  1941. // We need to offset by the origin.
  1942. x+=_originx;
  1943. y+=_originy;
  1944. int16_t x_char_start = x; // remember our X where we start outputting...
  1945. if((x >= _displayclipx2) || // Clip right
  1946. (y >= _displayclipy2) || // Clip bottom
  1947. ((x + 6 * size_x - 1) < _displayclipx1) || // Clip left TODO: this is not correct
  1948. ((y + 8 * size_y - 1) < _displayclipy1)) // Clip top TODO: this is not correct
  1949. return;
  1950. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  1951. if (_use_fbtft) {
  1952. uint16_t * pfbPixel_row = &_pfbtft[ y*_width + x];
  1953. for (yc=0; (yc < 8) && (y < _displayclipy2); yc++) {
  1954. for (yr=0; (yr < size_y) && (y < _displayclipy2); yr++) {
  1955. x = x_char_start; // get our first x position...
  1956. if (y >= _displayclipy1) {
  1957. uint16_t * pfbPixel = pfbPixel_row;
  1958. for (xc=0; xc < 5; xc++) {
  1959. if (glcdfont[c * 5 + xc] & mask) {
  1960. color = fgcolor;
  1961. } else {
  1962. color = bgcolor;
  1963. }
  1964. for (xr=0; xr < size_x; xr++) {
  1965. if ((x >= _displayclipx1) && (x < _displayclipx2)) {
  1966. *pfbPixel = color;
  1967. }
  1968. pfbPixel++;
  1969. x++;
  1970. }
  1971. }
  1972. for (xr=0; xr < size_x; xr++) {
  1973. if ((x >= _displayclipx1) && (x < _displayclipx2)) {
  1974. *pfbPixel = bgcolor;
  1975. }
  1976. pfbPixel++;
  1977. x++;
  1978. }
  1979. }
  1980. pfbPixel_row += _width; // setup pointer to
  1981. y++;
  1982. }
  1983. mask = mask << 1;
  1984. }
  1985. } else
  1986. #endif
  1987. {
  1988. // need to build actual pixel rectangle we will output into.
  1989. int16_t y_char_top = y; // remember the y
  1990. int16_t w = 6 * size_x;
  1991. int16_t h = 8 * size_y;
  1992. if(x < _displayclipx1) { w -= (_displayclipx1-x); x = _displayclipx1; }
  1993. if((x + w - 1) >= _displayclipx2) w = _displayclipx2 - x;
  1994. if(y < _displayclipy1) { h -= (_displayclipy1 - y); y = _displayclipy1; }
  1995. if((y + h - 1) >= _displayclipy2) h = _displayclipy2 - y;
  1996. beginSPITransaction();
  1997. setAddr(x, y, x + w -1, y + h - 1);
  1998. y = y_char_top; // restore the actual y.
  1999. writecommand(ST7735_RAMWR);
  2000. for (yc=0; (yc < 8) && (y < _displayclipy2); yc++) {
  2001. for (yr=0; (yr < size_y) && (y < _displayclipy2); yr++) {
  2002. x = x_char_start; // get our first x position...
  2003. if (y >= _displayclipy1) {
  2004. for (xc=0; xc < 5; xc++) {
  2005. if (glcdfont[c * 5 + xc] & mask) {
  2006. color = fgcolor;
  2007. } else {
  2008. color = bgcolor;
  2009. }
  2010. for (xr=0; xr < size_x; xr++) {
  2011. if ((x >= _displayclipx1) && (x < _displayclipx2)) {
  2012. writedata16(color);
  2013. }
  2014. x++;
  2015. }
  2016. }
  2017. for (xr=0; xr < size_x; xr++) {
  2018. if ((x >= _displayclipx1) && (x < _displayclipx2)) {
  2019. writedata16(bgcolor);
  2020. }
  2021. x++;
  2022. }
  2023. }
  2024. y++;
  2025. }
  2026. mask = mask << 1;
  2027. }
  2028. writecommand_last(ST7735_NOP);
  2029. endSPITransaction();
  2030. }
  2031. }
  2032. }
  2033. void ST7735_t3::setFont(const ILI9341_t3_font_t &f) {
  2034. font = &f;
  2035. _gfx_last_char_x_write = 0; // Don't use cached data here
  2036. if (gfxFont) {
  2037. cursor_y -= 6;
  2038. gfxFont = NULL;
  2039. }
  2040. fontbpp = 1;
  2041. // Calculate additional metrics for Anti-Aliased font support (BDF extn v2.3)
  2042. if (font && font->version==23){
  2043. fontbpp = (font->reserved & 0b000011)+1;
  2044. fontbppindex = (fontbpp >> 2)+1;
  2045. fontbppmask = (1 << (fontbppindex+1))-1;
  2046. fontppb = 8/fontbpp;
  2047. fontalphamx = 31/((1<<fontbpp)-1);
  2048. // Ensure text and bg color are different. Note: use setTextColor to set actual bg color
  2049. if (textcolor == textbgcolor) textbgcolor = (textcolor==0x0000)?0xFFFF:0x0000;
  2050. }
  2051. }
  2052. // Maybe support GFX Fonts as well?
  2053. void ST7735_t3::setFont(const GFXfont *f) {
  2054. font = NULL; // turn off the other font...
  2055. _gfx_last_char_x_write = 0; // Don't use cached data here
  2056. if (f == gfxFont) return; // same font or lack of so can bail.
  2057. if(f) { // Font struct pointer passed in?
  2058. if(!gfxFont) { // And no current font struct?
  2059. // Switching from classic to new font behavior.
  2060. // Move cursor pos down 6 pixels so it's on baseline.
  2061. cursor_y += 6;
  2062. }
  2063. // Test wondering high and low of Ys here...
  2064. int8_t miny_offset = 0;
  2065. #if 1
  2066. for (uint8_t i=0; i <= (f->last - f->first); i++) {
  2067. if (f->glyph[i].yOffset < miny_offset) {
  2068. miny_offset = f->glyph[i].yOffset;
  2069. }
  2070. }
  2071. #else
  2072. int max_delta = 0;
  2073. uint8_t index_min = 0;
  2074. uint8_t index_max = 0;
  2075. int8_t minx_offset = 127;
  2076. int8_t maxx_overlap = 0;
  2077. uint8_t indexx_min = 0;
  2078. uint8_t indexx_max = 0;
  2079. for (uint8_t i=0; i <= (f->last - f->first); i++) {
  2080. if (f->glyph[i].yOffset < miny_offset) {
  2081. miny_offset = f->glyph[i].yOffset;
  2082. index_min = i;
  2083. }
  2084. if (f->glyph[i].xOffset < minx_offset) {
  2085. minx_offset = f->glyph[i].xOffset;
  2086. indexx_min = i;
  2087. }
  2088. if ( (f->glyph[i].yOffset + f->glyph[i].height) > max_delta) {
  2089. max_delta = (f->glyph[i].yOffset + f->glyph[i].height);
  2090. index_max = i;
  2091. }
  2092. int8_t x_overlap = f->glyph[i].xOffset + f->glyph[i].width - f->glyph[i].xAdvance;
  2093. if (x_overlap > maxx_overlap) {
  2094. maxx_overlap = x_overlap;
  2095. indexx_max = i;
  2096. }
  2097. }
  2098. Serial.printf("Set GFX Font(%x): Y: %d %d(%c) %d(%c) X: %d(%c) %d(%c)\n", (uint32_t)f, f->yAdvance,
  2099. miny_offset, index_min + f->first, max_delta, index_max + f->first,
  2100. minx_offset, indexx_min + f->first, maxx_overlap, indexx_max + f->first);
  2101. #endif
  2102. _gfxFont_min_yOffset = miny_offset; // Probably only thing we need... May cache?
  2103. } else if(gfxFont) { // NULL passed. Current font struct defined?
  2104. // Switching from new to classic font behavior.
  2105. // Move cursor pos up 6 pixels so it's at top-left of char.
  2106. cursor_y -= 6;
  2107. }
  2108. gfxFont = f;
  2109. }
  2110. static uint32_t fetchbit(const uint8_t *p, uint32_t index)
  2111. {
  2112. if (p[index >> 3] & (1 << (7 - (index & 7)))) return 1;
  2113. return 0;
  2114. }
  2115. static uint32_t fetchbits_unsigned(const uint8_t *p, uint32_t index, uint32_t required)
  2116. {
  2117. uint32_t val = 0;
  2118. do {
  2119. uint8_t b = p[index >> 3];
  2120. uint32_t avail = 8 - (index & 7);
  2121. if (avail <= required) {
  2122. val <<= avail;
  2123. val |= b & ((1 << avail) - 1);
  2124. index += avail;
  2125. required -= avail;
  2126. } else {
  2127. b >>= avail - required;
  2128. val <<= required;
  2129. val |= b & ((1 << required) - 1);
  2130. break;
  2131. }
  2132. } while (required);
  2133. return val;
  2134. }
  2135. static uint32_t fetchbits_signed(const uint8_t *p, uint32_t index, uint32_t required)
  2136. {
  2137. uint32_t val = fetchbits_unsigned(p, index, required);
  2138. if (val & (1 << (required - 1))) {
  2139. return (int32_t)val - (1 << required);
  2140. }
  2141. return (int32_t)val;
  2142. }
  2143. uint32_t ST7735_t3::fetchpixel(const uint8_t *p, uint32_t index, uint32_t x)
  2144. {
  2145. // The byte
  2146. uint8_t b = p[index >> 3];
  2147. // Shift to LSB position and mask to get value
  2148. uint8_t s = ((fontppb-(x % fontppb)-1)*fontbpp);
  2149. // Mask and return
  2150. return (b >> s) & fontbppmask;
  2151. }
  2152. void ST7735_t3::drawFontChar(unsigned int c)
  2153. {
  2154. uint32_t bitoffset;
  2155. const uint8_t *data;
  2156. //Serial.printf("drawFontChar(%c) %d\n", c, c);
  2157. if (c >= font->index1_first && c <= font->index1_last) {
  2158. bitoffset = c - font->index1_first;
  2159. bitoffset *= font->bits_index;
  2160. } else if (c >= font->index2_first && c <= font->index2_last) {
  2161. bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
  2162. bitoffset *= font->bits_index;
  2163. } else if (font->unicode) {
  2164. return; // TODO: implement sparse unicode
  2165. } else {
  2166. return;
  2167. }
  2168. //Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
  2169. data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
  2170. uint32_t encoding = fetchbits_unsigned(data, 0, 3);
  2171. if (encoding != 0) return;
  2172. uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
  2173. bitoffset = font->bits_width + 3;
  2174. uint32_t height = fetchbits_unsigned(data, bitoffset, font->bits_height);
  2175. bitoffset += font->bits_height;
  2176. //Serial.printf(" size = %d,%d\n", width, height);
  2177. //Serial.printf(" line space = %d\n", font->line_space);
  2178. int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
  2179. bitoffset += font->bits_xoffset;
  2180. int32_t yoffset = fetchbits_signed(data, bitoffset, font->bits_yoffset);
  2181. bitoffset += font->bits_yoffset;
  2182. //Serial.printf(" offset = %d,%d\n", xoffset, yoffset);
  2183. //Serial.printf("DChar: %c %u, %u, wh:%d %d o:%d %d\n", c, cursor_x, cursor_y, width, height, xoffset, yoffset);
  2184. uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
  2185. bitoffset += font->bits_delta;
  2186. //Serial.printf(" delta = %d\n", delta);
  2187. //Serial.printf(" cursor = %d,%d\n", cursor_x, cursor_y);
  2188. //horizontally, we draw every pixel, or none at all
  2189. if (cursor_x < 0) cursor_x = 0;
  2190. int32_t origin_x = cursor_x + xoffset;
  2191. if (origin_x < 0) {
  2192. cursor_x -= xoffset;
  2193. origin_x = 0;
  2194. }
  2195. if (origin_x + (int)width > _width) {
  2196. if (!wrap) return;
  2197. origin_x = 0;
  2198. if (xoffset >= 0) {
  2199. cursor_x = 0;
  2200. } else {
  2201. cursor_x = -xoffset;
  2202. }
  2203. cursor_y += font->line_space;
  2204. }
  2205. if(wrap && scrollEnable && isWritingScrollArea && ((origin_x + (int)width) > (scroll_x+scroll_width))){
  2206. origin_x = 0;
  2207. if (xoffset >= 0) {
  2208. cursor_x = scroll_x;
  2209. } else {
  2210. cursor_x = -xoffset;
  2211. }
  2212. cursor_y += font->line_space;
  2213. }
  2214. if(scrollEnable && isWritingScrollArea && (cursor_y > (scroll_y+scroll_height - font->cap_height))){
  2215. scrollTextArea(font->line_space);
  2216. cursor_y -= font->line_space;
  2217. cursor_x = scroll_x;
  2218. }
  2219. if (cursor_y >= _height) return;
  2220. // vertically, the top and/or bottom can be clipped
  2221. int32_t origin_y = cursor_y + font->cap_height - height - yoffset;
  2222. //Serial.printf(" origin = %d,%d\n", origin_x, origin_y);
  2223. // TODO: compute top skip and number of lines
  2224. int32_t linecount = height;
  2225. //uint32_t loopcount = 0;
  2226. int32_t y = origin_y;
  2227. bool opaque = (textbgcolor != textcolor);
  2228. // Going to try a fast Opaque method which works similar to drawChar, which is near the speed of writerect
  2229. if (!opaque) {
  2230. // Anti-alias support
  2231. if (fontbpp>1){
  2232. // This branch should, in most cases, never happen. This is because if an
  2233. // anti-aliased font is being used, textcolor and textbgcolor should always
  2234. // be different. Even though an anti-alised font is being used, pixels in this
  2235. // case will all be solid because pixels are rendered on same colour as themselves!
  2236. // This won't look very good.
  2237. bitoffset = ((bitoffset + 7) & (-8)); // byte-boundary
  2238. uint32_t xp = 0;
  2239. uint8_t halfalpha = 1<<(fontbpp-1);
  2240. while (linecount) {
  2241. uint32_t x = 0;
  2242. while(x<width) {
  2243. // One pixel at a time, either on (if alpha > 0.5) or off
  2244. if (fetchpixel(data, bitoffset, xp)>=halfalpha){
  2245. Pixel(origin_x + x,y,textcolor);
  2246. }
  2247. bitoffset += fontbpp;
  2248. x++;
  2249. xp++;
  2250. }
  2251. y++;
  2252. linecount--;
  2253. }
  2254. }
  2255. // Soild pixels
  2256. else{
  2257. while (linecount > 0) {
  2258. //Serial.printf(" linecount = %d\n", linecount);
  2259. uint32_t n = 1;
  2260. if (fetchbit(data, bitoffset++) != 0) {
  2261. n = fetchbits_unsigned(data, bitoffset, 3) + 2;
  2262. bitoffset += 3;
  2263. }
  2264. uint32_t x = 0;
  2265. do {
  2266. int32_t xsize = width - x;
  2267. if (xsize > 32) xsize = 32;
  2268. uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
  2269. //Serial.printf(" multi line %d %d %x\n", n, x, bits);
  2270. drawFontBits(opaque, bits, xsize, origin_x + x, y, n);
  2271. bitoffset += xsize;
  2272. x += xsize;
  2273. } while (x < width);
  2274. y += n;
  2275. linecount -= n;
  2276. //if (++loopcount > 100) {
  2277. //Serial.println(" abort draw loop");
  2278. //break;
  2279. //}
  2280. }
  2281. } // 1bpp
  2282. }
  2283. // opaque
  2284. else {
  2285. // Now opaque mode...
  2286. // Now write out background color for the number of rows above the above the character
  2287. // figure out bounding rectangle...
  2288. // In this mode we need to update to use the offset and bounding rectangles as we are doing it it direct.
  2289. // also update the Origin
  2290. int cursor_x_origin = cursor_x + _originx;
  2291. int cursor_y_origin = cursor_y + _originy;
  2292. origin_x += _originx;
  2293. origin_y += _originy;
  2294. int start_x = (origin_x < cursor_x_origin) ? origin_x : cursor_x_origin;
  2295. if (start_x < 0) start_x = 0;
  2296. int start_y = (origin_y < cursor_y_origin) ? origin_y : cursor_y_origin;
  2297. if (start_y < 0) start_y = 0;
  2298. int end_x = cursor_x_origin + delta;
  2299. if ((origin_x + (int)width) > end_x)
  2300. end_x = origin_x + (int)width;
  2301. if (end_x >= _displayclipx2) end_x = _displayclipx2;
  2302. int end_y = cursor_y_origin + font->line_space;
  2303. if ((origin_y + (int)height) > end_y)
  2304. end_y = origin_y + (int)height;
  2305. if (end_y >= _displayclipy2) end_y = _displayclipy2;
  2306. end_x--; // setup to last one we draw
  2307. end_y--;
  2308. int start_x_min = (start_x >= _displayclipx1) ? start_x : _displayclipx1;
  2309. int start_y_min = (start_y >= _displayclipy1) ? start_y : _displayclipy1;
  2310. // See if anything is in the display area.
  2311. if((end_x < _displayclipx1) ||(start_x >= _displayclipx2) || (end_y < _displayclipy1) || (start_y >= _displayclipy2)) {
  2312. cursor_x += delta; // could use goto or another indent level...
  2313. return;
  2314. }
  2315. /*
  2316. Serial.printf("drawFontChar(%c) %d\n", c, c);
  2317. Serial.printf(" size = %d,%d\n", width, height);
  2318. Serial.printf(" line space = %d\n", font->line_space);
  2319. Serial.printf(" offset = %d,%d\n", xoffset, yoffset);
  2320. Serial.printf(" delta = %d\n", delta);
  2321. Serial.printf(" cursor = %d,%d\n", cursor_x, cursor_y);
  2322. Serial.printf(" origin = %d,%d\n", origin_x, origin_y);
  2323. Serial.printf(" Bounding: (%d, %d)-(%d, %d)\n", start_x, start_y, end_x, end_y);
  2324. Serial.printf(" mins (%d %d),\n", start_x_min, start_y_min);
  2325. */
  2326. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  2327. if (_use_fbtft) {
  2328. uint16_t * pfbPixel_row = &_pfbtft[ start_y*_width + start_x];
  2329. uint16_t * pfbPixel;
  2330. int screen_y = start_y;
  2331. int screen_x;
  2332. // Clear above character
  2333. while (screen_y < origin_y) {
  2334. pfbPixel = pfbPixel_row;
  2335. // only output if this line is within the clipping region.
  2336. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2337. for (screen_x = start_x; screen_x <= end_x; screen_x++) {
  2338. if (screen_x >= _displayclipx1) {
  2339. *pfbPixel = textbgcolor;
  2340. }
  2341. pfbPixel++;
  2342. }
  2343. }
  2344. screen_y++;
  2345. pfbPixel_row += _width;
  2346. }
  2347. // Anti-aliased font
  2348. if (fontbpp>1){
  2349. screen_y = origin_y;
  2350. bitoffset = ((bitoffset + 7) & (-8)); // byte-boundary
  2351. uint32_t xp = 0;
  2352. int glyphend_x = origin_x+width;
  2353. while (linecount) {
  2354. pfbPixel = pfbPixel_row;
  2355. screen_x = start_x;
  2356. while(screen_x<=end_x) {
  2357. // XXX: I'm sure clipping could be done way more efficiently than just chekcing every single pixel, but let's just get this going
  2358. if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2) && (screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2359. // Clear before or after pixel
  2360. if ((screen_x<origin_x) || (screen_x>=glyphend_x)){
  2361. *pfbPixel = textbgcolor;
  2362. }
  2363. // Draw alpha-blended character
  2364. else{
  2365. uint8_t alpha = fetchpixel(data, bitoffset, xp);
  2366. *pfbPixel = alphaBlendRGB565Premultiplied( textcolorPrexpanded, textbgcolorPrexpanded, (uint8_t)(alpha * fontalphamx) );
  2367. bitoffset += fontbpp;
  2368. xp++;
  2369. }
  2370. } // clip
  2371. screen_x++;
  2372. pfbPixel++;
  2373. }
  2374. pfbPixel_row += _width;
  2375. screen_y++;
  2376. linecount--;
  2377. }
  2378. } // anti-aliased
  2379. // 1bpp solid font
  2380. else{
  2381. // Now lets process each of the data lines (draw character)
  2382. screen_y = origin_y;
  2383. while (linecount > 0) {
  2384. //Serial.printf(" linecount = %d\n", linecount);
  2385. uint32_t b = fetchbit(data, bitoffset++);
  2386. uint32_t n;
  2387. if (b == 0) {
  2388. //Serial.println("Single");
  2389. n = 1;
  2390. } else {
  2391. //Serial.println("Multi");
  2392. n = fetchbits_unsigned(data, bitoffset, 3) + 2;
  2393. bitoffset += 3;
  2394. }
  2395. uint32_t bitoffset_row_start = bitoffset;
  2396. while (n--) {
  2397. pfbPixel = pfbPixel_row;
  2398. // Clear to left
  2399. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2400. bitoffset = bitoffset_row_start; // we will work through these bits maybe multiple times
  2401. for (screen_x = start_x; screen_x < origin_x; screen_x++) {
  2402. if (screen_x >= _displayclipx1) {
  2403. *pfbPixel = textbgcolor;
  2404. } // make sure not clipped
  2405. pfbPixel++;
  2406. }
  2407. }
  2408. // Pixel bits
  2409. screen_x = origin_x;
  2410. uint32_t x = 0;
  2411. do {
  2412. uint32_t xsize = width - x;
  2413. if (xsize > 32) xsize = 32;
  2414. uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
  2415. uint32_t bit_mask = 1 << (xsize-1);
  2416. //Serial.printf(" %d %d %x %x\n", x, xsize, bits, bit_mask);
  2417. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2418. while (bit_mask && (screen_x <= end_x)) {
  2419. if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
  2420. *pfbPixel = (bits & bit_mask) ? textcolor : textbgcolor;
  2421. }
  2422. pfbPixel++;
  2423. bit_mask = bit_mask >> 1;
  2424. screen_x++; // increment our pixel position.
  2425. }
  2426. }
  2427. bitoffset += xsize;
  2428. x += xsize;
  2429. } while (x < width);
  2430. // Clear to right
  2431. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2432. // output bg color and right hand side
  2433. while (screen_x++ <= end_x) {
  2434. *pfbPixel++ = textbgcolor;
  2435. }
  2436. }
  2437. screen_y++;
  2438. pfbPixel_row += _width;
  2439. linecount--;
  2440. }
  2441. }
  2442. } // 1bpp
  2443. // clear below character
  2444. while (screen_y++ <= end_y) {
  2445. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2446. pfbPixel = pfbPixel_row;
  2447. for (screen_x = start_x; screen_x <= end_x; screen_x++) {
  2448. if (screen_x >= _displayclipx1) {
  2449. *pfbPixel = textbgcolor;
  2450. }
  2451. pfbPixel++;
  2452. }
  2453. }
  2454. pfbPixel_row += _width;
  2455. }
  2456. } else
  2457. #endif
  2458. {
  2459. beginSPITransaction();
  2460. //Serial.printf("SetAddr %d %d %d %d\n", start_x_min, start_y_min, end_x, end_y);
  2461. // output rectangle we are updating... We have already clipped end_x/y, but not yet start_x/y
  2462. setAddr( start_x, start_y_min, end_x, end_y);
  2463. writecommand(ST7735_RAMWR);
  2464. int screen_y = start_y_min;
  2465. int screen_x;
  2466. // Clear above character
  2467. while (screen_y < origin_y) {
  2468. for (screen_x = start_x_min; screen_x <= end_x; screen_x++) {
  2469. writedata16(textbgcolor);
  2470. }
  2471. screen_y++;
  2472. }
  2473. // Anti-aliased font
  2474. if (fontbpp>1){
  2475. screen_y = origin_y;
  2476. bitoffset = ((bitoffset + 7) & (-8)); // byte-boundary
  2477. int glyphend_x = origin_x+width;
  2478. uint32_t xp = 0;
  2479. while (linecount) {
  2480. screen_x = start_x;
  2481. while(screen_x<=end_x) {
  2482. // XXX: I'm sure clipping could be done way more efficiently than just chekcing every single pixel, but let's just get this going
  2483. if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2) && (screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2484. // Clear before or after pixel
  2485. if ((screen_x<origin_x) || (screen_x>=glyphend_x)){
  2486. writedata16(textbgcolor);
  2487. }
  2488. // Draw alpha-blended character
  2489. else{
  2490. uint8_t alpha = fetchpixel(data, bitoffset, xp);
  2491. writedata16( alphaBlendRGB565Premultiplied( textcolorPrexpanded, textbgcolorPrexpanded, (uint8_t)(alpha * fontalphamx) ) );
  2492. bitoffset += fontbpp;
  2493. xp++;
  2494. }
  2495. } // clip
  2496. screen_x++;
  2497. }
  2498. screen_y++;
  2499. linecount--;
  2500. }
  2501. } // anti-aliased
  2502. // 1bpp
  2503. else{
  2504. // Now lets process each of the data lines.
  2505. screen_y = origin_y;
  2506. while (linecount > 0) {
  2507. //Serial.printf(" linecount = %d\n", linecount);
  2508. uint32_t b = fetchbit(data, bitoffset++);
  2509. uint32_t n;
  2510. if (b == 0) {
  2511. //Serial.println(" Single");
  2512. n = 1;
  2513. } else {
  2514. //Serial.println(" Multi");
  2515. n = fetchbits_unsigned(data, bitoffset, 3) + 2;
  2516. bitoffset += 3;
  2517. }
  2518. uint32_t bitoffset_row_start = bitoffset;
  2519. while (n--) {
  2520. // do some clipping here.
  2521. bitoffset = bitoffset_row_start; // we will work through these bits maybe multiple times
  2522. // We need to handle case where some of the bits may not be visible, but we still need to
  2523. // read through them
  2524. //Serial.printf("y:%d %d %d %d %d\n", screen_y, start_x, origin_x, _displayclipx1, _displayclipx2);
  2525. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2526. for (screen_x = start_x; screen_x < origin_x; screen_x++) {
  2527. if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
  2528. //Serial.write('-');
  2529. writedata16(textbgcolor);
  2530. }
  2531. }
  2532. }
  2533. uint32_t x = 0;
  2534. screen_x = origin_x;
  2535. do {
  2536. uint32_t xsize = width - x;
  2537. if (xsize > 32) xsize = 32;
  2538. uint32_t bits = fetchbits_unsigned(data, bitoffset, xsize);
  2539. uint32_t bit_mask = 1 << (xsize-1);
  2540. //Serial.printf(" %d %d %x %x - ", x, xsize, bits, bit_mask);
  2541. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2542. while (bit_mask) {
  2543. if ((screen_x >= _displayclipx1) && (screen_x < _displayclipx2)) {
  2544. writedata16((bits & bit_mask) ? textcolor : textbgcolor);
  2545. //Serial.write((bits & bit_mask) ? '*' : '.');
  2546. }
  2547. bit_mask = bit_mask >> 1;
  2548. screen_x++ ; // Current actual screen X
  2549. }
  2550. //Serial.println();
  2551. bitoffset += xsize;
  2552. }
  2553. x += xsize;
  2554. } while (x < width) ;
  2555. if ((screen_y >= _displayclipy1) && (screen_y < _displayclipy2)) {
  2556. // output bg color and right hand side
  2557. while (screen_x++ <= end_x) {
  2558. writedata16(textbgcolor);
  2559. //Serial.write('+');
  2560. }
  2561. //Serial.println();
  2562. }
  2563. screen_y++;
  2564. linecount--;
  2565. }
  2566. }
  2567. } // 1bpp
  2568. // clear below character - note reusing xcreen_x for this
  2569. screen_x = (end_y + 1 - screen_y) * (end_x + 1 - start_x_min); // How many bytes we need to still output
  2570. //Serial.printf("Clear Below: %d\n", screen_x);
  2571. while (screen_x-- > 1) {
  2572. writedata16(textbgcolor);
  2573. }
  2574. writedata16_last(textbgcolor);
  2575. endSPITransaction();
  2576. }
  2577. }
  2578. // Increment to setup for the next character.
  2579. cursor_x += delta;
  2580. }
  2581. //strPixelLen - gets pixel length of given ASCII string
  2582. int16_t ST7735_t3::strPixelLen(const char * str)
  2583. {
  2584. // //Serial.printf("strPixelLen %s\n", str);
  2585. if (!str) return(0);
  2586. if (gfxFont)
  2587. {
  2588. // BUGBUG:: just use the other function for now... May do this for all of them...
  2589. int16_t x, y;
  2590. uint16_t w, h;
  2591. getTextBounds(str, cursor_x, cursor_y, &x, &y, &w, &h);
  2592. return w;
  2593. }
  2594. uint16_t len=0, maxlen=0;
  2595. while (*str)
  2596. {
  2597. if (*str=='\n')
  2598. {
  2599. if ( len > maxlen )
  2600. {
  2601. maxlen=len;
  2602. len=0;
  2603. }
  2604. }
  2605. else
  2606. {
  2607. if (!font)
  2608. {
  2609. len+=textsize_x*6;
  2610. }
  2611. else
  2612. {
  2613. uint32_t bitoffset;
  2614. const uint8_t *data;
  2615. uint16_t c = *str;
  2616. // //Serial.printf("char %c(%d)\n", c,c);
  2617. if (c >= font->index1_first && c <= font->index1_last) {
  2618. bitoffset = c - font->index1_first;
  2619. bitoffset *= font->bits_index;
  2620. } else if (c >= font->index2_first && c <= font->index2_last) {
  2621. bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
  2622. bitoffset *= font->bits_index;
  2623. } else if (font->unicode) {
  2624. continue;
  2625. } else {
  2626. continue;
  2627. }
  2628. //Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
  2629. data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
  2630. uint32_t encoding = fetchbits_unsigned(data, 0, 3);
  2631. if (encoding != 0) continue;
  2632. // uint32_t width = fetchbits_unsigned(data, 3, font->bits_screenWidth);
  2633. // //Serial.printf(" width = %d\n", width);
  2634. bitoffset = font->bits_width + 3;
  2635. bitoffset += font->bits_height;
  2636. // int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
  2637. // //Serial.printf(" xoffset = %d\n", xoffset);
  2638. bitoffset += font->bits_xoffset;
  2639. bitoffset += font->bits_yoffset;
  2640. uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
  2641. bitoffset += font->bits_delta;
  2642. // //Serial.printf(" delta = %d\n", delta);
  2643. len += delta;//+width-xoffset;
  2644. // //Serial.printf(" len = %d\n", len);
  2645. }
  2646. if ( len > maxlen )
  2647. {
  2648. maxlen=len;
  2649. // //Serial.printf(" maxlen = %d\n", maxlen);
  2650. }
  2651. }
  2652. str++;
  2653. }
  2654. // //Serial.printf("Return maxlen = %d\n", maxlen);
  2655. return( maxlen );
  2656. }
  2657. void ST7735_t3::charBounds(char c, int16_t *x, int16_t *y,
  2658. int16_t *minx, int16_t *miny, int16_t *maxx, int16_t *maxy) {
  2659. // BUGBUG:: Not handling offset/clip
  2660. if (font) {
  2661. if(c == '\n') { // Newline?
  2662. *x = 0; // Reset x to zero, advance y by one line
  2663. *y += font->line_space;
  2664. } else if(c != '\r') { // Not a carriage return; is normal char
  2665. uint32_t bitoffset;
  2666. const uint8_t *data;
  2667. if (c >= font->index1_first && c <= font->index1_last) {
  2668. bitoffset = c - font->index1_first;
  2669. bitoffset *= font->bits_index;
  2670. } else if (c >= font->index2_first && c <= font->index2_last) {
  2671. bitoffset = c - font->index2_first + font->index1_last - font->index1_first + 1;
  2672. bitoffset *= font->bits_index;
  2673. } else if (font->unicode) {
  2674. return; // TODO: implement sparse unicode
  2675. } else {
  2676. return;
  2677. }
  2678. //Serial.printf(" index = %d\n", fetchbits_unsigned(font->index, bitoffset, font->bits_index));
  2679. data = font->data + fetchbits_unsigned(font->index, bitoffset, font->bits_index);
  2680. uint32_t encoding = fetchbits_unsigned(data, 0, 3);
  2681. if (encoding != 0) return;
  2682. uint32_t width = fetchbits_unsigned(data, 3, font->bits_width);
  2683. bitoffset = font->bits_width + 3;
  2684. uint32_t height = fetchbits_unsigned(data, bitoffset, font->bits_height);
  2685. bitoffset += font->bits_height;
  2686. //Serial.printf(" size = %d,%d\n", width, height);
  2687. //Serial.printf(" line space = %d\n", font->line_space);
  2688. int32_t xoffset = fetchbits_signed(data, bitoffset, font->bits_xoffset);
  2689. bitoffset += font->bits_xoffset;
  2690. int32_t yoffset = fetchbits_signed(data, bitoffset, font->bits_yoffset);
  2691. bitoffset += font->bits_yoffset;
  2692. uint32_t delta = fetchbits_unsigned(data, bitoffset, font->bits_delta);
  2693. bitoffset += font->bits_delta;
  2694. int16_t
  2695. x1 = *x + xoffset,
  2696. y1 = *y + yoffset,
  2697. x2 = x1 + width,
  2698. y2 = y1 + height;
  2699. if(wrap && (x2 > _width)) {
  2700. *x = 0; // Reset x to zero, advance y by one line
  2701. *y += font->line_space;
  2702. x1 = *x + xoffset,
  2703. y1 = *y + yoffset,
  2704. x2 = x1 + width,
  2705. y2 = y1 + height;
  2706. }
  2707. if(x1 < *minx) *minx = x1;
  2708. if(y1 < *miny) *miny = y1;
  2709. if(x2 > *maxx) *maxx = x2;
  2710. if(y2 > *maxy) *maxy = y2;
  2711. *x += delta; // ? guessing here...
  2712. }
  2713. }
  2714. else if(gfxFont) {
  2715. if(c == '\n') { // Newline?
  2716. *x = 0; // Reset x to zero, advance y by one line
  2717. *y += textsize_y * gfxFont->yAdvance;
  2718. } else if(c != '\r') { // Not a carriage return; is normal char
  2719. uint8_t first = gfxFont->first,
  2720. last = gfxFont->last;
  2721. if((c >= first) && (c <= last)) { // Char present in this font?
  2722. GFXglyph *glyph = gfxFont->glyph + (c - first);
  2723. uint8_t gw = glyph->width,
  2724. gh = glyph->height,
  2725. xa = glyph->xAdvance;
  2726. int8_t xo = glyph->xOffset,
  2727. yo = glyph->yOffset + gfxFont->yAdvance/2;
  2728. if(wrap && ((*x+(((int16_t)xo+gw)*textsize_x)) > _width)) {
  2729. *x = 0; // Reset x to zero, advance y by one line
  2730. *y += textsize_y * gfxFont->yAdvance;
  2731. }
  2732. int16_t tsx = (int16_t)textsize_x,
  2733. tsy = (int16_t)textsize_y,
  2734. x1 = *x + xo * tsx,
  2735. y1 = *y + yo * tsy,
  2736. x2 = x1 + gw * tsx - 1,
  2737. y2 = y1 + gh * tsy - 1;
  2738. if(x1 < *minx) *minx = x1;
  2739. if(y1 < *miny) *miny = y1;
  2740. if(x2 > *maxx) *maxx = x2;
  2741. if(y2 > *maxy) *maxy = y2;
  2742. *x += xa * tsx;
  2743. }
  2744. }
  2745. } else { // Default font
  2746. if(c == '\n') { // Newline?
  2747. *x = 0; // Reset x to zero,
  2748. *y += textsize_y * 8; // advance y one line
  2749. // min/max x/y unchaged -- that waits for next 'normal' character
  2750. } else if(c != '\r') { // Normal char; ignore carriage returns
  2751. if(wrap && ((*x + textsize_x * 6) > _width)) { // Off right?
  2752. *x = 0; // Reset x to zero,
  2753. *y += textsize_y * 8; // advance y one line
  2754. }
  2755. int x2 = *x + textsize_x * 6 - 1, // Lower-right pixel of char
  2756. y2 = *y + textsize_y * 8 - 1;
  2757. if(x2 > *maxx) *maxx = x2; // Track max x, y
  2758. if(y2 > *maxy) *maxy = y2;
  2759. if(*x < *minx) *minx = *x; // Track min x, y
  2760. if(*y < *miny) *miny = *y;
  2761. *x += textsize_x * 6; // Advance x one char
  2762. }
  2763. }
  2764. }
  2765. // Add in Adafruit versions of text bounds calculations.
  2766. void ST7735_t3::getTextBounds(const uint8_t *buffer, uint16_t len, int16_t x, int16_t y,
  2767. int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
  2768. *x1 = x;
  2769. *y1 = y;
  2770. *w = *h = 0;
  2771. int16_t minx = _width, miny = _height, maxx = -1, maxy = -1;
  2772. while(len--)
  2773. charBounds(*buffer++, &x, &y, &minx, &miny, &maxx, &maxy);
  2774. if(maxx >= minx) {
  2775. *x1 = minx;
  2776. *w = maxx - minx + 1;
  2777. }
  2778. if(maxy >= miny) {
  2779. *y1 = miny;
  2780. *h = maxy - miny + 1;
  2781. }
  2782. }
  2783. void ST7735_t3::getTextBounds(const char *str, int16_t x, int16_t y,
  2784. int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
  2785. uint8_t c; // Current character
  2786. *x1 = x;
  2787. *y1 = y;
  2788. *w = *h = 0;
  2789. int16_t minx = _width, miny = _height, maxx = -1, maxy = -1;
  2790. while((c = *str++))
  2791. charBounds(c, &x, &y, &minx, &miny, &maxx, &maxy);
  2792. if(maxx >= minx) {
  2793. *x1 = minx;
  2794. *w = maxx - minx + 1;
  2795. }
  2796. if(maxy >= miny) {
  2797. *y1 = miny;
  2798. *h = maxy - miny + 1;
  2799. }
  2800. }
  2801. void ST7735_t3::getTextBounds(const String &str, int16_t x, int16_t y,
  2802. int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h) {
  2803. if (str.length() != 0) {
  2804. getTextBounds(const_cast<char*>(str.c_str()), x, y, x1, y1, w, h);
  2805. }
  2806. }
  2807. void ST7735_t3::drawFontPixel( uint8_t alpha, uint32_t x, uint32_t y ){
  2808. // Adjust alpha based on the number of alpha levels supported by the font (based on bpp)
  2809. // Note: Implemented look-up table for alpha, but made absolutely no difference in speed (T3.6)
  2810. alpha = (uint8_t)(alpha * fontalphamx);
  2811. uint32_t result = ((((textcolorPrexpanded - textbgcolorPrexpanded) * alpha) >> 5) + textbgcolorPrexpanded) & 0b00000111111000001111100000011111;
  2812. Pixel(x,y,(uint16_t)((result >> 16) | result));
  2813. }
  2814. void ST7735_t3::drawFontBits(bool opaque, uint32_t bits, uint32_t numbits, int32_t x, int32_t y, uint32_t repeat)
  2815. {
  2816. if (bits == 0) {
  2817. if (opaque) {
  2818. fillRect(x, y, numbits, repeat, textbgcolor);
  2819. }
  2820. } else {
  2821. int32_t x1 = x;
  2822. uint32_t n = numbits;
  2823. int w;
  2824. int bgw;
  2825. w = 0;
  2826. bgw = 0;
  2827. do {
  2828. n--;
  2829. if (bits & (1 << n)) {
  2830. if (bgw>0) {
  2831. if (opaque) {
  2832. fillRect(x1 - bgw, y, bgw, repeat, textbgcolor);
  2833. }
  2834. bgw=0;
  2835. }
  2836. w++;
  2837. } else {
  2838. if (w>0) {
  2839. fillRect(x1 - w, y, w, repeat, textcolor);
  2840. w = 0;
  2841. }
  2842. bgw++;
  2843. }
  2844. x1++;
  2845. } while (n > 0);
  2846. if (w > 0) {
  2847. fillRect(x1 - w, y, w, repeat, textcolor);
  2848. }
  2849. if (bgw > 0) {
  2850. if (opaque) {
  2851. fillRect(x1 - bgw, y, bgw, repeat, textbgcolor);
  2852. }
  2853. }
  2854. }
  2855. }
  2856. void ST7735_t3::drawGFXFontChar(unsigned int c) {
  2857. // Lets do Adafruit GFX character output here as well
  2858. if(c == '\r') return;
  2859. // Some quick and dirty tests to see if we can
  2860. uint8_t first = gfxFont->first;
  2861. if((c < first) || (c > gfxFont->last)) return;
  2862. GFXglyph *glyph = gfxFont->glyph + (c - first);
  2863. uint8_t w = glyph->width,
  2864. h = glyph->height;
  2865. // wonder if we should look at xo, yo instead?
  2866. if((w == 0 || h == 0) && (c != 32)) return; // Is there an associated bitmap?
  2867. int16_t xo = glyph->xOffset; // sic
  2868. int16_t yo = glyph->yOffset + gfxFont->yAdvance/2;
  2869. if(wrap && ((cursor_x + textsize_x * (xo + w)) > _width)) {
  2870. cursor_x = 0;
  2871. cursor_y += (int16_t)textsize_y * gfxFont->yAdvance;
  2872. }
  2873. // Lets do the work to output the font character
  2874. uint8_t *bitmap = gfxFont->bitmap;
  2875. uint16_t bo = glyph->bitmapOffset;
  2876. uint8_t xx, yy, bits = 0, bit = 0;
  2877. //Serial.printf("DGFX_char: %c (%d,%d) : %u %u %u %u %d %d %x %x %d\n", c, cursor_x, cursor_y, w, h,
  2878. // glyph->xAdvance, gfxFont->yAdvance, xo, yo, textcolor, textbgcolor, _use_fbtft);Serial.flush();
  2879. if (textcolor == textbgcolor) {
  2880. //Serial.printf("DGFXChar: %c %u, %u, wh:%d %d o:%d %d\n", c, cursor_x, cursor_y, w, h, xo, yo);
  2881. // NOTE: Adafruit GFX does not support Opaque font output as there
  2882. // are issues with proportionally spaced characters that may overlap
  2883. // So the below is not perfect as we may overwrite a small portion
  2884. // of a letter with the next one, when we blank out...
  2885. // But: I prefer to let each of us decide if the limitations are
  2886. // worth it or not. If Not you still have the option to not
  2887. // Do transparent mode and instead blank out and blink...
  2888. for(yy=0; yy<h; yy++) {
  2889. uint8_t w_left = w;
  2890. xx = 0;
  2891. while (w_left) {
  2892. if(!(bit & 7)) {
  2893. bits = bitmap[bo++];
  2894. }
  2895. // Could try up to 8 bits at time, but start off trying up to 4
  2896. uint8_t xCount;
  2897. if ((w_left >= 8) && ((bits & 0xff) == 0xff)) {
  2898. xCount = 8;
  2899. //Serial.print("8");
  2900. fillRect(cursor_x+(xo+xx)*textsize_x, cursor_y+(yo+yy)*textsize_y,
  2901. xCount * textsize_x, textsize_y, textcolor);
  2902. } else if ((w_left >= 4) && ((bits & 0xf0) == 0xf0)) {
  2903. xCount = 4;
  2904. //Serial.print("4");
  2905. fillRect(cursor_x+(xo+xx)*textsize_x, cursor_y+(yo+yy)*textsize_y,
  2906. xCount * textsize_x, textsize_y, textcolor);
  2907. } else if ((w_left >= 3) && ((bits & 0xe0) == 0xe0)) {
  2908. //Serial.print("3");
  2909. xCount = 3;
  2910. fillRect(cursor_x+(xo+xx)*textsize_x, cursor_y+(yo+yy)*textsize_y,
  2911. xCount * textsize_x, textsize_y, textcolor);
  2912. } else if ((w_left >= 2) && ((bits & 0xc0) == 0xc0)) {
  2913. //Serial.print("2");
  2914. xCount = 2;
  2915. fillRect(cursor_x+(xo+xx)*textsize_x, cursor_y+(yo+yy)*textsize_y,
  2916. xCount * textsize_x, textsize_y, textcolor);
  2917. } else {
  2918. xCount = 1;
  2919. if(bits & 0x80) {
  2920. if((textsize_x == 1) && (textsize_y == 1)){
  2921. drawPixel(cursor_x+xo+xx, cursor_y+yo+yy, textcolor);
  2922. } else {
  2923. fillRect(cursor_x+(xo+xx)*textsize_x, cursor_y+(yo+yy)*textsize_y,
  2924. textsize_x, textsize_y, textcolor);
  2925. }
  2926. }
  2927. }
  2928. xx += xCount;
  2929. w_left -= xCount;
  2930. bit += xCount;
  2931. bits <<= xCount;
  2932. }
  2933. }
  2934. _gfx_last_char_x_write = 0;
  2935. } else {
  2936. // To Do, properly clipping and offsetting...
  2937. // This solid background approach is about 5 time faster
  2938. // Lets calculate bounding rectangle that we will update
  2939. // We need to offset by the origin.
  2940. // We are going direct so do some offsets and clipping
  2941. int16_t x_offset_cursor = cursor_x + _originx; // This is where the offseted cursor is.
  2942. int16_t x_start = x_offset_cursor; // I am assuming no negative x offsets.
  2943. int16_t x_end = x_offset_cursor + (glyph->xAdvance * textsize_x);
  2944. if (glyph->xAdvance < (xo + w)) x_end = x_offset_cursor + ((xo + w)* textsize_x); // BUGBUG Overlflows into next char position.
  2945. int16_t x_left_fill = x_offset_cursor + xo * textsize_x;
  2946. int16_t x;
  2947. if (xo < 0) {
  2948. // Unusual character that goes back into previous character
  2949. //Serial.printf("GFX Font char XO < 0: %c %d %d %u %u %u\n", c, xo, yo, w, h, glyph->xAdvance );
  2950. x_start += xo * textsize_x;
  2951. x_left_fill = 0; // Don't need to fill anything here...
  2952. }
  2953. int16_t y_start = cursor_y + _originy + (_gfxFont_min_yOffset * textsize_y)+ gfxFont->yAdvance*textsize_y/2; // UP to most negative value.
  2954. int16_t y_end = y_start + gfxFont->yAdvance * textsize_y; // how far we will update
  2955. int16_t y = y_start;
  2956. //int8_t y_top_fill = (yo - _gfxFont_min_yOffset) * textsize_y; // both negative like -10 - -16 = 6...
  2957. int8_t y_top_fill = (yo - gfxFont->yAdvance/2 - _gfxFont_min_yOffset) * textsize_y;
  2958. // See if anything is within clip rectangle, if not bail
  2959. if((x_start >= _displayclipx2) || // Clip right
  2960. (y_start >= _displayclipy2) || // Clip bottom
  2961. (x_end < _displayclipx1) || // Clip left
  2962. (y_end < _displayclipy1)) // Clip top
  2963. {
  2964. // But remember to first update the cursor position
  2965. cursor_x += glyph->xAdvance * (int16_t)textsize_x;
  2966. return;
  2967. }
  2968. // If our y_end > _displayclipy2 set it to _displayclipy2 as to not have to test both Likewise X
  2969. if (y_end > _displayclipy2) y_end = _displayclipy2;
  2970. if (x_end > _displayclipx2) x_end = _displayclipx2;
  2971. // If we get here and
  2972. if (_gfx_last_cursor_y != (cursor_y + _originy)) _gfx_last_char_x_write = 0;
  2973. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  2974. if (_use_fbtft) {
  2975. // lets try to output the values directly...
  2976. uint16_t * pfbPixel_row = &_pfbtft[ y_start *_width + x_start];
  2977. uint16_t * pfbPixel;
  2978. // First lets fill in the top parts above the actual rectangle...
  2979. while (y_top_fill--) {
  2980. pfbPixel = pfbPixel_row;
  2981. if ( (y >= _displayclipy1) && (y < _displayclipy2)) {
  2982. for (int16_t xx = x_start; xx < x_end; xx++) {
  2983. if ((xx >= _displayclipx1) && (xx >= x_offset_cursor)) {
  2984. if ((xx >= _gfx_last_char_x_write) || (*pfbPixel != _gfx_last_char_textcolor))
  2985. *pfbPixel = textbgcolor;
  2986. }
  2987. pfbPixel++;
  2988. }
  2989. }
  2990. pfbPixel_row += _width;
  2991. y++;
  2992. }
  2993. // Now lets output all of the pixels for each of the rows..
  2994. for(yy=0; (yy<h) && (y < _displayclipy2); yy++) {
  2995. uint16_t bo_save = bo;
  2996. uint8_t bit_save = bit;
  2997. uint8_t bits_save = bits;
  2998. for (uint8_t yts = 0; (yts < textsize_y) && (y < _displayclipy2); yts++) {
  2999. pfbPixel = pfbPixel_row;
  3000. // need to repeat the stuff for each row...
  3001. bo = bo_save;
  3002. bit = bit_save;
  3003. bits = bits_save;
  3004. x = x_start;
  3005. if (y >= _displayclipy1) {
  3006. while (x < x_left_fill) {
  3007. if ( (x >= _displayclipx1) && (x < _displayclipx2)) {
  3008. if ((x >= _gfx_last_char_x_write) || (*pfbPixel != _gfx_last_char_textcolor))
  3009. *pfbPixel = textbgcolor;
  3010. }
  3011. pfbPixel++;
  3012. x++;
  3013. }
  3014. for(xx=0; xx<w; xx++) {
  3015. if(!(bit++ & 7)) {
  3016. bits = bitmap[bo++];
  3017. }
  3018. for (uint8_t xts = 0; xts < textsize_x; xts++) {
  3019. if ( (x >= _displayclipx1) && (x < _displayclipx2)) {
  3020. if (bits & 0x80)
  3021. *pfbPixel = textcolor;
  3022. else if (x >= x_offset_cursor) {
  3023. if ((x >= _gfx_last_char_x_write) || (*pfbPixel != _gfx_last_char_textcolor))
  3024. *pfbPixel = textbgcolor;
  3025. }
  3026. }
  3027. pfbPixel++;
  3028. x++; // remember our logical position...
  3029. }
  3030. bits <<= 1;
  3031. }
  3032. // Fill in any additional bg colors to right of our output
  3033. while (x++ < x_end) {
  3034. if (x >= _displayclipx1) {
  3035. *pfbPixel = textbgcolor;
  3036. }
  3037. pfbPixel++;
  3038. }
  3039. }
  3040. y++; // remember which row we just output
  3041. pfbPixel_row += _width;
  3042. }
  3043. }
  3044. // And output any more rows below us...
  3045. while (y < y_end) {
  3046. if (y >= _displayclipy1) {
  3047. pfbPixel = pfbPixel_row;
  3048. for (int16_t xx = x_start; xx < x_end; xx++) {
  3049. if ((xx >= _displayclipx1) && (xx >= x_offset_cursor)) {
  3050. if ((xx >= _gfx_last_char_x_write) || (*pfbPixel != _gfx_last_char_textcolor))
  3051. *pfbPixel = textbgcolor;
  3052. }
  3053. pfbPixel++;
  3054. }
  3055. }
  3056. pfbPixel_row += _width;
  3057. y++;
  3058. }
  3059. } else
  3060. #endif
  3061. {
  3062. // lets try to output text in one output rectangle
  3063. //Serial.printf(" SPI (%d %d) (%d %d)\n", x_start, y_start, x_end, y_end);Serial.flush();
  3064. // compute the actual region we will output given
  3065. beginSPITransaction();
  3066. setAddr((x_start >= _displayclipx1) ? x_start : _displayclipx1,
  3067. (y_start >= _displayclipy1) ? y_start : _displayclipy1,
  3068. x_end - 1, y_end - 1);
  3069. writecommand(ST7735_RAMWR);
  3070. //Serial.printf("SetAddr: %u %u %u %u\n", (x_start >= _displayclipx1) ? x_start : _displayclipx1,
  3071. // (y_start >= _displayclipy1) ? y_start : _displayclipy1,
  3072. // x_end - 1, y_end - 1);
  3073. // First lets fill in the top parts above the actual rectangle...
  3074. //Serial.printf(" y_top_fill %d x_left_fill %d\n", y_top_fill, x_left_fill);
  3075. while (y_top_fill--) {
  3076. if ( (y >= _displayclipy1) && (y < _displayclipy2)) {
  3077. for (int16_t xx = x_start; xx < x_end; xx++) {
  3078. if (xx >= _displayclipx1) {
  3079. writedata16(gfxFontLastCharPosFG(xx,y)? _gfx_last_char_textcolor : (xx < x_offset_cursor)? _gfx_last_char_textbgcolor : textbgcolor);
  3080. }
  3081. }
  3082. }
  3083. y++;
  3084. }
  3085. //Serial.println(" After top fill"); Serial.flush();
  3086. // Now lets output all of the pixels for each of the rows..
  3087. for(yy=0; (yy<h) && (y < _displayclipy2); yy++) {
  3088. uint16_t bo_save = bo;
  3089. uint8_t bit_save = bit;
  3090. uint8_t bits_save = bits;
  3091. for (uint8_t yts = 0; (yts < textsize_y) && (y < _displayclipy2); yts++) {
  3092. // need to repeat the stuff for each row...
  3093. bo = bo_save;
  3094. bit = bit_save;
  3095. bits = bits_save;
  3096. x = x_start;
  3097. if (y >= _displayclipy1) {
  3098. while (x < x_left_fill) {
  3099. if ( (x >= _displayclipx1) && (x < _displayclipx2) ) {
  3100. // Don't need to check if we are in previous char as in this case x_left_fill is set to 0...
  3101. writedata16(gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : textbgcolor);
  3102. }
  3103. x++;
  3104. }
  3105. for(xx=0; xx<w; xx++) {
  3106. if(!(bit++ & 7)) {
  3107. bits = bitmap[bo++];
  3108. }
  3109. for (uint8_t xts = 0; xts < textsize_x; xts++) {
  3110. if ( (x >= _displayclipx1) && (x < _displayclipx2)) {
  3111. if (bits & 0x80)
  3112. writedata16(textcolor);
  3113. else
  3114. writedata16(gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : (x < x_offset_cursor)? _gfx_last_char_textbgcolor : textbgcolor);
  3115. }
  3116. x++; // remember our logical position...
  3117. }
  3118. bits <<= 1;
  3119. }
  3120. // Fill in any additional bg colors to right of our output
  3121. while (x < x_end) {
  3122. if (x >= _displayclipx1) {
  3123. writedata16(gfxFontLastCharPosFG(x,y)? _gfx_last_char_textcolor : (x < x_offset_cursor)? _gfx_last_char_textbgcolor : textbgcolor);
  3124. }
  3125. x++;
  3126. }
  3127. }
  3128. y++; // remember which row we just output
  3129. }
  3130. }
  3131. // And output any more rows below us...
  3132. //Serial.println(" Bottom fill"); Serial.flush();
  3133. while (y < y_end) {
  3134. if (y >= _displayclipy1) {
  3135. for (int16_t xx = x_start; xx < x_end; xx++) {
  3136. if (xx >= _displayclipx1 ) {
  3137. writedata16(gfxFontLastCharPosFG(xx,y)? _gfx_last_char_textcolor : (xx < x_offset_cursor)? _gfx_last_char_textbgcolor : textbgcolor);
  3138. }
  3139. }
  3140. }
  3141. y++;
  3142. }
  3143. writecommand_last(ST7735_NOP);
  3144. endSPITransaction();
  3145. }
  3146. // Save away info about this last char
  3147. _gfx_c_last = c;
  3148. _gfx_last_cursor_x = cursor_x + _originx;
  3149. _gfx_last_cursor_y = cursor_y + _originy;
  3150. _gfx_last_char_x_write = x_end;
  3151. _gfx_last_char_textcolor = textcolor;
  3152. _gfx_last_char_textbgcolor = textbgcolor;
  3153. }
  3154. cursor_x += glyph->xAdvance * (int16_t)textsize_x;
  3155. }
  3156. // Some fonts overlap characters if we detect that the previous
  3157. // character wrote out more width than they advanced in X direction
  3158. // we may want to know if the last character output a FG or BG at a position.
  3159. // Opaque font chracter overlap?
  3160. // unsigned int _gfx_c_last;
  3161. // int16_t _gfx_last_cursor_x, _gfx_last_cursor_y;
  3162. // int16_t _gfx_last_x_overlap = 0;
  3163. bool ST7735_t3::gfxFontLastCharPosFG(int16_t x, int16_t y) {
  3164. GFXglyph *glyph = gfxFont->glyph + (_gfx_c_last - gfxFont->first);
  3165. uint8_t w = glyph->width,
  3166. h = glyph->height;
  3167. int16_t xo = glyph->xOffset; // sic
  3168. int16_t yo = glyph->yOffset + gfxFont->yAdvance/2;
  3169. if (x >= _gfx_last_char_x_write) return false; // we did not update here...
  3170. if (y < (_gfx_last_cursor_y + (yo*textsize_y))) return false; // above
  3171. if (y >= (_gfx_last_cursor_y + (yo+h)*textsize_y)) return false; // below
  3172. // Lets compute which Row this y is in the bitmap
  3173. int16_t y_bitmap = (y - ((_gfx_last_cursor_y + (yo*textsize_y))) + textsize_y - 1) / textsize_y;
  3174. int16_t x_bitmap = (x - ((_gfx_last_cursor_x + (xo*textsize_x))) + textsize_x - 1) / textsize_x;
  3175. uint16_t pixel_bit_offset = y_bitmap * w + x_bitmap;
  3176. return ((gfxFont->bitmap[glyph->bitmapOffset + (pixel_bit_offset >> 3)]) & (0x80 >> (pixel_bit_offset & 0x7)));
  3177. }
  3178. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3179. void ST7735_t3::dmaInterrupt(void) {
  3180. if (_dmaActiveDisplay[0]) {
  3181. _dmaActiveDisplay[0]->process_dma_interrupt();
  3182. }
  3183. }
  3184. void ST7735_t3::dmaInterrupt1(void) {
  3185. if (_dmaActiveDisplay[1]) {
  3186. _dmaActiveDisplay[1]->process_dma_interrupt();
  3187. }
  3188. }
  3189. void ST7735_t3::dmaInterrupt2(void) {
  3190. if (_dmaActiveDisplay[2]) {
  3191. _dmaActiveDisplay[2]->process_dma_interrupt();
  3192. }
  3193. }
  3194. //=============================================================================
  3195. // Frame buffer support.
  3196. //=============================================================================
  3197. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3198. #ifdef DEBUG_ASYNC_UPDATE
  3199. extern void dumpDMA_TCD(DMABaseClass *dmabc);
  3200. #endif
  3201. void ST7735_t3::process_dma_interrupt(void) {
  3202. #ifdef DEBUG_ASYNC_LEDS
  3203. digitalWriteFast(DEBUG_PIN_2, HIGH);
  3204. #endif
  3205. // Serial.println(" ST7735_t3::process_dma_interrupt");
  3206. #if defined(__MK66FX1M0__)
  3207. // T3.6
  3208. _dma_frame_count++;
  3209. _dmatx.clearInterrupt();
  3210. // See if we are in continuous mode or not..
  3211. if ((_dma_state & ST77XX_DMA_CONT) == 0) {
  3212. // We are in single refresh mode or the user has called cancel so
  3213. // Lets try to release the CS pin
  3214. while (((_pkinetisk_spi->SR) & (15 << 12)) > _fifo_full_test) ; // wait if FIFO full
  3215. writecommand_last(ST7735_NOP);
  3216. endSPITransaction();
  3217. _dma_state &= ~ST77XX_DMA_ACTIVE;
  3218. _dmaActiveDisplay[_spi_num] = 0; // We don't have a display active any more...
  3219. }
  3220. #elif defined(__IMXRT1062__) // Teensy 4.x
  3221. // T4
  3222. bool still_more_dma = true;
  3223. _dma_sub_frame_count++;
  3224. #if defined(DEBUG_ASYNC_UPDATE)
  3225. Serial.print(".");
  3226. #endif
  3227. if (_dma_sub_frame_count == _dma_cnt_sub_frames_per_frame) {
  3228. #ifdef DEBUG_ASYNC_LEDS
  3229. digitalWriteFast(DEBUG_PIN_3, HIGH);
  3230. #endif
  3231. #if defined(DEBUG_ASYNC_UPDATE)
  3232. Serial.println("*");
  3233. #endif
  3234. // We completed a frame.
  3235. _dma_frame_count++;
  3236. // See if we are logically done
  3237. if (_dma_state & ST77XX_DMA_FINISH) {
  3238. //Serial.println("$");
  3239. still_more_dma = false;
  3240. // We are in single refresh mode or the user has called cancel so
  3241. // Lets try to release the CS pin
  3242. // Lets wait until FIFO is not empty
  3243. //Serial.printf("Before FSR wait: %x %x\n", _pimxrt_spi->FSR, _pimxrt_spi->SR);
  3244. while (_pimxrt_spi->FSR & 0x1f) ; // wait until this one is complete
  3245. //Serial.printf("Before SR busy wait: %x\n", _pimxrt_spi->SR);
  3246. while (_pimxrt_spi->SR & LPSPI_SR_MBF) ; // wait until this one is complete
  3247. _dma_data[_spi_num]._dmatx.clearComplete();
  3248. //Serial.println("Restore FCR");
  3249. _pimxrt_spi->FCR = LPSPI_FCR_TXWATER(15); // _spi_fcr_save; // restore the FSR status...
  3250. _pimxrt_spi->DER = 0; // DMA no longer doing TX (or RX)
  3251. _pimxrt_spi->CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; // actually clear both...
  3252. _pimxrt_spi->SR = 0x3f00; // clear out all of the other status...
  3253. // maybeUpdateTCR(_tcr_dc_assert | LPSPI_TCR_FRAMESZ(7)); // output Command with 8 bits
  3254. // Serial.printf("Output NOP (SR %x CR %x FSR %x FCR %x %x TCR:%x)\n", _pimxrt_spi->SR, _pimxrt_spi->CR, _pimxrt_spi->FSR,
  3255. // _pimxrt_spi->FCR, _spi_fcr_save, _pimxrt_spi->TCR);
  3256. _pending_rx_count = 0; // Make sure count is zero
  3257. // writecommand_last(ST7735_NOP);
  3258. // Serial.println("Do End transaction");
  3259. endSPITransaction();
  3260. _dma_state &= ~(ST77XX_DMA_ACTIVE | ST77XX_DMA_FINISH);
  3261. _dmaActiveDisplay[_spi_num] = 0; // We don't have a display active any more...
  3262. // Serial.println("After End transaction");
  3263. #if defined(DEBUG_ASYNC_UPDATE)
  3264. Serial.println("$");
  3265. #endif
  3266. }
  3267. _dma_sub_frame_count = 0;
  3268. #ifdef DEBUG_ASYNC_LEDS
  3269. digitalWriteFast(DEBUG_PIN_3, LOW);
  3270. #endif
  3271. }
  3272. if (still_more_dma) {
  3273. // we are still in a sub-frame so we need to copy memory down...
  3274. if (_dma_sub_frame_count == (_dma_cnt_sub_frames_per_frame-2)) {
  3275. if ((_dma_state & ST77XX_DMA_CONT) == 0) {
  3276. if (_dma_sub_frame_count & 1) _dma_data[_spi_num]._dmasettings[0].disableOnCompletion();
  3277. else _dma_data[_spi_num]._dmasettings[1].disableOnCompletion();
  3278. #if defined(DEBUG_ASYNC_UPDATE)
  3279. Serial.println("!");
  3280. #endif
  3281. _dma_state |= ST77XX_DMA_FINISH; // let system know we set the finished state
  3282. }
  3283. }
  3284. if (_dma_sub_frame_count & 1) {
  3285. memcpy(_dma_data[_spi_num]._dma_buffer1, &_pfbtft[_dma_pixel_index], _dma_buffer_size*2);
  3286. } else {
  3287. memcpy(_dma_data[_spi_num]._dma_buffer2, &_pfbtft[_dma_pixel_index], _dma_buffer_size*2);
  3288. }
  3289. _dma_pixel_index += _dma_buffer_size;
  3290. if (_dma_pixel_index >= (_count_pixels))
  3291. _dma_pixel_index = 0; // we will wrap around
  3292. }
  3293. _dma_data[_spi_num]._dmatx.clearInterrupt();
  3294. _dma_data[_spi_num]._dmatx.clearComplete();
  3295. asm("dsb");
  3296. #else
  3297. //--------------------------------------------------------------------
  3298. // T3.5...
  3299. _dmarx.clearInterrupt();
  3300. _dmatx.clearComplete();
  3301. _dmarx.clearComplete();
  3302. if (!_dma_count_remaining && !(_dma_state & ST77XX_DMA_CONT)) {
  3303. // The DMA transfers are done.
  3304. _dma_frame_count++;
  3305. #ifdef DEBUG_ASYNC_LEDS
  3306. digitalWriteFast(DEBUG_PIN_3, HIGH);
  3307. #endif
  3308. _pkinetisk_spi->RSER = 0;
  3309. //_pkinetisk_spi->MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F); // clear out the queue
  3310. _pkinetisk_spi->SR = 0xFF0F0000;
  3311. _pkinetisk_spi->CTAR0 &= ~(SPI_CTAR_FMSZ(8)); // Hack restore back to 8 bits
  3312. writecommand_last(ST7735_NOP);
  3313. endSPITransaction();
  3314. _dma_state &= ~ST77XX_DMA_ACTIVE;
  3315. _dmaActiveDisplay[_spi_num] = 0; // We don't have a display active any more...
  3316. #ifdef DEBUG_ASYNC_LEDS
  3317. digitalWriteFast(DEBUG_PIN_3, LOW);
  3318. #endif
  3319. } else {
  3320. uint16_t w;
  3321. if (_dma_count_remaining) { // Still part of one frome.
  3322. _dma_count_remaining -= _dma_write_size_words;
  3323. w = *((uint16_t*)_dmatx.TCD->SADDR);
  3324. _dmatx.TCD->SADDR = (volatile uint8_t*)(_dmatx.TCD->SADDR) + 2;
  3325. } else { // start a new frame
  3326. _dma_frame_count++;
  3327. _dmatx.sourceBuffer(&_pfbtft[1], (_dma_write_size_words-1)*2);
  3328. _dmatx.TCD->SLAST = 0; // Finish with it pointing to next location
  3329. w = _pfbtft[0];
  3330. _dma_count_remaining = _count_pixels - _dma_write_size_words; // how much more to transfer?
  3331. }
  3332. #ifdef DEBUG_ASYNC_UPDATE
  3333. // dumpDMA_TCD(&_dmatx);
  3334. // dumpDMA_TCD(&_dmarx);
  3335. #endif
  3336. _pkinetisk_spi->PUSHR = (w | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT);
  3337. _dmarx.enable();
  3338. _dmatx.enable();
  3339. }
  3340. #endif
  3341. #ifdef DEBUG_ASYNC_LEDS
  3342. digitalWriteFast(DEBUG_PIN_2, LOW);
  3343. #endif
  3344. }
  3345. //=======================================================================
  3346. // Add optinal support for using frame buffer to speed up complex outputs
  3347. //=======================================================================
  3348. void ST7735_t3::setFrameBuffer(uint16_t *frame_buffer)
  3349. {
  3350. _pfbtft = frame_buffer;
  3351. // we may not know the size of it, if called before init.
  3352. /* if (_pfbtft != NULL) {
  3353. memset(_pfbtft, 0, _screenWidth*_screenHeight*2);
  3354. } */
  3355. }
  3356. uint8_t ST7735_t3::useFrameBuffer(boolean b) // use the frame buffer? First call will allocate
  3357. {
  3358. if (b) {
  3359. // First see if we need to allocate buffer
  3360. _count_pixels = _width * _height; // We may have called setFrameBuffer before we know with and height...
  3361. if (_pfbtft == NULL) {
  3362. // Hack to start frame buffer on 32 byte boundary
  3363. // Note: If called before init maybe larger than we need
  3364. _we_allocated_buffer = (uint16_t *)malloc(_count_pixels*2+32);
  3365. if (_we_allocated_buffer == NULL)
  3366. return 0; // failed
  3367. _pfbtft = (uint16_t*) (((uintptr_t)_we_allocated_buffer + 32) & ~ ((uintptr_t) (31)));
  3368. memset(_pfbtft, 0, _count_pixels*2);
  3369. }
  3370. _use_fbtft = 1;
  3371. } else
  3372. _use_fbtft = 0;
  3373. return _use_fbtft;
  3374. }
  3375. void ST7735_t3::freeFrameBuffer(void) // explicit call to release the buffer
  3376. {
  3377. if (_we_allocated_buffer) {
  3378. free(_we_allocated_buffer);
  3379. _pfbtft = NULL;
  3380. _use_fbtft = 0; // make sure the use is turned off
  3381. _we_allocated_buffer = NULL;
  3382. }
  3383. }
  3384. void ST7735_t3::updateScreen(void) // call to say update the screen now.
  3385. {
  3386. // Not sure if better here to check flag or check existence of buffer.
  3387. // Will go by buffer as maybe can do interesting things?
  3388. if (_use_fbtft) {
  3389. beginSPITransaction();
  3390. // Doing full window.
  3391. setAddr(0, 0, _width-1, _height-1);
  3392. writecommand(ST7735_RAMWR);
  3393. // BUGBUG doing as one shot. Not sure if should or not or do like
  3394. // main code and break up into transactions...
  3395. uint16_t *pfbtft_end = &_pfbtft[(_count_pixels)-1]; // setup
  3396. uint16_t *pftbft = _pfbtft;
  3397. // Quick write out the data;
  3398. while (pftbft < pfbtft_end) {
  3399. writedata16(*pftbft++);
  3400. }
  3401. writedata16_last(*pftbft);
  3402. endSPITransaction();
  3403. }
  3404. }
  3405. #ifdef DEBUG_ASYNC_UPDATE
  3406. void dumpDMA_TCD(DMABaseClass *dmabc)
  3407. {
  3408. Serial.printf("%x %x:", (uint32_t)dmabc, (uint32_t)dmabc->TCD);
  3409. Serial.printf("SA:%x SO:%d AT:%x NB:%x SL:%d DA:%x DO: %d CI:%x DL:%x CS:%x BI:%x\n", (uint32_t)dmabc->TCD->SADDR,
  3410. dmabc->TCD->SOFF, dmabc->TCD->ATTR, dmabc->TCD->NBYTES, dmabc->TCD->SLAST, (uint32_t)dmabc->TCD->DADDR,
  3411. dmabc->TCD->DOFF, dmabc->TCD->CITER, dmabc->TCD->DLASTSGA, dmabc->TCD->CSR, dmabc->TCD->BITER);
  3412. }
  3413. #endif
  3414. //==============================================
  3415. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3416. void ST7735_t3::initDMASettings(void)
  3417. {
  3418. // Serial.printf("initDMASettings called %d\n", _dma_state);
  3419. if (_dma_state) { // should test for init, but...
  3420. return; // we already init this.
  3421. }
  3422. #ifdef DEBUG_ASYNC_LEDS
  3423. pinMode(DEBUG_PIN_1, OUTPUT); digitalWrite(DEBUG_PIN_1, LOW);
  3424. pinMode(DEBUG_PIN_2, OUTPUT); digitalWrite(DEBUG_PIN_2, LOW);
  3425. pinMode(DEBUG_PIN_3, OUTPUT); digitalWrite(DEBUG_PIN_3, LOW);
  3426. #endif
  3427. //Serial.println("InitDMASettings");
  3428. uint8_t dmaTXevent = _spi_hardware->tx_dma_channel;
  3429. _count_pixels = _width*_height; // cache away the size of the display.
  3430. // Serial.printf("cbDisplay: %u COUNT_WORDS_WRITE:%d(%x) spi_num:%d\n", _count_pixels, COUNT_WORDS_WRITE, COUNT_WORDS_WRITE, _spi_num);
  3431. #if defined(__MK66FX1M0__)
  3432. uint8_t cnt_dma_settings = 2; // how many do we need for this display?
  3433. uint32_t COUNT_WORDS_WRITE = (_count_pixels) / 2;
  3434. // The 240x320 display requires us to expand to another DMA setting.
  3435. if (COUNT_WORDS_WRITE >= 32768) {
  3436. COUNT_WORDS_WRITE = (_count_pixels) / 3;
  3437. cnt_dma_settings = 3;
  3438. }
  3439. // T3.6
  3440. //Serial.printf("CWW: %d %d %d\n", CBALLOC, SCREEN_DMA_NUM_SETTINGS, count_words_write);
  3441. // Now lets setup DMA access to this memory...
  3442. _cnt_dma_settings = cnt_dma_settings; // save away code that needs to update
  3443. _dmasettings[_spi_num][0].sourceBuffer(&_pfbtft[1], (COUNT_WORDS_WRITE-1)*2);
  3444. _dmasettings[_spi_num][0].destination(_pkinetisk_spi->PUSHR);
  3445. // Hack to reset the destination to only output 2 bytes.
  3446. _dmasettings[_spi_num][0].TCD->ATTR_DST = 1;
  3447. _dmasettings[_spi_num][0].replaceSettingsOnCompletion(_dmasettings[_spi_num][1]);
  3448. _dmasettings[_spi_num][1].sourceBuffer(&_pfbtft[COUNT_WORDS_WRITE], COUNT_WORDS_WRITE*2);
  3449. _dmasettings[_spi_num][1].destination(_pkinetisk_spi->PUSHR);
  3450. _dmasettings[_spi_num][1].TCD->ATTR_DST = 1;
  3451. _dmasettings[_spi_num][1].replaceSettingsOnCompletion(_dmasettings[_spi_num][2]);
  3452. if (cnt_dma_settings == 3) {
  3453. _dmasettings[_spi_num][2].sourceBuffer(&_pfbtft[COUNT_WORDS_WRITE*2], COUNT_WORDS_WRITE*2);
  3454. _dmasettings[_spi_num][2].destination(_pkinetisk_spi->PUSHR);
  3455. _dmasettings[_spi_num][2].TCD->ATTR_DST = 1;
  3456. _dmasettings[_spi_num][2].replaceSettingsOnCompletion(_dmasettings[_spi_num][3]);
  3457. }
  3458. // Sort of hack - but wrap around to output the first word again.
  3459. _dmasettings[_spi_num][cnt_dma_settings].sourceBuffer(_pfbtft, 2);
  3460. _dmasettings[_spi_num][cnt_dma_settings].destination(_pkinetisk_spi->PUSHR);
  3461. _dmasettings[_spi_num][cnt_dma_settings].TCD->ATTR_DST = 1;
  3462. _dmasettings[_spi_num][cnt_dma_settings].replaceSettingsOnCompletion(_dmasettings[_spi_num][0]);
  3463. // Setup DMA main object
  3464. //Serial.println("Setup _dmatx");
  3465. _dmatx.begin(true);
  3466. _dmatx.triggerAtHardwareEvent(dmaTXevent);
  3467. _dmatx = _dmasettings[_spi_num][0];
  3468. // probably could use const table of functio_ns...
  3469. if (_spi_num == 0) _dmatx.attachInterrupt(dmaInterrupt);
  3470. else if (_spi_num == 1) _dmatx.attachInterrupt(dmaInterrupt1);
  3471. else _dmatx.attachInterrupt(dmaInterrupt2);
  3472. #elif defined(__IMXRT1062__) // Teensy 4.x
  3473. // See if moving the frame buffer to other memory that is not cached helps out
  3474. // to remove tearing and the like...I know with 256 it will be either 256 or 248...
  3475. _dma_buffer_size = ST77XX_DMA_BUFFER_SIZE;
  3476. _dma_cnt_sub_frames_per_frame = (_count_pixels) / _dma_buffer_size;
  3477. while ((_dma_cnt_sub_frames_per_frame * _dma_buffer_size) != (_count_pixels)) {
  3478. _dma_buffer_size--;
  3479. _dma_cnt_sub_frames_per_frame = (_count_pixels) / _dma_buffer_size;
  3480. }
  3481. #if defined(DEBUG_ASYNC_UPDATE)
  3482. Serial.printf("DMA Init buf size: %d sub frames:%d spi num: %d\n", _dma_buffer_size, _dma_cnt_sub_frames_per_frame, _spi_num);
  3483. #endif
  3484. _dma_data[_spi_num]._dmasettings[0].sourceBuffer(_dma_data[_spi_num]._dma_buffer1, _dma_buffer_size*2);
  3485. _dma_data[_spi_num]._dmasettings[0].destination(_pimxrt_spi->TDR);
  3486. _dma_data[_spi_num]._dmasettings[0].TCD->ATTR_DST = 1;
  3487. _dma_data[_spi_num]._dmasettings[0].replaceSettingsOnCompletion(_dma_data[_spi_num]._dmasettings[1]);
  3488. _dma_data[_spi_num]._dmasettings[0].interruptAtCompletion();
  3489. _dma_data[_spi_num]._dmasettings[1].sourceBuffer(_dma_data[_spi_num]._dma_buffer2, _dma_buffer_size*2);
  3490. _dma_data[_spi_num]._dmasettings[1].destination(_pimxrt_spi->TDR);
  3491. _dma_data[_spi_num]._dmasettings[1].TCD->ATTR_DST = 1;
  3492. _dma_data[_spi_num]._dmasettings[1].replaceSettingsOnCompletion(_dma_data[_spi_num]._dmasettings[0]);
  3493. _dma_data[_spi_num]._dmasettings[1].interruptAtCompletion();
  3494. // Setup DMA main object
  3495. //Serial.println("Setup _dmatx");
  3496. // Serial.println("DMA initDMASettings - before dmatx");
  3497. _dma_data[_spi_num]._dmatx.begin(true);
  3498. _dma_data[_spi_num]._dmatx.triggerAtHardwareEvent(dmaTXevent);
  3499. _dma_data[_spi_num]._dmatx = _dma_data[_spi_num]._dmasettings[0];
  3500. // probably could use const table of functions...
  3501. if (_spi_num == 0) _dma_data[_spi_num]._dmatx.attachInterrupt(dmaInterrupt);
  3502. else if (_spi_num == 1) _dma_data[_spi_num]._dmatx.attachInterrupt(dmaInterrupt1);
  3503. else _dma_data[_spi_num]._dmatx.attachInterrupt(dmaInterrupt2);
  3504. #else
  3505. // T3.5
  3506. // Lets setup the write size. For SPI we can use up to 32767 so same size as we use on T3.6...
  3507. // But SPI1 and SPI2 max of 511. We will use 480 in that case as even divider...
  3508. uint32_t COUNT_WORDS_WRITE = (_count_pixels) / 2;
  3509. // The 240x320 display requires us to expand to another DMA setting.
  3510. if (COUNT_WORDS_WRITE >= 32768) {
  3511. COUNT_WORDS_WRITE = (_count_pixels) / 3;
  3512. }
  3513. _dmarx.disable();
  3514. _dmarx.source(_pkinetisk_spi->POPR);
  3515. _dmarx.TCD->ATTR_SRC = 1;
  3516. _dmarx.destination(_dma_dummy_rx);
  3517. _dmarx.disableOnCompletion();
  3518. _dmarx.triggerAtHardwareEvent(_spi_hardware->rx_dma_channel);
  3519. // probably could use const table of functions...
  3520. if (_spi_num == 0) _dmarx.attachInterrupt(dmaInterrupt);
  3521. else if (_spi_num == 1) _dmarx.attachInterrupt(dmaInterrupt1);
  3522. else _dmarx.attachInterrupt(dmaInterrupt2);
  3523. _dmarx.interruptAtCompletion();
  3524. // We may be using settings chain here so lets set it up.
  3525. // Now lets setup TX chain. Note if trigger TX is not set
  3526. // we need to have the RX do it for us.
  3527. _dmatx.disable();
  3528. _dmatx.destination(_pkinetisk_spi->PUSHR);
  3529. _dmatx.TCD->ATTR_DST = 1;
  3530. _dmatx.disableOnCompletion();
  3531. // Current SPIN, has both RX/TX same for SPI1/2 so just know f
  3532. if (_pspi == &SPI) {
  3533. _dmatx.triggerAtHardwareEvent(dmaTXevent);
  3534. _dma_write_size_words = COUNT_WORDS_WRITE;
  3535. } else {
  3536. _dma_write_size_words = 480;
  3537. _dmatx.triggerAtTransfersOf(_dmarx);
  3538. }
  3539. //Serial.printf("Init DMA Settings: TX:%d size:%d\n", dmaTXevent, _dma_write_size_words);
  3540. #endif
  3541. _dma_state = ST77XX_DMA_INIT; // Should be first thing set!
  3542. // Serial.println("DMA initDMASettings - end");
  3543. }
  3544. #endif
  3545. void ST7735_t3::dumpDMASettings() {
  3546. #ifdef DEBUG_ASYNC_UPDATE
  3547. #if defined(__MK66FX1M0__)
  3548. // T3.6
  3549. Serial.printf("DMA dump TCDs %d\n", _dmatx.channel);
  3550. dumpDMA_TCD(&_dmatx);
  3551. dumpDMA_TCD(&_dmasettings[_spi_num][0]);
  3552. dumpDMA_TCD(&_dmasettings[_spi_num][1]);
  3553. dumpDMA_TCD(&_dmasettings[_spi_num][2]);
  3554. dumpDMA_TCD(&_dmasettings[_spi_num][3]);
  3555. #elif defined(__IMXRT1062__) // Teensy 4.x
  3556. // Serial.printf("DMA dump TCDs %d\n", _dmatx.channel);
  3557. dumpDMA_TCD(&_dma_data[_spi_num]._dmatx);
  3558. dumpDMA_TCD(&_dma_data[_spi_num]._dmasettings[0]);
  3559. dumpDMA_TCD(&_dma_data[_spi_num]._dmasettings[1]);
  3560. #else
  3561. Serial.printf("DMA dump TX:%d RX:%d\n", _dmatx.channel, _dmarx.channel);
  3562. dumpDMA_TCD(&_dmatx);
  3563. dumpDMA_TCD(&_dmarx);
  3564. #endif
  3565. #endif
  3566. }
  3567. bool ST7735_t3::updateScreenAsync(bool update_cont) // call to say update the screen now.
  3568. {
  3569. // Not sure if better here to check flag or check existence of buffer.
  3570. // Will go by buffer as maybe can do interesting things?
  3571. // BUGBUG:: only handles full screen so bail on the rest of it...
  3572. // Also bail if we are working with a hardware SPI port.
  3573. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3574. if (!_use_fbtft || !_pspi) return false;
  3575. #if defined(__MK64FX512__) || defined(__MK20DX256__) // If T3.5 only allow on SPI...
  3576. // The T3.5 DMA to SPI has issues with preserving stuff like we want 16 bit mode
  3577. // and we want CS to stay on... So hack it. We will turn off using CS for the CS
  3578. // pin.
  3579. if (!cspin && (_cs != 0xff)) {
  3580. //Serial.println("***T3.5 CS Pin hack");
  3581. pcs_data = 0;
  3582. pcs_command = pcs_data | _pspi->setCS(_rs);
  3583. pinMode(_cs, OUTPUT);
  3584. cspin = portOutputRegister(digitalPinToPort(_cs));
  3585. *cspin = 1;
  3586. }
  3587. #endif
  3588. #ifdef DEBUG_ASYNC_LEDS
  3589. digitalWriteFast(DEBUG_PIN_1, HIGH);
  3590. #endif
  3591. // Init DMA settings.
  3592. initDMASettings();
  3593. // Don't start one if already active.
  3594. if (_dma_state & ST77XX_DMA_ACTIVE) {
  3595. #ifdef DEBUG_ASYNC_LEDS
  3596. digitalWriteFast(DEBUG_PIN_1, LOW);
  3597. #endif
  3598. return false;
  3599. }
  3600. #if defined(__MK66FX1M0__)
  3601. //==========================================
  3602. // T3.6
  3603. //==========================================
  3604. if (update_cont) {
  3605. // Try to link in #3 into the chain (_cnt_dma_settings)
  3606. _dmasettings[_spi_num][_cnt_dma_settings-1].replaceSettingsOnCompletion(_dmasettings[_spi_num][_cnt_dma_settings]);
  3607. _dmasettings[_spi_num][_cnt_dma_settings-1].TCD->CSR &= ~(DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_DREQ); // Don't interrupt on this one...
  3608. _dmasettings[_spi_num][_cnt_dma_settings].interruptAtCompletion();
  3609. _dmasettings[_spi_num][_cnt_dma_settings].TCD->CSR &= ~(DMA_TCD_CSR_DREQ); // Don't disable on this one
  3610. _dma_state |= ST77XX_DMA_CONT;
  3611. } else {
  3612. // In this case we will only run through once...
  3613. _dmasettings[_spi_num][_cnt_dma_settings-1].replaceSettingsOnCompletion(_dmasettings[_spi_num][0]);
  3614. _dmasettings[_spi_num][_cnt_dma_settings-1].interruptAtCompletion();
  3615. _dmasettings[_spi_num][_cnt_dma_settings-1].disableOnCompletion();
  3616. _dma_state &= ~ST77XX_DMA_CONT;
  3617. }
  3618. #ifdef DEBUG_ASYNC_UPDATE
  3619. dumpDMASettings();
  3620. #endif
  3621. beginSPITransaction();
  3622. // Doing full window.
  3623. setAddr(0, 0, _width-1, _height-1);
  3624. writecommand(ST7735_RAMWR);
  3625. // Write the first Word out before enter DMA as to setup the proper CS/DC/Continue flaugs
  3626. writedata16(*_pfbtft);
  3627. // now lets start up the DMA
  3628. // volatile uint16_t biter = _dmatx.TCD->BITER;
  3629. //DMA_CDNE_CDNE(_dmatx.channel);
  3630. // _dmatx = _dmasettings[0];
  3631. // _dmatx.TCD->BITER = biter;
  3632. _dma_frame_count = 0; // Set frame count back to zero.
  3633. _dmaActiveDisplay[_spi_num] = this;
  3634. _dma_state |= ST77XX_DMA_ACTIVE;
  3635. _pkinetisk_spi->RSER |= SPI_RSER_TFFF_DIRS | SPI_RSER_TFFF_RE; // Set DMA Interrupt Request Select and Enable register
  3636. _pkinetisk_spi->MCR &= ~SPI_MCR_HALT; //Start transfers.
  3637. _dmatx.enable();
  3638. //==========================================
  3639. // T4
  3640. //==========================================
  3641. #elif defined(__IMXRT1062__) // Teensy 4.x
  3642. // TODO
  3643. // Start off remove disable on completion from both...
  3644. // it will be the ISR that disables it...
  3645. _dma_data[_spi_num]._dmasettings[0].TCD->CSR &= ~( DMA_TCD_CSR_DREQ);
  3646. _dma_data[_spi_num]._dmasettings[1].TCD->CSR &= ~( DMA_TCD_CSR_DREQ);
  3647. #ifdef DEBUG_ASYNC_UPDATE
  3648. dumpDMASettings();
  3649. #endif
  3650. // Lets copy first parts of frame buffer into our two sub-frames
  3651. memcpy(_dma_data[_spi_num]._dma_buffer1, _pfbtft, _dma_buffer_size*2);
  3652. memcpy(_dma_data[_spi_num]._dma_buffer2, &_pfbtft[_dma_buffer_size], _dma_buffer_size*2);
  3653. _dma_pixel_index = _dma_buffer_size*2;
  3654. _dma_sub_frame_count = 0; //
  3655. beginSPITransaction();
  3656. // Doing full window.
  3657. setAddr(0, 0, _width-1, _height-1);
  3658. writecommand_last(ST7735_RAMWR);
  3659. // Update TCR to 16 bit mode. and output the first entry.
  3660. _spi_fcr_save = _pimxrt_spi->FCR; // remember the FCR
  3661. _pimxrt_spi->FCR = 0; // clear water marks...
  3662. maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(15) | LPSPI_TCR_RXMSK /*| LPSPI_TCR_CONT*/);
  3663. // _pimxrt_spi->CFGR1 |= LPSPI_CFGR1_NOSTALL;
  3664. // maybeUpdateTCR(_tcr_dc_not_assert | LPSPI_TCR_FRAMESZ(15) | LPSPI_TCR_CONT);
  3665. _pimxrt_spi->DER = LPSPI_DER_TDDE;
  3666. _pimxrt_spi->SR = 0x3f00; // clear out all of the other status...
  3667. _dma_data[_spi_num]._dmatx.triggerAtHardwareEvent( _spi_hardware->tx_dma_channel );
  3668. _dma_data[_spi_num]._dmatx = _dma_data[_spi_num]._dmasettings[0];
  3669. _dma_data[_spi_num]._dmatx.begin(false);
  3670. _dma_data[_spi_num]._dmatx.enable();
  3671. _dma_frame_count = 0; // Set frame count back to zero.
  3672. _dmaActiveDisplay[_spi_num] = this;
  3673. if (update_cont) {
  3674. _dma_state |= ST77XX_DMA_CONT;
  3675. } else {
  3676. _dma_state &= ~ST77XX_DMA_CONT;
  3677. }
  3678. _dma_state |= ST77XX_DMA_ACTIVE;
  3679. #else
  3680. //==========================================
  3681. // T3.5
  3682. //==========================================
  3683. // lets setup the initial pointers.
  3684. _dmatx.sourceBuffer(&_pfbtft[1], (_dma_write_size_words-1)*2);
  3685. _dmatx.TCD->SLAST = 0; // Finish with it pointing to next location
  3686. _dmarx.transferCount(_dma_write_size_words);
  3687. _dma_count_remaining = _count_pixels - _dma_write_size_words; // how much more to transfer?
  3688. //Serial.printf("SPI1/2 - TC:%d TR:%d\n", _dma_write_size_words, _dma_count_remaining);
  3689. #ifdef DEBUG_ASYNC_UPDATE
  3690. dumpDMASettings();
  3691. #endif
  3692. beginSPITransaction();
  3693. // Doing full window.
  3694. setAddr(0, 0, _width-1, _height-1);
  3695. writecommand(ST7735_RAMWR);
  3696. // Write the first Word out before enter DMA as to setup the proper CS/DC/Continue flaugs
  3697. // On T3.5 DMA only appears to work with CTAR 0 so hack it up...
  3698. _pkinetisk_spi->CTAR0 |= SPI_CTAR_FMSZ(8); // Hack convert from 8 bit to 16 bit...
  3699. _pkinetisk_spi->MCR = SPI_MCR_MSTR | SPI_MCR_CLR_RXF | SPI_MCR_PCSIS(0x1F);
  3700. _pkinetisk_spi->SR = 0xFF0F0000;
  3701. // Lets try to output the first byte to make sure that we are in 16 bit mode...
  3702. _pkinetisk_spi->PUSHR = *_pfbtft | SPI_PUSHR_CTAS(0) | SPI_PUSHR_CONT;
  3703. if (_pspi == &SPI) {
  3704. // SPI - has both TX and RX so use it
  3705. _pkinetisk_spi->RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS | SPI_RSER_TFFF_RE | SPI_RSER_TFFF_DIRS;
  3706. _dmarx.enable();
  3707. _dmatx.enable();
  3708. } else {
  3709. _pkinetisk_spi->RSER = SPI_RSER_RFDF_RE | SPI_RSER_RFDF_DIRS ;
  3710. _dmatx.triggerAtTransfersOf(_dmarx);
  3711. _dmatx.enable();
  3712. _dmarx.enable();
  3713. }
  3714. _dma_frame_count = 0; // Set frame count back to zero.
  3715. _dmaActiveDisplay[_spi_num] = this;
  3716. if (update_cont) {
  3717. _dma_state |= ST77XX_DMA_CONT;
  3718. } else {
  3719. _dma_state &= ~ST77XX_DMA_CONT;
  3720. }
  3721. _dma_state |= ST77XX_DMA_ACTIVE;
  3722. #endif
  3723. #ifdef DEBUG_ASYNC_LEDS
  3724. digitalWriteFast(DEBUG_PIN_1, LOW);
  3725. #endif
  3726. return true;
  3727. #else
  3728. return false; // no frame buffer so will never start...
  3729. #endif
  3730. }
  3731. void ST7735_t3::endUpdateAsync() {
  3732. // make sure it is on
  3733. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3734. if (_dma_state & ST77XX_DMA_CONT) {
  3735. _dma_state &= ~ST77XX_DMA_CONT; // Turn of the continueous mode
  3736. #if defined(__MK66FX1M0__)
  3737. _dmasettings[_spi_num][_cnt_dma_settings].disableOnCompletion();
  3738. #endif
  3739. }
  3740. #endif
  3741. }
  3742. void ST7735_t3::waitUpdateAsyncComplete(void)
  3743. {
  3744. #ifdef ENABLE_ST77XX_FRAMEBUFFER
  3745. #ifdef DEBUG_ASYNC_LEDS
  3746. digitalWriteFast(DEBUG_PIN_3, HIGH);
  3747. #endif
  3748. while ((_dma_state & ST77XX_DMA_ACTIVE)) {
  3749. // asm volatile("wfi");
  3750. };
  3751. #ifdef DEBUG_ASYNC_LEDS
  3752. digitalWriteFast(DEBUG_PIN_3, LOW);
  3753. #endif
  3754. #endif
  3755. }
  3756. #endif
  3757. #endif