SPI example on Kinetis KL16

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SPI example on Kinetis KL16

6,615 Views
marcopalestro
Contributor II

Hello,

     does anybody know where to find an example for configuring SPI as a master and performing write/read synchronously (no interrupts, no dma) on KL16 devices? I've only found a thread showing some tips for DSPI on K10 and similar MCUs, but the peripheral on KL devices seems totally different.

Labels (1)
11 Replies

2,920 Views
ZhangJennie
NXP TechSupport
NXP TechSupport

please refer the processor expert sample code under CW10.5 install\MCU\CodeWarrior_Examples\Processor_Expert\Kinetis\TWR-KL25Z48M\SPI

hope this helps!

0 Kudos
Reply

2,920 Views
marcopalestro
Contributor II

Hello, I'm using IAR Embedded Workbench for developing on Kinetis, is there another way to access the example you're referring to?

0 Kudos
Reply

2,920 Views
perlam_i_au
Senior Contributor I

Just as ZhangJennie mentioned, you can download the FRDM-KL25Z and TWRKL25Z48M Sample Code Package and use the IAR example code as base for your development, you will find it in this path:

C:\(your download path)\KL25 Sample Code\kl25_sc_rev10\klxx-sc-pex\projects\spi_demo\iar


Have a nice day :P,
Perla

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos
Reply

2,920 Views
microeval
Contributor I

Hi Perla,

Is there any SPI example for the KL26Z MCu? I am configuring the KL25Z SPI example to Kl26Z board not succeeded.

Thanks in advance for you are help,

Regards,

Ram.

0 Kudos
Reply

2,920 Views
mjbcswitzerland
Specialist V

Ram

See https://community.freescale.com/message/427243?et=watches.email.thread

The SPI peripheral in the KL26 is not fully compatible with the one in the KL25. You need KL46 example code, which is compatible in relation to the SPI, or you can take complete working code from the uTasker project which works on all KL and K parts (automatically adapts itself accordingly).

Regards

Mark

http://www.utasker.com/kinetis.html

0 Kudos
Reply

2,920 Views
xiaodong_zhang
NXP Employee
NXP Employee

hi Palestro,

     You can use PE to create a SPI example for KL16. PE is best for helping you study how to use Kinetis MCU. You can download PE at Processor Expert Software and Embedded Components.

0 Kudos
Reply

2,920 Views
marcopalestro
Contributor II

Hello, I tried PE but found it way too difficult to use, so I'm looking for a simple example that just explain how to do things!

Thank you anyway for the help!

0 Kudos
Reply

2,920 Views
mjbcswitzerland
Specialist V

Hi

The SPI in the KL devices is different from the DSPI in the K devices; it doesn't have FIFOs and doesn't automatically control the chip select line in the same way.

Below is configuration code for master use of SPI1 on a certain set of pins (you can interpret it for the names since the code is from the uTaker project which uses some special configuration macros).

POWER_UP(4, SIM_SCGC4_SPI1); // enable the SPI

_CONFIG_PERIPHERAL(E, 2, PE_2_SPI1_SCK); // configure interface lines

_CONFIG_PERIPHERAL(E, 1, (PE_1_SPI1_MOSI | PORT_SRE_FAST | PORT_DSE_HIGH));

_CONFIG_PERIPHERAL(E, 3, (PE_3_SPI1_MISO | PORT_PS_UP_ENABLE));

_CONFIG_DRIVE_PORT_OUTPUT_VALUE(E, CS0_LINE, CS0_LINE, (PORT_SRE_FAST | PORT_DSE_HIGH));

SPI1_C1 = (SPI_C1_CPHA | SPI_C1_CPOL | SPI_C1_MSTR | SPI_C1_SPE); // configure master mode

SPI1_BR = (SPI_BR_SPPR_PRE_1 | SPI_BR_SPR_DIV_2); // set speed

(volatile unsigned char)SPI1_S; // read status register and data register to prepare for operation

