kl25z spi basics

cancel
Showing results for 
Search instead for 
Did you mean: 

kl25z spi basics

2,558 Views
mjg8t
Contributor IV

I am working with the SPI0 peripheral on the kl25z freedom board.  I was having some trouble getting the SPI receive buffer to give me the expected output. I used a loopback test on MOSI and MISO for self testing. I found that the problem was that I had to insert a manual delay in order for the proper data to be received from the buffer (see delay_nop(100) in code).  After inserting the delay the "r" receive buffer five the correct data of 0xa1- 0xa4.  Without the delay the buffer returns 0x03,0x04,0xa1,0xa2, which are the last 2 bytes from the transmit loop and the first 2 bytes of the receive loop.  You can see the attached image capture to see the sequence of events with and without the delay, with a test signal being triggered.

1) I thought that the "SPI_WAIT_RX_DATA" would throttle the SPI transaction without needing the manual delay.  I thought that the SPRF flag would wait upon each SPI send until it was received then proceed.  But, it seems to blow past the data actually being received?

It seems that there would be a more efficient way of writing the routines than using a manual delay that would have to be tuned depending on the clock rate.  But, I do not see any other flags that might help in the process.  Is a manual delay the only option?

#define TEST_LOW GPIOD_PCOR |= GPIO_PCOR_PTCO(0x01)  //debug pin

#define TEST_HIGH GPIOD_PSOR |= GPIO_PSOR_PTSO(0x01) //debug pin

#define SPI_WAIT_RX_DATA while (!(SPI0_S & SPI_S_SPRF_MASK))  //waits for the rx data ready ready

#define SPI_WAIT_TX_DATA while (!(SPI0_S & SPI_S_SPTEF_MASK)) //waits for the txef data ready ready

#define SPI_RD_WR_REG SPI0_D  //kinetis read write register

void spi_test_io( void){

  uint8_t i=0,junk,status;

  uint8_t j = 0xA0;

  uint8_t r[4] = {0};

  TEST_LOW;

  //clock out 1-8 on spi bus

  for(i=0;i<4;i++){

  SPI_WAIT_TX_DATA; //WAITS UNTIL THE TX EMPTY BUFFER FLAG GOES HIGH

  SPI_RD_WR_REG = i+1; //OUTPUT A1 ON SPI

  SPI_WAIT_RX_DATA; //THOUGHT THIS WOULD THROTTLE THE LOOP - WAIT FOR DATA TO BE TX (RECEIVED)

  }

  //delay_nop(100);  //ENOUGH TIME TO OUTPUT THE DATA....

  TEST_HIGH;

  //TRYING TO CLEAR BUFFER SO THAT CAN READ CURRENT SPI DATA

  //junk = SPI_STATUS; //read status

  junk = SPI_RD_WR_REG; //MUST READ DATA IN ORDER FOR NEXT READS TO BE CORRECT

  for(i=0;i<4;i++){

  //output and read 0xa0 - 0xa4

  SPI_WAIT_TX_DATA; //WAITS UNTIL THE TX EMPTY BUFFER FLAG GOES HIGH

  SPI_RD_WR_REG = j++; //OUTPUT A1-a4 ON SPI

  SPI_WAIT_RX_DATA; //wait for data to be tx

  r[i] = SPI_RD_WR_REG; //READ REG TO UPDATE BUFFER

  }

  TEST_LOW;

  asm("nop");

}

void spi_init( void){

  //SPI0 module initialization

  SIM_SCGC4 |= SIM_SCGC4_SPI0_MASK; // Turn on clock to SPI0 module

  SIM_SCGC5 |= SIM_SCGC5_PORTD_MASK; // Turn on clock to Port D module

  PORTD_PCR1 = PORT_PCR_MUX(0x02); // PTD1 pin is SPI0 CLK line

  PORTD_PCR2 = PORT_PCR_MUX(0x02); // PTD2 pin is SPI0 MOSI line

  PORTD_PCR3 = PORT_PCR_MUX(0x02); // PTD3 pin is SPI0 MISO line

  PORTD_PCR0 = PORT_PCR_MUX(0x01); // PTD0 pin is configured as GPIO (CS line driven manually)

  GPIOD_PSOR |= GPIO_PSOR_PTSO(0x01); // PTD0 = 1 (CS inactive)

  GPIOD_PDDR |= GPIO_PDDR_PDD(0x01); // PTD0 pin is GPIO output

  //SETUP SPI0 PERIPHERAL

  SPI0_C1 = SPI_C1_SPE_MASK | SPI_C1_MSTR_MASK; // Enable SPI0 module, master mode //SETS SPI ENABLE AND SPI MASTER BITS

  SPI0_BR = SPI_BR_SPPR(0x04) | SPI_BR_SPR(0x02) ; // BaudRate = BusClock / ((SPPR+1) * 2^(SPR+1)) = 20970000 / ((4+1) * 2^(2+1)) = 524.25 kHz

  //SPI0_BR = SPI_BR_SPPR(0x00) | SPI_BR_SPR(0x00) ; // BaudRate = BusClock / ((SPPR+1) * 2^(SPR+1)) = 20970000 / ((4+1) * 2^(2+1)) = 5240.25 kHz

}

Labels (1)
0 Kudos
7 Replies

1,124 Views
martynhunt
NXP Employee
NXP Employee

Hi,

This document may help you out: L-Series SPI GPIO CS/SS

Best regards,

Martyn

