Merge branch 'feature/spi_master_support_8-line_mode' into 'master'

spi_master: support 8-line mode

Closes IDF-3354 and IDFGH-4553

See merge request espressif/esp-idf!14397
pull/7497/head
morris 2021-08-19 13:19:34 +00:00
commit 29e7456f37
31 zmienionych plików z 848 dodań i 541 usunięć

Wyświetl plik

@ -63,7 +63,8 @@ extern "C"
#define SPICOMMON_BUSFLAG_DUAL (1<<6) ///< Check MOSI and MISO pins can output. Or indicates bus able to work under DIO mode.
#define SPICOMMON_BUSFLAG_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_IO4_IO7 (1<<8) ///< Check existing of IO4~IO7 pins. Or indicates IO4~IO7 pins initialized.
#define SPICOMMON_BUSFLAG_OCTAL (SPICOMMON_BUSFLAG_QUAD|SPICOMMON_BUSFLAG_IO4_IO7) ///< Check existing of MOSI/MISO/WP/HD/SPIIO4/SPIIO5/SPIIO6/SPIIO7 pins as output. Or indicates bus able to work under octal mode.
#define SPICOMMON_BUSFLAG_NATIVE_PINS SPICOMMON_BUSFLAG_IOMUX_PINS
/**
@ -95,18 +96,34 @@ 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.
*/
typedef struct {
int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.
int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.
int sclk_io_num; ///< GPIO pin for Spi CLocK signal, 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.
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.
*/
union {
int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) 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.
};
union {
int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.
int data1_io_num; ///< GPIO pin for spi data1 signal in quad/octal mode, or -1 if not used.
};
int sclk_io_num; ///< GPIO pin for SPI Clock signal, or -1 if not used.
union {
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.
};
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.
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;

Wyświetl plik

@ -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_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_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.
* Leave to NULL if not needed.
* - ``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_WPHD`` The bus has WP and HD connected.
* - ``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
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_OK on success

Wyświetl plik

@ -104,8 +104,11 @@ 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_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_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_MULTILINE_CMD (1<<9) ///< The data lines used at command phase is the same as data phase (otherwise, only one data line is used at command phase)
#define SPI_TRANS_MODE_OCT (1<<10) ///< Transmit/receive data in 8-bit mode
#define SPI_TRANS_MULTILINE_ADDR SPI_TRANS_MODE_DIOQIO_ADDR ///< The data lines used at address phase is the same as data phase (otherwise, only one data line is used at address phase)
/**
* This structure describes one SPI transaction. The descriptor should not be modified until the transaction finishes.
*/
@ -148,7 +151,7 @@ typedef struct {
} spi_transaction_ext_t ;
typedef struct spi_device_t* spi_device_handle_t; ///< Handle for a device on a SPI bus
typedef struct spi_device_t *spi_device_handle_t; ///< Handle for a device on a SPI bus
/**
* @brief Allocate a device on a SPI bus
*
@ -341,7 +344,7 @@ void spi_device_release_bus(spi_device_handle_t dev);
*
* @return Actual working frequency that most fit.
*/
int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t* reg_o) __attribute__((deprecated));
int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t *reg_o) __attribute__((deprecated));
/**
* @brief Calculate the working frequency that is most close to desired frequency.
@ -368,7 +371,7 @@ int spi_get_actual_clock(int fapb, int hz, int duty_cycle);
*
* @note If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.
*/
void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o);
void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int *dummy_o, int *cycles_remain_o);
/**
* @brief Get the frequency limit of current configurations.

Wyświetl plik

@ -15,6 +15,7 @@
#include "esp_err.h"
#include "soc/soc.h"
#include "soc/soc_caps.h"
#include "soc/soc_pins.h"
#include "soc/lldesc.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
@ -350,32 +351,116 @@ esp_err_t spicommon_slave_free_dma(spi_host_device_t host_id)
}
//----------------------------------------------------------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)
{
if (host != SPI2_HOST) {
return false;
}
int io_nums[] = {bus_config->data0_io_num, bus_config->data1_io_num, bus_config->data2_io_num, bus_config->data3_io_num,
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 &&
bus_config->sclk_io_num != spi_periph_signal[host].spiclk_iomux_pin) {
return false;
}
return false;
}
if (bus_config->quadwp_io_num>=0 &&
bus_config->quadwp_io_num != spi_periph_signal[host].spiwp_iomux_pin) {
return false;
}
return false;
}
if (bus_config->quadhd_io_num>=0 &&
bus_config->quadhd_io_num != spi_periph_signal[host].spihd_iomux_pin) {
return false;
}
return false;
}
if (bus_config->mosi_io_num >= 0 &&
bus_config->mosi_io_num != spi_periph_signal[host].spid_iomux_pin) {
return false;
}
return false;
}
if (bus_config->miso_io_num>=0 &&
bus_config->miso_io_num != spi_periph_signal[host].spiq_iomux_pin) {
return false;
}
return false;
}
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
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 +468,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)
{
#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;
bool miso_need_output;
@ -414,13 +510,36 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
if (bus_config->quadhd_io_num>=0) {
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
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) {
temp_flag |= SPICOMMON_BUSFLAG_MOSI;
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;
SPI_CHECK_PIN(bus_config->miso_io_num, "miso", miso_need_output);
}
@ -443,12 +562,29 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
if (missing_flag != 0) {
//check pins existence
if (missing_flag & SPICOMMON_BUSFLAG_SCLK) ESP_LOGE(SPI_TAG, "sclk pin required.");
if (missing_flag & SPICOMMON_BUSFLAG_MOSI) ESP_LOGE(SPI_TAG, "mosi 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_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 (missing_flag & SPICOMMON_BUSFLAG_SCLK) {
ESP_LOGE(SPI_TAG, "sclk pin required.");
}
if (missing_flag & SPICOMMON_BUSFLAG_MOSI) {
ESP_LOGE(SPI_TAG, "mosi 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_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);
}
@ -456,27 +592,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
//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);
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);
}
temp_flag |= SPICOMMON_BUSFLAG_IOMUX_PINS;
bus_iomux_pins_set(host, bus_config);
} else {
//Use GPIO matrix
ESP_LOGD(SPI_TAG, "SPI%d use gpio matrix.", host+1);
@ -537,6 +653,26 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
#endif
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;

Wyświetl plik

@ -530,13 +530,17 @@ static void SPI_MASTER_ISR_ATTR spi_new_trans(spi_device_t *dev, spi_trans_priv_
hal_trans.cmd = trans->cmd;
hal_trans.addr = trans->addr;
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 ?
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_DIO : SPI_LL_IO_MODE_DUAL) :
(trans->flags & SPI_TRANS_MODE_QIO ?
(trans->flags & SPI_TRANS_MODE_DIOQIO_ADDR ? SPI_LL_IO_MODE_QIO : SPI_LL_IO_MODE_QUAD) :
SPI_LL_IO_MODE_NORMAL
));
//Set up OIO/QIO/DIO if needed
hal_trans.line_mode.data_lines = (trans->flags & SPI_TRANS_MODE_DIO) ? 2 :
(trans->flags & SPI_TRANS_MODE_QIO) ? 4 : 1;
#if SOC_SPI_SUPPORT_OCT
if (trans->flags & SPI_TRANS_MODE_OCT) {
hal_trans.line_mode.data_lines = 8;
}
#endif
hal_trans.line_mode.addr_lines = (trans->flags & SPI_TRANS_MULTILINE_ADDR) ? hal_trans.line_mode.data_lines : 1;
hal_trans.line_mode.cmd_lines = (trans->flags & SPI_TRANS_MULTILINE_CMD) ? hal_trans.line_mode.data_lines : 1;
if (trans->flags & SPI_TRANS_VARIABLE_CMD) {
hal_trans.cmd_bits = ((spi_transaction_ext_t *)trans)->command_bits;
@ -686,8 +690,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(is_half_duplex || trans_desc->rxlength <= trans_desc->length, "rx length > tx length in full duplex mode", ESP_ERR_INVALID_ARG);
//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);
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "incompatible iface params", ESP_ERR_INVALID_ARG);
#if SOC_SPI_SUPPORT_OCT
SPI_CHECK(!(host->id == SPI3_HOST && trans_desc->flags & SPI_TRANS_MODE_OCT), "SPI3 does not support octal mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && (handle->cfg.flags & SPI_DEVICE_3WIRE)), "Incompatible when setting to both Octal mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & SPI_TRANS_MODE_OCT) && !is_half_duplex), "Incompatible when setting to both Octal mode and half duplex mode", 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 when setting to both multi-line mode and 3-wire-mode", ESP_ERR_INVALID_ARG);
SPI_CHECK(!((trans_desc->flags & (SPI_TRANS_MODE_DIO|SPI_TRANS_MODE_QIO)) && !is_half_duplex), "Incompatible when setting to both multi-line mode and half duplex mode", ESP_ERR_INVALID_ARG);
#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 );
#elif CONFIG_IDF_TARGET_ESP32S3

Wyświetl plik

@ -80,6 +80,7 @@ typedef struct {
struct {
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 octal_mode: 1; /*!< transmit with octal mode (8 data lines), this mode is used to simulate Intel 8080 timing */
} flags;
} esp_lcd_panel_io_spi_config_t;

