Page 1 of 1

SPI has delays and quirky line behaviour

Posted: Wed Apr 16, 2014 1:27 pm
by goeck
Hey everyone,

I need a hint on the SPI peripheral optimization. I am using a L6470 dSPIN device at the SPI bus attached to a STM32F0Discovery, which pretty much works. If I look at the lines, I see ugly stuff though and I want to get rid of that, maybe one has some advices for me. Please have a look at the following screenshot showing a 4byte long communication with a selected L6470. I see some quirks:
    - MOSI line flips around in between transmission of single bytes (looks the same on a scope)
    - time between transmission of each byte takes very long
    - timespan from sending byte done until ENABLE goes high is very long
The L6470 has to be supplied with a ENABLE line double toggle after each byte is sent out, the high timespan has to be at least 800ns. If I do a spiExchange of N=4 bits this works yust like expected, but the L6470 just doesn't understand this. Basically I thought of just interrupting the quick transfer of bytes only by 1-2us. It turns out, I can't narrow the timegap closer than 28us :-(

The code looks like this

Code: Select all

/**
 * SPI1 configuration structure.
 * CPHA=0, CPOL=0, 8bits frames, MSb transmitted first.
 */
static SPIConfig dspin_spi_config = {
   NULL,
   /* HW dependent part.*/
   DSPIN_CS_PORT,
   DSPIN_CS_PIN,
   SPI_CR1_BR_0 | SPI_CR1_BR_1
};

/**
  * \brief  Transmits/Receives one byte to/from dSPIN over SPI.
  * \param  byte to be transmitted
  * \return Received byte
  */
/**
 * Acquires SPI bus for further transmissions.\n
 * Starts the SPI engine and selects a client -> dSPIN device
 */
void dSPIN_Acquire_Bus(void) {
   spiStart(&SPID1, &dspin_spi_config);
   spiSelect(&SPID1);
}

/**
 * Releases SPI bus.
 */
void dSPIN_Release_Bus(void) {   
   spiUnselect(&SPID1);
   spiStop(&SPID1);
}

uint8_t dSPIN_Write_Byte(uint8_t byte)
{
   uint8_t spiBufferTx[1] = { 0 };
   uint8_t spiBufferRx[1] = { 0 };
   spiBufferTx[0] = byte;

   dSPIN_Acquire_Bus();
   spiExchange(&SPID1, 1, spiBufferTx, spiBufferRx);
   dSPIN_Release_Bus();

   return spiBufferRx[0];
}
int main(){
   halInit();
   chSysInit();

   palSetPadMode(   SPI_MISO_PORT, SPI_MISO_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPadMode(   SPI_MOSI_PORT, SPI_MOSI_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPadMode(   SPI_SCK_PORT,  SPI_SCK_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPadMode(   DSPIN_CS_PORT,   DSPIN_CS_PIN,
               PAL_MODE_OUTPUT_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
   palSetPad(      DSPIN_CS_PORT, DSPIN_CS_PIN);
   spiObjectInit(&SPID1);

   spiAcquireBus(&SPID1);

   /* Send RUN operation code to dSPIN */
   dSPIN_Write_Byte(dSPIN_Command_RUN | direction);

   /* Send speed - byte 2 data dSPIN */
   dSPIN_Write_Byte((uint8_t)(speed >> 16));

   /* Send speed - byte 1 data dSPIN */
   dSPIN_Write_Byte((uint8_t)(speed >> 8));

   /* Send speed - byte 0 data dSPIN */
   dSPIN_Write_Byte((uint8_t)(speed));

   spiReleaseBus(&SPID1);

   while(TRUE){
      chThdSleepSeconds(2);
   }
}


I tried to post as a attachement the conf headers, but nothing gets through. If desired I paste the content or upload the elsewhere.

I would be evry happy to optimize this behaviour and get to a datasheet-clean communication.
Thanks in any case!

Cheers
Stefan

Re: SPI has delays and quirky line behaviour

Posted: Wed Apr 16, 2014 4:10 pm
by Giovanni
You don't need to acquire the bus and release it for each byte, if you don't access the SPI from multiple threads you can omit acquire/release at all.
Also, you may use send/receive where appropriate, exchange is a bit heavier.

I don't understand the problem with L6470, there should be no reason it should not accept a continuous transmission. This makes me think you also have some HW problem there.

Giovanni

Re: SPI has delays and quirky line behaviour

Posted: Thu Apr 17, 2014 12:15 pm
by goeck
Hey Giovanni,

thanks for your thoughts.
I looked up the datasheet SPI section of the L6470 and have to say, I was right.
datasheet_L6470_SPI.png
datasheet_L6470_SPI.png (25.37 KiB) Viewed 8091 times


I also deleted the aquire-functions, but nothing changed. Also I made use of only a send function, instead of a exchange, no improvement. I even used the code without the actual data sending/exchanging function, so only this was left in the communication

Code: Select all

spiStart();
spiSelect();
spiUnselect();
spiStop();

Still the quirks and the timelags where there. One thing that I gathered from this was, I saw a certain fixed pattern on the MOSI line, that must come from the four left functions and the lld driver (I guess). Looking into it I only ended up with some dma dealing functions and thread waiting and waking... I saw nothing suspicious.

Any comments are highly appreciated
Cheers

Re: SPI has delays and quirky line behaviour

Posted: Thu Apr 17, 2014 1:01 pm
by Giovanni
You don't need to start/stop for each transfer. Starting and stopping is a very heavy operation because it configures the DMAs, clocks and the SPI itself, you should just start the driver then use select-send/receive/exchange-unselect.

Giovanni

Re: SPI has delays and quirky line behaviour

Posted: Thu Apr 17, 2014 1:08 pm
by goeck
Hey Giovanni,

I also want to use I2C in the project... As far as I read, I have to start/stop each DMA demanding service for each usage, so they don't interfere with each other and do fights over hardware usage.

Re: SPI has delays and quirky line behaviour

Posted: Thu Apr 17, 2014 3:54 pm
by Giovanni
I don't see other solution except using non-conflicting peripherals. Can you change ports?

Giovanni

Re: SPI has delays and quirky line behaviour

Posted: Tue Apr 22, 2014 9:08 am
by goeck
Hey,

I just figured out, I could use polled exchange (for testing purposes at least), since it's implemented in the LLD.
See what I've got with that portion of code:

Code: Select all

uint8_t dSPIN_Write_Byte(uint8_t byte)
{
   static uint8_t spiBufferTx[1] = { 0 };
   static uint8_t spiBufferRx[1] = { 0 };
   spiBufferTx[0] = byte;

   dSPIN_Acquire_Bus();
//   spiExchange(&SPID1, 1, spiBufferTx, spiBufferRx);
   spiBufferRx[0] = spi_lld_polled_exchange(&SPID1, byte);
   dSPIN_Release_Bus();

   return spiBufferRx[0];
}

Whole transmission takes 25us.
Conclusion would be, exactly the comment of spi_lld_polled_exchange(): laying calling thread asleep and awaking it after spiExchange has finished is too expensive when doing single byte transfers?

Cheers

Re: SPI has delays and quirky line behaviour

Posted: Tue Apr 22, 2014 9:42 am
by Giovanni
Hi,

That is the reason for the spiPolledExchange(), it is convenient for exchanging single bytes at high speeds.

Giovanni

Re: SPI has delays and quirky line behaviour

Posted: Wed Apr 23, 2014 2:52 pm
by goeck
Ah, nice.
I just read the Programming Manual, Reference Manual and Datasheet of the STM32 and found something that saved my day.
There is a SPI feature called NSS Pulse, which actually exactly does what I want. After each byte in a consecutive byte sending, a toggle is executed on the NSS pin, one has to use hardware SlaveSelect though. In my case I either way already used the corect pin (SPI1: PA4). So in my SPI init routine i did not init the CS port/pin as output_push_pull, but as well as all other SPI pins as alternate function 0.
See what I've got: transmission time down to 13us :-)

Here's the relevant code:

Code: Select all

/**
 * SPI1 configuration structure.
 * CPHA=0, CPOL=0, 8bits frames, MSb transmitted first.
 * NSS Pulse mode (due to SPI @ L6470)
 */
static SPIConfig dspin_spi_config = {
//   spiSingleByteDone,
   NULL,
   /* HW dependent part.*/
   DSPIN_CS_PORT,
   DSPIN_CS_PIN,
   SPI_CR1_BR_1 | SPI_CR1_BR_0,   // Speed selection:  f_PCLK / 16
   SPI_CR2_NSSP               
};

/**
 * Initialise and start SPI hardware.
 */
void initSPI(void){
   palSetPadMode(   SPI_MISO_PORT, SPI_MISO_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPadMode(   SPI_MOSI_PORT, SPI_MOSI_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPadMode(   SPI_SCK_PORT,  SPI_SCK_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);

   // Use hardware NSS pin (software usage would lead to much
   palSetPadMode(   DSPIN_CS_PORT,   DSPIN_CS_PIN,
               PAL_MODE_ALTERNATE(0) | PAL_STM32_OSPEED_HIGHEST);
   palSetPad(      DSPIN_CS_PORT, DSPIN_CS_PIN);

   spiStart(&SPID1, &dspin_spi_config);
}

int main(void){
    ...

   uint8_t tx[4],rx[4];
   tx[0] = dSPIN_Command_RUN | direction;
   tx[1] = (uint8_t)(speed >> 16);
   tx[2] = (uint8_t)(speed >> 8);
   tx[3] = (uint8_t)(speed);

   spiExchange(&SPID1, 4, tx, rx);


Thanks for the thoughts, Giovanni.
Cheers
Stefan

Re: SPI has delays and quirky line behaviour

Posted: Wed Apr 23, 2014 3:38 pm
by Giovanni
Interesting solution, I never tested that mode directly on the STM32.

Giovanni