I don't quite agree with this approach. First of all an SPI transaction can be screwed up by a single short spike on the clock line. Secondly sharing structs between various parts of the software is a major pain in the ass. If it gets changed in one place, it must be changed everywhere and how do you deal with backward compatibility? In many protocols you see a message consists of seperate strucs of data each with header which has the type, length and sometimes a version. Still not ideal but at least it has some way to deal with incompatible versions. Last but not least I'm weary of sharing large / many data structures. That is usually a sign a distributed system hasn't been partioned properly and thus there are too many dependancies which need to cross the border from one device to the other.
I'm sorry but I'm really unable to find any valid point in your reply.
UART is immune to "single short spike"?
Checksumming against electrical corruption applies to any of the mentioned schemes similarly. Clear hardware framing makes this task so much easier, you can resync right to the next packet.
I'm actually using UART as well quite a lot and have tried several ways of framing, message ID'ing, error correction, etc. Not super complex stuff, but if at all possible, I'll avoid it.
Keeping the communication structure on the both sides pain in the ass? Tough game, that's what you have in the multi-MCU system. I can't see how you are going to magically get rid of this task. In multi-MCU system, especially on the same physical board, a simple struct{} is by far the easiest, and can even share the same .h file, so only once place needs modification. It's also readable enough so that it self-documents. I can't figure out any way to get it more maintainable than that - what you suggest requires 3-4 orders of magnitude more maintaining and matching between two systems. Define packet structure - change on both sides when necessary. Define message types - change on both sides when necessary. Document everything. Keep the documentation up-to-date.
Unless, of course, the system is just simple enough. For one or two simple messages, it doesn't matter. Especially if it doesn't need to be super robust.
ASCII UART based systems are nice for human intervention; most I have seen are
buggy as hell and
total pain in the ass to automate. Yeah, it's OK to use a buggy ASCII parser when you are a human. Everything happens slowly when you type, so race conditions are left untested. Framing does not work reliably? You hit some random keys or reset manually until it works again. Some command is totally left unaswerred? Not a big deal, just command it again. Nothing works? The parser crashes due to wrong type of line feed in use
- configure your terminal until it works. As a human, we have a great troubleshooting system called "brain" which "autonegotiates" the communication link.
Last time I needed to
actually use - i.e., not test it on the lab table - such a product (a scanning 2D LIDAR using such a "simple" text-based UART interface for configuration, switching to binary communication after init so you cannot do anything with it on terminal, anyway
), it took about 300 lines of code and a few days of bug hunting development and reverse engineering to get the it work reliably. The documentation of communication is nicely laid out and looks professional, but
it doesn't match the actual behavior 1:1. Things like "it randomly completely ignores a certain message" are extremely easy when you initialize it as a human. Or when it replies something completely unexpected back, you use your super capable
AI Brain^tm technology to parse and decide what to do! But in actual production code, it's added complexity to the state machine, again. And they seem to be going out of business right now.
In these kind of systems, you typically have two layers of state machines (actual messaging, and message-level parsing/framing/error checking) on both ends.
And yes, with this LIDAR, it's just normal that it gets stuck or crashes and you need to reset it. They even offer a handy hardware signal to make that easy. But they provide checksumming! Never had a checksum failure, though.
Sure, you can make the ASCII UART interface work, and it has many appealing points. But for multi-MCU system? Nah, it's not super difficult. But compared to many much easier options (which I have covered already), this is completely on a different level of complexity than a simple memory swap using a framed low-level protocol such as SPI or CAN, equipped with a simple checksum automatically calculated over the framed data. Especially if you need robustness and/or high performance in the system.
My issue is that I'm doing such complex systems alone right now that I simply don't have time to play "let's design a nice protocol" games - the solutions just need to be simple and robust.
And in the MCU world, we always don't have full libc with all fancy-pancy sscanf() stuff available.