Wyświetl plik

@ -45,6 +45,7 @@ typedef struct {
struct {
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 octal_mode: 1; // Indicates whether the transmitting is enabled with octal mode (8 data lines)
} flags;
lcd_spi_trans_descriptor_t trans_pool[]; // Transaction pool
} esp_lcd_panel_io_spi_t;
@ -60,6 +61,7 @@ esp_err_t esp_lcd_new_panel_io_spi(esp_lcd_spi_bus_handle_t bus, const esp_lcd_p
ESP_GOTO_ON_FALSE(spi_panel_io, ESP_ERR_NO_MEM, err, TAG, "no mem for spi panel io");
spi_device_interface_config_t devcfg = {
.flags = io_config->flags.octal_mode ? SPI_DEVICE_HALFDUPLEX : 0,
.clock_speed_hz = io_config->pclk_hz,
.mode = io_config->spi_mode,
.spics_io_num = io_config->cs_gpio_num,
@ -82,6 +84,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_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->lcd_cmd_bits = io_config->lcd_cmd_bits;
spi_panel_io->lcd_param_bits = io_config->lcd_param_bits;
@ -147,6 +150,10 @@ static esp_err_t panel_io_spi_tx_param(esp_lcd_panel_io_t *io, int lcd_cmd, cons
lcd_trans->flags.dc_gpio_level = !spi_panel_io->flags.dc_data_level; // set D/C line to command mode
lcd_trans->base.length = spi_panel_io->lcd_cmd_bits;
lcd_trans->base.tx_buffer = &lcd_cmd;
if (spi_panel_io->flags.octal_mode) {
// use 8 lines for transmitting command, address and data
lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
}
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;
}
@ -192,6 +199,10 @@ static esp_err_t panel_io_spi_tx_color(esp_lcd_panel_io_t *io, int lcd_cmd, cons
if (spi_panel_io->flags.dc_as_cmd_phase) { // encoding DC value to SPI command phase when necessary
lcd_trans->base.cmd = !spi_panel_io->flags.dc_data_level;
}
if (spi_panel_io->flags.octal_mode) {
// use 8 lines for transmitting command, address and data
lcd_trans->base.flags |= (SPI_TRANS_MULTILINE_CMD | SPI_TRANS_MULTILINE_ADDR | SPI_TRANS_MODE_OCT);
}
// command is short, using polling mode
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");

Wyświetl plik

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

Wyświetl plik

@ -9,24 +9,16 @@
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_system.h"
#include "soc/soc_caps.h"
#include "test_spi_board.h"
#define TEST_LCD_H_RES (240)
#define TEST_LCD_V_RES (280)
#define TEST_SPI_CLK_GPIO (2)
#define TEST_SPI_MOSI_GPIO (4)
#define TEST_LCD_RST_GPIO (5)
#define TEST_LCD_DC_GPIO (18)
#define TEST_LCD_BK_LIGHT_GPIO (19)
#define TEST_SPI_CS_GPIO (0)
#define TEST_SPI_HOST_ID (1)
#define TEST_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000)
#define TEST_LCD_PIXEL_CLOCK_HZ (20 * 1000 * 1000) // 20MHz
TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
typedef bool (*trans_done_callback_t)(esp_lcd_panel_io_handle_t, void *, void *);
static void lcd_initialize_spi(esp_lcd_panel_io_handle_t *io_handle, esp_lcd_panel_handle_t *panel_handle, trans_done_callback_t on_color_trans_done, void *user_data, bool oct_mode)
{
#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);
gpio_config_t bk_gpio_config = {
.mode = GPIO_MODE_OUTPUT,
.pin_bit_mask = 1ULL << TEST_LCD_BK_LIGHT_GPIO
@ -34,34 +26,55 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
TEST_ESP_OK(gpio_config(&bk_gpio_config));
spi_bus_config_t buscfg = {
.sclk_io_num = TEST_LCD_PCLK_GPIO,
.mosi_io_num = TEST_LCD_DATA0_GPIO,
.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 * sizeof(uint16_t)
};
if (oct_mode) {
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;
}
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,
.cs_gpio_num = TEST_LCD_CS_GPIO,
.pclk_hz = TEST_LCD_PIXEL_CLOCK_HZ,
.spi_mode = 0,
.trans_queue_depth = 10,
.lcd_cmd_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 (oct_mode) {
io_config.flags.octal_mode = 1;
io_config.spi_mode = 3;
}
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));
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
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
@ -80,7 +93,8 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
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_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_io_del(io_handle));
TEST_ESP_OK(spi_bus_free(TEST_SPI_HOST_ID));
@ -89,6 +103,24 @@ TEST_CASE("lcd panel with spi interface (st7789)", "[lcd]")
#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_spi(&io_handle, &panel_handle, NULL, NULL, true);
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_spi(&io_handle, &panel_handle, NULL, NULL, false);
lcd_panel_test(io_handle, panel_handle);
}
// 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
#if CONFIG_LV_USE_USER_DATA
@ -101,50 +133,10 @@ static bool notify_lvgl_ready_to_flush(esp_lcd_panel_io_handle_t panel_io, void
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
lv_disp_t *disp = NULL;
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
gpio_set_level(TEST_LCD_BK_LIGHT_GPIO, 0);
esp_lcd_panel_reset(panel_handle);
@ -155,7 +147,29 @@ TEST_CASE("lvgl gui with spi interface (st7789)", "[lcd][lvgl][ignore]")
// turn on backlight
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_spi(&io_handle, &panel_handle, notify_lvgl_ready_to_flush, &disp, true);
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_spi(&io_handle, &panel_handle, notify_lvgl_ready_to_flush, &disp, false);
lvgl_gui_test(io_handle, panel_handle, &disp);
}
#endif // CONFIG_LV_USE_USER_DATA

Wyświetl plik

@ -28,6 +28,8 @@
#include "esp32/rom/lldesc.h"
#include "soc/spi_periph.h"
#include "hal/misc.h"
#include "hal/spi_types.h"
#include "hal/assert.h"
#ifdef __cplusplus
extern "C" {
@ -37,6 +39,9 @@ extern "C" {
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// 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)
/// 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
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency
@ -53,16 +58,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.
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
*----------------------------------------------------------------------------*/
@ -449,37 +444,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 io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
* @param hw Beginning address of the peripheral registers.
* @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->user.val &= ~(SPI_FWRITE_DUAL | SPI_FWRITE_QUAD | SPI_FWRITE_DIO | SPI_FWRITE_QIO);
switch (io_mode) {
case SPI_LL_IO_MODE_DIO:
hw->ctrl.fread_dio = 1;
hw->user.fwrite_dio = 1;
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
if (line_mode.cmd_lines > 1) {
HAL_ASSERT(false);
}
switch (line_mode.data_lines) {
case 2:
if (line_mode.addr_lines == 1) {
// 1-line-cmd + 1-line-addr + 2-line-data
hw->ctrl.fread_dual = 1;
hw->user.fwrite_dual = 1;
} else if (line_mode.addr_lines == 2) {
// 1-line-cmd + 2-line-addr + 2-line-data
hw->ctrl.fread_dio = 1;
hw->user.fwrite_dio = 1;
} else {
HAL_ASSERT(false);
}
hw->ctrl.fastrd_mode = 1;
break;
case SPI_LL_IO_MODE_DUAL:
hw->ctrl.fread_dual = 1;
hw->user.fwrite_dual = 1;
break;
case SPI_LL_IO_MODE_QIO:
hw->ctrl.fread_qio = 1;
hw->user.fwrite_qio = 1;
break;
case SPI_LL_IO_MODE_QUAD:
hw->ctrl.fread_quad = 1;
hw->user.fwrite_quad = 1;
case 4:
if (line_mode.addr_lines == 1) {
// 1-line-cmd + 1-line-addr + 4-line-data
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 {
HAL_ASSERT(false);
}
hw->ctrl.fastrd_mode = 1;
break;
default:
// 1-line-cmd + 1-line-addr + 1-line-data
break;
};
if (io_mode != SPI_LL_IO_MODE_NORMAL) {
hw->ctrl.fastrd_mode = 1;
}
}
@ -502,7 +510,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated
*/
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) {
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->pin.cs_keep_active = (keep_active != 0) ? 1 : 0;
}

Wyświetl plik

@ -30,6 +30,7 @@
#include "soc/lldesc.h"
#include "hal/assert.h"
#include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus
extern "C" {
@ -37,6 +38,9 @@ extern "C" {
/// 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)
/// 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
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency
@ -51,15 +55,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_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
typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -354,7 +349,7 @@ static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_s
*/
static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *data, int len)
{
HAL_ASSERT(byte_id+len <= 64);
HAL_ASSERT(byte_id + len <= 64);
HAL_ASSERT(len > 0);
HAL_ASSERT(byte_id >= 0);
@ -362,10 +357,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word;
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
//read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read
if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write
@ -409,7 +408,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len;
@ -540,41 +541,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 io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
* @param hw Beginning address of the peripheral registers.
* @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.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.fread_dual = 1;
hw->user.fwrite_dual = 1;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) {
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;
}
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->user.fwrite_quad = (line_mode.data_lines == 4);
}
/**
@ -610,7 +593,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated
*/
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) {
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
}
@ -994,35 +978,35 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda=1)
static inline void spi_ll_enable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define ENA_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 1;
FOR_EACH_ITEM(ENA_INTR, INTR_LIST);
#undef ENA_INTR
}
static inline void spi_ll_disable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_disable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define DIS_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 0;
FOR_EACH_ITEM(DIS_INTR, INTR_LIST);
#undef DIS_INTR
}
static inline void spi_ll_set_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_set_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define SET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit)) hw->st_reg = 1;
FOR_EACH_ITEM(SET_INTR, INTR_LIST);
#undef SET_INTR
}
static inline void spi_ll_clear_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_clear_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define CLR_INTR(intr_bit, _, __, clr_reg) if (intr_mask & (intr_bit)) hw->clr_reg;
FOR_EACH_ITEM(CLR_INTR, INTR_LIST);
#undef CLR_INTR
}
static inline bool spi_ll_get_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline bool spi_ll_get_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define GET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit) && hw->st_reg) return true;
FOR_EACH_ITEM(GET_INTR, INTR_LIST);
@ -1076,7 +1060,7 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
/*------------------------------------------------------------------------------
* Slave HD
*----------------------------------------------------------------------------*/
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_cond_t cond_mask)
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t *hw, spi_ll_trans_len_cond_t cond_mask)
{
hw->slave.rdbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDBUF) ? 1 : 0;
hw->slave.wrbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRBUF) ? 1 : 0;
@ -1084,12 +1068,12 @@ static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_
hw->slave.wrdma_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRDMA) ? 1 : 0;
}
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t* hw)
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t *hw)
{
return hw->slave1.data_bitlen / 8;
}
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t* hw)
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw)
{
return hw->slave1.last_addr;
}

