mirror of https://github.com/arendst/Tasmota.git
Merge pull request #11892 from gemu2015/lvgl_dma
lvgl initial dma support
This commit is contained in:
commit
0c29c74667
|
@ -624,8 +624,8 @@ char *Renderer::devname(void) {
|
|||
return (char*)dname;
|
||||
}
|
||||
|
||||
uint16_t Renderer::lvgl_pars(void) {
|
||||
return lvgl_param;
|
||||
LVGL_PARAMS *Renderer::lvgl_pars(void) {
|
||||
return &lvgl_param;
|
||||
}
|
||||
|
||||
void VButton::xdrawButton(bool inverted) {
|
||||
|
|
|
@ -16,6 +16,23 @@
|
|||
// a. in class GFX setCursor,setTextSize => virtual
|
||||
// b. textcolor,textbgcolor => public;
|
||||
|
||||
typedef struct LVGL_PARAMS {
|
||||
uint16_t fluslines;
|
||||
union {
|
||||
uint8_t data;
|
||||
struct {
|
||||
uint8_t resvd_0 : 1;
|
||||
uint8_t resvd_1 : 1;
|
||||
uint8_t resvd_2 : 1;
|
||||
uint8_t resvd_3 : 1;
|
||||
uint8_t resvd_4 : 1;
|
||||
uint8_t resvd_5 : 1;
|
||||
uint8_t resvd_6 : 1;
|
||||
uint8_t use_dma : 1;
|
||||
};
|
||||
};
|
||||
}LVGL_PARAMS;
|
||||
|
||||
typedef void (*pwr_cb)(uint8_t);
|
||||
typedef void (*dim_cb)(uint8_t);
|
||||
|
||||
|
@ -51,7 +68,7 @@ public:
|
|||
virtual int8_t color_type(void);
|
||||
virtual void Splash(void);
|
||||
virtual char *devname(void);
|
||||
virtual uint16_t lvgl_pars(void);
|
||||
virtual LVGL_PARAMS *lvgl_pars(void);
|
||||
|
||||
void setDrawMode(uint8_t mode);
|
||||
uint8_t drawmode;
|
||||
|
@ -60,7 +77,7 @@ public:
|
|||
virtual uint8_t *allocate_framebuffer(uint32_t size);
|
||||
pwr_cb pwr_cbp = 0;
|
||||
dim_cb dim_cbp = 0;
|
||||
uint16_t lvgl_param = 0;
|
||||
LVGL_PARAMS lvgl_param;
|
||||
private:
|
||||
void DrawCharAt(int16_t x, int16_t y, char ascii_char,int16_t colored);
|
||||
inline void drawFastVLineInternal(int16_t x, int16_t y, int16_t h, uint16_t color) __attribute__((always_inline));
|
||||
|
|
|
@ -283,7 +283,8 @@ uDisplay::uDisplay(char *lp) : Renderer(800, 600) {
|
|||
lut3time = next_val(&lp1);
|
||||
break;
|
||||
case 'B':
|
||||
lvgl_param = next_val(&lp1);
|
||||
lvgl_param.fluslines = next_val(&lp1);
|
||||
lvgl_param.use_dma = next_val(&lp1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -462,9 +463,18 @@ Renderer *uDisplay::Init(void) {
|
|||
if (spi_nr == 1) {
|
||||
uspi = &SPI;
|
||||
uspi->begin(spi_clk, spi_miso, spi_mosi, -1);
|
||||
if (lvgl_param.use_dma) {
|
||||
spi_host = VSPI_HOST;
|
||||
initDMA(spi_cs);
|
||||
}
|
||||
|
||||
} else if (spi_nr == 2) {
|
||||
uspi = new SPIClass(HSPI);
|
||||
uspi->begin(spi_clk, spi_miso, spi_mosi, -1);
|
||||
if (lvgl_param.use_dma) {
|
||||
spi_host = HSPI_HOST;
|
||||
initDMA(spi_cs);
|
||||
}
|
||||
} else {
|
||||
pinMode(spi_clk, OUTPUT);
|
||||
digitalWrite(spi_clk, LOW);
|
||||
|
@ -1018,6 +1028,8 @@ static inline uint8_t ulv_color_to1(uint16_t color) {
|
|||
void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean first) {
|
||||
uint16_t color;
|
||||
|
||||
//Serial.printf("push %x - %d\n", (uint32_t)data, len);
|
||||
|
||||
if (bpp != 16) {
|
||||
// stupid monchrome version
|
||||
for (uint32_t y = seta_yp1; y < seta_yp2; y++) {
|
||||
|
@ -1038,7 +1050,11 @@ void uDisplay::pushColors(uint16_t *data, uint16_t len, boolean first) {
|
|||
uspi->write(*data++);
|
||||
}
|
||||
#else
|
||||
uspi->writePixels(data, len * 2);
|
||||
if (lvgl_param.use_dma) {
|
||||
pushPixelsDMA(data, len );
|
||||
} else {
|
||||
uspi->writePixels(data, len * 2);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
while (len--) {
|
||||
|
@ -1758,3 +1774,164 @@ void uDisplay::drawFastHLine_EPD(int16_t x, int16_t y, int16_t w, uint16_t color
|
|||
x++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void uDisplay::beginTransaction(SPISettings s) {
|
||||
#ifdef ESP32
|
||||
if (lvgl_param.use_dma) {
|
||||
dmaWait();
|
||||
} else {
|
||||
uspi->beginTransaction(s);
|
||||
}
|
||||
#else
|
||||
uspi->beginTransaction(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
void uDisplay::endTransaction(void) {
|
||||
#ifdef ESP32
|
||||
if (lvgl_param.use_dma) {
|
||||
dmaBusy();
|
||||
} else {
|
||||
uspi->endTransaction();
|
||||
}
|
||||
#else
|
||||
uspi->endTransaction();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// ESP 32 DMA section , derived from TFT_eSPI
|
||||
#ifdef ESP32
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: initDMA
|
||||
** Description: Initialise the DMA engine - returns true if init OK
|
||||
***************************************************************************************/
|
||||
bool uDisplay::initDMA(bool ctrl_cs)
|
||||
{
|
||||
if (DMA_Enabled) return false;
|
||||
|
||||
esp_err_t ret;
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = spi_mosi,
|
||||
.miso_io_num = -1,
|
||||
.sclk_io_num = spi_clk,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = width() * height() * 2 + 8, // TFT screen size
|
||||
.flags = 0,
|
||||
.intr_flags = 0
|
||||
};
|
||||
|
||||
int8_t pin = -1;
|
||||
if (ctrl_cs) pin = spi_cs;
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.command_bits = 0,
|
||||
.address_bits = 0,
|
||||
.dummy_bits = 0,
|
||||
.mode = SPI_MODE3,
|
||||
.duty_cycle_pos = 0,
|
||||
.cs_ena_pretrans = 0,
|
||||
.cs_ena_posttrans = 0,
|
||||
.clock_speed_hz = spi_speed*1000000,
|
||||
.input_delay_ns = 0,
|
||||
.spics_io_num = pin,
|
||||
.flags = SPI_DEVICE_NO_DUMMY, //0,
|
||||
.queue_size = 1,
|
||||
.pre_cb = 0, //dc_callback, //Callback to handle D/C line
|
||||
.post_cb = 0
|
||||
};
|
||||
ret = spi_bus_initialize(spi_host, &buscfg, 1);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
ret = spi_bus_add_device(spi_host, &devcfg, &dmaHAL);
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
DMA_Enabled = true;
|
||||
spiBusyCheck = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: deInitDMA
|
||||
** Description: Disconnect the DMA engine from SPI
|
||||
***************************************************************************************/
|
||||
void uDisplay::deInitDMA(void) {
|
||||
if (!DMA_Enabled) return;
|
||||
spi_bus_remove_device(dmaHAL);
|
||||
spi_bus_free(spi_host);
|
||||
DMA_Enabled = false;
|
||||
}
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: dmaBusy
|
||||
** Description: Check if DMA is busy
|
||||
***************************************************************************************/
|
||||
bool uDisplay::dmaBusy(void) {
|
||||
if (!DMA_Enabled || !spiBusyCheck) return false;
|
||||
|
||||
spi_transaction_t *rtrans;
|
||||
esp_err_t ret;
|
||||
uint8_t checks = spiBusyCheck;
|
||||
for (int i = 0; i < checks; ++i) {
|
||||
ret = spi_device_get_trans_result(dmaHAL, &rtrans, 0);
|
||||
if (ret == ESP_OK) spiBusyCheck--;
|
||||
}
|
||||
|
||||
//Serial.print("spiBusyCheck=");Serial.println(spiBusyCheck);
|
||||
if (spiBusyCheck == 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: dmaWait
|
||||
** Description: Wait until DMA is over (blocking!)
|
||||
***************************************************************************************/
|
||||
void uDisplay::dmaWait(void) {
|
||||
if (!DMA_Enabled || !spiBusyCheck) return;
|
||||
spi_transaction_t *rtrans;
|
||||
esp_err_t ret;
|
||||
for (int i = 0; i < spiBusyCheck; ++i) {
|
||||
ret = spi_device_get_trans_result(dmaHAL, &rtrans, portMAX_DELAY);
|
||||
assert(ret == ESP_OK);
|
||||
}
|
||||
spiBusyCheck = 0;
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************************
|
||||
** Function name: pushPixelsDMA
|
||||
** Description: Push pixels to TFT (len must be less than 32767)
|
||||
***************************************************************************************/
|
||||
// This will byte swap the original image if setSwapBytes(true) was called by sketch.
|
||||
void uDisplay::pushPixelsDMA(uint16_t* image, uint32_t len) {
|
||||
|
||||
if ((len == 0) || (!DMA_Enabled)) return;
|
||||
|
||||
dmaWait();
|
||||
|
||||
/*
|
||||
if(_swapBytes) {
|
||||
for (uint32_t i = 0; i < len; i++) (image[i] = image[i] << 8 | image[i] >> 8);
|
||||
}*/
|
||||
|
||||
esp_err_t ret;
|
||||
|
||||
memset(&trans, 0, sizeof(spi_transaction_t));
|
||||
|
||||
trans.user = (void *)1;
|
||||
trans.tx_buffer = image; //finally send the line data
|
||||
trans.length = len * 16; //Data length, in bits
|
||||
trans.flags = 0; //SPI_TRANS_USE_TXDATA flag
|
||||
|
||||
ret = spi_device_queue_trans(dmaHAL, &trans, portMAX_DELAY);
|
||||
assert(ret == ESP_OK);
|
||||
|
||||
spiBusyCheck++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // ESP32
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
#include <renderer.h>
|
||||
#include <Wire.h>
|
||||
#include <SPI.h>
|
||||
#ifdef ESP32
|
||||
#include "driver/spi_master.h"
|
||||
#endif
|
||||
|
||||
#define _UDSP_I2C 1
|
||||
#define _UDSP_SPI 2
|
||||
|
@ -58,8 +61,8 @@ enum uColorType { uCOLOR_BW, uCOLOR_COLOR };
|
|||
|
||||
#endif
|
||||
|
||||
#define SPI_BEGIN_TRANSACTION if (spi_nr <= 2) uspi->beginTransaction(spiSettings);
|
||||
#define SPI_END_TRANSACTION if (spi_nr <= 2) uspi->endTransaction();
|
||||
#define SPI_BEGIN_TRANSACTION if (spi_nr <= 2) beginTransaction(spiSettings);
|
||||
#define SPI_END_TRANSACTION if (spi_nr <= 2) endTransaction();
|
||||
#define SPI_CS_LOW if (spi_cs >= 0) GPIO_CLR(spi_cs);
|
||||
#define SPI_CS_HIGH if (spi_cs >= 0) GPIO_SET(spi_cs);
|
||||
#define SPI_DC_LOW if (spi_dc >= 0) GPIO_CLR(spi_dc);
|
||||
|
@ -94,6 +97,8 @@ class uDisplay : public Renderer {
|
|||
void SetDimCB(dim_cb cb) { dim_cbp = cb; };
|
||||
|
||||
private:
|
||||
void beginTransaction(SPISettings s);
|
||||
void endTransaction(void);
|
||||
void setAddrWindow(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1);
|
||||
void drawPixel(int16_t x, int16_t y, uint16_t color);
|
||||
void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
|
||||
|
@ -209,6 +214,20 @@ class uDisplay : public Renderer {
|
|||
uint16_t seta_xp2;
|
||||
uint16_t seta_yp1;
|
||||
uint16_t seta_yp2;
|
||||
#ifdef ESP32
|
||||
// dma section
|
||||
bool DMA_Enabled = false;
|
||||
uint8_t spiBusyCheck = 0;
|
||||
spi_transaction_t trans;
|
||||
spi_device_handle_t dmaHAL;
|
||||
spi_host_device_t spi_host = VSPI_HOST;
|
||||
// spi_host_device_t spi_host = VSPI_HOST;
|
||||
bool initDMA(bool ctrl_cs);
|
||||
void deInitDMA(void);
|
||||
bool dmaBusy(void);
|
||||
void dmaWait(void);
|
||||
void pushPixelsDMA(uint16_t* image, uint32_t len);
|
||||
#endif // ESP32
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -482,8 +482,8 @@ void RA8876::DisplayInit(int8_t p,int8_t size,int8_t rot,int8_t font) {
|
|||
|
||||
bool RA8876::initDisplay() {
|
||||
|
||||
lvgl_param = 10;
|
||||
|
||||
lvgl_param.fluslines = 10;
|
||||
|
||||
SPI.beginTransaction(m_spiSettings);
|
||||
|
||||
// Set chip config register
|
||||
|
|
|
@ -61,7 +61,7 @@ int32_t Epd47::Init(void) {
|
|||
epd_init(EPD_LUT_1K);
|
||||
hl = epd_hl_init(WAVEFORM);
|
||||
epd47_buffer = epd_hl_get_framebuffer(&hl);
|
||||
lvgl_param = 10;
|
||||
lvgl_param.fluslines = 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
16
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/Adafruit_LvGL_Glue.cpp
Normal file → Executable file
16
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/Adafruit_LvGL_Glue.cpp
Normal file → Executable file
|
@ -198,8 +198,6 @@ LvGLStatus Adafruit_LvGL_Glue::begin(Renderer *tft, bool debug) {
|
|||
|
||||
LvGLStatus Adafruit_LvGL_Glue::begin(Renderer *tft, void *touch, bool debug) {
|
||||
|
||||
|
||||
|
||||
lv_init();
|
||||
|
||||
// Allocate LvGL display buffer (x2 because DMA double buffering)
|
||||
|
@ -209,9 +207,17 @@ LvGLStatus Adafruit_LvGL_Glue::begin(Renderer *tft, void *touch, bool debug) {
|
|||
uint32_t lvgl_buffer_size;
|
||||
|
||||
//lvgl_buffer_size = LV_HOR_RES_MAX * LV_BUFFER_ROWS;
|
||||
uint8_t flushlines = tft->lvgl_pars();
|
||||
uint8_t flushlines = tft->lvgl_pars()->fluslines;
|
||||
lvgl_buffer_size = tft->width() * (flushlines ? flushlines:LV_BUFFER_ROWS);
|
||||
//Serial.printf("%d\n", lvgl_buffer_size);
|
||||
if (tft->lvgl_pars()->use_dma) {
|
||||
lvgl_buffer_size /= 2;
|
||||
lv_pixel_buf2 = new lv_color_t[lvgl_buffer_size];
|
||||
if (!lv_pixel_buf2) {
|
||||
return status;
|
||||
}
|
||||
} else {
|
||||
lv_pixel_buf2 = nullptr;
|
||||
}
|
||||
|
||||
if ((lv_pixel_buf = new lv_color_t[lvgl_buffer_size])) {
|
||||
|
||||
|
@ -227,7 +233,7 @@ LvGLStatus Adafruit_LvGL_Glue::begin(Renderer *tft, void *touch, bool debug) {
|
|||
// Initialize LvGL display buffers
|
||||
lv_disp_buf_init(
|
||||
&lv_disp_buf, lv_pixel_buf, // 1st half buf
|
||||
nullptr, // 2nd half buf
|
||||
lv_pixel_buf2, // 2nd half buf
|
||||
lvgl_buffer_size);
|
||||
|
||||
// Initialize LvGL display driver
|
||||
|
|
|
@ -44,6 +44,7 @@ private:
|
|||
lv_disp_drv_t lv_disp_drv;
|
||||
lv_disp_buf_t lv_disp_buf;
|
||||
lv_color_t *lv_pixel_buf;
|
||||
lv_color_t *lv_pixel_buf2;
|
||||
lv_indev_drv_t lv_indev_drv;
|
||||
lv_indev_t *lv_input_dev_ptr;
|
||||
Ticker tick;
|
||||
|
|
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_changes/hello_changes.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_changes/hello_changes.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_clue/.clue.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_clue/.clue.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_clue/hello_clue.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_clue/hello_clue.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_featherwing/.cpx_ada.test.skip
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_featherwing/.cpx_ada.test.skip
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_featherwing/hello_featherwing.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_featherwing/hello_featherwing.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_gizmo/.cpb.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_gizmo/.cpb.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_gizmo/hello_gizmo.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_gizmo/hello_gizmo.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_pyportal/.pyportal.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_pyportal/.pyportal.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_pyportal/hello_pyportal.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/hello_pyportal/hello_pyportal.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_clue/.clue.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_clue/.clue.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_clue/widgets_clue.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_clue/widgets_clue.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_featherwing/.cpx_ada.test.skip
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_featherwing/.cpx_ada.test.skip
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_featherwing/widgets_featherwing.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_featherwing/widgets_featherwing.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_gizmo/.cpb.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_gizmo/.cpb.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_gizmo/widgets_gizmo.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_gizmo/widgets_gizmo.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_pyportal/.pyportal.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_pyportal/.pyportal.test.only
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_pyportal/widgets_pyportal.ino
Normal file → Executable file
0
lib/libesp32_lvgl/Adafruit_LvGL_Glue-shadinger/examples/widgets_pyportal/widgets_pyportal.ino
Normal file → Executable file
Loading…
Reference in New Issue