Core Concepts

This guide explains the fundamental concepts and design principles of the Nexus platform.

Architecture Overview

Layered Design

Nexus follows a strict layered architecture:

┌─────────────────────────────────────┐
│        Application Layer            │  Your code
├─────────────────────────────────────┤
│        Framework Layer              │  Log, Shell, Config, Init
├─────────────────────────────────────┤
│    OS Abstraction Layer (OSAL)      │  Tasks, Mutex, Queue, Timer
├─────────────────────────────────────┤
│  Hardware Abstraction Layer (HAL)   │  GPIO, UART, SPI, I2C, ADC
├─────────────────────────────────────┤
│        Platform Layer               │  STM32F4, STM32H7, Native
└─────────────────────────────────────┘

Key Principles:

  1. Separation of Concerns: Each layer has a specific responsibility

  2. Abstraction: Hide implementation details behind clean interfaces

  3. Portability: Minimize platform-specific code in applications

  4. Dependency Rule: Upper layers depend on lower layers, never reverse

Design Patterns

Factory Pattern

HAL uses factory functions to create device instances:

/* Create GPIO device */
nx_gpio_write_t* led = nx_factory_gpio_write('A', 5);

/* Use device */
led->toggle(led);

/* Release device */
nx_factory_gpio_release((nx_gpio_t*)led);

Interface Pattern

Devices expose interfaces with function pointers:

typedef struct {
    void (*write)(nx_gpio_write_t* self, uint8_t value);
    void (*toggle)(nx_gpio_write_t* self);
    uint8_t (*read)(nx_gpio_write_t* self);
} nx_gpio_write_t;

Adapter Pattern

OSAL adapts different RTOS backends to a common interface:

/* Same API works with FreeRTOS, bare-metal, etc. */
osal_task_create(task_func, "task", 512, NULL, 1, NULL);

Hardware Abstraction Layer (HAL)

Purpose

HAL provides unified APIs for hardware peripherals, hiding platform differences.

Benefits:

  • Write once, run on multiple platforms

  • Consistent API across different MCUs

  • Easy to test with native simulation

  • Simplified driver development

Key Concepts

Device Instances

Each peripheral is represented as a device instance:

/* GPIO device */
nx_gpio_write_t* led;

/* UART device */
nx_uart_t* uart;

/* SPI device */
nx_spi_t* spi;

Configuration Structures

Devices are configured using structures:

nx_gpio_config_t gpio_cfg = {
    .mode  = NX_GPIO_MODE_OUTPUT_PP,
    .pull  = NX_GPIO_PULL_NONE,
    .speed = NX_GPIO_SPEED_LOW,
};

nx_uart_config_t uart_cfg = {
    .baudrate    = 115200,
    .word_length = 8,
    .stop_bits   = 1,
    .parity      = 0,
};

Factory Functions

Create devices using factory functions:

/* Simple creation */
nx_gpio_write_t* led = nx_factory_gpio_write('A', 5);

/* Creation with configuration */
nx_gpio_t* gpio = nx_factory_gpio_with_config('A', 5, &gpio_cfg);

/* Release when done */
nx_factory_gpio_release(gpio);

Interface Methods

Access device functionality through interface methods:

/* GPIO operations */
led->write(led, 1);
led->toggle(led);
uint8_t state = led->read(led);

/* UART operations */
nx_tx_sync_t* tx = uart->get_tx_sync(uart);
tx->send(tx, data, len, timeout);

Peripheral Types

GPIO - General Purpose I/O

  • Digital input/output

  • Interrupt support

  • Pull-up/pull-down configuration

UART - Serial Communication

  • Asynchronous serial

  • Configurable baud rate

  • TX/RX with timeouts

SPI - Serial Peripheral Interface

  • Master/slave modes

  • Full-duplex communication

  • Configurable clock polarity/phase

I2C - Inter-Integrated Circuit

  • Master/slave modes

  • 7-bit/10-bit addressing

  • Clock stretching support

ADC - Analog-to-Digital Converter

  • Single/continuous conversion

  • Multiple channels

  • DMA support

Timer - Hardware Timers

  • PWM generation

  • Input capture

  • Output compare

See 硬件抽象层 (HAL) for detailed HAL documentation.

OS Abstraction Layer (OSAL)

Purpose

OSAL provides portable RTOS primitives, allowing applications to work with different operating systems.

Supported Backends:

  • Bare-metal (cooperative scheduling)

  • FreeRTOS

  • RT-Thread

  • Zephyr

Key Concepts

Tasks/Threads

Create and manage concurrent execution:

/* Task function */
void my_task(void* arg) {
    while (1) {
        /* Task work */
        osal_task_delay(1000);
    }
}

/* Create task */
osal_task_handle_t task;
osal_task_create(my_task, "my_task", 512, NULL, 1, &task);

