thanks4opensource / buck50

STM32F103 logic analyzer and more
GNU General Public License v3.0
565 stars 59 forks source link

Chip Select (NSS) in SPI transfers #10

Open epninety opened 3 years ago

epninety commented 3 years ago

Trying to send multiple bytes in an SPI transfer from buck50.py, I'm unable to find a method to keep /CS (NSS) asserted for the complete transfer. Presently /CS rises briefly after each byte is sent, which spi devices read as completion of the transfer.

e.g. I enter 'spi' to get to spi mode, then '12 34 56' to send three bytes of data. On the scope, I see /CS rise for ~2uSec between bytes.

Have I missed a trick in the documentation, is there a way to hold /CS low for the complete transfer?

Current settings mode : master xmit-only : disabled snoop : disabled select : hardware baud : 281.25KHz endian : msb idle : low phase : 1st miso : open-drain pull : up speed : 2MHz nss : active tx-data : 00 rate : unlimited busy-wait : 1ms rx-wait : 1ms nss-time : 100μs(error:+1.35525e-08ps) ascii-num : numeric end : END

(Hardware is a custom board with a genuine STM32 fitted.)

thanks4opensource commented 3 years ago

How did you choose the value fornss-time=100μs? In themode=master nss=active SPI configuration, buck50 lowers PA4/NSS1 at the beginning of a byte transfer, waits nss-time to allow the data to be serially shifted out the PA7/MOSI port, and then raises PA4/NSS1. Can you set nss-time to baud_rate 8 bits num_bytes + extra for CPU overhead? Alternately, can you just permanently (for the duration of the test) set NSS low using nss=low, or even more simply tie the slave's select input to directly to ground? Do you need to test the its ability to dynamically respond to its slave select line?

Note that the ST SPI peripheral's handling of NSS output in master mode is very limited. RM0008 documents the spectacularly useless "This configuration is used only when the device operates in master mode. The NSS signal is driven low when the master starts the communication and is kept low until the SPI is disabled." I wasn't able to find any way for hardware to lower and then re-raise the NSS output at the beginning and end of an 8 (or 16 bit) SPI transfer, much less at the beginning and end of a multi-byte packet.

At one point it occurred to me that there are many spare ports when buck50 is in SPI mode and they could be used (under software control) to select multiple slaves on an SPI bus. The interactive mode would then be something like "3 12 34 56" to select the slave connected to port PA3 and send it those three bytes. I might add that feature someday if I have use for it, but I currently don't and, as you've discovered, buck50 is already complex enough.

epninety commented 3 years ago

baud_rate 8bits num_bytes = 85us for 3 bytes at 280kHz ? So 100us seems to be about right?

But from the scope, nss-time seems to set the time delay between NSS assertion and the actual byte transfer? So with nss-time=100us...

NSS goes low ~100us delay 1 byte is clocked out, ~100us delay NSS goes high ~2us delay NSS goes low etc. etc.

I've never understood how hardware driven NSS is supposed to be useful on STM32, but I assumed they knew something I didn't. I can't think of any SPI device that could be controlled with 8bit transactions. Even 16 bit transactions aren't very useful for many (most?) that I've used in the past. In my own code previously I configured the NSS pin as GPIO, then drop and raise it manually at the start and end of the transaction.

I will hardwire the NSS for now, and think about a 'proper' solution later if I need it.

I like your idea for multiple slaves, my testboard has jumpers at present to select devices on the target hardware. As you say, buck50 is complex enough, and very capable. Thank you for your efforts.

thanks4opensource commented 3 years ago

You're welcome. Glad you've found it useful.

Reviewing the code, it looks like I should remove the nss-time parameter and instead check Spi::Sr::BUSY. I seem to recall trying something like that without success, but it might have been my own misunderstanding and/or bug.

Removing thenss=active value and splitting it into something like=single and =multi to assert/de-assert the slave select on a per-byte or per-packet basis might also be nice. But if also doing the multiple select outputs it would then need to be independently settable for each port , and once again the complexity starts growing exponentially. I kept running into this issue during buck50's long development and always had to decide where to stop.

epninety commented 3 years ago

What is the use case for =single though? I still cant think of any SPI device I've used where it would be useful.

It seems simpler and cleaner to consider each line entry as a discrete SPI transaction, so drop NSS at the start, and raise it at the end. (8 bit transactions are still possible of course, it just means entering one byte per line.)

Alternatively, add a control character for the NSS line. Some thing like \ 12 34 / \ 56 / is probably self explanatory, but I think it's just adding unnecessary complication when 12 34 <cr> 56 <cr> would achieve the same thing.

thanks4opensource commented 3 years ago

Just because neither of us can think of a use for =single right now doesn't mean there isn't any. ;) For example, I use SPI (both from buck50 and otherwise) for generic serial I/O, things like APA102 "smart" LEDs. Something/somebody/somewhere/someday might want NSS per-byte.

Your embedded raise/lower controls are even more general the my =single/=multi configuration scheme. Yes, using separate buck50 SPI lines could do the same thing in terminal mode, but keep in mind that buck50 SPI (and I2C and USART) can also be accessed via the socket and pty interfaces.

epninety commented 3 years ago

OK, hands up, I was wrong. While working on something else today, I found a use for an 8 bit SPI transfer. You can set or lift the Write Enable latch on a 25AA256 E2PROM.

Of course, you still can't actually write any data to it, or read from it, so it's not very useful. And if you hold NSS low, you'll only get one transaction, so the only way to actually write to the device would be to go in to spi mode, clear the latch, exit SPI mode, re-enter SPI mode, and then write some data.

At least when reading the device, you could go into SPI mode, and then read the whole of the device in one long transaction of just over 32k bytes. That's probably best done over the socket or pty interfaces I would think. I'd probably lose count otherwise :)