I need to make choice on this. I have a project where one micro controller with usbMIDI functionality will be passing info back and forth with 8 other micros (in this case standalone 328s with arduino bootloader). I’m using the 328s essentially as pin expanders but I’ll be making use of both interrupt pins, analog reading, some PID control outputting high frequency PWM… more than than just a few extra gpio… 8 identical setups talking to a 9th device.
I have enough pins for either protocol and everything will be tightly packed in one box. I’ll be passing, say, 25 bytes back and forth pretty much constantly. I know SPI is faster, and maybe less memory intensive (?), but my search results indicate that 328s aren’t great as SPI slaves… something about read speed and not having an interrupt that is fast enough to catch the first few bytes. For this small amount of data the extra speed is perhaps a luxury but I’d also like to keep the I2C bus on each satellite micro open for future expansion. If I set them up as I2C slaves now they obviously can’t communicate with another peripheral as the bus master.
So, I’m looking for help assessing pro/con on these two protocols in this role. I plan to assemble a small prototype with the usb capable chip and one satellite - I actually had one together years ago but dropped the project due to cost - but I’m not sure what will come up when I expand two 8.
Why the ATmega328P ? Can you buy those ?
It would be nice if you can switch to other boards, however, some boards are bad I2C Slaves.
If you scroll down here: http://www.gammon.com.au/spi then you see that a SPI Slave is just an interrupt that handles one byte.
You could be at the limit of what is possible, and both I2C and SPI could be too much for the ATmega chip. What is going on at interrupt level ? Do you need to act fast on interrupts ?
Serial communication is the most straightforward, but the ATmega328P uses that to upload a sketch. With SoftwareSerial, your project probably won't work anymore.
How many pins are still available and do you need to transfer data both ways ? With 4 signals it is possible to create a handshake communication without timing and without a clock signal.
There could be a project for a VGA or TV signal that uses an alternative way to output messages, I either remember that vaguely or I have dreamed about it
Conclusion: You are at the limit of the ATmega chip. All the options (I2C, SPI, Serial, others) are still open.
Seems like you have it figured out, but sounds like a very bad idea to me; mainly because 8 state machines reacting to messages on a bus have no hope of proper timing and bus response. Your 9th device will need to deal with out-of-sequence responses just like Ethernet packets.
There is not enough technical speak in your introduction to suggest alternative approaches but I suspect that unless you take care and develop your own messaging protocol that you are going to be very frustrated after the 3rd node.
My choice of 328p (which I may or may not be able to buy due to supply chain volatility) is based solely on my experience with it in the Uno. There is a lot of reference material, including on using it as a standalone, I can give it a bootloader, and my initial mockups used it. I had no issues with I2C in terms of competing interrupts though I haven't tried SPI (need to build it back up for that).
The interrupt on the peripheral 328s is monitoring a hand turned rotary encoder. The ISR was a very tight and fast state machine. When I polled the input ins loop I had very noticeable missed pulses, with the interrupts it probably over performs given the speeds in involved.
You mention serial. Why would serial between these devices be more appealing than I2C? Or any other built in protocol?
My experience of using I2C to pass data between Arduinos, ATMega328Ps in this case was not good.
If the slave Arduino is doing stuff which requires interrupts, Serial.prints() or accessing another SPI device in my case, it does not work well at all.
Is that vote in the direction of a different protocol or a caution to find another way to handle all the tasks that I intend to do using a single controller?
The Serial communication gets an interrupt and then puts the received byte in a buffer. That's all. Nothing is broken when interrupts are turned off for some time or are missed. A checksum can be added to detect missing bytes.
Using the I2C bus will probably work, if you do everything just right.
However, the I2C uses a longer interrupt routine and the Slave uses clock pulse stretching (in the onRequest handler). If something goes wrong, the Wire library might halt the Arduino board. A timeout was added, but writing code to deal with a timeout and trying to get it all going again is not easy. The I2C bus is also a weak bus, since the high level is made with a resistor.
I have a project with I2C Slave and then I decided to add a DS18B20 to a Slave. That didn't work because the OneWire library turns off the interrupts for a while. So I changed the OneWire library to catch the DS18B20 data without interrupts. That can fail, so I keep trying until I get valid data.
I can go on with this, I have not even started
I see. Well I looked over my notes and I probably overstated the amount of data and the frequency of external interrupts.
The external interrupts will probably be catching a pulse and running an ISR every 4mS or so worst case, and even then it will only be sporadically relevant when I go to twist a knob. There will be times when the master controller will output 25 bytes of data to each peripheral in quick succession but these will be times when user input will be unlikely (like startup and file changes on the computer). Data sends from the master can be restricted to when something changes and the peripherals will hold on the status for when the master requests it - whether it requests at a convenient time for the peripheral (due to interrupts) is the core of the problem it seems.
I rebuilt my prototype and did some testing. With Wire running at 400kHz a send of 9 byte is 340uS and receive of 4 bytes is about 200. So, 8 channels is about 4.3mS. Upping the clock to 800kHz improved this by about 25% but any faster and the total time actually went up. I didn't try reducing the pull-ups from 4k7 though. The rest of my code on the I2C master side is a tiny in comparison so I think I'll be OK.
Which is good because I don't think SPI is an option. It seems the atmega can't have its SS pin moved and I need pin 10 for a PWM signal. I thought it could be moved but only if you are using it as the master (then you can use any pin) but as a slave it seems it must be 10.