spi_master:support octal mode for esp32s2 and esp32s3

Add support for 8-line spi for lcd on esp32s2 and esp32s3

Closes https://github.com/espressif/esp-idf/issues/6371
This commit is contained in:
bizhuangyang 2021-07-09 16:46:27 +08:00 committed by morris
parent c81d45280e
commit 8143832041
30 changed files with 709 additions and 414 deletions

View File

@ -63,7 +63,10 @@ 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_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
/** /**
@ -95,18 +98,36 @@ typedef spi_common_dma_t spi_dma_chan_t;
* @note Be advised that the slave driver does not use the quadwp/quadhd lines and fields in spi_bus_config_t refering to these lines will be ignored and can thus safely be left uninitialized. * @note Be advised that the slave driver does not use the quadwp/quadhd lines and fields in spi_bus_config_t refering to these lines will be ignored and can thus safely be left uninitialized.
*/ */
typedef struct { typedef struct {
int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used. union {
int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used. int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.
int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used. int data0_io_num; ///< GPIO pin for spi data0 signal in quad/octal mode, or -1 if not used.
int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used. };
int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used. union {
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 miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.
uint32_t flags; ///< Abilities of bus to be checked by the driver. Or-ed value of ``SPICOMMON_BUSFLAG_*`` flags. int data1_io_num; ///< GPIO pin for spi data1 signal in quad/octal mode, or -1 if not used.
int intr_flags; /**< Interrupt flag for the bus to set the priority, and IRAM attribute, see };
* ``esp_intr_alloc.h``. Note that the EDGE, INTRDISABLED attribute are ignored int sclk_io_num; ///< GPIO pin for SPI Clock signal, or -1 if not used.
* by the driver. Note that if ESP_INTR_FLAG_IRAM is set, ALL the callbacks of union {
* the driver, and their callee functions, should be put in the IRAM. int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal, or -1 if not used.
*/ int data2_io_num; ///< GPIO pin for spi data2 signal in quad/octal mode, or -1 if not used.
};
union {
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.
};
#if SOC_SPI_SUPPORT_OCT
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 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.
#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.
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
* ``esp_intr_alloc.h``. Note that the EDGE, INTRDISABLED attribute are ignored
* by the driver. Note that if ESP_INTR_FLAG_IRAM is set, ALL the callbacks of
* the driver, and their callee functions, should be put in the IRAM.
*/
} spi_bus_config_t; } spi_bus_config_t;

View File

@ -155,6 +155,8 @@ esp_err_t spicommon_slave_free_dma(spi_host_device_t host_id);
* - ``SPICOMMON_BUSFLAG_DUAL``: Make sure both MISO and MOSI are output capable so that DIO mode is capable. * - ``SPICOMMON_BUSFLAG_DUAL``: Make sure both MISO and MOSI are output capable so that DIO mode is capable.
* - ``SPICOMMON_BUSFLAG_WPHD`` Make sure WP and HD are set to valid output GPIOs. * - ``SPICOMMON_BUSFLAG_WPHD`` Make sure WP and HD are set to valid output GPIOs.
* - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``. * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
* - ``SPICOMMON_BUSFLAG_IO4_IO7``: Make sure spi data4 ~ spi data7 are set to valid output GPIOs.
* - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``.
* @param[out] flags_o A SPICOMMON_BUSFLAG_* flag combination of bus abilities will be written to this address. * @param[out] flags_o A SPICOMMON_BUSFLAG_* flag combination of bus abilities will be written to this address.
* Leave to NULL if not needed. * Leave to NULL if not needed.
* - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: The bus is connected to iomux pins. * - ``SPICOMMON_BUSFLAG_IOMUX_PINS``: The bus is connected to iomux pins.
@ -163,6 +165,8 @@ esp_err_t spicommon_slave_free_dma(spi_host_device_t host_id);
* - ``SPICOMMON_BUSFLAG_DUAL``: The bus is capable with DIO mode. * - ``SPICOMMON_BUSFLAG_DUAL``: The bus is capable with DIO mode.
* - ``SPICOMMON_BUSFLAG_WPHD`` The bus has WP and HD connected. * - ``SPICOMMON_BUSFLAG_WPHD`` The bus has WP and HD connected.
* - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``. * - ``SPICOMMON_BUSFLAG_QUAD``: Combination of ``SPICOMMON_BUSFLAG_DUAL`` and ``SPICOMMON_BUSFLAG_WPHD``.
* - ``SPICOMMON_BUSFLAG_IO4_IO7``: The bus has spi data4 ~ spi data7 connected.
* - ``SPICOMMON_BUSFLAG_OCTAL``: Combination of ``SPICOMMON_BUSFLAG_QUAL`` and ``SPICOMMON_BUSFLAG_IO4_IO7``.
* @return * @return
* - ESP_ERR_INVALID_ARG if parameter is invalid * - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success * - ESP_OK on success

View File

