lcd: update doc unit test and example to support 8-line spi

This commit is contained in:
morris
2021-08-18 19:19:55 +08:00
parent 8143832041
commit 71d475149d
18 changed files with 349 additions and 337 deletions

View File

@@ -63,10 +63,8 @@ extern "C"
#define SPICOMMON_BUSFLAG_DUAL (1<<6) ///< Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode. #define SPICOMMON_BUSFLAG_DUAL (1<<6) ///< Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode.
#define SPICOMMON_BUSFLAG_WPHD (1<<7) ///< Check existing of WP and HD pins. Or indicates WP & HD pins initialized. #define SPICOMMON_BUSFLAG_WPHD (1<<7) ///< Check existing of WP and HD pins. Or indicates WP & HD pins initialized.
#define SPICOMMON_BUSFLAG_QUAD (SPICOMMON_BUSFLAG_DUAL|SPICOMMON_BUSFLAG_WPHD) ///< Check existing of MOSI/MISO/WP/HD pins as output. Or indicates bus able to work under QIO mode. #define SPICOMMON_BUSFLAG_QUAD (SPICOMMON_BUSFLAG_DUAL|SPICOMMON_BUSFLAG_WPHD) ///< Check existing of MOSI/MISO/WP/HD pins as output. Or indicates bus able to work under QIO mode.
#if SOC_SPI_SUPPORT_OCT
#define SPICOMMON_BUSFLAG_IO4_IO7 (1<<8) ///< Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized. #define SPICOMMON_BUSFLAG_IO4_IO7 (1<<8) ///< Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized.
#define SPICOMMON_BUSFLAG_OCTAL (SPICOMMON_BUSFLAG_QUAD|SPICOMMON_BUSFLAG_IO4_IO7) ///< Check existing of MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7 pins as output. Or indicates bus able to work under octal mode. #define SPICOMMON_BUSFLAG_OCTAL (SPICOMMON_BUSFLAG_QUAD|SPICOMMON_BUSFLAG_IO4_IO7) ///< Check existing of MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7 pins as output. Or indicates bus able to work under octal mode.
#endif
#define SPICOMMON_BUSFLAG_NATIVE_PINS SPICOMMON_BUSFLAG_IOMUX_PINS #define SPICOMMON_BUSFLAG_NATIVE_PINS SPICOMMON_BUSFLAG_IOMUX_PINS
/** /**
@@ -115,12 +113,10 @@ typedef struct {
int quadhd_io_num; ///< GPIO pin for HD (Hold) signal, or -1 if not used. int quadhd_io_num; ///< GPIO pin for HD (Hold) signal, or -1 if not used.
int data3_io_num; ///< GPIO pin for spi data3 signal in quad/octal mode, or -1 if not used. int data3_io_num; ///< GPIO pin for spi data3 signal in quad/octal mode, or -1 if not used.
}; };
#if SOC_SPI_SUPPORT_OCT
int data4_io_num; ///< GPIO pin for spi data4 signal in octal mode, or -1 if not used. int data4_io_num; ///< GPIO pin for spi data4 signal in octal mode, or -1 if not used.
int data5_io_num; ///< GPIO pin for spi data5 signal in octal mode, or -1 if not used. int data5_io_num; ///< GPIO pin for spi data5 signal in octal mode, or -1 if not used.
int data6_io_num; ///< GPIO pin for spi data6 signal in octal mode, or -1 if not used. int data6_io_num; ///< GPIO pin for spi data6 signal in octal mode, or -1 if not used.
int data7_io_num; ///< GPIO pin for spi data7 signal in octal mode, or -1 if not used. int data7_io_num; ///< GPIO pin for spi data7 signal in octal mode, or -1 if not used.
#endif
int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4092 if 0 when DMA enabled, or to `SOC_SPI_MAXIMUM_BUFFER_SIZE` if DMA is disabled. int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4092 if 0 when DMA enabled, or to `SOC_SPI_MAXIMUM_BUFFER_SIZE` if DMA is disabled.
uint32_t flags; ///< Abilities of bus to be checked by the driver. Or-ed value of ``SPICOMMON_BUSFLAG_*`` flags. uint32_t flags; ///< Abilities of bus to be checked by the driver. Or-ed value of ``SPICOMMON_BUSFLAG_*`` flags.
int intr_flags; /**< Interrupt flag for the bus to set the priority, and IRAM attribute, see int intr_flags; /**< Interrupt flag for the bus to set the priority, and IRAM attribute, see

View File

@@ -105,11 +105,9 @@ typedef struct {
#define SPI_TRANS_VARIABLE_ADDR (1<<6) ///< Use the ``address_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. #define SPI_TRANS_VARIABLE_ADDR (1<<6) ///< Use the ``address_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``.
#define SPI_TRANS_VARIABLE_DUMMY (1<<7) ///< Use the ``dummy_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. #define SPI_TRANS_VARIABLE_DUMMY (1<<7) ///< Use the ``dummy_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``.
#define SPI_TRANS_CS_KEEP_ACTIVE (1<<8) ///< Keep CS active after data transfer #define SPI_TRANS_CS_KEEP_ACTIVE (1<<8) ///< Keep CS active after data transfer
#define MULTILINE_CMD (1<<9) ///< The number of lines transmitting command is the same as that transmitting data #define SPI_TRANS_MULTILINE_CMD (1<<9) ///< The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase)
#define MULTILINE_ADDR (1<<10) ///< the number of lines transmitting address is the same as that transmitting data (in dual and quad mode the same as ``SPI_TRANS_MODE_DIOQIO_ADDR``) #define SPI_TRANS_MODE_OCT (1<<10) ///< Transmit/receive data in 8-bit mode
#if SOC_SPI_SUPPORT_OCT #define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)
#define SPI_TRANS_MODE_OCT (1<<11) ///< Transmit/receive data in 8-bit mode
#endif
/** /**
* This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes. * This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.

View File

@@ -354,6 +354,9 @@ esp_err_t spicommon_slave_free_dma(spi_host_device_t host_id)
#if SOC_SPI_SUPPORT_OCT #if SOC_SPI_SUPPORT_OCT
static bool check_iomux_pins_oct(spi_host_device_t host, const spi_bus_config_t* bus_config) static bool check_iomux_pins_oct(spi_host_device_t host, const spi_bus_config_t* bus_config)
{ {
if (host != SPI2_HOST) {
return false;
}
int io_nums[] = {bus_config->data0_io_num, bus_config->data1_io_num, bus_config->data2_io_num, bus_config->data3_io_num, int io_nums[] = {bus_config->data0_io_num, bus_config->data1_io_num, bus_config->data2_io_num, bus_config->data3_io_num,
bus_config->sclk_io_num, bus_config->data4_io_num, bus_config->data5_io_num, bus_config->data6_io_num, bus_config->data7_io_num}; bus_config->sclk_io_num, bus_config->data4_io_num, bus_config->data5_io_num, bus_config->data6_io_num, bus_config->data7_io_num};
int io_mux_nums[] = {SPI2_IOMUX_PIN_NUM_MOSI_OCT, SPI2_IOMUX_PIN_NUM_MISO_OCT, SPI2_IOMUX_PIN_NUM_WP_OCT, SPI2_IOMUX_PIN_NUM_HD_OCT, int io_mux_nums[] = {SPI2_IOMUX_PIN_NUM_MOSI_OCT, SPI2_IOMUX_PIN_NUM_MISO_OCT, SPI2_IOMUX_PIN_NUM_WP_OCT, SPI2_IOMUX_PIN_NUM_HD_OCT,

View File

@@ -532,16 +532,15 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0; hal_trans.cs_keep_active = (trans->flags & SPI_TRANS_CS_KEEP_ACTIVE) ? 1 : 0;
//Set up OIO/QIO/DIO if needed //Set up OIO/QIO/DIO if needed
#if SOC_SPI_SUPPORT_OCT
hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_QIO) ? 4 :
(trans->flags & SPI_TRANS_MODE_OCT) ? 8 : 1;
#else
hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 : hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1; (trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1;
#if SOC_SPI_SUPPORT_OCT
if (trans->flags & SPI_TRANS_MODE_OCT) {
hal_trans.line_mode.data_lines = 8;
}
#endif #endif
hal_trans.line_mode.addr_lines = (trans->flags & (SPI_TRANS_MODE_DIOQIO_ADDR | MULTILINE_ADDR)) ? hal_trans.line_mode.data_lines : 1; hal_trans.line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans.line_mode.data_lines : 1;
hal_trans.line_mode.cmd_lines = (trans->flags & MULTILINE_CMD) ? hal_trans.line_mode.data_lines : 1; hal_trans.line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans.line_mode.data_lines : 1;
if (trans->flags & SPI_TRANS_VARIABLE_CMD) { if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits; hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
@@ -693,11 +692,11 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
//check working mode //check working mode
#if SOC_SPI_SUPPORT_OCT #if SOC_SPI_SUPPORT_OCT
SPI_CHECK(!(host->id == SPI3_HOST && trans_desc->flags & SPI_TRANS_MODE_OCT), "SPI3 does not support octal mode", ESP_ERR_INVALID_ARG); SPI_CHECK(!(host->id == SPI3_HOST && trans_desc->flags & SPI_TRANS_MODE_OCT), "SPI3 does not support octal mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible interface parameters", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both Octal mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && !is_half_duplex), "incompatible interface parameters", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && !is_half_duplex), "Incompatible when setting to both Octal mode and half duplex mode", ESP_ERR_INVALID_ARG);
#endif #endif
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible interface parameters", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both multi-line mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "incompatible interface parameters", ESP_ERR_INVALID_ARG); SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "Incompatible when setting to both multi-line mode and half duplex mode", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
SPI_CHECK(!is_half_duplex || !bus_attr->dma_enabled || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG ); SPI_CHECK(!is_half_duplex || !bus_attr->dma_enabled || !rx_enabled || !tx_enabled, "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
#elif CONFIG_IDF_TARGET_ESP32S3 #elif CONFIG_IDF_TARGET_ESP32S3

View File

@@ -61,9 +61,7 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p
ESP_GOTO_ON_FALSE(spi_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi panel io"); ESP_GOTO_ON_FALSE(spi_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi panel io");
spi_device_interface_config_t devcfg = { spi_device_interface_config_t devcfg = {
#if SOC_SPI_SUPPORT_OCT .flags = io_config->flags.octal_mode ? SPI_DEVICE_HALFDUPLEX : 0,
.flags = SPI_DEVICE_HALFDUPLEX, // lcd driver only use one transfer direction, so half duplex is enough.
#endif
.clock_speed_hz = io_config->pclk_hz, .clock_speed_hz = io_config->pclk_hz,
.mode = io_config->spi_mode, .mode = io_config->spi_mode,
.spics_io_num = io_config->cs_gpio_num, .spics_io_num = io_config->cs_gpio_num,
@@ -152,11 +150,10 @@ static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode
lcd_trans->base.length = spi_panel_io->lcd_cmd_bits; lcd_trans->base.length = spi_panel_io->lcd_cmd_bits;
lcd_trans->base.tx_buffer = &lcd_cmd; lcd_trans->base.tx_buffer = &lcd_cmd;
#if SOC_SPI_SUPPORT_OCT
if (spi_panel_io->flags.octal_mode) { if (spi_panel_io->flags.octal_mode) {
lcd_trans->base.flags |= (MULTILINE_CMD | MULTILINE_ADDR | SPI_TRANS_MODE_OCT); // use 8 lines for transmitting command, address and data
lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
} }
#endif
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level; lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level;
} }
@@ -202,11 +199,10 @@ static esp_err_t panel_io_spi_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level; lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level;
} }
#if SOC_SPI_SUPPORT_OCT
if (spi_panel_io->flags.octal_mode) { if (spi_panel_io->flags.octal_mode) {
lcd_trans->base.flags |= (MULTILINE_CMD | MULTILINE_ADDR | SPI_TRANS_MODE_OCT); // use 8 lines for transmitting command, address and data
lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
} }
#endif
// command is short, using polling mode // command is short, using polling mode
ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base); ret = spi_device_polling_transmit(spi_panel_io->spi_dev, &lcd_trans->base);
ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed"); ESP_GOTO_ON_ERROR(ret, err, TAG, "spi transmit (polling) command failed");

View File

@@ -0,0 +1,18 @@
#include "sdkconfig.h"
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#define TEST_LCD_BK_LIGHT_GPIO (18)
#define TEST_LCD_RST_GPIO (5)
#define TEST_LCD_CS_GPIO (0)
#define TEST_LCD_DC_GPIO (19)
#define TEST_LCD_PCLK_GPIO (2)
#define TEST_LCD_DATA0_GPIO (4)
#define TEST_LCD_DATA1_GPIO (7)
#define TEST_LCD_DATA2_GPIO (8)
#define TEST_LCD_DATA3_GPIO (9)
#define TEST_LCD_DATA4_GPIO (10)
#define TEST_LCD_DATA5_GPIO (11)
#define TEST_LCD_DATA6_GPIO (12)
#define TEST_LCD_DATA7_GPIO (13)

View File

@@ -9,29 +9,15 @@
#include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.h"
#include "esp_system.h" #include "esp_system.h"
#include "soc/soc_caps.h"
#include "test_spi_board.h"
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#define TEST_LCD_CLK_GPIO (2)
#define TEST_LCD_DATA0_GPIO (4)
#define TEST_LCD_RST_GPIO (5)
#define TEST_LCD_DC_GPIO (18)
#define TEST_LCD_BK_LIGHT_GPIO (19)
#define TEST_LCD_CS_GPIO (0)
#define TEST_LCD_DATA1_GPIO (7)
#define TEST_LCD_DATA2_GPIO (8)
#define TEST_LCD_DATA3_GPIO (9)
#define TEST_LCD_DATA4_GPIO (10)
#define TEST_LCD_DATA5_GPIO (11)
#define TEST_LCD_DATA6_GPIO (12)
#define TEST_LCD_DATA7_GPIO (13)
#define TEST_SPI_HOST_ID (1) #define TEST_SPI_HOST_ID (1)
#define TEST_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000) #define TEST_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000) // 20MHz
typedef bool (*trans_done_callback)(esp_lcd_panel_io_handle_t, void *, void *); typedef bool (*trans_done_callback_t)(esp_lcd_panel_io_handle_t, void *, void *);
static void lcd_initialize(bool is_8_line_lcd, esp_lcd_panel_io_handle_t *io_handle, esp_lcd_panel_handle_t *panel_handle, static void lcd_initialize_spi(esp_lcd_panel_io_handle_t *io_handle, esp_lcd_panel_handle_t *panel_handle, trans_done_callback_t on_color_trans_done, void *user_data, bool oct_mode)
int spi_mode, trans_done_callback on_color_trans_done, void *user_data)
{ {
gpio_config_t bk_gpio_config = { gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT, .mode = GPIO_MODE_OUTPUT,
@@ -40,15 +26,14 @@ static void lcd_initialize(bool is_8_line_lcd, esp_lcd_panel_io_handle_t *io_han
TEST_ESP_OK(gpio_config(&bk_gpio_config)); TEST_ESP_OK(gpio_config(&bk_gpio_config));
spi_bus_config_t buscfg = { spi_bus_config_t buscfg = {
.miso_io_num = -1, .sclk_io_num = TEST_LCD_PCLK_GPIO,
.mosi_io_num = TEST_LCD_DATA0_GPIO, .mosi_io_num = TEST_LCD_DATA0_GPIO,
.sclk_io_num = TEST_LCD_CLK_GPIO, .miso_io_num = -1,
.quadwp_io_num = -1, .quadwp_io_num = -1,
.quadhd_io_num = -1, .quadhd_io_num = -1,
.max_transfer_sz = TEST_LCD_H_RES * TEST_LCD_V_RES * sizeof(uint16_t) .max_transfer_sz = TEST_LCD_H_RES * TEST_LCD_V_RES * sizeof(uint16_t)
}; };
#if SOC_SPI_SUPPORT_OCT if (oct_mode) {
if (is_8_line_lcd) {
buscfg.data1_io_num = TEST_LCD_DATA1_GPIO; buscfg.data1_io_num = TEST_LCD_DATA1_GPIO;
buscfg.data2_io_num = TEST_LCD_DATA2_GPIO; buscfg.data2_io_num = TEST_LCD_DATA2_GPIO;
buscfg.data3_io_num = TEST_LCD_DATA3_GPIO; buscfg.data3_io_num = TEST_LCD_DATA3_GPIO;
@@ -58,25 +43,23 @@ static void lcd_initialize(bool is_8_line_lcd, esp_lcd_panel_io_handle_t *io_han
buscfg.data7_io_num = TEST_LCD_DATA7_GPIO; buscfg.data7_io_num = TEST_LCD_DATA7_GPIO;
buscfg.flags = SPICOMMON_BUSFLAG_OCTAL; buscfg.flags = SPICOMMON_BUSFLAG_OCTAL;
} }
#endif //SOC_SPI_SUPPORT_OCT
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST_ID, &buscfg, SPI_DMA_CH_AUTO)); TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST_ID, &buscfg, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_spi_config_t io_config = { esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = TEST_LCD_DC_GPIO, .dc_gpio_num = TEST_LCD_DC_GPIO,
.cs_gpio_num = TEST_LCD_CS_GPIO, .cs_gpio_num = TEST_LCD_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ, .pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.spi_mode = spi_mode, .spi_mode = 0,
.trans_queue_depth = 10, .trans_queue_depth = 10,
.lcd_cmd_bits = 8, .lcd_cmd_bits = 8,
.lcd_param_bits = 8, .lcd_param_bits = 8,
.on_color_trans_done = on_color_trans_done, .on_color_trans_done = on_color_trans_done,
.user_data = user_data .user_data = user_data
}; };
#if SOC_SPI_SUPPORT_OCT if (oct_mode) {
if (is_8_line_lcd) {
io_config.flags.octal_mode = 1; io_config.flags.octal_mode = 1;
io_config.spi_mode = 3;
} }
#endif
TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_SPI_HOST_ID, &io_config, io_handle)); TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_SPI_HOST_ID, &io_config, io_handle));
esp_lcd_panel_dev_config_t panel_config = { esp_lcd_panel_dev_config_t panel_config = {
@@ -125,7 +108,7 @@ TEST_CASE("lcd panel with 8-line spi interface (st7789)", "[lcd]")
{ {
esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(true, &io_handle, &panel_handle, 3, NULL, NULL); lcd_initialize_spi(&io_handle, &panel_handle, NULL, NULL, true);
lcd_panel_test(io_handle, panel_handle); lcd_panel_test(io_handle, panel_handle);
} }
#endif // SOC_SPI_SUPPORT_OCT #endif // SOC_SPI_SUPPORT_OCT
@@ -134,7 +117,7 @@ TEST_CASE("lcd panel with 1-line spi interface (st7789)", "[lcd]")
{ {
esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(false, &io_handle, &panel_handle, 0, NULL, NULL); lcd_initialize_spi(&io_handle, &panel_handle, NULL, NULL, false);
lcd_panel_test(io_handle, panel_handle); lcd_panel_test(io_handle, panel_handle);
} }
@@ -166,13 +149,14 @@ static void lvgl_gui_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_han
test_lvgl_task_loop(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES, disp); test_lvgl_task_loop(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES, disp);
} }
#if SOC_SPI_SUPPORT_OCT #if SOC_SPI_SUPPORT_OCT
TEST_CASE("lvgl gui with 8-line spi interface (st7789)", "[lcd][lvgl][ignore]") TEST_CASE("lvgl gui with 8-line spi interface (st7789)", "[lcd][lvgl][ignore]")
{ {
lv_disp_t *disp = NULL; lv_disp_t *disp = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(true, &io_handle, &panel_handle, 3, notify_lvgl_ready_to_flush, &disp); lcd_initialize_spi(&io_handle, &panel_handle, notify_lvgl_ready_to_flush, &disp, true);
lvgl_gui_test(io_handle, panel_handle, &disp); lvgl_gui_test(io_handle, panel_handle, &disp);
} }
@@ -183,7 +167,7 @@ TEST_CASE("lvgl gui with 1-line spi interface (st7789)", "[lcd][lvgl][ignore]")
lv_disp_t *disp = NULL; lv_disp_t *disp = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL; esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(false, &io_handle, &panel_handle, 0, notify_lvgl_ready_to_flush, &disp); lcd_initialize_spi(&io_handle, &panel_handle, notify_lvgl_ready_to_flush, &disp, false);
lvgl_gui_test(io_handle, panel_handle, &disp); lvgl_gui_test(io_handle, panel_handle, &disp);
} }

View File

@@ -29,6 +29,7 @@
#include "soc/spi_periph.h" #include "soc/spi_periph.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h" #include "hal/spi_types.h"
#include "hal/assert.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -453,7 +454,7 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK; hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK; hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
if (line_mode.cmd_lines > 1) { if (line_mode.cmd_lines > 1) {
abort(); HAL_ASSERT(false);
} }
switch (line_mode.data_lines) { switch (line_mode.data_lines) {
case 2: case 2:
@@ -466,7 +467,7 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li
hw->ctrl.fread_dio = 1; hw->ctrl.fread_dio = 1;
hw->user.fwrite_dio = 1; hw->user.fwrite_dio = 1;
} else { } else {
abort(); HAL_ASSERT(false);
} }
hw->ctrl.fastrd_mode = 1; hw->ctrl.fastrd_mode = 1;
break; break;
@@ -480,7 +481,7 @@ static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t li
hw->ctrl.fread_qio = 1; hw->ctrl.fread_qio = 1;
hw->user.fwrite_qio = 1; hw->user.fwrite_qio = 1;
} else { } else {
abort(); HAL_ASSERT(false);
} }
hw->ctrl.fastrd_mode = 1; hw->ctrl.fastrd_mode = 1;
break; break;
@@ -509,7 +510,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated * @param keep_active if 0 don't keep CS activated, else keep CS activated
*/ */
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->pin.cs_keep_active = (keep_active != 0) ? 1 : 0; hw->pin.cs_keep_active = (keep_active != 0) ? 1 : 0;
} }

