Skip to content
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

Add user callback interface to SPI class to enable DMA flow control #299

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
27 changes: 27 additions & 0 deletions libraries/SPI/SPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,13 +247,22 @@ void SPIClass::transfer(void *buf, size_t count)
// a reference back to the originating SPIClass object.
static SPIClass *spiPtr[DMAC_CH_NUM] = { 0 }; // Legit inits list to NULL

// Pointer to a user-defined callback function, one per DMA channel. These
// will be duplicated for read & write channels corresponding to the same
// SPIClass (see SPIClass::dmaAllocate()).
static SPIClass::UserCallbackFunction* userActiveCallback[DMAC_CH_NUM] = { 0 };

void SPIClass::dmaCallback(Adafruit_ZeroDMA *dma) {
// dmaCallback() receives an Adafruit_ZeroDMA object. From this we can get
// a channel number (0 to DMAC_CH_NUM-1, always unique per ZeroDMA object),
// then locate the originating SPIClass object using array lookup, setting
// the dma_busy element 'false' to indicate end of transfer. Doesn't matter
// if it's a read or write transfer...both channels get pointers to it.
spiPtr[dma->getChannel()]->dma_busy = false;

if (auto userCallback = userActiveCallback[dma->getChannel()]) {
userCallback(dma);
}
}

// For read-only and read+write transfers, a callback is assigned only
Expand Down Expand Up @@ -362,6 +371,24 @@ void SPIClass::dmaAllocate(void) {
// transfer() function will fall back on a manual byte-by-byte loop.
}

// User defined callback
// SPI uses two DMA channels (read & write), but only one is assigned
// a callback (see dmaCallback) for a given transfer, and the other gets
// a dummy. Store the user's callback function on both channels, since
// we don't know which one will be the source.
// See also: Adafruit_ZeroDMA::getChannel()
void SPIClass::dmaSetEndOfTransferCallback(UserCallbackFunction* callback)
{
userActiveCallback[readChannel.getChannel()] = callback;
userActiveCallback[writeChannel.getChannel()] = callback;
}

void SPIClass::dmaClearEndOfTransferCallback()
{
userActiveCallback[readChannel.getChannel()] = nullptr;
userActiveCallback[writeChannel.getChannel()] = nullptr;
}

void SPIClass::transfer(const void *txbuf, void *rxbuf, size_t count,
bool block) {

Expand Down
5 changes: 5 additions & 0 deletions libraries/SPI/SPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@ class SPIClass {
void attachInterrupt();
void detachInterrupt();

// DMA Configuation methods
using UserCallbackFunction = void(Adafruit_ZeroDMA*);
void dmaSetEndOfTransferCallback(UserCallbackFunction* callback);
void dmaClearEndOfTransferCallback();

void begin();
void end();

Expand Down