Some SPI questions...

cancel
Showing results for 
Search instead for 
Did you mean: 

Some SPI questions...

10,480 Views
pittbull
Contributor III
Hi all,

I'm using an S12X mcu as SPI slave and another one as master. The master simulates SPI wit I/O ports while the slave uses the integrated SPI module. I use both lines (MOSI, MISO) to transfer eight bits simultaneously and that works fine. Now my questions:

1. What is the best way to handle the situation if one device has fewer data to send than the other one? Do I need a software protocol, some type of framing?

2. I configured the mcu to automatically use the /SS line. How can the SPI module detect if the master releases the /SS line prematurely? Must I configure it as general I/O port (input) in order to work?

Thanks in advance,
pittbull

Message Edited by pittbull on 02-25-200601:58 PM

Labels (1)
0 Kudos
10 Replies

127 Views
bigmac
Specialist III
Hello Pittbull,
 
Another way of handling the SPI transactions that I would probably consider is for the first byte of each packet, originating from either the master or the slave, to always represent a "command" or "token" that would provide information on both the length of the data packet following and what the particular data represents, or how it is to be processed.  This would allow for the situation where different data could have the same packet length.
 
It would also be possible that a packet could consist of a single token byte, where it represents a request for the receiving MCU to return specific data.  In this case, the returned data might also be headed with the same token, after retrieival and assembly of the packet.
 
In the case of the slave, the token would be placed in the SPI buffer, ready for interrogation by the master, and the remainder of the packet in a FIFO buffer accessed by the interrupt handler.  So the master should always be aware of the number of bytes to complete the received packet, once it has received the non-zero token byte.
 
I cannot understand why you believe that the master would not be able to fully complete the SPI transaction with the slave.  If you are using a software SPI for the master, you will always have direct control over the SS ouput to the slave.  I also cannot see any reason why the master could not continue with byte transfers until it had no more data to send and it received a null token byte from the slave.
 
Anyway, I hope this presents a few more ideas.
 
Regards,
Mac
 
0 Kudos

127 Views
pittbull
Contributor III
Hi bigmac,

Thanks for your reply.

> I cannot understand why you believe that the master would not be able
> to fully complete the SPI transaction with the slave. If you are
> using a software SPI for the master, you will always have direct
> control over the SS ouput to the slave.

Yes, when I use my test equipment, I have full control over the master. But when other people use my SPI slave, they can make a mistake and this would confuse the device if not handled. The main reason that I need to detect change of /SS is to bring the software protocol to its initial state (Begin with e.g. packet header, token, lenght at next connection).

Cheers,
pittbull
0 Kudos

127 Views
bigmac
Specialist III

Hello Pittbull,

Pittbull wrote:
... But when other people use my SPI slave, they can make a mistake and this would confuse the device if not handled. The main reason that I need to detect change of /SS is to bring the software protocol to its initial state (Begin with e.g. packet header, token, lenght at next connection).

From what you say, I assume that the slave is not permanently connected to the master, and may be powered up separately from the master.  So the master would need to ascertain when the slave was actually connected and operating, before initiating a packet transfer, and the slave will not return anything until its /SS input becomes low.

Here is a suggested possibility:

  • If you arrange for a pull-up on the MISO line, the returned data value will always be $FF if the slave is not connected.  Let's assume that a header or token will never have this value.
  • Let's also assume that a special token value is allocated, specifically to detect the presence of the SPI connection.  So when a slave powers up, this token would be placed in the SPI buffer, waiting for interrogation by the master.  If the send buffer became empty, the same token would again be placed in the send buffer.  This would continue up to the point where this same token was correctly received from the master, and there would be a final returned token in response.  So the slave can then be confident that a connection is established, and could proceed with the first data packet token.
  • From the viewpoint of the master, it can firstly ascertain when returned data is no longer $FF in response to periodically sending null bytes.  The master could then send the special token value, expecting the return of the same value when the SPI connection is fully established.  When this occurs it could continue sending nulls until either a null is received, or a data packet token is received, followed by its data.
  • At the conclusion of the transfer of each data packet from the slave, the master might again send the special token, to verify that the connection had not been broken during the packet transfer.

I think this sort of approach avoids the problems that you anticipate with possibly incomplete initial data byte.

Regards,
Mac

0 Kudos

127 Views
pittbull
Contributor III
Hi bigmac, hi all,

You helped a lot. With your help and some own investigations I think I found my solution:

Because my slave device and the master have to send an arbitrary number of bytes and SPI can't send without receiving (and also vice versa), I must define a byte value that represents an 'invalid byte'.

Whenever one node has nothing to say, it sends the 'invalid byte'. To ensure that the invalid byte is not in the raw data, I use a widely known technique called 'byte stuffing'. For those who don't now what that means: http://www.cs.umd.edu/~shankar/417-F01/Slides/chapter5c-aus/sld016.htm

