Best way to receive on SPI for Arduino - Page 1
Forum section: electronics / general

author: ZeroResistance
author_rank: Frequent Contributor
timestamp: December 25, 2016, 08:31:19 pm
content: I am working on Arduino Nano which runs at 16Mhz.Basically I want to receive data on SPI and retransmit on UART.For the UART I have set the baud rate @ 1 MBaud which is approx 97KB/sOn SPI I could receive at similar rate or maybe slightly faster maybe 1Mhz clock rate at this I may get around 122KB/s.I would like to know what would be the best way to buffer the data .. I may be receiving packets of 20bytes every 10ms intervals or so ...Should I do this in polling mode for SPI receive or is interrupt mode better?I was thinking of wiring up the master slave select to the slaves INT0 pin so I get an interrupt when the transmission is about to start then I simply poll the SPI and get all 20 bytes .. is this a good way to go about it? ... My next question is where to send this received data to UART from should I do it in the INT0 interrupt itself or do it in main...I'm open to any alternative way too.. Many thanks!

author: kcbanner
author_rank: Contributor
timestamp: December 25, 2016, 08:40:16 pm
content: 20 bytes every 10 milliseconds is 2kB/s, which is much less than your send speed, so you shouldn't need a very big buffer at all. I'd just run the SPI in interrupt mode, receive the full packet, then immediately send out the UART (which will probably be idle waiting for data).

author: ZeroResistance
author_rank: Frequent Contributor
timestamp: December 25, 2016, 08:46:24 pm
content: Quote from: kcbanner on December 25, 2016, 08:40:16 pmOK! that sound's good! so do you think its better to do all the processing in the INT0 interrupt itself or set a flag and do the UART send part in main...

author: kcbanner
author_rank: Contributor
timestamp: December 25, 2016, 09:18:31 pm
content: I'd just append the received bytes to the buffer in the SPI received interrupt. In your main loop, if buffer has > 0 bytes in it, send via UART. You can use interrupts to drive your UART loop as well. Ideally you are doing a minimum amount of things in your interrupt handlers themselves. It's up to you though, and depends on how much else is going on in your program.

author: ZeroResistance
author_rank: Frequent Contributor
timestamp: December 26, 2016, 05:29:18 am
content: Quote from: kcbanner on December 25, 2016, 09:18:31 pmActually currently I'm not using the SPI receive interrupt since that is generated on every byte received instead I'm using the INT0 external interrupt on falling edge trigger this would be generated when the master pulls the SS line low and I would be reading the whole 20 byte packet within this interrupt ..

author: kcbanner
author_rank: Contributor
timestamp: December 27, 2016, 02:33:09 am
content: Quote from: ZeroResistance on December 26, 2016, 05:29:18 amThat may be a relatively long time spent in an interrupt handler.

author: ZeroResistance
author_rank: Frequent Contributor
timestamp: December 27, 2016, 05:12:34 am
content: Quote from: kcbanner on December 27, 2016, 02:33:09 amIf I use the SPI interrupt I would have to add a delay at the master end between each byte for giving time to the slave to respond or maybe reduce the clock speed ... currently I have slowed the clock to 1Mhz so that would be around 8uS per byte ...

author: kcbanner
author_rank: Contributor
timestamp: December 27, 2016, 04:05:08 pm
content: You should check out the datasheet for your avr, I'm assuming atmega328p.QuoteAnd about the interrupts:QuoteSo why do you think you'd need a delay at the master? Your ISR has the time it takes to receive the second byte to handle receiving the first byte. At 1 MHz (8 uS per byte), your ISR can take ~8uS. If your AVR is running at 8MHz, that's 8 million clocks per second. Looking at the instruction timing for AVR:QuoteUsing all this, you can calculate the length of ISR you can have and never miss a byte. 8uS / (1 / 8MHz) is 64 clocks per byte received. So your ISR just has to fit with 64 clocks. Adding a delay at the master end would defeat the whole purpose for why this interrupt and double buffering system exists.

author: rstofer
author_rank: Super Contributor
timestamp: December 27, 2016, 05:44:33 pm
content: Quote from: kcbanner on December 27, 2016, 04:05:08 pmSo, you have to be able to suspend the main() code, enter the ISR, save regs (context), grab the byte, insert it into a circular queue, update the insertion pointer, pop regs (restore context) and return to main() with around 40 instructions (some 1 cycle instructions, some 2 cycle instructions). It would also be nice if main() could get some work done before the next interrupt. I would sure want to see what the compiler generates!I haven't tried anything like this on the Arduino. I do have something similar on an mbed but that gadget runs at 96 MHz. Although I didn't use the feature, both the SPI and UART can be connected to a DMA channel. I suspect there is some pointer management but that will be on a per packet basis.

author: kcbanner
author_rank: Contributor
timestamp: December 27, 2016, 09:05:43 pm
content: Just realized OP said he was running at 16MHz, so my estimate of instructions per ISR would be double, 128 instead of 64. Plenty of room!

author: ZeroResistance
author_rank: Frequent Contributor
timestamp: February 21, 2017, 10:51:56 am
content: Quote from: kcbanner on December 27, 2016, 04:05:08 pmYes I didn't want to add the delay it didn't make sense to me I was following Nick gammon's post's on the SPI using the arduinohttp://www.gammon.com.au/spihe seems to have done lots of work on the arduino spi and has documented it well, probably the best resource I have found on the spi for ARDUINO or in general too.and he also had trouble sending data at high rates to the slave so he had to pull back on the master clock, I didn't have time to investigate into the issue but I would have very much liked to get it working without any delays.

author: rstofer
author_rank: Super Contributor
timestamp: February 21, 2017, 11:53:06 pm
content: If your incoming packets are truly of a fixed length or there is some way to determine the end of a finite length packet, I would create a circular queue of packet structures and in my case, I added a 'filled' flag to the structure along with the data (and data size since my packets weren't easily delimited but had a finite length). Then just let the SPI interrupt routine build the structure (packet), one byte at a time and when complete, mark the structure as 'filled'. When the UART is finished sending a packet structure, it resets the 'filled' flag.Now, each of the packet structures represents an element of a circular queue of packets so you can have as many packets in the queue as you want. Perhaps just a few... But when things go sideways, it is easy to increase the size of the queue without changing the definition of the packet structure or the code that fills or empties the queue.I was taking incoming SPI bytes, doing some manipulation, converting the results to an HPGL sentence in a packet and ultimately sending them over TCP to a LaserJet.