Analog Signal Processing
Analog-to-Digital Conversion (ADC)
ADC is the process of converting a continuous analog signal into discrete digital values that a microcontroller can work with. Physical quantities like voltage or current get mapped to binary numbers, and the quality of that conversion depends on a few key parameters.
- Sampling rate determines how frequently the analog signal is measured. If you sample too slowly relative to the highest frequency in your input signal, you get aliasing, where high-frequency components masquerade as lower frequencies. The Nyquist theorem says your sampling rate must be at least twice the highest frequency component to avoid this.
- Resolution is the number of bits the ADC uses to represent each sample. An n-bit ADC produces discrete levels. A 10-bit ADC, for example, divides the input range into 1024 steps. More bits means finer granularity.
- Quantization error is the unavoidable rounding that happens when a continuous value gets snapped to the nearest discrete step. For an n-bit ADC with a reference voltage , the step size is , and the maximum quantization error is half that step.
Signal Conditioning and Amplification
Raw sensor signals are often too small, too noisy, or outside the range your ADC expects. Signal conditioning prepares the analog signal before digitization.
- Amplification boosts weak signals to improve the signal-to-noise ratio and make better use of the ADC's full range. The gain is the ratio of output amplitude to input amplitude.
- Operational amplifiers (op-amps) are the building blocks for most amplification circuits. A differential amplifier amplifies the difference between two inputs while rejecting noise that appears on both lines (common-mode noise).
- Instrumentation amplifiers are a specialized type built from multiple op-amps. They offer high input impedance, low drift, and a high common-mode rejection ratio (CMRR). These are the go-to choice for precise measurement of very small signals from sensors like strain gauges and thermocouples.
Filtering Techniques
Filters remove unwanted frequency components from the signal before it reaches the ADC.
- Low-pass filter: passes frequencies below a cutoff frequency, attenuates those above it. Useful for removing high-frequency noise from a slowly changing sensor signal.
- High-pass filter: passes frequencies above a cutoff, attenuates those below. Useful for blocking DC offsets.
- Band-pass filter: passes a specific frequency range and attenuates everything outside it.
- Notch filter (band-stop): attenuates a narrow frequency band while passing everything else. A classic use is rejecting 50/60 Hz power line interference.
Filters come in two flavors:
- Active filters use op-amps along with resistors and capacitors. They can provide gain and are easier to cascade without signal loss.
- Passive filters use only resistors, capacitors, and inductors. They're simpler but provide no gain and can load down the source.

Communication Protocols
I2C (Inter-Integrated Circuit) Protocol
I2C is a synchronous, multi-master, multi-slave serial bus that uses just two bidirectional lines:
- SDA (Serial Data Line) carries the data
- SCL (Serial Clock Line) carries the clock
Each device on the bus has a unique 7-bit (or 10-bit) address. Communication follows this sequence:
- The master sends a start condition (SDA pulled low while SCL is high).
- The master transmits the 7-bit slave address plus a read/write bit.
- The addressed slave responds with an ACK (acknowledge) bit.
- Data bytes are transferred, with the receiver sending ACK after each byte.
- The master sends a stop condition to release the bus.
I2C supports standard mode (100 kbit/s), fast mode (400 kbit/s), and high-speed mode (3.4 Mbit/s). It's commonly used for low-speed peripherals like temperature sensors, EEPROMs, and real-time clocks. The two-wire design keeps wiring simple, but bandwidth is lower than SPI.
SPI (Serial Peripheral Interface) Protocol
SPI is a synchronous, full-duplex, master-slave protocol that uses four lines:
- MOSI (Master Out Slave In): data from master to slave
- MISO (Master In Slave Out): data from slave to master
- SCLK (Serial Clock): clock generated by the master
- SS/CS (Slave Select / Chip Select): active-low line to select a specific slave
Communication works like this:
- The master pulls the target slave's SS line low.
- The master generates clock pulses on SCLK.
- On each clock cycle, one bit is simultaneously sent on MOSI and received on MISO.
- After the transfer, the master releases SS back to high.
SPI achieves higher data rates than I2C (often tens of Mbit/s) because it's full-duplex and has no addressing overhead. The tradeoff is that each slave needs its own SS line, so wiring gets more complex with many devices. SPI is commonly used for ADCs, DACs, display controllers, and SD cards.

UART (Universal Asynchronous Receiver/Transmitter) Communication
UART is an asynchronous, full-duplex, point-to-point protocol using two lines:
- TX (Transmit): sends data
- RX (Receive): receives data
There's no shared clock. Instead, both sides agree on a baud rate (bits per second) ahead of time. Each data frame is structured as:
- Start bit (logic low) signals the beginning of a frame.
- Data bits (typically 8, but configurable from 5 to 9).
- Parity bit (optional) for basic error detection.
- Stop bit(s) (1 or 2, logic high) mark the end of the frame.
UART is straightforward to use and widely supported. You'll see it for microcontroller-to-PC communication (via RS-232 level shifters or USB-to-serial adapters), GPS modules, Bluetooth modules, and debug consoles. The main limitation is that it's point-to-point, so you can't put multiple devices on the same bus without additional protocol layers.
Quick protocol comparison:
- I2C: 2 wires, multi-device, lower speed, address-based
- SPI: 4+ wires, full-duplex, higher speed, chip-select-based
- UART: 2 wires, point-to-point, asynchronous, no clock needed
Sensor Reading Techniques
Interrupt-Driven Sensing
With interrupt-driven sensing, the microcontroller doesn't actively watch the sensor. Instead, the sensor (or an external comparator circuit) generates an interrupt signal when new data is ready or a threshold is crossed.
- The sensor triggers an interrupt on a specific GPIO pin.
- The microcontroller suspends its current task and jumps to the interrupt service routine (ISR).
- The ISR reads the sensor data, stores or processes it, and returns.
- The microcontroller resumes whatever it was doing before.
This approach is efficient for sporadic or unpredictable events because the processor can do other work, or even enter a low-power sleep mode, between readings. The downside is added complexity: you need to keep ISRs short, handle potential interrupt priority conflicts, and be careful with shared variables (use volatile and disable interrupts around critical sections when needed).
Polling-Based Sensing
Polling is the simpler approach: the microcontroller periodically checks the sensor for new data in a loop.
- The main loop waits for a timer or delay to expire.
- The microcontroller reads the sensor's status register or data output.
- If new data is available, it processes the reading.
- The loop repeats.
Polling is easy to implement and works well when sensor data arrives at predictable, regular intervals and the microcontroller has processing time to spare. The drawback is that the processor stays busy checking the sensor even when nothing has changed, which wastes CPU cycles and power. If the polling interval is too long, you might miss events; too short, and you burn resources unnecessarily.
When to use which: Choose interrupt-driven sensing for low-power applications or infrequent/unpredictable events. Choose polling when data arrives at regular intervals and simplicity matters more than power efficiency.