Binary Data in Embedded Systems

May 28, 20266 min read

Embedded systems operate at the boundary between software and hardware, where every bit matters. Microcontrollers communicate with sensors, actuators, and communication peripherals through registers — memory-mapped addresses where individual bits control hardware behavior. Understanding binary representation is not optional in this domain; it is the language the hardware speaks.

Register Manipulation with Bitwise Operations

Hardware registers pack multiple fields into a single byte or word. Setting, clearing, or toggling individual bits without disturbing others requires bitwise operations.

Setting a Bit (OR)

To enable a peripheral, you set a specific bit to 1:

// Set bit 3 of register CTRL (enable interrupt)
CTRL |= (1 << 3);
// Binary: if CTRL was 01010010, result is 01011010

The OR operation leaves all other bits unchanged because x | 0 = x.

Clearing a Bit (AND with NOT)

To disable a feature, you clear a bit to 0:

// Clear bit 5 of register MODE (disable watchdog)
MODE &= ~(1 << 5);
// ~(1 << 5) = 11011111
// AND with 11011111 forces bit 5 to 0, preserves everything else

Toggling a Bit (XOR)

To flip a bit — useful for blinking an LED — use XOR:

// Toggle bit 2 of PORTB (blink LED on pin 2)
PORTB ^= (1 << 2);

Reading a Bit (AND)

To check whether a flag is set:

// Check if bit 4 of STATUS is set (data ready)
if (STATUS & (1 << 4)) {
    read_sensor_data();
}

Multi-Bit Fields

Many registers group adjacent bits into fields. For example, an ADC control register might use bits 2:0 to select a channel:

Bits 2:0Channel
000Channel 0
001Channel 1
010Channel 2
011Channel 3

To write a 3-bit value into this field without affecting other bits:

// Set channel to 5 (binary 101) in bits [2:0]
ADC_CTRL = (ADC_CTRL & ~0x07) | 0x05;
// 0x07 = 00000111 — mask for bits [2:0]
// ~0x07 = 11111000 — clears bits [2:0], preserves rest
// | 0x05 = sets bits [2:0] to 101

To extract the channel value:

uint8_t channel = ADC_CTRL & 0x07;

Endianness in Communication Protocols

Embedded systems frequently exchange binary data over serial buses (SPI, I2C, UART) and network interfaces (CAN, Ethernet). Byte order — endianness — determines how multi-byte values are laid out in memory and on the wire.

OrderLayout of 0x1234Used By
Big-endian12 34 (MSB first)Network protocols, CAN, Modbus
Little-endian34 12 (LSB first)x86, ARM (default), most RTOS

When an ARM microcontroller sends a 16-bit value over a CAN bus (big-endian protocol), you must swap bytes:

uint16_t value = 0x1234;
uint16_t network_order = ((value & 0xFF) << 8) | ((value >> 8) & 0xFF);
// Result: 0x3412 → transmitted as bytes 0x12, 0x34

Many compiler toolchains provide htons() / ntohs() helpers for this purpose.

Bitfields in C: Convenient but Risky

C allows you to define bitfields in structs:

typedef struct {
    uint8_t channel  : 3;
    uint8_t enabled  : 1;
    uint8_t mode     : 2;
    uint8_t reserved : 2;
} ADC_Config;

Bitfields improve readability but have portability issues: the C standard does not specify the allocation order of bits within a byte, so the layout can differ between compilers. For hardware register access, explicit bitwise operations are safer and more predictable across toolchains.

Practical Example: Configuring a UART

A typical UART configuration register might look like this:

BitsFieldValues
1:0Parity00=none, 01=even, 10=odd
3:2Stop bits00=1 bit, 01=1.5 bits, 10=2 bits
6:4Data bits000=5bit, 001=6bit, 010=7bit, 011=8bit
7Enable0=off, 1=on

Configuring 8-N-1 (8 data bits, no parity, 1 stop bit, enabled):

UART_CFG = (1 << 7)   // enable
         | (3 << 4)   // 8 data bits
         | (0 << 2)   // 1 stop bit
         | 0;         // no parity
// Binary: 10110000 = 0xB0

Key Takeaways

  • Bitwise OR sets bits, AND with NOT clears bits, XOR toggles bits — these three operations cover most register manipulation tasks.
  • Multi-bit fields require a clear-then-set pattern: mask out the field bits with AND, then write the new value with OR.
  • Always respect protocol endianness; big-endian is standard for network and automotive buses.
  • Prefer explicit bitwise operations over C bitfields for hardware register access to avoid compiler-specific layout differences.

Try It Yourself

Need to convert register values or debug binary protocols? Use the Text to Binary Converter to encode data, or check the reverse conversion to decode binary output from your embedded device.