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.
pull/7510/head
Jonathan Hogg 2021-07-16 16:12:45 +01:00
rodzic eb3029c669
commit 8be29b9b1b
1 zmienionych plików z 41 dodań i 11 usunięć

Wyświetl plik

@ -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);
}
}