Reputation: 31
I have a rather simple question regarding setting a finite-state machine (FSM) in a MCU through the UART.
I have a rather small FSM, approximately 15 states, each only associated with one event. The FSM is being set by sending data through the UART, however, simple data are also received through the UART. An example could be:
As the UART works as a SPI control, I'm wondering if there is some standard or smart method, to distinguish between if the received data is a Set FSM or if it's raw data being sent. My current solution is limited to, that two bytes are send each time, one setting the FSM and the other is raw data. This solution carries a lot of redundant data, as the FSM part is being sent for each byte of raw data I'm sending. Also the number of data can vary, which complicates the task of finding a method, which simplifies the problem and reducing the number of redundant data being said.
So I was thinking if any of you know a smarter method, or if there is some general standard to follow?
Upvotes: 1
Views: 831
Reputation: 40347
Although not the lowest in overhead, one of the simplest ways of unambiguously encoding structured data in a serial stream is to represent all values with human readable encoding, and use printable or non-printable characters to provide framing breaks between them.
At a "wordy extreme" this could be something like
display 192\n
But there are far more terse possibilities which remain quite readable, for example
05c0\n
Where "05" could be the two-byte hex encoding of an 8-bit FSM mode, and "C0" would be the two-byte hex encoding of an 8-bit data value, and "\n" is a newline character. Since the newline character cannot ever be mistaken for hex data, your receiver knows that whatever it hears following one must be the mode number of a new message.
This has a number of advantages:
(You can of course make such a system additionally robust against corruption of data in transit by adding checksums of any warranted length, for example at the end of the data and preceding the newline delimiter)
However, you did want to avoid redundant data, and this clearly adds some. There's a tradeoff between simplicity and compactness, but there are some things you can easily do:
Of course this is still a bit over 2x overheard. With serial port speeds often quite high compared to the amount of data needing to be moved for many problems, that may not be an issue. But maybe it is not a price you are willing to pay. In that case, there are some other possibilities in common use:
You can use a binary encoding, but declare some value as an illegal character. For example let's say you make 0xff an illegal byte, and any time you see it you treat it as a "header" indicating a new record. The problem is now that if this could be an actual value you need to be able to transmit, you need a way to escape it in the data stream. So you introduce another byte as an escape character, and escape any occurrences of the framing byte or the escape byte. This can get tricky, but it is very much a scheme in real use - things like Ethernet and many radio protocols use closely related ideas. Just as with other means of introducing framing, once you have designated a scheme for delimiting the frames you can dedicate part of the frame to a checksum or error correction code.
You can use time gaps in the transmission to indicate a new message. But the gaps tend to slow things down quite a bit, compared to the overhead of a printable hex encoding. The danger here is that a fairly short gap between characters may register just fine when you have a direct connection between amply serviced local bus UARTs on two systems. But often today, you'll end up with something else in between, such as serial proxied over a packetized USB interface (or even some Serial-Ethernet bridge, or Bluetooth, or whatever clever new invention someone sticks in the middle of your system next year). Time based framing certainly has been used, but to do so in a way futureproof against intermediates which may insert their own delay, you have to make the gaps long enough that unless the messages themselves are also fairly long, you loose any efficiency advantage.
You could use a 9-bit serial format, and have that extra bit indicate the start of a message. The trick is that your hardware (and operating system on each end) has to support this - now and in the future. Sometimes you can achieve interoperability with 9-bit serial using an 8-bit-plus-parity encoding and monkeying with the parity settings in real time, or even use switching parity to introduce an intentional error as the design of your framing scheme. This too has been done, but can be a real headache if you have to re-implement one end of your scheme on a new platform with a different UART or serial API.
You can use the hardware flow control signals (if present) as framing indicators - but they have to be wired up, and you have to be careful to assert them in a manner synchronized with the data itself. This can be a bit harder than it first seems - almost all UARTs will provide a handy interrupt when their transmit buffer is empty, but only some can also provide an interrupt after the last byte has actually been clocked out on the wire, so you may need a timer to come back a byte-time later and change the control signal if you want it to actually change between bytes.
You can make the communication bidirectional, where the receiver acknowledges receipt or complains if it loses frame synchronization. Possibly you can use a long time gap as part of the negotiation. This certainly has advantages in reliability. But over almost anything in common use today except a direct UART-UART connection, it also results in a severe slowdown. Packetized intermediaries such as the USB-serial converters which are more commonly encountered than actual local bus UARTs today end up introducing a lot of round trip delay, such that serial protocols requiring individual acks of short messages are a lot slower than those which either do not require them, or those which share something like TCP's ability to optimistically push ahead but rewind and recover if an ack is not eventually received.
As the saying goes, you can pick any two of simplicity, reliability, or efficiency - and with almost countless degrees of trade-off or even definition of what those goals practically mean.
Upvotes: 2