Go Down

Topic: Continuous SPI for multiple bytes (Read 2738 times) previous topic - next topic

mcleung

Mar 28, 2014, 10:39 pm Last Edit: Apr 02, 2014, 03:27 pm by mcleung Reason: 1
As of April 2, 2014, this has been fixed using DMA SPI. See reply #15 for working copy of the code.
DMA SPI obtained from
https://github.com/manitou48/DUEZoo/blob/master/dmaspi.ino



Hi guys,

I'm trying to program a ship that requires 4 wire serial programming (essentially SPI)
For those who are curious, it's the LM96570.

The chip uses up to 80MHz on the serial line.
I've been able to create a simple program for my Due to communicate with the chip.
The issue I'm having is that although the SPICONTINUE works as it is intended to (by leaving the LE low), the data is transmitted in little "burts" of information, one byte (8 bits) at a time, then a long delay before the next byte.
The clock flips as I expect it to, but again, flips 8 times and then there is a long pause.

I am suspecting that it's causing mis-communication problems with my IC chip, and the lowest I've been able to achieve a constant clock is down at 800KHz. I was wondering if any of you had any luck pushing continuous SPI communication to tens of MHz without these long pauses between bytes.

Any suggestions to make my code more efficient is also welcomed.
I am passing the bytes from a python serial script.

Code: [Select]
#include <SPI.h>
// Define SPI Pins
// Can only use 4 10 and 52
#define LEPin 10


// Save Words to write to API
// Integer is 4 byte in Due (32 bits)
// Worst Case scenario is need to write 70 bits (5 addr + 0 + 64 data)
unsigned char Word1 = 0xAA;
unsigned char Word2 = 0xFF;
unsigned char Word3 = 0x00;
unsigned char Word4 = 0x00;
unsigned char Word5 = 0x00;
unsigned char Word6 = 0x00;
unsigned char Word7 = 0x00;
unsigned char Word8 = 0x00;
unsigned char Word9 = 0x00;


void setup()
{
 // Set up Serial communication (Computer)
 Serial.begin(115200);
 Serial.println("Initialisation Started");
 
 // Set up SPI communication (Chip)
 SPI.begin(LEPin);
 SPI.setClockDivider(LEPin, 42); // Desired 80 MHz Due is 84, so try div 2
 // 84 should correspond to 1MHz
 SPI.setBitOrder(LEPin, MSBFIRST);
 
 // TODO: SPI Set Data Mode http://arduino.cc/en/Reference/SPISetDataMode
 SPI.setDataMode(LEPin, 0);
 
 Serial.println("Initialisation Completed");
}


void loop() {
 if (Serial.available()) {
   serialRead();
 }
}

/*
 SerialEvent occurs whenever a new data comes in the
hardware serial RX.  This routine is run between each
time loop() runs, so using delay inside loop can delay
response.  Multiple bytes of data may be available.
*/
void serialRead() {
 Serial.println("    "); // Unknown why it's required
 
 //Serial.print("READ");
 Word1 = Serial.read();
 Word2 = Serial.read();
 Word3 = Serial.read();
 Word4 = Serial.read();
 Word5 = Serial.read();
 Word6 = Serial.read();
 Word7 = Serial.read();
 Word8 = Serial.read();
 Word9 = Serial.read();
 //Serial.print("READSUCESS");
 
 SPI.transfer(LEPin, Word1, SPI_CONTINUE);
 SPI.transfer(LEPin, Word2, SPI_CONTINUE);
 SPI.transfer(LEPin, Word3, SPI_CONTINUE);
 SPI.transfer(LEPin, Word4, SPI_CONTINUE);
 SPI.transfer(LEPin, Word5, SPI_CONTINUE);
 SPI.transfer(LEPin, Word6, SPI_CONTINUE);
 SPI.transfer(LEPin, Word7, SPI_CONTINUE);
 SPI.transfer(LEPin, Word8, SPI_CONTINUE);
 SPI.transfer(LEPin, Word9, SPI_LAST);
   
 // Return 1 so computer knows that Arduino is ready to receive next command
 delay(1000);
 Serial.print("1");
 //Serial.print("DONE");
}

Magician

I'm not sure about 10th of MHz, but to go faster you better use DMA + SPI channel, w/o a library.  Look inside spi.c file for start.

Graynomad