Wyświetl plik

@ -30,6 +30,7 @@
#include "soc/lldesc.h"
#include "hal/assert.h"
#include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus
extern "C" {
@ -37,6 +38,9 @@ extern "C" {
/// 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)
/// 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
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency
@ -51,15 +55,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_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
typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -352,7 +347,7 @@ static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_s
*/
static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *data, int len)
{
HAL_ASSERT(byte_id+len <= 64);
HAL_ASSERT(byte_id + len <= 64);
HAL_ASSERT(len > 0);
HAL_ASSERT(byte_id >= 0);
@ -360,10 +355,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word;
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
//read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read
if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write
@ -407,7 +406,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len;
@ -538,41 +539,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 io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
* @param hw Beginning address of the peripheral registers.
* @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.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.fread_dual = 1;
hw->user.fwrite_dual = 1;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) {
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;
}
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->user.fwrite_quad = (line_mode.data_lines == 4);
}
/**
@ -609,7 +592,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated
*/
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) {
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
}
@ -993,35 +977,35 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda=1)
static inline void spi_ll_enable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define ENA_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 1;
FOR_EACH_ITEM(ENA_INTR, INTR_LIST);
#undef ENA_INTR
}
static inline void spi_ll_disable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_disable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define DIS_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 0;
FOR_EACH_ITEM(DIS_INTR, INTR_LIST);
#undef DIS_INTR
}
static inline void spi_ll_set_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_set_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define SET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit)) hw->st_reg = 1;
FOR_EACH_ITEM(SET_INTR, INTR_LIST);
#undef SET_INTR
}
static inline void spi_ll_clear_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_clear_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define CLR_INTR(intr_bit, _, __, clr_reg) if (intr_mask & (intr_bit)) hw->clr_reg;
FOR_EACH_ITEM(CLR_INTR, INTR_LIST);
#undef CLR_INTR
}
static inline bool spi_ll_get_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline bool spi_ll_get_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define GET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit) && hw->st_reg) return true;
FOR_EACH_ITEM(GET_INTR, INTR_LIST);
@ -1075,7 +1059,7 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
/*------------------------------------------------------------------------------
* Slave HD
*----------------------------------------------------------------------------*/
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_cond_t cond_mask)
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t *hw, spi_ll_trans_len_cond_t cond_mask)
{
hw->slave.rdbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDBUF) ? 1 : 0;
hw->slave.wrbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRBUF) ? 1 : 0;
@ -1083,12 +1067,12 @@ static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_
hw->slave.wrdma_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRDMA) ? 1 : 0;
}
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t* hw)
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t *hw)
{
return hw->slave1.data_bitlen / 8;
}
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t* hw)
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw)
{
return hw->slave1.last_addr;
}

