SPI Example

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

SPI Example

Jump to solution
11,337 Views
ignisuti
Contributor IV

Where can I find a basic bare-bones SPI example for any Kinetis component?

 

I've tried the Processor Expert, but it has WAY TOO MUCH bloat and garbage in there that doesn't need to be.

 

I don't need, or even want, interrupts.

 

I've got my functions setup as best I could on my own, but doesn't seem to be working. Once I put data in the TXDATA register, do I need to do anything else to start shifting it out?

1 Solution
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

Here is some sample code (from an SPI Flash driver in the uTasker project) showing how to send one single byte and read a single byte of data returned. The chip select is self-timing and the only strang(ish) thing about the SPI in the Kinetis is that its Rx flag is not self-clearing and so has to be done by code (example assumes SPI2 - defines and macros should be self-explanatory). First the SPI is powered up, its pins configured, its operation and speed set and then a single byte is sent.

 

POWER_UP(3, SIM_SCGC3_SPI2);_CONFIG_PERIPHERAL(D, 11, (PD_11_SPI2_PCS0 | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 12, (PD_12_SPI2_SCK | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 13, (PD_13_SPI2_SOUT | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 14, PD_14_SPI2_SIN);SPI2_MCR = (SPI_MCR_MSTR | SPI_MCR_DCONF_SPI | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS_CS0 | SPI_MCR_PCSIS_CS1 | SPI_MCR_PCSIS_CS2 | SPI_MCR_PCSIS_CS3 | SPI_MCR_PCSIS_CS4 | SPI_MCR_PCSIS_CS5);\SPI2_CTAR0 = (SPI_CTAR_DBR | SPI_CTAR_FMSZ_8 | SPI_CTAR_PDT_7 | SPI_CTAR_BR_2 | SPI_CTAR_CPHA | SPI_CTAR_CPOL); // for 50MHz bus, 25MHz speed and 140ns min de-select timebyte = 0x55; // test single byte transmissionSPI2_PUSHR = (byte | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0) // write a single byte to the output FIFO - assert CS linewhile (!(SPI2_SR & SPI_SR_RFDF)) {} // wait for byte to be sent and a byte to be read inSPI2_SR |= SPI_SR_RFDF; // clear the reception flag (not self-clearing)

 

Regards

 

Mark

 

View solution in original post

0 Kudos
19 Replies
2,916 Views
mjbcswitzerland
Specialist V

Hi

 

Here is some sample code (from an SPI Flash driver in the uTasker project) showing how to send one single byte and read a single byte of data returned. The chip select is self-timing and the only strang(ish) thing about the SPI in the Kinetis is that its Rx flag is not self-clearing and so has to be done by code (example assumes SPI2 - defines and macros should be self-explanatory). First the SPI is powered up, its pins configured, its operation and speed set and then a single byte is sent.

 

POWER_UP(3, SIM_SCGC3_SPI2);_CONFIG_PERIPHERAL(D, 11, (PD_11_SPI2_PCS0 | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 12, (PD_12_SPI2_SCK | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 13, (PD_13_SPI2_SOUT | PORT_SRE_FAST | PORT_DSE_HIGH));_CONFIG_PERIPHERAL(D, 14, PD_14_SPI2_SIN);SPI2_MCR = (SPI_MCR_MSTR | SPI_MCR_DCONF_SPI | SPI_MCR_CLR_RXF | SPI_MCR_CLR_TXF | SPI_MCR_PCSIS_CS0 | SPI_MCR_PCSIS_CS1 | SPI_MCR_PCSIS_CS2 | SPI_MCR_PCSIS_CS3 | SPI_MCR_PCSIS_CS4 | SPI_MCR_PCSIS_CS5);\SPI2_CTAR0 = (SPI_CTAR_DBR | SPI_CTAR_FMSZ_8 | SPI_CTAR_PDT_7 | SPI_CTAR_BR_2 | SPI_CTAR_CPHA | SPI_CTAR_CPOL); // for 50MHz bus, 25MHz speed and 140ns min de-select timebyte = 0x55; // test single byte transmissionSPI2_PUSHR = (byte | SPI_PUSHR_CONT | SPI_PUSHR_PCS0 | SPI_PUSHR_CTAS_CTAR0) // write a single byte to the output FIFO - assert CS linewhile (!(SPI2_SR & SPI_SR_RFDF)) {} // wait for byte to be sent and a byte to be read inSPI2_SR |= SPI_SR_RFDF; // clear the reception flag (not self-clearing)

 

Regards

 

Mark

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

That helped me fill in the few missing pieces to my puzzle. 

 

THANK YOU VERY MUCH!!!

0 Kudos
2,915 Views
ignisuti
Contributor IV

 

I have another question about this code snipit. Is this the best method for knowing when the transfer is complete?

 

Wouldn't TCF be a better bit to monitor? 

 

I ask because some of the chips I'm talking to via SPI don't have communicate back to my master (no MISO pin on master).

 

while (!(SPI2_SR & SPI_SR_RFDF)) {} // wait for byte to be sent and a byte to be read inSPI2_SR |= SPI_SR_RFDF; // clear the reception flag (not self-clearing)

 

0 Kudos
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

There are two flags - one for transmit complete (a foloowing byte can be written to the output buffer) and one for rx complete (the complete reception byte can be read).

 

The tx complete flag is set earlier and can be used when it is not necessary to wait for the byte reception to also be read, to feed the transmit buffer slightly in advance.

 

If the reception byte is to be read it is necessary to await for the rx flag because the tx flag is set before the complete byte has been shifted in.

 

Regards

 

Mark

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

Well I've got Transmitting down, but receiving is giving me greif. 

 

It's essentially the same setup. I just PUSH a random byte of data out to pump the serial clock and then read data in from the POP register, right?

 

I'm watching my registers in the debugger. I can see the SPI1_PUSHR and SPI1_TXFR0 appears with the correct data, but SPI1_POPR and SPI1_RXFR0 never change...

 

I've attempted this on a couple different SPI devices now. I can transmit just fine, but I get nothing when I try to receive.


What else should I double-check?

0 Kudos
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

Check that the Rx signal really exists.

 

You can send anything when you just need to clock in a byte (I use 0xff so that the Tx line doesn't move though).

 

It is not always practical to use the debugger to view the FIFO since it will be extracting receptions from it so that they may be missing when the code copies them. It is best to close the SPI register windo and look at the data that is read to the code's variable.

 

Regards

 

Mark

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

If you're referring to SPI_SR_RFDF, then yes, it's present...

 

I put on scope on the MISO lines are don't see any pulses.

 

I'm really scratching my head on this one....

:robotsad:

0 Kudos
2,915 Views
comsosysarch
Contributor III

If you have SR RFDF then I'd say your receive code is at least working to get the bytes back that are present (or not) when they should be.

 

If transmit is "working" and you see data going out the pin on MOSI, but no data coming back on MISO, and the chip you are targeting is supposed to read data back simultaneously?

 

Is the SPI clock going out to the target chip?

For sure the read is simultaneous with the write? Some devices you have to read while shifting out an extra dummy tx byte/word/whatever.

 

Oh, and some SPI slave devices you need a slave chip select, so maybe another thing to verify.

0 Kudos
2,915 Views
ignisuti
Contributor IV

Okay, figured out why I wasn't seeing MISO pules on the scope. It was a hardware bug. Now that I've got my pulses, I'm still not seeing data in the POPR register. I'm pushing this data into a variable and reading that as well, but still not matching the value shown on my scope (which is showing the correct value).

 

Is there something special I need to do to get POPR to see the data?


I'm pushing out my read command, waiting for Transfer Complete Flag to be set, pushing out a dummy byte and then waiting for my Receive FIFO Drain Flag to be set.

0 Kudos
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

I am note sure whether this is a problem with the external device you are trying to read from (not responding) or a driver difficults.

 

Try tying the MISO line to '0' and verify that you always read 0x00, then to '1' to verify that you always read 0xff. This will distinguish whether it is a driver difficulty of due to the chip you are reading from not returning the expected data.

 

Regards

 

Mark

 

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

Good idea Mark.

 

Yes, it's clearly the driver. I tied MISO high, verified the high signal on the scope, but POPR remains 0 no matter what I do. I've pumped a dozen dummy bytes into PUSHR, but still nothing.

0 Kudos
2,915 Views
ignisuti
Contributor IV

Well.... I found an issue with the hardware which was holding MISO low. 

 

I've tweaked dozens of things in the code now and am having yet another SPI problem...

 

The problem I’m facing now is that it looks like the 4 TX buffers just fill up and don’t appear shift data out while I’m processing code. 

 

My understanding was that if I fill the 4 buffers and then sit in a loop, the MCU will handle shifting that info out of the buffers onto the bus all itself without further input from my code. 

 

Here is some code I wrote to wait until the data has all be shifted out. After a few calls to the SPI write driver, it'll just sit here forever. I’ve stopped the debugger in this section of code and verified my SPI bus is enabled. My scope doesn't show any clock pulses though. If I have data in the TX buffer and I'm sitting in a loop, the clock should be pulsing, right?

 

    do      {      /* Wait for the transfer complete flag. */      tx_counter = ( SPI_SR_REG( spi_base_ptr_tbl[ channel_number ] ) & SPI_SR_TXCTR_MASK ) >> SPI_SR_TXCTR_SHIFT;       } while( tx_counter > 0 );

 

0 Kudos
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

Yes one can queue up to 4 words deep transmission and also have up to 4 words waiting to be read.

 

How are you writing the words? The write needs to contain the data plus some flags to tell the DSPI how to behave with it - eg.

 

SPI2_PUSHR = (byte | SPI_PUSHR_CONT | ulChipSelectLine | SPI_PUSHR_CTAS_CTAR0);

 

Apart from the fact that one has to carefully control which word resets the CS line and the strange behaviour of the Rx flag I found the DSPI efficient and easy to use and used it for several SPI Flash drivers and SD card FAT. At the moment I don't understand what is going wrong in your project.

 

I have attached the uTasker ATMEL AT45DBxxx driver as reference (it shows the FIFOs being used) and don't forget that all code is available in the uTasker project to work with the DSPI and embedded or FAT file systems in SPI based Flash and SD cards (as well as using the SDHC interface).

 

Regards

 

Mark

 

Notes:

#define WRITE_SPI_CMD0(byte)                SPI2_PUSHR = (byte | SPI_PUSHR_CONT | ulChipSelectLine | SPI_PUSHR_CTAS_CTAR0) // write a single byte to the output FIFO - assert CS line

#define WRITE_SPI_CMD0_LAST(byte)           SPI2_PUSHR = (byte | SPI_PUSHR_EOQ  | ulChipSelectLine | SPI_PUSHR_CTAS_CTAR0) // write final byte to output FIFO - this will negate the CS line when complete

#define READ_SPI_FLASH_DATA()               (unsigned char)SPI2_POPR
#define WAIT_SPI_RECEPTION_END()            while (!(SPI2_SR & SPI_SR_RFDF)) {}
#define CLEAR_RECEPTION_FLAG()              SPI2_SR |= SPI_SR_RFDF

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

Shouldn't these two lines of code be swapped?

 

#define READ_SPI_FLASH_DATA()               (unsigned char)SPI2_POPR
#define WAIT_SPI_RECEPTION_END()            while (!(SPI2_SR & SPI_SR_RFDF)) {}

 

You'd want to wait for data to be received before attempting to read it out of POPR, right?

0 Kudos
2,915 Views
ignisuti
Contributor IV

Also... I've seen very strange behavior with POPR. Is this register affected by me viewing it in the debugger? I get significantly different behavior when I step through code versus running through it and I've added numerous delays to try and see if that made a difference, but it does not.

 

I understand how simple the SPI functionality is supposed to be. I've worked with them in the past and found them to be quite easy. But, this is just driving me bonkers. I'm begining to suspect my issues might lie with the CW10.2 compiler.

0 Kudos
2,915 Views
ignisuti
Contributor IV

Here's a good example of the "strange behavior" I'm seeing...

 

When the code stops on this 10us delay, POPR is 0xF2. At the end of the 10us delay, POPR is 0x00.

Why would this register change?

 

 SPI_PUSHR_REG( SPI0_BASE_PTR ) |= DUMMY_BYTE; TIMERS__delay( DELAY_TIMER__10us, 1 ); new_byte = SPI_POPR_REG( SPI0_BASE_PTR );

 

0 Kudos
2,915 Views
mjbcswitzerland
Specialist V

Hi

 

>>#define READ_SPI_FLASH_DATA()               (unsigned char)SPI2_POPR
>>#define WAIT_SPI_RECEPTION_END()            while (!(SPI2_SR & SPI_SR_RFDF)) {}

 

>>You'd want to wait for data to be received before attempting to read it out of POPR, right?

 

These are defines and not code. The code is attached.

 

Basically one should avoid viewing registers based on FIFOs in the debugger viewer since the debugger's reads often cause wierd affects. Therefore always keep the DPSI register viewer closed.

 

Don't forget that each read of POPR will cause the register to be filled with the next waiting value in the FIFO.

 

Maybe not important in the one line shown but PUSHR |= DUMMY_BYTE will just set bits on the data. Generally I suppose something like PUSHR = ((PUSHR & ~0xff) | byte); is used (?)

 

Regards

 

Mark

 

0 Kudos
2,915 Views
ignisuti
Contributor IV

Mark, 

Looks like I glossed over your previous post too quickly. My apologies.

 

Wanted to report back that I believe I finally have this thing going now. Here a few important things anyone else doing this should be aware of.

a.            Gotcha: Attempting to clear a flag in the Status Register via OR’ing with a mask, clears all flags! This is because it’s taking flags that are already set to a 1, OR’ing them back into the register which is essentially writes a 1 to all flags. This explains much of the "odd" behavior I was experiencing because I was checking and clearing each flag one by one.

b.            Gotcha: Viewing the registers with the debugger counts as a read. So, the POPR register would pop the data into the debugger and then simultaneously remove it from the POPR register. So, stepping through code as I was doing produces non-desirable results.

c.             Also, I needed to flush the TX & RX buffers in order to start from a known state. I had at some point added code to do this, but I mistakenly set the MCR register EQUAL to the flush bits instead of OR’ing it which cleared my MCR register and caused unexpected behavior which caused me to take this code back out.

 

Thanks for all your help and paitence. 

0 Kudos
2,915 Views
japex92
Contributor I

Joe,

I am having the same problem as you, would you post the piece of code to read and transmit data?

I always read 0x00 on my POPR register.

Thanks,

0 Kudos