Project 1: Board Bring-Up and Clock Audit

A bring-up checklist and measurement harness that proves the MCU clock tree and SysTick timing match real-world time.

Quick Reference

Attribute Value
Difficulty Level 1: Beginner
Time Estimate 1-2 weeks
Main Programming Language C (Alternatives: C++, Rust, Ada)
Alternative Programming Languages C++, Rust, Ada
Coolness Level Level 3: Genuinely Clever
Business Potential 1. The “Resume Gold”
Prerequisites C basics, LED toggling, basic UART logging
Key Topics Boot sequence, clock tree, SysTick, measurement discipline

1. Learning Objectives

By completing this project, you will:

  1. Validate boot and clock configuration with measurable evidence.
  2. Compute and verify SysTick timing against physical measurements.
  3. Produce a repeatable bring-up report with pass/fail criteria.
  4. Distinguish clock errors from firmware logic errors during debugging.

2. All Theory Needed (Per-Concept Breakdown)

Boot and Reset Sequence (Vector Table, Startup, and Memory Map)

Fundamentals On a Cortex-M MCU, power-up and reset are not vague events; they are a strict and observable sequence of reads and writes. The core starts by fetching the initial stack pointer and reset handler from the vector table at a fixed address. It then runs startup code that sets up memory, initializes .data and .bss, and finally calls main. If any of those steps are wrong, nothing else matters: your program never truly starts. For STM32F3, the memory map is fixed by the silicon, and your linker script determines where code and data live. Understanding how the vector table is laid out, how the stack pointer is initialized, and how the reset handler transitions into C code is the difference between ‘it sometimes boots’ and ‘it always boots’. Bring-up work relies on this knowledge because every later peripheral setup is layered on top of a correct reset path.

Deep Dive into the concept The reset sequence on Cortex-M is deterministic and documented, which makes it measurable. At reset, the core reads address 0x00000000 (after any memory remap) to load the initial Main Stack Pointer (MSP). The next word is the reset handler address. This means your linker script and vector table must be in the right place; otherwise, the CPU jumps into invalid memory. Startup code then performs three vital tasks: it configures the vector table location (VTOR if you remap), initializes the data segment by copying initial values from flash to SRAM, and clears the .bss segment to zero. Only after those steps does it call SystemInit() (often to set clocks) and then main(). If any of these steps are skipped, global variables may contain garbage and peripheral drivers may read invalid configuration. On STM32 devices, option bytes and BOOT pins determine whether the device boots from user flash, system memory (factory bootloader), or SRAM. That means a hardware strap or option byte can change the initial vector table base. For robust firmware, you should treat boot configuration as a first-class system requirement: document the expected BOOT pin state, verify it, and log it if possible. Another common subtlety is stack alignment. The Cortex-M requires 8-byte alignment on exception entry when the FPU is used. If your linker or startup code sets a misaligned stack, you will see hard faults only under interrupt load. Memory map knowledge is crucial for fault handling too. The STM32F303 has flash and SRAM ranges, plus peripheral address ranges. When a hard fault occurs, the fault status registers (CFSR, HFSR) and stacked registers point to an address. Knowing whether that address is in flash, SRAM, or peripheral space tells you whether the fault came from a bad pointer, an invalid register access, or an executing-from-data bug. Finally, boot-time initialization controls determinism. If your clock setup depends on external crystals, startup time may vary with temperature or load. If you use the system bootloader for DFU, it may reconfigure clocks or remap memory. A disciplined bring-up includes measuring the reset-to-main latency, capturing the clock source at startup, and asserting that vector table and stack pointers are in range. That is why a ‘boot checklist’ is not busywork; it is a measurable contract between the MCU and your firmware.

How this fit on projects In Board Bring-Up and Clock Audit, you build a bring-up checklist that proves the reset path is correct. You verify that the vector table is in the right place, that the startup code reached main, and that the system can print or blink predictable outputs without random early faults.

Definitions & key terms

  • Vector table -> An array of exception and interrupt handler addresses located at a fixed base address.
  • Reset handler -> The first code executed after reset; it initializes memory and calls main.
  • MSP/PSP -> Main and Process Stack Pointers used by the core; MSP is used during reset and exceptions.
  • .data/.bss -> Memory sections for initialized and zero-initialized globals in SRAM.
  • BOOT pins -> Hardware straps that select boot source (flash, system memory, or SRAM).

Mental model diagram (ASCII)