(volatile unsigned char)SPI1_D;

To write and read a byte:

SPI1_D = byte; // write a byte that is sent

while (!(SPI1_S & (SPI_S_SPRF))) {} // wait for the byte to be completely transmitted

byte = SPI1_D; // read the byte that was read

Repeate for additional data.

The only tricky bit is the fact that the status and data register should be read "once" before starting. If this is not performed the first loop will hang since the status will not be set accordingly.

Also, never let the debugger view the SPI registers when stepping such code since it will also read the data register and reset status flags in the process (causing the loop to hang).

Regards

Mark

2,920 Views
marcopalestro
Contributor II

Hello Mark,

     thank you for the help, I'm trying to implement this; one thing I don't understand is how is driven the Chip Select pin: It seems to me from reading the datasheet that I should use MODFEN = 1 and SSOE = 1 to use SS as "slave select output", but I don't see any flag regarding SS on your example. Maybe it must be driven by software?

Also, I'm writing the DSPI driver for K series, and I found the usual problems with hardware SPI devices: I cannot use various queue/buffer flags to successfully enqueue next byte without waiting the end of transmission; the result is that, even with high speed, resulting throughput is very low, because the bytes get transmitted fast but there is a long pause between two bytes.

Here is an example of a communication made of 1 byte tx (value CMD_READ_ID) and 3 bytes rx, with chip select asserted during the whole communication:

     SPI0_PUSHR = (CMD_READ_ID | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1));   

     while ((SPI0_SR & SPI_SR_RXCTR_MASK) == 0);

    manufacturer_id = SPI0_POPR;    // dummy

    SPI0_PUSHR = (0xFF | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1));

    while ((SPI0_SR & SPI_SR_RXCTR_MASK) == 0);

    manufacturer_id = SPI0_POPR;

    SPI0_PUSHR = (0xFF | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1));

    while ((SPI0_SR & SPI_SR_RXCTR_MASK) == 0);

    device_id1 = SPI0_POPR;

    SPI0_PUSHR = (0xFF | SPI_PUSHR_PCS(1));

    while ((SPI0_SR & SPI_SR_RXCTR_MASK) == 0);

    device_id2 = SPI0_POPR;

Do you know how to speed up the process? I'm expecting the same problem with SPI driver in KL series. In fact I wrote:

SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK;

    PORTD_PCR4 = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK;   // alt2: spi1 cs
    PORTD_PCR5 = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK;   // alt2: spi1 clk
    PORTD_PCR6 = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK;   // alt2: spi1 mosi
    PORTD_PCR7 = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK;   // alt2: spi1 miso

    SPI1_C1 = SPI_C1_MSTR_MASK | SPI_C1_CPHA_MASK | SPI_C1_CPOL_MASK | SPI_C1_SPE_MASK;

    SPI1_BR = SPI_BR_SPR(1);

    SPI1_DL = CMD_READ_ID;

    while (SPI1_S & SPI_S_SPRF_MASK);

    manufacturer_id = SPI1_DL;    // dummy

    SPI1_DL = 0xFF;

    while (SPI1_S & SPI_S_SPRF_MASK);

    manufacturer_id = SPI1_DL;

    SPI1_DL = 0xFF;

    while (SPI1_S & SPI_S_SPRF_MASK);

    device_id1 = SPI1_DL;

    SPI1_DL = 0xFF;

    while (SPI1_S & SPI_S_SPRF_MASK);

    device_id2 = SPI1_DL;

But the execution freezes in the last while.

Thank you again

Best regards

Marco

0 Kudos
Reply

2,920 Views
mjbcswitzerland
Specialist V

Hi Marco

I simply control the CS line using an output with the KL's SPI:

_CLEARBITS(E, PORTE_BIT4); // assert the CS line

write/read a number of bytes

_SETBITS(E, PORTE_BIT4); // negate the chip select line

I see no reason why your KL driver should freeze in the last while - as pointed out previously, make sure that this is not due to the debugger reading its status register and clearing the flag that the code is waiting for.

In the case of the DSPI you can slightly improve performance by making use of the 4 deep FIFO. This allows 4 bytes to be sent at maximum speed (no space between them) but you will still need to wait on the final byte to be completely read in before extracting the final rx data byte. In your case, when reading the manufacturer's ID of an SPI Flash device, you can do.

SPI0_PUSHR = (CMD_READ_ID | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1)); // queue 4 writes, controlling the CS line to be asserted at the start and negated after transmission

SPI0_PUSHR = (0xFF | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1));

SPI0_PUSHR = (0xFF | SPI_PUSHR_CONT_MASK | SPI_PUSHR_PCS(1));

SPI0_PUSHR = (0xFF | SPI_PUSHR_PCS(1));

while ((SPI0_SR & SPI_SR_RXCTR_MASK) < (3 << 4)); // wait until 3 bytes have been received

device_id2 = SPI0_POPR; // 3 dummy reads

device_id2 = SPI0_POPR;

device_id2 = SPI0_POPR;

while ((SPI0_SR & SPI_SR_RXCTR_MASK) == 0); // wait until the final byte has been received

device_id2 = SPI0_POPR; // the result

It is also possible to wait for all 4 bytes, read 3 dummy ones and then keep the fourth. The above will just be two or three instructions faster (a few ns...) since the 3 byte flush is performed during the fourth byte transmission/reception.

Since the SPI in the KL doesn't have FIFOs the techniques to improve performance are:

1. Send the first byte.

2. Wait for the TX status to indicate that a further byte can be written. This is before the Rx byte has been received (maybe after about 2 bits transmission - I didn't measure this though).

3. Send the next byte (if there is further data to be sent)

4. Wait for the Rx and read when it is ready.
5. Go to 2.

Or

Configure DMA for Tx and Rx. Let the DMA controller send and receive and wait until the DMA controller informs that the requested number of bytes have been transmitted and received. Then read the 4th byte from the DMA Rx buffer in the example case.

Regards

Mark

P.S.  I use the DSPI and SPI for SD card and AT45DBxxx, ST25P(E)xxx, SSTxxx SPI Flash.

2,920 Views
marcopalestro
Contributor II

Thank you Mark, I'm slowly getting this thing to work. In the last test, I confused the test condition:

while ((SPI1_S & SPI_S_SPRF_MASK) == 0);     // read buffer empty

Anyway, it still works weird:

If I put a breakpoint just after turning on the peripheral:

SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK;

then the test works fine; if I don't put the breakpoint or execute it without the debugger it stops looping on the first while:

SIM_SCGC4 |= SIM_SCGC4_SPI1_MASK;

    PORTD_PCR4 = PORT_PCR_MUX(1);

    //PORTD_PCR4 = PORT_PCR_MUX(2) | PORT_PCR_DSE_MASK;   // alt2: spi1 cs
    PORTD_PCR5 = PORT_PCR_MUX(2);   // alt2: spi1 clk
    PORTD_PCR6 = PORT_PCR_MUX(2);   // alt2: spi1 mosi
    PORTD_PCR7 = PORT_PCR_MUX(2);   // alt2: spi1 miso

    SPI1_C1 = SPI_C1_MSTR_MASK | SPI_C1_CPHA_MASK | SPI_C1_CPOL_MASK | SPI_C1_SPE_MASK;

    SPI1_BR = SPI_BR_SPR(1);

    // test

    F_CS_H;

    F_CS_L;

    SPI1_DL = CMD_READ_ID;

    while ((SPI1_S & SPI_S_SPRF_MASK) == 0);

    manufacturer_id = SPI1_DL;    // dummy

    SPI1_DL = 0xFF;

    while ((SPI1_S & SPI_S_SPRF_MASK) == 0);

    manufacturer_id = SPI1_DL;

Really can't get how it's supposed to be...

0 Kudos
Reply