@ -104,8 +104,13 @@ typedef struct {
#define SPI_TRANS_VARIABLE_CMD (1<<5) ///< Use the ``command_bits`` in ``spi_transaction_ext_t`` rather than default value in ``spi_device_interface_config_t``. #define SPI_TRANS_VARIABLE_CMD (1<<5) ///< Use the ``command_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_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_SET_CD (1<<7) ///< Set the CD pin
#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 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``)
#if SOC_SPI_SUPPORT_OCT
#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

@ -15,6 +15,7 @@
#include "esp_err.h" #include "esp_err.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/soc_pins.h"
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "driver/periph_ctrl.h" #include "driver/periph_ctrl.h"
@ -350,32 +351,113 @@ esp_err_t spicommon_slave_free_dma(spi_host_device_t host_id)
} }
//----------------------------------------------------------IO general-------------------------------------------------------// //----------------------------------------------------------IO general-------------------------------------------------------//
static bool bus_uses_iomux_pins(spi_host_device_t host, const spi_bus_config_t* bus_config) #if SOC_SPI_SUPPORT_OCT
static bool check_iomux_pins_oct(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
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};
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,
SPI2_IOMUX_PIN_NUM_CLK_OCT, SPI2_IOMUX_PIN_NUM_IO4_OCT, SPI2_IOMUX_PIN_NUM_IO5_OCT, SPI2_IOMUX_PIN_NUM_IO6_OCT, SPI2_IOMUX_PIN_NUM_IO7_OCT};
for (size_t i = 0; i < sizeof(io_nums)/sizeof(io_nums[0]); i++) {
if (io_nums[i] >= 0 && io_nums[i] != io_mux_nums[i]) {
return false;
}
}
return true;
}
#endif
static bool check_iomux_pins_quad(spi_host_device_t host, const spi_bus_config_t* bus_config)
{ {
if (bus_config->sclk_io_num>=0 && if (bus_config->sclk_io_num>=0 &&
bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) { bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) {
return false; return false;
} }
if (bus_config->quadwp_io_num>=0 && if (bus_config->quadwp_io_num>=0 &&
bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) { bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) {
return false; return false;
} }
if (bus_config->quadhd_io_num>=0 && if (bus_config->quadhd_io_num>=0 &&
bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) { bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) {
return false; return false;
} }
if (bus_config->mosi_io_num >= 0 && if (bus_config->mosi_io_num >= 0 &&
bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) { bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) {
return false; return false;
} }
if (bus_config->miso_io_num>=0 && if (bus_config->miso_io_num>=0 &&
bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) { bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) {
return false; return false;
} }
return true; return true;
} }
static bool bus_uses_iomux_pins(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
//Check if SPI pins could be routed to iomux.
#if SOC_SPI_SUPPORT_OCT
//The io mux pins available for Octal mode is not the same as the ones we use for non-Octal mode.
if ((bus_config->flags & SPICOMMON_BUSFLAG_OCTAL) == SPICOMMON_BUSFLAG_OCTAL) {
return check_iomux_pins_oct(host, bus_config);
}
#endif
return check_iomux_pins_quad(host, bus_config);
}
#if SOC_SPI_SUPPORT_OCT
static void bus_iomux_pins_set_oct(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
assert(host == SPI2_HOST);
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};
int io_signals[] = {spi_periph_signal[host].spid_in, spi_periph_signal[host].spiq_in, spi_periph_signal[host].spiwp_in,
spi_periph_signal[host].spihd_in,spi_periph_signal[host].spiclk_in, spi_periph_signal[host].spid4_out,
spi_periph_signal[host].spid5_out, spi_periph_signal[host].spid6_out, spi_periph_signal[host].spid7_out};
for (size_t i = 0; i < sizeof(io_nums)/sizeof(io_nums[0]); i++) {
if (io_nums[i] > 0) {
gpio_iomux_in(io_nums[i], io_signals[i]);
// In Octal mode use function channel 2
gpio_iomux_out(io_nums[i], SPI2_FUNC_NUM_OCT, false);
}
}
}
#endif //SOC_SPI_SUPPORT_OCT
static void bus_iomux_pins_set_quad(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
if (bus_config->mosi_io_num >= 0) {
gpio_iomux_in(bus_config->mosi_io_num, spi_periph_signal[host].spid_in);
gpio_iomux_out(bus_config->mosi_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->miso_io_num >= 0) {
gpio_iomux_in(bus_config->miso_io_num, spi_periph_signal[host].spiq_in);
gpio_iomux_out(bus_config->miso_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->quadwp_io_num >= 0) {
gpio_iomux_in(bus_config->quadwp_io_num, spi_periph_signal[host].spiwp_in);
gpio_iomux_out(bus_config->quadwp_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->quadhd_io_num >= 0) {
gpio_iomux_in(bus_config->quadhd_io_num, spi_periph_signal[host].spihd_in);
gpio_iomux_out(bus_config->quadhd_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->sclk_io_num >= 0) {
gpio_iomux_in(bus_config->sclk_io_num, spi_periph_signal[host].spiclk_in);
gpio_iomux_out(bus_config->sclk_io_num, spi_periph_signal[host].func, false);
}
}
static void bus_iomux_pins_set(spi_host_device_t host, const spi_bus_config_t* bus_config)
{
#if SOC_SPI_SUPPORT_OCT
if ((bus_config->flags & SPICOMMON_BUSFLAG_OCTAL) == SPICOMMON_BUSFLAG_OCTAL) {
bus_iomux_pins_set_oct(host, bus_config);
return;
}
#endif
bus_iomux_pins_set_quad(host, bus_config);
}
/* /*
Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pins. Feed it a host number and a Do the common stuff to hook up a SPI host to a bus defined by a bunch of GPIO pins. Feed it a host number and a
bus config struct and it'll set up the GPIO matrix and enable the device. If a pin is set to non-negative value, bus config struct and it'll set up the GPIO matrix and enable the device. If a pin is set to non-negative value,
@ -383,6 +465,17 @@ it should be able to be initialized.
*/ */
esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o) esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_config_t *bus_config, uint32_t flags, uint32_t* flags_o)
{ {
#if SOC_SPI_SUPPORT_OCT
// In the driver of previous version, spi data4 ~ spi data7 are not in spi_bus_config_t struct. So the new-added pins come as 0
// if they are not really set. Add this boolean variable to check if the user has set spi data4 ~spi data7 pins .
bool io4_7_is_blank = !bus_config->data4_io_num && !bus_config->data5_io_num && !bus_config->data6_io_num && !bus_config->data7_io_num;
// This boolean variable specifies if user sets pins used for octal mode (users can set spi data4 ~ spi data7 to -1).
bool io4_7_enabled = !io4_7_is_blank && bus_config->data4_io_num >= 0 && bus_config->data5_io_num >= 0 &&
bus_config->data6_io_num >= 0 && bus_config->data7_io_num >= 0;
SPI_CHECK((flags & SPICOMMON_BUSFLAG_MASTER) || !((flags & SPICOMMON_BUSFLAG_OCTAL) == SPICOMMON_BUSFLAG_OCTAL), "Octal SPI mode / OPI mode only works when SPI is used as Master", ESP_ERR_INVALID_ARG);
SPI_CHECK(host == SPI2_HOST || !((flags & SPICOMMON_BUSFLAG_OCTAL) == SPICOMMON_BUSFLAG_OCTAL), "Only SPI2 supports Octal SPI mode / OPI mode", ESP_ERR_INVALID_ARG);
#endif //SOC_SPI_SUPPORT_OCT
uint32_t temp_flag = 0; uint32_t temp_flag = 0;
bool miso_need_output; bool miso_need_output;
@ -414,13 +507,36 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
if (bus_config->quadhd_io_num>=0) { if (bus_config->quadhd_io_num>=0) {
SPI_CHECK_PIN(bus_config->quadhd_io_num, "hd", hd_need_output); SPI_CHECK_PIN(bus_config->quadhd_io_num, "hd", hd_need_output);
} }
#if SOC_SPI_SUPPORT_OCT
const bool io4_need_output = true;
const bool io5_need_output = true;
const bool io6_need_output = true;
const bool io7_need_output = true;
// set flags for OCTAL mode according to the existence of spi data4 ~ spi data7
if (io4_7_enabled) {
temp_flag |= SPICOMMON_BUSFLAG_IO4_IO7;
if (bus_config->data4_io_num >= 0) {
SPI_CHECK_PIN(bus_config->data4_io_num, "spi data4", io4_need_output);
}
if (bus_config->data5_io_num >= 0) {
SPI_CHECK_PIN(bus_config->data5_io_num, "spi data5", io5_need_output);
}
if (bus_config->data6_io_num >= 0) {
SPI_CHECK_PIN(bus_config->data6_io_num, "spi data6", io6_need_output);
}
if (bus_config->data7_io_num >= 0) {
SPI_CHECK_PIN(bus_config->data7_io_num, "spi data7", io7_need_output);
}
}
#endif //SOC_SPI_SUPPORT_OCT
//set flags for QUAD mode according to the existence of wp and hd //set flags for QUAD mode according to the existence of wp and hd
if (bus_config->quadhd_io_num >= 0 && bus_config->quadwp_io_num >= 0) temp_flag |= SPICOMMON_BUSFLAG_WPHD; if (bus_config->quadhd_io_num >= 0 && bus_config->quadwp_io_num >= 0) temp_flag |= SPICOMMON_BUSFLAG_WPHD;
if (bus_config->mosi_io_num >= 0) { if (bus_config->mosi_io_num >= 0) {
temp_flag |= SPICOMMON_BUSFLAG_MOSI; temp_flag |= SPICOMMON_BUSFLAG_MOSI;
SPI_CHECK_PIN(bus_config->mosi_io_num, "mosi", mosi_need_output); SPI_CHECK_PIN(bus_config->mosi_io_num, "mosi", mosi_need_output);
} }
if (bus_config->miso_io_num>=0) { if (bus_config->miso_io_num >= 0) {
temp_flag |= SPICOMMON_BUSFLAG_MISO; temp_flag |= SPICOMMON_BUSFLAG_MISO;
SPI_CHECK_PIN(bus_config->miso_io_num, "miso", miso_need_output); SPI_CHECK_PIN(bus_config->miso_io_num, "miso", miso_need_output);
} }
@ -443,12 +559,29 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
if (missing_flag != 0) { if (missing_flag != 0) {
//check pins existence //check pins existence
if (missing_flag & SPICOMMON_BUSFLAG_SCLK) ESP_LOGE(SPI_TAG, "sclk pin required."); if (missing_flag & SPICOMMON_BUSFLAG_SCLK) {
if (missing_flag & SPICOMMON_BUSFLAG_MOSI) ESP_LOGE(SPI_TAG, "mosi pin required."); ESP_LOGE(SPI_TAG, "sclk pin required.");
if (missing_flag & SPICOMMON_BUSFLAG_MISO) ESP_LOGE(SPI_TAG, "miso pin required."); }
if (missing_flag & SPICOMMON_BUSFLAG_DUAL) ESP_LOGE(SPI_TAG, "not both mosi and miso output capable"); if (missing_flag & SPICOMMON_BUSFLAG_MOSI) {
if (missing_flag & SPICOMMON_BUSFLAG_WPHD) ESP_LOGE(SPI_TAG, "both wp and hd required."); ESP_LOGE(SPI_TAG, "mosi pin required.");
if (missing_flag & SPICOMMON_BUSFLAG_IOMUX_PINS) ESP_LOGE(SPI_TAG, "not using iomux pins"); }
if (missing_flag & SPICOMMON_BUSFLAG_MISO) {
ESP_LOGE(SPI_TAG, "miso pin required.");
}
if (missing_flag & SPICOMMON_BUSFLAG_DUAL) {
ESP_LOGE(SPI_TAG, "not both mosi and miso output capable");
}
if (missing_flag & SPICOMMON_BUSFLAG_WPHD) {
ESP_LOGE(SPI_TAG, "both wp and hd required.");
}
if (missing_flag & SPICOMMON_BUSFLAG_IOMUX_PINS) {
ESP_LOGE(SPI_TAG, "not using iomux pins");
}
#if SOC_SPI_SUPPORT_OCT
if (missing_flag & SPICOMMON_BUSFLAG_IO4_IO7) {
ESP_LOGE(SPI_TAG, "spi data4 ~ spi data7 are required.");
}
#endif
SPI_CHECK(missing_flag == 0, "not all required capabilities satisfied.", ESP_ERR_INVALID_ARG); SPI_CHECK(missing_flag == 0, "not all required capabilities satisfied.", ESP_ERR_INVALID_ARG);
} }
@ -456,27 +589,7 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
//All SPI iomux pin selections resolve to 1, so we put that here instead of trying to figure //All SPI iomux pin selections resolve to 1, so we put that here instead of trying to figure
//out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway. //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
ESP_LOGD(SPI_TAG, "SPI%d use iomux pins.", host+1); ESP_LOGD(SPI_TAG, "SPI%d use iomux pins.", host+1);
if (bus_config->mosi_io_num >= 0) { bus_iomux_pins_set(host, bus_config);
gpio_iomux_in(bus_config->mosi_io_num, spi_periph_signal[host].spid_in);
gpio_iomux_out(bus_config->mosi_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->miso_io_num >= 0) {
gpio_iomux_in(bus_config->miso_io_num, spi_periph_signal[host].spiq_in);
gpio_iomux_out(bus_config->miso_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->quadwp_io_num >= 0) {
gpio_iomux_in(bus_config->quadwp_io_num, spi_periph_signal[host].spiwp_in);
gpio_iomux_out(bus_config->quadwp_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->quadhd_io_num >= 0) {
gpio_iomux_in(bus_config->quadhd_io_num, spi_periph_signal[host].spihd_in);
gpio_iomux_out(bus_config->quadhd_io_num, spi_periph_signal[host].func, false);
}
if (bus_config->sclk_io_num >= 0) {
gpio_iomux_in(bus_config->sclk_io_num, spi_periph_signal[host].spiclk_in);
gpio_iomux_out(bus_config->sclk_io_num, spi_periph_signal[host].func, false);
}
temp_flag |= SPICOMMON_BUSFLAG_IOMUX_PINS;
} else { } else {
//Use GPIO matrix //Use GPIO matrix
ESP_LOGD(SPI_TAG, "SPI%d use gpio matrix.", host+1); ESP_LOGD(SPI_TAG, "SPI%d use gpio matrix.", host+1);
@ -537,6 +650,26 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
#endif #endif
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], FUNC_GPIO); gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], FUNC_GPIO);
} }
#if SOC_SPI_SUPPORT_OCT
if (flags & SPICOMMON_BUSFLAG_OCTAL) {
int io_nums[] = {bus_config->data4_io_num, bus_config->data5_io_num, bus_config->data6_io_num, bus_config->data7_io_num};
uint8_t io_signals[4][2] = {{spi_periph_signal[host].spid4_out, spi_periph_signal[host].spid4_in},
{spi_periph_signal[host].spid5_out, spi_periph_signal[host].spid5_in},
{spi_periph_signal[host].spid6_out, spi_periph_signal[host].spid6_in},
{spi_periph_signal[host].spid7_out, spi_periph_signal[host].spid7_in}};
for (size_t i = 0; i < sizeof(io_nums) / sizeof(io_nums[0]); i++) {
if (io_nums[i] >= 0) {
gpio_set_direction(io_nums[i], GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(io_nums[i], io_signals[i][0], false, false);
esp_rom_gpio_connect_in_signal(io_nums[i], io_signals[i][1], false);
#if CONFIG_IDF_TARGET_ESP32S2
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[io_nums[i]]);
#endif
gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[io_nums[i]], FUNC_GPIO);
}
}
}
#endif //SOC_SPI_SUPPORT_OCT
} }
if (flags_o) *flags_o = temp_flag; if (flags_o) *flags_o = temp_flag;

