Project 2: Clock Detective - Timing and Frequency Verifier
Measure real timer and PWM frequencies and compare them to expected values derived from clock math.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Level 2: Intermediate |
| Time Estimate | 6-10 hours |
| Main Programming Language | C++ (Arduino-style) |
| Alternative Programming Languages | C, Rust |
| Coolness Level | Level 3: Genuinely Clever |
| Business Potential | 1. The “Resume Gold” |
| Prerequisites | C/C++ basics, Teensyduino setup, basic electronics, ability to use a multimeter/logic analyzer |
| Key Topics | clock tree, PLLs, prescalers, timer math, jitter |
1. Learning Objectives
By completing this project, you will:
- Explain the core question for this project in your own words.
- Implement the main workflow and validate it with measurements.
- Handle at least two failure modes and document recovery.
- Produce a deterministic report that matches hardware behavior.
2. All Theory Needed (Per-Concept Breakdown)
Clock Tree, Prescalers, and Timer Math
Fundamentals Every timer tick comes from a clock source that is derived from oscillators and PLLs. A prescaler divides that clock to trade resolution for range, and a period register defines when a timer event occurs. If you cannot compute expected frequency on paper, you cannot validate timing in hardware. Teensy 4.x has multiple clock domains, so peripherals may run at different rates. Measurement is the only way to confirm assumptions.
Deep Dive into the concept The i.MX RT1062 clock tree begins with oscillators and PLLs and fans out into CPU and peripheral domains. The timer you use may not be on the same clock as the CPU. The output frequency is F_out = F_source / prescaler / (period + 1), and each term is a deliberate choice. Small period values yield high resolution but short maximum periods; large period values do the opposite. Jitter and drift are real. Jitter comes from interrupts, DMA, or bus contention; drift comes from oscillator tolerance and temperature. Measurement should include both short-term jitter and long-term drift. Capture edges with a logic analyzer, compute deviations, and report percent error. This turns the clock tree into an empirically verified system rather than a theoretical one. When you change CPU frequency, some peripherals scale and others do not. Timer code that worked at 600 MHz may break at 528 MHz. This project builds a timing report so you can prove what the hardware is doing instead of guessing.
How this fit on projects This concept directly powers the implementation choices and validation steps in this project.
Definitions & key terms
- PLL: Phase-Locked Loop; multiplies a base clock frequency.
- Prescaler: Divider that reduces a clock to a lower frequency.
- Timer period: Count value at which a timer resets or triggers.
- Jitter: Short-term variation in timing from ideal.
Mental model diagram (ASCII)
Oscillator -> PLL -> Divider -> Timer -> Output Pin
How it works (step-by-step)
- Identify the timer’s clock source in the reference manual.
- Compute expected period from prescaler and period registers.
- Configure timer output or interrupt.
- Measure with a logic analyzer and compute deviation.
Minimal concrete example
IntervalTimer t;
void tick() { digitalToggleFast(13); }
t.begin(tick, 1000); // 1000 us
Common misconceptions
- CPU clock and peripheral clock are always the same.
- Timer accuracy is perfect if math is correct.
- One measurement is enough to characterize jitter.
Check-your-understanding questions
- Why can changing CPU clock leave PWM unchanged?
- What does the prescaler trade off?
- How would you measure long-term drift?
Check-your-understanding answers
- Some peripherals use fixed clock domains independent of F_CPU.
- It trades time resolution for maximum period length.
- Measure over many seconds and compute average period.
Real-world applications
- Motor control timing
- Audio sample-rate stability
- Precise scheduling in embedded systems
Where you’ll apply it
- See §3.2 Functional Requirements and §5.10 Implementation Phases in this file.
- Also used in: P03-interrupt-storm-latency-and-priority-explorer.md, P04-pwm-workshop-motor-and-led-control-lab.md
References
- NXP i.MX RT1062 reference manual (CCM and timer chapters)
- PJRC Teensy core documentation
Key insights
Timer math is only real when confirmed by measurement on hardware.
Summary
Clock trees and prescalers define every timing guarantee you can make.
Homework/Exercises to practice the concept
- Compute expected PWM frequency for two prescalers and measure both.
- Create a 1 Hz toggle and record drift over 60 seconds.
Solutions to the homework/exercises
- Use F_source/prescaler/(period+1) and compare to scope output.
- Log timestamps for 60 edges and compute average period.
3. Project Specification
3.1 What You Will Build
Measure real timer and PWM frequencies and compare them to expected values derived from clock math.
3.2 Functional Requirements
- Generate at least three timer frequencies and measure them.
- Compute expected frequency by hand and compare.
- Measure jitter under idle and load.
- Produce a timing report with percent error.
3.3 Non-Functional Requirements
- Performance: Meet the target timing/throughput for the project.
- Reliability: Detect errors and recover without undefined behavior.
- Usability: Provide clear logs and a repeatable workflow.
3.4 Example Usage / Output
./P02-clock-detective-timing-and-frequency-verifier --run
3.5 Data Formats / Schemas / Protocols
CSV with columns: test_id, expected_hz, measured_hz, jitter_us
3.6 Edge Cases
- Clock changed by overclock settings
- Timer overflow at large periods
- Measurement aliasing at high frequency
3.7 Real World Outcome
You will run the project and see deterministic logs and measurements that match physical hardware behavior.
3.7.1 How to Run (Copy/Paste)
cd project-root
make
./P02-clock-detective-timing-and-frequency-verifier --run
3.7.2 Golden Path Demo (Deterministic)
Use a fixed input configuration and a known test signal. Capture output for 60 seconds and verify it matches expected values.
3.7.3 If CLI: exact terminal transcript
$ ./P02-clock-detective-timing-and-frequency-verifier --run --seed 42
[INFO] Clock Detective - Timing and Frequency Verifier starting
[INFO] Report saved to data/report.csv
[INFO] Status: OK
$ echo $?
0
Failure Demo (Deterministic)
$ ./P02-clock-detective-timing-and-frequency-verifier --run --missing-device
[ERROR] Device not detected
$ echo $?
2
4. Solution Architecture
4.1 High-Level Design
Inputs -> Acquisition -> Processing -> Output/Log
4.2 Key Components
| Component | Responsibility | Key Decisions |
|---|---|---|
| Acquisition | Configure peripherals and capture data | Use stable clock settings |
| Processing | Convert raw data to meaningful values | Apply calibration/filters |
| Output/Log | Emit reports and logs | CSV for reproducibility |
4.3 Data Structures (No Full Code)
struct Sample {
uint32_t timestamp_us;
uint32_t value;
uint32_t flags;
};
4.4 Algorithm Overview
Key Algorithm: Measurement + Report
- Initialize hardware and verify configuration.
- Capture data and record timestamps.
- Compute metrics and write report.
Complexity Analysis:
- Time: O(n) in samples
- Space: O(n) for log storage
5. Implementation Guide
5.1 Development Environment Setup
# Arduino IDE + Teensyduino must be installed
# Optional CLI workflow
arduino-cli core update-index
arduino-cli core install teensy:avr
5.2 Project Structure
project-root/
├── src/
│ ├── main.ino
│ ├── hw_config.h
│ └── measurements.cpp
├── tools/
│ └── analyze.py
├── data/
│ └── samples.csv
└── README.md
5.3 The Core Question You’re Answering
“How do I prove that my clock assumptions are actually true on hardware?”
5.4 Concepts You Must Understand First
Stop and research these before coding:
- clock tree, PLLs, prescalers, timer math, jitter
- Data logging and measurement techniques
- Basic timing math and error analysis
5.5 Questions to Guide Your Design
- Which clock domain feeds your timer?
- How will you separate measurement error from clock error?
- How will you log jitter over time?
5.6 Thinking Exercise
Derive the timer formula for two prescalers and predict output frequency.
5.7 The Interview Questions They’ll Ask
- What is a PLL and why is it used?
- How do you compute timer tick frequency?
- What causes jitter in embedded systems?
5.8 Hints in Layers
- Start with a 1 Hz signal.
- Use the same measurement tool for all tests.
- Measure over long windows to detect drift.
5.9 Books That Will Help
| Topic | Book | Chapter | |——-|——|———| | Timing basics | Making Embedded Systems | Ch. 4 | | Microcontroller timing | Arduino Workshop | Ch. 8 | | Debugging timing | The Art of Debugging | Ch. 1 |
5.10 Implementation Phases
Phase 1: Foundation (3 hours)
Goals:
- Derive timer equations
- Set up logic analyzer
Tasks:
- Derive timer equations
- Set up logic analyzer
Checkpoint: First measured frequency
Phase 2: Core Functionality (4 hours)
Goals:
- Generate multiple frequencies
- Log jitter data
Tasks:
- Generate multiple frequencies
- Log jitter data
Checkpoint: Timing report drafted
Phase 3: Polish (2 hours)
Goals:
- Compare results
- Write deviation analysis
Tasks:
- Compare results
- Write deviation analysis
Checkpoint: Final report completed
5.11 Key Implementation Decisions
| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Buffering | Single buffer, double buffer | Double buffer | Avoids data loss during processing | | Logging format | CSV, binary | CSV | Human-readable while still scriptable | | Clock speed | Default, overclock | Default | Keeps peripherals in spec |
6. Testing Strategy
6.1 Test Categories
| Category | Purpose | Examples | |———-|———|———-| | Unit Tests | Validate math, parsing, and conversions | Timer math, CRC checks | | Integration Tests | Verify peripherals and pipelines | DMA -> buffer -> log | | Edge Case Tests | Handle boundary conditions | Brownout, missing sensor |
6.2 Critical Test Cases
{test_cases}
6.3 Test Data
Use a fixed test input pattern and record outputs to data/report.csv
7. Common Pitfalls & Debugging
7.1 Frequent Mistakes
| Pitfall | Symptom | Solution | |———|———|———-| {pitfalls}
7.2 Debugging Strategies
{debug_strats}
7.3 Performance Traps
Large buffers improve stability but increase latency. Measure both throughput and jitter to choose the right size.
8. Extensions & Challenges
8.1 Beginner Extensions
{ex_begin}
8.2 Intermediate Extensions
{ex_inter}
8.3 Advanced Extensions
{ex_adv}
9. Real-World Connections
9.1 Industry Applications
{industry_apps}
9.2 Related Open Source Projects
{open_source}
9.3 Interview Relevance
{interview_rel}
10. Resources
10.1 Essential Reading
{resources}
10.2 Video Resources
- Embedded systems timing walkthrough (YouTube)
- Teensy hardware deep dive (Conference talk)
10.3 Tools & Documentation
- Teensyduino: Toolchain for Teensy boards
- Logic Analyzer: Timing verification
- Multimeter: Voltage and current measurement
10.4 Related Projects in This Series
{related_projects}
11. Self-Assessment Checklist
11.1 Understanding
- I can explain the main concept without notes.
- I can explain why the measurements match (or do not match) expectations.
- I understand at least one tradeoff made in this project.
11.2 Implementation
- All functional requirements are met.
- All critical test cases pass.
- Logs and reports are reproducible.
- Edge cases are handled.
11.3 Growth
- I documented lessons learned.
- I can explain this project in a job interview.
- I identified one improvement for next iteration.
12. Submission / Completion Criteria
Minimum Viable Completion: {comp_min}
Full Completion: {comp_full}
Excellence (Going Above & Beyond): {comp_ex}