EDIT: Added updated source code for the document above.

1,124 Views
mjg8t
Contributor IV

Hi Martyn and Rong,

Rong you were correct that the buffer did need to be cleared.  Interestingly by adding the delay in my original version also fixed the issue I was having.  I am not sure why the delay would help in this case.

Martyn, thank for the very helpful document.  Wish I had seen this couple of days ago.

So, to ensure that the buffer is clear before running SPI routines I thought I would be safe put together a spi_clear_buf function as follows.  But, the buffer is only cleared when I use the delay routine.  Do you have why the delay would be required, is this expected?  Seems strange to me.

void spi_clear_buf(){

  uint8_t junk;

  while (SPI0_S & SPI_S_SPRF_MASK){      //while the rx data is available flag is set.

  junk = SPI_RD_WR_REG;

  delay_nop(50);  // for some reason the delay is needed, why?

  }

}

0 Kudos

1,124 Views
mjbcswitzerland
Specialist V

Hi

In the uTasker KL project the SPI status is cleared (ex. for SPI1) by using:

(volatile unsigned char)SPI1_S;

(volatile unsigned char)SPI1_DL;

This is required only once after the interface has been configured and no delays should be necessary.

The other thing to be aware of is not to have an SPI peripheral view open when stepping code since it will be reading and clearing flags in the process, which can cause unexpected behaviour.

Regards

Mark

0 Kudos

1,124 Views
mjg8t
Contributor IV

Thanks Mark.  I have a couple follow up questions.

"SPI status is cleared (ex. for SPI1) by using: (volatile unsigned char)SPI1_S;"


1) Can you explain why ""clearing" the status allows the code to run without the need of the manual delay?

2) How does declaring the SPI_S register as volatile fix this scenario?  My understanding is that volatile ensures the compiler does not make any assumptions and optimizations for the declared variable.  Does this apply here?

Thanks for helping me see the light here.  Hope to be a pro on the kinetis soon :smileyhappy:.

0 Kudos

1,124 Views
mjbcswitzerland
Specialist V

Hi

1. I can't explain why you needed a delay as I also can't actually explain why the status needs to be cleared since there is no reason why it should be set out of reset (it isn',t according to the user's manual and the bit may in fact actually be reading '0' - I don't remember the details) but practically it needs to be done otherwise the first (ever) transfer is never correctly flagged as complete. Possibly it is an errata of sorts - but the workaround is harmless enough - and maybe it is by design (for some reason ?).

To clear the status, the SPI_S register needs to be read "with the flag set" (the first access) and then the data register needs to be read - therefore the two accesses.

Since I never needed a delay I can't explain why it also worked for you with one...

2. SPI_S is already declared as volatile in the register definition but the codes help to make it clear that the access is a dummy read. The compiler may optimise the read since the result is not being used so volatile is certainly important. Sometimes it is written as

dummy = SPI_S; but there are often associated warnings that the result in dummy is not used - a simple SPI_S; should essentially be adequate (as long as the compiler understands that it may not optimise it away).

Regards

Mark

0 Kudos

1,124 Views
mjg8t
Contributor IV

Thanks for the very insightful information!

Cheers.

0 Kudos

1,124 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

First of all, in actual spi transfer, I suggest you clear the GPIO pin which is connected to the /SS pin of slave spi when you transfer each byte. this is the procedure for master spi:

clear GPIO

write SPI_RD_WR_REG

wait SPRF bit is set

read SPI_RD_WR_REG

set GPIO

delay some time

......


In the first loop code, you do not read the receiver register, how about reading the receiver register after the SPRF bit is set by the code I modified based on your original code, I suspect that the first loop receiver buffer is not emptied:

  1.   TEST_LOW; 
  2.   //clock out 1-8 on spi bus 
  3.   for(i=0;i<4;i++){ 
  4.   SPI_WAIT_TX_DATA; //WAITS UNTIL THE TX EMPTY BUFFER FLAG GOES HIGH 
  5.   SPI_RD_WR_REG = i+1; //OUTPUT A1 ON SPI 
  6.   SPI_WAIT_RX_DATA; //THOUGHT THIS WOULD THROTTLE THE LOOP - WAIT FOR DATA TO BE TX (RECEIVED) 
  7.   r[i] = SPI_RD_WR_REG; //READ REG TO UPDATE BUFFER  //Rong wrote
  8.   } 
  9.   //delay_nop(100);  //ENOUGH TIME TO OUTPUT THE DATA.... 
  10.   TEST_HIGH; 

  11.  
  12.   //TRYING TO CLEAR BUFFER SO THAT CAN READ CURRENT SPI DATA 
  13.   //junk = SPI_STATUS; //read status 
  14.   junk = SPI_RD_WR_REG; //MUST READ DATA IN ORDER FOR NEXT READS TO BE CORRECT 
  15.  
  16.   for(i=0;i<4;i++){ 
  17.   //output and read 0xa0 - 0xa4 
  18.   SPI_WAIT_TX_DATA; //WAITS UNTIL THE TX EMPTY BUFFER FLAG GOES HIGH 
  19.   SPI_RD_WR_REG = j++; //OUTPUT A1-a4 ON SPI 
  20.   SPI_WAIT_RX_DATA; //wait for data to be tx 
  21.   r[i] = SPI_RD_WR_REG; //READ REG TO UPDATE BUFFER 
  22.   } 
  23.  
  24.   TEST_LOW; 
  25.   asm("nop");