spi: support new chip esp32s2beta

This commit is contained in:
Michael (XIAO Xufeng)
2019-06-13 14:19:31 +08:00
parent 93ef3980e7
commit 17378fd4c2
16 changed files with 1057 additions and 106 deletions

View File

@@ -19,7 +19,7 @@
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "esp32/rom/lldesc.h"
#include "soc/lldesc.h"
#include "soc/spi_periph.h"
#include "hal/spi_types.h"
#include "sdkconfig.h"
@@ -106,9 +106,6 @@ typedef struct {
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.
#if CONFIG_IDF_TARGET_ESP32S2BETA
int spicd_io_num; ///< CD GPIO pin for this device, or -1 if not used
#endif
int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4094 if 0.
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

View File

@@ -15,10 +15,8 @@
#include <string.h>
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "driver/spi_master.h"
#include "soc/spi_periph.h"
#include "esp32/rom/ets_sys.h"
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_log.h"
@@ -32,9 +30,6 @@
#include "driver/spi_common.h"
#include "stdatomic.h"
#include "hal/spi_hal.h"
#if CONFIG_IDF_TARGET_ESP32S2BETA
#include "cas.h"
#endif
static const char *SPI_TAG = "spi";
@@ -60,6 +55,9 @@ typedef struct spi_device_t spi_device_t;
//Periph 1 is 'claimed' by SPI flash code.
static atomic_bool spi_periph_claimed[SOC_SPI_PERIPH_NUM] = { ATOMIC_VAR_INIT(true), ATOMIC_VAR_INIT(false), ATOMIC_VAR_INIT(false),
#if SOC_SPI_PERIPH_NUM >= 4
ATOMIC_VAR_INIT(false),
#endif
};
static const char* spi_claiming_func[3] = {NULL, NULL, NULL};
static uint8_t spi_dma_chan_enabled = 0;
@@ -112,7 +110,20 @@ spi_dev_t *spicommon_hw_for_host(spi_host_device_t host)
static inline uint32_t get_dma_periph(int dma_chan)
{
#ifdef CONFIG_IDF_TARGET_ESP32S2BETA
if (dma_chan==1) {
return PERIPH_SPI2_DMA_MODULE;
} else if (dma_chan==2) {
return PERIPH_SPI3_DMA_MODULE;
} else if (dma_chan==3) {
return PERIPH_SPI_SHARED_DMA_MODULE;
} else {
abort();
return -1;
}
#elif defined(CONFIG_IDF_TARGET_ESP32)
return PERIPH_SPI_DMA_MODULE;
#endif
}
bool spicommon_dma_chan_claim (int dma_chan)
@@ -367,21 +378,12 @@ esp_err_t spicommon_bus_initialize_io(spi_host_device_t host, const spi_bus_conf
#endif
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], FUNC_GPIO);
}
#if CONFIG_IDF_TARGET_ESP32S2BETA
if (bus_config->spicd_io_num >= 0) {
gpio_set_direction(bus_config->spicd_io_num, GPIO_MODE_INPUT_OUTPUT);
gpio_matrix_out(bus_config->spicd_io_num, spi_periph_signal[host].spicd_out, false, false);
gpio_matrix_in(bus_config->spicd_io_num, spi_periph_signal[host].spicd_in, false);
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[bus_config->spicd_io_num]);
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->spicd_io_num], FUNC_GPIO);
}
#endif
}
//Select DMA channel.
#if CONFIG_IDF_TARGET_ESP32
#ifdef CONFIG_IDF_TARGET_ESP32
DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_chan, (host * 2));
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#elif defined(CONFIG_IDF_TARGET_ESP32S2BETA)
if (dma_chan==VSPI_HOST) {
DPORT_SET_PERI_REG_MASK(DPORT_SPI_DMA_CHAN_SEL_REG, DPORT_SPI_SHARED_DMA_SEL_M);
}
@@ -452,9 +454,7 @@ void spicommon_cs_initialize(spi_host_device_t host, int cs_io_num, int cs_num,
gpio_set_direction(cs_io_num, GPIO_MODE_INPUT);
}
if (cs_num == 0) gpio_matrix_in(cs_io_num, spi_periph_signal[host].spics_in, false);
#if CONFIG_IDF_TARGET_ESP32S2BETA
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[cs_io_num]);
#endif
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[cs_io_num], FUNC_GPIO);
}
}
@@ -483,13 +483,13 @@ void IRAM_ATTR spicommon_setup_dma_desc_links(lldesc_t *dmadesc, int len, const
/*
Code for workaround for DMA issue in ESP32 v0/v1 silicon
*/
#if CONFIG_IDF_TARGET_ESP32
static volatile int dmaworkaround_channels_busy[2] = {0, 0};
static dmaworkaround_cb_t dmaworkaround_cb;
static void *dmaworkaround_cb_arg;
static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED;
static int dmaworkaround_waiting_for_chan = 0;
#endif
bool IRAM_ATTR spicommon_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg)
{
@@ -510,14 +510,19 @@ bool IRAM_ATTR spicommon_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t
}
portEXIT_CRITICAL_ISR(&dmaworkaround_mux);
return ret;
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#else
//no need to reset
return true;
#endif
}
bool IRAM_ATTR spicommon_dmaworkaround_reset_in_progress()
{
#if CONFIG_IDF_TARGET_ESP32
return (dmaworkaround_waiting_for_chan != 0);
#else
return false;
#endif
}
void IRAM_ATTR spicommon_dmaworkaround_idle(int dmachan)
@@ -539,9 +544,9 @@ void IRAM_ATTR spicommon_dmaworkaround_idle(int dmachan)
void IRAM_ATTR spicommon_dmaworkaround_transfer_active(int dmachan)
{
#if CONFIG_IDF_TARGET_ESP32
portENTER_CRITICAL_ISR(&dmaworkaround_mux);
dmaworkaround_channels_busy[dmachan-1] = 1;
portEXIT_CRITICAL_ISR(&dmaworkaround_mux);
}
#endif
}

View File