Mutexes

Mutual exclusion for shared resources:

/* Create mutex */
osal_mutex_handle_t mutex;
osal_mutex_create(&mutex);

/* Lock */
osal_mutex_lock(mutex, OSAL_WAIT_FOREVER);

/* Critical section */
shared_resource++;

/* Unlock */
osal_mutex_unlock(mutex);

Semaphores

Signaling and resource counting:

/* Create binary semaphore */
osal_sem_handle_t sem;
osal_sem_create(&sem, 0, 1);

/* Wait for signal */
osal_sem_wait(sem, OSAL_WAIT_FOREVER);

/* Signal */
osal_sem_give(sem);

Message Queues

Inter-task communication:

/* Create queue */
osal_queue_handle_t queue;
osal_queue_create(&queue, 10, sizeof(message_t));

/* Send message */
message_t msg = {.id = 1, .data = 42};
osal_queue_send(queue, &msg, OSAL_WAIT_FOREVER);

/* Receive message */
message_t received;
osal_queue_receive(queue, &received, OSAL_WAIT_FOREVER);

Software Timers

Periodic or one-shot timers:

/* Timer callback */
void timer_callback(void* arg) {
    /* Timer expired */
}

/* Create timer */
osal_timer_handle_t timer;
osal_timer_create(&timer, "timer", 1000, true, NULL, timer_callback);

/* Start timer */
osal_timer_start(timer);

Synchronization Patterns

Producer-Consumer

/* Producer task */
void producer_task(void* arg) {
    while (1) {
        data_t data = produce_data();
        osal_queue_send(queue, &data, OSAL_WAIT_FOREVER);
    }
}

/* Consumer task */
void consumer_task(void* arg) {
    while (1) {
        data_t data;
        osal_queue_receive(queue, &data, OSAL_WAIT_FOREVER);
        consume_data(&data);
    }
}

Resource Protection

/* Shared resource with mutex */
static int shared_counter = 0;
static osal_mutex_handle_t counter_mutex;

void increment_counter(void) {
    osal_mutex_lock(counter_mutex, OSAL_WAIT_FOREVER);
    shared_counter++;
    osal_mutex_unlock(counter_mutex);
}

See 操作系统抽象层 (OSAL) for detailed OSAL documentation.

Framework Layer

Logging Framework

Flexible logging with multiple backends and log levels.

Log Levels:

LOG_ERROR("Critical error: %d", error_code);
LOG_WARN("Warning: %s", warning_msg);
LOG_INFO("Information: %d", value);
LOG_DEBUG("Debug: %s", debug_info);

Module Filtering:

/* Define module */
#define LOG_MODULE "MyModule"

/* Log with module tag */
LOG_INFO("Module-specific log");

Backends:

  • Console (stdout)

  • UART

  • File

  • Custom backends

See 日志框架 for detailed logging documentation.

Shell Framework

Interactive command-line interface for debugging and control.

Command Registration:

static int cmd_handler(int argc, char* argv[]) {
    shell_printf("Command executed\n");
    return 0;
}

static const shell_command_t cmd_def = {
    .name = "mycommand",
    .handler = cmd_handler,
    .help = "My custom command",
    .usage = "mycommand [args]",
    .completion = NULL
};

shell_register_command(&cmd_def);

Features:

  • Command history

  • Tab completion

  • Built-in commands (help, clear, reboot)

  • Custom command registration

See Shell 框架 for detailed shell documentation.

Configuration Framework

Key-value configuration storage with persistence.

Basic Usage:

/* Store values */
config_set_i32("app.timeout", 5000);
config_set_str("device.name", "MyDevice");
config_set_bool("feature.enabled", true);

/* Retrieve values */
int32_t timeout;
config_get_i32("app.timeout", &timeout, 0);

Namespaces:

/* Open namespace */
config_ns_handle_t wifi_ns;
config_open_namespace("wifi", &wifi_ns);

/* Store in namespace */
config_ns_set_str(wifi_ns, "ssid", "MyNetwork");

/* Close namespace */
config_close_namespace(wifi_ns);

Import/Export:

/* Export to JSON */
char buffer[1024];
size_t size;
config_export(CONFIG_FORMAT_JSON, 0, buffer, sizeof(buffer), &size);

/* Import from JSON */
config_import(CONFIG_FORMAT_JSON, 0, buffer, size);

See 配置管理器 for detailed configuration documentation.

Configuration System (Kconfig)

Purpose

Kconfig provides compile-time configuration for:

  • Platform selection

  • Peripheral enablement

  • Feature selection

  • Resource allocation

Key Concepts

Configuration Options

Define what features to include:

config HAL_GPIO
    bool "Enable GPIO support"
    default y
    help
      Enable GPIO peripheral support

Dependencies