Wyświetl plik

@ -30,6 +30,7 @@
#include "soc/lldesc.h"
#include "hal/assert.h"
#include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus
extern "C" {
@ -39,6 +40,10 @@ extern "C" {
#define SPI_LL_DMA_FIFO_RST_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST)
/// 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)
/// 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
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// 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.
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
typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -348,14 +344,16 @@ static inline void spi_ll_read_buffer(spi_dev_t *hw, uint8_t *buffer_to_rcv, siz
static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_addr, uint8_t *out_data, int len)
{
while (len>0) {
uint32_t word = hw->data_buf[byte_addr/4];
while (len > 0) {
uint32_t word = hw->data_buf[byte_addr / 4];
int offset = byte_addr % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t*)&word)+offset, copy_len);
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_addr += copy_len;
out_data += copy_len;
len -= copy_len;
@ -373,10 +371,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_addr, uint8_
int offset = byte_addr % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
//read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_addr / 4];
if (copy_len != 4) {
word = hw->data_buf[byte_addr / 4];
}
memcpy(((uint8_t *)&word) + offset, data, copy_len);
hw->data_buf[byte_addr / 4] = word;
@ -508,41 +510,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 io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
* @param hw Beginning address of the peripheral registers.
* @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.fcmd_dual= (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.faddr_dual= (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.fread_dual=1;
hw->user.fwrite_dual=1;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) {
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;
}
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->ctrl.fcmd_oct = (line_mode.cmd_lines == 8);
hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.faddr_oct = (line_mode.addr_lines == 8);
hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fread_oct = (line_mode.data_lines == 8);
hw->user.fwrite_oct = (line_mode.data_lines == 8);
}
static inline void spi_ll_slave_set_seg_mode(spi_dev_t *hw, bool seg_trans)
@ -572,7 +560,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated
*/
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) {
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
}
@ -985,35 +974,35 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda=1)
static inline void spi_ll_enable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define ENA_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 1;
FOR_EACH_ITEM(ENA_INTR, INTR_LIST);
#undef ENA_INTR
}
static inline void spi_ll_disable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_disable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define DIS_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 0;
FOR_EACH_ITEM(DIS_INTR, INTR_LIST);
#undef DIS_INTR
}
static inline void spi_ll_set_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_set_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define SET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit)) hw->st_reg = 1;
FOR_EACH_ITEM(SET_INTR, INTR_LIST);
#undef SET_INTR
}
static inline void spi_ll_clear_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_clear_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define CLR_INTR(intr_bit, _, __, clr_reg) if (intr_mask & (intr_bit)) hw->clr_reg;
FOR_EACH_ITEM(CLR_INTR, INTR_LIST);
#undef CLR_INTR
}
static inline bool spi_ll_get_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline bool spi_ll_get_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define GET_INTR(intr_bit, _, st_reg, ...) if (intr_mask & (intr_bit) && hw->st_reg) return true;
FOR_EACH_ITEM(GET_INTR, INTR_LIST);
@ -1067,7 +1056,7 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
/*------------------------------------------------------------------------------
* Slave HD
*----------------------------------------------------------------------------*/
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_cond_t cond_mask)
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t *hw, spi_ll_trans_len_cond_t cond_mask)
{
hw->slv_rd_byte.rdbuf_bytelen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDBUF) ? 1 : 0;
hw->slv_rd_byte.wrbuf_bytelen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRBUF) ? 1 : 0;
@ -1075,12 +1064,12 @@ static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_
hw->slv_rd_byte.wrdma_bytelen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRDMA) ? 1 : 0;
}
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t* hw)
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t *hw)
{
return hw->slv_rd_byte.data_bytelen;
}
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t* hw)
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw)
{
return hw->slave1.last_addr;
}