View File

@@ -357,10 +357,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word; uint32_t word;
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
//read-modify-write //read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write hw->data_buf[byte_id / 4] = word; //write
@@ -404,7 +408,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4]; uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len); memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len; byte_id += copy_len;
@@ -587,7 +593,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated * @param keep_active if 0 don't keep CS activated, else keep CS activated
*/ */
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
} }

View File

@@ -355,10 +355,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word; uint32_t word;
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
//read-modify-write //read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write hw->data_buf[byte_id / 4] = word; //write
@@ -402,7 +406,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4]; uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len); memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len; byte_id += copy_len;
@@ -586,7 +592,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated * @param keep_active if 0 don't keep CS activated, else keep CS activated
*/ */
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
} }

View File

@@ -349,7 +349,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_addr, uint8_t
int offset = byte_addr % 4; int offset = byte_addr % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len); memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_addr += copy_len; byte_addr += copy_len;
@@ -369,10 +371,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_addr, uint8_
int offset = byte_addr % 4; int offset = byte_addr % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
//read-modify-write //read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_addr / 4]; if (copy_len != 4) {
word = hw->data_buf[byte_addr / 4];
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); memcpy(((uint8_t *)&word) + offset, data, copy_len);
hw->data_buf[byte_addr / 4] = word; hw->data_buf[byte_addr / 4] = word;
@@ -554,7 +560,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated * @param keep_active if 0 don't keep CS activated, else keep CS activated
*/ */
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
} }

