26 #include "freertos/FreeRTOS.h" 27 #include "freertos/task.h" 35 #define TFT_UPDATETASK_STACK 1024 36 #define TFT_UPDATETASK_PRIORITY 5 38 #define TFT_BACKGROUND_PRIMITIVE_TIMEOUT 10000 // uS 40 #define TFT_SPI_WRITE_FREQUENCY 40000000 41 #define TFT_SPI_MODE 3 42 #define TFT_DMACHANNEL 2 55 inline uint16_t preparePixel(RGB888
const & px)
57 return ((uint16_t)(px.G & 0xe0) >> 5) |
58 ((uint16_t)(px.R & 0xf8)) |
59 ((uint16_t)(px.B & 0xf8) << 5) |
60 ((uint16_t)(px.G & 0x1c) << 11);
64 inline RGB888 nativeToRGB888(uint16_t pattern)
68 ((pattern & 7) << 5) | ((pattern & 0xe000) >> 11),
69 ((pattern & 0x1f00) >> 5)
74 inline RGBA8888 nativeToRGBA8888(uint16_t pattern)
78 ((pattern & 7) << 5) | ((pattern & 0xe000) >> 11),
79 ((pattern & 0x1f00) >> 5),
85 inline uint16_t RGBA2222toNative(uint8_t rgba2222)
87 return preparePixel(RGB888((rgba2222 & 3) * 85, ((rgba2222 >> 2) & 3) * 85, ((rgba2222 >> 4) & 3) * 85));
91 inline uint16_t RGBA8888toNative(
RGBA8888 const & rgba8888)
93 return preparePixel(RGB888(rgba8888.R, rgba8888.G, rgba8888.B));
97 TFTController::TFTController()
102 m_SPIDevHandle(nullptr),
104 m_controllerWidth(240),
105 m_controllerHeight(320),
108 m_updateTaskHandle(nullptr),
109 m_updateTaskRunning(false),
111 m_reverseHorizontal(false)
116 TFTController::~TFTController()
123 void TFTController::setupGPIO()
126 configureGPIO(m_DC, GPIO_MODE_OUTPUT);
127 gpio_set_level(m_DC, 1);
130 if (m_RESX != GPIO_UNUSED) {
131 configureGPIO(m_RESX, GPIO_MODE_OUTPUT);
132 gpio_set_level(m_RESX, 1);
136 if (m_CS != GPIO_UNUSED) {
137 configureGPIO(m_CS, GPIO_MODE_OUTPUT);
138 gpio_set_level(m_CS, 1);
146 void TFTController::begin(SPIClass * spi, gpio_num_t DC, gpio_num_t RESX, gpio_num_t CS)
164 void TFTController::begin(SPIClass * spi,
int DC,
int RESX,
int CS)
166 begin(spi, int2gpio(DC), int2gpio(RESX), int2gpio(CS));
173 void TFTController::begin(
int SCK,
int MOSI,
int DC,
int RESX,
int CS,
int host)
175 m_SPIHost = (spi_host_device_t)host;
176 m_SCK = int2gpio(SCK);
177 m_MOSI = int2gpio(MOSI);
179 m_RESX = int2gpio(RESX);
187 void TFTController::begin()
189 begin(18, 23, 22, 21, 5, VSPI_HOST);
193 void TFTController::end()
195 if (m_updateTaskHandle)
196 vTaskDelete(m_updateTaskHandle);
197 m_updateTaskHandle =
nullptr;
205 void TFTController::setResolution(
char const * modeline,
int viewPortWidth,
int viewPortHeight,
bool doubleBuffered)
208 int pos = 0, swidth, sheight;
209 int count = sscanf(modeline,
"\"%[^\"]\" %d %d %n", label, &swidth, &sheight, &pos);
210 if (count != 3 || pos == 0)
213 m_screenWidth = swidth;
214 m_screenHeight = sheight;
219 setScreenSize(m_screenWidth, m_screenHeight);
221 setDoubleBuffered(doubleBuffered);
223 m_viewPortWidth = viewPortWidth < 0 ? m_screenWidth : viewPortWidth;
224 m_viewPortHeight = viewPortHeight < 0 ? m_screenHeight : viewPortHeight;
226 m_rot0ViewPortWidth = m_viewPortWidth;
227 m_rot0ViewPortHeight = m_viewPortHeight;
235 xTaskCreate(&updateTaskFunc,
"", TFT_UPDATETASK_STACK,
this, TFT_UPDATETASK_PRIORITY, &m_updateTaskHandle);
238 m_updateTaskFuncSuspended = 0;
242 void TFTController::setScreenCol(
int value)
244 if (value != m_screenCol) {
245 m_screenCol = iclamp(value, 0, m_viewPortWidth - m_screenWidth);
246 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
252 void TFTController::setScreenRow(
int value)
254 if (value != m_screenRow) {
255 m_screenRow = iclamp(value, 0, m_viewPortHeight - m_screenHeight);
256 Primitive p(PrimitiveCmd::Refresh,
Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
262 void TFTController::hardReset()
264 if (m_RESX != GPIO_UNUSED) {
267 configureGPIO(m_RESX, GPIO_MODE_OUTPUT);
268 gpio_set_level(m_RESX, 1);
269 vTaskDelay(5 / portTICK_PERIOD_MS);
270 gpio_set_level(m_RESX, 0);
271 vTaskDelay(20 / portTICK_PERIOD_MS);
272 gpio_set_level(m_RESX, 1);
276 vTaskDelay(150 / portTICK_PERIOD_MS);
281 void TFTController::setupOrientation()
284 m_viewPortWidth = m_rot0ViewPortWidth;
285 m_viewPortHeight = m_rot0ViewPortHeight;
288 uint8_t MX = m_reverseHorizontal ? 0x40 : 0;
289 uint8_t madclt = 0x08 | MX;
290 switch (m_orientation) {
291 case TFTOrientation::Rotate90:
292 tswap(m_viewPortWidth, m_viewPortHeight);
296 case TFTOrientation::Rotate180:
299 m_rotOffsetY = m_controllerHeight - m_viewPortHeight;
300 m_rotOffsetX = m_controllerWidth - m_viewPortWidth;
302 case TFTOrientation::Rotate270:
303 tswap(m_viewPortWidth, m_viewPortHeight);
304 madclt |= 0x20 | 0x80;
305 m_rotOffsetX = m_controllerHeight - m_viewPortWidth;
311 writeCommand(TFT_MADCTL);
319 p.cmd = PrimitiveCmd::Reset;
326 if (m_orientation != value || force) {
327 suspendBackgroundPrimitiveExecution();
328 m_orientation = value;
332 resumeBackgroundPrimitiveExecution();
338 void TFTController::setReverseHorizontal(
bool value)
340 m_reverseHorizontal = value;
341 setOrientation(m_orientation,
true);
345 void TFTController::SPIBegin()
352 spi_bus_config_t busconf;
353 memset(&busconf, 0,
sizeof(busconf));
354 busconf.mosi_io_num = m_MOSI;
355 busconf.miso_io_num = -1;
356 busconf.sclk_io_num = m_SCK;
357 busconf.quadwp_io_num = -1;
358 busconf.quadhd_io_num = -1;
359 busconf.flags = SPICOMMON_BUSFLAG_MASTER;
360 auto r = spi_bus_initialize(m_SPIHost, &busconf, TFT_DMACHANNEL);
361 if (r == ESP_OK || r == ESP_ERR_INVALID_STATE) {
362 spi_device_interface_config_t devconf;
363 memset(&devconf, 0,
sizeof(devconf));
364 devconf.mode = TFT_SPI_MODE;
365 devconf.clock_speed_hz = TFT_SPI_WRITE_FREQUENCY;
366 devconf.spics_io_num = -1;
368 devconf.queue_size = 1;
369 spi_bus_add_device(m_SPIHost, &devconf, &m_SPIDevHandle);
372 if (m_updateTaskFuncSuspended)
373 resumeBackgroundPrimitiveExecution();
377 void TFTController::SPIEnd()
384 suspendBackgroundPrimitiveExecution();
386 if (m_SPIDevHandle) {
387 spi_bus_remove_device(m_SPIDevHandle);
388 m_SPIDevHandle =
nullptr;
389 spi_bus_free(m_SPIHost);
394 void TFTController::SPIBeginWrite()
398 m_spi->beginTransaction(SPISettings(TFT_SPI_WRITE_FREQUENCY, SPI_MSBFIRST, TFT_SPI_MODE));
402 if (m_SPIDevHandle) {
403 spi_device_acquire_bus(m_SPIDevHandle, portMAX_DELAY);
406 if (m_CS != GPIO_UNUSED) {
407 gpio_set_level(m_CS, 0);
412 void TFTController::SPIEndWrite()
414 if (m_CS != GPIO_UNUSED) {
415 gpio_set_level(m_CS, 1);
419 gpio_set_level(m_DC, 1);
423 m_spi->endTransaction();
427 if (m_SPIDevHandle) {
428 spi_device_release_bus(m_SPIDevHandle);
433 void TFTController::SPIWriteByte(uint8_t
data)
441 if (m_SPIDevHandle) {
442 spi_transaction_t ta;
443 ta.flags = SPI_TRANS_USE_TXDATA;
446 ta.tx_data[0] =
data;
447 ta.rx_buffer =
nullptr;
448 spi_device_polling_transmit(m_SPIDevHandle, &ta);
453 void TFTController::SPIWriteWord(uint16_t
data)
457 m_spi->write(
data >> 8);
458 m_spi->write(
data & 0xff);
462 if (m_SPIDevHandle) {
463 spi_transaction_t ta;
464 ta.flags = SPI_TRANS_USE_TXDATA;
467 ta.tx_data[0] =
data >> 8;
468 ta.tx_data[1] =
data & 0xff;
469 ta.rx_buffer =
nullptr;
470 spi_device_polling_transmit(m_SPIDevHandle, &ta);
475 void TFTController::SPIWriteBuffer(
void *
data,
size_t size)
479 m_spi->writeBytes((uint8_t*)
data, size);
483 if (m_SPIDevHandle) {
484 spi_transaction_t ta;
486 ta.length = 8 * size;
489 ta.rx_buffer =
nullptr;
490 spi_device_polling_transmit(m_SPIDevHandle, &ta);
495 void TFTController::writeCommand(uint8_t cmd)
497 gpio_set_level(m_DC, 0);
502 void TFTController::writeByte(uint8_t
data)
504 gpio_set_level(m_DC, 1);
509 void TFTController::writeData(
void *
data,
size_t size)
511 gpio_set_level(m_DC, 1);
512 SPIWriteBuffer(
data, size);
517 void TFTController::writeWord(uint16_t
data)
519 gpio_set_level(m_DC, 1);
524 void TFTController::sendRefresh()
526 Primitive p(PrimitiveCmd::Refresh, Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
531 void TFTController::sendScreenBuffer(Rect updateRect)
535 updateRect = updateRect.intersection(Rect(0, 0, m_viewPortWidth - 1, m_viewPortHeight - 1));
538 writeCommand(TFT_CASET);
539 writeWord(m_rotOffsetX + updateRect.X1);
540 writeWord(m_rotOffsetX + updateRect.X2);
543 writeCommand(TFT_RASET);
544 writeWord(m_rotOffsetY + updateRect.Y1);
545 writeWord(m_rotOffsetY + updateRect.Y2);
547 writeCommand(TFT_RAMWR);
548 const int width = updateRect.width();
549 for (
int row = updateRect.Y1; row <= updateRect.Y2; ++row) {
550 writeData(m_viewPort[row] + updateRect.X1,
sizeof(uint16_t) *
width);
557 void TFTController::allocViewPort()
559 m_viewPort = (uint16_t**) heap_caps_malloc(m_viewPortHeight *
sizeof(uint16_t*), MALLOC_CAP_32BIT | MALLOC_CAP_INTERNAL);
560 for (
int i = 0; i < m_viewPortHeight; ++i) {
561 m_viewPort[i] = (uint16_t*) heap_caps_malloc(m_viewPortWidth *
sizeof(uint16_t), MALLOC_CAP_DMA);
562 memset(m_viewPort[i], 0, m_viewPortWidth *
sizeof(uint16_t));
567 void TFTController::freeViewPort()
570 for (
int i = 0; i < m_viewPortHeight; ++i)
571 heap_caps_free(m_viewPort[i]);
572 heap_caps_free(m_viewPort);
573 m_viewPort =
nullptr;
578 void TFTController::updateTaskFunc(
void * pvParameters)
580 TFTController * ctrl = (TFTController*) pvParameters;
584 ctrl->waitForPrimitives();
587 if (ctrl->m_updateTaskFuncSuspended > 0)
588 ulTaskNotifyTake(
true, portMAX_DELAY);
590 ctrl->m_updateTaskRunning =
true;
592 Rect updateRect = Rect(SHRT_MAX, SHRT_MAX, SHRT_MIN, SHRT_MIN);
594 int64_t startTime = ctrl->backgroundPrimitiveTimeoutEnabled() ? esp_timer_get_time() : 0;
598 if (ctrl->getPrimitive(&prim, TFT_BACKGROUND_PRIMITIVE_TIMEOUT / 1000) ==
false)
601 ctrl->execPrimitive(prim, updateRect,
false);
603 if (ctrl->m_updateTaskFuncSuspended > 0)
606 }
while (!ctrl->backgroundPrimitiveTimeoutEnabled() || (startTime + TFT_BACKGROUND_PRIMITIVE_TIMEOUT > esp_timer_get_time()));
608 ctrl->showSprites(updateRect);
610 ctrl->m_updateTaskRunning =
false;
612 if (!ctrl->isDoubleBuffered())
613 ctrl->sendScreenBuffer(updateRect);
619 void TFTController::suspendBackgroundPrimitiveExecution()
621 ++m_updateTaskFuncSuspended;
622 while (m_updateTaskRunning)
627 void TFTController::resumeBackgroundPrimitiveExecution()
629 m_updateTaskFuncSuspended = tmax(0, m_updateTaskFuncSuspended - 1);
630 if (m_updateTaskFuncSuspended == 0)
631 xTaskNotifyGive(m_updateTaskHandle);
635 void TFTController::setPixelAt(PixelDesc
const & pixelDesc,
Rect & updateRect)
637 genericSetPixelAt(pixelDesc, updateRect,
638 [&] (
RGB888 const & color) {
return preparePixel(color); },
639 [&] (
int X,
int Y, uint16_t pattern) { m_viewPort[
Y][
X] = pattern; }
646 void TFTController::absDrawLine(
int X1,
int Y1,
int X2,
int Y2, RGB888 color)
648 genericAbsDrawLine(
X1,
Y1,
X2,
Y2, color,
649 [&] (RGB888
const & color) {
return preparePixel(color); },
650 [&] (
int Y,
int X1,
int X2, uint16_t pattern) { rawFillRow(
Y,
X1,
X2, pattern); },
651 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); },
652 [&] (
int X,
int Y, uint16_t pattern) { m_viewPort[
Y][
X] = pattern; },
653 [&] (
int X,
int Y) { m_viewPort[
Y][
X] = ~m_viewPort[
Y][
X]; }
659 void TFTController::rawFillRow(
int y,
int x1,
int x2, uint16_t pattern)
661 auto px = m_viewPort[y] + x1;
662 for (
int x = x1; x <= x2; ++x, ++px)
668 void TFTController::rawFillRow(
int y,
int x1,
int x2, RGB888 color)
670 rawFillRow(y, x1, x2, preparePixel(color));
676 void TFTController::swapRows(
int yA,
int yB,
int x1,
int x2)
678 auto pxA = m_viewPort[yA] + x1;
679 auto pxB = m_viewPort[yB] + x1;
680 for (
int x = x1; x <= x2; ++x, ++pxA, ++pxB)
685 void TFTController::rawInvertRow(
int y,
int x1,
int x2)
687 auto px = m_viewPort[y] + x1;
688 for (
int x = x1; x <= x2; ++x, ++px)
693 void TFTController::drawEllipse(Size
const & size, Rect & updateRect)
695 genericDrawEllipse(size, updateRect,
696 [&] (RGB888
const & color) {
return preparePixel(color); },
697 [&] (
int X,
int Y, uint16_t pattern) { m_viewPort[
Y][
X] = pattern; }
702 void TFTController::clear(Rect & updateRect)
704 hideSprites(updateRect);
705 auto pattern = preparePixel(getActualBrushColor());
706 for (
int y = 0; y < m_viewPortHeight; ++y)
707 rawFillRow(y, 0, m_viewPortWidth - 1, pattern);
711 void TFTController::VScroll(
int scroll, Rect & updateRect)
713 genericVScroll(scroll, updateRect,
714 [&] (
int yA,
int yB,
int x1,
int x2) { swapRows(yA, yB, x1, x2); },
715 [&] (
int yA,
int yB) { tswap(m_viewPort[yA], m_viewPort[yB]); },
716 [&] (
int y,
int x1,
int x2, RGB888 pattern) { rawFillRow(y, x1, x2, pattern); }
721 void TFTController::HScroll(
int scroll, Rect & updateRect)
723 genericHScroll(scroll, updateRect,
724 [&] (RGB888
const & color) {
return preparePixel(color); },
725 [&] (
int y) {
return m_viewPort[y]; },
726 [&] (uint16_t * row,
int x) {
return row[x]; },
727 [&] (uint16_t * row,
int x,
int pattern) { row[x] = pattern; }
732 void TFTController::drawGlyph(Glyph
const & glyph, GlyphOptions glyphOptions, RGB888 penColor, RGB888 brushColor, Rect & updateRect)
734 genericDrawGlyph(glyph, glyphOptions, penColor, brushColor, updateRect,
735 [&] (RGB888
const & color) {
return preparePixel(color); },
736 [&] (
int y) {
return m_viewPort[y]; },
737 [&] (uint16_t * row,
int x, uint16_t pattern) { row[x] = pattern; }
742 void TFTController::invertRect(Rect
const & rect, Rect & updateRect)
744 genericInvertRect(rect, updateRect,
745 [&] (
int Y,
int X1,
int X2) { rawInvertRow(
Y,
X1,
X2); }
752 genericSwapFGBG(rect, updateRect,
753 [&] (RGB888
const & color) {
return preparePixel(color); },
754 [&] (
int y) {
return m_viewPort[y]; },
755 [&] (uint16_t * row,
int x) {
return row[x]; },
756 [&] (uint16_t * row,
int x, uint16_t pattern) { row[x] = pattern; }
762 void TFTController::copyRect(Rect
const & source, Rect & updateRect)
764 genericCopyRect(source, updateRect,
765 [&] (
int y) {
return m_viewPort[y]; },
766 [&] (uint16_t * row,
int x) {
return row[x]; },
767 [&] (uint16_t * row,
int x, uint16_t pattern) { row[x] = pattern; }
773 void TFTController::readScreen(Rect
const & rect, RGB888 * destBuf)
775 for (
int y = rect.Y1; y <= rect.Y2; ++y) {
776 auto row = m_viewPort[y] + rect.X1;
777 for (
int x = rect.X1; x <= rect.X2; ++x, ++destBuf, ++row)
778 *destBuf = nativeToRGB888(*row);
783 void TFTController::rawDrawBitmap_Native(
int destX,
int destY, Bitmap
const * bitmap,
int X1,
int Y1,
int XCount,
int YCount)
785 genericRawDrawBitmap_Native(destX, destY, (uint16_t*) bitmap->data, bitmap->width,
X1,
Y1, XCount, YCount,
786 [&] (
int y) { return m_viewPort[y]; },
787 [&] (uint16_t * row,
int x, uint16_t src) { row[x] = src; }
792 void TFTController::rawDrawBitmap_Mask(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
794 auto foregroundPattern = preparePixel(bitmap->foregroundColor);
795 genericRawDrawBitmap_Mask(destX, destY, bitmap, (uint16_t*)saveBackground,
X1,
Y1, XCount, YCount,
796 [&] (
int y) {
return m_viewPort[y]; },
797 [&] (uint16_t * row,
int x) {
return row[x]; },
798 [&] (uint16_t * row,
int x) { row[x] = foregroundPattern; }
803 void TFTController::rawDrawBitmap_RGBA2222(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
805 genericRawDrawBitmap_RGBA2222(destX, destY, bitmap, (uint16_t*)saveBackground,
X1,
Y1, XCount, YCount,
806 [&] (
int y) {
return m_viewPort[y]; },
807 [&] (uint16_t * row,
int x) {
return row[x]; },
808 [&] (uint16_t * row,
int x, uint8_t src) { row[x] = RGBA2222toNative(src); }
813 void TFTController::rawDrawBitmap_RGBA8888(
int destX,
int destY, Bitmap
const * bitmap,
void * saveBackground,
int X1,
int Y1,
int XCount,
int YCount)
815 genericRawDrawBitmap_RGBA8888(destX, destY, bitmap, (uint16_t*)saveBackground,
X1,
Y1, XCount, YCount,
816 [&] (
int y) {
return m_viewPort[y]; },
817 [&] (uint16_t * row,
int x) {
return row[x]; },
818 [&] (uint16_t * row,
int x,
RGBA8888 const & src) { row[x] = RGBA8888toNative(src); }
823 void TFTController::swapBuffers()
826 sendScreenBuffer(Rect(0, 0, getViewPortWidth() - 1, getViewPortHeight() - 1));
Represents a 24 bit RGB color.
This file contains some utility classes and functions.
TFTOrientation
This enum defines TFT orientation.
This file contains fabgl::TFTController definition.