>
section 2 of 184 min read

2. Embedded Hardware

2.1 Analog around the digital island

Even pure digital MCUs sit in an analog world. The signals from a thermistor, a strain gauge, a microphone, an accelerometer are continuous voltages. Before a digital system can reason about them, an analog front end must condition them: bias, amplify, filter, anti-alias, level-shift.

Typical signal-conditioning chain for a thermistor:

plaintext
  Thermistor --(R divider)--> Op-amp buffer --> RC anti-alias filter --> ADC

The divider turns resistance into voltage. The buffer presents a low-impedance source to the ADC's sample-and-hold capacitor. The RC filter rejects above-Nyquist content so we do not alias noise into the signal band. From the firmware side, this is just ADC->DR, a 12-bit number, but the analog work upstream determines whether that number is a temperature or noise.

2.2 Power management

Battery-powered embedded systems live or die by their power architecture. The simplest tool is the LDO (low-dropout linear regulator): drop voltage as heat to produce a clean rail. Cheap, low noise, but lossy. A 3.3 V LDO from a 3.7 V LiPo wastes (3.7 - 3.3) / 3.7 ~= 11 % at best. From 5 V USB to 3.3 V it wastes 34 %.

The smarter alternative is a switching regulator (buck or boost): chop the input at high frequency through an inductor, recover smooth output. 85-95 % efficient. But it costs more (inductor, more pins), and the switching noise can couple into RF and ADCs if layout is sloppy.

For coin-cell devices the goal is quiescent current (Iq) measured in nanoamps to single microamps. A CR2032 holds about 220 mAh. To last five years on it, average current must be under 5 microamps. That demands deep sleep most of the time, an LDO with sub-microamp Iq, and very brief radio bursts.

We will return to power in Section 12; here it is enough to plant the flag: power is a first-class concern in embedded, often the concern.

2.3 GPIO, ADC, DAC, PWM

GPIO (general-purpose input/output) is the universal interface. A pin can be configured as input (read voltage as 0 or 1), output (drive high or low), or alternate function (route to UART, SPI, timer, etc.). Behind each pin is a slew-rate-controlled output driver, optional pull-up/pull-down resistors, Schmitt-trigger input, and an interrupt detector. The choice of push-pull vs open-drain output, of internal vs external pull, of speed and drive strength all matter for EMI and signal integrity.

Direct register example on STM32:

c
// Enable clock to GPIOA
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
 
// Configure PA5 as output (mode = 01)
GPIOA->MODER &= ~(0b11 << (5 * 2));
GPIOA->MODER |=  (0b01 << (5 * 2));
 
// Set push-pull, no pull, low speed
GPIOA->OTYPER  &= ~(1 << 5);
GPIOA->OSPEEDR &= ~(0b11 << (5 * 2));
GPIOA->PUPDR   &= ~(0b11 << (5 * 2));
 
// Toggle the LED in a loop
while (1) {
    GPIOA->ODR ^= (1 << 5);
    for (volatile int i = 0; i < 1000000; i++);
}

This is the bare-metal mental model: every peripheral is a memory-mapped register; configuring it is just bit-banging the right address.

ADC. Almost every modern MCU has a successive-approximation (SAR) ADC, 10-12 bits typical, 16-24 bits in dedicated audio/instrumentation parts (sigma-delta). Multiplexed across 8-16 channels. The trick is sampling time: each channel needs the sample-and-hold cap to settle, and that depends on source impedance.

DAC. Less common on small MCUs. Used for audio output, function generation, motor reference signals.

PWM. A square wave with controlled duty cycle. From the timer's point of view, you load a counter with a period NN and compare it against a duty DD. Output is high while counter < D, low otherwise. With a 16-bit timer at 84 MHz, a 20 kHz PWM gives period N = 84e6 / 20e3 = 4200 counts, so 12-bit duty resolution. Increase frequency, lose resolution. Decrease, gain resolution but worse motor response. The math:

PWM resolution (bits)=log2(ftimerfPWM)\text{PWM resolution (bits)} = \log_2 \left( \frac{f_\text{timer}}{f_\text{PWM}} \right)

This is a constant tension: at 100 kHz PWM with an 84 MHz timer, you only have log2(840)9.7\log_2(840) \approx 9.7 bits of duty resolution. You cannot escape it without a faster clock.