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.
This commit is contained in:
parent
eb3029c669
commit
8be29b9b1b
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue