SPI without delays

18 Jul 2013

Hello ! I am very beginner with the mbed and I would like to program a SPI communication between the MBED and my product. My problem is that I do not know how to set properly the different registers.

What I did is:

#include "mbed.h"

int angle;

int main()
{
    // BitEnable=1, CPHA=1, CPOL=1, MSTR=1, LSBF=0, SPIE=0, BITS=0000
    LPC_SPI->SPCR = 0x0000003C;

    //Function 3 for pin15 (SCK)
    LPC_PINCON->PINSEL0 = 0xC0000000;

    //Function 3 for pins 16,17,18 (SSEL,MISO,MOSI)
    LPC_PINCON->PINSEL1 = 0x0000003F;

    //Set clock frequency to a random value
    LPC_SPI->SPCCR = 0x00000008;
          
    while(1)
    {
        //Write the Data register to start a SPI transfer
        LPC_SPI->SPDR = 0xAAAAAAAA;
    }
}

When I compile it, I do not get any error but when I boot it on the MBED, the 4 LEDs are blinking and I do not see anything on the oscilloscope.

Could someone help me fixing this code ? The only thing I want to do is perform a SPI transfer with a SCK frequency of 20MHz.

My first idea was to use something like:

#include "mbed.h"
 
SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p8);
 
int main()
{
    spi.format(16,3);
    spi.frequency(25000000);
    cs=1;

    while(1)
    {

       cs = 0;
       spi.write(0x8888);
       cs = 1;
    }
}

But If I do like this then I obtain something like 1us delay between the chip select falling edge and the first clock rising edge. This is a delay I can not afford because my product is a 500kHz refresh rate sensor so I really need a fast SPI communication.

Thanks in advance to those who will be able to help me.

Regards, Sebastien

18 Jul 2013

The 4 leds are blinking in the error pattern? (So outside 2 on, then inside 2 on)? Didn't know it could do that without error function being called by a library (which can't happen in your top code), but maybe fault handler is also linked to that pattern.

Does your product require CS line to be raised between every communication?

Back to your top code, without testing it on my mbed yet, some things: SPI block is legacy, SSP is in general better (mbed libraries also use it). For example they have 8 deep FIFO buffers, which is handy when you want to use it at high speeds.

SPDR is a 16-bit register, well 32-bit, but top 16-bit shouldn't be written one, which is done in your code.

When you start commenting out lines, when does it stop giving the blinking leds?

19 Jul 2013

Hi Erik,

Thanks for your answer. I do not know why but the LEDs stopped blinking. However, I still do not get anything on the oscilloscope.

For my project, I can not use SSP, I really need to establish a SPI communication.

To make a long story short, here is exactly what I need to do: The product is a magnetic angle sensor. Each time angle is ready to be measured (500kHz frequency), I need to send a SPI communication with 16 bits in order to read the angle.

This is why I can not "live" with the delays I mentioned in my first post.

What I need to program is: - a while loop with an interrupt pin connected to my "data ready" signal - at the rising edge of this signal, I send a 16 bits SPI communication with a clock frequency at 40MHz for example.

I need to do that playing with the PINSEL0 and PINSEL1 registers but also with the SPCR register to set CPHA, CPOL, etc...

How can I do that ?

19 Jul 2013

SSP == SPI. It is just the new version of the SPI peripheral with more options, such as a FIFO buffer.

It might be easier if you make an mbed SPI object on the pins you need, and then manually set some registers. That way besides the CS pin, the pins are already set, you can set the format the easy way, etc. You only need to manually write a few registers. And yeah then you do need to use the SSP peripheral, but I don't see why you can't use SSP.

19 Jul 2013

Maybe I am confusing between SSP and SPI. My real problem is that I do not know how to manually write the registers.

If I refer to my first post, I copied the following code:

#include "mbed.h"

int angle;

int main()
{
    // BitEnable=1, CPHA=1, CPOL=1, MSTR=1, LSBF=0, SPIE=0, BITS=0000
    LPC_SPI->SPCR = 0x0000003C;

    //Function 3 for pin15 (SCK)
    LPC_PINCON->PINSEL0 = 0xC0000000;

    //Function 3 for pins 16,17,18 (SSEL,MISO,MOSI)
    LPC_PINCON->PINSEL1 = 0x0000003F;

    //Set clock frequency to a random value
    LPC_SPI->SPCCR = 0x00000008;
          
    while(1)
    {
        //Write the Data register to start a SPI transfer
        LPC_SPI->SPDR = 0xAAAAAAAA;
    }
}

Is this the good way to change manually the registers ?

Then in my infinite loop, I try to access manually the data register by writing something in it to start the SPI communication.

I guess that something is missing because I can not see anything on the oscilloscope but my problem is that I do not see what is missing or wrong in this code.

19 Jul 2013

That is the correct way to manually address registers yes. Possible problem with your code (besides that you aren't allowed to write 0xAAAAAAAA to the data register), is that probably the peripheral clock is not activated for the SPI module. Which is why I would start by just defining an mbed SPI object, and then manually writing a few registers. That way the majority of the registers are already set for you.

22 Jul 2013

Hi, Based on your advice, I made the following test:

#include "mbed.h"

SPI spi(p11, p12, p13); // mosi, miso, sclk

int angle;

int main()
{
  
    //LPC_SPI->SPCR = 0x0000003C;
    //LPC_PINCON->PINSEL0 = 0xC0000000;
    //LPC_PINCON->PINSEL1 = 0x0000003F;
    //LPC_SPI->SPCCR = 0x00000008;
    
          
    while(1)
    {
      spi.format(16,3);
      angle = spi.write(0x8888);
      wait_us(1);
    }
}

When I check on the oscilloscope, I see what I expect on the following pins: - pin 11 mosi, I can see 0x8888 - pin 13 sclk I can see the clock

But unfortunately, I can not find any chip select signal.

My guess is that there is something wrong with these lines:

    LPC_PINCON->PINSEL0 = 0xC0000000;
    LPC_PINCON->PINSEL1 = 0x0000003F;

This is my guess because when I put the line "LPC_PINCON->PINSEL0 = 0xC0000000;" as I comment and I "activate" the line "LPC_PINCON->PINSEL1 = 0x0000003F;", then I can see the clock on the oscilloscope (Port0.15, meaning pin 13).

When I do the opposite, meaning when I "activate" the line "LPC_PINCON->PINSEL0 = 0xC0000000;" and when I put the line "LPC_PINCON->PINSEL1 = 0x0000003F;" as a comment, then the pin 13 (clock) is blocked at high state" whereas I can see properly the mosi signal.

So I am sure that my problem is that I am not "activating" properly the port 0.16 (pin 14) as a chip select because I see all other signals but this signal does not work.

Any idea ?

22 Jul 2013

The mbed SPI masters library does not support automatic chip select, so you have to add that yourself. If you try that with those commented lines: mbed uses SSP, not SPI. So writing to SPI registers won't affect the library function.

22 Jul 2013

Then what you mean is that I will not be able to achieve what I want (SPI without the huge delay between ChipSelect down and the first clock) by using the functions from the SPI object ?

So what exactly are the solutions that I can use in order to achieve something similar ?

22 Jul 2013

You can use some functions of the SPI object (mainly those setting format, pins, etc), but besides that you need to manually address the relevant SSP peripheral, not SPI.

You can of course also do it completely manual, but then I think it is harder to do, for example with problems like possibly a peripheral clock not enabled.

Edit: Alternative is to check the spi_api.c source code to see which registers are set by it.

22 Jul 2013

Hi again ! Thanks for your advice, I finally made it work by using a SPI object and by changing manually some SSP registers.

#include "mbed.h"

SPI spi(p5, p6, p7);
int angle;

void main(void)
{
  // initialize SSP
  LPC_SSP0->CR0 = 0x02;  // SSP max speed
  LPC_SSP0->CR0 = 0x000000CF;  // SSP max speed, 8 bits
  LPC_SSP0->CR1 = 0x02;  // SSP master mode
  LPC_PINCON->PINSEL0 |= (2<<18) | (2<<16) | (2<<12) | (2<<10);        //MOSO, MISO,, SCK, SSEL
  
  spi.format(16,3);
  spi.frequency(20000000);
  
  while(1)
  {
    angle = spi.write(0x8888);
  }
}

I do not know if this is the optimal method but at least I get all the signals on my oscilloscope and I do not have this huge delay between the SSEL falling edge and the first clock event.

Thanks a lot for your help Erik, I really appreciate.