DmaSpi for teensyduino
Old original post below, I'll now put the zipped library here.
Zipped Library:
Repo:
The repo is still at https://github.com/crteensy/DmaSpi and the master branch should be up to date.
Installation:
Example Sketch:
There is an example included in the library (examples/DMASpi_example1/DMASpi_example1.ino) that demonstrates most of the DmaSpi's capabilities. If the library is installed correctly, the arduino IDE should list it in File->Examples.
Old original post: (kept for reference only)
Yay!
Here's the new and disimproved DmaSpi library: https://github.com/crteensy/DmaSpi/tree/teensyduino_1.20_RC2 (make sure you get the correct branch)
It's only for use with teensyduino 1.20 RC2 (current master from https://github.com/paulstoffregen/cores). It's important to get the current fixes to DmaChannel.h which Paul pulled today (thanks!).
The following teensyduino RC2 features are used:
The interface has changed a bit, I'll include a sketch for testing purposes further down in this post. It demonstrates the use of transfers, starting, stopping and resuming the DmaSpi operation.
Especially starting and stopping of DMA transfers to and from the SPI has changed. Transfers can be registered after a call to DmaSpi0::begin(), but nothing will happen (yet), because the driver doesn't yet know if it may use the SPI. That's why the DmaSpi0::start() method has been added, which calls SPI.beginTransaction() with default settings.
DMA operation can be stopped by calling DmaSpi0::stop() and then waiting until DmaSpi0::stopped() returns true (the driver will finish a running transfer first and not start a new one when DmaSpi0::stop() was called). After that, other drivers can call SPI.beginTransaction(), use the SPI, and call SPI.endTransaction() again. After that, a call to DmaSpi0::start() allows the driver to handle any transfers that are still pending.
The ChipSelect classes are responsible for setting the SPI's speed, mode and bit order. The test sketch below includes a usage example (last block) that applies standard settings (you can change them, though) and uses pin 0 as chip select.
Test sketch (wiring info in line 10):
Old original post below, I'll now put the zipped library here.
Zipped Library:
- Current: View attachment DmaSpi_20141015.zip
- Older:
- (none yet)
Repo:
The repo is still at https://github.com/crteensy/DmaSpi and the master branch should be up to date.
Installation:
- If you have the zip from here (name ends with a date):
- Put the zip file wherever you want
- In the arduino IDE, go to Sketch -> Import Library -> Add Library and pick the zip
- If you have checked out the repo:
- Close your arduino IDE
- The library folder name after checking out is "DmaSpi-master" (or similar, depending on the branch), which is not a legal arduino library name (because it contains a dash)
- copy or move it into a folder recognized as a library folder by your arduino installation
- rename the library folder to "DmaSpi_master" or similar (the point here is to remove the dash)
- reopen your arduino IDE and see if it shows up.
- also see http://arduino.cc/en/pmwiki.php?n=Guide/Libraries
Example Sketch:
There is an example included in the library (examples/DMASpi_example1/DMASpi_example1.ino) that demonstrates most of the DmaSpi's capabilities. If the library is installed correctly, the arduino IDE should list it in File->Examples.
Old original post: (kept for reference only)
Yay!
Here's the new and disimproved DmaSpi library: https://github.com/crteensy/DmaSpi/tree/teensyduino_1.20_RC2 (make sure you get the correct branch)
It's only for use with teensyduino 1.20 RC2 (current master from https://github.com/paulstoffregen/cores). It's important to get the current fixes to DmaChannel.h which Paul pulled today (thanks!).
The following teensyduino RC2 features are used:
- SPI transactions
- Dynamic DMA channel allocation
- Interrupt vector table in RAM
The interface has changed a bit, I'll include a sketch for testing purposes further down in this post. It demonstrates the use of transfers, starting, stopping and resuming the DmaSpi operation.
Especially starting and stopping of DMA transfers to and from the SPI has changed. Transfers can be registered after a call to DmaSpi0::begin(), but nothing will happen (yet), because the driver doesn't yet know if it may use the SPI. That's why the DmaSpi0::start() method has been added, which calls SPI.beginTransaction() with default settings.
DMA operation can be stopped by calling DmaSpi0::stop() and then waiting until DmaSpi0::stopped() returns true (the driver will finish a running transfer first and not start a new one when DmaSpi0::stop() was called). After that, other drivers can call SPI.beginTransaction(), use the SPI, and call SPI.endTransaction() again. After that, a call to DmaSpi0::start() allows the driver to handle any transfers that are still pending.
The ChipSelect classes are responsible for setting the SPI's speed, mode and bit order. The test sketch below includes a usage example (last block) that applies standard settings (you can change them, though) and uses pin 0 as chip select.
Test sketch (wiring info in line 10):
Code:
#include <WProgram.h>
#include <SPI.h>
#include <DmaSpi.h>
/** Hardware setup:
plain Teensy 3.1 with DOUT connected to DIN, and an active low LED on pin 0
nothing else
**/
/** buffers to send from and to receive to **/
#define DMASIZE 100
uint8_t src[DMASIZE];
volatile uint8_t dest[DMASIZE];
volatile uint8_t dest1[DMASIZE];
/** Wait for and consume a keypress over USB **/
void waitForKeyPress()
{
Serial.println("\nPress a key to continue\n");
while(!Serial.available());
while(Serial.available())
{
Serial.read();
}
}
void dumpBuffer(const volatile uint8_t* buf, const char* prefix)
{
Serial.print(prefix);
for (size_t i = 0; i < DMASIZE; i++)
{
Serial.printf("0x%02x ", buf[i]);
}
Serial.print('\n');
}
/** Compare the buffers and print the destination contents if there's a mismatch **/
void compareBuffers(const uint8_t* src_, const uint8_t* dest_)
{
int n = memcmp((const void*)src_, (const void*)dest_, DMASIZE);
if (n == 0)
{
Serial.println("src and dest match");
}
else
{
Serial.println("src and dest don't match");
dumpBuffer(src_, " src: " );
dumpBuffer(dest_, "dest: ");
}
}
void setSrc()
{
for (size_t i = 0; i < DMASIZE; i++)
{
src[i] = i;
}
}
void clrDest(uint8_t* dest_)
{
memset((void*)dest_, 0x00, DMASIZE);
}
void setup()
{
waitForKeyPress();
Serial.println("Hi!");
/** Prepare source and destination **/
setSrc();
clrDest((uint8_t*)dest);
Serial.println("Buffers are prepared");Serial.flush();
/** set up SPI **/
SPISettings spiSettings;
SPI.begin();
// transmit 10 bytes and measure time to get a feel of how long that takes
SPI.beginTransaction(spiSettings);
elapsedMicros us;
for (size_t i = 0; i < DMASIZE; i++)
{
dest[i] = SPI.transfer(src[i]);
}
uint32_t t = us;
Serial.print("Time for non-DMA transfer: ");Serial.print(t);Serial.println("us");
SPI.endTransaction();
compareBuffers(src, (const uint8_t*)dest);
waitForKeyPress();
DMASPI0.begin();
DMASPI0.start();
Serial.println("Testing src -> dest, single transfer");
Serial.println("--------------------------------------------------");
DmaSpi0::Transfer trx(src, DMASIZE, dest);
clrDest((uint8_t*)dest);
DMASPI0.registerTransfer(trx);
while(trx.busy())
{
}
Serial.println("Finished DMA transfer");
compareBuffers(src, (const uint8_t*)dest);
Serial.println("==================================================\n\n");
Serial.println("Testing src -> discard, single transfer");
Serial.println("--------------------------------------------------");
trx = DmaSpi0::Transfer(src, DMASIZE, nullptr);
DMASPI0.registerTransfer(trx);
while(trx.busy())
{
}
Serial.println("Finished DMA transfer");
Serial.printf("last discarded value is 0x%02x\n", DMASPI0.devNull());
if (DMASPI0.devNull() == src[DMASIZE-1])
{
Serial.println("That appears to be correct");
}
else
{
Serial.printf("That appears to be wrong, it should be src[DMASIZE-1] which is 0x%02x\n", src[DMASIZE-1]);
}
Serial.println("==================================================\n\n");
Serial.println("Testing 0xFF dummy data -> dest, single transfer");
Serial.println("--------------------------------------------------");
trx = DmaSpi0::Transfer(nullptr, DMASIZE, dest, 0xFF);
memset((void*)src, 0xFF, DMASIZE); // we need this for checking the dest buffer
clrDest((uint8_t*)dest);
DMASPI0.registerTransfer(trx);
while(trx.busy())
{
}
Serial.println("Finished DMA transfer");
compareBuffers(src, (const uint8_t*)dest);
Serial.println("==================================================\n\n");
Serial.println("Testing multiple queued transfers");
Serial.println("--------------------------------------------------");
trx = DmaSpi0::Transfer(src, DMASIZE, dest, 0xFF);
setSrc();
clrDest((uint8_t*)dest);
clrDest((uint8_t*)dest1);
DmaSpi0::Transfer trx1(src, DMASIZE, dest1);
DMASPI0.registerTransfer(trx);
DMASPI0.registerTransfer(trx1);
while(trx.busy());
Serial.println("Finished DMA transfer");
while(trx1.busy());
Serial.println("Finished DMA transfer1");
compareBuffers(src, (const uint8_t*)dest);
compareBuffers(src, (const uint8_t*)dest1);
Serial.println("==================================================\n\n");
Serial.println("Testing pause and restart");
Serial.println("--------------------------------------------------");
clrDest((uint8_t*)dest);
clrDest((uint8_t*)dest1);
DMASPI0.registerTransfer(trx);
DMASPI0.registerTransfer(trx1);
DMASPI0.stop();
us = elapsedMicros();
while(!DMASPI0.stopped());
t = us;
while(trx.busy());
Serial.printf("Time until stopped: %lu us\n", t);
Serial.println("Finished DMA transfer");
if (DMASPI0.stopped())
{
Serial.println("DMA SPI appears to have stopped (this is good)\nrestarting");
}
else
{
Serial.println("DMA SPI does not report stopped state, but it should. (this is bad)");
}
DMASPI0.start();
while(trx1.busy());
Serial.println("Finished DMA transfer1");
compareBuffers(src, (const uint8_t*)dest);
compareBuffers(src, (const uint8_t*)dest1);
Serial.println("==================================================\n\n");
Serial.println("Testing src -> dest, with chip select object");
Serial.println("--------------------------------------------------");
ActiveLowChipSelect cs(0, SPISettings());
trx = DmaSpi0::Transfer(src, DMASIZE, dest, 0, &cs);
clrDest((uint8_t*)dest);
DMASPI0.registerTransfer(trx);
while(trx.busy())
{
}
Serial.println("Finished DMA transfer");
compareBuffers(src, (const uint8_t*)dest);
Serial.println("==================================================\n\n");
DmaSpi0::Transfer(src, DMASIZE, dest, 0, &cs);
if (DMASPI0.stopped())
{
Serial.println("DMA SPI stopped.");
}
else
{
Serial.println("DMA SPI is still running");
}
DMASPI0.stop();
if (DMASPI0.stopped())
{
Serial.println("DMA SPI stopped.");
}
else
{
Serial.println("DMA SPI is still running");
}
DMASPI0.end();
SPI.end();
pinMode(LED_BUILTIN, OUTPUT);
}
void loop()
{
digitalWriteFast(LED_BUILTIN, true);
delay(500);
digitalWriteFast(LED_BUILTIN, false);
delay(500);
}
Last edited: