2
\$\begingroup\$

I have communications bus I am trying to reverse engineer. It is based on a 9600 bps UART but inverted so it sits at 0V when idle, and pulses at 5V to send a binary 0. Unlike a normal UART the TX and RX lines are shared so only one wire is used for both transmit and receive. When one side wants to transmit a binary 0 it pulses the line at 5V, otherwise when idle it is weakly pulled towards 0V.

The problem is that I need to interface this bus with a 3.3V microcontroller UART (that is not 5V tolerant), and none of the usual level shifting solutions work. This is because none of them have a high impedance (Hi-Z) output state, rather they are always driving the output high or low, whereas I need a Hi-Z option as well so that another device on the bus can drive it while my transmitter is idle.

I have figured out the receive logic by having the bus feed into the base of an NPN transistor, and with the transistor output pulled high to 3.3V normally, when a 5V pulse arrives on the bus it causes the transistor to conduct, dropping the output from 3.3V down to 0V, and this has allowed me to successfully receive data from the bus.

However I am a bit stuck on how to transmit data. The microcontroller output needs to be both inverted and shifted up from 3.3V to 5V, but instead of the level shifter outputting 5V and 0V, it needs to output 5V and Hi-Z. This is so that when the microcontroller's TX pin is idle, the bus is not affected (and if nobody else is transmitting it will pull itself down to 0V).

Here is a truth table just to be extra clear:

Signal received from uC UART TX pin Output to bus
3.3 V Hi-Z
0 V 5 V

I can't quite see how to achieve this with one transistor, but perhaps it's possible with two. However I am thinking there's probably a trick to this so I thought I would ask before coming up with a my own barely functional solution!

As a side note, here is the truth table of the receiver side in case there's any way it can be built into the same circuit, rather than having two independent TX/RX blocks:

Signal from bus Output to uC UART RX pin
5 V 0 V
0 V 3.3 V
\$\endgroup\$
10
  • \$\begingroup\$ A P-MOSFET with a threshold voltage between 2 and 4 V would solve this. \$\endgroup\$
    – Jens
    Oct 3, 2023 at 10:32
  • \$\begingroup\$ Hi (I may recognize you from other contexts), Which bus is it, does it have a name or specification? Sounds awfully similar to SDI-12 sensor bus, but at 9600 bps. Same methods for level shifting may work. \$\endgroup\$
    – Justme
    Oct 3, 2023 at 13:05
  • \$\begingroup\$ @Justme Hello! I don't think it is SDI-12 from reading up on it. It is the protocol between a Makita 40V battery and charger/power tool. I am in the process of reverse engineering it so I don't know if it has a name. It would probably be fine if I was using a 5V tolerant microcontroller but I am doing it on a Raspberry Pico (because ultimately it would be nice to Bluetooth it to a phone and use that to view/edit battery information) so I need to do some level shifting to make that work. \$\endgroup\$
    – Malvineous
    Oct 3, 2023 at 13:14
  • \$\begingroup\$ Can you confirm that you're able to use dts/rts from UART.init? This is very important to avoid contention. \$\endgroup\$
    – Reinderien
    Oct 3, 2023 at 13:59
  • 1
    \$\begingroup\$ I'd definitely leave the MCU always receiving from the wire, whether it's transmitting or something else is. Extra electronics isn't needed. During MCU transmission, the MCU can either (a) ignore the receiver and disable any interrupts from it while transmitting which costs nothing, or (b) receive each transmitted byte back from the wire and verify it in software for free and simple fault detection. That's similar to how collision detection in ethernet works. At the least, you could light an LED if the bytes differ, for use in system debugging. \$\endgroup\$
    – TonyM
    Oct 4, 2023 at 16:19

2 Answers 2

1
\$\begingroup\$

A BJT needs only about 0.7 V to switch on, but you can add diodes to make the threshold higher:

schematic

simulate this circuit – Schematic created using CircuitLab

R1 needs to be able to discharge Q1's base so that it switches off quickly.

Alternatively, use a buffer that has a three-state output and accepts 3.3 V signals at its inputs, with the data input connected to 5 V and the /OE input to TX; one such device is the (SN)74AHCT1G125.

\$\endgroup\$
5
  • \$\begingroup\$ Forgive my ignorance - do the diodes prevent the higher voltage from reaching the TX pin, which is not 5V tolerant? \$\endgroup\$
    – Malvineous
    Oct 3, 2023 at 12:32
  • \$\begingroup\$ When the voltage drop is less than 0.7 V, a very small current can still flow (e.g., about 10 µA at 0.4 V). So assuming that your TX pin has a clamping diode to its VCC, the TX voltage will be less than about 3.7 V. \$\endgroup\$
    – CL.
    Oct 3, 2023 at 12:57
  • \$\begingroup\$ I looked into the buffer you suggested and I think this might actually be the cleanest solution for me given the low part count. Do you happen to know of a part number that can handle the RX side as well? It would have to be a 3.3V part with a 5V tolerant /OE. I've tried to look around but I'm finding it a bit difficult to identify which parts have these features. \$\endgroup\$
    – Malvineous
    Oct 3, 2023 at 16:35
  • \$\begingroup\$ @Malvineous, I think the logic gate solution is the much better way to go. For 5V-tolerant 3V3-supply logic, look at the 74LVC family. \$\endgroup\$
    – TonyM
    Oct 3, 2023 at 17:53
  • 1
    \$\begingroup\$ @Malvineous (SN)74AHC1G14 or (SN)74LVC1G14. \$\endgroup\$
    – CL.
    Oct 3, 2023 at 19:44
0
\$\begingroup\$

schematic

simulate this circuit – Schematic created using CircuitLab

You want the line in to hit a Schmitt trigger, and for it to be disabled via OR gate with RTS - otherwise you'll hear yourself talking.

The way out is slightly easier - most standard tristate buffers will have a Vih compatible with 3.3V in and can put 5Vout with an adequate supply. Most tristates are either inverting output inverting enable, or non-inverting output non-inverting enable, so you'll likely need to precede this with a NOT.

Again, please attempt to use a true RTS first. If that doesn't work, then use a GPIO.

\$\endgroup\$
3
  • \$\begingroup\$ Why would you want extra electronics to disable the MCU's receiver when it's transmitting? Downvoting, I'm afraid, as the answer's based on that premise. The MCU can just ignore the receiver and disable any interrupts from it while transmitting. That's a free and simple software solution to save spending money on more gates. Alternatively, the MCU can use its receiver to verify each transmitted byte it sends, letting it detect that it wasn't corrupted. Verification may well be a benefit to the system. \$\endgroup\$
    – TonyM
    Oct 4, 2023 at 15:08
  • \$\begingroup\$ @TonyM That's definitely an opinion. A hardware UART assumes no echo. Software workarounds are definitely possible but there are non-trivial trade-offs. \$\endgroup\$
    – Reinderien
    Oct 4, 2023 at 15:10
  • \$\begingroup\$ Rather than seeing it as an opinion, look at it from an engineering perspective. What's the requirement for an extra IC. Then what's the justification for the additional cost of that IC. The OP had no requirement so it's something you're putting forward. The software isn't a 'workaround' - that insinuates there is a problem. The receiver is switched on when in use, off when not. That's not a 'workaround'. \$\endgroup\$
    – TonyM
    Oct 4, 2023 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.