mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-01 03:34:32 +02:00
spi: support new chip esp32s2beta
This commit is contained in:
@@ -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
|
||||
|
@@ -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
|
||||
}
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
||||
|
874
components/soc/esp32s2beta/include/hal/spi_ll.h
Normal file
874
components/soc/esp32s2beta/include/hal/spi_ll.h
Normal 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, ®_val);
|
||||
spi_ll_master_set_clock_by_reg(hw, ®_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
|
@@ -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 */
|
||||
|
@@ -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
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
|
@@ -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)
|
||||
|
@@ -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)
|
||||
|
Reference in New Issue
Block a user