您的第一个 Nexus 应用程序

This tutorial guides you through creating your first Nexus application from scratch. You'll learn how to set up a project, configure the build system, and create a simple LED blinky application.

学习目标

完成本教程后,您将能够:

  • 理解 Nexus 应用程序的基本结构

  • 了解如何为 Nexus 项目配置 CMake

  • 能够初始化 HAL 子系统

  • 创建和控制 GPIO 设备

  • 构建应用程序并烧录到硬件

前置条件

开始之前,请确保您已:

  • Completed the Environment Setup guide

  • A supported development board (we'll use STM32F4 Discovery)

  • 已安装 ARM GCC 工具链

  • 用于烧录的 OpenOCD 或 ST-Link 工具

参见

步骤 1:创建项目结构

首先,为您的项目创建一个新目录:

mkdir my_first_nexus_app
cd my_first_nexus_app

创建以下目录结构:

my_first_nexus_app/
├── CMakeLists.txt
├── main.c
└── nexus/           (git submodule or copy of Nexus)

步骤 2:添加 Nexus 依赖

您可以通过两种方式将 Nexus 添加到项目中:

Option A: Git Submodule (Recommended)

git init
git submodule add https://github.com/X-Gen-Lab/nexus.git nexus
git submodule update --init --recursive

Option B: Copy Nexus

cp -r /path/to/nexus ./nexus

步骤 3:创建 CMakeLists.txt

创建包含以下内容的 CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

# Project name and language
project(my_first_nexus_app C ASM)

# Set C standard
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

# Add Nexus as subdirectory
add_subdirectory(nexus)

# Create executable
add_executable(${PROJECT_NAME}
    main.c
)

# Link against Nexus libraries
target_link_libraries(${PROJECT_NAME} PRIVATE
    nexus::hal
    nexus::platform
)

# Set output name
set_target_properties(${PROJECT_NAME} PROPERTIES
    OUTPUT_NAME "app"
)

Understanding the CMakeLists.txt:

  • add_subdirectory(nexus) - Includes Nexus build system

  • nexus::hal - Links the Hardware Abstraction Layer

  • nexus::platform - Links platform-specific code

步骤 4:编写您的第一个应用程序

创建一个简单的 LED 闪烁程序 main.c

/**
 * \file            main.c
 * \brief           My First Nexus Application
 * \author          Your Name
 */

#include "hal/hal.h"

/*-----------------------------------------------------------------------*/
/* Configuration                                                         */
/*-----------------------------------------------------------------------*/

/** LED pin definitions for STM32F4 Discovery */
#define LED_PORT    HAL_GPIO_PORT_D
#define LED_PIN     12  /* Green LED */

/** Blink delay in milliseconds */
#define BLINK_DELAY_MS  500

/*-----------------------------------------------------------------------*/
/* Helper Functions                                                      */
/*-----------------------------------------------------------------------*/

/**
 * \brief           Initialize LED GPIO
 * \return          HAL_OK on success, error code otherwise
 */
static hal_status_t led_init(void) {
    /* Configure GPIO as output */
    hal_gpio_config_t config = {
        .direction = HAL_GPIO_DIR_OUTPUT,
        .pull = HAL_GPIO_PULL_NONE,
        .output_mode = HAL_GPIO_OUTPUT_PP,
        .speed = HAL_GPIO_SPEED_LOW,
        .init_level = HAL_GPIO_LEVEL_LOW
    };

    /* Initialize GPIO */
    return hal_gpio_init(LED_PORT, LED_PIN, &config);
}

/*-----------------------------------------------------------------------*/
/* Main Function                                                         */
/*-----------------------------------------------------------------------*/

/**
 * \brief           Main entry point
 * \return          Should never return
 */
int main(void) {
    /* Initialize HAL subsystem */
    if (hal_init() != HAL_OK) {
        /* Initialization failed - stay in error loop */
        while (1) {
            /* Error state */
        }
    }

    /* Initialize LED */
    if (led_init() != HAL_OK) {
        /* LED initialization failed */
        while (1) {
            /* Error state */
        }
    }

    /* Main loop: blink LED */
    while (1) {
        /* Toggle LED state */
        hal_gpio_toggle(LED_PORT, LED_PIN);

        /* Wait for blink period */
        hal_delay_ms(BLINK_DELAY_MS);
    }

    return 0;
}

Understanding the Code:

  1. HAL Initialization: hal_init() initializes the HAL subsystem, including system clock, SysTick timer, and platform-specific hardware.

  2. GPIO Configuration: The hal_gpio_config_t structure defines GPIO behavior:

    • direction: Input or output mode

    • pull: Pull-up, pull-down, or none

    • output_mode: Push-pull or open-drain

    • speed: GPIO speed (low, medium, high, very high)

    • init_level: Initial output level (high or low)

  3. GPIO Operations:

    • hal_gpio_init(): Initialize GPIO with configuration

    • hal_gpio_toggle(): Toggle GPIO output state

    • hal_delay_ms(): Blocking delay in milliseconds

Step 5: Build for Native Platform (Testing)

First, let's build for the native platform to test our code structure:

# Configure for native platform
CMake -B build-native -DNEXUS_PLATFORM=native

# Build
CMake --build build-native

# Run (LED operations will be simulated)
./build-native/app

本地平台适用于:

  • 在没有硬件的情况下测试应用程序逻辑

  • 运行单元测试

  • 在开发机器上调试

步骤 6:为 STM32F4 构建

现在让我们为实际目标硬件构建:

# Configure for STM32F4 with ARM toolchain
CMake -B build-stm32f4 \
    -DCMAKE_TOOLCHAIN_FILE=nexus/CMake/toolchains/arm-none-eabi.CMake \
    -DNEXUS_PLATFORM=stm32f4

# Build
CMake --build build-stm32f4

这将创建 build-stm32f4/app.elfbuild-stm32f4/app.bin 文件。

步骤 7:烧录到硬件

Using OpenOCD:

# Flash with OpenOCD
openocd -f interface/stlink.cfg -f target/stm32f4x.cfg \
    -c "program build-stm32f4/app.elf verify reset exit"

Using ST-Link Utility (Windows):

  1. 打开 ST-Link 实用程序

  2. 连接到开发板

  3. 在地址 0x08000000 加载 build-stm32f4/app.bin

  4. 点击"编程和验证"

Using st-flash (Linux/macOS):

st-flash write build-stm32f4/app.bin 0x08000000

步骤 8:验证运行

After flashing:

  1. The green LED (PD12) on the STM32F4 Discovery board should blink

  2. LED 应该每 500 毫秒切换一次

  3. 按下复位按钮重启应用程序

如果 LED 不闪烁:

  • 检查开发板是否通电

  • 验证是否配置了正确的 LED 引脚

  • 检查应用程序是否成功烧录

  • Try debugging with GDB (see 测试)

Understanding HAL 初始化

The hal_init() function performs several important tasks:

  1. System Clock Configuration: Sets up the main system clock (typically 168 MHz for STM32F4)

  2. SysTick Timer: Configures the SysTick timer for hal_delay_ms() and hal_get_tick()

  3. FPU Initialization: Enables the Floating Point Unit (if available)

  4. Platform-Specific Setup: Initializes platform-specific hardware

Always call hal_init() before using any HAL functions.

GPIO 配置 Options

hal_gpio_config_t 结构提供了细粒度控制:

Direction:

  • HAL_GPIO_DIR_INPUT: Configure as input

  • HAL_GPIO_DIR_OUTPUT: Configure as output

Pull Resistors:

  • HAL_GPIO_PULL_NONE: No pull resistor

  • HAL_GPIO_PULL_UP: Enable pull-up resistor

  • HAL_GPIO_PULL_DOWN: Enable pull-down resistor

Output Mode:

  • HAL_GPIO_OUTPUT_PP: Push-pull output (can drive high and low)

  • HAL_GPIO_OUTPUT_OD: Open-drain output (can only pull low)

Speed:

  • HAL_GPIO_SPEED_LOW: Low speed (2 MHz)

  • HAL_GPIO_SPEED_MEDIUM: Medium speed (25 MHz)

  • HAL_GPIO_SPEED_HIGH: High speed (50 MHz)

  • HAL_GPIO_SPEED_VERY_HIGH: Very high speed (100 MHz)

对于 LED 控制,低速就足够了。对于高频信号使用更高的速度。

常见问题和解决方案

Issue: Build fails with "nexus not found"

Solution: Ensure Nexus is properly added as a subdirectory and the path in add_subdirectory() is correct.

Issue: Linker errors about undefined references

Solution: Make sure you're linking against the correct Nexus libraries (nexus::hal and nexus::platform).

Issue: Application doesn't run after flashing

Solution: - Verify the correct flash address (0x08000000 for STM32F4) - Check that the toolchain file is specified correctly - Ensure the platform is set to stm32f4

Issue: LED doesn't blink

Solution: - Verify the LED pin number matches your board - Check that hal_init() succeeded - Try a longer delay to make blinking more visible

下一步

恭喜!您已创建了第一个 Nexus 应用程序。现在您可以:

  1. GPIO Control 教程 - Learn advanced GPIO techniques

  2. UART Communication 教程 - Add serial communication

  3. 使用 OSAL 的多任务 - Create multi-tasking applications

  4. 探索 硬件抽象层 (HAL) 了解更多 HAL 特性

附加练习

尝试这些练习来巩固您的学习:

  1. Multiple LEDs: Modify the code to blink all four LEDs on the STM32F4 Discovery board in sequence

  2. Variable Speed: Add a button to change the blink speed when pressed

  3. Pattern: Create a custom blink pattern (e.g., two quick blinks, pause, repeat)

  4. Error Handling: Add more robust error handling with LED error indicators

示例:闪烁多个 LED

/* LED definitions */
#define LED_GREEN_PORT   HAL_GPIO_PORT_D
#define LED_GREEN_PIN    12
#define LED_ORANGE_PORT  HAL_GPIO_PORT_D
#define LED_ORANGE_PIN   13
#define LED_RED_PORT     HAL_GPIO_PORT_D
#define LED_RED_PIN      14
#define LED_BLUE_PORT    HAL_GPIO_PORT_D
#define LED_BLUE_PIN     15

/* Initialize all LEDs */
static hal_status_t leds_init(void) {
    hal_gpio_config_t config = {
        .direction = HAL_GPIO_DIR_OUTPUT,
        .pull = HAL_GPIO_PULL_NONE,
        .output_mode = HAL_GPIO_OUTPUT_PP,
        .speed = HAL_GPIO_SPEED_LOW,
        .init_level = HAL_GPIO_LEVEL_LOW
    };

    if (hal_gpio_init(LED_GREEN_PORT, LED_GREEN_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_ORANGE_PORT, LED_ORANGE_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_RED_PORT, LED_RED_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }
    if (hal_gpio_init(LED_BLUE_PORT, LED_BLUE_PIN, &config) != HAL_OK) {
        return HAL_ERR_FAIL;
    }

    return HAL_OK;
}

/* Main loop with sequence */
while (1) {
    hal_gpio_toggle(LED_GREEN_PORT, LED_GREEN_PIN);
    hal_delay_ms(250);
    hal_gpio_toggle(LED_ORANGE_PORT, LED_ORANGE_PIN);
    hal_delay_ms(250);
    hal_gpio_toggle(LED_RED_PORT, LED_RED_PIN);
    hal_delay_ms(250);
    hal_gpio_toggle(LED_BLUE_PORT, LED_BLUE_PIN);
    hal_delay_ms(250);
}

最佳实践

  1. Always Initialize HAL First: Call hal_init() before using any HAL functions

  2. Check Return Values: Always check return values from HAL functions for errors

  3. Use Appropriate GPIO Speed: Don't use high-speed GPIO for slow signals (wastes power)

  4. Document Pin Assignments: Clearly document which pins are used for what purpose

  5. Error Handling: Implement proper error handling with visual indicators (LEDs)

  6. Code Organization: Keep initialization code separate from main loop logic

  7. Use Meaningful Names: Use descriptive names for constants and functions

资源