DMA with OLED and DAC

Status
Not open for further replies.

MacroMachines

Well-known member
I am currently trying to speed up communication on my project by using DMA. It appears when I use the test example for the OLED_ssd1306 library it works fine, but when I try to use my spi DAC things completely stop. Am I supposed to use 2 different spi busses to realize this? Where can I find more info on how to go about this?
 
Here is a code snippet of my current stripped down test:
#include <SPI.h>
#include <Adafruit_GFX2.h>
#include <OLED_SSD1306.h>

#define OLED_DC 9
#define OLED_CS 10
#define OLED_RESET 14
OLED_SSD1306 display(OLED_CS, OLED_DC, OLED_RESET);

#define DAC_CS 15 // this is the part I have no idea about
#define DAC_DATA 7 // what can I do to let the OLED use DMA on the hardware SPI pins
#define DAC_CLK 8 //14 // and get the dac to work?

byte totalWaves = 4;
word wave[4][2];
word wavePhase = 0;

void setup() {
display.begin();
display.setBitrate(24000000);

pinMode(DAC_CS, OUTPUT);
digitalWrite(DAC_CS, HIGH);
SPI.begin();
SPI.setBitOrder(MSBFIRST);
}


void loop(){
for (int16_t i=0; i<display.height()/2; i+=2) {
display.drawRect(i, i, display.width()-2*i, display.height()-2*i, WHITE);
display.display();
}
display.clearDisplay();

DACoutSPI();
}


void DACoutSPI(){ //SPI DAC write
for(byte channel = 0; channel < totalWaves; channel++){
wave[channel][wavePhase] = random(4096);

digitalWrite(DAC_CS, LOW);
byte formatDacMSB = (channel<<6 | B00010000 | ((highByte(wave[channel][wavePhase])) & B00001111));
byte formatDacLSB = (lowByte(wave[channel][wavePhase]));
SPI.transfer(formatDacMSB);
SPI.transfer(formatDacLSB);
digitalWrite(DAC_CS,HIGH);
}
}
 
sumotoy will know more about potential conflicts, but you may just have configured SPI incorrectly?

this for instance

Code:
#define DAC_CLK 8

obviously doesn't look right. it should be either 13 or 14, i suppose (ie SPI SCK). 8 is DIN/MISO. looking at commonInit() in OLED_SSD1306.cpp, the library defaults to

SCK : 13, DOUT/MOSI: 11

is your display wired like this? or did you move things to pins 14 and 7 (presumably no, seeing that OLED_RESET = 14)?

as a first step, i'd make sure the OLED and DAC share the same SCK and MOSI lines. presumably that would be

Code:
#define DAC_DATA 11
#define DAC_CLK 13

SPI.begin() is already invoked by display.begin(), so it's not needed again. if things still fail, ... doesn't look like the library features transactions.
 
I guess one of the things that is confusing to me is that if the DOUT/MOSI pin is sending SPI - DMA to OLED, then if I want to write out the word to the SPI DAC on the same pin, would there even be a performance gain? Isn't the point to be able to let the DMA do its thing through that pin while the micro controller does other stuff that doesn't touch that pin? Thus I was trying to figure out if I can use the greyed out secondary SPI pins on the reference card to write out to the DAC so that they can be as independent as possible.

I am trying to find a "dummies guide" to getting started with DMA on the teensy and everything I find is not very introductory, its mainly people talking about the library they made as if the reader already knows everything about DMA inside and out.. from what I gather its useful enough and complex enough to warrant some kind of startup literature.

I should mention I did try it with those shared pins you mentioned and it just stopps doing anything.. but I will try again as I have changed a few other things as well.. Ill report back shortly..

I was also thinking of maybe just bit-banging the DAC.. The FastSPI_LED library had some interesting stuff to that effect but it looks like it is set up more specifically then their wordpress entry would lead me to believe:

Code:
#include "FastSPI_LED2.h"
SPIOutput<11, 13, 10, 0> SPI;
setup() { SPI.init(); }
loop() { 
  // setup some data in a buffer
  SPI.writeBytes(pData, nBufferLen);
}

and

Code:
if(b & (1 << BIT)) {
			Pin<DATA_PIN>::fastset(dataPort, dataHi);
		} else { 
			Pin<DATA_PIN>::fastset(dataPort, dataLo);
		}
		Pin<CLOCK_PIN>::fastset(clockPort, clockHi);
		Pin<CLOCK_PIN>::fastset(clockPort, clockLo);


and finally

Code:
if(b & (1 << BIT)) {
			Pin<DATA_PIN>::fastset(dataPort, dataHiCLockHi);
			Pin<DATA_PIN>::fastset(dataPort, dataHiCLockLo);
		} else { 
			Pin<DATA_PIN>::fastset(dataPort, dataLoCLockHi);
			Pin<DATA_PIN>::fastset(dataPort, dataLoCLockHi);
		}


all referenced from this page :


http://waitingforbigo.com/2013/02/19/introducing-fastspi-for-most-of-your-data-pushing-needs/


Thanks! I really do appreciate any input greatly
 
I guess one of the things that is confusing to me is that if the DOUT/MOSI pin is sending SPI - DMA to OLED, then if I want to write out the word to the SPI DAC on the same pin, would there even be a performance gain? Isn't the point to be able to let the DMA do its thing through that pin while the micro controller does other stuff that doesn't touch that pin? Thus I was trying to figure out if I can use the greyed out secondary SPI pins on the reference card to write out to the DAC so that they can be as independent as possible.

