The MeasLib project is a modular, C11-based generic firmware framework for measurement instruments (VNA, SA, Signal Generators, Multimeters). It is designed strictly for Embedded Execution, running directly on hardware (AT32, FPGA) without an underlying OS or transport layer.
The system architecture is strictly divided into two logical planes to ensure deterministic performance:
- Low Bandwidth / High Latency Tolerance.
- Property System:
meas_object_t/meas_variant_t. - Orchestration:
meas_device_t->meas_channel_t. - Messaging:
meas_event_t(Publisher/Subscriber) for async updates. - Files:
object.h,device.h,channel.h,event.h.
- High Bandwidth / Zero-Copy / Real-time.
- Direct Memory Access: Drivers write directly to app-provided buffers.
- Pipeline:
meas_data_block_t->meas_trace_t->meas_ui_t. - No Allocation: All data paths use static/shared memory.
- Files:
data.h,trace.h,render.h(UI).
- Bridge: Connects Hardware Events to Application Logic.
- Services:
input_service: Polling buttons/encoders.touch_service: Touchscreen coordinate mapping and gesture detection.render_service: Consumes UI dirty map and pushes pixels to LCD.shell_service: SCPI-like command interface over USB/VCP.
- Files:
measlib/sys/*.h
MeasLib/
├── boards/ # Target Specific Implementations
│ ├── AT32F403/ # Cortex-M4F (Artery)
│ ├── STM32F072/ # Cortex-M0 (ST)
│ └── STM32F303/ # Cortex-M4F (ST)
├── include/
│ ├── measlib/
│ │ ├── types.h # Core Primitives
│ │ ├── utils/
│ │ │ └── math.h # Interpolation, Approx, Statistics
│ │ ├── dsp/
│ │ │ ├── dsp.h # FFT, Windowing, Filtering
│ │ │ ├── chain.h # Node Pipeline
│ │ │ ├── node_types.h # Shared Context Structs
│ │ │ └── analysis.h # Peak Search, Regression, Matching
│ │ ├── core/ # Framework Kernel
│ │ │ ├── object.h # Base Object
│ │ │ ├── device.h # Device Facade
│ │ │ ├── channel.h # Measurement Channel
│ │ │ ├── data.h # Data Blocks
│ │ │ ├── trace.h # Trace Handling
│ │ │ ├── marker.h # Markers
│ │ │ ├── event.h # Messaging
│ │ │ ├── io.h # Streams
│ │ │ └── storage.h # Filesystem
│ │ ├── modules/ # Instrument Domains
│ │ │ ├── vna/ # Vector Network Analyzer
│ │ │ ├── sa/ # Spectrum Analyzer
│ │ │ ├── gen/ # Signal Generator
│ │ │ └── dmm/ # Multimeter
│ │ ├── ui/ # Generic UI System
│ │ │ ├── core.h # UI Core & Ticking
│ │ │ ├── render.h # Drawing Backend API
│ │ │ ├── input.h # Input Types & Calibration
│ │ │ ├── colors.h # Theme Definitions
│ │ │ ├── fonts.h # Font Assets
│ │ │ ├── menu.h # Menu System
│ │ │ └── components/ # Reusable Widgets
│ │ ├── sys/ # System Services
│ │ │ ├── input_service.h # Button/Encoder Polling
│ │ │ ├── touch_service.h # Touch Processing
│ │ │ ├── shell_service.h # Command Line Interface
│ │ │ └── render_service.h# Display Refresh Manager
│ │ └── drivers/ # Abstract Hardware Interfaces
│ │ └── api.h # Driver Registry API
├── src/
│ ├── main.c # Entry Point & Superloop
│ ├── core/
│ │ ├── object.c # Object System Implementation
│ │ ├── event.c # Event Dispatcher
│ │ ├── device.c # Device Facade
│ │ ├── channel.c # Channel Manager
│ │ ├── trace.c # Trace Types
│ │ └── io.c # IO Streams
│ ├── drivers/
│ │ ├── registry.c # Driver Registration Logic
│ │ └── stm32f303/ # Reference Target Implementation
│ │ ├── drv_adc.c # ADC Driver
│ │ ├── drv_controls.c # GPIO/Rotary
│ │ ├── drv_flash.c # Internal Flash Access
│ │ ├── drv_i2c.c # I2C Bus Driver
│ │ ├── drv_lcd.c # LCD Interface
│ │ ├── drv_sd.c # SD Card SPI
│ │ ├── drv_synth.c # Frequency Synthesizer
│ │ ├── drv_touch.c # Touch Controller
│ │ ├── drv_usb_vcp.c # USB Serial (VCP)
│ │ └── drv_watchdog.c # Independent Watchdog
│ ├── dsp/
│ │ ├── dsp.c # Core DSP functions
│ │ ├── chain.c # Node Processing Pipeline
│ │ ├── analysis.c # Higher Level Analysis
│ │ └── nodes/ # Individual DSP Nodes
│ │ ├── node_gain.c # Gain/Offset/Linear
│ │ ├── node_math.c # Mag/Phase/Avg/GroupDelay
│ │ ├── node_spectral.c # FFT/Window
│ │ ├── node_source.c # Signal Generation
│ │ ├── node_radio.c # DDC/Downconversion
│ │ ├── node_sink.c # Data Sinks
│ │ └── node_calibration.c # Vector Error Correction
│ ├── modules/ # Domain Logic Implementation
│ │ ├── vna/
│ │ ├── sa/
│ │ ├── gen/
│ │ └── dmm/
│ ├── sys/ # Service Implementations
│ │ ├── input_service.c
│ │ ├── render_service.c
│ │ ├── shell_service.c
│ │ └── touch_service.c
│ ├── ui/
│ │ ├── core.c # UI Lifecycle
│ │ ├── render_cell.c # Tile-based Renderer
│ │ ├── layout_main.c # Main Screen Layout
│ │ ├── colors.c # Color Palette
│ │ ├── components/ # Widget implementations
│ │ └── fonts/ # Font Data
│ └── utils/
│ └── math.c
└── tests/ # Host-based Test Suite
├── mocks/ # Simulated hardware drivers
└── src/ # Unit/Integration tests
The framework is designed to run in environments without dynamic memory allocation (malloc/free).
-
Objects: All framework objects (
meas_device_t,meas_channel_t) are designed effectively as "c-style classes" that can be statically allocated in.bssor on the stack. -
Initialization: API functions accept pointers to pre-allocated memory:
// Example: Static initialization static meas_channel_t my_channel; meas_status_t status = device->api->create_channel(device, 1, &my_channel);
- Caller-Owned: The framework never allocates data buffers internally.
- Shared Memory: Channels operate on buffers provided by the application via properties (e.g.,
MEAS_PROP_VNA_BUFFER_PTR). - Zero-Copy: Data pointers are passed through the system without copying (or via Event Payload).
The project uses CMake (3.16+) with support for multiple targets:
- MEASLIB_TARGET:
STM32F303,STM32F072,AT32F403. - MEASLIB_BUILD_TESTS: Build for Linux Host (Unit Tests).
All entities in the framework inherit from meas_object_t. This provides a uniform way to handle configuration (properties) and lifecycle.
// Base Object Handle
typedef struct meas_object_s meas_object_t;
// Generic Variant for Properties
typedef enum {
PROP_TYPE_INT64,
PROP_TYPE_REAL, // Abstracted floating point (float/double/fixed)
PROP_TYPE_STRING,
PROP_TYPE_BOOL,
PROP_TYPE_COMPLEX,
PROP_TYPE_PTR // Pointer to external memory (Caller-Owned Buffers)
} meas_prop_type_t;
// Numeric Abstraction
typedef double meas_real_t; // Default to double, configurable in build
typedef uint32_t meas_id_t; // Resource Identifier
typedef struct {
meas_real_t re;
meas_real_t im;
} meas_complex_t;
typedef enum {
MEAS_OK = 0,
MEAS_ERROR,
MEAS_PENDING,
MEAS_BUSY
} meas_status_t;
typedef struct {
meas_prop_type_t type;
union {
int64_t i_val;
meas_real_t r_val;
char* s_val;
bool b_val;
meas_complex_t c_val;
void* p_val; // For PROP_TYPE_PTR
};
} meas_variant_t;
// Object Interface (VTable)
typedef struct {
const char* (*get_name)(meas_object_t* obj);
meas_status_t (*set_prop)(meas_object_t* obj, meas_id_t key, meas_variant_t val);
meas_status_t (*get_prop)(meas_object_t* obj, meas_id_t key, meas_variant_t* val);
void (*destroy)(meas_object_t* obj);
} meas_object_api_t;
// The Base Struct impementing inheritance
struct meas_object_s {
const meas_object_api_t* api; // Virtual Function Table
void* impl; // Private Implementation (PIMPL)
uint32_t ref_count; // Reference Counting
};The framework uses a Composite Driver Model to handle complex embedded devices. The "Device" is a facade that orchestrates lower-level hardware components.
The Application Layer interacts only with this interface.
// Device Handle
typedef struct {
meas_object_t base; // Inherits Object
} meas_device_t;
// Forward Declaration for Factory
typedef struct meas_channel_s meas_channel_t;
typedef struct {
char name[32];
uint32_t version;
uint32_t capabilities;
} meas_device_info_t;
// Device Interface
typedef struct {
meas_object_api_t base;
// Core Operations
// resource_id: Hardware identifier (e.g., "ADC1", "SPI2")
meas_status_t (*open)(meas_device_t* dev, const char* resource_id);
meas_status_t (*close)(meas_device_t* dev);
meas_status_t (*reset)(meas_device_t* dev);
meas_status_t (*get_info)(meas_device_t* dev, meas_device_info_t* info);
// Factory: Creates Channels bound to this device
meas_status_t (*create_channel)(meas_device_t* dev, meas_id_t ch_id, meas_channel_t** out_ch);
} meas_device_api_t;Drivers are built by composing these reusable abstractions. These are not exposed to the App layer directly.
// Frequency Synthesizer (e.g. Si5351, ADF4350)
typedef struct {
meas_status_t (*set_freq)(void* ctx, meas_real_t hz);
meas_status_t (*set_power)(void* ctx, meas_real_t dbm);
meas_status_t (*enable_output)(void* ctx, bool enable);
} meas_hal_synth_api_t;
// Receiver / ADC (e.g. TLV320, Internal ADC)
typedef struct {
meas_status_t (*configure)(void* ctx, meas_real_t sample_rate, int decimation);
meas_status_t (*start)(void* ctx, void* buffer, size_t size); // DMA Start
meas_status_t (*stop)(void* ctx);
} meas_hal_rx_api_t;
// RF Frontend (Switches, Attenuators)
typedef struct {
meas_status_t (*set_path)(void* ctx, int path_id); // e.g., S11, S21
} meas_hal_fe_api_t;Mechanims for registering hardware drivers at runtime (or link time).
typedef struct {
const char* name;
meas_status_t (*init)(void); // Called during sys_init()
meas_status_t (*probe)(void);
} meas_driver_desc_t;
// Registry API
meas_status_t meas_driver_register(const meas_driver_desc_t* desc);In src/drivers/stm32f303/, the following drivers are implemented:
drv_adc.c: Internal ADC driver.drv_synth.c: Frequency synthesizer control.drv_lcd.c,drv_touch.c: Display and Touch Input.drv_usb_vcp.c: Virtual COM Port for CLI.drv_flash.c: Internal Flash storage.
A Logic Unit that coordinates a sweep. It owns Traces and manages the timing of the Driver components.
struct meas_channel_s {
meas_object_t base;
};
typedef struct {
meas_object_api_t base;
meas_status_t (*configure)(meas_channel_t* ch); // Applies properties to hardware
meas_status_t (*start_sweep)(meas_channel_t* ch);
meas_status_t (*abort_sweep)(meas_channel_t* ch);
} meas_channel_api_t;Container for measurement data.
typedef enum {
TRACE_FMT_COMPLEX, // Real/Imag
TRACE_FMT_REAL // Magnitude only
} meas_trace_fmt_t;
typedef struct {
meas_object_t base;
} meas_trace_t;
typedef struct {
meas_object_api_t base;
// Zero-Copy Data Access
meas_status_t (*get_data)(meas_trace_t* t, const meas_real_t** x, const meas_real_t** y, size_t* count);
meas_status_t (*set_format)(meas_trace_t* t, meas_trace_fmt_t fmt);
} meas_trace_api_t;Markers attach to a Trace and provide readout of specific data points.
typedef struct {
meas_object_t base;
} meas_marker_t;
typedef struct {
meas_object_api_t base;
// Configuration
meas_status_t (*set_source)(meas_marker_t* m, meas_trace_t* trace);
meas_status_t (*set_index)(meas_marker_t* m, size_t index);
// Readout
meas_status_t (*get_value)(meas_marker_t* m, meas_complex_t* val);
meas_status_t (*get_stimulus)(meas_marker_t* m, meas_real_t* freq);
} meas_marker_api_t;Decouples UI/Logic from Hardware Events.
typedef enum {
EVENT_PROP_CHANGED,
EVENT_DATA_READY,
EVENT_STATE_CHANGED,
EVENT_ERROR
} meas_event_type_t;
typedef struct {
meas_event_type_t type;
meas_object_t* source;
meas_variant_t payload;
} meas_event_t;
typedef void (*meas_event_cb_t)(const meas_event_t* event, void* user_data);
meas_status_t meas_subscribe(meas_object_t* pub, meas_event_cb_t cb, void* ctx);Constraints:
MAX_SUBSCRIBERS(32): Fixed static array.MAX_EVENT_QUEUE(16): Fixed ring buffer for pending events.
Used for high-throughput data transfer (e.g., ADC data to Processing Chain).
typedef struct {
meas_id_t source_id;
uint32_t sequence;
size_t size;
void* data; // Pointer to DMA buffer or Shared Memory
} meas_data_block_t;The UI layer is designed as a Consumer of the core framework. It runs on the same zero-copy principles and strictly avoids heap allocation.
Abstracts the physical display hardware and drawing primitives.
// Pixel Format (e.g., RGB565)
typedef uint16_t meas_pixel_t;
// Render Context (Stack/Static)
// Defines the clipping region and buffer for drawing operations
typedef struct {
meas_pixel_t* buffer; // Pointer to display buffer (or tile)
int16_t width;
int16_t height;
int16_t x_offset; // Absolute X (for tile rendering)
int16_t y_offset; // Absolute Y (for tile rendering)
meas_pixel_t fg_color;
meas_pixel_t bg_color;
} meas_render_ctx_t;
// Drawing Interface (VTable)
// Implemented by the Display Driver (LCD/OLED)
typedef struct {
void (*draw_pixel)(meas_render_ctx_t* ctx, int16_t x, int16_t y);
void (*draw_line)(meas_render_ctx_t* ctx, int16_t x0, int16_t y0, int16_t x1, int16_t y1);
void (*fill_rect)(meas_render_ctx_t* ctx, int16_t x, int16_t y, int16_t w, int16_t h);
void (*blit)(meas_render_ctx_t* ctx, int16_t x, int16_t y, int16_t w, int16_t h, const void* img, uint8_t alpha);
// Font System
void (*set_font)(meas_render_ctx_t* ctx, const meas_font_t* font);
int16_t (*get_text_width)(meas_render_ctx_t* ctx, const char* text);
int16_t (*get_text_height)(meas_render_ctx_t* ctx, const char* text);
void (*draw_text)(meas_render_ctx_t* ctx, int16_t x, int16_t y, const char* text, uint8_t alpha);
void (*get_dims)(meas_render_ctx_t* ctx, int16_t* w, int16_t* h);
} meas_render_api_t;Fonts are defined as meas_font_t structures (in measlib/ui/fonts.h).
- 5x7: Standard system font.
- 11x14: Large readable font.
- Fonts are distinct zero-copy resources commonly stored in Flash.
The central object creating the graphical interface. It subscribes to Device/channel events to trigger redraws.
// Hit-Test Region
typedef struct {
int16_t x, y, w, h;
meas_id_t widget_id; // ID for callback routing
void (*on_touch)(void* ctx, int16_t x, int16_t y);
} meas_ui_zone_t;
typedef struct {
meas_object_t base;
// State
meas_id_t focused_id; // Currently manipulated widget (Graph/Marker)
meas_object_t* active_modal; // Blocking Popup (e.g. Numpad)
bool menu_open;
// Scene Descriptor (Static list of clickable zones for current frame)
meas_ui_zone_t hit_zones[16]; // Max 16 interactive areas
uint8_t zone_count;
} meas_ui_t;
typedef struct {
meas_object_api_t base;
// Core UI Loop
meas_status_t (*update)(meas_ui_t* ui); // Process inputs/animations
meas_status_t (*draw)(meas_ui_t* ui, const meas_render_api_t* draw_api);
// Input Handling
meas_status_t (*handle_input)(meas_ui_t* ui, meas_variant_t input_event);
} meas_ui_api_t;The UI uses a Stage-Based Pipeline to strictly order drawing operations. This allows optimization (e.g. skipping "Traces" if the channel is idle).
typedef enum {
RENDER_STAGE_BG, // Clear / Gradient
RENDER_STAGE_GRID, // Grids / Smith Charts
RENDER_STAGE_TRACE, // Measurement Points
RENDER_STAGE_MARKER, // Markers / Deltas
RENDER_STAGE_OVERLAY, // Menus / Status Bar
RENDER_STAGE_COUNT
} meas_render_stage_t;
typedef struct {
meas_render_stage_t stage;
bool (*condition)(const void* ctx);
void (*execute)(const void* ctx, const meas_render_api_t* api);
} meas_render_step_t;Input is handled via a unified event system affecting the Focus Model.
typedef enum {
INPUT_TYPE_TOUCH_PRESS,
INPUT_TYPE_TOUCH_MOVE,
INPUT_TYPE_TOUCH_RELEASE,
INPUT_TYPE_KEY_PRESS,
INPUT_TYPE_ROTARY_ENC
} meas_input_type_t;
typedef struct {
meas_input_type_t type;
int16_t x; // Touch X or Key Code
int16_t y; // Touch Y or Delta
uint32_t timestamp;
} meas_input_event_t;Menus are defined as static constant arrays of meas_menu_item_t to save RAM.
typedef enum {
MENU_ITEM_ACTION,
MENU_ITEM_SUBMENU,
MENU_ITEM_TOGGLE,
MENU_ITEM_EDIT_NUM // Spawns Keypad
} meas_menu_type_t;
typedef struct meas_menu_item_s {
const char* label;
meas_menu_type_t type;
union {
void (*action)(void* ctx);
const struct meas_menu_item_s* submenu;
struct {
bool* check_target;
} toggle;
};
} meas_menu_item_t;Handles vector error correction (SOLT model). It attaches to a Channel and intercepts data before it reaches Traces.
// Calibration Standard Types
typedef enum {
CAL_STD_OPEN,
CAL_STD_SHORT,
CAL_STD_LOAD,
CAL_STD_THRU,
CAL_STD_ISOLATION
} meas_cal_std_t;
// Error Terms Container (SOLT)
// Uses meas_real_t (float/double) to match system precision
typedef struct {
meas_complex_t* ed; // Directivity
meas_complex_t* es; // Source Match
meas_complex_t* er; // Reflection Tracking
meas_complex_t* et; // Transmission Tracking
meas_complex_t* ex; // Isolation
} meas_cal_coefs_t;
typedef struct {
meas_object_t base;
} meas_cal_t;
typedef struct {
meas_object_api_t base;
// Application
meas_status_t (*apply)(meas_cal_t* cal, meas_data_block_t* data);
// Collection
meas_status_t (*measure_std)(meas_cal_t* cal, meas_cal_std_t std);
meas_status_t (*compute)(meas_cal_t* cal, meas_cal_coefs_t* out_coefs);
} meas_cal_api_t;Transforms raw touch coordinates to screen space using a 3-point calibration matrix.
typedef struct {
int32_t a, b, c, d, e, f, div;
} meas_touch_cal_t;
// Interface for Input Driver
meas_status_t meas_ui_calibrate_touch(meas_input_event_t* ev, const meas_touch_cal_t* cal);
meas_status_t meas_cal_save(meas_cal_t* cal, const char* filename);
meas_status_t meas_cal_load(meas_cal_t* cal, const char* filename);The framework includes a high-performance math layer to handle signal processing tasks. It relies on meas_real_t to auto-scale precision (Float/Double/Fixed).
Provides generic algorithms for analysis.
// Interpolation, Extrapolation & Approximation
meas_real_t meas_math_interp_linear(meas_real_t x, meas_real_t x0, meas_real_t y0, meas_real_t x1, meas_real_t y1);
meas_real_t meas_math_interp_parabolic(meas_real_t y1, meas_real_t y2, meas_real_t y3, meas_real_t x);
meas_real_t meas_math_interp_cosine(meas_real_t y1, meas_real_t y2, meas_real_t x);
meas_real_t meas_math_extrap_linear(meas_real_t x, meas_real_t x0, meas_real_t y0, meas_real_t x1, meas_real_t y1);
bool meas_math_is_close(meas_real_t a, meas_real_t b, meas_real_t epsilon);
// Fast Math (Platform Optimized)
// Uses ASM instructions (VSQRT) or LUTs on supported boards
meas_real_t meas_math_sqrt(meas_real_t x);
meas_real_t meas_math_cbrt(meas_real_t x);
meas_real_t meas_math_log(meas_real_t x);
meas_real_t meas_math_log10(meas_real_t x);
meas_real_t meas_math_exp(meas_real_t x);
meas_real_t meas_math_atan(meas_real_t x);
meas_real_t meas_math_atan2(meas_real_t y, meas_real_t x);
meas_real_t meas_math_modf(meas_real_t x, meas_real_t *iptr);
void meas_math_sincos(meas_real_t angle, meas_real_t *sin_val, meas_real_t *cos_val);
// Statistics & Filtering
void meas_math_stats(const meas_real_t* data, size_t count, meas_real_t* mean, meas_real_t* std_dev, meas_real_t* min_val, meas_real_t* max_val);
meas_real_t meas_math_rms(const meas_real_t *data, size_t count);
void meas_math_sma(const meas_real_t *input, size_t count, size_t window_size, meas_real_t *output, size_t *out_count);
meas_real_t meas_math_ema(meas_real_t current_avg, meas_real_t new_sample, meas_real_t alpha);
// Complex Operations (if not supported by compiler)
meas_real_t meas_cabs(meas_complex_t z);
meas_real_t meas_carg(meas_complex_t z);Abstracts hardware acceleration using dsp_ops.h (inline assembly for Cortex-M4 DSP instructions) instead of CMSIS-DSP dependency.
Analysis Layer:
- Peak Search (
meas_dsp_peak_find_max) - RF Matching (
meas_dsp_lc_match)
typedef enum {
DSP_WINDOW_RECT,
DSP_WINDOW_HANN,
DSP_WINDOW_HAMMING,
DSP_WINDOW_BLACKMAN
} meas_dsp_window_t;
// FFT Context (Pre-allocated tables)
typedef struct {
size_t length;
bool inverse;
void* backend_handle; // e.g., arm_rfft_fast_instance_f32
} meas_dsp_fft_t;
// API
meas_status_t meas_dsp_fft_init(meas_dsp_fft_t* ctx, size_t length, bool inverse);
meas_status_t meas_dsp_fft_exec(meas_dsp_fft_t* ctx, const meas_complex_t* input, meas_complex_t* output);
meas_status_t meas_dsp_apply_window(meas_real_t* buffer, size_t size, meas_dsp_window_t win_type);A Zero-Copy, Static Linked-List architecture for defining signal processing flows at runtime. Replaces hardcoded function calls with a configurable chain of Nodes:
Source (ADC) -> Node (Window) -> Node (FFT) -> Sink (Trace)
- Node Interface: Standardized
process(ctx, input, output)vtable. - Zero-Copy: Nodes pass pointers (
meas_data_block_t*) downstream. - Static Memory: Nodes are allocated in
.bssor Stack, never Heap. - Implemented Nodes:
Node_Gain,Node_Linear(Basic Math)Node_Window,Node_FFT(Spectral)Node_Magnitude,Node_LogMag,Node_Phase,Node_GroupDelay,Node_Average(Analysis)Node_DDC,Node_SParam,Node_Calibration(Radio/VNA)Node_WaveGen(Source)Node_SinkTrace(Output)
The architecture uses a Cooperative Superloop combined with Event-Driven FSMs to achieve determinism without an RTOS.
The logical core of the firmware (main.c) is a non-blocking loop dispatching events and ticking state machines.
int main(void) {
// 1. Hardware Initialization
sys_init();
meas_dsp_tables_init();
// 2. Main Superloop
while (1) {
// 2.1 Dispatch Events (High Priority)
// Processes the queue populated by ISRs (Touch, USB, DMA)
meas_dispatch_events();
// Poll Input Service
meas_input_service_poll();
meas_touch_service_poll();
meas_shell_service_poll();
// 2.2 Component Ticks (Cooperative Tasks)
// Each function must execute < 100us to maintain UI responsiveness
if (device)
meas_device_tick(device);
if (active_ch)
meas_channel_tick(active_ch);
if (ui)
meas_ui_tick(ui);
// 2.3 Power Management
// Wait for next Interrupt (WFI) if idle
sys_wait_for_interrupt();
}
return 0;
}Long-running operations (Sweeps, Calibration) are implemented as Finite State Machines to avoid blocking the CPU.
Example: Sweep FSM (meas_channel_t)
- IDLE: Waits for
startcommand. - SETUP: Configures PLL/Switch. Sets callback timer for settling time. Returns immediately.
- WAIT_SETTLE: Entered via
EVENT_TIMER. Triggers ADC DMA. Returns. - WAIT_DMA: Entered via
EVENT_DATA_READY(from ISR). Processes data chunk. - NEXT_POINT: Loops back or completes.
- ISRs: Only flag events (Publisher). No complex logic.
- Main Loop: Consumers (Subscribers). Runs atomic logic.
- Data Integrity: Double-buffering is used where ISR writes and Main reads (e.g., Audio/ADC stream).
This layer handles communication with the outside world (PC, SD Card) using the same non-blocking, zero-copy principles.
Abstracts byte-oriented interfaces (USB CDC, UART, BLE). Acts as both an Event Source (Input) and Data Sink (Output).
typedef struct {
meas_object_t base;
} meas_io_t;
typedef struct {
meas_object_api_t base;
// Transmission (Zero-Copy)
// Queues buffer pointer for DMA transmission
meas_status_t (*send)(meas_io_t* io, const void* data, size_t size);
// Status
size_t (*get_available)(meas_io_t* io); // Input buffer bytes
bool (*is_connected)(meas_io_t* io); // DTR state etc.
meas_status_t (*receive)(meas_io_t* io, void* buffer, size_t size, size_t* read_count);
meas_status_t (*flush)(meas_io_t* io);
} meas_io_api_t;Unified interface for file systems (FatFS on SD, LittleFS on Flash).
typedef struct {
meas_object_t base;
} meas_file_t;
// Filesystem Facade
typedef struct {
meas_object_api_t base;
meas_status_t (*mount)(meas_object_t* fs_drv);
meas_status_t (*unmount)(meas_object_t* fs_drv);
// File Operations
meas_status_t (*open)(meas_object_t* fs_drv, const char* path, meas_file_t** out_file);
meas_status_t (*write)(meas_file_t* file, const void* data, size_t size);
meas_status_t (*read)(meas_file_t* file, void* buffer, size_t size, size_t* read_count);
meas_status_t (*seek)(meas_file_t* file, size_t offset);
meas_status_t (*tell)(meas_file_t* file, size_t* offset);
meas_status_t (*close)(meas_file_t* file);
// Management
meas_status_t (*mkdir)(meas_object_t* fs_drv, const char* path);
meas_status_t (*remove)(meas_object_t* fs_drv, const char* path);
meas_status_t (*stat)(meas_object_t* fs_drv, const char* path, size_t* size);
} meas_fs_api_t;