Looking for a fast read and write SPI object.

I'm presently working on an object for the Nordic nRF24L01+ modules.
As I've mentioned in a previous thread, I took out the delays in the SPI_Asm object which has sped it up.
This is working relatively well except the SPI_Asm object can either read or write via SPI not both at the same time.
The nRF24L01+ modules output the status register as a command in sent to the device.
I'd like to be able to capture this status information as I write a command.
I've looked through the OBEX and see some drivers of various devices that use SPI. I'm hoping someone here can save me from looking through all these specialized drivers to see if there is anything useful for my needs.
Does anyone know of a PASM SPI driver that can read as it writes?
Another item on my wish list is a SPI driver that can read or write up to 32 bytes at a time.
I think my programing skills are just about up to satisfying these needs myself, but I'd sure rather spend my programming time and energy on other projects.
As I've mentioned in a previous thread, I took out the delays in the SPI_Asm object which has sped it up.
This is working relatively well except the SPI_Asm object can either read or write via SPI not both at the same time.
The nRF24L01+ modules output the status register as a command in sent to the device.
I'd like to be able to capture this status information as I write a command.
I've looked through the OBEX and see some drivers of various devices that use SPI. I'm hoping someone here can save me from looking through all these specialized drivers to see if there is anything useful for my needs.
Does anyone know of a PASM SPI driver that can read as it writes?
Another item on my wish list is a SPI driver that can read or write up to 32 bytes at a time.
I think my programing skills are just about up to satisfying these needs myself, but I'd sure rather spend my programming time and energy on other projects.
Comments
I'm glad to see you have some code I can use.
It's always a pleasure to read your code. I always learn a lot when I do.
These Nordic modules can do SPI at 10MHz. I'm hoping to have the airwaves buzzing with data.
Doe's this driver read and write at the same time? Can you point me to the section of code I should be looking?
I see your "Command SPI" section and "Response SPI" section but I as far as I can tell these either read or write via SPI not both at once.
These sections are still very educational. I need to learn how to use counters to control a clock signal. I was wondering how you could write a 20MHz, pretty cool.
The 20 and 10 Mhz rates are done via those clocks and the speeds are the fastest they can run and still manipulate the PHSB register to pull off what he did.
Writing is 1 instruction per bit, 4 clock cycles per instruction, so 80Mhz/4 is 20Mhz for the write rate. Reading is 2 instructions, 8 clock cycles per bit, or 10Mhz @ 80MHz system clock.
It's incredibly nice code for byte-oriented transfer.
One thing I don't know, is that although SPI allows "simultaneous transfer", where each bit cycle has read AND write, is if anything actually requires that? I have an SPI routine I've been knocking around, which allows for it, so I found I couldn't use Kye's technique without driving the clock down further than I needed since I have more overhead during transfer.
But does any device really need that? SD, SRAM, Flash, all seem to all do "in-then-out" or "out-then-in" in a byte oriented way, ignoring whats read on writes, and feeding dummy values on reads. (I see two flavors of devices for the latter, some like $FF, some like $00?)
If it's really not needed ever, I'd love to ditch what I have and do something heavily inspired by what Kye did.
While these devices can communicate via SPI at 10MHz, I'm very willing to use a slower speed if I can read and write simultaneously.
Just in case anyone in interested, the MOSI line is read on the rising edge of the clock. New data is ready on the MISO line a few nanoseconds after the falling clock. The Propeller may read this before or after the rising clock. The data needs to be present on the MOSI line for 2ns prior to the rising clock so the clock can be driven high the instruction following setting the data line.
The data is read (and written) MSBit first LSByte first.
kuroneko, I confess to not really understanding your code. It looks like your shifting out the data, and I see you reading bits on "ina" but I don't see how these bits are assembled into a byte.
I'm relatively new to the world of PASM and this stuff is just barely hitting the top of my head (I'm hoping it's not over my head).
I was kind of hoping someone had an object similar to SPI_Asm but that can read as it writes. I'm not being lazy I'm being . . . efficient.
Thanks again to Kye and kuroneko for your help.
- The first line places the byte into outa, MSB on top of bit 7.
- The test instruction tests bit 8 and affects the C flag (parity) depending on what it sees (C set for '1').
- rcl (rotate carry left) shifts outa to the left (exposing the next bit to be sent) and also inserts the C flag into bit 0.
- After doing this 8 times the byte you sent will occupy bits 15..8 of outa and bits 7..0 now hold whatever came in from DIN.
outa[15..0] after executing rcl 3 times. HTHThese devices don't "require" a simultaneous transfer but it would be very useful. The status register may be read as a command is given to a nRF24L01+. I'd like to frequently read this status register since it contains information about the success of the transmission of a data packet and other useful information. An ideal write method would return this status register so I could take appropriate action based on the information. As it now, I can read the status register after my the original write.
As I think about this a bit more, the only byte I'm interested in is this first one. The other bytes read during a write are dummy values. So an even better ideal write method would only read this first byte and not worry about the rest. So after the first byte is written the driver should switch to high speed mode and not worry about reading any more.
I'm not so sure how fast I'll really need these transceivers to communicate. I just thought it would be nice if the full speed of these modules were available.
Thank you for further clarification. I didn't know the "outa" could be used that way. That is pretty slick.
Thanks again for your help.
Duane
That's multitasking, I LOVE the propeller.
It isn't released due to some issues with synchronizing the PLL phase. It works like 95% of the times the driver is started in a cog.
/Ahle2
The core loop of "my" (it's all inspired from elsewhere) routine is this:
How many bits to transfer comes out on SPILayer1Param
Bits to go out are in spiBitsOut (must be aligned to the MSB of the long, IE if sending only 8 bits, it needs to be the first byte of the long.)
Bits that come in are in spiBitsIn, and will be lined up to the LSB of the long.
Based on the length of the loop (28 clocks), it probably gets just shy of 3Mhz, but bidirectionally.
I think I'd worked out the best I could get Kye's method to work bidirectionally was 16 cycles (1 write, 2 read, 1 for loop), but it starts to get a bit more complex as the loop gets longer. I wondered too about the relationship between the cycles of instruction processing to the hardware clocks rise and fall. Also, that the end of the loop takes 8 cycles further complicates things re: start/stopping the clock... you'll notice Kye's code actually skips the last bit of a byte, so the clock can be immediately turned off, and THEN it brings the last bit's processing into play. So I'd have to go down another clock divider to effectively 32 cycles per clock, which is why I stopped looking. I could do that, but @ 2.5 MHz, just keeping the loop I had would be faster in the end for doing bidirectional SPI.
PS: I need to read the original post before asking silly questions re: "is bidirectional even needed?", when that was half the point of the original post... :P
One technique that is new to me is
I'm guessing c is written if the MSB is 1? (Duh, Maybe that's why it's called the carry flag.)
I confess to not using the c flag much myself. I don't think I know enough to know when it would be useful. (Though I'm seeing how it is used a lot in this latest project.)
This does seem faster than Beau's method in SPI_Asm. (I've changed some of the variable names.)
t3 holds the data and t5 had previously been setup as a MSB mask.
I think your method is one instruction shorter.
I like in your method, four bytes are sent/received at a time.
Do you have to be careful about the order your bytes are placed in the long? The littleendian thing often messes me up. In this case the littleendian could be a good thing since if the long only held one byte, the long's MSB would also be the byte's MSB.
Yes, my original post did request a way to read and write simultaneously. As I've thought more about when I'd want to be able to do so, I realize it's not really as important as I first thought.
My main need as I previously mentioned, is to read the status register. But the times the status register information is going to be the most important is when the interrupt pin is set (active low). There are several reasons this may be set low and I wont know the reason until I read the status register. I doubt I'll want to write anything to the modules as I read this register. And then once I do know the contents of the status register, I wont need to be checking it as I write the next data to the module.
I still think it would be useful to be able to read and write at the same time but I think I can live without it for a while.
These modules like a $FF to be written as a read is performed. I just set the MOSI pin high as I perform a read. Although $00 didn't seem to cause a problem before I made this modification.
Thanks again for all your help. I've learned a lot.
(Note to self: Don't add code until after spellcheck.)
Yes, you need to be careful about placement in the long if only doing a byte, it must live in the 8 most significant bits of the long.
The advantage of the code you pasted is it's not destructive to the data being sent, where the one I showed will "destroy" the original data being sent, because I am shifting the bits out. (Where that shifts a mask used for plucking bits out)