Wyświetl plik

@ -30,6 +30,7 @@
#include "soc/lldesc.h"
#include "hal/assert.h"
#include "hal/misc.h"
#include "hal/spi_types.h"
#ifdef __cplusplus
extern "C" {
@ -37,6 +38,11 @@ extern "C" {
/// 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)
/// 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
#define HAL_SPI_SWAP_DATA_TX(data, len) HAL_SWAP32((uint32_t)(data) << (32 - len))
/// This is the expected clock frequency
@ -51,15 +57,6 @@ extern "C" {
typedef uint32_t spi_ll_clock_val_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
typedef enum {
SPI_LL_INTR_TRANS_DONE = BIT(0), ///< A transaction has done
@ -354,7 +351,7 @@ static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_s
*/
static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *data, int len)
{
HAL_ASSERT(byte_id+len <= 64);
HAL_ASSERT(byte_id + len <= 64);
HAL_ASSERT(len > 0);
HAL_ASSERT(byte_id >= 0);
@ -362,10 +359,14 @@ static inline void spi_ll_write_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t
uint32_t word;
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
//read-modify-write
if (copy_len != 4) word = hw->data_buf[byte_id / 4]; //read
if (copy_len != 4) {
word = hw->data_buf[byte_id / 4]; //read
}
memcpy(((uint8_t *)&word) + offset, data, copy_len); //modify
hw->data_buf[byte_id / 4] = word; //write
@ -409,7 +410,9 @@ static inline void spi_ll_read_buffer_byte(spi_dev_t *hw, int byte_id, uint8_t *
uint32_t word = hw->data_buf[byte_id / 4];
int offset = byte_id % 4;
int copy_len = 4 - offset;
if (copy_len > len) copy_len = len;
if (copy_len > len) {
copy_len = len;
}
memcpy(out_data, ((uint8_t *)&word) + offset, copy_len);
byte_id += copy_len;
@ -540,41 +543,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 io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
* @param hw Beginning address of the peripheral registers.
* @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.fcmd_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.faddr_dual = (io_mode == SPI_LL_IO_MODE_DIO) ? 1 : 0;
hw->ctrl.fread_dual = 1;
hw->user.fwrite_dual = 1;
hw->ctrl.fcmd_quad = 0;
hw->ctrl.faddr_quad = 0;
hw->ctrl.fread_quad = 0;
hw->user.fwrite_quad = 0;
} else if (io_mode == SPI_LL_IO_MODE_QIO || io_mode == SPI_LL_IO_MODE_QUAD) {
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;
}
hw->ctrl.val &= ~SPI_LL_ONE_LINE_CTRL_MASK;
hw->user.val &= ~SPI_LL_ONE_LINE_USER_MASK;
hw->ctrl.fcmd_dual = (line_mode.cmd_lines == 2);
hw->ctrl.fcmd_quad = (line_mode.cmd_lines == 4);
hw->ctrl.fcmd_oct = (line_mode.cmd_lines == 8);
hw->ctrl.faddr_dual = (line_mode.addr_lines == 2);
hw->ctrl.faddr_quad = (line_mode.addr_lines == 4);
hw->ctrl.faddr_oct = (line_mode.addr_lines == 8);
hw->ctrl.fread_dual = (line_mode.data_lines == 2);
hw->user.fwrite_dual = (line_mode.data_lines == 2);
hw->ctrl.fread_quad = (line_mode.data_lines == 4);
hw->user.fwrite_quad = (line_mode.data_lines == 4);
hw->ctrl.fread_oct = (line_mode.data_lines == 8);
hw->user.fwrite_oct = (line_mode.data_lines == 8);
}
/**
@ -618,7 +607,8 @@ static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
* @param hw Beginning address of the peripheral registers.
* @param keep_active if 0 don't keep CS activated, else keep CS activated
*/
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active) {
static inline void spi_ll_master_keep_cs(spi_dev_t *hw, int keep_active)
{
hw->misc.cs_keep_active = (keep_active != 0) ? 1 : 0;
}
@ -1003,35 +993,35 @@ static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
item(SPI_LL_INTR_CMDA, dma_int_ena.cmda, dma_int_raw.cmda, dma_int_clr.cmda, dma_int_set.cmda_int_set)
static inline void spi_ll_enable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_enable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define ENA_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 1;
FOR_EACH_ITEM(ENA_INTR, INTR_LIST);
#undef ENA_INTR
}
static inline void spi_ll_disable_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_disable_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define DIS_INTR(intr_bit, en_reg, ...) if (intr_mask & (intr_bit)) hw->en_reg = 0;
FOR_EACH_ITEM(DIS_INTR, INTR_LIST);
#undef DIS_INTR
}
static inline void spi_ll_set_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_set_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define SET_INTR(intr_bit, _, __, ___, set_reg) if (intr_mask & (intr_bit)) hw->set_reg = 1;
FOR_EACH_ITEM(SET_INTR, INTR_LIST);
#undef SET_INTR
}
static inline void spi_ll_clear_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline void spi_ll_clear_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define CLR_INTR(intr_bit, _, __, clr_reg, ...) if (intr_mask & (intr_bit)) hw->clr_reg = 1;
FOR_EACH_ITEM(CLR_INTR, INTR_LIST);
#undef CLR_INTR
}
static inline bool spi_ll_get_intr(spi_dev_t* hw, spi_ll_intr_t intr_mask)
static inline bool spi_ll_get_intr(spi_dev_t *hw, spi_ll_intr_t intr_mask)
{
#define GET_INTR(intr_bit, _, raw_reg, ...) if (intr_mask & (intr_bit) && hw->raw_reg) return true;
FOR_EACH_ITEM(GET_INTR, INTR_LIST);
@ -1085,7 +1075,7 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
/*------------------------------------------------------------------------------
* Slave HD
*----------------------------------------------------------------------------*/
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_cond_t cond_mask)
static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t *hw, spi_ll_trans_len_cond_t cond_mask)
{
hw->slave.rdbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_RDBUF) ? 1 : 0;
hw->slave.wrbuf_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRBUF) ? 1 : 0;
@ -1093,12 +1083,12 @@ static inline void spi_ll_slave_hd_set_len_cond(spi_dev_t* hw, spi_ll_trans_len_
hw->slave.wrdma_bitlen_en = (cond_mask & SPI_LL_TRANS_LEN_COND_WRDMA) ? 1 : 0;
}
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t* hw)
static inline int spi_ll_slave_get_rx_byte_len(spi_dev_t *hw)
{
return hw->slave1.data_bitlen / 8;
}
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t* hw)
static inline uint32_t spi_ll_slave_hd_get_last_addr(spi_dev_t *hw)
{
return hw->slave1.last_addr;
}

