Fast Software Manchester Encode/Decode - Page 1
Forum section: electronics / general

author: CurtisSeizert
author_rank: Regular Contributor
timestamp: May 09, 2025, 12:25:10 am
content: I am in the process of writing the firmware for an STM32 project that uses cheap AC coupled Toslink transmitters and receivers (from Cliff components) to implement an isolated UART connection. This is a situation where NRZ is NFG because there should be no DC component to the signal due to the AGC on the receiver side. The usual approach is to use Manchester encoding where each bit encodes a transition (HI maps to rising, LO maps to falling). In software, this means that 0x01 becomes 0x02, and 0x00 becomes 0x01 for each bit. So one byte of normal binary becomes two bytes.All the implementations I could find encode the first four bits to the first byte and the last four to the second byte. They do this by iterating through the bits, so you might get code that looks like this:Code:void encode_byte_iter(uint8_t input, uint8_t * output){for(int i = 0; i < 4; i++){output >>= 2;if(input & 0x01) output |= 0x80;else output |= 0x40;input >>= 1;}for(int i = 0; i < 4; i++){output >>= 2;if(input & 0x01) output |= 0x80;else output |= 0x40;input >>= 1;}}With the -O2 flag, this turns into 25 lines of assembly. Another way of doing it that I saw suggested would be to use a look up table because memory reads are fast on MCUs. For the price of 512 bytes of memory, you could run this, and with DMA the CPU overhead could be pretty reasonable.A way I thought of that may be useful to someone in the same situation is to use the first byte to encode the even (and zero) bits of the input and the second byte to encode the odds. This opens up the possibility of doing the encode in just a handful of bitwise operations. The encode and decode functions look like this:Code:void encode_byte(uint8_t input, uint8_t * output){uint8_t le1, le2, input2;input2 = input & 0xAA;/* Mask every other bit in input and generate the leading edge bit sequence*/input = input & 0x55;le1 = ~input & 0x55;input <<= 1;/* OR the two to make the first encoded byte */output = input | le1;/* Do the same for the second encoded byte */le2 = (~input2 & 0xAA) >> 1;output = input2 | le2;}uint8_t decode_byte(uint8_t * input){uint8_t output = (input & 0xAA) >> 1;output |= input & 0xAA;return output;}With the -O2 flag, this becomes nine lines of assembly for the encode and seven lines for the decode. The real advantage comes if you can work with 32 bits at a time, as those functions take the same number of instructions as the one byte versions. I didn't bother writing the iterating encode function for a 32 bit word, but it will probably end up taking about four times as many instructions as the one byte version. But even if you are running the encode/decode at the byte level and handling it in an ISR, you are looking at more clock cycles to enter/exit the ISR on an STM32 (~20) than it does to run the encode or decode operations. To make sure this does work, I compiled it on my PC with both byte and word encode versions and iterated through every possible uint8_t and uint32_t to make sure the encode/decode operation was operating correctly.I am sure I am not the first one to think of this, but since I couldn't find an implementation like this in my limited searching on the topic, I decided I would post it.Edit: Fixed a typo where I meant to say "bits" instead of "bytes"

author: Peabody
author_rank: Super Contributor
timestamp: May 09, 2025, 01:03:23 am
content: Quote from: CurtisSeizert on May 09, 2025, 12:25:10 amI understand what Manchester encoding is, but I have no clue what your description means. Could you show an example of how a stream of incoming data is operated on by what you're describing above?

author: CurtisSeizert
author_rank: Regular Contributor
timestamp: May 09, 2025, 01:30:11 am
content: Hopefully this makes it more concrete. Say you're encoding 0x0F on the tx side. In the "traditional" way of encoding where the first four bits become the first byte, etc., this becomes 0x55AA. Bits 1, 3, 5, and 7 of each byte represent the trailing edges and contain the data you're encoding, so if you just drop the others and combine everything while maintaining the order, you get the same data back, 0x0F.In the method I am proposing, 0x0F encodes to 0x5A5A, so if you do the same drop every other bit operation here, you get 0x55 or 0b01010101. So now, the four MSBs are the even bits (or bit pairs in the 16-bit encoded sequence) and the LSBs are the odds. Of course, the decode doesn't work by dropping every other bit, it just masks each byte with 0xAA to get the trailing edges and right shifts one of the bytes. Does that make sense?

author: brucehoult
author_rank: Super Contributor
timestamp: May 09, 2025, 01:40:23 am
content: Sure, there's no reason not to effectively permute the bits in the process of encoding them.Except ... you'll need to be really sure that you maintain synchronisation on the byte or word level. You need to think also about the effects of error bursts.

