Level Shifting 5V and 3.3V Logic Levels in Embedded Projects
Implementing level shifting for 5V and 3.3V logic levels is essential when connecting modern microcontrollers (like the ESP32, STM32, or Raspberry Pi) to legacy 5V sensors. Connecting a 5V logic signal directly to a 3.3V GPIO pin over-stresses the internal ESD protection diodes, risking permanent silicon damage.
1. Unidirectional vs bidirectional level translation
Different communication buses demand specific level shifting architectures:
- Unidirectional (e.g., UART TX, SPI MOSI): Data flows in one direction only. A simple passive resistor divider or 74LVC245 buffer suffices.
- Bidirectional (e.g., I2C SDA/SCL, 1-Wire): Both sides pull lines LOW. Requires active MOSFET circuits or dedicated transceiver ICs.
2. Passive resistor dividers for unidirectional signals
To step down a 5V UART TX output to a 3.3V RX input safely, use a voltage divider:
Vout = Vin * [R2 / (R1 + R2)] = 5V * [2k / (1k + 2k)] = 3.33V
Note: Never use resistor dividers to step up 3.3V to 5V, nor on bidirectional buses like I2C, as they cannot pull lines actively.
3. Discrete BSS138 N-channel MOSFET shifting circuit
For open-drain bidirectional buses like I2C, a classic BSS138 N-channel MOSFET with two pull-up resistors translates logic levels bidirectionally without bus contention. When either side pulls LOW, the MOSFET conducts, pulling the opposite side LOW through its internal body diode. For protocol details, see our guide on I2C, SPI, and UART protocols[cite: 1].
4. Dedicated auto-sensing ICs (TXS0108E vs TXB0108)
For multi-channel setups, deploy dedicated level translator ICs: use the TXS0108E for open-drain buses (I2C) and the TXB0108 for push-pull high-speed buses (SPI, SD card).
When to get help
If you'd rather have this handled directly, see our Embedded Circuit Design Services for scope and turnaround times.