I also use the byte stuffing to implement flow control. My slave device tells the master if its receive FIFO gets filled up and sends a special message to the master. If the slave has enough space again, it sends another message telling the master to go on. It works like XON/XOFF. The master itself does not need flow control because it simply stops clocking when it temporarily can't receive.

My slave runs on an S12XA256, SPI is completely interrupt-driven and uses FIFOs for TX and RX. When I'm initializing the SPI module, I first put the 'invalid byte' into SPI0DR, then setting SPTIE to ensure that the first byte transmitted is not a zero but my 'invalid byte'.

....
SPI0CR1_SPIE = 1; // Enable RX interrupts
SPI0CR1_MSTR = 0; // Slave
SPI0CR1_CPOL = (spi_params & 1); // Clock polarity, 0 == active high
SPI0CR1_CPHA = !(spi_params & 2); // Sampling at even edges
SPI0CR1_LSBFE = !(spi_params & 4); // LSB first
SPI0CR2_SPC0 = 0; // Normal mode
SPI0CR1_SPE = 1; // Start SPI module

// Put dummy byte into send register on startup
while (SPI0SR_SPTEF == 0)
{
}
SPI0DR = DUMMY_BYTE;

SPI0CR1_SPTIE = 1; // Enable 'TX register free' interrupts
....

I monitor the /SS to reset the 'byte stuffer' when /SS goes high. I do this by reading the PORTS input register (PTIS_PTIS7). I don't know if it makes a difference to use PTS_PTS7 instead.

The only situation I can't detect until now is when someone switched the slave off and on again while the master is continously clocking. Although, that doesn't confuse the slave but it would be nice if the master can recognize it.

Any suggestions?
0 Kudos

127 Views
bigmac
Specialist III
Hello Pittbull,
 
Some further comments:
Quote:
Because my slave device and the master have to send an arbitrary number of bytes and SPI can't send without receiving (and also vice versa), I must define a byte value that represents an 'invalid byte'.
You have not previously mentioned the amount of data that needs to be transferred.  I would tend to limit the "packet" size to 16 bytes maximum (perhaps 32 bytes at the outside).  If the data to be transfered exceeds this limit, multiple packets can easily be sent.  This would mean that the header (token) byte could indicate the data length within the lowest four (or five) bits.  The upper four (or three) bits would be a unique, non-zero value for that type of header.  Hence, the master (or slave) would always know how many data bytes to expect within the packet.

I am inclined to rename your "invalid" byte as an "idle" byte or token.  So this byte would also have a unique value, not replicated by any data packet header.  I would consider a suitable value $55 or $AA (alternating 0's and 1's), that cannot be replicated in the event of an incomplete byte.

I would not be too concerned if any of the data bytes within a packet were to have the same value as the idle byte, or the packet header byte.  This is because the master (or slave) already knows how many data bytes it expects to receive, and would not be looking for further headers (tokens) until after it has received the required packet data.

 ... I first put the 'invalid byte' into SPI0DR, then setting SPTIE to ensure that the first byte transmitted is not a zero but my 'invalid byte'.

I don't think this would really be a problem since the master would need to treat both $00 and $FF values as invalid header values.


I monitor the /SS to reset the 'byte stuffer' when /SS goes high. I do this by reading the PORTS input register (PTIS_PTIS7). I don't know if it makes a difference to use PTS_PTS7 instead.

I still cannot understand why this complication is necessary.  I think I would simply not put any packet data into the slave send buffer until I had received an idle byte from the master, and would also expect at least one idle byte as acknowledgment, before placing subsequent packets.


The only situation I can't detect until now is when someone switched the slave off and on again while the master is continously clocking. Although, that doesn't confuse the slave but it would be nice if the master can recognize it.

Perhaps if the master does not receive at least one idle byte immediately following a received packet, it should discard that packet data.  Another special token to request a resend of the prior packet might prove useful.

If the slave can lose power whilst connected to the master, you may need to consider separate open collector (drain) buffers on the MOSI, /SS and clock lines at the master, with resistive pull-ups at the slave.  If the interconnection between master and slave is potentially  subject to static discharge, buffers on all lines, for both master and slave, are probably a good idea anyway.

Regards,
Mac

 

0 Kudos

127 Views
pittbull
Contributor III
Hello bigmac,

bigmac:
> You have not previously mentioned the amount of data that needs to
> be transferred.

My device contains lots of software e.g WLAN driver, TCP/IP, webserver, eMail client and so on (Yes, all fits into a single S12XA256 without an opreating system). Users can connect to it using either SPI, RS232, I2C, or a proprietary parallel protocol.

The low level protocols (SPI, RS232 etc.) must be fast. They must not be too complicated to use but should have somewhat of reliability. Data loss is handled on a higher level (TCP/IP or a command/response interface). Therefore I do not packetize everything at low level which would generate too much overhead. The simple byte stuffing does exactly what I need: Escaping 'idle bytes' from the payload and inserting a few messages into a raw data stream. It is also simple to use for customers, even if they connect another embedded device whith very low memory and computing power.