author: Peabody
author_rank: Super Contributor
timestamp: May 09, 2025, 04:16:47 am
content: I appreciate the effort, but as a hobbyist I'm just not able to follow your descriptions. My method for decoding Manchester is to have an interrupt on each transition of the incoming stream that determines the time since the previous interrupt, and places either one or two bytes of the current value into a circular buffer, each byte representing one half-bit. Then back in Main(), bytes are taken out of the buffer two at a time, and their value determines which bit is to be shifted into the current output byte. That also tells you if you have an illegal result, such as both half-bits high or both half-bits low.I'm sure that is far less efficient than any of the methods you're talking about, but it's logical to me. Anyway, I'm sure your new method will make more sense to someone who actually knows how to code. It's just above my pay grade.

author: peter-h
author_rank: Super Contributor
timestamp: May 09, 2025, 05:49:04 am
content: I have used Manchester for optical links years ago. The encoder is just a XOR gate, of the clock and the data. The decoder can be done in many ways e.g. a DPLL for clock recovery, but you can do it with a one-shot monostable and a few gates. I would not have thought software to do this should be complicated. What speed are you trying to achieve?

author: Doctorandus_P
author_rank: Super Contributor
timestamp: May 09, 2025, 08:16:00 am
content: I have not thought it though completely, but I would probably start with two LUT's. A 16 byte LUT to translate 4 bits into 8 Manchester "half" bits, and 256 bytes to convert 8 Manchester half bits into 4 "real" bits.It's a bit of a shame that Manchester encode / decode hardware is not built into (many) microcontrollers. A quick search suggests you can do it with the PIO peripheral of the RP2040.https://html.duckduckgo.com/html?q=Manchester+RP2040It's also becoming more common to have a bit of programmable logic inside microcontrollers for tasks like this.

author: CurtisSeizert
author_rank: Regular Contributor
timestamp: May 09, 2025, 12:05:43 pm
content: The background on this is that in early April I decided to crank out as many projects as I could to be able to get the boards made by JLC before the de minimus exemption for tariffs Chinese imports ended. On this particular project, I pretty much copied the schematic from something I made in the past and replaced the optocouplers with Toslink connectors and level translators. I only realized the issue after the boards were made, which means I've gotta deal with it in software.As an aside, the first serious coding I did was writing algorithms to run on GPUs, where you take a big hit from thread divergence when you are evaluating conditionals. The habits I picked up from that have stayed with me. Even though the architecture is completely different here, with no opportunity for thread divergence, I had a physical reaction to the idea of evaluating a conditional inside a for loop that has to iterate over every bit you are sending.I had originally thought of doing the encode with LUTs. I am using an STM32U575, so I have plenty of SRAM for the application. With a 512 byte LUT for all the 16-bit encoded values corresponding to each byte, you could do the encode in just a few instructions in an ISR, with or without DMA, or use a linked list with DMA to handle a number of bytes at a time. But when you're talking about 32-bit words, you are looking at a couple instructions per byte * 4 bytes. In either case, most of the CPU time would be spent on entering and exiting the ISR.That said, this is UART, and the maximum baud rate is 1/8th of the APB clock up to (I think) 10 Mbit for the U575. I am planning on running at between 1 and 4 Mbit depending on various other things unrelated to this, with the MCU running at 40 to 80 MHz, so I will have plenty of CPU time for other tasks either way.Quote from: brucehoult on May 09, 2025, 01:40:23 amI agree, and I may end up implementing some error checking to deal with this. That said, I don't think the situation is any worse than with keeping the order of bits because losing synchronization will give you nonsense data on the rx end either way.The point of all this is that you can do Manchester encoding in software at reasonably low cost without a LUT and that scales well to handle 4-byte data. Maybe I am the only person who has taken a "shoot first" approach to using Toslink for UART on STM32, but if not, perhaps this will be helpful.

author: SiliconWizard
author_rank: Super Contributor
timestamp: May 09, 2025, 02:20:41 pm
content: Quote from: Doctorandus_P on May 09, 2025, 08:16:00 amYes I have used LUTs for this. Unless you're very short on memory, this is the simplest and most efficient way of doing it.And yes on the RPi PIO you can write a simple state machine for this. There's an example of Manchester provided by the RPi.

author: spostma
author_rank: Regular Contributor
timestamp: May 12, 2025, 04:16:47 pm
content: If your microcontroller UART has an IrDA SIR of FIR mode, then the UART will encode every zero bit as a brief pulse;incoming pulses will be stretched to a pull bit period inside the UART.This should work fine with the Toslink devices, and save some energy as well.

author: Harjit
author_rank: Regular Contributor
timestamp: May 12, 2025, 08:37:01 pm
content: Very clever to make space in the source byte by masking with 0xAA/0x55, and then bit manipulating to encode!The ARMv7-m (in the STM32) has bit field insert/clear instructions - I haven't thought through it completely, I wonder if there is a way to do your "create" bit space, and then inject using those and it would be more compact (but probably not portable).

author: brucehoult
author_rank: Super Contributor
timestamp: May 13, 2025, 01:18:32 am
content: Bit field instructions aren't useful here because they deal with a single bitfield at a time.What we want here is to make every odd-numbered bit be the inverse of the even-numbered bit next to it. This can be done for all bits in parallel -- 32 or 64 depending on your CPU -- with regular bitwise operators/instructions.