Wyświetl plik

@ -38,6 +38,7 @@
#include <esp_err.h>
#include "soc/lldesc.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
@ -100,7 +101,7 @@ typedef struct {
uint64_t addr; ///< Address value to be sent
uint8_t *send_buffer; ///< Data to be sent
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
} spi_hal_trans_config_t;

Wyświetl plik

@ -14,8 +14,9 @@
#pragma once
#include <stdint.h>
#include "esp_attr.h"
#include <esp_bit_defs.h>
#include "esp_bit_defs.h"
#include "soc/soc_caps.h"
#include "sdkconfig.h"
@ -45,6 +46,15 @@ typedef enum {
} spi_event_t;
FLAG_ATTR(spi_event_t)
/**
* @brief Line mode of SPI transaction phases: CMD, ADDR, DOUT/DIN.
*/
typedef struct {
uint8_t cmd_lines; ///< The line width of command phase, e.g. 2-line-cmd-phase.
uint8_t addr_lines; ///< The line width of address phase, e.g. 1-line-addr-phase.
uint8_t 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 */

Wyświetl plik

@ -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);
//We should be done with the transmission.
HAL_ASSERT(spi_ll_get_running_cmd(hw) == 0);
spi_ll_master_set_io_mode(hw, trans->io_mode);
//set transaction line mode
spi_ll_master_set_line_mode(hw, trans->line_mode);
int extra_dummy = 0;
//when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist

Wyświetl plik

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

Wyświetl plik

