From 8be29b9b1b0fc2709d6ace3a9ff32092052359f6 Mon Sep 17 00:00:00 2001 From: Jonathan Hogg Date: Fri, 16 Jul 2021 16:12:45 +0100 Subject: [PATCH] esp32/machine_hw_spi: Use a 2 item SPI queue for long transfers. Using a 2-item transaction queue instead of 1 allows long transfers to be executed with the minimum inter-transaction delay. Limit maximum transaction length to ensure an integer multiple of the SPI `bits` setting are transferred. Fixes #7511. --- ports/esp32/machine_hw_spi.c | 52 ++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/ports/esp32/machine_hw_spi.c b/ports/esp32/machine_hw_spi.c index 2792216cc0..5b59f2431c 100644 --- a/ports/esp32/machine_hw_spi.c +++ b/ports/esp32/machine_hw_spi.c @@ -217,7 +217,7 @@ STATIC void machine_hw_spi_init_internal( .clock_speed_hz = self->baudrate, .mode = self->phase | (self->polarity << 1), .spics_io_num = -1, // No CS pin - .queue_size = 1, + .queue_size = 2, .flags = self->firstbit == MICROPY_PY_MACHINE_SPI_LSB ? SPI_DEVICE_TXBIT_LSBFIRST | SPI_DEVICE_RXBIT_LSBFIRST : 0, .pre_cb = NULL }; @@ -273,6 +273,17 @@ STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) { } } +STATIC mp_uint_t gcd(mp_uint_t x, mp_uint_t y) { + while (x != y) { + if (x > y) { + x -= y; + } else { + y -= x; + } + } + return x; +} + STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_hw_spi_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -281,13 +292,16 @@ STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const ui return; } - struct spi_transaction_t transaction = { 0 }; - // Round to nearest whole set of bits int bits_to_send = len * 8 / self->bits * self->bits; + if (!bits_to_send) { + mp_raise_ValueError(MP_ERROR_TEXT("buffer too short")); + } if (len <= 4) { + spi_transaction_t transaction = { 0 }; + if (src != NULL) { memcpy(&transaction.tx_data, src, len); } @@ -302,26 +316,42 @@ STATIC void machine_hw_spi_transfer(mp_obj_base_t *self_in, size_t len, const ui } else { int offset = 0; int bits_remaining = bits_to_send; + int optimum_word_size = 8 * self->bits / gcd(8, self->bits); + int max_transaction_bits = MP_HW_SPI_MAX_XFER_BITS / optimum_word_size * optimum_word_size; + spi_transaction_t *transaction, *result, transactions[2]; + int i = 0; + + spi_device_acquire_bus(self->spi, portMAX_DELAY); while (bits_remaining) { - memset(&transaction, 0, sizeof(transaction)); + transaction = transactions + i++ % 2; + memset(transaction, 0, sizeof(spi_transaction_t)); - transaction.length = - bits_remaining > MP_HW_SPI_MAX_XFER_BITS ? MP_HW_SPI_MAX_XFER_BITS : bits_remaining; + transaction->length = + bits_remaining > max_transaction_bits ? max_transaction_bits : bits_remaining; if (src != NULL) { - transaction.tx_buffer = src + offset; + transaction->tx_buffer = src + offset; } if (dest != NULL) { - transaction.rx_buffer = dest + offset; + transaction->rx_buffer = dest + offset; } - spi_device_transmit(self->spi, &transaction); - bits_remaining -= transaction.length; + spi_device_queue_trans(self->spi, transaction, portMAX_DELAY); + bits_remaining -= transaction->length; + + if (offset > 0) { + // wait for previously queued transaction + spi_device_get_trans_result(self->spi, &result, portMAX_DELAY); + } // doesn't need ceil(); loop ends when bits_remaining is 0 - offset += transaction.length / 8; + offset += transaction->length / 8; } + + // wait for last transaction + spi_device_get_trans_result(self->spi, &result, portMAX_DELAY); + spi_device_release_bus(self->spi); } }