Skip to content

Commit b2f745b

Browse files
Allow unaligned input/output to SPI::transferBytes
Fixes esp8266#4967 Support any alignment of input and output pointers and transfer lengths in SPI::transferBytes. Use 32-bit transfers and FIFO as much as possible.
1 parent 1cacf92 commit b2f745b

File tree

1 file changed

+70
-31
lines changed

1 file changed

+70
-31
lines changed

libraries/SPI/SPI.cpp

+70-31
Original file line numberDiff line numberDiff line change
@@ -509,9 +509,6 @@ void SPIClass::writePattern(const uint8_t * data, uint8_t size, uint32_t repeat)
509509
}
510510

511511
/**
512-
* Note:
513-
* in and out need to be aligned to 32Bit
514-
* or you get an Fatal exception (9)
515512
* @param out uint8_t *
516513
* @param in uint8_t *
517514
* @param size uint32_t
@@ -531,48 +528,90 @@ void SPIClass::transferBytes(const uint8_t * out, uint8_t * in, uint32_t size) {
531528
}
532529

533530
/**
534-
* Note:
535-
* in and out need to be aligned to 32Bit
536-
* or you get an Fatal exception (9)
537531
* @param out uint8_t *
538532
* @param in uint8_t *
539533
* @param size uint8_t (max 64)
540534
*/
541535
void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) {
542-
while(SPI1CMD & SPIBUSY) {}
543-
// Set in/out Bits to transfer
536+
while(SPI1CMD & SPIBUSY) {} // Make sure we're IDLE on the SPI
544537

545-
setDataBits(size * 8);
538+
int bytesToTransfer = size; // How much to read/write from the FIFO in bytes
546539

547-
volatile uint32_t * fifoPtr = &SPI1W0;
548-
uint8_t dataSize = ((size + 3) / 4);
540+
if (out && !in) {
541+
int bytesLeft = size;
549542

550-
if(out) {
551-
uint32_t * dataPtr = (uint32_t*) out;
552-
while(dataSize--) {
553-
*fifoPtr = *dataPtr;
554-
dataPtr++;
555-
fifoPtr++;
543+
// Only transmitting, get out 32b aligned
544+
while (bytesLeft && ((uint32_t)out & 3)) {
545+
SPI.transfer(*(out++));
546+
bytesLeft--;
556547
}
557-
} else {
558-
// no out data only read fill with dummy data!
559-
while(dataSize--) {
560-
*fifoPtr = 0xFFFFFFFF;
561-
fifoPtr++;
548+
549+
if (!bytesLeft) return;
550+
bytesToTransfer = bytesLeft;
551+
552+
// Now do 32b writes until we have 0 or fewer bytes left
553+
// Note we can read and store past the end of string because it will not be sent (setDataBits)
554+
uint32_t *dataPtr = (uint32_t*)out;
555+
volatile uint32_t *fifoPtr = &SPI1W0;
556+
while (bytesLeft > 0) {
557+
*(fifoPtr++) = *(dataPtr++);
558+
bytesLeft -= 4;
562559
}
560+
// Remainder will be sent out of loaded FIFO below
561+
} else if (in && !out) {
562+
int bytesLeft = size;
563+
564+
// Only receiving, get in 32b aligned
565+
while (bytesLeft && ((uint32_t)in & 3)) {
566+
*(in++) = SPI.transfer(0xff);
567+
bytesLeft--;
568+
}
569+
570+
if (!bytesLeft) return;
571+
bytesToTransfer = bytesLeft;
572+
573+
// Now send 32b writes of all 0xff, actual read will be done below
574+
// Note we can write add'l 0xffs and they won't be sent thanks to setDataBits below
575+
volatile uint32_t *fifoPtr = &SPI1W0;
576+
while (bytesLeft > 0) {
577+
*(fifoPtr++) = 0xffffffff;
578+
bytesLeft -= 4;
579+
}
580+
} else if (in && out && (((uint32_t)in & 3) || ((uint32_t)out & 3))) {
581+
// Bidirectional and we have one or both pointers misaligned
582+
while (size) {
583+
*(in++) = SPI.transfer(*(out++));
584+
size--;
585+
}
586+
// All the data was transferred byte-wise, we're done
587+
return;
563588
}
564589

590+
// At this point we've got aligned (or null) in and out and the FIFO is filled
591+
// with whatever data we're going to transmit as aligned
592+
593+
// Set in/out bits to transfer
594+
while (SPI1CMD & SPIBUSY) {} // Paranoia, make sure SPI is idle
595+
setDataBits(bytesToTransfer * 8);
596+
597+
// Start the transfer and wait for done
565598
SPI1CMD |= SPIBUSY;
566-
while(SPI1CMD & SPIBUSY) {}
599+
while (SPI1CMD & SPIBUSY) { /* noop, waiting */ }
600+
601+
if (in) {
602+
// Bulk read out by 4 until we have 0..3 left
603+
uint32_t *dataPtr = (uint32_t*)in;
604+
volatile uint32_t *fifoPtr = &SPI1W0;
605+
while (bytesToTransfer >= 4) {
606+
*(dataPtr++) = *(fifoPtr++);
607+
bytesToTransfer -= 4;
608+
in += 4; // Keep track of the in ptr for any stragglers
609+
}
567610

568-
if(in) {
569-
uint32_t * dataPtr = (uint32_t*) in;
570-
fifoPtr = &SPI1W0;
571-
dataSize = ((size + 3) / 4);
572-
while(dataSize--) {
573-
*dataPtr = *fifoPtr;
574-
dataPtr++;
575-
fifoPtr++;
611+
// Bytewise read out the remainder
612+
volatile uint8_t *fifoPtrB = (uint8_t*)fifoPtr;
613+
while (bytesToTransfer--) {
614+
*(in++) = *(fifoPtrB++);
576615
}
577616
}
578617
}

0 commit comments

Comments
 (0)