Code: [Select]
  if (Serial.available()) {
    serialRead();
...
  Word1 = Serial.read();
  Word2 = Serial.read();
  Word3 = Serial.read();
  Word4 = Serial.read();
  Word5 = Serial.read();
  Word6 = Serial.read();
  Word7 = Serial.read();
  Word8 = Serial.read();
  Word9 = Serial.read();

You wait for what is almost certainly a single byte then read 9 bytes from the serial port, that doesn't seem kosher.

The whole point of sync serial comms is that it's not timing dependant, gaps between bytes should have no effect.

How about you test one thing at a time, try sending hard-coded bytes to the chip, when that works prove that you are receiving bytes correctly, then join the two up.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

dlloyd

#3
Mar 29, 2014, 02:39 am Last Edit: Mar 29, 2014, 02:51 am by dlloyd Reason: 1
Try this line right after SPI.setDataMode ...
Code: [Select]
SPI0->SPI_CSR[0] = SPI0->SPI_CSR[0] & 0xFFFFFF; // clear delay between consecutive transfers (DLYBCT = 0)

Note: The default (DLYBCT = 1) keeps CS enabled for 32 MCLK after a completed transfer. Some device needs that for working properly (from spi.cpp).

MarkT


The whole point of sync serial comms is that it's not timing dependant, gaps between bytes should have no effect.


You don't mean synchronous serial, you mean SPI.  Synchronous serial is stuff like I2S
that can be driven by the SSC module and for which timing is everything.
[ I won't respond to messages, use the forum please ]

mcleung

I'll give this a try and report back to you guys.
I've posted the corresponding question to the TI E2E forums, and I'm awaiting their responses.
http://e2e.ti.com/support/applications/high_reliability/f/30/t/331309.aspx

I would hope that the clock doesn't need to be synchronous (hence why it needs a clock line) but we will see what the TI Engineers say. Hopefully I don't need to port my system to an FPGA to meet the timing requirements, I prefer programming with Arduino :)

Graynomad:  I know, for now I am transferring a total of 9 bytes, but eventually I want to be able to transfer an arbitrary number of bytes.


Try this line right after SPI.setDataMode ...
Code: [Select]
SPI0->SPI_CSR[0] = SPI0->SPI_CSR[0] & 0xFFFFFF; // clear delay between consecutive transfers (DLYBCT = 0)

Note: The default (DLYBCT = 1) keeps CS enabled for 32 MCLK after a completed transfer. Some device needs that for working properly (from spi.cpp).


Magician

I wander, what they may respond, if there is an ARDUINO library experts working in TI ?

Graynomad

Quote
I know, for now I am transferring a total of 9 bytes, but eventually I want to be able to transfer an arbitrary number of bytes.

Either way the code is wrong, you can't just do N Serial.reads() unless you know there are N characters to read.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

dlloyd

To elaborate a little more about changing DLYBCT, I've been able to communicate with SPI using 0 delay between transfers at a clock speed of 12 MHz and temporarily at 30 MHz, but my interconnect leads were too long to maintain 30 MHz reliably. A major difference with my testing is that I had the Due in SPI slave mode and directly utilized the SPI registers. The master (which controlled the SCLK rate) was an FTDI 2232H interface.

Note that Due's SPI can be set for 16 bit (maximum) transfer mode. When transferring more than 16 bits, there needs to be enough time to check the flags and read the Receive Data Register (a few MCLK cycles). This may limit the maximum SCLK rate for reliable transmission, but perhaps 21 MHz would work when the delay between transfers is set to 0.

The delay between transfers by default will put a 32 MCK gap between every transfer. It can only be changed in steps of 32 MCKs.

The drawback for large bit size continuous transfers is that low level programming using the SPI User Interface is required.

mcleung

@dlloyd, I'm not too familiar with low level programming on micro-controllers. I'll try implementing your one line change tomorrow. But from my understanding is that the SPI library coded the delay, and therefore I can remove/override it with DLYBCT = 0.

I also found this DMA SPI on Github. Would I be able to use it? (I'm trying to understand the arrow notation -> )
https://github.com/manitou48/DUEZoo/blob/master/dmaspi.ino

Arduino community is awesome, I get answers much faster here than with TI  :)

dlloyd

Yes, the one line will only change DLYBCT to 0 and keep all other settings. I'm still quite green at "C" and digging under the hood of Arduino. Just starting to use the arrow notation in spi.c. To me, its descriptive enough, but knowing where all the variables are set and a list of all possible arrow notation commands would be good to have. I find ATMEL's ASF and website good resources also.

I've also seen the DMA SPI on Github. I think DMA would be beneficial for your application, especially as you get in to the high end of SPI clock rates. Unfortunately, I've never used it and am waiting for more examples.

Good luck with your project ... and by the way, All I can guarantee is that the code "should" copy and paste OK - that's it. (after seeing that the LM96570 chip is used medical ultrasound applications)  ;)

Graynomad

Quote
You don't mean synchronous serial, you mean SPI.

Any serial comms with an accompanying clock signal is synchronous AFAIK.

Wiki
Quote
The Serial Peripheral Interface or SPI bus is a synchronous serial data link,


Arduino reference
Quote
Serial Peripheral Interface (SPI) is a synchronous serial data protocol


______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

mcleung

One line solution did not help.

DMA found at https://github.com/manitou48/DUEZoo/blob/master/dmaspi.ino works perfectly.
I tested at 42MHz (Div by 2)

Still didn't manage to program the chip, but I don't think it's Arduino's fault anymore, probably my wires can't handle the high frequency.
See scope obtained, I transmitted 0xAAAA 0xAAAA ... etc
Channel 1 is the MOSI, at 42 MHz
Channel 2 is the latch enable which is pulled low!

Thanks for your help everyone :)


Graynomad

Yikes.

Quote
probably my wires can't handle the high frequency.

I would say so.

______
Rob
Rob Gray aka the GRAYnomad www.robgray.com

dlloyd

#14
Apr 02, 2014, 03:26 am Last Edit: Apr 02, 2014, 03:30 am by dlloyd Reason: 1
Just some observations:

From my understanding, it looks like latch enable (Channel 2) is your CS line which is continuously held low and never goes high to end the transfer which prevents the chip from being programmed.

dmaspi.ino from GitHub says "a year ago" and this sketch is always controlling the CS (pin 10) independently with digitalWrite. However, Arduino (newer 1.5X versions) already automatically controls CS so it looks like this could create a conflict (especially if line 3 is changed to  #define USE_ARDUINO_SPI_LIBRARY 1)

http://arduino.cc/en/Reference/DueExtendedSPI

33.7.3.9 Peripheral Deselection with DMAC (page 691 in the datasheet):
It looks like there's some relevant info here as to why NPCS does not rise in all cases.

Go Up