Reset -> Read MSP -> Read Reset Handler -> Init data/bss -> SystemInit -> main()
   |               |                     |                     |
   |               v                     v                     v
 Vector Table    Stack Pointer       Memory Layout        App Logic

How it works (step-by-step, with invariants and failure modes)

  1. Reset is asserted; core fetches MSP and reset handler from vector table.
  2. Startup code initializes .data and clears .bss, ensuring globals are valid.
  3. SystemInit configures clocks and vector table relocation if needed.
  4. main() runs; peripheral drivers assume memory and clock configuration are stable.
  5. Invariant: vector table address and stack pointer must point to valid memory; failure mode: hard fault before main.

Minimal concrete example

extern unsigned long _estack;
void Reset_Handler(void);
__attribute__((section(".isr_vector")))
const void* vector_table[] = {
(void*)&_estack,
Reset_Handler,
};
void Reset_Handler(void) {
SystemInit();
main();
}

Common misconceptions

  • Reset just jumps to main ignores memory initialization and vector table requirements.
  • If code compiles, it will boot ignores linker script and BOOT pin configuration.
  • Stack alignment only matters for floating point’ is false; misalignment can break exception entry.

Check-your-understanding questions

  1. Why does the MCU read two words from address 0x00000000 during reset?
  2. What happens if the vector table is placed in the wrong memory region?
  3. How can you prove that .bss was cleared correctly?
  4. Why might a firmware boot correctly when debugging but fail after power cycle?

Check-your-understanding answers

  1. The first word initializes MSP and the second provides the reset handler address so the core knows where to execute.
  2. The CPU jumps to an invalid address, often leading to a hard fault before main.
  3. Place a global variable without initialization and check that it starts at zero after reset.
  4. Debug tools can remap memory or configure clocks; power cycle uses raw BOOT configuration.

Real-world applications

  • Bootloader designs that switch between factory and application firmware.
  • Field-upgradable devices that must always recover from partial updates.
  • Safety-critical systems that log reset causes for post-mortem analysis.

Where you’ll apply it

References

  • Joseph Yiu, ‘The Definitive Guide to ARM Cortex-M3/M4’ (startup and vector tables).
  • STMicroelectronics STM32F3 Reference Manual (memory map and system configuration).
  • Elecia White, ‘Making Embedded Systems’ (bring-up discipline).

Key insights

  • Boot is just a deterministic memory fetch sequence; if you can validate each step, you can trust every later subsystem.

Summary A reliable firmware starts with a reliable reset path. The vector table, stack pointer, and startup code are the first contracts your MCU executes. Proving they are correct is the foundation for every timing, peripheral, and safety claim you make later.

Homework/Exercises to practice the concept

  1. Open the linker map file and identify where the vector table is located.
  2. Write a test that prints the MSP value at boot and verify it is within SRAM.
  3. Simulate a wrong BOOT pin configuration and observe the effect.

Solutions to the homework/exercises

  1. The vector table should live at the flash base address unless you intentionally remap.
  2. On STM32F303, a valid MSP should fall inside the SRAM address range; if not, fix the linker script.
  3. With BOOT0 asserted, the device enters system memory and your application in flash will not start.

Clock Tree, PLL, and Prescalers (System Timing Backbone)

Fundamentals Every peripheral timing and every delay in your firmware depends on the clock tree. The STM32F3 can run from internal or external oscillators, and uses a PLL to multiply those sources to reach the desired system clock. From that system clock, prescalers divide down to generate the AHB, APB1, and APB2 bus clocks, which in turn feed timers, ADCs, UARTs, and other peripherals. If you misconfigure any divider, you will see subtle failures: UART baud errors, PWM drift, incorrect ADC sampling, and watchdog timeouts. A clock tree audit is the practice of proving, with measurements, that your configured frequencies match reality. It is the single most valuable habit for embedded reliability.

Deep Dive into the concept The STM32F3 clock system is a directed graph of sources, multipliers, and dividers. At the root are the HSI (internal RC oscillator) and HSE (external crystal or clock). The PLL takes one of those sources and applies multiplication factors to produce a higher-frequency output used as the system clock. On STM32 devices, the system clock (SYSCLK) feeds the AHB bus, while APB1 and APB2 buses are derived by additional prescalers. Timers on APB buses often have a ‘x2’ effect when the prescaler is not 1, which means timer clocks can be double the APB frequency. If you forget that rule, PWM frequencies are off by exactly 2x, a classic bring-up trap. The clock tree is also linked to power: higher frequencies increase current draw, and some peripherals require specific clock ranges. USB and ADCs, for example, require precise clocks to meet protocol or sampling accuracy requirements. The RCC (Reset and Clock Control) registers configure all of this. A good audit reads those registers at runtime and recomputes derived clocks, then compares expected timing (based on configuration) to observed timing (based on measurement). Measurement can be as simple as toggling a GPIO at a known rate and timing it with a stopwatch, or as precise as using a logic analyzer to measure a timer output. A thorough audit includes the SysTick frequency, timer tick frequency, and peripheral baud rate verification. There are also failure modes: the external crystal may not start, the PLL may fail to lock, or the clock security system may switch back to HSI without warning. In robust firmware, you detect and log these events. The clock tree is therefore not just a setup routine, but an operational dependency that must be measured. When you develop on the STM32F3DISCOVERY, you should record expected frequencies, read RCC_CFGR to confirm clock sources, and validate at least one timer output. This practice makes you immune to the most common cause of ‘mystery bugs’ in embedded projects: silent timing errors.

How this fit on projects In Board Bring-Up and Clock Audit, you audit the clock tree by reading RCC configuration, computing derived clocks, and validating time with measured outputs (LEDs, timers, or UART).

Definitions & key terms

  • HSI -> Internal high-speed oscillator, convenient but less accurate.
  • HSE -> External crystal oscillator, more accurate but requires hardware.
  • PLL -> Phase-Locked Loop that multiplies an input frequency.
  • SYSCLK -> System clock feeding the CPU core.
  • AHB/APB -> Buses that distribute clocks to memory and peripherals.

Mental model diagram (ASCII)

HSI/HSE -> PLL -> SYSCLK -> AHB -> APB1/APB2 -> Peripherals
         |                |            |
       prescalers       timers       UART/ADC

How it works (step-by-step, with invariants and failure modes)

  1. Select a clock source (HSI or HSE) and enable it.
  2. Configure PLL multipliers/dividers to reach target SYSCLK.
  3. Set AHB/APB prescalers to keep peripherals within spec.
  4. Validate that timers and UARTs use the expected derived clocks.
  5. Invariant: All configured clocks must be within device limits; failure mode: peripheral timing mismatch or PLL unlock.

Minimal concrete example

// Pseudocode: compute SYSCLK from RCC registers
uint32_t sysclk = rcc_get_sysclk();
uint32_t hclk = sysclk / ahb_prescaler();
uint32_t pclk1 = hclk / apb1_prescaler();
printf("SYSCLK=%lu HCLK=%lu PCLK1=%lu\n", sysclk, hclk, pclk1);

Common misconceptions

  • If SYSCLK is correct, peripherals are correct ignores bus prescalers and timer x2 behavior.
  • HSI is good enough for all peripherals ignores baud accuracy and ADC sampling constraints.
  • PLL always locks ignores startup and clock security failures.

Check-your-understanding questions

  1. Why can a timer run at twice the APB clock on STM32?
  2. How would you detect that the HSE crystal failed to start?
  3. What is the difference between SYSCLK and HCLK?
  4. Why does UART baud depend on clock prescalers?

Check-your-understanding answers

  1. When APB prescaler is not 1, timer clocks are doubled to preserve timer resolution.
  2. Check RCC flags for HSE ready or clock security events and log a fallback to HSI.
  3. SYSCLK feeds the core; HCLK is SYSCLK after the AHB prescaler.
  4. UART divider is computed from the peripheral clock; a mismatch shifts baud rate.

Real-world applications

  • Motor control where PWM frequency accuracy matters.
  • Precision sensing with ADC sampling rate requirements.
  • Communication interfaces that need accurate baud rates.

Where you’ll apply it

References

  • STM32F3 Reference Manual (RCC chapter).
  • Joseph Yiu, ‘The Definitive Guide to ARM Cortex-M3/M4’ (system clocking).
  • Elecia White, ‘Making Embedded Systems’ (timing and measurement).

Key insights

  • If you cannot measure your clock tree, you cannot trust any timing claim in your system.

Summary Clock configuration is a system-wide contract. A small prescaler mistake can propagate into every peripheral. A clock audit makes timing visible and protects you from silent configuration errors.

Homework/Exercises to practice the concept

  1. Compute expected timer tick rates for APB1 and APB2 given a target SYSCLK.
  2. Toggle a GPIO every 1000 SysTick ticks and measure the period with a stopwatch.
  3. Force a fallback to HSI and log the resulting SYSCLK change.

Solutions to the homework/exercises

  1. Use the RCC_CFGR register to determine prescalers and apply the x2 rule for timers.
  2. 1,000 ticks at 1 kHz should be 1 second; if not, check SysTick reload value.
  3. When HSE fails, the system clock source bit will indicate HSI; log the event over UART.

Timebase, SysTick, and Measurement Discipline

Fundamentals A reliable timebase is how you reason about time in firmware. SysTick is a dedicated timer tied to the core clock that can generate periodic interrupts. If you configure it for 1 kHz, you get a millisecond tick that becomes your system heartbeat. This tick drives delays, scheduling, timeouts, and timestamps. But SysTick is only reliable if you know the core clock frequency and if you measure the actual timing. Without measurement, a ‘1 ms tick’ is just a guess. A bring-up project must therefore treat SysTick as a calibration point and validate it with real-world observation.

Deep Dive into the concept SysTick is a 24-bit down-counter integrated into the Cortex-M core. It takes the core clock (or core clock divided by 8) and counts down from a reload value to zero, then sets a flag and optionally fires an interrupt. Because it is in the core, it is unaffected by peripheral bus prescalers, which makes it a good reference for measuring the system clock. To configure SysTick, you load the reload register with (core_clock / desired_tick) - 1, select the clock source, enable the counter, and enable its interrupt. The interrupt handler typically increments a global tick counter. On STM32, HAL_Delay and other drivers are often built on this tick. The main danger is implicit coupling: if SysTick is configured incorrectly, every delay and timeout in your system is wrong. Another hazard is jitter. SysTick interrupts can be delayed by higher-priority interrupts, which means the tick is not a precise real-time clock, but a best-effort scheduler. For measurement, you should use SysTick only as a reference and then validate it with a GPIO toggle or timer output. A robust timebase audit logs the computed reload value, the configured clock source, and the observed period. The audit should also include a drift test: toggle a pin every N ticks for several minutes and measure drift relative to a stopwatch or logic analyzer. If drift is observed, the root cause is often clock source accuracy (HSI vs HSE) or an incorrect reload value due to a wrong core clock assumption. You also need to consider wraparound. A 24-bit counter at 72 MHz will wrap quickly if used without an interrupt, and a 32-bit tick counter will wrap after ~49 days at 1 kHz. In embedded systems, you typically handle wraparound by using unsigned arithmetic and comparing time differences rather than absolute values. Finally, you must decide what ‘good enough’ measurement means. For LED blinking, 1-2% accuracy is acceptable. For UART baud or ADC sampling, you need tighter tolerances. The discipline is to tie every time-dependent feature to a measured reference rather than an assumption.

How this fit on projects In Board Bring-Up and Clock Audit, SysTick is your baseline for clock validation. You compute the reload value, log it, and then validate the resulting tick with physical measurement.

Definitions & key terms

  • SysTick -> Core-integrated timer used for periodic interrupts and timekeeping.
  • Reload value -> The count value loaded into SysTick before it starts counting down.
  • Tick -> A periodic time event, commonly 1 ms in embedded systems.
  • Jitter -> Variation in the timing of periodic events due to interrupt latency.
  • Drift -> Long-term timing error relative to a reference clock.

Mental model diagram (ASCII)

Core clock -> SysTick down-counter -> interrupt -> tick++
                  |
                  v
             timing reference

How it works (step-by-step, with invariants and failure modes)

  1. Compute reload value based on expected core clock and desired tick rate.
  2. Configure SysTick to use core clock and enable interrupt.
  3. Increment a global tick counter in the SysTick handler.
  4. Toggle a GPIO every N ticks and measure the period.
  5. Invariant: tick increments at configured frequency; failure mode: drift or jitter due to wrong clock or interrupt priority.

Minimal concrete example

void SysTick_Handler(void) {
g_tick_ms++;
}
void delay_ms(uint32_t ms) {
uint32_t start = g_tick_ms;
while ((uint32_t)(g_tick_ms - start) < ms) {
    __WFI();
}
}

Common misconceptions

  • SysTick gives precise real-time scheduling ignores interrupt latency and jitter.
  • If LED blinks, tick is correct ignores the need to measure drift over time.
  • Tick counters never overflow ignores wraparound in long-running systems.