@ -22,21 +22,37 @@
#define SPI_IOMUX_PIN_NUM_MISO 31
#define SPI_IOMUX_PIN_NUM_WP 28
#define SPI2_FUNC_NUM FSPI_FUNC_NUM
#define SPI2_IOMUX_PIN_NUM_HD FSPI_IOMUX_PIN_NUM_HD
#define SPI2_IOMUX_PIN_NUM_CS FSPI_IOMUX_PIN_NUM_CS
#define SPI2_IOMUX_PIN_NUM_MOSI FSPI_IOMUX_PIN_NUM_MOSI
#define SPI2_IOMUX_PIN_NUM_CLK FSPI_IOMUX_PIN_NUM_CLK
#define SPI2_IOMUX_PIN_NUM_MISO FSPI_IOMUX_PIN_NUM_MISO
#define SPI2_IOMUX_PIN_NUM_WP FSPI_IOMUX_PIN_NUM_WP
// 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_IOMUX_PIN_NUM_HD 9
#define SPI2_IOMUX_PIN_NUM_CS 10
#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
//Following Macros are deprecated. Please use the Macros above
#define FSPI_FUNC_NUM 4
#define FSPI_IOMUX_PIN_NUM_HD 9
#define FSPI_IOMUX_PIN_NUM_CS 10
#define FSPI_IOMUX_PIN_NUM_MOSI 11
#define FSPI_IOMUX_PIN_NUM_CLK 12
#define FSPI_IOMUX_PIN_NUM_MISO 13
#define FSPI_IOMUX_PIN_NUM_WP 14
#define FSPI_FUNC_NUM SPI2_FUNC_NUM
#define FSPI_IOMUX_PIN_NUM_HD SPI2_IOMUX_PIN_NUM_HD
#define FSPI_IOMUX_PIN_NUM_CS SPI2_IOMUX_PIN_NUM_CS
#define FSPI_IOMUX_PIN_NUM_MOSI SPI2_IOMUX_PIN_NUM_MOSI
#define FSPI_IOMUX_PIN_NUM_CLK SPI2_IOMUX_PIN_NUM_CLK
#define FSPI_IOMUX_PIN_NUM_MISO SPI2_IOMUX_PIN_NUM_MISO
#define FSPI_IOMUX_PIN_NUM_WP SPI2_IOMUX_PIN_NUM_WP

Wyświetl plik

