Go Down

Topic: Faster SPI communication? (Read 1 time) previous topic - next topic

danny92

Greetings,

Anyone knows if there's a faster SPI library for Arduino Due, or a faster way handling SPI communication. The TurboSPI library functions should be faster, but they are actually slower compared to those of the SPI library. The Due has a 84 MHz clock, why this functions need more than 100 clock cycles?

All SPI transfer functions seem to take at least about 2 or 3 microseconds.

Is it possible to use for exemple an external SPI DAC, an external SPI ADC and an SD card "at the same time"?
The DueTimer library disables interrupts inside the callback functions? I can't communicate with SPI hardware inside the timer callback functions! Why?

Regards,
Daniel

PieterP

You'll probably want to look into DMA (direct memory access).
AFAIK, you can't use it "at the same time", because every time the SD card's CS pin goes high, the file is closed.

Pieter

ard_newbie

See this thread, reply #36 (TurboSpi 42 MHz):


https://forum.arduino.cc/index.php?topic=437243.30

You can have several SPI slaves connected on the same SPI bus, but the SPI Master will not address all the slaves exactely at the same time.

BTW, you can have two more SPI buses on USART0 and USART1, but you will have to program your own code since SPI Library is only for the ICSP.

danny92

You'll probably want to look into DMA (direct memory access).
AFAIK, you can't use it "at the same time", because every time the SD card's CS pin goes high, the file is closed.

Pieter
Greetings,

Thank you for clarifing this question.

So that means you can't save samples to a file using an external SPI ADC, or read samples and send them to an external SPI DAC?

I need for example an external SPI RAM chip?
If I use one I could copy the contents of the file and then read them from the RAM?
And I could save the samples from the ADC to the RAM and then write them in the SD card.
The problem is I need a very large RAM to store the files (about 50-100 MB).

Regards,
Daniel

DrDiettrich

Please understand that (in your case) SPI limits the maximum transfer rate, not the connected devices.

For best throughput you should use different (independent) transmission devices or protocols, like Serial or I2C in addition to SPI.

danny92

Is it possible to use both DAC channels in free running mode on Arduino Due?

ard_newbie

The TurboSPI library functions should be faster, but they are actually slower compared to those of the SPI library.
Wrong

So that means you can't save samples to a file using an external SPI ADC, or read samples and send them to an external SPI DAC?
No

Is it possible to use both DAC channels in free running mode on Arduino Due?
Yes - With a DUE clocked at 84 MHz, the DACs output frequency in free running mode is 1.68 MHz.

danny92

#7
Mar 06, 2018, 11:07 pm Last Edit: Mar 06, 2018, 11:19 pm by danny92
Wrong
I've tested both libraries using micros() and the turboSPI library was actually slower. What Am I doing wrong?

No
You'll probably want to look into DMA (direct memory access).
AFAIK, you can't use it "at the same time", because every time the SD card's CS pin goes high, the file is closed.
SD card uses SPI and requires the CS to be low, otherwise the file will close, unless you use a huge external RAM, reading or writing directly to or from the SD from a ADC or to an DAC, seems impossible. Am I wrong?

Yes - With a DUE clocked at 84 MHz, the DACs output frequency in free running mode is 1.68 MHz.
The problem is I have no idea how to do that.

DrDiettrich

In theory you could wire the ADC MISO to the SD card MOSI, and the SD card MISO to the DAC MOSI. Then you can transfer data from the ADC to the SD card, and from there to the DAC, by enabling (CS) the right devices at the same time. In practice all MOSI are tied together, and all MISO, and all data transfers go between a device and memory.

ard_newbie

The problem is I have no idea how to do that.

See this thread, reply #8 for an example sketch of an output on DAC 1 from an input ADC conversion:

https://forum.arduino.cc/index.php?topic=527853.0

A tip to write your own code, read DAC section of Sam3x datasheet.


danny92

Thank you so much. I've seen the thread you suggested, and I've read the DAC part of the datasheet, it's interesting the things you can do if you know more about the hardware you're using.

Anyway, I'm using the DAC in free running mode but there are 3 strange things:

- 1st -> if I don't disable interrupts after setting the DAC registers the voltage values are not full swing, I'm only getting values between 0.9 V and 2.3 V on both channels
- 2nd -> even disabling interrupts after setting DAC values, the voltage swing is only between 0.6 V and 2.65 V on channel 1 (I've checked with analogWrite and it should be betwen 0.55 V and 2.71 V), on channel 0 is spot on (0.55 V to 2.71 V)
- 3rd -> my code still doesnt' work with 44100 Hz sampled wave files, only 32000Hz and I've no idea why this happens, because the DAC values are changed within 2-3 microseconds, I think there's something wrong with my code. There's no way I can find a solution to this.  :(

Could you help me finding the bug!  :smiley-razz:

I also want to try a library I found, made by TMRh20, that implements a SPI using the UART, do you know if this library really works as supposed? Any examples? I want to try my code using an external DAC.

Links to the library
https://github.com/arduino/Arduino/issues/5605
https://github.com/TMRh20/Sketches

ard_newbie

#11
Mar 08, 2018, 05:49 am Last Edit: Mar 08, 2018, 08:04 am by ard_newbie
You can't use the DAC peripheral in free running mode AND have a frequency conversion of 44.1 KHz ! you have to choose between free running mode and hardware trigger.

Here is an example sketch to trigger ADC conversions with a 44.1 KHz frequency. You can easily adapt it for DAC conversions :

Code: [Select]

/******************************************************************/
/*      ADC conversions triggered by a Timer Counter 0 Channel 2  */
/*                Frequency = 44.1 KHz                            */
/******************************************************************/
volatile uint16_t LastValueRead;
volatile boolean FlagReadAvailable;
const uint32_t BufferSize = 1000;    
uint16_t Buffer[BufferSize];        // For 12_bit conversions

void setup()
{
  Serial.begin(250000);
  adc_setup();
  tc_setup();
}

void loop()
{
  uint32_t Index;
  Index = 0;
  printf(" Start of %d conversions\n", BufferSize);
  while (Index < BufferSize) {
    if (FlagReadAvailable == true) {
      FlagReadAvailable = false;
      Buffer[Index] = LastValueRead;
      Index++;
    }
  }
  printf(" End of %d conversions\n", BufferSize);
  delay(1000);
}

/*************  Configure ADC function  *******************/
void adc_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;                    // ADC power on
  ADC->ADC_CR = ADC_CR_SWRST;                           // Reset ADC
  ADC->ADC_MR |=  ADC_MR_TRGEN_EN                       // Hardware trigger select
                  | ADC_MR_TRGSEL_ADC_TRIG3;            // Trigger by TIOA2


  ADC->ADC_IER = ADC_IER_EOC7;                         // Interrupt on End of Conversion channel 7
  ADC->ADC_CHER = ADC_CHER_CH7;                        // Enable ADC CH7 = A0

  NVIC_EnableIRQ(ADC_IRQn);                            // Enable ADC interruptions

}

void ADC_Handler(void) {
  LastValueRead = ADC->ADC_CDR[7];   // Read ADC conversion of channel 7
  FlagReadAvailable = true;
}

/*************  Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2  ************/
void tc_setup() {

  PMC->PMC_PCER0 |= PMC_PCER0_PID29;                      // TC2 power ON : Timer Counter 0 channel 2 IS TC2
  TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2  // MCK/8, clk on rising edge
                              | TC_CMR_WAVE               // Waveform mode
                              | TC_CMR_WAVSEL_UP_RC        // UP mode with automatic trigger on RC Compare
                              | TC_CMR_ACPA_CLEAR          // Clear TIOA2 on RA compare match
                              | TC_CMR_ACPC_SET;           // Set TIOA2 on RC compare match


  TC0->TC_CHANNEL[2].TC_RC = 238;  //<*********************  Frequency = (Mck/8)/TC_RC  Hz = 44.117 Hz
  TC0->TC_CHANNEL[2].TC_RA = 40;  //<********************   Any Duty cycle in between 1 and 238

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable

}



Plus you are not using the TAG option properly to output conversions on DAC0 and DAC1. This should be something like this :

Code: [Select]

uint16_t val0, val1;
val0 = val0 & 0xFFF;  // 12-bit value
val1 = val1 & 0xFFF;
uint32_t ValToDac;    // 32-bit value to convert on DAC0 and DAC1

// Bits 12 and 28 select the DAC outputs
// Note that (0 << 28) is not useful here
uint32_t chsel = (1 << 12) | (0 << 28);  

// The low part will be converted on DAC1, the high part on DAC0
ValToDac = val1 << 16 | val0 | chsel;




For a full swing of your builtin DACs, see this tutorial:
http://ardupiclab.blogspot.fr/2015/07/how-to-modify-analog-output-range-of.html

If you want to use an external DAC, Google DAC DUE SPI and you will find a relevant .pdf from chianorobotics.


danny92

Greetings

Thank you for the examples, but I don't understand how the DAC handler function is called, neither what name should it have, I can only hear static, the DAC is not running at all, what Am I doing wrong? Could you please take a look at my code? I'm confused.  :smiley-confuse:

I'm still having the voltage swing issues, even using your example code, could you test it, and see if it also happens with your arduino?

If you want to use an external DAC, Google DAC DUE SPI and you will find a relevant .pdf from chianorobotics.
Thank you, but I've already tried that configuration, and I've those DACs, the MCP4922, but the problem is, I can't use them when I'm reading a file from the SD card unless I use a software defined SPI or UART SPI, because the SD card's CS can't go low without closing the file. Any suggestions to solve this problem, I don't know much about software SPI or UART SPI.

Wav file reader code that it's not working: DumpAudioFile5_optimized_dma.ino
Voltage swing not reaching limits on channel 1: sketch_mar08a.ino

Regards,
Daniel


ard_newbie

#13
Mar 09, 2018, 07:57 am Last Edit: Mar 09, 2018, 08:21 am by ard_newbie
I did some modifications in your sketch to show DACs correct behavior (always attach a 1 or 2 K resistor in serie to each DAC !):

Code: [Select]

volatile uint16_t dac0Value = 0;
volatile uint16_t dac1Value = 4095;

void dac_setup () {

  PMC->PMC_PCER1 = PMC_PCER1_PID38;     // DACC power ON

  DACC->DACC_CR = DACC_CR_SWRST ;       // Reset DACC

  DACC->DACC_MR = DACC_MR_TRGEN_DIS                   // Free running mode
                  | DACC_MR_TAG_EN                    // enable TAG to set channel in CDR
                  | DACC_MR_WORD_WORD                 // write to both channels
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_IER |= DACC_IER_EOC;                     // Interrupt on End Of Conversion

  // setup analog current
  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b10)
                   | DACC_ACR_IBCTLCH1(0b10)
                   | DACC_ACR_IBCTLDACCORE(0b01);

  NVIC_EnableIRQ(DACC_IRQn);               // Enable DACC interrupt

  DACC->DACC_CHER = DACC_CHER_CH0          // enable channel 0 = DAC0
                    | DACC_CHER_CH1;       // enable channel 1 = DAC1
}