the greyed out pins are "ALT", not a second SPI port, so that won't work. I'd guess something DMA might still be doable, but you'll be talking to two peripheral devices, which will need a more elaborate scheme... but DMA isn't my forte either
 
Neither your code nor Adafruit's library seem to be use SPI transactions. So both are sharing the same settings.

Maybe you could use a lower speed and otherwise the same settings as Adafruit's library?

The (much harder but much better) alternative would be the edit the library to add SPI.beginTransaction() and SPI.endTransaction() around each of these SPI usages, and of course the same in your code.
 
I am currently trying to speed up communication on my project by using DMA

on a different note, so are you concerned about the DAC sampling rate/jitter/etc, or the display refresh rate, or both?

assuming a/the module's performance is mostly dictated by the performance of the DAC and the display is more, well, eye-candy, did you consider using the i2c version of said adafruit OLED display? or bitbang/use software SPI for the display? or wait for the teensy 3++, which will have more than one SPI, i gather. or, if you can't wait, use a stm32f10x, or stm32F4xx?
 
Neither your code nor Adafruit's library seem to be use SPI transactions. So both are sharing the same settings.

Maybe you could use a lower speed and otherwise the same settings as Adafruit's library?

The (much harder but much better) alternative would be the edit the library to add SPI.beginTransaction() and SPI.endTransaction() around each of these SPI usages, and of course the same in your code.


I am not sure what you are referring to as not having the beginTransaction, the libraries I am using for the OLED and graphics are a significant mod to the Adafruit library by sumotoy here:
https://github.com/sumotoy/Adafruit-GFX-Library
https://github.com/sumotoy/OLED_SSD1306

So what is the difference between SPI transaction and SPI transfer?
 
on a different note, so are you concerned about the DAC sampling rate/jitter/etc, or the display refresh rate, or both?

assuming a/the module's performance is mostly dictated by the performance of the DAC and the display is more, well, eye-candy, did you consider using the i2c version of said adafruit OLED display? or bitbang/use software SPI for the display? or wait for the teensy 3++, which will have more than one SPI, i gather. or, if you can't wait, use a stm32f10x, or stm32F4xx?

I tried the i2c version (same version as I have now but you just connect some jumper pads on the back to switch between i2c and spi). With i2c the display runs so slow that my dac writes and the core waveform generation code get slowed way down. IE: the screen writes take longer each one and thus it hangs each time for everything else.

As far as jitter and refresh rate, both are irrelevant to me on this as long as it doesn't hang. I am actually trying out a new core built around intervalTImer interrupts and priorities. My current test is here:

Code:
#include <SPI.h>
#include <Adafruit_GFX2.h>
#include <OLED_SSD1306.h>
#include <elapsedMillis.h>

#define DAC_CS 15 
#define OLED_DC     9
#define OLED_CS     10
#define OLED_RESET  14
OLED_SSD1306 display(OLED_CS, OLED_DC, OLED_RESET);



IntervalTimer DACtimer;
byte totalWaves = 4;
word wave[4][2];
word wavePhase = 0;

void setup()   {                
  display.begin();
  display.setBitrate(24000000);
  pinMode(DAC_CS, OUTPUT);
  digitalWrite(DAC_CS, HIGH);
  SPI.setBitOrder(MSBFIRST);
  DACtimer.begin(DACoutSPI, 20);
  //OLEDtimer.begin(OLEDout, 33333);
}


void loop(){
    DACoutSPI(); 
    display.drawLine(random(128), random(64), random(128), random(64), WHITE);
    display.display();
    display.clearDisplay();
}


void DACoutSPI(){     //SPI DAC write
  for(byte channel = 0; channel < totalWaves; channel++){
    wave[channel][wavePhase] = random(4096);
    digitalWrite(DAC_CS, LOW);
    byte formatDacMSB = (channel<<6 | B00010000 | ((highByte(wave[channel][wavePhase])) & B00001111));
    byte formatDacLSB = (lowByte(wave[channel][wavePhase]));
    SPI.transfer(formatDacMSB);
    SPI.transfer(formatDacLSB);
    digitalWrite(DAC_CS,HIGH);
  }
}
 
I tried the i2c version (same version as I have now but you just connect some jumper pads on the back to switch between i2c and spi). With i2c the display runs so slow that my dac writes and the core waveform generation code get slowed way down. IE: the screen writes take longer each one and thus it hangs each time for everything else.

i see. depending on how slow, i guess getting to work DMA for the DAC (https://github.com/crteensy/DmaSpi ?) might help in that case, in as much you'd presumably free up some cpu time the thing can spend talking to the display.

but as long as the PIT thing + priorities works ... i'm using u8glib myself and have no need for kHz sampling rates in this case so i never really looked into sharing the SPI bus efficiently/effectively. i must admit i don't quite get the test code though. why are you calling DACoutSPI in loop()? wouldn't that be your ISR? presumably you could speed it up by using SPIFIFO.


edit. as for transactions, see Paul's write up here: http://dorkbotpdx.org/blog/paul/spi_transactions_in_arduino

and info dispersed over the forum.
 
Last edited:
Status
Not open for further replies.
Back
Top