@ -50,10 +50,18 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = {
.spiq_out = FSPIQ_OUT_IDX,
.spiwp_out = FSPIWP_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,
.spiq_in = FSPIQ_IN_IDX,
.spiwp_in = FSPIWP_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_in = FSPICS0_IN_IDX,
.spiclk_iomux_pin = FSPI_IOMUX_PIN_NUM_CLK,

Wyświetl plik

@ -126,7 +126,24 @@
#define SOC_SIGMADELTA_CHANNEL_NUM (8) // 8 channels
/*-------------------------- 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 ----------------------------------------*/
#define SOC_SPIRAM_SUPPORTED 1

Wyświetl plik

@ -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

Wyświetl plik

@ -22,6 +22,9 @@
#define SPI_IOMUX_PIN_NUM_MISO 31
#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_IOMUX_PIN_NUM_HD 9
#define SPI2_IOMUX_PIN_NUM_CS 10
@ -30,4 +33,17 @@
#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 have no iomux pins

Wyświetl plik

@ -50,10 +50,18 @@ const spi_signal_conn_t spi_periph_signal[SOC_SPI_PERIPH_NUM] = {
.spiq_out = FSPIQ_OUT_IDX,
.spiwp_out = FSPIWP_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,
.spiq_in = FSPIQ_IN_IDX,
.spiwp_in = FSPIWP_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_in = FSPICS0_IN_IDX,
.spiclk_iomux_pin = SPI2_IOMUX_PIN_NUM_CLK,

Wyświetl plik

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

Wyświetl plik

@ -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.
**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.
**MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host.
**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/OPI mode.
**MISO** Master In, Slave Out, a.k.a. Q. Data transmission from a Device to Host. Also data1 signal in Octal/OPI mode.
**SCLK** Serial Clock. Oscillating signal generated by a Host that keeps the transmission of data bits in sync.
**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.
**QUADHD** Hold 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/OPI mode.
**QUADHD** Hold signal. Used for 4-bit (qio/qout) transactions. Also for data3 signal in Octal/OPI mode.
**DATA4** Data4 signal in Octal/OPI mode.
**DATA5** Data5 signal in Octal/OPI mode.
**DATA6** Data6 signal in Octal/OPI mode.
**DATA7** Data7 signal in Octal/OPI mode.
**Assertion** The action of activating a line.
**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.
@ -144,6 +148,59 @@ All the tasks that use interrupt transactions can be blocked by the queue. At th
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 us to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
.. _transaction-line-mode:
Transaction Line Mode
^^^^^^^^^^^^^^^^^^^^^
Supported line modes for {IDF_TARGET_NAME} are listed as follows, to make use of these modes, set the member `flags` in the struct :cpp:type:`spi_transaction_t` as shown in the `Transaction Flag` column. If you want to check if corresponding IO pins are set or not, set the member `flags` in the :cpp:type:`spi_bus_config_t` as shown in the `Bus IO setting Flag` column.
.. only:: not SOC_SPI_SUPPORT_OCT
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
+==============+====================+====================+=================+============================+=========================+
| Normal SPI | 1 | 1 | 1 | 0 | 0 |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
| | | | | | |
| | | | | | SPICOMMON_BUSFLAG_DUAL |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
| Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
.. only:: SOC_SPI_SUPPORT_OCT
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Mode name | Command Line Width | Address Line Width | Data Line Width | Transaction Flag | Bus IO setting Flag |
+==============+====================+====================+=================+============================+=========================+
| Normal SPI | 1 | 1 | 1 | 0 | 0 |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual Output | 1 | 1 | 2 | SPI_TRANS_MODE_DIO | |
| | | | | | |
| | | | | | SPICOMMON_BUSFLAG_DUAL |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Dual I/O | 1 | 2 | 2 | SPI_TRANS_MODE_DIO | | |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Quad Output | 1 | 1 | 4 | SPI_TRANS_MODE_QIO | |
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
| Quad I/O | 1 | 4 | 4 | SPI_TRANS_MODE_QIO | | SPICOMMON_BUSFLAG_QUAD |
| | | | | SPI_TRANS_MULTILINE_ADDR | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
| Octal Output | 1 | 1 | 8 | SPI_TRANS_MODE_OCT | |
+--------------+--------------------+--------------------+-----------------+----------------------------+ |
| OPI | 8 | 8 | 8 | SPI_TRANS_MODE_OCT | | SPICOMMON_BUSFLAG_OCTAL |
| | | | | SPI_TRANS_MULTILINE_ADDR | | |
| | | | | SPI_TRANS_MULTILINE_CMD | |
+--------------+--------------------+--------------------+-----------------+----------------------------+-------------------------+
Command and Address Phases
^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -152,6 +209,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 command and address phase need to be as the same number of lines as data phase, you need to set `SPI_TRANS_MULTILINE_CMD` and/or `SPI_TRANS_MULTILINE_ADDR` to the `flags` member in the struct :cpp:type:`spi_transaction_t`. Also see :ref:`transaction-line-mode`.
Write and Read Phases
^^^^^^^^^^^^^^^^^^^^^
@ -163,6 +222,8 @@ 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 using more than one data lines to transmit, please set `SPI_DEVICE_HALFDUPLEX` flag for the member `flags` in the struct :cpp:type:`spi_device_interface_config_t`. And the member `flags` in the struct :cpp:type:`spi_transaction_t` should be set as described in :ref:`transaction-line-mode`.
.. only:: esp32
.. note::

Wyświetl plik

@ -31,7 +31,7 @@ Introduction
------------
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):
- Command: 8-bit, master to slave

Wyświetl plik

@ -14,51 +14,70 @@ If you want to adapt this example to another type of display or pinout, check [l
### Hardware Required
* An ESP development board
* An ESP development board
* An SPI-interfaced LCD
* An USB cable for power supply and programming
### Hardware Connection
If using default settings, the hardware connection can be as below:
The connection between ESP Board and the LCD is as follows:
```
Board LCD Screen
+--------+ +---------------------------------+
| | | |
| 3V3 +--------------+ VCC +----------------------+ |
| | | | | |
| GND +--------------+ GND | | |
| | | | | |
| 23 +--------------+ MOSI | | |
| | | | | |
| 19 +--------------+ SCK | | |
| | | | | |
| 22 +--------------+ CS | | |
| | | | | |
| 21 +--------------+ DC | | |
| | | | | |
| 18 +--------------+ RST | | |
| | | | | |
| 5 +--------------+ BCKL +----------------------+ |
| | | |
+--------+ +---------------------------------+
ESP Board LCD Screen
+---------+ +---------------------------------+
| | | |
| 3V3 +--------------+ VCC +----------------------+ |
| | | | | |
| GND +--------------+ GND | | |
| | | | | |
| DATA0 +--------------+ MOSI | | |
| | | | | |
| PCLK +--------------+ SCK | | |
| | | | | |
| CS +--------------+ CS | | |
| | | | | |
| D/C +--------------+ D/C | | |
| | | | | |
| RST +--------------+ RST | | |
| | | | | |
|BK_LIGHT +--------------+ BCKL +----------------------+ |
| | | |
+---------+ +---------------------------------+
```
If not using default settings, the interface GPIOs should be set by macros in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where:
The GPIO number used by this example can be changed in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where:
PIN_NUM_MOSI stands for the GPIO number connected to 'MOSI' pin at LCD screen;
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_CS stands for the GPIO number connected to 'CS' pin at LCD screen;
PIN_NUM_DC stands for the GPIO number connected to 'DC' pin at LCD screen;
PIN_NUM_RST stands for the GPIO number connected to 'RST' pin at LCD screen;
PIN_NUM_BCKL stands for the GPIO number connected to 'LED' pin at LCD screen;
| GPIO number | LCD pin |
| ------------------------ | ------- |
| EXAMPLE_PIN_NUM_PCLK | SCK |
| EXAMPLE_PIN_NUM_CS | CS |
| EXAMPLE_PIN_NUM_DC | DC |
| EXAMPLE_PIN_NUM_RST | RST |
| EXAMPLE_PIN_NUM_DATA0 | MOSI |
| EXAMPLE_PIN_NUM_BK_LIGHT | BCKL |
Especially, please pay attention to the level used to turn on the LCD backlight, some LCD module needs a low level to turn it on, while others take a high level. You can change the backlight level macro `EXAMPLE_LCD_BK_LIGHT_ON_LEVEL` in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c).
#### Extra connections for 8-line LCD (Octal SPI)
Firstly, please run `idf.py menuconfig` and set the `Drive a LCD with 8 data lines` option at `Example Configuration`.
Change the extra GPOIs used by octal SPI in [lcd_tjpgd_example_main.c](main/lcd_tjpgd_example_main.c), where:
| GPIO number | LCD pin |
| ------------- | ------- |
| 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
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen.
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. A flowing picture will be shown on the LCD screen.
(To exit the serial monitor, type ``Ctrl-]``.)

Wyświetl plik

@ -1,26 +1,10 @@
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 EXAMPLE_LCD_SPI_8_LINE_MODE
bool
prompt "Run LCD at higher clock speed than allowed"
prompt "Drive a LCD with 8 data lines"
depends on IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default "n"
help
The ILI9341 and ST7789 specify that the maximum clock speed for the SPI interface is 10MHz. However,
in practice the driver chips work fine with a higher clock rate, and using that gives a better framerate.
Select this to try using the out-of-spec clock rate.
This option can be chosen when using 8-line lcd.
endmenu

Wyświetl plik

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

Wyświetl plik

@ -26,8 +26,9 @@ static inline uint16_t get_bgnd_pixel(int x, int y)
y+=8;
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
//TODO: add support for esp32s3 (IDF-3615)
static inline uint16_t get_bgnd_pixel(int x, int y)
{
return ((x<<3)^(y<<3)^(x*y));
@ -69,8 +70,9 @@ esp_err_t pretty_effect_init(void)
{
#ifdef CONFIG_IDF_TARGET_ESP32
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
//TODO: add support for esp32s3 (IDF-3615)
return ESP_OK;
#endif
}