Check-your-understanding questions

  1. Why might SysTick interrupts be delayed even if configured correctly?
  2. How do you compute reload for 1 kHz at 72 MHz?
  3. What is the safest way to compare timeouts with wraparound?

Check-your-understanding answers

  1. Higher-priority interrupts can preempt SysTick, causing jitter.
  2. Reload = (72,000,000 / 1,000) - 1 = 71,999.
  3. Use unsigned subtraction: if (now - start) >= timeout.

Real-world applications

  • Scheduling periodic sensor sampling.
  • Timeouts in communication protocols.
  • Measuring CPU load and real-time performance.

Where you’ll apply it

References

  • ARM Cortex-M4 Technical Reference Manual (SysTick).
  • Joseph Yiu, ‘The Definitive Guide to ARM Cortex-M3/M4’ (timers and exceptions).
  • Elecia White, ‘Making Embedded Systems’ (timing discipline).

Key insights

  • A tick is only trustworthy when you can measure it and bound its jitter and drift.

Summary SysTick provides a convenient timebase, but it is only as accurate as your clock configuration and interrupt discipline. Treat it as a calibrated instrument, not a magic delay source.

Homework/Exercises to practice the concept

  1. Compute the reload value for 2 kHz and verify it in code.
  2. Measure the drift of a 1 Hz LED blink over 10 minutes.
  3. Experiment with interrupt priorities and observe SysTick jitter.

Solutions to the homework/exercises

  1. Reload for 2 kHz at 72 MHz is 35,999.
  2. A 1 Hz blink should be 600 seconds over 10 minutes; log the error and compute percent drift.
  3. Setting a higher-priority timer interrupt increases SysTick jitter; adjust priorities if needed.

3. Project Specification

3.1 What You Will Build

A bring-up checklist and firmware harness that prints computed clock frequencies, toggles a GPIO at a known rate, and produces a timing validation report showing measured vs expected results. It includes UART logs and a simple LED heartbeat.

3.2 Functional Requirements

  1. Clock Source Reporting: Read RCC registers and print the active clock source, SYSCLK, AHB, APB1, and APB2 frequencies.
  2. SysTick Calibration: Configure SysTick for 1 kHz and log the reload value and tick rate.
  3. Measurement Harness: Toggle a GPIO at 1 Hz and measure the period with a stopwatch or logic analyzer.
  4. Bring-Up Report: Generate a text report (UART output) with expected vs measured timing and pass/fail status.

3.3 Non-Functional Requirements

  • Performance: Timing error within 2% for 1 Hz blink; UART logging must not distort timing measurements.
  • Reliability: Report must be reproducible across power cycles; clock source must be detected correctly.
  • Usability: Clear log format and checklist steps so another engineer can replicate results.

3.4 Example Usage / Output

BOOT OK
Clock source: HSE -> PLL
SYSCLK: 72 MHz  AHB: 72 MHz  APB1: 36 MHz  APB2: 72 MHz
SysTick reload: 71999 (1 kHz)
Measured LED period: 1.01 s
Status: PASS (error 1.0%)

3.5 Data Formats / Schemas / Protocols

UART log format (one line per report field):

  • CLK_SRC=<HSI|HSE|PLL>
  • SYSCLK_HZ=<integer>
  • AHB_HZ=<integer>
  • APB1_HZ=<integer>
  • APB2_HZ=<integer>
  • SYSTICK_RELOAD=<integer>
  • MEAS_LED_PERIOD_S=<float>
  • RESULT=<PASS|FAIL>

3.6 Edge Cases

  • External crystal fails to start; system falls back to HSI.
  • UART baud incorrect due to clock mismatch.
  • SysTick configured with wrong clock source (core/8).
  • GPIO toggling uses blocking delay instead of SysTick and drifts over time.

3.7 Real World Outcome

You will have a documented bring-up report and a hardware-verified timing measurement.

3.7.1 How to Run (Copy/Paste)

# From project root
$ make flash
$ screen /dev/tty.usbmodem* 115200

3.7.2 Golden Path Demo (Deterministic)

  • Set MEASURE_MODE=fixed in firmware to use a fixed 1 Hz toggle.
  • Measure 20 LED transitions and compute average period.

3.7.3 CLI Transcript (Success)

$ make flash
Flashing... OK
$ screen /dev/tty.usbmodem* 115200
CLK_SRC=PLL
SYSCLK_HZ=72000000
AHB_HZ=72000000
APB1_HZ=36000000
APB2_HZ=72000000
SYSTICK_RELOAD=71999
MEAS_LED_PERIOD_S=1.01
RESULT=PASS
# Exit code: 0

3.7.4 Failure Demo (Bad Config)

  • Force HSI and keep PLL config assuming HSE.
  • Observe UART baud gibberish or period error >5%.
$ screen /dev/tty.usbmodem* 115200
CLK_SRC=HSI
SYSCLK_HZ=64000000
MEAS_LED_PERIOD_S=0.89
RESULT=FAIL
# Exit code: 2

4. Solution Architecture

The solution is a small measurement harness that reads clock registers, configures SysTick, and exposes a deterministic LED timing output.

4.1 High-Level Design

Reset -> Startup -> Clock Config -> SysTick -> LED Toggle -> UART Report

4.2 Key Components

Component Responsibility Key Decisions
Clock Auditor Reads RCC registers and computes derived clocks Use direct register reads to avoid HAL ambiguity
SysTick Driver Provides 1 kHz tick and timing utilities Use core clock source for determinism
GPIO Heartbeat Toggles LED at fixed rate for measurement Use a dedicated timer or SysTick-based scheduler
UART Reporter Outputs report and pass/fail status Fixed log format for repeatable parsing

4.3 Data Structures (No Full Code)

typedef struct {
uint32_t sysclk_hz;
uint32_t ahb_hz;
uint32_t apb1_hz;
uint32_t apb2_hz;
uint32_t systick_reload;
float measured_period_s;
uint8_t pass;
} clock_audit_t;

4.4 Algorithm Overview

Clock Calculation

  1. Read RCC_CFGR and RCC_CR.
  2. Determine clock source and PLL multipliers.
  3. Compute SYSCLK and bus clocks.

Timing Validation

  1. Toggle LED every 1000 ticks.
  2. Measure period externally.
  3. Compute error percentage and classify PASS/FAIL.

Complexity Analysis:

  • Time: O(1) per report
  • Space: O(1)

5. Implementation Guide

5.1 Development Environment Setup

brew install arm-none-eabi-gcc openocd
# Or install via STM32CubeIDE
make init

5.2 Project Structure

project-root/
|-- src/
|   |-- main.c
|   |-- clock_audit.c
|   |-- systick.c
|   `-- uart_log.c
|-- include/
|   `-- clock_audit.h
|-- Makefile
`-- README.md

5.3 The Core Question You’re Answering

“How do I prove the MCU is running at the frequency I think it is?” This project makes timing a measurable contract rather than a guess.

5.4 Concepts You Must Understand First

Stop and verify these before coding:

  1. Clock Sources and PLLs
    • Difference between HSI and HSE
    • PLL multiplication and prescalers
    • Book: Making Embedded Systems Ch. 3
  2. SysTick and Timebases
    • Reload value calculation
    • Interrupt jitter
    • Book: Definitive Guide to ARM Cortex-M Ch. 4
  3. GPIO Basics
    • Output mode and speed
    • Book: Making Embedded Systems Ch. 5

5.5 Questions to Guide Your Design

  1. How will you validate timing without a scope?
  2. What is your acceptable timing error threshold?
  3. How will you log clock source changes if HSE fails?

5.6 Thinking Exercise

Timing Without Tools

1) Start a stopwatch
2) Count 20 LED transitions
3) Record total time
4) Compute average period

If the average period is 0.9 s, what clock assumption is wrong?

5.7 The Interview Questions They’ll Ask

  1. How do you validate MCU clock frequency without lab equipment?
  2. What is the difference between HSI and HSE?
  3. Why does SysTick drift when PLL is misconfigured?

5.8 Hints in Layers

Hint 1: Start simple Blink an LED at 1 Hz using SysTick.

Hint 2: Read RCC registers Print RCC_CFGR and compute SYSCLK.

Hint 3: Measure over time Measure 20 periods to average drift.

Hint 4: Verify UART baud If logs are garbled, your clock is wrong.

5.9 Books That Will Help

Topic Book Chapter
Bring-up discipline Making Embedded Systems Ch. 2-3
Cortex-M timing Definitive Guide to ARM Cortex-M Ch. 4

5.10 Implementation Phases

Phase 1: Bring-Up Baseline (2-3 days)

Goals: LED toggle, UART output, initial clock readout. Tasks: configure GPIO, enable SysTick, print clock registers. Checkpoint: LED blinks at ~1 Hz and UART prints clock source.

Phase 2: Measurement Harness (3-5 days)

Goals: deterministic timing measurement and report. Tasks: implement period measurement, compute error. Checkpoint: PASS/FAIL report produced.

Phase 3: Robustness (3-4 days)

Goals: handle HSE failure and drift analysis. Tasks: simulate HSE failure, log fallback. Checkpoint: correct reporting under fault conditions.

5.11 Key Implementation Decisions

Decision Options Recommendation Rationale
Clock source HSI vs HSE+PLL HSE+PLL Better accuracy for measurements
Timing check Stopwatch vs logic analyzer Stopwatch + optional analyzer Accessible and repeatable

6. Testing Strategy

6.1 Test Categories

Category Purpose Examples
Unit Tests Validate clock computation functions Simulated RCC register values
Integration Tests Validate full measurement pipeline LED toggle with UART report
Edge Case Tests Handle HSE failure and UART baud mismatch Force HSI fallback

6.2 Critical Test Cases

  1. Clock Calculation: Given known RCC register values, computed SYSCLK matches expected.
  2. SysTick Accuracy: Measured LED period is within 2% of 1 Hz.
  3. Fallback Handling: If HSE fails, log shows HSI and report marks FAIL.

6.3 Test Data

RCC_CFGR=0x00000008 -> SYSCLK=72MHz
Measured periods: 1.00, 1.01, 0.99 (avg 1.00)

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

Pitfall Symptom Solution
Wrong clock source UART gibberish or drift Read RCC_CFGR and recompute clocks
SysTick uses core/8 Timing off by 8x Select core clock as SysTick source
Blocking delay Large drift over minutes Use SysTick or timer-based scheduling

7.2 Debugging Strategies

  • Toggle a GPIO at known rates and measure with a logic analyzer.
  • Print raw RCC register values before interpreting them.
  • Compare expected vs measured with a simple spreadsheet to spot scaling errors.

7.3 Performance Traps

Avoid using printf in high-frequency loops; it can distort timing measurements.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a second LED heartbeat at 2 Hz for comparison.
  • Log the SysTick tick count every second.

8.2 Intermediate Extensions

  • Measure APB timer clock by outputting TIM1 PWM.
  • Add a command to switch between HSI and HSE at runtime.

8.3 Advanced Extensions

  • Implement a clock failure detector and automatic recovery.
  • Build a PC-side script to parse the UART report and graph timing error.

9. Real-World Connections

9.1 Industry Applications

  • Industrial controllers: Clock accuracy determines control loop stability and timing budgets.
  • Medical devices: Timing validation is required for safety-critical sampling.
  • STM32CubeF3: Vendor HAL and examples for clock configuration.
  • libopencm3: Open-source STM32 peripheral library with RCC examples.

9.3 Interview Relevance

  • Clock tree validation and timing measurement questions.
  • SysTick configuration and use for delays/timeouts.

10. Resources

10.1 Essential Reading

  • Making Embedded Systems by Elecia White - Chapters on bring-up and timing discipline.
  • Definitive Guide to ARM Cortex-M3/M4 by Joseph Yiu - SysTick and exception handling.

10.2 Video Resources

  • Clock tree configuration walkthrough - ST community webinar.
  • Cortex-M SysTick basics - ARM training video.

10.3 Tools & Documentation

  • STM32CubeIDE: Build, flash, and debug firmware.
  • Logic analyzer: Measure timing accuracy of GPIO toggles.

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how the vector table and reset handler work.
  • I can compute SysTick reload values from SYSCLK.
  • I can explain why timer frequencies depend on bus prescalers.

11.2 Implementation

  • Clock report prints correct frequencies.
  • LED timing is within 2% of target.
  • UART output is stable and readable.

11.3 Growth

  • I documented the measured error and its cause.
  • I can reproduce the bring-up report after power cycling.
  • I can explain how I would validate clocks on a new board.

12. Submission / Completion Criteria

Minimum Viable Completion:

  • LED toggles at ~1 Hz with SysTick.
  • UART report prints clock source and frequencies.
  • Measured timing error is documented.

Full Completion:

  • Pass/fail classification implemented.
  • Clock fallback detection included.

Excellence (Going Above & Beyond):

  • Automated measurement script and graph created.
  • Report includes jitter and drift analysis.