@@ -133,19 +133,11 @@ We have two bits to control the interrupt:
#include "freertos/xtensa_api.h"
#include "freertos/task.h"
#include "soc/soc_memory_layout.h"
#include "soc/dport_access.h"
#include "esp32/rom/lldesc.h"
#include "driver/gpio.h"
#include "esp_heap_caps.h"
#include "stdatomic.h"
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "soc/dport_reg.h"
#include "esp32/rom/ets_sys.h"
#include "hal/spi_hal.h"
#if CONFIG_IDF_TARGET_ESP32S2BETA
#include "cas.h"
#endif
typedef struct spi_device_t spi_device_t;
@@ -188,9 +180,6 @@ typedef struct {
int dma_chan;
int max_transfer_sz;
spi_bus_config_t bus_cfg;
#if CONFIG_IDF_TARGET_ESP32S2BETA
int id;
#endif
#ifdef CONFIG_PM_ENABLE
esp_pm_lock_handle_t pm_lock;
#endif
@@ -229,7 +218,11 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
SPI_CHECK(host!=SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED);
SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32
SPI_CHECK( dma_chan >= 0 && dma_chan <= 2, "invalid dma channel", ESP_ERR_INVALID_ARG );
#elif CONFIG_IDF_TARGET_ESP32S2BETA
SPI_CHECK( dma_chan == 0 || dma_chan == host, "invalid dma channel", ESP_ERR_INVALID_ARG );
#endif
SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH|ESP_INTR_FLAG_EDGE|ESP_INTR_FLAG_INTRDISABLED))==0, "intr flag not allowed", ESP_ERR_INVALID_ARG);
#ifndef CONFIG_SPI_MASTER_ISR_IN_IRAM
SPI_CHECK((bus_config->intr_flags & ESP_INTR_FLAG_IRAM)==0, "ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_MASTER_ISR_IN_IRAM is not set.", ESP_ERR_INVALID_ARG);
@@ -254,9 +247,6 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
memset(spihost[host], 0, sizeof(spi_host_t));
memcpy( &spihost[host]->bus_cfg, bus_config, sizeof(spi_bus_config_t));
#if CONFIG_IDF_TARGET_ESP32S2BETA
spihost[host]->id = host;
#endif
#ifdef CONFIG_PM_ENABLE
err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "spi_master",
&spihost[host]->pm_lock);
@@ -271,13 +261,6 @@ esp_err_t spi_bus_initialize(spi_host_device_t host, const spi_bus_config_t *bus
ret = err;
goto cleanup;
}
#if CONFIG_IDF_TARGET_ESP32
DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_chan, (host * 2));
#elif CONFIG_IDF_TARGET_ESP32S2BETA
if (host==VSPI_HOST) {
DPORT_SET_PERI_REG_BITS(DPORT_SPI_SHARED_DMA_SEL_REG, DPORT_SPI_SHARED_DMA_SEL_M, 1, DPORT_SPI_SHARED_DMA_SEL_S);
}
#endif
int dma_desc_ct=0;
spihost[host]->dma_chan=dma_chan;
if (dma_chan == 0) {
@@ -397,10 +380,12 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
if (atomic_compare_exchange_strong(&spihost[host]->device[freecs], &null, (spi_device_t *)1)) break;
}
SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND);
#ifdef CONFIG_IDF_TARGET_ESP32
//The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
//duplex mode does absolutely nothing on the ESP32.
SPI_CHECK( dev_config->cs_ena_pretrans <= 1 || (dev_config->address_bits == 0 && dev_config->command_bits == 0) ||
(dev_config->flags & SPI_DEVICE_HALFDUPLEX), "In full-duplex mode, only support cs pretrans delay = 1 and without address_bits and command_bits", ESP_ERR_INVALID_ARG);
#endif
duty_cycle = (dev_config->duty_cycle_pos==0) ? 128 : dev_config->duty_cycle_pos;
@@ -822,8 +807,12 @@ static SPI_MASTER_ISR_ATTR esp_err_t check_trans_valid(spi_device_handle_t handl
//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)) && (!(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX))), "incompatible iface params", ESP_ERR_INVALID_ARG);
#ifdef CONFIG_IDF_TARGET_ESP32
SPI_CHECK( !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || host->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL)
|| !(trans_desc->flags & SPI_TRANS_USE_TXDATA || trans_desc->tx_buffer!=NULL), "SPI half duplex mode does not support using DMA with both MOSI and MISO phases.", ESP_ERR_INVALID_ARG );
#else
(void)host;
#endif
//MOSI phase is skipped only when both tx_buffer and SPI_TRANS_USE_TXDATA are not set.
SPI_CHECK(trans_desc->length != 0 || (trans_desc->tx_buffer == NULL && !(trans_desc->flags & SPI_TRANS_USE_TXDATA)),
"trans tx_buffer should be NULL and SPI_TRANS_USE_TXDATA should be cleared to skip MOSI phase.", ESP_ERR_INVALID_ARG);
@@ -1104,4 +1093,3 @@ esp_err_t SPI_MASTER_ISR_ATTR spi_device_polling_transmit(spi_device_handle_t ha
return ESP_OK;
}
#endif

View File

@@ -14,7 +14,6 @@
#include <string.h>
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include <hal/spi_ll.h>
#include <hal/spi_slave_hal.h>
#include <soc/lldesc.h>
@@ -32,10 +31,9 @@
#include "freertos/xtensa_api.h"
#include "freertos/task.h"
#include "soc/soc_memory_layout.h"
#include "esp32/rom/lldesc.h"
#include "driver/gpio.h"
#include "esp_heap_caps.h"
#include "esp32/rom/ets_sys.h"
static const char *SPI_TAG = "spi_slave";
#define SPI_CHECK(a, str, ret_val) \
if (!(a)) { \
@@ -105,7 +103,11 @@ esp_err_t spi_slave_initialize(spi_host_device_t host, const spi_bus_config_t *b
esp_err_t err;
//We only support HSPI/VSPI, period.
SPI_CHECK(VALID_HOST(host), "invalid host", ESP_ERR_INVALID_ARG);
#if defined(CONFIG_IDF_TARGET_ESP32)
SPI_CHECK( dma_chan >= 0 && dma_chan <= 2, "invalid dma channel", ESP_ERR_INVALID_ARG );
#elif defined(CONFIG_IDF_TARGET_ESP32S2BETA)
SPI_CHECK( dma_chan == 0 || dma_chan == host, "invalid dma channel", ESP_ERR_INVALID_ARG );
#endif
SPI_CHECK((bus_config->intr_flags & (ESP_INTR_FLAG_HIGH|ESP_INTR_FLAG_EDGE|ESP_INTR_FLAG_INTRDISABLED))==0, "intr flag not allowed", ESP_ERR_INVALID_ARG);
#ifndef CONFIG_SPI_SLAVE_ISR_IN_IRAM
SPI_CHECK((bus_config->intr_flags & ESP_INTR_FLAG_IRAM)==0, "ESP_INTR_FLAG_IRAM should be disabled when CONFIG_SPI_SLAVE_ISR_IN_IRAM is not set.", ESP_ERR_INVALID_ARG);
@@ -393,4 +395,3 @@ static void SPI_SLAVE_ISR_ATTR spi_intr(void *arg)
if (do_yield) portYIELD_FROM_ISR();
}
#endif

View File

@@ -52,6 +52,12 @@ typedef enum {
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;
/// Interrupt type for different working pattern
typedef enum {
SPI_LL_INT_TYPE_NORMAL = 0, ///< Typical pattern, only wait for trans done
} spi_ll_slave_intr_type;
/*------------------------------------------------------------------------------
* Control
*----------------------------------------------------------------------------*/
@@ -259,6 +265,10 @@ static inline void spi_ll_enable_int(spi_dev_t *hw)
hw->slave.trans_inten = 1;
}
static inline void spi_ll_slave_set_int_type(spi_dev_t *hw, spi_ll_slave_intr_type int_type)
{
hw->slave.trans_inten = 1;
}
/*------------------------------------------------------------------------------
* Configs: mode

View File

@@ -1,9 +1,9 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 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
@@ -12,8 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_SPI_CAPS_H_
#define _SOC_SPI_CAPS_H_
#pragma once
#define SOC_SPI_PERIPH_NUM 3
#define SOC_SPI_DMA_CHAN_NUM 2
@@ -47,5 +46,6 @@
#define SOC_SPI_MAXIMUM_BUFFER_SIZE 64
#define SOC_SPI_SUPPORT_AS_CS 1 //Support to toggle the CS while the clock toggles
#endif /* _SOC_SPI_CAPS_H_ */
//#define SOC_SPI_SUPPORT_DDRCLK
//#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS
//#define SOC_SPI_SUPPORT_CD_SIG

View File

@@ -0,0 +1,874 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The LL layer for ESP32s2beta SPI register operations
#pragma once
#include "hal/hal_defs.h"
#include "soc/spi_periph.h"
#include "esp32/rom/lldesc.h"
#include <string.h>
#include <esp_types.h>
#include <stdlib.h> //for abs()
/// Registers to reset during initialization. Don't use in app.
#define SPI_LL_RST_MASK (SPI_OUT_RST | SPI_IN_RST | 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)
/// 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))
/**
* The data structure holding calculated clock configuration. Since the
* calculation needs long time, it should be calculated during initialization and
* stored somewhere to be quickly used.
*/
typedef uint32_t spi_ll_clock_val_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;
/// Interrupt type for different working pattern
typedef enum {
SPI_LL_INT_TYPE_NORMAL = 0, ///< Typical pattern, only wait for trans done
SPI_LL_INT_TYPE_SEG = 1, ///< Wait for DMA signals
} spi_ll_slave_intr_type;
/*------------------------------------------------------------------------------
* Control
*----------------------------------------------------------------------------*/
/**
* Initialize SPI peripheral (master).
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_master_init(spi_dev_t *hw)
{
//Reset DMA
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
//Reset timing
hw->ctrl2.val = 0;
//use all 64 bytes of the buffer
hw->user.usr_miso_highpart = 0;
hw->user.usr_mosi_highpart = 0;
//Disable unneeded ints
hw->slave.val &= ~SPI_LL_UNUSED_INT_MASK;
}
/**
* Initialize SPI peripheral (slave).
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_slave_init(spi_dev_t *hw)
{
//it's stupid, but if something goes wrong, try to uncomment it
//hw->slave.slave_mode = 1;
//Configure slave
hw->clock.val = 0;
hw->user.val = 0;
hw->ctrl.val = 0;
hw->user.doutdin = 1; //we only support full duplex
hw->user.sio = 0;
hw->user.tx_start_bit = 7;
hw->slave.slave_mode = 1;
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->slave.sync_reset = 1;
hw->slave.sync_reset = 0;
//use all 64 bytes of the buffer
hw->user.usr_miso_highpart = 0;
hw->user.usr_mosi_highpart = 0;
//by default seg mode is disabled
hw->dma_conf.dma_continue = 0;
//Disable unneeded ints
hw->slave.val &= ~SPI_LL_UNUSED_INT_MASK;
hw->dma_int_ena.val = 0;
}
/**
* Reset TX and RX DMAs.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_reset_dma(spi_dev_t *hw)
{
//Reset DMA peripheral
hw->dma_conf.val |= SPI_LL_RST_MASK;
hw->dma_out_link.start = 0;
hw->dma_in_link.start = 0;
hw->dma_conf.val &= ~SPI_LL_RST_MASK;
hw->dma_conf.out_data_burst_en = 1;
hw->dma_conf.indscr_burst_en = 1;
hw->dma_conf.outdscr_burst_en = 1;
hw->dma_in_link.dma_rx_ena = 0;
assert(hw->dma_in_link.dma_rx_ena==0);
}
/**
* Start RX DMA.
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_rxdma_start(spi_dev_t *hw, lldesc_t *addr)
{
//if something breaks, uncomment this line
//hw->dma_in_link.restart = 1;
hw->dma_in_link.addr = (int) addr & 0xFFFFF;
hw->dma_in_link.start = 1;
}
/**
* Start TX DMA.
*
* @param hw Beginning address of the peripheral registers.
* @param addr Address of the beginning DMA descriptor.
*/
static inline void spi_ll_txdma_start(spi_dev_t *hw, lldesc_t *addr)
{
//if something breaks, uncomment this line
hw->dma_out_link.restart = 1;
hw->dma_out_link.addr = (int) addr & 0xFFFFF;
hw->dma_out_link.start = 1;
}
/**
* Write to SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_send Data address to copy to the buffer.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_write_buffer(spi_dev_t *hw, const uint8_t *buffer_to_send, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Use memcpy to get around alignment issues for txdata
uint32_t word;
memcpy(&word, &buffer_to_send[x / 8], 4);
hw->data_buf[(x / 32)] = word;
}
}
/**
* Read from SPI buffer.
*
* @param hw Beginning address of the peripheral registers.
* @param buffer_to_rcv Address to copy buffer data to.
* @param bitlen Length to copy, in bits.
*/
static inline void spi_ll_read_buffer(spi_dev_t *hw, uint8_t *buffer_to_rcv, size_t bitlen)
{
for (int x = 0; x < bitlen; x += 32) {
//Do a memcpy to get around possible alignment issues in rx_buffer
uint32_t word = hw->data_buf[x / 32];
int len = bitlen - x;
if (len > 32) {
len = 32;
}
memcpy(&buffer_to_rcv[x / 8], &word, (len + 7) / 8);
}
}
/**
* Check whether user-defined transaction is done.
*
* @param hw Beginning address of the peripheral registers.
*
* @return true if transaction is done, otherwise false.
*/
static inline bool spi_ll_usr_is_done(spi_dev_t *hw)
{
return hw->slave.trans_done;
}
/**
* Trigger start of user-defined transaction.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_user_start(spi_dev_t *hw)
{
hw->cmd.usr = 1;
}
/**
* Get current running command bit-mask. (Preview)
*
* @param hw Beginning address of the peripheral registers.
*
* @return Bitmask of running command, see ``SPI_CMD_REG``. 0 if no in-flight command.
*/
static inline uint32_t spi_ll_get_running_cmd(spi_dev_t *hw)
{
return hw->cmd.val;
}
/**
* Disable the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_disable_int(spi_dev_t *hw)
{
hw->slave.int_trans_done_en = 0;
}
/**
* Clear the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_clear_int_stat(spi_dev_t *hw)
{
hw->slave.trans_done = 0;
hw->dma_int_clr.val = UINT32_MAX;
}
/**
* Set the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_set_int_stat(spi_dev_t *hw)
{
hw->slave.trans_done = 1;
}
/**
* Enable the trans_done interrupt.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_enable_int(spi_dev_t *hw)
{
hw->slave.int_trans_done_en = 1;
}
/**
* Set different interrupt types for the slave.
*
* @param hw Beginning address of the peripheral registers.
* @param int_type Interrupt type
*/
static inline void spi_ll_slave_set_int_type(spi_dev_t *hw, spi_ll_slave_intr_type int_type)
{
switch (int_type)
{
case SPI_LL_INT_TYPE_SEG:
hw->dma_int_ena.in_suc_eof = 1;
hw->dma_int_ena.out_total_eof = 1;
hw->slave.int_trans_done_en = 0;
break;
default:
hw->dma_int_ena.in_suc_eof = 0;
hw->dma_int_ena.out_total_eof = 0;
hw->slave.int_trans_done_en = 1;
}
}
/*------------------------------------------------------------------------------
* Configs: mode
*----------------------------------------------------------------------------*/
/**
* Enable/disable the postive-cs feature.
*
* @param hw Beginning address of the peripheral registers.
* @param cs One of the CS (0-2) to enable/disable the feature.
* @param pos_cs true to enable the feature, otherwise disable (default).
*/
static inline void spi_ll_master_set_pos_cs(spi_dev_t *hw, int cs, uint32_t pos_cs)
{
if (pos_cs) {
hw->misc.master_cs_pol |= (1 << cs);
} else {
hw->misc.master_cs_pol &= (1 << cs);
}
}
/**
* Enable/disable the LSBFIRST feature for TX data.
*
* @param hw Beginning address of the peripheral registers.
* @param lsbfirst true if LSB of TX data to be sent first, otherwise MSB is sent first (default).
*/
static inline void spi_ll_set_tx_lsbfirst(spi_dev_t *hw, bool lsbfirst)
{
hw->ctrl.wr_bit_order = lsbfirst;
}
/**
* Enable/disable the LSBFIRST feature for RX data.
*
* @param hw Beginning address of the peripheral registers.
* @param lsbfirst true if first bit received as LSB, otherwise as MSB (default).
*/
static inline void spi_ll_set_rx_lsbfirst(spi_dev_t *hw, bool lsbfirst)
{
hw->ctrl.rd_bit_order = lsbfirst;
}
/**
* Set SPI mode for the peripheral as master.
*
* @param hw Beginning address of the peripheral registers.
* @param mode SPI mode to work at, 0-3.
*/
static inline void spi_ll_master_set_mode(spi_dev_t *hw, uint8_t mode)
{
//Configure polarity
if (mode == 0) {
hw->misc.ck_idle_edge = 0;
hw->user.ck_out_edge = 0;
} else if (mode == 1) {
hw->misc.ck_idle_edge = 0;
hw->user.ck_out_edge = 1;
} else if (mode == 2) {
hw->misc.ck_idle_edge = 1;
hw->user.ck_out_edge = 1;
} else if (mode == 3) {
hw->misc.ck_idle_edge = 1;
hw->user.ck_out_edge = 0;
}
}
/**
* Set SPI mode for the peripheral as slave.
*
* @param hw Beginning address of the peripheral registers.
* @param mode SPI mode to work at, 0-3.
*/
static inline void spi_ll_slave_set_mode(spi_dev_t *hw, const int mode, bool dma_used)
{
if (mode == 0) {
hw->misc.ck_idle_edge = 0;
hw->user.rsck_i_edge = 0;
hw->user.tsck_i_edge = 0;
hw->ctrl1.rsck_data_out = 0;
hw->ctrl1.clk_mode_13 = 0;
} else if (mode == 1) {
hw->misc.ck_idle_edge = 0;
hw->user.rsck_i_edge = 1;
hw->user.tsck_i_edge = 1;
hw->ctrl1.rsck_data_out = 0;
hw->ctrl1.clk_mode_13 = 1;
} else if (mode == 2) {
hw->misc.ck_idle_edge = 1;
hw->user.rsck_i_edge = 1;
hw->user.tsck_i_edge = 1;
hw->ctrl1.rsck_data_out = 0;
hw->ctrl1.clk_mode_13 = 0;
} else if (mode == 3) {
hw->misc.ck_idle_edge = 1;
hw->user.rsck_i_edge = 0;
hw->user.tsck_i_edge = 0;
hw->ctrl1.rsck_data_out = 0;
hw->ctrl1.clk_mode_13 = 1;
}
//hw->ctrl1.rsck_data_out = 1;
}
/**
* Set SPI to work in full duplex or half duplex mode.
*
* @param hw Beginning address of the peripheral registers.
* @param half_duplex true to work in half duplex mode, otherwise in full duplex mode.
*/
static inline void spi_ll_set_half_duplex(spi_dev_t *hw, bool half_duplex)
{
hw->user.doutdin = !half_duplex;
}
/**
* Set SPI to work in SIO mode or not.
*
* SIO is a mode which MOSI and MISO share a line. The device MUST work in half-duplexmode.
*
* @param hw Beginning address of the peripheral registers.
* @param sio_mode true to work in SIO mode, otherwise false.
*/
static inline void spi_ll_set_sio_mode(spi_dev_t *hw, int sio_mode)
{
hw->user.sio = sio_mode;
}
/**
* Configure the io mode for the master to work at.
*
* @param hw Beginning address of the peripheral registers.
* @param io_mode IO mode to work at, see ``spi_ll_io_mode_t``.
*/
static inline void spi_ll_master_set_io_mode(spi_dev_t *hw, spi_ll_io_mode_t io_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;
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;
break;
default:
break;
};
// if (io_mode != SPI_LL_IO_MODE_NORMAL) {
// hw->ctrl.fastrd_mode = 1;
// }
}
/**
* Select one of the CS to use in current transaction.
*
* @param hw Beginning address of the peripheral registers.
* @param cs_id The cs to use, 0-2, otherwise none of them is used.
*/
static inline void spi_ll_master_select_cs(spi_dev_t *hw, int cs_id)
{
hw->misc.cs0_dis = (cs_id == 0) ? 0 : 1;
hw->misc.cs1_dis = (cs_id == 1) ? 0 : 1;
hw->misc.cs2_dis = (cs_id == 2) ? 0 : 1;
}
/*------------------------------------------------------------------------------
* Configs: parameters
*----------------------------------------------------------------------------*/
/**
* Set the clock for master by stored value.
*
* @param hw Beginning address of the peripheral registers.
* @param val stored clock configuration calculated before (by ``spi_ll_cal_clock``).
*/
static inline void spi_ll_master_set_clock_by_reg(spi_dev_t *hw, spi_ll_clock_val_t *val)
{
hw->clock.val = *(uint32_t *)val;
}
/**
* Get the frequency of given dividers. Don't use in app.
*
* @param fapb APB clock of the system.
* @param pre Pre devider.
* @param n main divider.
*
* @return Frequency of given dividers.
*/
static inline int spi_ll_freq_for_pre_n(int fapb, int pre, int n)
{
return (fapb / (pre * n));
}
/**
* Calculate the nearest frequency avaliable for master.
*
* @param fapb APB clock of the system.
* @param hz Frequncy desired.
* @param duty_cycle Duty cycle desired.
* @param out_reg Output address to store the calculated clock configurations for the return frequency.
*
* @return Actual (nearest) frequency.
*/
static inline int spi_ll_master_cal_clock(int fapb, int hz, int duty_cycle, spi_ll_clock_val_t *out_reg)
{
typeof(GPSPI2.clock) reg;
int eff_clk;
//In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value.
if (hz > ((fapb / 4) * 3)) {
//Using Fapb directly will give us the best result here.
reg.clkcnt_l = 0;
reg.clkcnt_h = 0;
reg.clkcnt_n = 0;
reg.clkdiv_pre = 0;
reg.clk_equ_sysclk = 1;
eff_clk = fapb;
} else {
//For best duty cycle resolution, we want n to be as close to 32 as possible, but
//we also need a pre/n combo that gets us as close as possible to the intended freq.
//To do this, we bruteforce n and calculate the best pre to go along with that.
//If there's a choice between pre/n combos that give the same result, use the one
//with the higher n.
int pre, n, h, l;
int bestn = -1;
int bestpre = -1;
int besterr = 0;
int errval;
for (n = 2; n <= 64; n++) { //Start at 2: we need to be able to set h/l so we have at least one high and one low pulse.
//Effectively, this does pre=round((fapb/n)/hz).
pre = ((fapb / n) + (hz / 2)) / hz;
if (pre <= 0) {
pre = 1;
}
if (pre > 8192) {
pre = 8192;
}
errval = abs(spi_ll_freq_for_pre_n(fapb, pre, n) - hz);
if (bestn == -1 || errval <= besterr) {
besterr = errval;
bestn = n;
bestpre = pre;
}
}
n = bestn;
pre = bestpre;
l = n;
//This effectively does round((duty_cycle*n)/256)
h = (duty_cycle * n + 127) / 256;
if (h <= 0) {
h = 1;
}
reg.clk_equ_sysclk = 0;
reg.clkcnt_n = n - 1;
reg.clkdiv_pre = pre - 1;
reg.clkcnt_h = h - 1;
reg.clkcnt_l = l - 1;
eff_clk = spi_ll_freq_for_pre_n(fapb, pre, n);
}
if (out_reg != NULL) {
*(uint32_t *)out_reg = reg.val;
}
return eff_clk;
}
/**
* Calculate and set clock for SPI master according to desired parameters.
*
* This takes long, suggest to calculate the configuration during
* initialization by ``spi_ll_master_cal_clock`` and store the result, then
* configure the clock by stored value when used by
* ``spi_ll_msater_set_clock_by_reg``.
*
* @param hw Beginning address of the peripheral registers.
* @param fapb APB clock of the system.
* @param hz Frequncy desired.
* @param duty_cycle Duty cycle desired.
*
* @return Actual frequency that is used.
*/
static inline int spi_ll_master_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle)
{
spi_ll_clock_val_t reg_val;
int freq = spi_ll_master_cal_clock(fapb, hz, duty_cycle, &reg_val);
spi_ll_master_set_clock_by_reg(hw, &reg_val);
return freq;
}
/**
* Set the mosi delay after the output edge to the signal. (Preview)
*
* The delay mode/num is a Espressif conception, may change in the new chips.
*
* @param hw Beginning address of the peripheral registers.
* @param delay_mode Delay mode, see TRM.
* @param delay_num APB clocks to delay.
*/
static inline void spi_ll_set_mosi_delay(spi_dev_t *hw, int delay_mode, int delay_num)
{
//TODO: this doesn't make sense
hw->dout_num.dout0_num = 0;
hw->dout_num.dout1_num = 0;
}
/**
* Set the miso delay applied to the input signal before the internal peripheral. (Preview)
*
* The delay mode/num is a Espressif conception, may change in the new chips.
*
* @param hw Beginning address of the peripheral registers.
* @param delay_mode Delay mode, see TRM.
* @param delay_num APB clocks to delay.
*/
static inline void spi_ll_set_miso_delay(spi_dev_t *hw, int delay_mode, int delay_num)
{
//TODO: this doesn't make sense
hw->din_num.din0_num = 1;
hw->din_num.din1_num = 1;
}
/**
* Set dummy clocks to output before RX phase (master), or clocks to skip
* before the data phase and after the address phase (slave).
*
* Note this phase is also used to compensate RX timing in half duplex mode.
*
* @param hw Beginning address of the peripheral registers.
* @param dummy_n Dummy cycles used. 0 to disable the dummy phase.
*/
static inline void spi_ll_set_dummy(spi_dev_t *hw, int dummy_n)
{
hw->user.usr_dummy = dummy_n ? 1 : 0;
hw->user1.usr_dummy_cyclelen = dummy_n - 1;
}
/**
* Set the delay of SPI clocks before the CS inactive edge after the last SPI clock.
*
* @param hw Beginning address of the peripheral registers.
* @param hold Delay of SPI clocks after the last clock, 0 to disable the hold phase.
*/
static inline void spi_ll_master_set_cs_hold(spi_dev_t *hw, int hold)
{
hw->ctrl2.cs_hold_time = hold - 1;
hw->user.cs_hold = hold ? 1 : 0;
}
/**
* Set the delay of SPI clocks before the first SPI clock after the CS active edge.
*
* Note ESP32 doesn't support to use this feature when command/address phases
* are used in full duplex mode.
*
* @param hw Beginning address of the peripheral registers.
* @param setup Delay of SPI clocks after the CS active edge, 0 to disable the setup phase.
*/
static inline void spi_ll_master_set_cs_setup(spi_dev_t *hw, uint8_t setup)
{
hw->ctrl2.cs_setup_time = setup - 1;
hw->user.cs_setup = setup ? 1 : 0;
}
/**
* Enable/disable the segment transfer feature for the slave.
*
* @param hw Beginning address of the peripheral registers.
* @param en true to enable, false to disable.
*/
static inline void spi_ll_slave_set_seg_en(spi_dev_t *hw, bool en)
{
hw->dma_conf.slv_rx_seg_trans_en = en;
}
/*------------------------------------------------------------------------------
* Configs: data
*----------------------------------------------------------------------------*/
/**
* Set the input length (master).
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen input length, in bits.
*/
static inline void spi_ll_set_miso_bitlen(spi_dev_t *hw, size_t bitlen)
{
hw->miso_dlen.usr_miso_bit_len = bitlen - 1;
}
/**
* Set the output length (master).
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen output length, in bits.
*/
static inline void spi_ll_set_mosi_bitlen(spi_dev_t *hw, size_t bitlen)
{
hw->mosi_dlen.usr_mosi_bit_len = bitlen - 1;
}
/**
* Set the maximum input length (slave).
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen input length, in bits.
*/
static inline void spi_ll_slave_set_rx_bitlen(spi_dev_t *hw, size_t bitlen)
{
hw->slv_wrbuf_dlen.bit_len = bitlen - 1;
}
/**
* Set the maximum output length (slave).
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen output length, in bits.
*/
static inline void spi_ll_slave_set_tx_bitlen(spi_dev_t *hw, size_t bitlen)
{
hw->slv_rdbuf_dlen.bit_len = bitlen - 1;
}
/**
* Set the length of command phase.
*
* When in 4-bit mode, the SPI cycles of the phase will be shorter. E.g. 16-bit
* command phases takes 4 cycles in 4-bit mode.
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen Length of command phase, in bits. 0 to disable the command phase.
*/
static inline void spi_ll_set_command_bitlen(spi_dev_t *hw, int bitlen)
{
hw->user2.usr_command_bitlen = bitlen - 1;
hw->user.usr_command = bitlen ? 1 : 0;
}
/**
* Set the length of address phase.
*
* When in 4-bit mode, the SPI cycles of the phase will be shorter. E.g. 16-bit
* address phases takes 4 cycles in 4-bit mode.
*
* @param hw Beginning address of the peripheral registers.
* @param bitlen Length of address phase, in bits. 0 to disable the address phase.
*/
static inline void spi_ll_set_addr_bitlen(spi_dev_t *hw, int bitlen)
{
hw->user1.usr_addr_bitlen = bitlen - 1;
hw->user.usr_addr = bitlen ? 1 : 0;
}
/**
* Set the address value in an intuitive way.
*
* The length and lsbfirst is required to shift and swap the address to the right place.
*
* @param hw Beginning address of the peripheral registers.
* @param address Address to set
* @param addrlen Length of the address phase
* @param lsbfirst whether the LSB first feature is enabled.
*/
static inline void spi_ll_set_address(spi_dev_t *hw, uint64_t addr, int addrlen, uint32_t lsbfirst)
{
if (lsbfirst) {
/* The output address start from the LSB of the highest byte, i.e.
* addr[24] -> addr[31]
* ...
* addr[0] -> addr[7]
* slv_wr_status[24] -> slv_wr_status[31]
* ...
* slv_wr_status[0] -> slv_wr_status[7]
* So swap the byte order to let the LSB sent first.
*/
addr = HAL_SWAP64(addr);
hw->addr = addr >> 32;
hw->slv_wr_status = addr;
} else {
// shift the address to MSB of addr (and maybe slv_wr_status) register.
// output address will be sent from MSB to LSB of addr register, then comes the MSB to LSB of slv_wr_status register.
if (addrlen > 32) {
hw->addr = addr >> (addrlen - 32);
hw->slv_wr_status = addr << (64 - addrlen);
} else {
hw->addr = addr << (32 - addrlen);
}
}
}
/**
* Set the command value in an intuitive way.
*
* The length and lsbfirst is required to shift and swap the command to the right place.
*
* @param hw Beginning command of the peripheral registers.
* @param command Command to set
* @param addrlen Length of the command phase
* @param lsbfirst whether the LSB first feature is enabled.
*/
static inline void spi_ll_set_command(spi_dev_t *hw, uint16_t cmd, int cmdlen, bool lsbfirst)
{
if (lsbfirst) {
// The output command start from bit0 to bit 15, kept as is.
hw->user2.usr_command_value = cmd;
} else {
/* Output command will be sent from bit 7 to 0 of command_value, and
* then bit 15 to 8 of the same register field. Shift and swap to send
* more straightly.
*/
hw->user2.usr_command_value = HAL_SPI_SWAP_DATA_TX(cmd, cmdlen);
}
}
/**
* Enable/disable the RX data phase.
*
* @param hw Beginning address of the peripheral registers.
* @param enable true if RX phase exist, otherwise false.
*/
static inline void spi_ll_enable_miso(spi_dev_t *hw, int enable)
{
hw->user.usr_miso = enable;
}
/**
* Enable/disable the TX data phase.
*
* @param hw Beginning address of the peripheral registers.
* @param enable true if TX phase exist, otherwise false.
*/
static inline void spi_ll_enable_mosi(spi_dev_t *hw, int enable)
{
hw->user.usr_mosi = enable;
}
/**
* Reset the slave peripheral before next transaction.
*
* @param hw Beginning address of the peripheral registers.
*/
static inline void spi_ll_slave_reset(spi_dev_t *hw)
{
hw->slave.sync_reset = 1;
hw->slave.sync_reset = 0;
}
/**
* Get the received bit length of the slave.
*
* @param hw Beginning address of the peripheral registers.
*
* @return Received bits of the slave.
*/
static inline uint32_t spi_ll_slave_get_rcv_bitlen(spi_dev_t *hw)
{
return hw->slv_rd_byte.slv_rdata_bit * 8;
}
#undef SPI_LL_RST_MASK
#undef SPI_LL_UNUSED_INT_MASK

