esp32/machine_hw_spi: Fix large HW SPI transfers by splitting them up.
Breaks up HW SPI transfers into maximum chunks of 32736 bits (4092 bytes),
because this is the maximum that the underlying ESP IDF will accept.
#include "driver/spi_master.h"
+#define MP_HW_SPI_MAX_XFER_BYTES (4092)
+#define MP_HW_SPI_MAX_XFER_BITS (MP_HW_SPI_MAX_XFER_BYTES * 8) // Has to be an even multiple of 8
+
typedef struct _machine_hw_spi_obj_t {
mp_obj_base_t base;
spi_host_device_t host;
@@ -227,35 +230,52 @@ STATIC void machine_hw_spi_deinit(mp_obj_base_t *self_in) {
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);
- int bits_to_send = len * self->bits;
- bool shortMsg = len <= 4;
-
if (self->state == MACHINE_HW_SPI_STATE_DEINIT) {
mp_raise_msg(&mp_type_OSError, "transfer on deinitialized SPI");
return;
}
- struct spi_transaction_t transaction = {
- .flags = 0,
- .length = bits_to_send,
- .tx_buffer = NULL,
- .rx_buffer = NULL,
- };
+ struct spi_transaction_t transaction = { 0 };
+
+ // Round to nearest whole set of bits
+ int bits_to_send = len * 8 / self->bits * self->bits;
+
- if (shortMsg) {
+ if (len <= 4) {
if (src != NULL) {
memcpy(&transaction.tx_data, src, len);
}
- transaction.flags |= (SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA);
+
+ transaction.flags = SPI_TRANS_USE_TXDATA | SPI_TRANS_USE_RXDATA;
+ transaction.length = bits_to_send;
+ spi_device_transmit(self->spi, &transaction);
+
+ if (dest != NULL) {
+ memcpy(dest, &transaction.rx_data, len);
+ }
} else {
- transaction.tx_buffer = src;
- transaction.rx_buffer = dest;
+ int offset = 0;
+ int bits_remaining = bits_to_send;
+
+ while (bits_remaining) {
+ memset(&transaction, 0, sizeof(transaction));
+
+ transaction.length =
+ bits_remaining > MP_HW_SPI_MAX_XFER_BITS ? MP_HW_SPI_MAX_XFER_BITS : bits_remaining;
+
+ if (src != NULL) {
+ transaction.tx_buffer = src + offset;
+ }
+ if (dest != NULL) {
+ transaction.rx_buffer = dest + offset;
}
spi_device_transmit(self->spi, &transaction);
+ bits_remaining -= transaction.length;
- if (shortMsg && dest != NULL) {
- memcpy(dest, &transaction.rx_data, len);
+ // doesn't need ceil(); loop ends when bits_remaining is 0
+ offset += transaction.length / 8;
+ }
}
}