View File

@@ -359,10 +359,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word; uint32_t word;
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
//read-modify-write //read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write hw->data_buf[byte_id / 4] = word; //write
@@ -406,7 +410,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4]; uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4; int offset = byte_id % 4;
int copy_len = 4 - offset; int copy_len = 4 - offset;
if (copy_len > len) copy_len = len; if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len); memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len; byte_id += copy_len;
@@ -601,7 +607,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated * @param keep_active if 0 don't keep CS activated, else keep CS activated
*/ */
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) { static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0; hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
} }

View File

@@ -14,8 +14,9 @@
#pragma once #pragma once
#include <stdint.h>
#include "esp_attr.h" #include "esp_attr.h"
#include <esp_bit_defs.h> #include "esp_bit_defs.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "sdkconfig.h" #include "sdkconfig.h"
@@ -49,9 +50,9 @@ FLAG_ATTR(spi_event_t)
* @brief Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN. * @brief Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN.
*/ */
typedef struct { typedef struct {
unsigned char cmd_lines; ///< The line width of command phase, e.g. 2-line-cmd-phase. uint8_t cmd_lines; ///< The line width of command phase, e.g. 2-line-cmd-phase.
unsigned char addr_lines; ///< The line width of address phase, e.g. 1-line-addr-phase. uint8_t addr_lines; ///< The line width of address phase, e.g. 1-line-addr-phase.
unsigned char data_lines; ///< The line width of data phase, e.g. 4-line-data-phase. uint8_t data_lines; ///< The line width of data phase, e.g. 4-line-data-phase.
} spi_line_mode_t; } spi_line_mode_t;

