@@ -95,6 +95,7 @@ typedef struct {
9595typedef struct {
9696 spi_clock_reg_t reg ;
9797 int eff_clk ;
98+ int dummy_num ;
9899} clock_config_t ;
99100
100101struct 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