View File

@ -530,13 +530,18 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
hal_trans.cmd = trans->cmd; hal_trans.cmd = trans->cmd;
hal_trans.addr = trans->addr; hal_trans.addr = trans->addr;
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 QIO/DIO if needed
hal_trans.io_mode = (trans->flags & SPI_TRANS_MODE_DIO ? //Set up OIO/QIO/DIO if needed
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_DIO : SPI_LL_IO_MODE_DUAL) : #if SOC_SPI_SUPPORT_OCT
(trans->flags & SPI_TRANS_MODE_QIO ? hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) : (trans->flags & SPI_TRANS_MODE_QIO) ? 4 :
SPI_LL_IO_MODE_NORMAL (trans->flags & SPI_TRANS_MODE_OCT) ? 8 : 1;
)); #else
hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1;
#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.cmd_lines = (trans->flags & 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;
@ -686,8 +691,13 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG); SPI_CHECK(trans_desc->rxlength <= bus_attr->max_transfer_sz*8, "rxdata transfer > host maximum", ESP_ERR_INVALID_ARG);
SPI_CHECK(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG); SPI_CHECK(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG);
//check working mode //check working mode
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "incompatible iface params", ESP_ERR_INVALID_ARG); #if SOC_SPI_SUPPORT_OCT
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "incompatible iface params", 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) && !is_half_duplex), "incompatible interface parameters", ESP_ERR_INVALID_ARG);
#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)) && !is_half_duplex), "incompatible interface parameters", 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

@ -80,6 +80,7 @@ typedef struct {
struct { struct {
unsigned int dc_as_cmd_phase: 1; /*!< D/C line value is encoded into SPI transaction command phase */ unsigned int dc_as_cmd_phase: 1; /*!< D/C line value is encoded into SPI transaction command phase */
unsigned int dc_low_on_data: 1; /*!< If this flag is enabled, DC line = 0 means transfer data, DC line = 1 means transfer command; vice versa */ unsigned int dc_low_on_data: 1; /*!< If this flag is enabled, DC line = 0 means transfer data, DC line = 1 means transfer command; vice versa */
unsigned int octal_mode: 1; /*!< transmit with octal mode (8 data lines), this mode is used to simulate Intel 8080 timing */
} flags; } flags;
} esp_lcd_panel_io_spi_config_t; } esp_lcd_panel_io_spi_config_t;

View File

@ -45,6 +45,7 @@ typedef struct {
struct { struct {
unsigned int dc_as_cmd_phase: 1; // D/C line value is encoded into SPI transaction command phase unsigned int dc_as_cmd_phase: 1; // D/C line value is encoded into SPI transaction command phase
unsigned int dc_data_level: 1; // Indicates the level of DC line when tranfering data unsigned int dc_data_level: 1; // Indicates the level of DC line when tranfering data
unsigned int octal_mode: 1; // Indicates whether the transmitting is enabled with octal mode (8 data lines)
} flags; } flags;
lcd_spi_trans_descriptor_t trans_pool[]; // Transaction pool lcd_spi_trans_descriptor_t trans_pool[]; // Transaction pool
} esp_lcd_panel_io_spi_t; } esp_lcd_panel_io_spi_t;
@ -60,6 +61,9 @@ 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 = 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,
@ -82,6 +86,7 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p
spi_panel_io->flags.dc_as_cmd_phase = io_config->flags.dc_as_cmd_phase; spi_panel_io->flags.dc_as_cmd_phase = io_config->flags.dc_as_cmd_phase;
spi_panel_io->flags.dc_data_level = !io_config->flags.dc_low_on_data; spi_panel_io->flags.dc_data_level = !io_config->flags.dc_low_on_data;
spi_panel_io->flags.octal_mode = io_config->flags.octal_mode;
spi_panel_io->on_color_trans_done = io_config->on_color_trans_done; spi_panel_io->on_color_trans_done = io_config->on_color_trans_done;
spi_panel_io->lcd_cmd_bits = io_config->lcd_cmd_bits; spi_panel_io->lcd_cmd_bits = io_config->lcd_cmd_bits;
spi_panel_io->lcd_param_bits = io_config->lcd_param_bits; spi_panel_io->lcd_param_bits = io_config->lcd_param_bits;
@ -147,6 +152,11 @@ 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) {
lcd_trans->base.flags |= (MULTILINE_CMD | 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;
} }
@ -192,6 +202,11 @@ 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) {
lcd_trans->base.flags |= (MULTILINE_CMD | 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

@ -12,21 +12,27 @@
#define TEST_LCD_H_RES (240) #define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280) #define TEST_LCD_V_RES (280)
#define TEST_SPI_CLK_GPIO (2) #define TEST_LCD_CLK_GPIO (2)
#define TEST_SPI_MOSI_GPIO (4) #define TEST_LCD_DATA0_GPIO (4)
#define TEST_LCD_RST_GPIO (5) #define TEST_LCD_RST_GPIO (5)
#define TEST_LCD_DC_GPIO (18) #define TEST_LCD_DC_GPIO (18)
#define TEST_LCD_BK_LIGHT_GPIO (19) #define TEST_LCD_BK_LIGHT_GPIO (19)
#define TEST_SPI_CS_GPIO (0) #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)
TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]") typedef bool (*trans_done_callback)(esp_lcd_panel_io_handle_t, void *, void *);
{
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(img);
static void lcd_initialize(bool is_8_line_lcd, esp_lcd_panel_io_handle_t *io_handle, esp_lcd_panel_handle_t *panel_handle,
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,
.pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO .pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO
@ -35,33 +41,57 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
spi_bus_config_t buscfg = { spi_bus_config_t buscfg = {
.miso_io_num = -1, .miso_io_num = -1,
.mosi_io_num = TEST_SPI_MOSI_GPIO, .mosi_io_num = TEST_LCD_DATA0_GPIO,
.sclk_io_num = TEST_SPI_CLK_GPIO, .sclk_io_num = TEST_LCD_CLK_GPIO,
.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 (is_8_line_lcd) {
buscfg.data1_io_num = TEST_LCD_DATA1_GPIO;
buscfg.data2_io_num = TEST_LCD_DATA2_GPIO;
buscfg.data3_io_num = TEST_LCD_DATA3_GPIO;
buscfg.data4_io_num = TEST_LCD_DATA4_GPIO;
buscfg.data5_io_num = TEST_LCD_DATA5_GPIO;
buscfg.data6_io_num = TEST_LCD_DATA6_GPIO;
buscfg.data7_io_num = TEST_LCD_DATA7_GPIO;
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_handle_t io_handle = NULL;
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_SPI_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 = 0, .spi_mode = spi_mode,
.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,
.user_data = user_data
}; };
TEST_ESP_OK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)TEST_SPI_HOST_ID, &io_config, &io_handle)); #if SOC_SPI_SUPPORT_OCT
if (is_8_line_lcd) {
io_config.flags.octal_mode = 1;
}
#endif
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_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = { esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = TEST_LCD_RST_GPIO, .reset_gpio_num = TEST_LCD_RST_GPIO,
.color_space = ESP_LCD_COLOR_SPACE_RGB, .color_space = ESP_LCD_COLOR_SPACE_RGB,
.bits_per_pixel = 16, .bits_per_pixel = 16,
}; };
TEST_ESP_OK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle)); TEST_ESP_OK(esp_lcd_new_panel_st7789(*io_handle, &panel_config, panel_handle));
}
static void lcd_panel_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle)
{
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
uint8_t *img = heap_caps_malloc(TEST_IMG_SIZE, MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(img);
// turn off backlight // turn off backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0); gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
@ -80,7 +110,8 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
memset(img, color_byte, TEST_IMG_SIZE); memset(img, color_byte, TEST_IMG_SIZE);
esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img); esp_lcd_panel_draw_bitmap(panel_handle, x_start, y_start, x_start + 100, y_start + 100, img);
} }
esp_lcd_panel_disp_off(panel_handle, true); // turn off screen // turn off screen
esp_lcd_panel_disp_off(panel_handle, true);
TEST_ESP_OK(esp_lcd_panel_del(panel_handle)); TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
TEST_ESP_OK(esp_lcd_panel_io_del(io_handle)); TEST_ESP_OK(esp_lcd_panel_io_del(io_handle));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST_ID)); TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST_ID));
@ -89,6 +120,24 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
#undef TEST_IMG_SIZE #undef TEST_IMG_SIZE
} }
#if SOC_SPI_SUPPORT_OCT
TEST_CASE("lcd panel with 8-line spi interface (st7789)", "[lcd]")
{
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(true, &io_handle, &panel_handle, 3, NULL, NULL);
lcd_panel_test(io_handle, panel_handle);
}
#endif // SOC_SPI_SUPPORT_OCT
TEST_CASE("lcd panel with 1-line spi interface (st7789)", "[lcd]")
{
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(false, &io_handle, &panel_handle, 0, NULL, NULL);
lcd_panel_test(io_handle, panel_handle);
}
// The following test shows a porting example of LVGL GUI library // The following test shows a porting example of LVGL GUI library
// To run the LVGL tests, you need to clone the LVGL library into components directory firstly // To run the LVGL tests, you need to clone the LVGL library into components directory firstly
#if CONFIG_LV_USE_USER_DATA #if CONFIG_LV_USE_USER_DATA
@ -101,50 +150,10 @@ static bool notify_lvgl_ready_to_flush(esp_lcd_panel_io_handle_t panel_io, void
return false; return false;
} }
TEST_CASE("lvgl gui with spi interface (st7789)", "[lcd][lvgl][ignore]") static void lvgl_gui_test(esp_lcd_panel_io_handle_t io_handle, esp_lcd_panel_handle_t panel_handle, lv_disp_t **disp)
{ {
// initialize LVGL graphics library // initialize LVGL graphics library
lv_disp_t *disp = NULL;
lv_init(); lv_init();
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO
};
TEST_ESP_OK(gpio_config(&bk_gpio_config));
spi_bus_config_t buscfg = {
.miso_io_num = -1,
.mosi_io_num = TEST_SPI_MOSI_GPIO,
.sclk_io_num = TEST_SPI_CLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = TEST_LCD_H_RES * TEST_LCD_V_RES * 2
};
TEST_ESP_OK(spi_bus_initialize(TEST_SPI_HOST_ID, &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 = TEST_LCD_DC_GPIO,
.cs_gpio_num = TEST_SPI_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.spi_mode = 0,
.trans_queue_depth = 10,
.on_color_trans_done = notify_lvgl_ready_to_flush,
.user_data = &disp, // we must use "address of disp" here, since the disp object has not been allocated
.lcd_cmd_bits = 8,
.lcd_param_bits = 8,
};
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_handle_t panel_handle = NULL;
esp_lcd_panel_dev_config_t panel_config = {
.reset_gpio_num = TEST_LCD_RST_GPIO,
.color_space = ESP_LCD_COLOR_SPACE_RGB,
.bits_per_pixel = 16,
};
TEST_ESP_OK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
// turn off backlight // turn off backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0); gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
esp_lcd_panel_reset(panel_handle); esp_lcd_panel_reset(panel_handle);
@ -155,7 +164,28 @@ TEST_CASE("lvgl gui with spi interface (st7789)", "[lcd][lvgl][ignore]")
// turn on backlight // turn on backlight
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1); gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 1);
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
TEST_CASE("lvgl gui with 8-line spi interface (st7789)", "[lcd][lvgl][ignore]")
{
lv_disp_t *disp = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(true, &io_handle, &panel_handle, 3, notify_lvgl_ready_to_flush, &disp);
lvgl_gui_test(io_handle, panel_handle, &disp);
}
#endif // SOC_SPI_SUPPORT_OCT
TEST_CASE("lvgl gui with 1-line spi interface (st7789)", "[lcd][lvgl][ignore]")
{
lv_disp_t *disp = NULL;
esp_lcd_panel_io_handle_t io_handle = NULL;
esp_lcd_panel_handle_t panel_handle = NULL;
lcd_initialize(false, &io_handle, &panel_handle, 0, notify_lvgl_ready_to_flush, &disp);
lvgl_gui_test(io_handle, panel_handle, &disp);
} }
#endif // CONFIG_LV_USE_USER_DATA #endif // CONFIG_LV_USE_USER_DATA

View File

@ -28,6 +28,7 @@
#include "esp32/rom/lldesc.h" #include "esp32/rom/lldesc.h"
#include "soc/spi_periph.h" #include "soc/spi_periph.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -37,6 +38,9 @@ extern "C" {
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST) #define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// Interrupt not used. Don't use in app. /// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_INT_EN | SPI_SLV_WR_STA_DONE | SPI_SLV_RD_STA_DONE | SPI_SLV_WR_BUF_DONE | SPI_SLV_RD_BUF_DONE) #define SPI_LL_UNUSED_INT_MASK (SPI_INT_EN | SPI_SLV_WR_STA_DONE | SPI_SLV_RD_STA_DONE | SPI_SLV_WR_BUF_DONE | SPI_SLV_RD_BUF_DONE)
/// These 2 masks together will set SPI transaction to one line mode
#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_DUAL | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_QIO)
#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_DUAL | SPI_FWRITE_QUAD | SPI_FWRITE_DIO | SPI_FWRITE_QIO)
/// Swap the bit order to its correct place to send /// Swap the bit order to its correct place to send
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) #define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency /// This is the expected clock frequency
@ -53,16 +57,6 @@ typedef uint32_t spi_ll_clock_val_t;
//On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA. //On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA.
typedef spi_dev_t spi_dma_dev_t; typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
SPI_LL_IO_MODE_DIO, ///< 2-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_DUAL, ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
SPI_LL_IO_MODE_QIO, ///< 4-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
/*------------------------------------------------------------------------------ /*------------------------------------------------------------------------------
* Control * Control
*----------------------------------------------------------------------------*/ *----------------------------------------------------------------------------*/
@ -449,37 +443,50 @@ static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
} }
/** /**
* Configure the io mode for the master to work at. * Configure the SPI transaction line mode for the master to use.
* *
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``. * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``.
*/ */
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_mode) static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode)
{ {
hw->ctrl.val &= ~(SPI_FREAD_DUAL | SPI_FREAD_QUAD | SPI_FREAD_DIO | SPI_FREAD_QIO); hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~(SPI_FWRITE_DUAL | SPI_FWRITE_QUAD | SPI_FWRITE_DIO | SPI_FWRITE_QIO); hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
switch (io_mode) { if (line_mode.cmd_lines > 1) {
case SPI_LL_IO_MODE_DIO: abort();
hw->ctrl.fread_dio = 1; }
hw->user.fwrite_dio = 1; switch (line_mode.data_lines) {
break; case 2:
case SPI_LL_IO_MODE_DUAL: if (line_mode.addr_lines == 1) {
hw->ctrl.fread_dual = 1; // 1-line-cmd + 1-line-addr + 2-line-data
hw->user.fwrite_dual = 1; hw->ctrl.fread_dual = 1;
break; hw->user.fwrite_dual = 1;
case SPI_LL_IO_MODE_QIO: } else if (line_mode.addr_lines == 2) {
hw->ctrl.fread_qio = 1; // 1-line-cmd + 2-line-addr + 2-line-data
hw->user.fwrite_qio = 1; hw->ctrl.fread_dio = 1;
break; hw->user.fwrite_dio = 1;
case SPI_LL_IO_MODE_QUAD: } else {
hw->ctrl.fread_quad = 1; abort();
hw->user.fwrite_quad = 1; }
break; hw->ctrl.fastrd_mode = 1;
default: break;
break; case 4:
}; if (line_mode.addr_lines == 1) {
if (io_mode != SPI_LL_IO_MODE_NORMAL) { // 1-line-cmd + 1-line-addr + 4-line-data
hw->ctrl.fastrd_mode = 1; hw->ctrl.fread_quad = 1;
hw->user.fwrite_quad = 1;
} else if (line_mode.addr_lines == 4) {
// 1-line-cmd + 4-line-addr + 4-line-data
hw->ctrl.fread_qio = 1;
hw->user.fwrite_qio = 1;
} else {
abort();
}
hw->ctrl.fastrd_mode = 1;
break;
default:
// 1-line-cmd + 1-line-addr + 1-line-data
break;
} }
} }

View File

@ -30,6 +30,7 @@
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -37,6 +38,9 @@ extern "C" {
/// Interrupt not used. Don't use in app. /// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA) #define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA)
/// These 2 masks together will set SPI transaction to one line mode
#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_QUAD | SPI_FADDR_DUAL)
#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_QUAD | SPI_FWRITE_DUAL)
/// Swap the bit order to its correct place to send /// Swap the bit order to its correct place to send
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) #define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency /// This is the expected clock frequency
@ -51,15 +55,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_t; typedef uint32_t spi_ll_clock_val_t;
typedef spi_dev_t spi_dma_dev_t; typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
SPI_LL_IO_MODE_DIO, ///< 2-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_DUAL, ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
SPI_LL_IO_MODE_QIO, ///< 4-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
// Type definition of all supported interrupts // Type definition of all supported interrupts
typedef enum { typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -540,41 +535,23 @@ static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
} }
/** /**
* Configure the io mode for the master to work at. * Configure the SPI transaction line mode for the master to use.
* *
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``. * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``.
*/ */
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_mode) static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode)
{ {
if (io_mode == SPI_LL_IO_MODE_DIO || io_mode == SPI_LL_IO_MODE_DUAL) { hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->ctrl.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fread_dual = 1; hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->user.fwrite_dual = 1; hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.fcmd_quad = 0; hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.faddr_quad = 0; hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = 0; hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->user.fwrite_quad = 0; hw->ctrl.fread_quad = (line_mode.data_lines == 4);
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) { hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fcmd_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0;
hw->ctrl.faddr_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0;
hw->ctrl.fread_quad = 1;
hw->user.fwrite_quad = 1;
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
} else {
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
}
} }
/** /**

View File

@ -30,6 +30,7 @@
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -37,6 +38,9 @@ extern "C" {
/// Interrupt not used. Don't use in app. /// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA) #define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA)
/// These 2 masks together will set SPI transaction to one line mode
#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_QUAD | SPI_FADDR_DUAL)
#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_QUAD | SPI_FWRITE_DUAL)
/// Swap the bit order to its correct place to send /// Swap the bit order to its correct place to send
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) #define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency /// This is the expected clock frequency
@ -51,15 +55,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_t; typedef uint32_t spi_ll_clock_val_t;
typedef spi_dev_t spi_dma_dev_t; typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
SPI_LL_IO_MODE_DIO, ///< 2-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_DUAL, ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
SPI_LL_IO_MODE_QIO, ///< 4-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
// Type definition of all supported interrupts // Type definition of all supported interrupts
typedef enum { typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -538,41 +533,23 @@ static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
} }
/** /**
* Configure the io mode for the master to work at. * Configure the SPI transaction line mode for the master to use.
* *
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``. * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``.
*/ */
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_mode) static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode)
{ {
if (io_mode == SPI_LL_IO_MODE_DIO || io_mode == SPI_LL_IO_MODE_DUAL) { hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->ctrl.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fread_dual = 1; hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->user.fwrite_dual = 1; hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.fcmd_quad = 0; hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.faddr_quad = 0; hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = 0; hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->user.fwrite_quad = 0; hw->ctrl.fread_quad = (line_mode.data_lines == 4);
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) { hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fcmd_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0;
hw->ctrl.faddr_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0;
hw->ctrl.fread_quad = 1;
hw->user.fwrite_quad = 1;
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
} else {
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
}
} }
/** /**

View File

@ -30,6 +30,7 @@
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -39,6 +40,10 @@ extern "C" {
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST) #define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// Interrupt not used. Don't use in app. /// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_INT_TRANS_DONE_EN | SPI_INT_WR_DMA_DONE_EN | SPI_INT_RD_DMA_DONE_EN | SPI_INT_WR_BUF_DONE_EN | SPI_INT_RD_BUF_DONE_EN) #define SPI_LL_UNUSED_INT_MASK (SPI_INT_TRANS_DONE_EN | SPI_INT_WR_DMA_DONE_EN | SPI_INT_RD_DMA_DONE_EN | SPI_INT_WR_BUF_DONE_EN | SPI_INT_RD_BUF_DONE_EN)
/// These 2 masks together will set SPI transaction to one line mode
#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_OCT | SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_OCT | \
SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_OCT | SPI_FADDR_QUAD | SPI_FADDR_DUAL)
#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_OCT | SPI_FWRITE_QUAD | SPI_FWRITE_DUAL)
/// Swap the bit order to its correct place to send /// Swap the bit order to its correct place to send
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) #define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency /// This is the expected clock frequency
@ -55,15 +60,6 @@ typedef uint32_t spi_ll_clock_val_t;
//On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA. //On ESP32-S2 and earlier chips, DMA registers are part of SPI registers. So set the registers of SPI peripheral to control DMA.
typedef spi_dev_t spi_dma_dev_t; typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
SPI_LL_IO_MODE_DIO, ///< 2-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_DUAL, ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
SPI_LL_IO_MODE_QIO, ///< 4-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
/// Type definition of all supported interrupts /// Type definition of all supported interrupts
typedef enum { typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -508,41 +504,27 @@ static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
} }
/** /**
* Configure the io mode for the master to work at. * Configure the SPI transaction line mode for the master to use.
* *
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``. * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``.
*/ */
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_mode) static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode)
{ {
if (io_mode == SPI_LL_IO_MODE_DIO || io_mode == SPI_LL_IO_MODE_DUAL) { hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->ctrl.fcmd_dual= (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.faddr_dual= (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fread_dual=1; hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->user.fwrite_dual=1; hw->ctrl.fcmd_oct = (line_mode.cmd_lines == 8);
hw->ctrl.fcmd_quad = 0; hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = 0; hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.fread_quad = 0; hw->ctrl.faddr_oct = (line_mode.addr_lines == 8);
hw->user.fwrite_quad = 0; hw->ctrl.fread_dual = (line_mode.data_lines == 2);
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) { hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fcmd_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0; hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->ctrl.faddr_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0; hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fread_quad=1; hw->ctrl.fread_oct = (line_mode.data_lines == 8);
hw->user.fwrite_quad=1; hw->user.fwrite_oct = (line_mode.data_lines == 8);
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
} else {
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
}
} }
static inline void spi_ll_slave_set_seg_mode(spi_dev_t *hw, bool seg_trans) static inline void spi_ll_slave_set_seg_mode(spi_dev_t *hw, bool seg_trans)

View File

@ -30,6 +30,7 @@
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -37,6 +38,11 @@ extern "C" {
/// Interrupt not used. Don't use in app. /// Interrupt not used. Don't use in app.
#define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA) #define SPI_LL_UNUSED_INT_MASK (SPI_TRANS_DONE_INT_ENA | SPI_SLV_WR_DMA_DONE_INT_ENA | SPI_SLV_RD_DMA_DONE_INT_ENA | SPI_SLV_WR_BUF_DONE_INT_ENA | SPI_SLV_RD_BUF_DONE_INT_ENA)
/// These 2 masks together will set SPI transaction to one line mode
#define SPI_LL_ONE_LINE_CTRL_MASK (SPI_FREAD_OCT | SPI_FREAD_QUAD | SPI_FREAD_DUAL | SPI_FCMD_OCT | \
SPI_FCMD_QUAD | SPI_FCMD_DUAL | SPI_FADDR_OCT | SPI_FADDR_QUAD | SPI_FADDR_DUAL)
#define SPI_LL_ONE_LINE_USER_MASK (SPI_FWRITE_OCT | SPI_FWRITE_QUAD | SPI_FWRITE_DUAL)
/// Swap the bit order to its correct place to send /// Swap the bit order to its correct place to send
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len)) #define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency /// This is the expected clock frequency
@ -51,15 +57,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_t; typedef uint32_t spi_ll_clock_val_t;
typedef spi_dev_t spi_dma_dev_t; typedef spi_dev_t spi_dma_dev_t;
/** IO modes supported by the master. */
typedef enum {
SPI_LL_IO_MODE_NORMAL = 0, ///< 1-bit mode for all phases
SPI_LL_IO_MODE_DIO, ///< 2-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_DUAL, ///< 2-bit mode for data phases only, 1-bit mode for command and address phases
SPI_LL_IO_MODE_QIO, ///< 4-bit mode for address and data phases, 1-bit mode for command phase
SPI_LL_IO_MODE_QUAD, ///< 4-bit mode for data phases only, 1-bit mode for command and address phases
} spi_ll_io_mode_t;
// Type definition of all supported interrupts // Type definition of all supported interrupts
typedef enum { typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -540,41 +537,27 @@ static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
} }
/** /**
* Configure the io mode for the master to work at. * Configure the SPI transaction line mode for the master to use.
* *
* @param hw Beginning address of the peripheral registers. * @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``. * @param line_mode SPI transaction line mode to use, see ``spi_line_mode_t``.
*/ */
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_mode) static inline void spi_ll_master_set_line_mode(spi_dev_t *hw, spi_line_mode_t line_mode)
{ {
if (io_mode == SPI_LL_IO_MODE_DIO || io_mode == SPI_LL_IO_MODE_DUAL) { hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->ctrl.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0; hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fread_dual = 1; hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->user.fwrite_dual = 1; hw->ctrl.fcmd_oct = (line_mode.cmd_lines == 8);
hw->ctrl.fcmd_quad = 0; hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = 0; hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.fread_quad = 0; hw->ctrl.faddr_oct = (line_mode.addr_lines == 8);
hw->user.fwrite_quad = 0; hw->ctrl.fread_dual = (line_mode.data_lines == 2);
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) { hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fcmd_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0; hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->ctrl.faddr_quad = (io_mode == SPI_LL_IO_MODE_QIO) ? 1 : 0; hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fread_quad = 1; hw->ctrl.fread_oct = (line_mode.data_lines == 8);
hw->user.fwrite_quad = 1; hw->user.fwrite_oct = (line_mode.data_lines == 8);
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
} else {
hw->ctrl.fcmd_dual = 0;
hw->ctrl.faddr_dual = 0;
hw->ctrl.fread_dual = 0;
hw->user.fwrite_dual = 0;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
}
} }
/** /**

View File

@ -38,6 +38,7 @@
#include <esp_err.h> #include <esp_err.h>
#include "soc/lldesc.h" #include "soc/lldesc.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "hal/spi_types.h"
/** /**
* Input parameters to the ``spi_hal_cal_clock_conf`` to calculate the timing configuration * Input parameters to the ``spi_hal_cal_clock_conf`` to calculate the timing configuration
@ -100,7 +101,7 @@ typedef struct {
uint64_t addr; ///< Address value to be sent uint64_t addr; ///< Address value to be sent
uint8_t *send_buffer; ///< Data to be sent uint8_t *send_buffer; ///< Data to be sent
uint8_t *rcv_buffer; ///< Buffer to hold the receive data. uint8_t *rcv_buffer; ///< Buffer to hold the receive data.
spi_ll_io_mode_t io_mode; ///< IO mode of the master spi_line_mode_t line_mode; ///< SPI line mode of this transaction
int cs_keep_active; ///< Keep CS active after transaction int cs_keep_active; ///< Keep CS active after transaction
} spi_hal_trans_config_t; } spi_hal_trans_config_t;

View File

@ -45,6 +45,15 @@ typedef enum {
} spi_event_t; } spi_event_t;
FLAG_ATTR(spi_event_t) FLAG_ATTR(spi_event_t)
/**
* @brief Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN.
*/
typedef struct {
unsigned char 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.
unsigned char data_lines; ///< The line width of data phase, e.g. 4-line-data-phase.
} spi_line_mode_t;
/** @cond */ //Doxy command to hide preprocessor definitions from docs */ /** @cond */ //Doxy command to hide preprocessor definitions from docs */

View File

@ -66,8 +66,8 @@ void spi_hal_setup_trans(spi_hal_context_t *hal, const spi_hal_dev_config_t *dev
spi_ll_clear_int_stat(hal->hw); spi_ll_clear_int_stat(hal->hw);
//We should be done with the transmission. //We should be done with the transmission.
HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0); HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0);
//set transaction line mode
spi_ll_master_set_io_mode(hw, trans->io_mode); spi_ll_master_set_line_mode(hw, trans->line_mode);
int extra_dummy = 0; int extra_dummy = 0;
//when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist //when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist

View File

@ -218,6 +218,7 @@
#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1 #define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1
#define SOC_MEMSPI_IS_INDEPENDENT 1 #define SOC_MEMSPI_IS_INDEPENDENT 1
#define SOC_SPI_SUPPORT_OCT 1
/*-------------------------- SYSTIMER CAPS ----------------------------------*/ /*-------------------------- SYSTIMER CAPS ----------------------------------*/
#define SOC_SYSTIMER_COUNTER_NUM (1) // Number of counter units #define SOC_SYSTIMER_COUNTER_NUM (1) // Number of counter units

View File

@ -22,21 +22,37 @@
#define SPI_IOMUX_PIN_NUM_MISO 31 #define SPI_IOMUX_PIN_NUM_MISO 31
#define SPI_IOMUX_PIN_NUM_WP 28 #define SPI_IOMUX_PIN_NUM_WP 28
#define SPI2_FUNC_NUM FSPI_FUNC_NUM // There are 2 sets of GPIO pins which could be routed to FSPICS0, FSPICLK, FSPID, FSPIQ, FSPIHD, FSPIWP.
#define SPI2_IOMUX_PIN_NUM_HD FSPI_IOMUX_PIN_NUM_HD // However, there is only one set of GPIO pins which could be routed to FSPIIO4, FSPIIO5, FSPIIO6, FSPIIO7.
#define SPI2_IOMUX_PIN_NUM_CS FSPI_IOMUX_PIN_NUM_CS // As default (when we are not going to use Octal SPI), we make use of SPI2_FUNC_NUM to route one of the 2 sets of GPIO pins to FSPICS0 ~ FSPIWP as follows.
#define SPI2_IOMUX_PIN_NUM_MOSI FSPI_IOMUX_PIN_NUM_MOSI #define SPI2_FUNC_NUM 4
#define SPI2_IOMUX_PIN_NUM_CLK FSPI_IOMUX_PIN_NUM_CLK #define SPI2_IOMUX_PIN_NUM_HD 9
#define SPI2_IOMUX_PIN_NUM_MISO FSPI_IOMUX_PIN_NUM_MISO #define SPI2_IOMUX_PIN_NUM_CS 10
#define SPI2_IOMUX_PIN_NUM_WP FSPI_IOMUX_PIN_NUM_WP #define SPI2_IOMUX_PIN_NUM_MOSI 11
#define SPI2_IOMUX_PIN_NUM_CLK 12
#define SPI2_IOMUX_PIN_NUM_MISO 13
#define SPI2_IOMUX_PIN_NUM_WP 14
// When using Octal SPI, we make use of SPI2_FUNC_NUM_OCT to route them as follows.
#define SPI2_FUNC_NUM_OCT 2
#define SPI2_IOMUX_PIN_NUM_HD_OCT 33
#define SPI2_IOMUX_PIN_NUM_CS_OCT 34
#define SPI2_IOMUX_PIN_NUM_MOSI_OCT 35
#define SPI2_IOMUX_PIN_NUM_CLK_OCT 36
#define SPI2_IOMUX_PIN_NUM_MISO_OCT 37
#define SPI2_IOMUX_PIN_NUM_WP_OCT 38
#define SPI2_IOMUX_PIN_NUM_IO4_OCT 10
#define SPI2_IOMUX_PIN_NUM_IO5_OCT 11
#define SPI2_IOMUX_PIN_NUM_IO6_OCT 12
#define SPI2_IOMUX_PIN_NUM_IO7_OCT 13
//SPI3 has no iomux pins //SPI3 has no iomux pins
//Following Macros are deprecated. Please use the Macros above //Following Macros are deprecated. Please use the Macros above
#define FSPI_FUNC_NUM 4 #define FSPI_FUNC_NUM SPI2_FUNC_NUM
#define FSPI_IOMUX_PIN_NUM_HD 9 #define FSPI_IOMUX_PIN_NUM_HD SPI2_IOMUX_PIN_NUM_HD
#define FSPI_IOMUX_PIN_NUM_CS 10 #define FSPI_IOMUX_PIN_NUM_CS SPI2_IOMUX_PIN_NUM_CS
#define FSPI_IOMUX_PIN_NUM_MOSI 11 #define FSPI_IOMUX_PIN_NUM_MOSI SPI2_IOMUX_PIN_NUM_MOSI
#define FSPI_IOMUX_PIN_NUM_CLK 12 #define FSPI_IOMUX_PIN_NUM_CLK SPI2_IOMUX_PIN_NUM_CLK
#define FSPI_IOMUX_PIN_NUM_MISO 13 #define FSPI_IOMUX_PIN_NUM_MISO SPI2_IOMUX_PIN_NUM_MISO
#define FSPI_IOMUX_PIN_NUM_WP 14 #define FSPI_IOMUX_PIN_NUM_WP SPI2_IOMUX_PIN_NUM_WP

View File

@ -50,10 +50,18 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = {
.spiq_out = FSPIQ_OUT_IDX, .spiq_out = FSPIQ_OUT_IDX,
.spiwp_out = FSPIWP_OUT_IDX, .spiwp_out = FSPIWP_OUT_IDX,
.spihd_out = FSPIHD_OUT_IDX, .spihd_out = FSPIHD_OUT_IDX,
.spid4_out = FSPIIO4_OUT_IDX,
.spid5_out = FSPIIO5_OUT_IDX,
.spid6_out = FSPIIO6_OUT_IDX,
.spid7_out = FSPIIO7_OUT_IDX,
.spid_in = FSPID_IN_IDX, .spid_in = FSPID_IN_IDX,
.spiq_in = FSPIQ_IN_IDX, .spiq_in = FSPIQ_IN_IDX,
.spiwp_in = FSPIWP_IN_IDX, .spiwp_in = FSPIWP_IN_IDX,
.spihd_in = FSPIHD_IN_IDX, .spihd_in = FSPIHD_IN_IDX,
.spid4_in = FSPIIO4_IN_IDX,
.spid5_in = FSPIIO5_IN_IDX,
.spid6_in = FSPIIO6_IN_IDX,
.spid7_in = FSPIIO7_IN_IDX,
.spics_out = {FSPICS0_OUT_IDX, FSPICS1_OUT_IDX, FSPICS2_OUT_IDX}, .spics_out = {FSPICS0_OUT_IDX, FSPICS1_OUT_IDX, FSPICS2_OUT_IDX},
.spics_in = FSPICS0_IN_IDX, .spics_in = FSPICS0_IN_IDX,
.spiclk_iomux_pin = FSPI_IOMUX_PIN_NUM_CLK, .spiclk_iomux_pin = FSPI_IOMUX_PIN_NUM_CLK,

View File

@ -126,7 +126,24 @@
#define SOC_SIGMADELTA_CHANNEL_NUM (8) // 8 channels #define SOC_SIGMADELTA_CHANNEL_NUM (8) // 8 channels
/*-------------------------- SPI CAPS ----------------------------------------*/ /*-------------------------- SPI CAPS ----------------------------------------*/
#include "spi_caps.h" #define SOC_SPI_PERIPH_NUM 3
#define SOC_SPI_DMA_CHAN_NUM 3
#define SOC_SPI_PERIPH_CS_NUM(i) 3
#define SOC_SPI_MAXIMUM_BUFFER_SIZE 64
#define SOC_SPI_SUPPORT_DDRCLK 1
#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS 1
#define SOC_SPI_SUPPORT_CD_SIG 1
#define SOC_SPI_SUPPORT_CONTINUOUS_TRANS 1
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
// Peripheral supports DIO, DOUT, QIO, or QOUT
#define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) ({(void)host_id; 1;})
// Peripheral supports output given level during its "dummy phase"
#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1
#define SOC_MEMSPI_IS_INDEPENDENT 1
#define SOC_SPI_MAX_PRE_DIVIDER 16
#define SOC_SPI_SUPPORT_OCT 1
/*-------------------------- SPIRAM CAPS ----------------------------------------*/ /*-------------------------- SPIRAM CAPS ----------------------------------------*/
#define SOC_SPIRAM_SUPPORTED 1 #define SOC_SPIRAM_SUPPORTED 1

View File

@ -1,38 +0,0 @@
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#define SOC_SPI_PERIPH_NUM 3
#define SOC_SPI_DMA_CHAN_NUM 3
#define SOC_SPI_PERIPH_CS_NUM(i) 3
#define SOC_SPI_MAXIMUM_BUFFER_SIZE 64
#define SOC_SPI_SUPPORT_DDRCLK 1
#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS 1
#define SOC_SPI_SUPPORT_CD_SIG 1
#define SOC_SPI_SUPPORT_CONTINUOUS_TRANS 1
#define SOC_SPI_SUPPORT_SLAVE_HD_VER2 1
// Peripheral supports DIO, DOUT, QIO, or QOUT
// VSPI (SPI3) only support 1-bit mode
#define SOC_SPI_PERIPH_SUPPORT_MULTILINE_MODE(host_id) ({(void)host_id; 1;})
// Peripheral supports output given level during its "dummy phase"
#define SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUTPUT 1
#define SOC_MEMSPI_IS_INDEPENDENT 1
#define SOC_SPI_MAX_PRE_DIVIDER 16

View File

@ -22,6 +22,9 @@
#define SPI_IOMUX_PIN_NUM_MISO 31 #define SPI_IOMUX_PIN_NUM_MISO 31
#define SPI_IOMUX_PIN_NUM_WP 28 #define SPI_IOMUX_PIN_NUM_WP 28
// There are 2 sets of GPIO pins which could be routed to FSPICS0, FSPICLK, FSPID, FSPIQ, FSPIHD, FSPIWP.
// However, there is only one set of GPIO pins which could be routed to FSPIIO4, FSPIIO5, FSPIIO6, FSPIIO7.
// As default (when we are not going to use Octal SPI), we make use of SPI2_FUNC_NUM to route one of the 2 sets of GPIO pins to FSPICS0 ~ FSPIWP as follows.
#define SPI2_FUNC_NUM 4 #define SPI2_FUNC_NUM 4
#define SPI2_IOMUX_PIN_NUM_HD 9 #define SPI2_IOMUX_PIN_NUM_HD 9
#define SPI2_IOMUX_PIN_NUM_CS 10 #define SPI2_IOMUX_PIN_NUM_CS 10
@ -30,4 +33,17 @@
#define SPI2_IOMUX_PIN_NUM_MISO 13 #define SPI2_IOMUX_PIN_NUM_MISO 13
#define SPI2_IOMUX_PIN_NUM_WP 14 #define SPI2_IOMUX_PIN_NUM_WP 14
// When using Octal SPI, we make use of SPI2_FUNC_NUM_OCT to route them as follows.
#define SPI2_FUNC_NUM_OCT 2
#define SPI2_IOMUX_PIN_NUM_HD_OCT 33
#define SPI2_IOMUX_PIN_NUM_CS_OCT 34
#define SPI2_IOMUX_PIN_NUM_MOSI_OCT 35
#define SPI2_IOMUX_PIN_NUM_CLK_OCT 36
#define SPI2_IOMUX_PIN_NUM_MISO_OCT 37
#define SPI2_IOMUX_PIN_NUM_WP_OCT 38
#define SPI2_IOMUX_PIN_NUM_IO4_OCT 10
#define SPI2_IOMUX_PIN_NUM_IO5_OCT 11
#define SPI2_IOMUX_PIN_NUM_IO6_OCT 12
#define SPI2_IOMUX_PIN_NUM_IO7_OCT 13
//SPI3 have no iomux pins //SPI3 have no iomux pins

View File

@ -50,10 +50,18 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = {
.spiq_out = FSPIQ_OUT_IDX, .spiq_out = FSPIQ_OUT_IDX,
.spiwp_out = FSPIWP_OUT_IDX, .spiwp_out = FSPIWP_OUT_IDX,
.spihd_out = FSPIHD_OUT_IDX, .spihd_out = FSPIHD_OUT_IDX,
.spid4_out = FSPIIO4_OUT_IDX,
.spid5_out = FSPIIO5_OUT_IDX,
.spid6_out = FSPIIO6_OUT_IDX,
.spid7_out = FSPIIO7_OUT_IDX,
.spid_in = FSPID_IN_IDX, .spid_in = FSPID_IN_IDX,
.spiq_in = FSPIQ_IN_IDX, .spiq_in = FSPIQ_IN_IDX,
.spiwp_in = FSPIWP_IN_IDX, .spiwp_in = FSPIWP_IN_IDX,
.spihd_in = FSPIHD_IN_IDX, .spihd_in = FSPIHD_IN_IDX,
.spid4_in = FSPIIO4_IN_IDX,
.spid5_in = FSPIIO5_IN_IDX,
.spid6_in = FSPIIO6_IN_IDX,
.spid7_in = FSPIIO7_IN_IDX,
.spics_out = {FSPICS0_OUT_IDX, FSPICS1_OUT_IDX, FSPICS2_OUT_IDX}, .spics_out = {FSPICS0_OUT_IDX, FSPICS1_OUT_IDX, FSPICS2_OUT_IDX},
.spics_in = FSPICS0_IN_IDX, .spics_in = FSPICS0_IN_IDX,
.spiclk_iomux_pin = SPI2_IOMUX_PIN_NUM_CLK, .spiclk_iomux_pin = SPI2_IOMUX_PIN_NUM_CLK,

View File

@ -53,10 +53,18 @@ 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;
const uint8_t spid4_out;
const uint8_t spid5_out;
const uint8_t spid6_out;
const uint8_t spid7_out;
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;
const uint8_t spid4_in;
const uint8_t spid5_in;
const uint8_t spid6_in;
const uint8_t spid7_in;
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,12 +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. **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. **MOSI** Master Out, Slave In, a.k.a. D. Data transmission from a Host to Device. Also data0 signal in octal 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. Only used for 4-bit (qio/qout) transactions. **QUADWP** Write Protect signal. Used for 4-bit (qio/qout) transactions. Also for data2 signal in octal mode.
**QUADHD** Hold signal. Only used for 4-bit (qio/qout) transactions. **QUADHD** Hold signal. Used for 4-bit (qio/qout) transactions. Also for data3 signal in octal mode.
**DATA4** Data4 signal in octal mode.
**DATA5** Data5 signal in octal mode.
**DATA6** Data6 signal in octal mode.
**DATA7** Data7 signal in octal 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.
@ -144,6 +148,41 @@ 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
^^^^^^^^^^^^^^^^^^^^^
Supported line modes are as follows:
====================== ====================== ====================== ==================
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:
+-------------------+--------------------------+
| Mode name | Flags |
+===================+==========================+
| Dual Output | |
+-------------------+ SPICOMMON_BUSFLAG_DUAL |
| Dual I/O | |
+-------------------+--------------------------+
| Quad Output | |
+-------------------+ SPICOMMON_BUSFLAG_QUAD |
| Quad I/O | |
+-------------------+--------------------------+
| Octal Output | |
+-------------------+ SPICOMMON_BUSFLAG_OCTAL |
| OPI | |
+-------------------+--------------------------+
Command and Address Phases Command and Address Phases
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -152,6 +191,8 @@ 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`.
Write and Read Phases Write and Read Phases
^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
@ -163,6 +204,24 @@ 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:
+-------------------+--------------------+
| 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
.. note:: .. note::

View File

@ -31,7 +31,7 @@ Introduction
------------ ------------
In the half duplex mode, the master has to use the protocol defined by the slave to communicate In the half duplex mode, the master has to use the protocol defined by the slave to communicate
with the slave. Each transaction may consists of the following phases (list by the order they with the slave. Each transaction may consist of the following phases (list by the order they
should exist): should exist):
- Command: 8-bit, master to slave - Command: 8-bit, master to slave

View File

@ -47,13 +47,37 @@ If using default settings, the hardware connection can be as below:
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: 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:
PIN_NUM_MOSI stands for the GPIO number connected to 'MOSI' pin at LCD screen; | GPIO number | LCD pin |
PIN_NUM_CLK stands for the GPIO number connected to 'SCK' pin at LCD screen; | :-----------: | :--: |
PIN_NUM_MOSI stands for the GPIO number connected to 'MOSI' pin at LCD screen; | PIN_NUM_CLK | SCK |
PIN_NUM_CS stands for the GPIO number connected to 'CS' pin at LCD screen; | PIN_NUM_CS | CS |
PIN_NUM_DC stands for the GPIO number connected to 'DC' pin at LCD screen; | PIN_NUM_DC | DC |
PIN_NUM_RST stands for the GPIO number connected to 'RST' pin at LCD screen; | PIN_NUM_RST | RST |
PIN_NUM_BCKL stands for the GPIO number connected to 'LED' pin at LCD screen; | PIN_NUM_BCKL | LED |
| PIN_NUM_MOSI | MOSI |
### 8-line LCD Usage
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:
| 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_DATA2 | D2 |
| PIN_NUM_DATA3 | D3 |
| PIN_NUM_DATA4 | D4 |
| PIN_NUM_DATA5 | D5 |
| PIN_NUM_DATA6 | D6 |
| PIN_NUM_DATA7 | D7 |
### Build and Flash ### Build and Flash

View File

@ -1,19 +1,5 @@
menu "Example Configuration" menu "Example Configuration"
choice LCD_TYPE
prompt "LCD module type"
default LCD_TYPE_AUTO
help
The type of LCD on the evaluation board.
config LCD_TYPE_AUTO
bool "Auto detect"
config LCD_TYPE_ST7789V
bool "ST7789V (WROVER Kit v2 or v3)"
config LCD_TYPE_ILI9341
bool "ILI9341 (WROVER Kit v1 or DevKitJ v1)"
endchoice
config LCD_OVERCLOCK config LCD_OVERCLOCK
bool bool
prompt "Run LCD at higher clock speed than allowed" prompt "Run LCD at higher clock speed than allowed"
@ -23,4 +9,12 @@ menu "Example Configuration"
in practice the driver chips work fine with a higher clock rate, and using that gives a better framerate. 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. Select this to try using the out-of-spec clock rate.
config LCD_SPI_8_LINE_MODE
bool
prompt "Drive a LCD with 8 data lines"
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default "n"
help
This option can be chosen when using 8-line lcd.
endmenu endmenu

View File

@ -14,26 +14,37 @@
#include "esp_lcd_panel_ops.h" #include "esp_lcd_panel_ops.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 */
/** /**
* If not using the default settings, the SPI peripheral on LCD and the GPIO numbers can be * If not using the default settings, the SPI peripheral on LCD and the GPIO numbers can be
* changed below. * changed below.
*/ */
#define LCD_HOST SPI2_HOST /*!< spi peripheral for LCD */ #define PIN_NUM_MOSI 23 /*!< gpio number for LCD MOSI, also the gpio number for LCD DATA0 at 8-line mode */
#define PIN_NUM_MOSI 23 /*!< gpio number for LCD MOSI */
#define PIN_NUM_CLK 19 /*!< gpio number for LCD clock */ #define PIN_NUM_CLK 19 /*!< gpio number for LCD clock */
#define PIN_NUM_CS 22 /*!< gpio number for LCD CS */ #define PIN_NUM_CS 22 /*!< gpio number for LCD CS */
#define PIN_NUM_DC 21 /*!< gpio number for LCD DC */ #define PIN_NUM_DC 21 /*!< gpio number for LCD DC */
#define PIN_NUM_RST 18 /*!< gpio number for LCD RST */ #define PIN_NUM_RST 18 /*!< gpio number for LCD RST */
#define PIN_NUM_BCKL 5 /*!< gpio number for LCD Back Light */ #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 // The pixel number in horizontal and vertical
#define EXAMPLE_LCD_H_RES (320) #define EXAMPLE_LCD_H_RES (320)
#define EXAMPLE_LCD_V_RES (240) #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.
@ -78,11 +89,22 @@ void app_main(void)
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 = {
.miso_io_num = -1,
.mosi_io_num = PIN_NUM_MOSI, .mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK, .sclk_io_num = PIN_NUM_CLK,
#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,
.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; esp_lcd_panel_io_handle_t io_handle = NULL;
@ -94,8 +116,11 @@ void app_main(void)
#else #else
.pclk_hz = 10 * 1000 * 1000, // Clock out at 10 MHz .pclk_hz = 10 * 1000 * 1000, // Clock out at 10 MHz
#endif #endif
.spi_mode = 0, .spi_mode = LCD_SPI_MODE,
.trans_queue_depth = 7, .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));

View File

@ -26,8 +26,9 @@ static inline uint16_t get_bgnd_pixel(int x, int y)
y+=8; y+=8;
return pixels[y][x]; return pixels[y][x];
} }
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 #else
//esp32s2/c3 doesn't have enough memory to hold the decoded image, calculate instead //esp32s2/c3 doesn't have enough memory to hold the decoded image, calculate instead
//TODO: add support for esp32s3 (IDF-3615)
static inline uint16_t get_bgnd_pixel(int x, int y) static inline uint16_t get_bgnd_pixel(int x, int y)
{ {
return ((x<<3)^(y<<3)^(x*y)); return ((x<<3)^(y<<3)^(x*y));
@ -69,8 +70,9 @@ esp_err_t pretty_effect_init(void)
{ {
#ifdef CONFIG_IDF_TARGET_ESP32 #ifdef CONFIG_IDF_TARGET_ESP32
return decode_image(&pixels); return decode_image(&pixels);
#elif CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32C3 #else
//esp32s2/c3 doesn't have enough memory to hold the decoded image, calculate instead //esp32s2/c3 doesn't have enough memory to hold the decoded image, calculate instead
//TODO: add support for esp32s3 (IDF-3615)
return ESP_OK; return ESP_OK;
#endif #endif
} }