pittbull:
>> I monitor the /SS to reset the 'byte stuffer' when /SS goes high.
>> I do this by reading the PORTS input register (PTIS_PTIS7).
>> I don't know if it makes a difference to use PTS_PTS7 instead.
bigmac:
> I still cannot understand why this complication is necessary.
> I think I would simply not put any packet data into the slave send
> buffer until I had received an idle byte from the master, and
> would also expect at least one idle byte as acknowledgment,
> before placing subsequent packets.

That is because at low level both can send whenever and whatever they want. A fast communication should not wait for the other end to allow transmission (with only few exceptions, e.g. when there's no buffer space, flow-control). The idle byte only exists because SPI has no independent RX/TX mechanism. I monitor /SS to reset the byte stuffer. If I would not and the master releases /SS too early (e.g. because of a crash), the byte stuffer could probably be left in the wrong state. This would cause some missed or wrong bytes, not very dramatic because the high level protocols can recover from this, but it causes packet repetitions which degrades overall performance.

Cheers and thanks again for your replies,
pittbull
0 Kudos

127 Views
rocco
Senior Contributor II
I have used the SPI to communicate between processors a number of projects, and I can safely say that it is tedious. The software protocol is typically complex, and extra I/O is usually required.

Some of the problems with the SPI for inter-processor communications are:

1) Without additional I/O, the slave has no indication that a transfer is about to take place, yet he needs to have his byte preloaded.

2) The slave does not know when transfers have stopped, unless the master uses slave-select with Clock-Phase set to 1, and the slave monitors it. (What we need is an interrupt when SS goes both low and high).

3) Without additional I/O, a slave has no way to initiate or request a transfer.

Lately, I've been using an FPGA to handle Slave-Select and interrupts. I can provide details if you like, but I doubt you want to go there.

To try to help with your first question, I will describe one of my software protocols, which may be similar to what you are looking for:

The master uses a timer to exchange data with the slave. Since the slave cannot initiate a transfer, this assures that the slave gets serviced. In one implementation, I ran the timer's output-compare to the slave's IRQ so that the slave received prior warning of the transfer (the slave needs to pre-load it's first byte PRIOR to the transfer). This can serve as your framing, as it does for me.

The master then asserts Slave-select, and transfers a fixed number of bytes (Clock-Phase is set to 1). Each processor's first byte is the count of the number of bytes it has to offer, but the fixed-maximum is always exchanged to make the ISR simpler (my scheduler likes fast ISRs).

When the respective ISRs have exchanged all of the bytes in the packet, the master deasserts Slave-select, and both processors invoke their respective tasks to digest the data.


As for your second question, when you ask "if the master releases the /SS line prematurely?" do you mean during one of the bytes? or before the last expected byte?

If SS goes high during a byte, the Mode-Fault flag will set, and you receive an interrupt.

But if you mean prior to the last-expected byte, then you need to monitor SS. I believe that if you have the slave-select's port-pin set for input, you can see the external state of SS on the pin through the port register, even though the SPI has control of the pin. At least it works that way on the HC08GP32.

Hope that help a little.
0 Kudos

127 Views
pittbull
Contributor III
Hello eetee and rocco,

Thanks for your replies.

>> As for your second question, when you ask "if the master releases
>> the /SS line prematurely?" do you mean during one of the bytes? or
>> before the last expected byte?
>> If SS goes high during a byte, the Mode-Fault flag will set, and
>> you receive an interrupt.
>> But if you mean prior to the last-expected byte, then you need
>> to monitor SS. I believe that if you have the slave-select's
>> port-pin set for input, you can see the external state of SS on
>> the pin through the port register, even though the SPI has control
>> of the pin. At least it works that way on the HC08GP32.

These are good news. Maybe it's possible that the /SS pin can issue an interrupt even it is under control of the SPI module? However, if the slave can always detect a release of /SS, I'll try it this way:

- Master initiates communication by driving /SS low
- Both devices send data length first (two bytes).
- Data received above this value will be ignored. If one has nothing to transmit, it sends 0x00, 0x00
- If the master wants to abort communication, it drives the /SS line high
- The Slave recognizes the /SS transition and thus it knows which byte the master has received completely
0 Kudos

127 Views
eeetee
Contributor I

hi:

 

"I can safely say that it is tedious" is the understatement of the year!

i've been working on a 6 processor scada system for over a year now. the communications protocol issues have dominated about 10% to 15% of the design time. i't more choreography than programming.

 

 

ed

0 Kudos

127 Views
eeetee
Contributor I
hi:
 
i,m not sure of the specific chip you are using, but the e128 has a bi-directional mode you may consider. when you set the spie bit in control register1 it will configure (overriding any port configuration) the input/output corretly.
 
one way to handle variable data lengths is to send the number of bytes ready to transmit as the first byte or word of the xmission.
 
a second way is to send a consistant packet size padded with nulls at the end. the drawback to this method is if you actually have a bunch of zeros as actual data it will be tough to find the eof.
 
although it may be over-kill i always send crc or checksum data just in case.
 
 
regards,
 
ed
0 Kudos