View File

@@ -30,8 +30,7 @@
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" extern "C" {
{
#endif #endif
#if CONFIG_IDF_TARGET_ESP32S2 #if CONFIG_IDF_TARGET_ESP32S2
@@ -41,8 +40,6 @@ extern "C"
#define SPI_FWRITE_QIO 0 #define SPI_FWRITE_QIO 0
#endif #endif
/* /*
Stores a bunch of per-spi-peripheral data. Stores a bunch of per-spi-peripheral data.
*/ */
@@ -53,18 +50,22 @@ typedef struct {
const uint8_t spiq_out; const uint8_t spiq_out;
const uint8_t spiwp_out; const uint8_t spiwp_out;
const uint8_t spihd_out; const uint8_t spihd_out;
#if SOC_SPI_SUPPORT_OCT
const uint8_t spid4_out; const uint8_t spid4_out;
const uint8_t spid5_out; const uint8_t spid5_out;
const uint8_t spid6_out; const uint8_t spid6_out;
const uint8_t spid7_out; const uint8_t spid7_out;
#endif // SOC_SPI_SUPPORT_OCT
const uint8_t spid_in; //GPIO mux input signals const uint8_t spid_in; //GPIO mux input signals
const uint8_t spiq_in; const uint8_t spiq_in;
const uint8_t spiwp_in; const uint8_t spiwp_in;
const uint8_t spihd_in; const uint8_t spihd_in;
#if SOC_SPI_SUPPORT_OCT
const uint8_t spid4_in; const uint8_t spid4_in;
const uint8_t spid5_in; const uint8_t spid5_in;
const uint8_t spid6_in; const uint8_t spid6_in;
const uint8_t spid7_in; const uint8_t spid7_in;
#endif // SOC_SPI_SUPPORT_OCT
const uint8_t spics_out[3]; // /CS GPIO output mux signals const uint8_t spics_out[3]; // /CS GPIO output mux signals
const uint8_t spics_in; const uint8_t spics_in;
const uint8_t spidqs_out; const uint8_t spidqs_out;

View File

@@ -47,16 +47,16 @@ Term Definition
**Host** The SPI controller peripheral inside {IDF_TARGET_NAME} that initiates SPI transmissions over the bus, and acts as an SPI Master. **Host** The SPI controller peripheral inside {IDF_TARGET_NAME} that initiates SPI transmissions over the bus, and acts as an SPI Master.
**Device** SPI slave device. An SPI bus may be connected to one or more Devices. Each Device shares the MOSI, MISO and SCLK signals but is only active on the bus when the Host asserts the Device's individual CS line. **Device** SPI slave device. An SPI bus may be connected to one or more Devices. Each Device shares the MOSI, MISO and SCLK signals but is only active on the bus when the Host asserts the Device's individual CS line.
**Bus** A signal bus, common to all Devices connected to one Host. In general, a bus includes the following lines: MISO, MOSI, SCLK, one or more CS lines, and, optionally, QUADWP and QUADHD. So Devices are connected to the same lines, with the exception that each Device has its own CS line. Several Devices can also share one CS line if connected in the daisy-chain manner. **Bus** A signal bus, common to all Devices connected to one Host. In general, a bus includes the following lines: MISO, MOSI, SCLK, one or more CS lines, and, optionally, QUADWP and QUADHD. So Devices are connected to the same lines, with the exception that each Device has its own CS line. Several Devices can also share one CS line if connected in the daisy-chain manner.
**MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host. Also data1 signal in octal mode. **MOSI** Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device. Also data0 signal in Octal/OPI mode.
**MOSI** Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device. Also data0 signal in octal mode. **MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host. Also data1 signal in Octal/OPI mode.
**SCLK** Serial Clock. Oscillating signal generated by a Host that keeps the transmission of data bits in sync. **SCLK** Serial Clock. Oscillating signal generated by a Host that keeps the transmission of data bits in sync.
**CS** Chip Select. Allows a Host to select individual Device(s) connected to the bus in order to send or receive data. **CS** Chip Select. Allows a Host to select individual Device(s) connected to the bus in order to send or receive data.
**QUADWP** Write Protect signal. Used for 4-bit (qio/qout) transactions. Also for data2 signal in octal mode. **QUADWP** Write Protect signal. Used for 4-bit (qio/qout) transactions. Also for data2 signal in Octal/OPI mode.
**QUADHD** Hold signal. Used for 4-bit (qio/qout) transactions. Also for data3 signal in octal mode. **QUADHD** Hold signal. Used for 4-bit (qio/qout) transactions. Also for data3 signal in Octal/OPI mode.
**DATA4** Data4 signal in octal mode. **DATA4** Data4 signal in Octal/OPI mode.
**DATA5** Data5 signal in octal mode. **DATA5** Data5 signal in Octal/OPI mode.
**DATA6** Data6 signal in octal mode. **DATA6** Data6 signal in Octal/OPI mode.
**DATA7** Data7 signal in octal mode. **DATA7** Data7 signal in Octal/OPI mode.
**Assertion** The action of activating a line. **Assertion** The action of activating a line.
**De-assertion** The action of returning the line back to inactive (back to idle) status. **De-assertion** The action of returning the line back to inactive (back to idle) status.
**Transaction** One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction. **Transaction** One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction.
@@ -148,41 +148,59 @@ All the tasks that use interrupt transactions can be blocked by the queue. At th
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 us to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`. The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 us to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
.. _transaction-line-mode:
Transaction Line Mode Transaction Line Mode
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
Supported line modes are as follows: Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of these modes, set the member `flags` in the struct :cpp:type:`spi_transaction_t` as shown in the `Transaction Flag` column. If you want to check if corresponding IO pins are set or not, set the member `flags` in the :cpp:type:`spi_bus_config_t` as shown in the `Bus IO setting Flag` column.
====================== ====================== ====================== ==================
Mode name Command Line Width Address Line Width Data Line Width
====================== ====================== ====================== ==================
**Dual Output** 1 1 2
**Dual I/O** 1 2 2
**Quad Output** 1 1 4
**Quad I/O** 1 4 4
**Octal Output** 1 1 8
**OPI** 8 8 8
====================== ====================== ====================== ==================
To make use of these modes, `data0_io_num` to `data7_io_num` I/O pins in the struct :cpp:type:`spi_bus_config_t` and the member `flags` in the struct :cpp:type:`spi_bus_config_t` should be set as shown below: .. only:: not SOC_SPI_SUPPORT_OCT
+-------------------+--------------------------+ +--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Mode name | Flags | | Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
+===================+==========================+ +==============+====================+====================+=================+============================+=========================+
| Dual Output | | | Normal SPI | 1 | 1 | 1 | 0 | 0 |
+-------------------+ SPICOMMON_BUSFLAG_DUAL | +--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual I/O | | | Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
+-------------------+--------------------------+ | | | | | | |
| Quad Output | | | | | | | | SPICOMMON_BUSFLAG_DUAL |
+-------------------+ SPICOMMON_BUSFLAG_QUAD | +--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Quad I/O | | | Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
+-------------------+--------------------------+ | | | | | SPI_TRANS_MULTILINE_ADDR | |
| Octal Output | | +--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
+-------------------+ SPICOMMON_BUSFLAG_OCTAL | | Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
| OPI | | +--------------+--------------------+--------------------+-----------------+----------------------------+ |
+-------------------+--------------------------+ | Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
.. only:: SOC_SPI_SUPPORT_OCT
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
+==============+====================+====================+=================+============================+=========================+
| Normal SPI | 1 | 1 | 1 | 0 | 0 |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
| | | | | | |
| | | | | | SPICOMMON_BUSFLAG_DUAL |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
| Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Octal Output | 1 | 1 | 8 | SPI_TRANS_MODE_OCT | |
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
| OPI | 8 | 8 | 8 | SPI_TRANS_MODE_OCT | | SPICOMMON_BUSFLAG_OCTAL |
| | | | | SPI_TRANS_MULTILINE_ADDR | | |
| | | | | SPI_TRANS_MULTILINE_CMD | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
Command and Address Phases Command and Address Phases
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -191,7 +209,7 @@ During the command and address phases, the members :cpp:member:`cmd` and :cpp:me
If the lengths of the command and address phases need to be variable, declare the struct :cpp:type:`spi_transaction_ext_t`, set the flags :cpp:type:`SPI_TRANS_VARIABLE_CMD` and/or :cpp:type:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_ext_t::base` and configure the rest of base as usual. Then the length of each phase will be equal to :cpp:member:`command_bits` and :cpp:member:`address_bits` set in the struct :cpp:type:`spi_transaction_ext_t`. If the lengths of the command and address phases need to be variable, declare the struct :cpp:type:`spi_transaction_ext_t`, set the flags :cpp:type:`SPI_TRANS_VARIABLE_CMD` and/or :cpp:type:`SPI_TRANS_VARIABLE_ADDR` in the member :cpp:member:`spi_transaction_ext_t::base` and configure the rest of base as usual. Then the length of each phase will be equal to :cpp:member:`command_bits` and :cpp:member:`address_bits` set in the struct :cpp:type:`spi_transaction_ext_t`.
If using more than one data lines to transmit, please set flags at the member `flags` in the struct :cpp:type:`spi_transaction_t`. If the number of lines transmitting command is the same as that transmitting data, please set `MULTILINE_CMD`. If the number of lines transmitting address is the same as that transmitting data, please set `MULTILINE_ADDR`. If the command and address phase need to be as the same number of lines as data phase, you need to set `SPI_TRANS_MULTILINE_CMD` and/or `SPI_TRANS_MULTILINE_ADDR` to the `flags` member in the struct :cpp:type:`spi_transaction_t`. Also see :ref:`transaction-line-mode`.
Write and Read Phases Write and Read Phases
@@ -204,23 +222,7 @@ Normally, the data that needs to be transferred to or from a Device will be read
If these requirements are not satisfied, the transaction efficiency will be affected due to the allocation and copying of temporary buffers. If these requirements are not satisfied, the transaction efficiency will be affected due to the allocation and copying of temporary buffers.
If using more than one data lines to transmit, please set `SPI_DEVICE_HALFDUPLEX` flag at the member `flags` in the struct :cpp:type:`spi_device_interface_config_t`. And the member `flags` in the struct :cpp:type:`spi_transaction_t` should be set as below: If using more than one data lines to transmit, please set `SPI_DEVICE_HALFDUPLEX` flag for the member `flags` in the struct :cpp:type:`spi_device_interface_config_t`. And the member `flags` in the struct :cpp:type:`spi_transaction_t` should be set as described in :ref:`transaction-line-mode`.
+-------------------+--------------------+
| Mode name | Flags |
+===================+====================+
| Dual Output | |
+-------------------+ SPI_TRANS_MODE_DIO |
| Dual I/O | |
+-------------------+--------------------+
| Quad Output | |
+-------------------+ SPI_TRANS_MODE_QIO |
| Quad I/O | |
+-------------------+--------------------+
| Octal Output | |
+-------------------+ SPI_TRANS_MODE_OCT |
| OPI | |
+-------------------+--------------------+
.. only:: esp32 .. only:: esp32

View File

@@ -20,57 +20,53 @@ If you want to adapt this example to another type of display or pinout, check [l
### Hardware Connection ### Hardware Connection
If using default settings, the hardware connection can be as below: The connection between ESP Board and the LCD is as follows:
``` ```
Board LCD Screen ESP Board LCD Screen
+--------+ +---------------------------------+ +---------+ +---------------------------------+
| | | | | | | |
| 3V3 +--------------+ VCC +----------------------+ | | 3V3 +--------------+ VCC +----------------------+ |
| | | | | | | | | | | |
| GND +--------------+ GND | | | | GND +--------------+ GND | | |
| | | | | | | | | | | |
| 23 +--------------+ MOSI | | | | DATA0 +--------------+ MOSI | | |
| | | | | | | | | | | |
| 19 +--------------+ SCK | | | | PCLK +--------------+ SCK | | |
| | | | | | | | | | | |
| 22 +--------------+ CS | | | | CS +--------------+ CS | | |
| | | | | | | | | | | |
| 21 +--------------+ DC | | | | D/C +--------------+ D/C | | |
| | | | | | | | | | | |
| 18 +--------------+ RST | | | | RST +--------------+ RST | | |
| | | | | | | | | | | |
| 5 +--------------+ BCKL +----------------------+ | |BK_LIGHT +--------------+ BCKL +----------------------+ |
| | | | | | | |
+--------+ +---------------------------------+ +---------+ +---------------------------------+
``` ```
If not using default settings, the interface GPIOs should be set by macros in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where: The GPIO number used by this example can be changed in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where:
| GPIO number | LCD pin | | GPIO number | LCD pin |
| :-----------: | :--: | | ------------------------ | ------- |
| PIN_NUM_CLK | SCK | | EXAMPLE_PIN_NUM_PCLK | SCK |
| PIN_NUM_CS | CS | | EXAMPLE_PIN_NUM_CS | CS |
| PIN_NUM_DC | DC | | EXAMPLE_PIN_NUM_DC | DC |
| PIN_NUM_RST | RST | | EXAMPLE_PIN_NUM_RST | RST |
| PIN_NUM_BCKL | LED | | EXAMPLE_PIN_NUM_DATA0 | MOSI |
| PIN_NUM_MOSI | MOSI | | EXAMPLE_PIN_NUM_BK_LIGHT | BCKL |
Especially, please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c).
### 8-line LCD Usage #### Extra connections for 8-line LCD (Octal SPI)
Firstly, please run `idf.py menuconfig` and set the `Drive a LCD with 8 data lines` option at `Example Configuration`. Firstly, please run `idf.py menuconfig` and set the `Drive a LCD with 8 data lines` option at `Example Configuration`.
Check if the pins below are correctly connected as the settings by macros in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where: Change the extra GPOIs used by octal SPI in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where:
| GPIO number | LCD pin | | GPIO number | LCD pin |
| :-----------: | :--: | | ------------- | ------- |
| PIN_NUM_CLK | SCK |
| PIN_NUM_CS | CS |
| PIN_NUM_DC | DC |
| PIN_NUM_RST | RST |
| PIN_NUM_BCKL | LED |
| PIN_NUM_MOSI | D0 |
| PIN_NUM_DATA1 | D1 | | PIN_NUM_DATA1 | D1 |
| PIN_NUM_DATA2 | D2 | | PIN_NUM_DATA2 | D2 |
| PIN_NUM_DATA3 | D3 | | PIN_NUM_DATA3 | D3 |
@@ -79,7 +75,6 @@ Check if the pins below are correctly connected as the settings by macros in [lc
| PIN_NUM_DATA6 | D6 | | PIN_NUM_DATA6 | D6 |
| PIN_NUM_DATA7 | D7 | | PIN_NUM_DATA7 | D7 |
### Build and Flash ### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen. Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen.

View File

@@ -1,15 +1,5 @@
menu "Example Configuration" menu "Example Configuration"
config EXAMPLE_LCD_SPI_8_LINE_MODE
config LCD_OVERCLOCK
bool
prompt "Run LCD at higher clock speed than allowed"
default "n"
help
The ILI9341 and ST7789 specify that the maximum clock speed for the SPI interface is 10MHz. However,
in practice the driver chips work fine with a higher clock rate, and using that gives a better framerate.
Select this to try using the out-of-spec clock rate.
config LCD_SPI_8_LINE_MODE
bool bool
prompt "Drive a LCD with 8 data lines" prompt "Drive a LCD with 8 data lines"
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3

View File

@@ -12,47 +12,49 @@
#include "esp_lcd_panel_io.h" #include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h" #include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.h"
#include "esp_heap_caps.h"
#include "driver/spi_master.h" #include "driver/spi_master.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "pretty_effect.h" #include "pretty_effect.h"
#define LCD_HOST SPI2_HOST /*!< spi peripheral for LCD */ // Using SPI2 in the example, as it aslo supports octal modes on some targets
#define LCD_HOST SPI2_HOST
/**
* If not using the default settings, the SPI peripheral on LCD and the GPIO numbers can be
* changed below.
*/
#define PIN_NUM_MOSI 23 /*!< gpio number for LCD MOSI, also the gpio number for LCD DATA0 at 8-line mode */
#define PIN_NUM_CLK 19 /*!< gpio number for LCD clock */
#define PIN_NUM_CS 22 /*!< gpio number for LCD CS */
#define PIN_NUM_DC 21 /*!< gpio number for LCD DC */
#define PIN_NUM_RST 18 /*!< gpio number for LCD RST */
#define PIN_NUM_BCKL 5 /*!< gpio number for LCD Back Light */
#ifdef CONFIG_LCD_SPI_8_LINE_MODE // If using 8-line LCD
#define PIN_NUM_DATA1 6 /*!< gpio number for LCD DATA1 */
#define PIN_NUM_DATA2 7 /*!< gpio number for LCD DATA2 */
#define PIN_NUM_DATA3 8 /*!< gpio number for LCD DATA3 */
#define PIN_NUM_DATA4 9 /*!< gpio number for LCD DATA4 */
#define PIN_NUM_DATA5 10 /*!< gpio number for LCD DATA5 */
#define PIN_NUM_DATA6 11 /*!< gpio number for LCD DATA6 */
#define PIN_NUM_DATA7 12 /*!< gpio number for LCD DATA7 */
#endif
// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES (320)
#define EXAMPLE_LCD_V_RES (240)
// The SPI mode supported by the LCD
#define LCD_SPI_MODE 0
// To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. // To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many.
// More means more memory use, but less overhead for setting up / finishing transfers. Make sure 240 // More means more memory use, but less overhead for setting up / finishing transfers. Make sure 240
// is dividable by this. // is dividable by this.
#define PARALLEL_LINES 16 #define PARALLEL_LINES 16
// The number of frames to show before rotate the graph // The number of frames to show before rotate the graph
#define ROTATE_FRAME 30 #define ROTATE_FRAME 30
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (10 * 1000 * 1000)
#define EXAMPLE_LCD_BK_LIGHT_ON_LEVEL 0
#define EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL !EXAMPLE_LCD_BK_LIGHT_ON_LEVEL
#define EXAMPLE_PIN_NUM_DATA0 23 /*!< for 1-line SPI, this also refered as MOSI */
#define EXAMPLE_PIN_NUM_PCLK 19
#define EXAMPLE_PIN_NUM_CS 22
#define EXAMPLE_PIN_NUM_DC 21
#define EXAMPLE_PIN_NUM_RST 18
#define EXAMPLE_PIN_NUM_BK_LIGHT 5
// The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES 320
#define EXAMPLE_LCD_V_RES 240
// Bit number used to represent command and parameter
#define EXAMPLE_LCD_CMD_BITS 8
#define EXAMPLE_LCD_PARAM_BITS 8
#if CONFIG_EXAMPLE_LCD_SPI_8_LINE_MODE
#define EXAMPLE_PIN_NUM_DATA1 7
#define EXAMPLE_PIN_NUM_DATA2 8
#define EXAMPLE_PIN_NUM_DATA3 9
#define EXAMPLE_PIN_NUM_DATA4 10
#define EXAMPLE_PIN_NUM_DATA5 11
#define EXAMPLE_PIN_NUM_DATA6 12
#define EXAMPLE_PIN_NUM_DATA7 13
#endif // CONFIG_EXAMPLE_LCD_SPI_8_LINE_MODE
// Simple routine to generate some patterns and send them to the LCD. Because the // Simple routine to generate some patterns and send them to the LCD. Because the
// SPI driver handles transactions in the background, we can calculate the next line // SPI driver handles transactions in the background, we can calculate the next line
@@ -83,54 +85,52 @@ void app_main(void)
{ {
gpio_config_t bk_gpio_config = { gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT, .mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << PIN_NUM_BCKL .pin_bit_mask = 1ULL << EXAMPLE_PIN_NUM_BK_LIGHT
}; };
// Initialize the GPIO of backlight // Initialize the GPIO of backlight
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config)); ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
spi_bus_config_t buscfg = { spi_bus_config_t buscfg = {
.mosi_io_num = PIN_NUM_MOSI, .sclk_io_num = EXAMPLE_PIN_NUM_PCLK,
.sclk_io_num = PIN_NUM_CLK, .mosi_io_num = EXAMPLE_PIN_NUM_DATA0,
#ifdef CONFIG_LCD_SPI_8_LINE_MODE
.data1_io_num = PIN_NUM_DATA1,
.data2_io_num = PIN_NUM_DATA2,
.data3_io_num = PIN_NUM_DATA3,
.data4_io_num = PIN_NUM_DATA4,
.data5_io_num = PIN_NUM_DATA5,
.data6_io_num = PIN_NUM_DATA6,
.data7_io_num = PIN_NUM_DATA7,
.flags = SPICOMMON_BUSFLAG_OCTAL,
#else
.miso_io_num = -1, .miso_io_num = -1,
.quadwp_io_num = -1, .quadwp_io_num = -1,
.quadhd_io_num = -1, .quadhd_io_num = -1,
#endif
.max_transfer_sz = PARALLEL_LINES * EXAMPLE_LCD_H_RES * 2 + 8 .max_transfer_sz = PARALLEL_LINES * EXAMPLE_LCD_H_RES * 2 + 8
}; };
esp_lcd_panel_io_handle_t io_handle = NULL; #if CONFIG_EXAMPLE_LCD_SPI_8_LINE_MODE
esp_lcd_panel_io_spi_config_t io_config = { buscfg.data1_io_num = EXAMPLE_PIN_NUM_DATA1;
.dc_gpio_num = PIN_NUM_DC, buscfg.data2_io_num = EXAMPLE_PIN_NUM_DATA2;
.cs_gpio_num = PIN_NUM_CS, buscfg.data3_io_num = EXAMPLE_PIN_NUM_DATA3;
#ifdef CONFIG_LCD_OVERCLOCK buscfg.data4_io_num = EXAMPLE_PIN_NUM_DATA4;
.pclk_hz = 26 * 1000 * 1000, // Clock out at 26 MHz buscfg.data5_io_num = EXAMPLE_PIN_NUM_DATA5;
#else buscfg.data6_io_num = EXAMPLE_PIN_NUM_DATA6;
.pclk_hz = 10 * 1000 * 1000, // Clock out at 10 MHz buscfg.data7_io_num = EXAMPLE_PIN_NUM_DATA7;
buscfg.flags = SPICOMMON_BUSFLAG_OCTAL;
#endif #endif
.spi_mode = LCD_SPI_MODE,
.trans_queue_depth = 7,
#ifdef CONFIG_LCD_SPI_8_LINE_MODE
.flags.octal_mode = 1,
#endif
};
// Initialize the SPI bus // Initialize the SPI bus
ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO)); ESP_ERROR_CHECK(spi_bus_initialize(LCD_HOST, &buscfg, SPI_DMA_CH_AUTO));
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_io_spi_config_t io_config = {
.dc_gpio_num = EXAMPLE_PIN_NUM_DC,
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
.spi_mode = 0,
.trans_queue_depth = 10,
};
#if CONFIG_EXAMPLE_LCD_SPI_8_LINE_MODE
io_config.spi_mode = 3; // using mode 3 to simulate Intel 8080 timing
io_config.flags.octal_mode = 1;
#endif
// Attach the LCD to the SPI bus // Attach the LCD to the SPI bus
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle)); ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_HOST, &io_config, &io_handle));
esp_lcd_panel_handle_t panel_handle = NULL; esp_lcd_panel_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = { esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = PIN_NUM_RST, .reset_gpio_num = EXAMPLE_PIN_NUM_RST,
.color_space = ESP_LCD_COLOR_SPACE_BGR, .color_space = ESP_LCD_COLOR_SPACE_BGR,
.bits_per_pixel = 16, .bits_per_pixel = 16,
}; };
@@ -139,7 +139,7 @@ void app_main(void)
// Turn off backlight to avoid unpredictable display on the LCD screen while initializing // Turn off backlight to avoid unpredictable display on the LCD screen while initializing
// the LCD panel driver. (Different LCD screens may need different levels) // the LCD panel driver. (Different LCD screens may need different levels)
ESP_ERROR_CHECK(gpio_set_level(PIN_NUM_BCKL, 1)); ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_OFF_LEVEL));
// Reset the display // Reset the display
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
@@ -151,7 +151,7 @@ void app_main(void)
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true)); ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
// Turn on backlight (Different LCD screens may need different levels) // Turn on backlight (Different LCD screens may need different levels)
ESP_ERROR_CHECK(gpio_set_level(PIN_NUM_BCKL, 0)); ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_PIN_NUM_BK_LIGHT, EXAMPLE_LCD_BK_LIGHT_ON_LEVEL));
// Initialize the effect displayed // Initialize the effect displayed
ESP_ERROR_CHECK(pretty_effect_init()); ESP_ERROR_CHECK(pretty_effect_init());
@@ -173,5 +173,4 @@ void app_main(void)
display_pretty_colors(panel_handle); display_pretty_colors(panel_handle);
is_rotated = !is_rotated; is_rotated = !is_rotated;
} }
} }