Skip to content

Allow unaligned input/output to SPI::transferBytes #5709

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 47 additions & 22 deletions libraries/SPI/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,6 @@ void SPIClass::writePattern(const uint8_t * data, uint8_t size, uint32_t repeat)
}

/**
* Note:
* in and out need to be aligned to 32Bit
* or you get an Fatal exception (9)
* @param out uint8_t *
* @param in uint8_t *
* @param size uint32_t
Expand All @@ -538,45 +535,73 @@ void SPIClass::transferBytes(const uint8_t * out, uint8_t * in, uint32_t size) {
* @param in uint8_t *
* @param size uint8_t (max 64)
*/
void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) {

void SPIClass::transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size) {
if (!size)
return;

while(SPI1CMD & SPIBUSY) {}
// Set in/out Bits to transfer

setDataBits(size * 8);

volatile uint32_t * fifoPtr = &SPI1W0;
uint8_t dataSize = ((size + 3) / 4);
volatile uint32_t *fifoPtr = &SPI1W0;

if(out) {
uint32_t * dataPtr = (uint32_t*) out;
while(dataSize--) {
*fifoPtr = *dataPtr;
dataPtr++;
fifoPtr++;
if (out) {
uint8_t outSize = ((size + 3) / 4);
uint32_t *dataPtr = (uint32_t*) out;
while (outSize--) {
*(fifoPtr++) = *(dataPtr++);
}
} else {
uint8_t outSize = ((size + 3) / 4);
// no out data only read fill with dummy data!
while(dataSize--) {
*fifoPtr = 0xFFFFFFFF;
fifoPtr++;
while (outSize--) {
*(fifoPtr++) = 0xFFFFFFFF;
}
}

SPI1CMD |= SPIBUSY;
while(SPI1CMD & SPIBUSY) {}

if(in) {
uint32_t * dataPtr = (uint32_t*) in;
if (in) {
uint32_t *dataPtr = (uint32_t*) in;
fifoPtr = &SPI1W0;
dataSize = ((size + 3) / 4);
while(dataSize--) {
*dataPtr = *fifoPtr;
dataPtr++;
fifoPtr++;
int inSize = size;
// Unlike outSize above, inSize tracks *bytes* since we must transfer only the requested bytes to the app to avoid overwriting other vars.
while (inSize >= 4) {
*(dataPtr++) = *(fifoPtr++);
inSize -= 4;
in += 4;
}
volatile uint8_t *fifoPtrB = (volatile uint8_t *)fifoPtr;
while (inSize--) {
*(in++) = *(fifoPtrB++);
}
}
}


void SPIClass::transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size) {
if (!((uint32_t)out & 3) && !((uint32_t)in & 3)) {
// Input and output are both 32b aligned or NULL
transferBytesAligned_(out, in, size);
} else {
// HW FIFO has 64b limit and ::transferBytes breaks up large xfers into 64byte chunks before calling this function
// We know at this point at least one direction is misaligned, so use temporary buffer to align everything
// No need for separate out and in aligned copies, we can overwrite our out copy with the input data safely
uint8_t aligned[64]; // Stack vars will be 32b aligned
if (out) {
memcpy(aligned, out, size);
}
transferBytesAligned_(out ? aligned : nullptr, in ? aligned : nullptr, size);
if (in) {
memcpy(in, aligned, size);
}
}
}


#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SPI)
SPIClass SPI;
#endif
1 change: 1 addition & 0 deletions libraries/SPI/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class SPIClass {
uint8_t pinSet;
void writeBytes_(const uint8_t * data, uint8_t size);
void transferBytes_(const uint8_t * out, uint8_t * in, uint8_t size);
void transferBytesAligned_(const uint8_t * out, uint8_t * in, uint8_t size);
inline void setDataBits(uint16_t bits);
};

Expand Down