Skip to content

Commit 9d69d11

Browse files
committed
Merge branch 'feature/spi_dummy_bit' into 'master'
feat(spi_master): allow output high speed data when dummy bits are not used. See merge request idf/esp-idf!2113
2 parents 9ea3856 + 7563510 commit 9d69d11

File tree

2 files changed

+49
-17
lines changed

2 files changed

+49
-17
lines changed

components/driver/include/driver/spi_master.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ extern "C"
3535
#define SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative
3636
#define SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously
3737
#define SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active
38+
/** There are timing issue when reading at high frequency (the frequency is related to whether native pins are used, valid time after slave sees the clock).
39+
* In half-duplex mode, the driver automatically inserts dummy bits before reading phase to fix the timing issue. Set this flag to disable this feature.
40+
* However in full-duplex mode, dummy bits are not allowed to use and no way to prevent reading data from being corrupted.
41+
* Set this flag to confirm that you're going to work with output only, or read without dummy bits at your own risk.
42+
*/
43+
#define SPI_DEVICE_NO_DUMMY (1<<6)
3844

3945

4046
typedef struct spi_transaction_t spi_transaction_t;

components/driver/spi_master.c

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ typedef struct {
9595
typedef struct {
9696
spi_clock_reg_t reg;
9797
int eff_clk;
98+
int dummy_num;
9899
} clock_config_t;
99100

100101
struct spi_device_t {
@@ -238,6 +239,16 @@ esp_err_t spi_bus_free(spi_host_device_t host)
238239
return ESP_OK;
239240
}
240241

242+
static inline uint32_t spi_dummy_limit(bool gpio_is_used)
243+
{
244+
const int apbclk=APB_CLK_FREQ;
245+
if (!gpio_is_used) {
246+
return apbclk; //dummy bit workaround is not used when native pins are used
247+
} else {
248+
return apbclk/2; //the dummy bit workaround is used when freq is 40MHz and GPIO matrix is used.
249+
}
250+
}
251+
241252
/*
242253
Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks
243254
up the CS pin to whatever is specified.
@@ -246,6 +257,9 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
246257
{
247258
int freecs;
248259
int apbclk=APB_CLK_FREQ;
260+
int eff_clk;
261+
int duty_cycle;
262+
spi_clock_reg_t clk_reg;
249263
SPI_CHECK(host>=SPI_HOST && host<=VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
250264
SPI_CHECK(spihost[host]!=NULL, "host not initialized", ESP_ERR_INVALID_STATE);
251265
SPI_CHECK(dev_config->spics_io_num < 0 || GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num), "spics pin invalid", ESP_ERR_INVALID_ARG);
@@ -258,9 +272,17 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
258272
//The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
259273
//duplex mode does absolutely nothing on the ESP32.
260274
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);
275+
261276
//Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
262-
SPI_CHECK(!( ((dev_config->flags & SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)),
263-
"No speeds >26MHz supported for full-duplex, GPIO-matrix SPI transfers", ESP_ERR_INVALID_ARG);
277+
duty_cycle = (dev_config->duty_cycle_pos==0? 128: dev_config->duty_cycle_pos);
278+
eff_clk = spi_cal_clock(apbclk, dev_config->clock_speed_hz, duty_cycle, (uint32_t*)&clk_reg);
279+
uint32_t dummy_limit = spi_dummy_limit(!spihost[host]->no_gpio_matrix);
280+
SPI_CHECK( dev_config->flags & SPI_DEVICE_HALFDUPLEX || (eff_clk/1000/1000) < (dummy_limit/1000/1000) ||
281+
dev_config->flags & SPI_DEVICE_NO_DUMMY,
282+
"When GPIO matrix is used in full-duplex mode at frequency > 26MHz, device cannot read correct data.\n\
283+
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\
284+
Specify ``SPI_DEVICE_NO_DUMMY`` to ignore this checking. Then you can output data at higher speed, or read data at your own risk.",
285+
ESP_ERR_INVALID_ARG );
264286

265287
//Allocate memory for device
266288
spi_device_t *dev=malloc(sizeof(spi_device_t));
@@ -276,9 +298,13 @@ esp_err_t spi_bus_add_device(spi_host_device_t host, const spi_device_interface_
276298

277299
//We want to save a copy of the dev config in the dev struct.
278300
memcpy(&dev->cfg, dev_config, sizeof(spi_device_interface_config_t));
279-
if (dev->cfg.duty_cycle_pos==0) dev->cfg.duty_cycle_pos=128;
280-
// TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.
281-
dev->clk_cfg.eff_clk = spi_cal_clock(apbclk, dev->cfg.clock_speed_hz, dev->cfg.duty_cycle_pos, (uint32_t*)&dev->clk_cfg.reg);
301+
dev->cfg.duty_cycle_pos = duty_cycle;
302+
// TODO: if we have to change the apb clock among transactions, re-calculate this each time the apb clock lock is acquired.
303+
dev->clk_cfg= (clock_config_t) {
304+
.eff_clk = eff_clk,
305+
.dummy_num = (dev->clk_cfg.eff_clk >= dummy_limit? 1: 0),
306+
.reg = clk_reg,
307+
};
282308

283309
//Set CS pin, CS options
284310
if (dev_config->spics_io_num >= 0) {
@@ -465,7 +491,7 @@ static void IRAM_ATTR spi_intr(void *arg)
465491

466492
//Reconfigure according to device settings, but only if we change CSses.
467493
if (i!=host->prev_cs) {
468-
int apbclk=APB_CLK_FREQ;
494+
const int apbclk=APB_CLK_FREQ;
469495
int effclk=dev->clk_cfg.eff_clk;
470496
spi_set_clock(host->hw, dev->clk_cfg.reg);
471497
//Configure bit order
@@ -475,16 +501,13 @@ static void IRAM_ATTR spi_intr(void *arg)
475501
//Configure polarity
476502
//SPI iface needs to be configured for a delay in some cases.
477503
int nodelay=0;
478-
int extra_dummy=0;
479504
if (host->no_gpio_matrix) {
480505
if (effclk >= apbclk/2) {
481506
nodelay=1;
482507
}
483508
} else {
484-
if (effclk >= apbclk/2) {
485-
nodelay=1;
486-
extra_dummy=1; //Note: This only works on half-duplex connections. spi_bus_add_device checks for this.
487-
} else if (effclk >= apbclk/4) {
509+
uint32_t delay_limit = apbclk/4;
510+
if (effclk >= delay_limit) {
488511
nodelay=1;
489512
}
490513
}
@@ -506,10 +529,6 @@ static void IRAM_ATTR spi_intr(void *arg)
506529
host->hw->user.ck_out_edge=0;
507530
host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
508531
}
509-
510-
//configure dummy bits
511-
host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0;
512-
host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1;
513532
//Configure misc stuff
514533
host->hw->user.doutdin=(dev->cfg.flags & SPI_DEVICE_HALFDUPLEX)?0:1;
515534
host->hw->user.sio=(dev->cfg.flags & SPI_DEVICE_3WIRE)?1:0;
@@ -554,8 +573,8 @@ static void IRAM_ATTR spi_intr(void *arg)
554573
host->hw->ctrl.fastrd_mode=1;
555574
}
556575

557-
558576
//Fill DMA descriptors
577+
int extra_dummy=0;
559578
if (trans_buf->buffer_to_rcv) {
560579
host->hw->user.usr_miso_highpart=0;
561580
if (host->dma_chan == 0) {
@@ -566,6 +585,10 @@ static void IRAM_ATTR spi_intr(void *arg)
566585
host->hw->dma_in_link.addr=(int)(&host->dmadesc_rx[0]) & 0xFFFFF;
567586
host->hw->dma_in_link.start=1;
568587
}
588+
//when no_dummy is not set and in half-duplex mode, sets the dummy bit if RX phase exist
589+
if (((dev->cfg.flags&SPI_DEVICE_NO_DUMMY)==0) && (dev->cfg.flags&SPI_DEVICE_HALFDUPLEX)) {
590+
extra_dummy=dev->clk_cfg.dummy_num;
591+
}
569592
} else {
570593
//DMA temporary workaround: let RX DMA work somehow to avoid the issue in ESP32 v0/v1 silicon
571594
if (host->dma_chan != 0 ) {
@@ -594,6 +617,10 @@ static void IRAM_ATTR spi_intr(void *arg)
594617
}
595618
}
596619

620+
//configure dummy bits
621+
host->hw->user.usr_dummy=(dev->cfg.dummy_bits+extra_dummy)?1:0;
622+
host->hw->user1.usr_dummy_cyclelen=dev->cfg.dummy_bits+extra_dummy-1;
623+
597624
host->hw->mosi_dlen.usr_mosi_dbitlen=trans->length-1;
598625
if ( dev->cfg.flags & SPI_DEVICE_HALFDUPLEX ) {
599626
host->hw->miso_dlen.usr_miso_dbitlen=trans->rxlength-1;
@@ -660,7 +687,6 @@ esp_err_t spi_device_queue_trans(spi_device_handle_t handle, spi_transaction_t *
660687
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);
661688
SPI_CHECK( !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX) || handle->host->dma_chan == 0 || !(trans_desc->flags & SPI_TRANS_USE_RXDATA || trans_desc->rx_buffer != NULL)
662689
|| !(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 );
663-
664690
//In Full duplex mode, default rxlength to be the same as length, if not filled in.
665691
// set rxlength to length is ok, even when rx buffer=NULL
666692
if (trans_desc->rxlength==0 && !(handle->cfg.flags & SPI_DEVICE_HALFDUPLEX)) {

0 commit comments

Comments
 (0)