void DACC_Handler() {
  uint32_t ValToDac;    // 32-bit value to convert on DAC0 and DAC1

  // Bits 12 and 28 select the DAC outputs
  // Note that (0 << 28) is not useful here
  const uint32_t chsel = (1 << 12) | (0 << 28);

 // Reading DACC_ISR clears the EOC bit
 // enabling DACC_Handler() to be triggered again
  DACC->DACC_ISR;

  // This can be done elsewhere in loop() ******
  //dac0Value &= 0x0FFF;  // 12-bit value
  //dac1Value &= 0x0FFF;

  // The low part will be converted on DAC1, the high part on DAC0
  ValToDac = dac0Value << 16 | dac1Value | chsel;
  DACC->DACC_CDR = ValToDac;
}

void setup() {
  //Serial.begin(250000); // Always use the maximum output baudrate
  // while (!Serial);  // Useless with Serial, useful with SerialUSB
  dac_setup();
  
  // DACC_Handler(); // Don't call DACC_Handler () "manually" ***************
}


void loop() {

  dac0Value &= 0x0FFF;  // 12-bit value
  dac1Value &= 0x0FFF;
}


Some SD card devices do not tri-state correctly in SPI mode, see this tutorial:

https://www.dorkbotpdx.org/blog/paul/better_spi_bus_design_in_3_steps

danny92

#14
Mar 09, 2018, 06:37 pm Last Edit: Mar 09, 2018, 06:52 pm by danny92
Greetings,

If I use the code you suggested, the DAC outputs stay at 0 V. The DACs don't work at all. I've tested the code. If I call DAC_handle manually the DACs work, but the voltages are 0.9 V and 2.3 V (and not 0.5 and 2.7 V), I'm using a 10k resistor in series with the DAC output.

Regards,
Daniel

Go Up