kl25z spi basics

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
}


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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?
}
}

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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:.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thanks for the very insightful information!
Cheers.


- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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:
- 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)
- r[i] = SPI_RD_WR_REG; //READ REG TO UPDATE BUFFER //Rong wrote
- }
- //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");
- }