View File

@@ -232,6 +232,7 @@
#define TIMER_CLK_FREQ (80000000>>4) //80MHz divided by 16
#define SPI_CLK_DIV 4
#define TICKS_PER_US_ROM 40 // CPU is 80MHz
#define GPIO_MATRIX_DELAY_NS 25
//}}
/* Overall memory map */

View File

@@ -1,9 +1,9 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2019 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
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_SPI_PINS_H_
#define _SOC_SPI_PINS_H_
#pragma once
#define SPI_PERIPH_NUM 4
#define SOC_SPI_PERIPH_NUM 4
#define SOC_SPI_DMA_CHAN_NUM 3
#define SPI_FUNC_NUM 2
#define SPI_IOMUX_PIN_NUM_HD 27
@@ -37,4 +37,9 @@
//HSPI and VSPI have no iomux pins
#endif
#define SOC_SPI_MAXIMUM_BUFFER_SIZE 72
//#define SOC_SPI_SUPPORT_AS_CS //don't support to toggle the CS while the clock toggles
#define SOC_SPI_SUPPORT_DDRCLK 1
#define SOC_SPI_SLAVE_SUPPORT_SEG_TRANS 1
#define SOC_SPI_SUPPORT_CD_SIG 1

View File

@@ -493,7 +493,7 @@ extern "C" {
#define SPI_SLAVE_CS_POL_V 0x1
#define SPI_SLAVE_CS_POL_S 23
/* SPI_MASTER_CS_POL : R/W ;bitpos:[8:6] ;default: 3'b0 ; */
/*description: In the master mode the bits are the polarity of spi cs line
/*description: In the master mode the bits are the polarity of spi cs line
the value is equivalent to spi_cs ^ spi_master_cs_pol.*/
#define SPI_MASTER_CS_POL 0x00000007
#define SPI_MASTER_CS_POL_M ((SPI_MASTER_CS_POL_V)<<(SPI_MASTER_CS_POL_S))
@@ -543,12 +543,36 @@ extern "C" {
#define SPI_TRANS_CNT_M ((SPI_TRANS_CNT_V)<<(SPI_TRANS_CNT_S))
#define SPI_TRANS_CNT_V 0xF
#define SPI_TRANS_CNT_S 23
/* SPI_INT_EN : R/W ;bitpos:[9:5] ;default: 5'b1_1111 ; */
/*description: Interrupt enable bits for the below 5 sources*/
#define SPI_INT_EN 0x0000001F
#define SPI_INT_EN_M ((SPI_INT_EN_V)<<(SPI_INT_EN_S))
#define SPI_INT_EN_V 0x1F
#define SPI_INT_EN_S 5
/* SPI_INT_TRANS_DONE_EN : R/W ;bitpos:[9] ;default: 1'b1 ; */
/*description: spi_trans_done Interrupt enable. 1: enable 0: disable*/
#define SPI_INT_TRANS_DONE_EN (BIT(9))
#define SPI_INT_TRANS_DONE_EN_M (BIT(9))
#define SPI_INT_TRANS_DONE_EN_V 0x1
#define SPI_INT_TRANS_DONE_EN_S 9
/* SPI_INT_WR_DMA_DONE_EN : R/W ;bitpos:[8] ;default: 1'b0 ; */
/*description: spi_slv_wr_dma Interrupt enable. 1: enable 0: disable*/
#define SPI_INT_WR_DMA_DONE_EN (BIT(8))
#define SPI_INT_WR_DMA_DONE_EN_M (BIT(8))
#define SPI_INT_WR_DMA_DONE_EN_V 0x1
#define SPI_INT_WR_DMA_DONE_EN_S 8
/* SPI_INT_RD_DMA_DONE_EN : R/W ;bitpos:[7] ;default: 1'b0 ; */
/*description: spi_slv_rd_dma Interrupt enable. 1: enable 0: disable*/
#define SPI_INT_RD_DMA_DONE_EN (BIT(7))
#define SPI_INT_RD_DMA_DONE_EN_M (BIT(7))
#define SPI_INT_RD_DMA_DONE_EN_V 0x1
#define SPI_INT_RD_DMA_DONE_EN_S 7
/* SPI_INT_WR_BUF_DONE_EN : R/W ;bitpos:[6] ;default: 1'b0 ; */
/*description: spi_slv_wr_buf Interrupt enable. 1: enable 0: disable*/
#define SPI_INT_WR_BUF_DONE_EN (BIT(6))
#define SPI_INT_WR_BUF_DONE_EN_M (BIT(6))
#define SPI_INT_WR_BUF_DONE_EN_V 0x1
#define SPI_INT_WR_BUF_DONE_EN_S 6
/* SPI_INT_RD_BUF_DONE_EN : R/W ;bitpos:[5] ;default: 1'b0 ; */
/*description: spi_slv_rd_buf Interrupt enable. 1: enable 0: disable*/
#define SPI_INT_RD_BUF_DONE_EN (BIT(5))
#define SPI_INT_RD_BUF_DONE_EN_M (BIT(5))
#define SPI_INT_RD_BUF_DONE_EN_V 0x1
#define SPI_INT_RD_BUF_DONE_EN_S 5
/* SPI_TRANS_DONE : R/W ;bitpos:[4] ;default: 1'b0 ; */
/*description: The interrupt raw bit for the completion of any operation in
both the master mode and the slave mode.*/
@@ -693,6 +717,13 @@ extern "C" {
#define SPI_INT_HOLD_ENA_S 0
#define SPI_DMA_CONF_REG(i) (REG_SPI_BASE(i) + 0x058)
/* SPI_CONTINUE_POP_DATA_CLR : R/W ;bitpos:[17] ;default: 1'b0 ; */
/*description: Disable spi slave dma to pop data continuously in next transmission
in dma half duplex slave mode. 1: disable continue transmit. 0: enable continue transmit.*/
#define SPI_CONTINUE_POP_DATA_CLR (BIT(17))
#define SPI_CONTINUE_POP_DATA_CLR_M (BIT(17))
#define SPI_CONTINUE_POP_DATA_CLR_V 0x1
#define SPI_CONTINUE_POP_DATA_CLR_S 17
/* SPI_DMA_CONTINUE : R/W ;bitpos:[16] ;default: 1'b0 ; */
/*description: spi dma continue tx/rx data.*/
#define SPI_DMA_CONTINUE (BIT(16))
@@ -1628,7 +1659,7 @@ extern "C" {
#define SPI_SOP_ITL_S 0
#define SPI_DATE_REG(i) (REG_SPI_BASE(i) + 0x3FC)
/* SPI_DATE : RW ;bitpos:[27:0] ;default: 32'h1809190 ; */
/* SPI_DATE : RW ;bitpos:[27:0] ;default: 32'h1810100 ; */
/*description: SPI register version.*/
#define SPI_DATE 0x0FFFFFFF
#define SPI_DATE_M ((SPI_DATE_V)<<(SPI_DATE_S))

View File

@@ -173,20 +173,20 @@ typedef volatile struct {
} misc;
union {
struct {
uint32_t reserved0: 4; /*reserved*/
uint32_t trans_done: 1; /*The interrupt raw bit for the completion of any operation in both the master mode and the slave mode.*/
uint32_t rd_buf_inten: 1; /*The interrupt enable bit for the completion of read-buffer operation in the slave mode.*/
uint32_t wr_buf_inten: 1; /*The interrupt enable bit for the completion of write-buffer operation in the slave mode.*/
uint32_t rd_dma_inten: 1; /*The interrupt enable bit for the completion of read-status operation in the slave mode.*/
uint32_t wr_dma_inten: 1; /*The interrupt enable bit for the completion of write-status operation in the slave mode.*/
uint32_t trans_inten: 1; /*The interrupt enable bit for the completion of any operation in both the master mode and the slave mode.*/
uint32_t reserved10:13; /*reserved*/
uint32_t trans_cnt: 4; /*The operations counter in both the master mode and the slave mode.*/
uint32_t reserved27: 1; /*reserved*/
uint32_t reserved28: 1; /*reserved*/
uint32_t reserved29: 1; /*reserved*/
uint32_t slave_mode: 1; /*Set SPI work mode. 1: slave mode 0: master mode.*/
uint32_t sync_reset: 1; /*Software reset enable reset the spi clock line cs line and data lines.*/
uint32_t reserved0: 4; /*reserved*/
uint32_t trans_done: 1; /*The interrupt raw bit for the completion of any operation in both the master mode and the slave mode.*/
uint32_t int_rd_buf_done_en: 1; /*spi_slv_rd_buf Interrupt enable. 1: enable 0: disable*/
uint32_t int_wr_buf_done_en: 1; /*spi_slv_wr_buf Interrupt enable. 1: enable 0: disable*/
uint32_t int_rd_dma_done_en: 1; /*spi_slv_rd_dma Interrupt enable. 1: enable 0: disable*/
uint32_t int_wr_dma_done_en: 1; /*spi_slv_wr_dma Interrupt enable. 1: enable 0: disable*/
uint32_t int_trans_done_en: 1; /*spi_trans_done Interrupt enable. 1: enable 0: disable*/
uint32_t reserved10: 13; /*reserved*/
uint32_t trans_cnt: 4; /*The operations counter in both the master mode and the slave mode.*/
uint32_t reserved27: 1; /*reserved*/
uint32_t reserved28: 1; /*reserved*/
uint32_t reserved29: 1; /*reserved*/
uint32_t slave_mode: 1; /*Set SPI work mode. 1: slave mode 0: master mode.*/
uint32_t sync_reset: 1; /*Software reset enable reset the spi clock line cs line and data lines.*/
};
uint32_t val;
} slave;
@@ -254,23 +254,28 @@ typedef volatile struct {
} hold;
union {
struct {
uint32_t reserved0: 2; /*reserved*/
uint32_t in_rst: 1; /*The bit is used to reset in dma fsm and in data fifo pointer.*/
uint32_t out_rst: 1; /*The bit is used to reset out dma fsm and out data fifo pointer.*/
uint32_t ahbm_fifo_rst: 1; /*Reset spi dma ahb master fifo pointer.*/
uint32_t ahbm_rst: 1; /*Reset spi dma ahb master.*/
uint32_t in_loop_test: 1; /*Set bit to test in link.*/
uint32_t out_loop_test: 1; /*Set bit to test out link.*/
uint32_t out_auto_wrback: 1; /*when the bit is set DMA continue to use the next inlink node when the length of inlink is 0.*/
uint32_t out_eof_mode: 1; /*out eof flag generation mode . 1: when dma pop all data from fifo 0:when ahb push all data to fifo.*/
uint32_t outdscr_burst_en: 1; /*read descriptor use burst mode when read data for memory.*/
uint32_t indscr_burst_en: 1; /*read descriptor use burst mode when write data to memory.*/
uint32_t out_data_burst_en: 1; /*spi dma read data from memory in burst mode.*/
uint32_t mem_trans_en: 1;
uint32_t dma_rx_stop: 1; /*spi dma read data stop when in continue tx/rx mode.*/
uint32_t dma_tx_stop: 1; /*spi dma write data stop when in continue tx/rx mode.*/
uint32_t dma_continue: 1; /*spi dma continue tx/rx data.*/
uint32_t reserved17: 15; /*reserved*/
uint32_t reserved0: 2; /*reserved*/
uint32_t in_rst: 1; /*The bit is used to reset in dma fsm and in data fifo pointer.*/
uint32_t out_rst: 1; /*The bit is used to reset out dma fsm and out data fifo pointer.*/
uint32_t ahbm_fifo_rst: 1; /*Reset spi dma ahb master fifo pointer.*/
uint32_t ahbm_rst: 1; /*Reset spi dma ahb master.*/
uint32_t in_loop_test: 1; /*Set bit to test in link.*/
uint32_t out_loop_test: 1; /*Set bit to test out link.*/
uint32_t out_auto_wrback: 1; /*when the bit is set DMA continue to use the next inlink node when the length of inlink is 0.*/
uint32_t out_eof_mode: 1; /*out eof flag generation mode . 1: when dma pop all data from fifo 0:when ahb push all data to fifo.*/
uint32_t outdscr_burst_en: 1; /*read descriptor use burst mode when read data for memory.*/
uint32_t indscr_burst_en: 1; /*read descriptor use burst mode when write data to memory.*/
uint32_t out_data_burst_en: 1; /*spi dma read data from memory in burst mode.*/
uint32_t mem_trans_en: 1;
uint32_t dma_rx_stop: 1; /*spi dma read data stop when in continue tx/rx mode.*/
uint32_t dma_tx_stop: 1; /*spi dma write data stop when in continue tx/rx mode.*/
uint32_t dma_continue: 1; /*spi dma continue tx/rx data.*/
uint32_t continue_pop_data_clr: 1; /*Disable spi slave dma to pop data continuously in next transmission in dma half duplex slave mode. 1: disable continue transmit. 0: enable continue transmit.*/
uint32_t slv_rx_seg_trans_en: 1; /*enable DMA segment transfer in slave mode*/
uint32_t reserved19: 3; /*reserved*/
uint32_t infifo_full_clr: 1;
uint32_t outfifo_empty_clr: 1;
uint32_t reserved24: 8;
};
uint32_t val;
} dma_conf;

View File

@@ -1,3 +1,17 @@
// Copyright 2015-2019 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
#include "soc/spi_caps.h"
@@ -10,9 +24,20 @@ typedef enum {
SPI1_HOST=0, ///< SPI1
SPI2_HOST=1, ///< SPI2
SPI3_HOST=2, ///< SPI3
#if SOC_SPI_PERIPH_NUM > 3
SPI4_HOST=3, ///< SPI4
#endif
} spi_host_device_t;
//alias for different chips
#ifdef CONFIG_IDF_TARGET_ESP32
#define SPI_HOST SPI1_HOST
#define HSPI_HOST SPI2_HOST
#define VSPI_HOST SPI3_HOST
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#define SPI_HOST SPI1_HOST
#define FSPI_HOST SPI2_HOST
#define HSPI_HOST SPI3_HOST
#define VSPI_HOST SPI4_HOST
#endif

View File

@@ -14,7 +14,13 @@
#pragma once
#include <stdbool.h>
#include "sdkconfig.h"
#if CONFIG_IDF_TARGET_ESP32
#include "esp32/rom/lldesc.h"
#elif CONFIG_IDF_TARGET_ESP32S2BETA
#include "esp32s2beta/rom/lldesc.h"
#endif
//the size field has 12 bits, but 0 not for 4096.
//to avoid possible problem when the size is not word-aligned, we only use 4096-4 per desc.

View File

@@ -59,6 +59,7 @@ esp_err_t spi_hal_get_clock_conf(const spi_hal_context_t *hal, int speed_hz, int
//But these don't work for full-duplex connections.
spi_hal_cal_timing(eff_clk_n, use_gpio, input_delay_ns, &temp_conf.timing_dummy, &temp_conf.timing_miso_delay);
#ifdef CONFIG_IDF_TARGET_ESP32
const int freq_limit = spi_hal_get_freq_limit(use_gpio, input_delay_ns);
SPI_HAL_CHECK(hal->half_duplex || temp_conf.timing_dummy == 0 || hal->no_compensate,
@@ -67,6 +68,7 @@ Try to use IOMUX pins to increase the frequency limit, or use the half duplex mo
Please note the SPI master can only work at divisors of 80MHz, and the driver always tries to find the closest frequency to your configuration.\n\
Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
ESP_ERR_NOT_SUPPORTED, freq_limit / 1000. / 1000 );
#endif
if (timing_conf) {
*timing_conf = temp_conf;

View File

@@ -12,8 +12,8 @@ void spi_slave_hal_init(spi_slave_hal_context_t *hal, int host_id)
//Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
//disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
//any transactions that are queued.
spi_ll_enable_int(hal->hw);
spi_ll_set_int_stat(hal->hw);
spi_ll_slave_set_int_type(hal->hw, SPI_LL_INT_TYPE_NORMAL);
}
void spi_slave_hal_setup_device(const spi_slave_hal_context_t *hal)

View File

@@ -14,6 +14,7 @@ void spi_slave_hal_user_start(const spi_slave_hal_context_t *hal)
void spi_slave_hal_prepare_data(const spi_slave_hal_context_t *hal)
{
spi_ll_slave_reset(hal->hw);
if (hal->use_dma) {
spi_ll_reset_dma(hal->hw);
@@ -32,7 +33,6 @@ void spi_slave_hal_prepare_data(const spi_slave_hal_context_t *hal)
spi_ll_write_buffer(hal->hw, hal->tx_buffer, hal->bitlen);
}
}
spi_ll_slave_reset(hal->hw);
spi_ll_slave_set_rx_bitlen(hal->hw, hal->bitlen);
spi_ll_slave_set_tx_bitlen(hal->hw, hal->bitlen);
@@ -53,6 +53,7 @@ void spi_slave_hal_store_result(spi_slave_hal_context_t *hal)
//Copy result out
spi_ll_read_buffer(hal->hw, hal->rx_buffer, hal->bitlen);
}
spi_ll_slave_set_int_type(hal->hw, SPI_LL_INT_TYPE_NORMAL);
}
uint32_t spi_slave_hal_get_rcv_bitlen(spi_slave_hal_context_t *hal)