Express relationships between options:

config HAL_SPI
    bool "Enable SPI support"
    depends on HAL_GPIO
    help
      Enable SPI peripheral support

Defaults

Provide sensible defaults:

config HAL_UART_BAUDRATE
    int "Default UART baud rate"
    default 115200
    range 9600 921600

Generated Header

Kconfig generates nexus_config.h:

#define CONFIG_HAL_GPIO 1
#define CONFIG_HAL_UART 1
#define CONFIG_HAL_UART_BAUDRATE 115200

Usage in Code

#include "nexus_config.h"

#ifdef CONFIG_HAL_GPIO
/* GPIO code */
#endif

#ifdef CONFIG_HAL_UART
/* UART code */
#endif

See Configuration for detailed Kconfig usage.

Memory Management

Static Allocation

All memory allocated at compile time:

Advantages:

  • Deterministic behavior

  • No fragmentation

  • Suitable for safety-critical systems

Disadvantages:

  • Less flexible

  • May waste memory

Example:

#define MAX_DEVICES 10
static device_t devices[MAX_DEVICES];

Dynamic Allocation

Runtime memory allocation:

Advantages:

  • Flexible

  • Efficient memory usage

Disadvantages:

  • Fragmentation risk

  • Non-deterministic

Example:

device_t* device = malloc(sizeof(device_t));
/* Use device */
free(device);

Pool Allocation

Fixed-size memory pools:

Advantages:

  • Fast allocation

  • No fragmentation

  • Predictable behavior

Disadvantages:

  • Fixed block size

  • May waste memory

Example:

/* Create pool */
osal_pool_handle_t pool;
osal_pool_create(&pool, 10, sizeof(device_t));

/* Allocate from pool */
device_t* device = osal_pool_alloc(pool);

/* Return to pool */
osal_pool_free(pool, device);

Error Handling

Status Codes

All APIs return status codes:

typedef enum {
    HAL_OK = 0,
    HAL_ERR_FAIL,
    HAL_ERR_PARAM,
    HAL_ERR_STATE,
    HAL_ERR_TIMEOUT,
    HAL_ERR_NO_MEMORY,
} hal_status_t;

Error Checking

Always check return values:

hal_status_t status = hal_gpio_init(port, pin, &config);
if (status != HAL_OK) {
    LOG_ERROR("GPIO init failed: %d", status);
    return status;
}

Error Recovery

Implement appropriate recovery strategies:

/* Retry on timeout */
int retries = 3;
while (retries--) {
    status = hal_uart_send(uart, data, len, timeout);
    if (status == HAL_OK) {
        break;
    }
    if (status == HAL_ERR_TIMEOUT) {
        continue;  /* Retry */
    }
    return status;  /* Other errors */
}

Resource Management

Initialization

Proper initialization order:

int main(void) {
    /* 1. Initialize HAL */
    nx_hal_init();

    /* 2. Initialize OSAL */
    osal_init();

    /* 3. Initialize frameworks */
    log_init(NULL);
    shell_init(&shell_config);

    /* 4. Initialize application */
    app_init();

    /* 5. Start scheduler (if using RTOS) */
    osal_start_scheduler();

    return 0;
}

Cleanup

Proper resource cleanup:

void app_cleanup(void) {
    /* Release devices */
    nx_factory_gpio_release(led);
    nx_factory_uart_release(uart);

    /* Deinitialize frameworks */
    shell_deinit();
    log_deinit();

    /* Deinitialize HAL */
    nx_hal_deinit();
}

Best Practices

Code Organization

  1. Separate concerns: One file per module

  2. Use headers: Public API in headers, implementation in source

  3. Minimize dependencies: Reduce coupling between modules

  4. Document interfaces: Use Doxygen comments

Error Handling

  1. Check all return values: Never ignore errors

  2. Log errors: Use logging framework

  3. Fail gracefully: Implement recovery strategies

  4. Use assertions: For programming errors

Resource Management

  1. Initialize before use: Always initialize resources

  2. Clean up: Release resources when done

  3. Avoid leaks: Match allocations with deallocations

  4. Use RAII pattern: Acquire resources in initialization

Concurrency

  1. Protect shared data: Use mutexes

  2. Avoid deadlocks: Acquire locks in consistent order

  3. Minimize critical sections: Hold locks briefly

  4. Use queues: For inter-task communication

Testing

  1. Write unit tests: Test individual components

  2. Test error paths: Don't just test happy path

  3. Use mocks: Isolate dependencies

  4. Automate tests: Run tests in CI/CD

Next Steps

Now that you understand core concepts:

  1. Configuration - Master Kconfig configuration

  2. Examples Tour - Explore complex examples

  3. 架构 - Deep dive into architecture

  4. 教程 - Follow step-by-step tutorials

See Also