SPI Slave Message

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Been a while since I've visited here.  I don't want to beat a dead horse but I've gone through at least 12 pages of AVRFreaks search results I didn't find anything regarding a SPI slave that receives more than one byte at a time using an interrupt.  For starters, I think the last post of this discussion:

 

https://www.avrfreaks.net/forum/avr-spi-slave-shift-register?skey=avr%20spi%20slave

 

is misleading.  The MISO line (Master In Slave Out) is a bit for bit copy (since it is a shift register) of MOSI (Master Out Slave In) if the MISO line is configured as an output.  The incoming bits on MOSI are automatically boomeranged (one clock cycle behind) on MISO.

 

 

I have some snippets of code I started on for an ATmega328 project:

 

// SPI Receive interrupt vector
ISR(SPI_STC_vect)
{
  incoming = SPIReceive();

}




//*****************************************************************************
// Read SPI byte
char SPIReceive(void)
{
    // Read data byte from SPI port
    return SPDR;

} 



    DDRB = 0b00010000;                    // All output except b4 (MISO)
    PORTB = 0b11101111;                    // All inputs have pull-up, b4 output low




    // Enable SPI port and interrupt
    SPCR = ((1 << SPIE) | (1 << SPE));




//*****************************************************************************
// Read SPI byte
char SPIReceive(void)
{
    // Read data byte from SPI port
    return SPDR;

}


//*****************************************************************************

            
    // Clear SPI Interrupt flag by reading SPSR and SPDR
    dummy = SPSR;
    dummy = SPDR;



    // Enable global interrupts
    sei()

 

which I cobbled from various posts on the internet.  It is incomplete and untested as I am still coding other portions of the project.

 

 

 

My main question is how to deal with an incoming data message as opposed to a single byte.  The message can encompass values from 0-255 and varying message length so a designated termination character (i.e. nul or carriage return) is out.  Does the SPI interrupt fire off for EVERY byte (even if part of a consecutive string)?  Does the interrupt fire at the beginning of an incoming byte or end?  I could monitor the SS line that it remains low but for compatibility there is no guarantee the SS line will be used as intended (select line) as opposed to tied to ground.

 

I was thinking that once inside the ISR code of reverting to a polling/counter approach using the SPIF bit in the SPSR register.  Since all control comes from the master (particularly the clock rate) and data length is variable, this results in a wide range of possibilities.  Making some assumptions:

 

  minimum 8 MHz oscillator input to an AVR master with max SCLK division of 128

  maximum message length of 32 bytes

 

results in a SCK frequency of 62.5 kHz (period = 16 uS).  Since 8 bits transfer, that is roughly 128 uS per byte.   Would polling every the SPIF line every 1 uS for 250 counts within the ISR be a logical approach?  Does anyone have other suggestions?

 

-Thanks-

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:

The message can encompass values from 0-255 and varying message length so a designated termination character (i.e. nul or carriage return) is out.

 

Not necessarily.

 

Have a look at how SLIP handles frame delimiting or, for a newer method, COBS (consistent overhead byte stuffing).

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:
My main question is how to deal with an incoming data message as opposed to a single byte. 
The 328p datasheet says SPDR is double-buffered on receive so there should be no particular issue with having the ISR collect the most recently received byte (that triggered SPIF and led to the ISR) while the next byte is being clocked in. Obviously the ISR has to be over before the whole of the next byte is clocked in though!
attigeek wrote:
I was thinking that once inside the ISR code of reverting to a polling/counter approach using the SPIF bit in the SPSR register.
If you planned that why would you even bother with an interrupt at all? If you do use interrupts then the ISR has just one job - read the current byte then get out of there. Obviously for multiple bytes you need somewhere to put them as they come in - most people would use a FIFO (ring/circular buffer) for this.

 

So conceptually something like:

ISR(SPISTC_vect) {
    ringbufferInsert(buffer, SPDR);
}

and nothing more. 

 

You might want to also have some way to flag "end of packet" to trigger the main() code to then extract the buffered characters from the ring and use them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian-

Thanks for the correction.  I had to look up both SLIP and COBS.  While I do have some input as to protocol, I think something along the lines of SLIP or COBS would make the effort overly complex.  I'm more of a KISS (keep it simple stupid) fan.

 

One idea might be to require the protocol transmit the number of message bytes that follow as the first byte which would reduce the number of unknowns.  If anyone has direct knowledge of how the interrupt fires (initial byte or all bytes) that would still be helpful

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:
the protocol transmit the number of message bytes that follow as the first byte

Many protocols do that.

 

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson-

Thanks for pointing out the obvious which I glossed over.  I like the idea of the ring buffer.  I hadn't planned on the interrupt leading into the polling - it was just an idea.  That's why I was seeking some input before I started coding beyond the snippets I had.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

awneil-

Yes, I know many protocols transmit byte count.  I was trying to minimize the effort while maintaining flexibility.  Interesting how all the suggestions are coming from 'across the pond'.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:

... I had to look up both SLIP and COBS.  While I do have some input as to protocol, I think something along the lines of SLIP or COBS would make the effort overly complex.  I'm more of a KISS (keep it simple stupid) fan.

 

COBS encoding takes around 10 lines of trivial C; decoding a few less than that. SLIP runs to about double that.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:

awneil-

Yes, I know many protocols transmit byte count.  I was trying to minimize the effort while maintaining flexibility.  Interesting how all the suggestions are coming from 'across the pond'.

 

Americans had not woken up yet.   You will see them shortly.

 

You can have a pretty good guess where a reader comes from by her "active hours".

Life would be much easier if people just put their "country" into their profiles.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A lot of US was awake when #1 was posted ;-)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok.   OP would have been 10.38 in New York or 07.38 in San Francisco.

 

When does an American eat breakfast?

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The protocol of byte count followed by "bytecount" data bytes is very common and very easy to implement. 

 

That can be enhanced with a leading "message type" byte that allows for different data structures. It can also be enhanced by making bytecount into datacount such that it counts the actual data bytes which are then often followed by a checksum or crc. 

 

All of these are VERY low overhead, easily implemented, and easily documented.

 

As for American breakfast time, that depends strongly on external drivers such as job scheduling. For those who are retired or work at home, breakfast is commonly 0730 to 0930 (local) with some "outliers". For a few, breakfast happens some time during the day, usually before lunch.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Tue. Jan 14, 2020 - 06:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another advantage with having the byte count at a fixed position is that it makes it easy for a device to skip a message without understanding or interpreting it 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good Point.

 

An important factor in all  of the schemes I mentioned is that parsing can be done by basic byte counting. It does not get much more simple than that.

 

Now, things get a bit more dicy if you are NOT using async serial (for example Manchester or HDLC). In those cases, you have to decode into bytes and parse the byte stream rather than the undecoded bit-stream. But, this should not be an issue with  SPI since the transfer is always in byte-size blocks.

 

Actually, reviewing the initial post, I don't see why ANY of this is an issue. SPI devices are intended to work with a select input, typically active LOW. This event also functions as a frame start. Simply count  bytes from the frame start. Done.

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Tue. Jan 14, 2020 - 09:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brian-

Do you sample code for SLIP or COBS.  I'd like to take a look at for a potential future project.

 

-Thanks-

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
When does an American eat breakfast?

 

ka7ehk wrote:
As for American breakfast time, that depends strongly on external drivers such as job scheduling. For those who are retired or work at home, breakfast is commonly 0730 to 0930 (local) with some "outliers". For a few, breakfast happens some time during the day, usually before lunch.

 

Exactly.  I try to have breakfast with my 10 year old in the morning so we can talk about what his day, and mine look like.  Kinda nice.

 

 

As far as SPI goes, regardless of what side of a pond, or loo bowl the suggestions go, it's really up to you to determine how to 'Frame" your data.  As Cliff mentioned the receive is double buffered so set up a simple buffer thats one byte longer than your frame, and a simple byte counter, along with a timer.  THe timer is set to be one or two byte times longer than the total time to receive all teh bytes..  Set up a protocol that sends a preamble to wake up the receiver, and contains how many bytes of data.  Clock in to the buffer the received bytes and check the byte counter.  If the byte counter is 0, then disable teh timer and process the buffer.  If the counter s not zero wait for another ISR trigger.  If the timer times out, and the byte counter is not zero, then the data did not sync properly and the buffer should be flushed.

 

Right Coast Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:

Do you sample code for SLIP or COBS.  I'd like to take a look at for a potential future project.

 

For SLIP search out a pdf copy of  "TCP/IP Lean: Web Servers for Embedded Systems" and see chapter 10.

 

For COBS see the last page of this paper...

 

 

Attachment(s): 

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Another protocol to look at is S.N.A.P, although I have only used it with async comms, there is no reason for it to not work w/ SPI.  Either way you go set up a ring buffer and stuff it one byte at a time.

jim

edit: added link  http://www.hth.com/snap/

arduino version: https://github.com/PHaroZ/arduin...

 

Click Link: Get Free Stock: Retire early! PM for strategy

share.robinhood.com/jamesc3274
get $5 free gold/silver https://www.onegold.com/join/713...

 

 

 

 

Last Edited: Wed. Jan 15, 2020 - 01:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

attigeek wrote:
I could monitor the SS line that it remains low but for compatibility there is no guarantee the SS line will be used as intended (select line) as opposed to tied to ground.

The falling edge of /SS is used to reset the SPI module. If it is always active (i.e. low) your SPI module would never be able to re-synchronize with the SPI master's bit stream in case of incomplete bytes, EMI, collision on the SPI bus, etc. So having /SS tied permanently low is a bad idea.

 

With /SS being used as intended, it would of course serve perfectly as an indication of the start and end of a multi-byte frame/message.

/Jakob Selbing