mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
feat(spi_master): fine tune the timing of SPI
This commit is contained in:
parent
d441dd32ea
commit
5cf7d3768d
@ -1,4 +1,4 @@
|
||||
// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2010-2018 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.
|
||||
@ -22,6 +22,19 @@
|
||||
|
||||
#include "driver/spi_common.h"
|
||||
|
||||
/** SPI master clock is divided by 80MHz apb clock. Below defines are example frequencies, and are accurate. Be free to specify a random frequency, it will be rounded to closest frequency (to macros below if above 8MHz).
|
||||
* 8MHz
|
||||
*/
|
||||
#define SPI_MASTER_FREQ_8M (APB_CLK_FREQ/10)
|
||||
#define SPI_MASTER_FREQ_9M (APB_CLK_FREQ/9) ///< 8.89MHz
|
||||
#define SPI_MASTER_FREQ_10M (APB_CLK_FREQ/8) ///< 10MHz
|
||||
#define SPI_MASTER_FREQ_11M (APB_CLK_FREQ/7) ///< 11.43MHz
|
||||
#define SPI_MASTER_FREQ_13M (APB_CLK_FREQ/6) ///< 13.33MHz
|
||||
#define SPI_MASTER_FREQ_16M (APB_CLK_FREQ/5) ///< 16MHz
|
||||
#define SPI_MASTER_FREQ_20M (APB_CLK_FREQ/4) ///< 20MHz
|
||||
#define SPI_MASTER_FREQ_26M (APB_CLK_FREQ/3) ///< 26.67MHz
|
||||
#define SPI_MASTER_FREQ_40M (APB_CLK_FREQ/2) ///< 40MHz
|
||||
#define SPI_MASTER_FREQ_80M (APB_CLK_FREQ/1) ///< 80MHz
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@ -57,7 +70,12 @@ typedef struct {
|
||||
uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.
|
||||
uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.
|
||||
uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)
|
||||
int clock_speed_hz; ///< Clock speed, in Hz
|
||||
int clock_speed_hz; ///< Clock speed, divisors of 80MHz, in Hz. See ``SPI_MASTER_FREQ_*``.
|
||||
int input_delay_ns; /**< Maximum data valid time of slave. The time required between SCLK and MISO
|
||||
valid, including the possible clock delay from slave to master. The driver uses this value to give an extra
|
||||
delay before the MISO is ready on the line. Leave at 0 unless you know you need a delay. For better timing
|
||||
performance at high frequency (over 8MHz), it's suggest to have the right value.
|
||||
*/
|
||||
int spics_io_num; ///< CS GPIO pin for this device, or -1 if not used
|
||||
uint32_t flags; ///< Bitwise OR of SPI_DEVICE_* flags
|
||||
int queue_size; ///< Transaction queue size. This sets how many transactions can be 'in the air' (queued using spi_device_queue_trans but not yet finished using spi_device_get_trans_result) at the same time
|
||||
@ -253,6 +271,30 @@ esp_err_t spi_device_transmit(spi_device_handle_t handle, spi_transaction_t *tra
|
||||
*/
|
||||
int spi_cal_clock(int fapb, int hz, int duty_cycle, uint32_t* reg_o);
|
||||
|
||||
/**
|
||||
* @brief Calculate the timing settings of specified frequency and settings.
|
||||
*
|
||||
* @param gpio_is_used True if using GPIO matrix, or False if native pins are used.
|
||||
* @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
|
||||
* @param eff_clk Effective clock frequency (in Hz) from spi_cal_clock.
|
||||
* @param dummy_o Address of dummy bits used output. Set to NULL if not needed.
|
||||
* @param cycles_remain_o Address of cycles remaining (after dummy bits are used) output.
|
||||
* - -1 If too many cycles remaining, suggest to compensate half a clock.
|
||||
* - 0 If no remaining cycles or dummy bits are not used.
|
||||
* - positive value: cycles suggest to compensate.
|
||||
* @note If **dummy_o* is not zero, it means dummy bits should be applied in half duplex mode, and full duplex mode may not work.
|
||||
*/
|
||||
void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o);
|
||||
|
||||
/**
|
||||
* @brief Get the frequency limit of current configurations.
|
||||
* SPI master working at this limit is OK, while above the limit, full duplex mode and DMA will not work,
|
||||
* and dummy bits will be aplied in the half duplex mode.
|
||||
* @param gpio_is_used True if using GPIO matrix, or False if native pins are used.
|
||||
* @param input_delay_ns Input delay from SCLK launch edge to MISO data valid.
|
||||
* @return Frequency limit of current configurations.
|
||||
*/
|
||||
int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -96,6 +96,7 @@ typedef struct {
|
||||
spi_clock_reg_t reg;
|
||||
int eff_clk;
|
||||
int dummy_num;
|
||||
int miso_delay;
|
||||
} clock_config_t;
|
||||
|
||||
struct spi_device_t {
|
||||
@ -110,9 +111,9 @@ static spi_host_t *spihost[3];
|
||||
|
||||
|
||||
static const char *SPI_TAG = "spi_master";
|
||||
#define SPI_CHECK(a, str, ret_val) \
|
||||
#define SPI_CHECK(a, str, ret_val, ...) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
ESP_LOGE(SPI_TAG,"%s(%d): "str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
@ -256,14 +257,38 @@ esp_err_t spi_bus_free(spi_host_device_t host)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static inline uint32_t spi_dummy_limit(bool gpio_is_used)
|
||||
void spi_get_timing(bool gpio_is_used, int input_delay_ns, int eff_clk, int* dummy_o, int* cycles_remain_o)
|
||||
{
|
||||
const int apbclk=APB_CLK_FREQ;
|
||||
if (!gpio_is_used) {
|
||||
return apbclk; //dummy bit workaround is not used when native pins are used
|
||||
const int apbclk_kHz = APB_CLK_FREQ/1000;
|
||||
const int apbclk_n = APB_CLK_FREQ/eff_clk;
|
||||
const int gpio_delay_ns=(gpio_is_used?25:0);
|
||||
|
||||
//calculate how many apb clocks a period has, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
|
||||
int apb_period_n = (1 + input_delay_ns + gpio_delay_ns)*apbclk_kHz/1000/1000;
|
||||
int dummy_required = apb_period_n/apbclk_n;
|
||||
|
||||
int miso_delay = 0;
|
||||
if (dummy_required > 0) {
|
||||
//due to the clock delay between master and slave, there's a range in which data is random
|
||||
//give MISO a delay if needed to make sure we sample at the time MISO is stable
|
||||
miso_delay = (dummy_required+1)*apbclk_n-apb_period_n-1;
|
||||
} else {
|
||||
return apbclk/2; //the dummy bit workaround is used when freq is 40MHz and GPIO matrix is used.
|
||||
//if the dummy is not required, maybe we should also delay half a SPI clock if the data comes too early
|
||||
if (apb_period_n*4 <= apbclk_n) miso_delay = -1;
|
||||
}
|
||||
if (dummy_o!=NULL) *dummy_o = dummy_required;
|
||||
if (cycles_remain_o!=NULL) *cycles_remain_o = miso_delay;
|
||||
ESP_LOGD(SPI_TAG,"eff: %d, limit: %dk(/%d), %d dummy, %d delay", eff_clk/1000, apbclk_kHz/(apb_period_n+1), apb_period_n, dummy_required, miso_delay);
|
||||
}
|
||||
|
||||
int spi_get_freq_limit(bool gpio_is_used, int input_delay_ns)
|
||||
{
|
||||
const int apbclk_kHz = APB_CLK_FREQ/1000;
|
||||
const int gpio_delay_ns=(gpio_is_used?25:0);
|
||||
|
||||
//calculate how many apb clocks a period has, the 1 is to compensate in case ``input_delay_ns`` is rounded off.
|
||||
int apb_period_n = (1 + input_delay_ns + gpio_delay_ns)*apbclk_kHz/1000/1000;
|
||||
return APB_CLK_FREQ/(apb_period_n+1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -276,6 +301,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
|
||||
int apbclk=APB_CLK_FREQ;
|
||||
int eff_clk;
|
||||
int duty_cycle;
|
||||
int dummy_required;
|
||||
int miso_delay;
|
||||
|
||||
spi_clock_reg_t clk_reg;
|
||||
SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE);
|
||||
@ -288,18 +316,23 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
|
||||
SPI_CHECK(freecs!=NO_CS, "no free cs pins for host", ESP_ERR_NOT_FOUND);
|
||||
//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==0 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay incompatible with full-duplex", ESP_ERR_INVALID_ARG);
|
||||
SPI_CHECK(dev_config->cs_ena_pretrans <= 1 || (dev_config->flags & SPI_DEVICE_HALFDUPLEX), "cs pretrans delay > 1 incompatible with full-duplex", ESP_ERR_INVALID_ARG);
|
||||
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);
|
||||
|
||||
//Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
|
||||
duty_cycle = (dev_config->duty_cycle_pos==0? 128: dev_config->duty_cycle_pos);
|
||||
eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, duty_cycle, (uint32_t*)&clk_reg);
|
||||
uint32_t dummy_limit = spi_dummy_limit(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS));
|
||||
SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || (eff_clk/1000/1000) < (dummy_limit/1000/1000) ||
|
||||
int freq_limit = spi_get_freq_limit(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS), dev_config->input_delay_ns);
|
||||
//GPIO matrix can only change data at 80Mhz rate, which only allows 40MHz SPI clock.
|
||||
SPI_CHECK(eff_clk <= 40*1000*1000 || spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS, "80MHz only supported on native pins", ESP_ERR_INVALID_ARG);
|
||||
//Speed >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
|
||||
spi_get_timing(!(spihost[host]->flags&SPICOMMON_BUSFLAG_NATIVE_PINS), dev_config->input_delay_ns, eff_clk, &dummy_required, &miso_delay);
|
||||
SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || dummy_required == 0 ||
|
||||
dev_config->flags & SPI_DEVICE_NO_DUMMY,
|
||||
"When GPIO matrix is used in full-duplex mode at frequency > 26MHz, device cannot read correct data.\n\
|
||||
"When GPIO matrix is used in full-duplex mode at frequency > %.1fMHz, device cannot read correct data.\n\
|
||||
Please note the SPI 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_INVALID_ARG );
|
||||
ESP_ERR_INVALID_ARG, freq_limit/1000./1000 );
|
||||
|
||||
//Allocate memory for device
|
||||
spi_device_t *dev=malloc(sizeof(spi_device_t));
|
||||
@ -319,8 +352,9 @@ Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output dat
|
||||
// TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.
|
||||
dev->clk_cfg= (clock_config_t) {
|
||||
.eff_clk = eff_clk,
|
||||
.dummy_num = (dev->clk_cfg.eff_clk >= dummy_limit? 1: 0),
|
||||
.dummy_num = dummy_required,
|
||||
.reg = clk_reg,
|
||||
.miso_delay = miso_delay,
|
||||
};
|
||||
|
||||
//Set CS pin, CS options
|
||||
@ -338,6 +372,8 @@ Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output dat
|
||||
} else {
|
||||
spihost[host]->hw->pin.master_cs_pol &= (1<<freecs);
|
||||
}
|
||||
spihost[host]->hw->ctrl2.mosi_delay_mode = 0;
|
||||
spihost[host]->hw->ctrl2.mosi_delay_num = 0;
|
||||
*handle=dev;
|
||||
ESP_LOGD(SPI_TAG, "SPI%d: New device added to CS%d, effective clock: %dkHz", host, freecs, dev->clk_cfg.eff_clk/1000);
|
||||
return ESP_OK;
|
||||
@ -508,43 +544,24 @@ static void IRAM_ATTR spi_intr(void *arg)
|
||||
|
||||
//Reconfigure according to device settings, but only if we change CSses.
|
||||
if (i!=host->prev_cs) {
|
||||
const int apbclk=APB_CLK_FREQ;
|
||||
int effclk=dev->clk_cfg.eff_clk;
|
||||
spi_set_clock(host->hw, dev->clk_cfg.reg);
|
||||
//Configure bit order
|
||||
host->hw->ctrl.rd_bit_order=(dev->cfg.flags & SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
|
||||
host->hw->ctrl.wr_bit_order=(dev->cfg.flags & SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
|
||||
|
||||
//Configure polarity
|
||||
//SPI iface needs to be configured for a delay in some cases.
|
||||
int nodelay=0;
|
||||
if ((host->flags&SPICOMMON_BUSFLAG_NATIVE_PINS)!=0) {
|
||||
if (effclk >= apbclk/2) {
|
||||
nodelay=1;
|
||||
}
|
||||
} else {
|
||||
uint32_t delay_limit = apbclk/4;
|
||||
if (effclk >= delay_limit) {
|
||||
nodelay=1;
|
||||
}
|
||||
}
|
||||
|
||||
if (dev->cfg.mode==0) {
|
||||
host->hw->pin.ck_idle_edge=0;
|
||||
host->hw->user.ck_out_edge=0;
|
||||
host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
|
||||
} else if (dev->cfg.mode==1) {
|
||||
host->hw->pin.ck_idle_edge=0;
|
||||
host->hw->user.ck_out_edge=1;
|
||||
host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
|
||||
} else if (dev->cfg.mode==2) {
|
||||
host->hw->pin.ck_idle_edge=1;
|
||||
host->hw->user.ck_out_edge=1;
|
||||
host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
|
||||
} else if (dev->cfg.mode==3) {
|
||||
host->hw->pin.ck_idle_edge=1;
|
||||
host->hw->user.ck_out_edge=0;
|
||||
host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
|
||||
}
|
||||
//Configure misc stuff
|
||||
host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1;
|
||||
@ -552,8 +569,11 @@ static void IRAM_ATTR spi_intr(void *arg)
|
||||
|
||||
host->hw->ctrl2.setup_time=dev->cfg.cs_ena_pretrans-1;
|
||||
host->hw->user.cs_setup=dev->cfg.cs_ena_pretrans?1:0;
|
||||
host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans-1;
|
||||
host->hw->user.cs_hold=(dev->cfg.cs_ena_posttrans)?1:0;
|
||||
//set hold_time to 0 will not actually append delay to CS
|
||||
//set it to 1 since we do need at least one clock of hold time in most cases
|
||||
host->hw->ctrl2.hold_time=dev->cfg.cs_ena_posttrans;
|
||||
if ( host->hw->ctrl2.hold_time == 0 ) host->hw->ctrl2.hold_time = 1;
|
||||
host->hw->user.cs_hold=1;
|
||||
|
||||
//Configure CS pin
|
||||
host->hw->pin.cs0_dis=(i==0)?0:1;
|
||||
@ -614,6 +634,7 @@ static void IRAM_ATTR spi_intr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (trans_buf->buffer_to_send) {
|
||||
if (host->dma_chan == 0) {
|
||||
//Need to copy data to registers manually
|
||||
@ -634,10 +655,31 @@ static void IRAM_ATTR spi_intr(void *arg)
|
||||
}
|
||||
}
|
||||
|
||||
//SPI iface needs to be configured for a delay in some cases.
|
||||
//configure dummy bits
|
||||
host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0;
|
||||
host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1;
|
||||
|
||||
int miso_long_delay = 0;
|
||||
if (dev->clk_cfg.miso_delay<0) {
|
||||
//if the data comes too late, delay half a SPI clock to improve reading
|
||||
miso_long_delay = 1;
|
||||
host->hw->ctrl2.miso_delay_num = 0;
|
||||
} else {
|
||||
//if the data is so fast that dummy_bit is used, delay some apb clocks to meet the timing
|
||||
host->hw->ctrl2.miso_delay_num = (extra_dummy? dev->clk_cfg.miso_delay: 0);
|
||||
}
|
||||
|
||||
if (dev->cfg.mode==0) {
|
||||
host->hw->ctrl2.miso_delay_mode=miso_long_delay?2:0;
|
||||
} else if (dev->cfg.mode==1) {
|
||||
host->hw->ctrl2.miso_delay_mode=miso_long_delay?1:0;
|
||||
} else if (dev->cfg.mode==2) {
|
||||
host->hw->ctrl2.miso_delay_mode=miso_long_delay?1:0;
|
||||
} else if (dev->cfg.mode==3) {
|
||||
host->hw->ctrl2.miso_delay_mode=miso_long_delay?2:0;
|
||||
}
|
||||
|
||||
host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1;
|
||||
if ( dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ) {
|
||||
host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1;
|
||||
|
28
components/soc/esp32/include/driver/spi_pins.h
Normal file
28
components/soc/esp32/include/driver/spi_pins.h
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2015-2018 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.
|
||||
|
||||
#ifndef _DRIVER_SPI_PINS_H_
|
||||
#define _DRIVER_SPI_PINS_H_
|
||||
|
||||
#define HSPI_NATIVE_PIN_NUM_MISO 12
|
||||
#define HSPI_NATIVE_PIN_NUM_MOSI 13
|
||||
#define HSPI_NATIVE_PIN_NUM_CLK 14
|
||||
#define HSPI_NATIVE_PIN_NUM_CS 15
|
||||
|
||||
#define VSPI_NATIVE_PIN_NUM_MISO 19
|
||||
#define VSPI_NATIVE_PIN_NUM_MOSI 23
|
||||
#define VSPI_NATIVE_PIN_NUM_CLK 18
|
||||
#define VSPI_NATIVE_PIN_NUM_CS 5
|
||||
|
||||
#endif
|
17
docs/_static/diagrams/spi_master/miso_timing_waveform.rst
vendored
Normal file
17
docs/_static/diagrams/spi_master/miso_timing_waveform.rst
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
.. this picture is generated by https://wavedrom.com/, using the sphinx plugin sphinxcontrib-wavedrom
|
||||
.. due to plugin issue, we cannot place only the picture information in a standalone file, but have to take .. wavedrom:: inside
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{ signal: [
|
||||
{ name: 'SCLK', wave: 'p...', node: '.ad...' },
|
||||
{ name: 'MISO', wave: 'x3x.', node: '.b...', phase:-1.8 },
|
||||
{ name: 'MISO delayed', wave: 'x3x.', node: '.c.', phase:-2.4 },
|
||||
],
|
||||
edge: [
|
||||
'a|->b input delay',
|
||||
'b|->c gpio delay',
|
||||
'c-|>d setup slack'
|
||||
],
|
||||
config: { hscale: 3 }
|
||||
}
|
19
docs/_static/diagrams/spi_master/miso_timing_waveform_async.rst
vendored
Normal file
19
docs/_static/diagrams/spi_master/miso_timing_waveform_async.rst
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
.. this picture is generated by https://wavedrom.com/, using the sphinx plugin sphinxcontrib-wavedrom
|
||||
.. due to plugin issue, we cannot place only the picture information in a standalone file, but have to take .. wavedrom:: inside
|
||||
|
||||
.. wavedrom::
|
||||
|
||||
{ signal: [
|
||||
{ name: 'SCLK', wave: '0.1....0....1...', node: '..a.........e'},
|
||||
{ name: 'SLV_CLK',wave: 'p..............', node: '..b..', phase: -0.5 },
|
||||
{ name: 'MISO', wave: 'x...3.....x....', node: '....c', phase:-0.5},
|
||||
{ name: 'MISO delayed', wave: 'x.......3.....x.', node: '........d'},
|
||||
],
|
||||
edge: [
|
||||
'a|->b sample delay',
|
||||
'b|->c slave output delay',
|
||||
'c|->d gpio delay',
|
||||
'd-|>e setup slack',
|
||||
'a-|>c input delay'
|
||||
],
|
||||
}
|
33
docs/_static/diagrams/spi_master/spi_master_freq_tv.plt
vendored
Normal file
33
docs/_static/diagrams/spi_master/spi_master_freq_tv.plt
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
# this is a GNUPLOT script generating the figure of spi_master_freq_tv.png
|
||||
|
||||
set xlabel "Input delay (ns)"
|
||||
set xrange [0: 125]
|
||||
set ylabel "Fmax (MHz)"
|
||||
set yrange [0: 81]
|
||||
set xtics 12.5 textcolor rgb "black"
|
||||
set ytics 10 textcolor rgb "black"
|
||||
set border 3 lc rgb "gray" lw 2
|
||||
set grid lt -1 lc rgb "gray" lw 2
|
||||
set samples 10000
|
||||
set terminal png size 700,500
|
||||
set output "plot.png"
|
||||
|
||||
apb = 12.5
|
||||
|
||||
#each line is broken into 10 pieces by the range determined by i
|
||||
f1(i,x) = (x>= i*apb) && (x < (i+1)*apb) ? 80./(i+1) : 1/0
|
||||
|
||||
set style circle radius graph 0.008
|
||||
|
||||
#solid and empty circles are draw by the coordinates given in the csv
|
||||
plot [0:125]\
|
||||
f1(-1, x) lw 3lc rgb "blue" title "IOMUX",\
|
||||
for [i=0:9] f1(i, x) with lines lw 3 lc rgb "blue" notitle,\
|
||||
f1(0, x+25) lw 3 lc rgb "red" title "GPIO",\
|
||||
for [i=2:11] f1(i, x+25) with lines lw 3 lc rgb "red" notitle, \
|
||||
"tv.csv" using 1:2 with circles notitle fill solid fc rgb "blue", \
|
||||
"tv.csv" using 1:4 with circles notitle fc rgb "blue",\
|
||||
"tv.csv" using 1:3 with circles notitle fill solid fc rgb "red" ,\
|
||||
"tv.csv" using 1:5 with circles notitle fc rgb "red"
|
||||
|
||||
|
BIN
docs/_static/diagrams/spi_master/spi_timing.pptx
vendored
Normal file
BIN
docs/_static/diagrams/spi_master/spi_timing.pptx
vendored
Normal file
Binary file not shown.
29
docs/_static/diagrams/spi_master/tv.csv
vendored
Normal file
29
docs/_static/diagrams/spi_master/tv.csv
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
0 80 26.66666667 #DIV/0! #DIV/0!
|
||||
12.5 40 20 80 26.66666667
|
||||
25 26.66666667 16 40 20
|
||||
37.5 20 13.33333333 26.66666667 16
|
||||
50 16 11.42857143 20 13.33333333
|
||||
62.5 13.33333333 10 16 11.42857143
|
||||
75 11.42857143 8.888888889 13.33333333 10
|
||||
87.5 10 8 11.42857143 8.888888889
|
||||
100 8.888888889 7.272727273 10 8
|
||||
112.5 8 6.666666667 8.888888889 7.272727273
|
||||
125 7.272727273 6.153846154 8 6.666666667
|
||||
137.5 6.666666667 5.714285714 7.272727273 6.153846154
|
||||
150 6.153846154 5.333333333 6.666666667 5.714285714
|
||||
162.5 5.714285714 5 6.153846154 5.333333333
|
||||
175 5.333333333 4.705882353 5.714285714 5
|
||||
187.5 5 4.444444444 5.333333333 4.705882353
|
||||
200 4.705882353 4.210526316 5 4.444444444
|
||||
212.5 4.444444444 4 4.705882353 4.210526316
|
||||
225 4.210526316 3.80952381 4.444444444 4
|
||||
237.5 4 3.636363636 4.210526316 3.80952381
|
||||
250 3.80952381 3.47826087 4 3.636363636
|
||||
262.5 3.636363636 3.333333333 3.80952381 3.47826087
|
||||
275 3.47826087 3.2 3.636363636 3.333333333
|
||||
287.5 3.333333333 3.076923077 3.47826087 3.2
|
||||
300 3.2 2.962962963 3.333333333 3.076923077
|
||||
312.5 3.076923077 2.857142857 3.2 2.962962963
|
||||
325 2.962962963 2.75862069 3.076923077 2.857142857
|
||||
337.5 2.857142857 2.666666667 2.962962963 2.75862069
|
||||
350 2.75862069 2.580645161 2.857142857 2.666666667
|
|
BIN
docs/_static/miso_timing_waveform.png
vendored
Normal file
BIN
docs/_static/miso_timing_waveform.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
BIN
docs/_static/miso_timing_waveform_async.png
vendored
Normal file
BIN
docs/_static/miso_timing_waveform_async.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
BIN
docs/_static/spi_master_freq_tv.png
vendored
Normal file
BIN
docs/_static/spi_master_freq_tv.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
BIN
docs/_static/spi_miso.png
vendored
Normal file
BIN
docs/_static/spi_miso.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@ -73,18 +73,25 @@ Something similar is true for the read and write phase: not every transaction ne
|
||||
as well as data to be read. When ``rx_buffer`` is NULL (and SPI_USE_RXDATA) is not set) the read phase
|
||||
is skipped. When ``tx_buffer`` is NULL (and SPI_USE_TXDATA) is not set) the write phase is skipped.
|
||||
|
||||
GPIO matrix and native pins
|
||||
GPIO matrix and IOMUX
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most peripheral pins in ESP32 can directly connect to a GPIO, which is called *native pin*. When the peripherals are
|
||||
required to work with other pins than the native pins, ESP32 use a *GPIO matrix* to realize this. If one of the pins is
|
||||
not native, the driver automatically routes all the signals to the GPIO matrix, which works under 80MHz. The signals are
|
||||
sampled and sent to peripherals or the GPIOs.
|
||||
Most peripheral signals in ESP32 can connect directly to a specific GPIO, which is called its IOMUX pin. When a
|
||||
peripheral signal is routed to a pin other than its IOMUX pin, ESP32 uses the less direct GPIO matrix to make this
|
||||
connection.
|
||||
|
||||
When the GPIO matrix is used, signals cannot propogate to the peripherals over 40MHz, and the setup time of MISO is very
|
||||
likely violated. Hence the clock frequency limitation is a little lower than the case without GPIO matrix.
|
||||
If the driver is configured with all SPI signals set to their specific IOMUX pins (or left unconnected), it will bypass
|
||||
the GPIO matrix. If any SPI signal is configured to a pin other than its IOMUx pin, the driver will automatically route
|
||||
all the signals via the GPIO Matrix. The GPIO matrix samples all signals at 80MHz and sends them between the GPIO and
|
||||
the peripheral.
|
||||
|
||||
Native pins for SPI controllers are as below:
|
||||
When the GPIO matrix is used, signals faster than 40MHz cannot propagate and the setup time of MISO is more easily
|
||||
violated, since the input delay of MISO signal is increased. The maximum clock frequency with GPIO Matrix is 40MHz
|
||||
or less, whereas using all IOMUX pins allows 80MHz.
|
||||
|
||||
.. note:: More details about influence of input delay on the maximum clock frequency, see :ref:`timing_considerations` below.
|
||||
|
||||
IOMUX pins for SPI controllers are as below:
|
||||
|
||||
+----------+------+------+
|
||||
| Pin Name | HSPI | VSPI |
|
||||
@ -191,13 +198,15 @@ speed a lot if small transactions are used.
|
||||
|
||||
Typical transaction interval with one byte data is as below:
|
||||
|
||||
+-----------------------+---------+
|
||||
| Transaction Time (us) | Typical |
|
||||
+=======================+=========+
|
||||
| DMA | 24 |
|
||||
+-----------------------+---------+
|
||||
| No DMA | 22 |
|
||||
+-----------------------+---------+
|
||||
+--------+------------------+
|
||||
| | Transaction Time |
|
||||
+========+==================+
|
||||
| | Typical (us) |
|
||||
+--------+------------------+
|
||||
| DMA | 24 |
|
||||
+--------+------------------+
|
||||
| No DMA | 22 |
|
||||
+--------+------------------+
|
||||
|
||||
2. SPI clock frequency: Each byte transferred takes 8 times of the clock period *8/fspi*. If the clock frequency is
|
||||
too high, some functions may be limited to use. See :ref:`timing_considerations`.
|
||||
@ -209,7 +218,7 @@ clock speed:
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
| Frequency | Transaction Interval | Transaction Length | Total Time | Total Speed |
|
||||
| | | | | |
|
||||
| [MHz] | [us] | [bytes] | [us] | [kBps] |
|
||||
| (MHz) | (us) | (bytes) | (us) | (kBps) |
|
||||
+===========+======================+====================+============+=============+
|
||||
| 8 | 25 | 1 | 26 | 38.5 |
|
||||
+-----------+----------------------+--------------------+------------+-------------+
|
||||
@ -229,9 +238,24 @@ into one transaction if possible to get higher transfer speed.
|
||||
|
||||
Timing considerations
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
Due to the input delay of MISO pin, ESP32 SPI master cannot read data at very high speed. The frequency allowed is
|
||||
rather low when the GPIO matrix is used. Currently only frequency not greater than 8.8MHz is fully supported. When the
|
||||
frequency is higher, you have to use the native pins or the *dummy bit workaround*.
|
||||
|
||||
As shown in the figure below, there is a delay on the MISO signal after SCLK
|
||||
launch edge and before it's latched by the internal register. As a result,
|
||||
the MISO pin setup time is the limiting factor for SPI clock speed. When the
|
||||
delay is too large, setup slack is < 0 and the setup timing requirement is
|
||||
violated, leads to the failure of reading correctly.
|
||||
|
||||
.. image:: /../_static/spi_miso.png
|
||||
|
||||
""" wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
|
||||
.. include:: /../_static/miso_timing_waveform.png
|
||||
|
||||
The maximum frequency allowed is related to the *input delay* (maximum valid
|
||||
time after SCLK on the MISO bus), as well as the usage of GPIO matrix. The
|
||||
maximum frequency allowed is reduced to about 33~77% (related to existing
|
||||
*input delay*) when the GPIO matrix is used. To work at higher frequency, you
|
||||
have to use the IOMUX pins or the *dummy bit workaround*. You can get the
|
||||
maximum reading frequency of the master by ``spi_get_freq_limit``.
|
||||
|
||||
.. _dummy_bit_workaround:
|
||||
|
||||
@ -240,29 +264,97 @@ actually begins. The slave still sees the dummy clocks and gives out data, but t
|
||||
phase. This compensates the lack of setup time of MISO required by the host, allowing the host reading at higher
|
||||
frequency.
|
||||
|
||||
The maximum frequency (in MHz) host can read (or read and write) under different conditions is as below:
|
||||
In the ideal case (the slave is so fast that the input delay is shorter than an apb clock, 12.5ns), the maximum
|
||||
frequency host can read (or read and write) under different conditions is as below:
|
||||
|
||||
+-------------+-------------+-----------+-----------------------------+
|
||||
| Frequency Limit | Dummy Bits| Comments |
|
||||
+-------------+-------------+ Used + +
|
||||
| GPIO matrix | Native pins | By Driver | |
|
||||
+=============+=============+===========+=============================+
|
||||
| 8.8 | N.M. | 0 | |
|
||||
+-------------+-------------+-----------+-----------------------------+
|
||||
| N.M. | N.M. | 1 | Half Duplex, no DMA allowed |
|
||||
+-------------+-------------+-----------+ +
|
||||
| N.M. | N.M. | 2 | |
|
||||
+-------------+-------------+-----------+-----------------------------+
|
||||
|
||||
N.M.: Not Measured Yet.
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
| Frequency Limit (MHz) | Dummy Bits | Comments |
|
||||
+-------------+-------------+ Used + +
|
||||
| GPIO matrix | IOMUX pins | By Driver | |
|
||||
+=============+=============+============+=============================+
|
||||
| 26.6 | 80 | No | |
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
| 40 | -- | Yes | Half Duplex, no DMA allowed |
|
||||
+-------------+-------------+------------+-----------------------------+
|
||||
|
||||
And if the host only writes, the *dummy bit workaround* is not used and the frequency limit is as below:
|
||||
|
||||
+-------------+----------------------+
|
||||
| GPIO matrix | Native pins |
|
||||
+=============+======================+
|
||||
| 40 | 80 |
|
||||
+-------------+----------------------+
|
||||
+-------------------+------------------+
|
||||
| GPIO matrix (MHz) | IOMUX pins (MHz) |
|
||||
+===================+==================+
|
||||
| 40 | 80 |
|
||||
+-------------------+------------------+
|
||||
|
||||
The spi master driver can work even if the *input delay* in the ``spi_device_interface_config_t`` is set to 0.
|
||||
However, setting a accurate value helps to: (1) calculate the frequency limit in full duplex mode, and (2) compensate
|
||||
the timing correctly by dummy bits in half duplex mode. You may find the maximum data valid time after the launch edge
|
||||
of SPI clocks in the AC characteristics chapter of the device specifications, or measure the time on a oscilloscope or
|
||||
logic analyzer.
|
||||
|
||||
""" wavedrom don't support rendering pdflatex till now(1.3.1), so we use the png here
|
||||
.. include:: /../_static/miso_timing_waveform_async.png
|
||||
|
||||
As shown in the figure above, the input delay is usually:
|
||||
|
||||
*[input delay] = [sample delay] + [slave output delay]*
|
||||
|
||||
1. The sample delay is the maximum random delay due to the
|
||||
asynchronization of SCLK and peripheral clock of the slave. It's usually
|
||||
1 slave peripheral clock if the clock is asynchronize with SCLK, or 0 if
|
||||
the slave just use the SCLK to latch the SCLK and launch MISO data. e.g.
|
||||
for ESP32 slaves, the delay is 12.5ns (1 apb clock), while it is reduced
|
||||
to 0 if the slave is in the same chip as the master.
|
||||
|
||||
2. The slave output delay is the time for the MOSI to be stable after the
|
||||
launch edge. e.g. for ESP32 slaves, the output delay is 37.5ns (3 apb
|
||||
clocks) when IOMUX pins in the slave is used, or 62.5ns (5 apb clocks) if
|
||||
through the GPIO matrix.
|
||||
|
||||
Some typical delays are shown in the following table:
|
||||
|
||||
+--------------------+------------------+
|
||||
| Device | Input delay (ns) |
|
||||
+====================+==================+
|
||||
| Ideal device | 0 |
|
||||
+--------------------+------------------+
|
||||
| ESP32 slave IOMUX* | 50 |
|
||||
+--------------------+------------------+
|
||||
| ESP32 slave GPIO* | 75 |
|
||||
+--------------------+------------------+
|
||||
| ESP32 slave is on an independent |
|
||||
| chip, 12.5ns sample delay included. |
|
||||
+---------------------------------------+
|
||||
|
||||
The MISO path delay(tv), consists of slave *input delay* and master *GPIO matrix delay*, finally determines the
|
||||
frequency limit, above which the full duplex mode will not work, or dummy bits are used in the half duplex mode. The
|
||||
frequency limit is:
|
||||
|
||||
*Freq limit[MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
|
||||
|
||||
The figure below shows the relations of frequency limit against the input delay. 2 extra apb clocks should be counted
|
||||
into the MISO delay if the GPIO matrix in the master is used.
|
||||
|
||||
.. image:: /../_static/spi_master_freq_tv.png
|
||||
|
||||
Corresponding frequency limit for different devices with different *input delay* are shown in the following
|
||||
table:
|
||||
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
| Master | Input delay (ns) | MISO path delay (ns) | Freq. limit (MHz) |
|
||||
+========+==================+======================+===================+
|
||||
| IOMUX | 0 | 0 | 80 |
|
||||
+ (0ns) +------------------+----------------------+-------------------+
|
||||
| | 50 | 50 | 16 |
|
||||
+ +------------------+----------------------+-------------------+
|
||||
| | 75 | 75 | 11.43 |
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
| GPIO | 0 | 25 | 26.67 |
|
||||
+ (25ns) +------------------+----------------------+-------------------+
|
||||
| | 50 | 75 | 11.43 |
|
||||
+ +------------------+----------------------+-------------------+
|
||||
| | 75 | 100 | 8.89 |
|
||||
+--------+------------------+----------------------+-------------------+
|
||||
|
||||
|
||||
Thread Safety
|
||||
-------------
|
||||
|
Loading…
x
Reference in New Issue
Block a user