跳转至

系统优化

概述

系统优化是 Zephyr RTOS 开发中的关键环节,直接影响产品的性能、功耗和用户体验。本章将深入探讨系统裁剪、性能优化、实时性优化和功耗优化的方法和技巧。

学习目标

  • 掌握系统裁剪策略,减小代码体积和内存占用
  • 学习性能优化方法,提升 CPU 使用效率
  • 理解实时性优化技术,满足严格的时间要求
  • 掌握功耗优化技巧,延长电池寿命
  • 学会使用性能分析工具,定位性能瓶颈

系统裁剪

系统裁剪是优化的第一步,通过禁用不需要的功能模块,可以显著减小代码体积和内存占用。

裁剪策略

从需求出发:只启用应用实际需要的功能,避免"大而全"的配置。

分析依赖关系:理解各个配置选项之间的依赖关系,避免误删关键功能。

逐步裁剪:从最小配置开始,逐步添加必要功能,而不是从完整配置开始删减。

测试验证:每次裁剪后都要进行功能测试,确保应用正常工作。

Kconfig 裁剪

Zephyr 使用 Kconfig 系统进行配置管理,通过禁用未使用的子系统可以显著减小代码体积。

最小配置示例prj.conf):

# 基础系统配置
CONFIG_MINIMAL_LIBC=y
CONFIG_MINIMAL_LIBC_MALLOC=n
CONFIG_PRINTK=n
CONFIG_EARLY_CONSOLE=n

# 禁用未使用的子系统
CONFIG_NETWORKING=n
CONFIG_BT=n
CONFIG_USB=n
CONFIG_SHELL=n
CONFIG_LOG=n

# 禁用调试功能
CONFIG_DEBUG=n
CONFIG_ASSERT=n
CONFIG_THREAD_NAME=n
CONFIG_THREAD_MONITOR=n

# 禁用未使用的驱动
CONFIG_GPIO=n
CONFIG_I2C=n
CONFIG_SPI=n
CONFIG_UART_CONSOLE=n

常用裁剪选项

配置选项 说明 节省空间
CONFIG_MINIMAL_LIBC=y 使用最小 C 库 ~10-20KB
CONFIG_PRINTK=n 禁用 printk ~2-5KB
CONFIG_LOG=n 禁用日志系统 ~5-10KB
CONFIG_SHELL=n 禁用 Shell ~10-15KB
CONFIG_ASSERT=n 禁用断言 ~1-3KB
CONFIG_THREAD_NAME=n 禁用线程名称 ~1KB
CONFIG_NETWORKING=n 禁用网络栈 ~50-100KB
CONFIG_BT=n 禁用蓝牙栈 ~50-80KB

代码体积优化

启用大小优化

# 优化代码大小
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_COMPILER_OPT="-Os"

# 启用链接时优化(LTO)
CONFIG_LTO=y

# 移除未使用的代码
CONFIG_LINKER_ORPHAN_SECTION_PLACE=n

编译器优化级别对比

优化级别 说明 代码大小 性能
-O0 无优化 最大 最慢
-O1 基本优化 较大 较慢
-O2 标准优化 中等 较快
-O3 高级优化 较大 最快
-Os 大小优化 最小 中等
-Oz 极致大小优化 极小 较慢

优化建议

  • 对于资源受限的设备,使用 -Os 优化
  • 对于性能关键的应用,使用 -O2-O3
  • 启用 LTO 可以进一步减小 5-15% 的代码体积

内存优化

减小线程栈大小

// 分析实际栈使用情况
CONFIG_THREAD_STACK_INFO=y

// 在代码中查询栈使用
size_t unused;
k_thread_stack_space_get(k_current_get(), &unused);
printk("Stack unused: %zu bytes\n", unused);

// 根据实际使用调整栈大小
#define MY_THREAD_STACK_SIZE 512  // 从默认 1024 减小到 512
K_THREAD_STACK_DEFINE(my_thread_stack, MY_THREAD_STACK_SIZE);

使用内存池代替动态分配

// 避免使用 k_malloc/k_free
// char *buf = k_malloc(256);

// 使用内存池
K_MEM_POOL_DEFINE(my_pool, 64, 256, 4, 4);

void *buf = k_mem_pool_malloc(&my_pool, 256);
// 使用完毕后释放
k_mem_pool_free(&my_pool, &buf);

静态分配优先

// 避免动态分配
// struct sensor_data *data = k_malloc(sizeof(struct sensor_data));

// 使用静态分配
static struct sensor_data data;

裁剪示例:从 100KB 到 20KB

初始配置(hello_world 默认配置):

$ west build -b nrf52840dk_nrf52840 samples/hello_world
Memory region         Used Size  Region Size  %age Used
           FLASH:      102400 B         1 MB      9.76%
             RAM:       16384 B       256 KB      6.25%

裁剪后配置prj.conf):

CONFIG_MINIMAL_LIBC=y
CONFIG_MINIMAL_LIBC_MALLOC=n
CONFIG_PRINTK=n
CONFIG_EARLY_CONSOLE=n
CONFIG_CONSOLE=n
CONFIG_UART_CONSOLE=n
CONFIG_SERIAL=n
CONFIG_SIZE_OPTIMIZATIONS=y
CONFIG_LTO=y
CONFIG_ASSERT=n
CONFIG_THREAD_NAME=n
CONFIG_THREAD_MONITOR=n

裁剪后结果

$ west build -b nrf52840dk_nrf52840 samples/hello_world
Memory region         Used Size  Region Size  %age Used
           FLASH:       20480 B         1 MB      1.95%
             RAM:        4096 B       256 KB      1.56%

优化效果对比

指标 优化前 优化后 减少
Flash 使用 100 KB 20 KB 80%
RAM 使用 16 KB 4 KB 75%

性能优化

性能优化的目标是提升系统的响应速度和吞吐量,降低 CPU 使用率。

CPU 使用率分析

启用线程运行时统计

# prj.conf
CONFIG_THREAD_RUNTIME_STATS=y
CONFIG_SCHED_THREAD_USAGE=y

查看线程 CPU 使用率

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

void print_thread_stats(void)
{
    struct k_thread *thread;
    k_thread_runtime_stats_t stats;

    printk("Thread Name          CPU Usage\n");
    printk("--------------------------------\n");

    // 遍历所有线程
    K_THREAD_FOREACH(thread) {
        k_thread_runtime_stats_get(thread, &stats);
        uint32_t usage = (stats.execution_cycles * 100) / 
                         k_cycle_get_32();
        printk("%-20s %3u%%\n", 
               k_thread_name_get(thread), usage);
    }
}

使用 Shell 命令查看

uart:~$ kernel threads
Scheduler: 257 since last call
Threads:
 0x20000800 idle            
        options: 0x0, priority: 15 timeout: 0
        state: pending
        stack size 320, unused 280, usage 40 / 320 (12 %)

 0x20000900 main            
        options: 0x0, priority: 0 timeout: 0
        state: pending
        stack size 1024, unused 512, usage 512 / 1024 (50 %)

减少上下文切换

合并任务

// 避免:多个小任务频繁切换
void task1(void) {
    while (1) {
        process_sensor1();
        k_sleep(K_MSEC(10));
    }
}

void task2(void) {
    while (1) {
        process_sensor2();
        k_sleep(K_MSEC(10));
    }
}

// 推荐:合并为一个任务
void combined_task(void) {
    while (1) {
        process_sensor1();
        process_sensor2();
        k_sleep(K_MSEC(10));
    }
}

使用工作队列

// 避免:为每个事件创建线程
void button_handler(const struct device *dev, 
                    struct gpio_callback *cb, uint32_t pins)
{
    // 在中断上下文中处理(不推荐)
    process_button_event();
}

// 推荐:使用工作队列
static struct k_work button_work;

void button_work_handler(struct k_work *work)
{
    process_button_event();
}

void button_handler(const struct device *dev, 
                    struct gpio_callback *cb, uint32_t pins)
{
    k_work_submit(&button_work);
}

void init_button(void)
{
    k_work_init(&button_work, button_work_handler);
}

中断延迟优化

精简 ISR

// 避免:在 ISR 中执行复杂操作
void uart_isr(const struct device *dev)
{
    uint8_t data;
    uart_poll_in(dev, &data);

    // 复杂处理(不推荐)
    process_data(data);
    update_display();
    log_data(data);
}

// 推荐:ISR 只做最少的工作
static struct k_fifo uart_fifo;

void uart_isr(const struct device *dev)
{
    uint8_t data;
    uart_poll_in(dev, &data);

    // 只将数据放入队列
    k_fifo_put(&uart_fifo, &data);
}

// 在线程中处理
void uart_thread(void)
{
    while (1) {
        uint8_t *data = k_fifo_get(&uart_fifo, K_FOREVER);
        process_data(*data);
        update_display();
        log_data(*data);
    }
}

使用 DMA

// 避免:CPU 轮询传输数据
void spi_transfer_polling(const struct device *dev, 
                          uint8_t *tx_buf, uint8_t *rx_buf, size_t len)
{
    for (size_t i = 0; i < len; i++) {
        spi_transceive(dev, &tx_buf[i], 1, &rx_buf[i], 1);
    }
}

// 推荐:使用 DMA 传输
void spi_transfer_dma(const struct device *dev, 
                      uint8_t *tx_buf, uint8_t *rx_buf, size_t len)
{
    struct spi_buf tx_bufs[] = {
        { .buf = tx_buf, .len = len }
    };
    struct spi_buf rx_bufs[] = {
        { .buf = rx_buf, .len = len }
    };
    struct spi_buf_set tx = { .buffers = tx_bufs, .count = 1 };
    struct spi_buf_set rx = { .buffers = rx_bufs, .count = 1 };

    // DMA 传输,CPU 可以做其他事情
    spi_transceive(dev, &spi_cfg, &tx, &rx);
}

内存访问优化

缓存对齐

// 对齐到缓存行大小(通常 32 或 64 字节)
#define CACHE_LINE_SIZE 32

struct __aligned(CACHE_LINE_SIZE) sensor_data {
    uint32_t timestamp;
    float temperature;
    float humidity;
    uint8_t status;
};

// 避免伪共享
struct __aligned(CACHE_LINE_SIZE) thread_data {
    uint32_t counter;
    // 填充到缓存行大小
    uint8_t padding[CACHE_LINE_SIZE - sizeof(uint32_t)];
};

减少内存拷贝

// 避免:多次拷贝
void process_data_copy(uint8_t *input, size_t len)
{
    uint8_t temp[256];
    memcpy(temp, input, len);  // 第一次拷贝

    uint8_t output[256];
    transform(temp, output, len);  // 第二次拷贝

    send_data(output, len);
}

// 推荐:原地处理或使用指针
void process_data_inplace(uint8_t *input, size_t len)
{
    transform_inplace(input, len);  // 原地转换
    send_data(input, len);  // 直接发送
}

编译器优化

优化级别选择

# 性能优先(默认)
CONFIG_COMPILER_OPT="-O2"

# 极致性能(代码体积会增大)
CONFIG_COMPILER_OPT="-O3"

# 快速数学运算(牺牲精度)
CONFIG_COMPILER_OPT="-O3 -ffast-math"

函数内联

// 强制内联小函数
static inline __attribute__((always_inline)) 
uint32_t fast_multiply(uint32_t a, uint32_t b)
{
    return a * b;
}

// 避免内联大函数
__attribute__((noinline))
void complex_processing(void)
{
    // 复杂处理逻辑
}

性能测试工具

使用 Benchmark 程序

#include <zephyr/kernel.h>
#include <zephyr/timing/timing.h>

void benchmark_function(void)
{
    timing_t start, end;
    uint64_t cycles;

    timing_init();
    timing_start();

    start = timing_counter_get();

    // 被测试的函数
    function_to_benchmark();

    end = timing_counter_get();
    cycles = timing_cycles_get(&start, &end);

    uint64_t ns = timing_cycles_to_ns(cycles);
    printk("Execution time: %llu ns (%llu cycles)\n", ns, cycles);
}

测量中断延迟

static uint32_t irq_entry_time;
static uint32_t irq_latency_max = 0;

void gpio_isr(const struct device *dev, 
              struct gpio_callback *cb, uint32_t pins)
{
    uint32_t now = k_cycle_get_32();
    uint32_t latency = now - irq_entry_time;

    if (latency > irq_latency_max) {
        irq_latency_max = latency;
    }
}

void trigger_interrupt(void)
{
    irq_entry_time = k_cycle_get_32();
    // 触发中断
}

实时性优化

实时性优化确保系统能够在规定时间内响应事件,满足硬实时或软实时要求。

实时性需求分析

定义实时性指标

  • 最坏情况响应时间(WCRT):系统响应事件的最长时间
  • 平均响应时间:系统响应事件的平均时间
  • 抖动(Jitter):响应时间的变化范围

测量响应时间

void measure_response_time(void)
{
    uint32_t start, end, response_time;
    uint32_t min_time = UINT32_MAX;
    uint32_t max_time = 0;
    uint32_t total_time = 0;
    uint32_t count = 1000;

    for (uint32_t i = 0; i < count; i++) {
        start = k_cycle_get_32();

        // 触发事件并等待响应
        trigger_event();
        wait_for_response();

        end = k_cycle_get_32();
        response_time = end - start;

        if (response_time < min_time) min_time = response_time;
        if (response_time > max_time) max_time = response_time;
        total_time += response_time;
    }

    printk("Response time statistics:\n");
    printk("  Min: %u cycles\n", min_time);
    printk("  Max: %u cycles (WCRT)\n", max_time);
    printk("  Avg: %u cycles\n", total_time / count);
    printk("  Jitter: %u cycles\n", max_time - min_time);
}

优先级分配

Rate Monotonic(RM)调度

优先级与任务周期成反比,周期越短优先级越高。

// 任务 1:周期 10ms,优先级最高
#define TASK1_PRIORITY 5
#define TASK1_PERIOD K_MSEC(10)

void task1(void)
{
    while (1) {
        // 处理高频任务
        k_sleep(TASK1_PERIOD);
    }
}

// 任务 2:周期 50ms,优先级中等
#define TASK2_PRIORITY 7
#define TASK2_PERIOD K_MSEC(50)

void task2(void)
{
    while (1) {
        // 处理中频任务
        k_sleep(TASK2_PERIOD);
    }
}

// 任务 3:周期 100ms,优先级最低
#define TASK3_PRIORITY 9
#define TASK3_PERIOD K_MSEC(100)

void task3(void)
{
    while (1) {
        // 处理低频任务
        k_sleep(TASK3_PERIOD);
    }
}

Deadline Monotonic(DM)调度

优先级与任务截止时间成反比,截止时间越短优先级越高。

// 任务 1:截止时间 5ms,优先级最高
#define TASK1_PRIORITY 5
#define TASK1_DEADLINE K_MSEC(5)

// 任务 2:截止时间 20ms,优先级中等
#define TASK2_PRIORITY 7
#define TASK2_DEADLINE K_MSEC(20)

// 任务 3:截止时间 50ms,优先级最低
#define TASK3_PRIORITY 9
#define TASK3_DEADLINE K_MSEC(50)

优先级分配建议

  • 关键实时任务:优先级 0-5(最高)
  • 普通实时任务:优先级 6-10
  • 后台任务:优先级 11-14
  • 空闲任务:优先级 15(最低)

中断优先级配置

配置中断优先级

// 关键中断:最高优先级
#define CRITICAL_IRQ_PRIORITY 0

// 重要中断:高优先级
#define IMPORTANT_IRQ_PRIORITY 2

// 普通中断:中等优先级
#define NORMAL_IRQ_PRIORITY 4

// 低优先级中断
#define LOW_IRQ_PRIORITY 6

// 配置中断
IRQ_CONNECT(TIMER_IRQ, CRITICAL_IRQ_PRIORITY, 
            timer_isr, NULL, 0);
IRQ_CONNECT(UART_IRQ, NORMAL_IRQ_PRIORITY, 
            uart_isr, NULL, 0);
IRQ_CONNECT(GPIO_IRQ, LOW_IRQ_PRIORITY, 
            gpio_isr, NULL, 0);

中断优先级分组(ARM Cortex-M):

graph TB
    A[中断优先级] --> B[抢占优先级]
    A --> C[子优先级]
    B --> D[决定是否抢占]
    C --> E[相同抢占优先级时的顺序]

禁用抢占

关键代码段保护

// 方法 1:禁用中断
unsigned int key = irq_lock();
// 关键代码段
critical_section();
irq_unlock(key);

// 方法 2:禁用线程抢占
k_sched_lock();
// 关键代码段
critical_section();
k_sched_unlock();

// 方法 3:使用互斥锁
static K_MUTEX_DEFINE(critical_mutex);

k_mutex_lock(&critical_mutex, K_FOREVER);
// 关键代码段
critical_section();
k_mutex_unlock(&critical_mutex);

注意事项

  • 禁用中断的时间应尽可能短(< 100us)
  • 禁用抢占会影响系统实时性
  • 优先使用互斥锁而不是禁用中断

实时性测试

中断延迟测量

#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>

#define TEST_PIN DT_GPIO_PIN(DT_ALIAS(led0), gpios)
#define TEST_PORT DT_GPIO_LABEL(DT_ALIAS(led0), gpios)

static uint32_t irq_timestamps[1000];
static uint32_t irq_count = 0;

void test_gpio_isr(const struct device *dev, 
                   struct gpio_callback *cb, uint32_t pins)
{
    if (irq_count < 1000) {
        irq_timestamps[irq_count++] = k_cycle_get_32();
    }
}

void measure_interrupt_latency(void)
{
    const struct device *gpio = device_get_binding(TEST_PORT);
    uint32_t trigger_time;

    // 配置 GPIO 中断
    gpio_pin_configure(gpio, TEST_PIN, GPIO_INPUT);
    gpio_pin_interrupt_configure(gpio, TEST_PIN, 
                                 GPIO_INT_EDGE_RISING);

    // 触发中断并测量延迟
    for (int i = 0; i < 1000; i++) {
        trigger_time = k_cycle_get_32();
        // 触发中断
        gpio_pin_set(gpio, TEST_PIN, 1);
        k_busy_wait(10);
        gpio_pin_set(gpio, TEST_PIN, 0);
        k_sleep(K_MSEC(1));
    }

    // 分析延迟
    uint32_t min_latency = UINT32_MAX;
    uint32_t max_latency = 0;
    uint64_t total_latency = 0;

    for (int i = 0; i < irq_count; i++) {
        uint32_t latency = irq_timestamps[i] - trigger_time;
        if (latency < min_latency) min_latency = latency;
        if (latency > max_latency) max_latency = latency;
        total_latency += latency;
    }

    printk("Interrupt latency:\n");
    printk("  Min: %u cycles (%.2f us)\n", 
           min_latency, (float)min_latency / sys_clock_hw_cycles_per_sec() * 1000000);
    printk("  Max: %u cycles (%.2f us)\n", 
           max_latency, (float)max_latency / sys_clock_hw_cycles_per_sec() * 1000000);
    printk("  Avg: %u cycles (%.2f us)\n", 
           (uint32_t)(total_latency / irq_count), 
           (float)(total_latency / irq_count) / sys_clock_hw_cycles_per_sec() * 1000000);
    printk("  Jitter: %u cycles (%.2f us)\n", 
           max_latency - min_latency, 
           (float)(max_latency - min_latency) / sys_clock_hw_cycles_per_sec() * 1000000);
}

抖动分析

void analyze_jitter(uint32_t *timestamps, size_t count)
{
    uint32_t intervals[count - 1];

    // 计算相邻事件的时间间隔
    for (size_t i = 1; i < count; i++) {
        intervals[i - 1] = timestamps[i] - timestamps[i - 1];
    }

    // 计算平均间隔
    uint64_t total = 0;
    for (size_t i = 0; i < count - 1; i++) {
        total += intervals[i];
    }
    uint32_t avg_interval = total / (count - 1);

    // 计算标准差(抖动)
    uint64_t variance = 0;
    for (size_t i = 0; i < count - 1; i++) {
        int32_t diff = intervals[i] - avg_interval;
        variance += diff * diff;
    }
    uint32_t std_dev = sqrt(variance / (count - 1));

    printk("Jitter analysis:\n");
    printk("  Average interval: %u cycles\n", avg_interval);
    printk("  Standard deviation: %u cycles\n", std_dev);
    printk("  Jitter (3σ): %u cycles\n", std_dev * 3);
}

功耗优化

功耗优化对于电池供电的设备至关重要,可以显著延长电池寿命。

功耗分析

测量工具和方法

  1. 硬件测量
  2. 使用电流表串联在电源回路中
  3. 使用功耗分析仪(如 Nordic Power Profiler Kit)
  4. 使用示波器测量电流波形

  5. 软件估算

  6. 根据数据手册的典型功耗值
  7. 使用 MCU 内置的功耗监控功能

功耗测量示例

// 使用 Nordic Power Profiler Kit
void measure_power_consumption(void)
{
    printk("Starting power measurement...\n");

    // 正常运行模式
    printk("Active mode\n");
    k_sleep(K_SECONDS(5));

    // 进入低功耗模式
    printk("Sleep mode\n");
    k_sleep(K_SECONDS(5));

    // 深度睡眠模式
    printk("Deep sleep mode\n");
    k_sleep(K_SECONDS(5));
}

睡眠模式使用

Zephyr 睡眠模式

graph LR
    A[Active] -->|空闲| B[Idle]
    B -->|深度空闲| C[Standby]
    C -->|系统关闭| D[Suspend]
    D -->|唤醒| A
    B -->|唤醒| A
    C -->|唤醒| A

睡眠模式对比

模式 功耗 唤醒时间 保持状态 适用场景
Active 最高 - 全部 正常运行
Idle < 10us 全部 短暂空闲
Standby < 100us RAM 周期性任务
Suspend 最低 > 1ms 部分 RAM 长时间休眠

配置睡眠模式

# prj.conf
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_POLICY_DEFAULT=y

# 配置空闲时自动进入低功耗模式
CONFIG_PM_POLICY_APP=y

使用睡眠模式

#include <zephyr/pm/pm.h>
#include <zephyr/pm/policy.h>

void low_power_task(void)
{
    while (1) {
        // 执行任务
        process_sensor_data();

        // 进入低功耗模式
        k_sleep(K_SECONDS(10));  // 自动进入最深的睡眠模式
    }
}

// 自定义电源策略
const struct pm_state_info *pm_policy_next_state(uint8_t cpu, int32_t ticks)
{
    // 如果空闲时间 > 1 秒,进入 Suspend 模式
    if (ticks > k_ms_to_ticks_ceil32(1000)) {
        return &pm_states[PM_STATE_SUSPEND_TO_RAM];
    }
    // 如果空闲时间 > 100ms,进入 Standby 模式
    else if (ticks > k_ms_to_ticks_ceil32(100)) {
        return &pm_states[PM_STATE_STANDBY];
    }
    // 否则进入 Idle 模式
    else {
        return &pm_states[PM_STATE_RUNTIME_IDLE];
    }
}

外设管理

不用时关闭外设

#include <zephyr/pm/device.h>

void power_down_peripherals(void)
{
    const struct device *uart = device_get_binding("UART_0");
    const struct device *spi = device_get_binding("SPI_1");
    const struct device *i2c = device_get_binding("I2C_0");

    // 关闭外设
    pm_device_action_run(uart, PM_DEVICE_ACTION_SUSPEND);
    pm_device_action_run(spi, PM_DEVICE_ACTION_SUSPEND);
    pm_device_action_run(i2c, PM_DEVICE_ACTION_SUSPEND);
}

void power_up_peripherals(void)
{
    const struct device *uart = device_get_binding("UART_0");
    const struct device *spi = device_get_binding("SPI_1");
    const struct device *i2c = device_get_binding("I2C_0");

    // 唤醒外设
    pm_device_action_run(uart, PM_DEVICE_ACTION_RESUME);
    pm_device_action_run(spi, PM_DEVICE_ACTION_RESUME);
    pm_device_action_run(i2c, PM_DEVICE_ACTION_RESUME);
}

按需使用外设

void read_sensor_low_power(void)
{
    const struct device *i2c = device_get_binding("I2C_0");

    // 唤醒 I2C
    pm_device_action_run(i2c, PM_DEVICE_ACTION_RESUME);

    // 读取传感器
    uint8_t data[2];
    i2c_read(i2c, data, 2, SENSOR_ADDR);

    // 关闭 I2C
    pm_device_action_run(i2c, PM_DEVICE_ACTION_SUSPEND);
}

时钟管理

动态调频(DVFS)

// 高性能模式:64 MHz
void set_high_performance_mode(void)
{
    // 设置 CPU 频率为 64 MHz
    clock_control_set_rate(clock_dev, 
                          CLOCK_CONTROL_NRF_SUBSYS_HF, 
                          64000000);
}

// 低功耗模式:16 MHz
void set_low_power_mode(void)
{
    // 设置 CPU 频率为 16 MHz
    clock_control_set_rate(clock_dev, 
                          CLOCK_CONTROL_NRF_SUBSYS_HF, 
                          16000000);
}

// 根据负载动态调整
void adaptive_frequency_scaling(void)
{
    uint32_t cpu_usage = get_cpu_usage();

    if (cpu_usage > 80) {
        set_high_performance_mode();
    } else if (cpu_usage < 20) {
        set_low_power_mode();
    }
}

功耗优化案例:从 10mA 到 10uA

初始配置(高功耗):

// 主循环:持续运行
void main(void)
{
    const struct device *sensor = device_get_binding("SENSOR");

    while (1) {
        // 每 100ms 读取一次传感器
        read_sensor(sensor);
        k_sleep(K_MSEC(100));
    }
}

测量结果: - 平均功耗:10 mA - 电池寿命(200 mAh):20 小时

优化后配置(低功耗):

# prj.conf
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_POLICY_DEFAULT=y
CONFIG_TICKLESS_KERNEL=y
void main(void)
{
    const struct device *sensor = device_get_binding("SENSOR");

    // 关闭不需要的外设
    power_down_unused_peripherals();

    while (1) {
        // 唤醒传感器
        pm_device_action_run(sensor, PM_DEVICE_ACTION_RESUME);

        // 读取传感器
        read_sensor(sensor);

        // 关闭传感器
        pm_device_action_run(sensor, PM_DEVICE_ACTION_SUSPEND);

        // 进入深度睡眠 10 秒
        k_sleep(K_SECONDS(10));
    }
}

优化结果: - 平均功耗:10 uA - 电池寿命(200 mAh):20,000 小时(约 2.3 年) - 功耗降低:1000 倍

优化措施总结

措施 功耗降低
增加采样间隔(100ms → 10s) 100 倍
启用深度睡眠模式 5 倍
关闭不用的外设 2 倍
总计 1000 倍

性能分析工具

内置工具

Kernel Stats

#include <zephyr/kernel.h>

void print_kernel_stats(void)
{
    struct k_thread *thread;

    printk("\n=== Kernel Statistics ===\n");
    printk("System uptime: %lld ms\n", k_uptime_get());
    printk("Cycle count: %u\n", k_cycle_get_32());

    printk("\n=== Thread Information ===\n");
    K_THREAD_FOREACH(thread) {
        printk("Thread: %s\n", k_thread_name_get(thread));
        printk("  State: %d\n", thread->base.thread_state);
        printk("  Priority: %d\n", thread->base.prio);

        #ifdef CONFIG_THREAD_STACK_INFO
        size_t unused;
        k_thread_stack_space_get(thread, &unused);
        printk("  Stack: %zu / %zu bytes used\n", 
               thread->stack_info.size - unused, 
               thread->stack_info.size);
        #endif
    }
}

Thread Monitor

# prj.conf
CONFIG_THREAD_MONITOR=y
CONFIG_THREAD_NAME=y
CONFIG_THREAD_STACK_INFO=y
uart:~$ kernel threads
Scheduler: 257 since last call
Threads:
 0x20000800 idle            
        options: 0x0, priority: 15 timeout: 0
        state: pending
        stack size 320, unused 280, usage 40 / 320 (12 %)

 0x20000900 main            
        options: 0x0, priority: 0 timeout: 0
        state: pending
        stack size 1024, unused 512, usage 512 / 1024 (50 %)

外部工具

SystemView

SEGGER SystemView 是一个实时系统分析工具,可以可视化线程切换、中断和事件。

# prj.conf
CONFIG_SEGGER_SYSTEMVIEW=y
CONFIG_USE_SEGGER_RTT=y
#include <SEGGER_SYSVIEW.h>

void main(void)
{
    SEGGER_SYSVIEW_Conf();

    // 应用代码
}

使用步骤: 1. 在 prj.conf 中启用 SystemView 2. 编译并烧录程序 3. 打开 SystemView 软件 4. 连接到目标设备 5. 开始记录和分析

Tracealyzer

Percepio Tracealyzer 是另一个强大的实时系统分析工具。

# prj.conf
CONFIG_PERCEPIO_TRACERECORDER=y
CONFIG_PERCEPIO_TRC_CFG_RECORDER_MODE=TRC_RECORDER_MODE_SNAPSHOT

性能计数器

使用 ARM Cortex-M DWT

#include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h>

void dwt_init(void)
{
    // 启用 DWT
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;

    // 重置周期计数器
    DWT->CYCCNT = 0;

    // 启用周期计数器
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}

uint32_t dwt_get_cycles(void)
{
    return DWT->CYCCNT;
}

void measure_function_cycles(void)
{
    uint32_t start, end, cycles;

    start = dwt_get_cycles();

    // 被测试的函数
    function_to_measure();

    end = dwt_get_cycles();
    cycles = end - start;

    printk("Function took %u cycles\n", cycles);
}

Cache Miss 统计

void enable_cache_profiling(void)
{
    // 启用 I-Cache miss 计数
    DWT->CTRL |= DWT_CTRL_CPIEVTENA_Msk;

    // 启用 D-Cache miss 计数
    DWT->CTRL |= DWT_CTRL_EXCEVTENA_Msk;
}

void print_cache_stats(void)
{
    uint32_t icache_miss = DWT->CPICNT;
    uint32_t dcache_miss = DWT->EXCCNT;

    printk("I-Cache misses: %u\n", icache_miss);
    printk("D-Cache misses: %u\n", dcache_miss);
}

内存分析

Heap Usage

#include <zephyr/sys/heap_listener.h>

static void heap_event_handler(enum heap_event_type event, 
                               void *mem, size_t size)
{
    switch (event) {
    case HEAP_EVENT_ALLOC:
        printk("Allocated %zu bytes at %p\n", size, mem);
        break;
    case HEAP_EVENT_FREE:
        printk("Freed memory at %p\n", mem);
        break;
    }
}

void init_heap_monitoring(void)
{
    static struct heap_listener listener = {
        .event_handler = heap_event_handler
    };

    heap_listener_register(&listener);
}

Stack Usage

void check_stack_usage(void)
{
    struct k_thread *thread;

    printk("\n=== Stack Usage ===\n");
    K_THREAD_FOREACH(thread) {
        size_t unused;
        k_thread_stack_space_get(thread, &unused);

        size_t used = thread->stack_info.size - unused;
        float usage_percent = (float)used / thread->stack_info.size * 100;

        printk("%s: %zu / %zu bytes (%.1f%%)\n",
               k_thread_name_get(thread),
               used, thread->stack_info.size, usage_percent);

        if (usage_percent > 80) {
            printk("  WARNING: High stack usage!\n");
        }
    }
}

优化案例

案例 1:物联网传感器节点优化(功耗)

应用场景: - 电池供电的温湿度传感器 - 每 10 分钟上报一次数据 - 要求电池寿命 > 1 年

初始实现

void main(void)
{
    const struct device *sensor = device_get_binding("SHT3X");
    const struct device *lora = device_get_binding("LORA");

    while (1) {
        // 读取传感器
        float temp, humidity;
        read_sensor(sensor, &temp, &humidity);

        // 发送数据
        send_lora(lora, temp, humidity);

        // 等待 10 分钟
        k_sleep(K_MINUTES(10));
    }
}

初始功耗: - 平均功耗:5 mA - 电池寿命(1000 mAh):200 小时(8.3 天)

优化措施

  1. 启用电源管理

    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
    CONFIG_TICKLESS_KERNEL=y
    

  2. 关闭不用的外设

    void main(void)
    {
        // 关闭 UART、LED 等
        power_down_unused_peripherals();
    
        const struct device *sensor = device_get_binding("SHT3X");
        const struct device *lora = device_get_binding("LORA");
    
        while (1) {
            // 唤醒传感器
            pm_device_action_run(sensor, PM_DEVICE_ACTION_RESUME);
            float temp, humidity;
            read_sensor(sensor, &temp, &humidity);
            pm_device_action_run(sensor, PM_DEVICE_ACTION_SUSPEND);
    
            // 唤醒 LoRa
            pm_device_action_run(lora, PM_DEVICE_ACTION_RESUME);
            send_lora(lora, temp, humidity);
            pm_device_action_run(lora, PM_DEVICE_ACTION_SUSPEND);
    
            // 进入深度睡眠
            k_sleep(K_MINUTES(10));
        }
    }
    

  3. 优化 LoRa 发送

    // 使用最低功率和最低速率
    lora_set_tx_power(lora, 2);  // 2 dBm
    lora_set_spreading_factor(lora, 12);  // SF12
    

优化结果: - 平均功耗:50 uA - 电池寿命(1000 mAh):20,000 小时(2.3 年) - 功耗降低:100 倍

案例 2:电机控制系统优化(实时性)

应用场景: - 伺服电机控制 - PWM 频率:20 kHz - 要求控制周期:50 us - 最大抖动:< 5 us

初始实现

#define CONTROL_PERIOD_US 50

void motor_control_thread(void)
{
    while (1) {
        // 读取编码器
        int32_t position = read_encoder();

        // PID 控制
        float output = pid_calculate(position);

        // 更新 PWM
        update_pwm(output);

        // 等待下一个周期
        k_usleep(CONTROL_PERIOD_US);
    }
}

初始性能: - 平均控制周期:52 us - 最大抖动:15 us - 不满足实时性要求

优化措施

  1. 提高线程优先级

    #define MOTOR_CONTROL_PRIORITY 0  // 最高优先级
    
    K_THREAD_DEFINE(motor_control_tid, 2048,
                    motor_control_thread, NULL, NULL, NULL,
                    MOTOR_CONTROL_PRIORITY, 0, 0);
    

  2. 使用硬件定时器

    static struct k_timer control_timer;
    
    void control_timer_handler(struct k_timer *timer)
    {
        // 在定时器中断中触发控制任务
        k_sem_give(&control_sem);
    }
    
    void motor_control_thread(void)
    {
        k_timer_init(&control_timer, control_timer_handler, NULL);
        k_timer_start(&control_timer, K_USEC(CONTROL_PERIOD_US), 
                      K_USEC(CONTROL_PERIOD_US));
    
        while (1) {
            k_sem_take(&control_sem, K_FOREVER);
    
            // 控制逻辑
            int32_t position = read_encoder();
            float output = pid_calculate(position);
            update_pwm(output);
        }
    }
    

  3. 优化 PID 计算

    // 使用定点运算代替浮点运算
    typedef int32_t fixed_t;  // Q16.16 定点数
    
    fixed_t pid_calculate_fixed(int32_t position)
    {
        static int32_t integral = 0;
        static int32_t prev_error = 0;
    
        int32_t error = setpoint - position;
        integral += error;
        int32_t derivative = error - prev_error;
        prev_error = error;
    
        // 定点运算
        fixed_t output = (kp * error + ki * integral + kd * derivative) >> 16;
    
        return output;
    }
    

  4. 禁用不必要的中断

    void init_motor_control(void)
    {
        // 禁用低优先级中断
        irq_disable(UART_IRQ);
        irq_disable(USB_IRQ);
    
        // 只保留关键中断
        IRQ_CONNECT(TIMER_IRQ, 0, control_timer_isr, NULL, 0);
        IRQ_CONNECT(ENCODER_IRQ, 1, encoder_isr, NULL, 0);
    }
    

优化结果: - 平均控制周期:50.2 us - 最大抖动:3 us - 满足实时性要求

优化效果对比

指标 优化前 优化后 改善
平均周期 52 us 50.2 us 3.5%
最大抖动 15 us 3 us 80%
CPU 使用率 45% 35% 22%

案例 3:数据采集器优化(性能)

应用场景: - 多通道 ADC 数据采集 - 采样率:100 kHz - 8 个通道 - 要求 CPU 使用率 < 50%

初始实现

#define SAMPLE_RATE 100000
#define NUM_CHANNELS 8

void data_acquisition_thread(void)
{
    uint16_t samples[NUM_CHANNELS];

    while (1) {
        // 逐个通道采样
        for (int i = 0; i < NUM_CHANNELS; i++) {
            adc_read(adc_dev, i, &samples[i]);
        }

        // 处理数据
        process_samples(samples, NUM_CHANNELS);

        // 等待下一个采样周期
        k_usleep(10);  // 100 kHz = 10 us
    }
}

初始性能: - CPU 使用率:85% - 丢失采样:15% - 不满足性能要求

优化措施

  1. 使用 DMA 传输

    #define BUFFER_SIZE 1024
    
    static uint16_t adc_buffer[BUFFER_SIZE * NUM_CHANNELS];
    
    void init_adc_dma(void)
    {
        struct adc_sequence sequence = {
            .channels = BIT_MASK(NUM_CHANNELS),
            .buffer = adc_buffer,
            .buffer_size = sizeof(adc_buffer),
            .resolution = 12,
        };
    
        // 启动 DMA 传输
        adc_read_async(adc_dev, &sequence, &adc_async_sig);
    }
    
    void adc_dma_callback(const struct device *dev, 
                          const struct adc_sequence *sequence, 
                          uint16_t sampling_index)
    {
        // DMA 完成,通知处理线程
        k_sem_give(&data_ready_sem);
    }
    

  2. 批量处理数据

    void data_processing_thread(void)
    {
        while (1) {
            k_sem_take(&data_ready_sem, K_FOREVER);
    
            // 批量处理 1024 个采样
            process_samples_batch(adc_buffer, BUFFER_SIZE * NUM_CHANNELS);
        }
    }
    

  3. 优化数据处理算法

    // 使用 SIMD 指令加速
    void process_samples_simd(uint16_t *samples, size_t count)
    {
        // ARM NEON 指令
        #ifdef CONFIG_CPU_CORTEX_M7
        for (size_t i = 0; i < count; i += 8) {
            // 一次处理 8 个采样
            uint16x8_t data = vld1q_u16(&samples[i]);
            uint16x8_t result = process_vector(data);
            vst1q_u16(&samples[i], result);
        }
        #else
        // 标量处理
        for (size_t i = 0; i < count; i++) {
            samples[i] = process_sample(samples[i]);
        }
        #endif
    }
    

  4. 使用双缓冲

    static uint16_t adc_buffer[2][BUFFER_SIZE * NUM_CHANNELS];
    static uint8_t current_buffer = 0;
    
    void adc_dma_callback(const struct device *dev, 
                          const struct adc_sequence *sequence, 
                          uint16_t sampling_index)
    {
        // 切换缓冲区
        current_buffer = 1 - current_buffer;
    
        // 启动下一次 DMA
        sequence->buffer = adc_buffer[current_buffer];
        adc_read_async(adc_dev, sequence, &adc_async_sig);
    
        // 通知处理线程处理另一个缓冲区
        k_sem_give(&data_ready_sem);
    }
    

优化结果: - CPU 使用率:35% - 丢失采样:0% - 满足性能要求

优化效果对比

指标 优化前 优化后 改善
CPU 使用率 85% 35% 59%
丢失采样率 15% 0% 100%
吞吐量 85 kSPS 100 kSPS 18%

实操任务

任务 1:优化 hello_world,减小到最小体积

目标:将 hello_world 示例的 Flash 占用减小到 < 30 KB。

步骤

  1. 编译默认配置,记录初始大小:

    cd ~/zephyrproject/zephyr
    west build -b nrf52840dk_nrf52840 samples/hello_world
    

  2. 创建优化配置文件 prj_minimal.conf

    CONFIG_MINIMAL_LIBC=y
    CONFIG_MINIMAL_LIBC_MALLOC=n
    CONFIG_PRINTK=n
    CONFIG_EARLY_CONSOLE=n
    CONFIG_CONSOLE=n
    CONFIG_UART_CONSOLE=n
    CONFIG_SERIAL=n
    CONFIG_SIZE_OPTIMIZATIONS=y
    CONFIG_LTO=y
    CONFIG_ASSERT=n
    CONFIG_THREAD_NAME=n
    CONFIG_THREAD_MONITOR=n
    CONFIG_DEBUG=n
    

  3. 使用优化配置重新编译:

    west build -b nrf52840dk_nrf52840 samples/hello_world -- -DCONF_FILE=prj_minimal.conf
    

  4. 对比优化前后的大小:

    # 查看内存使用
    west build -t rom_report
    west build -t ram_report
    

验证标准: - Flash 使用 < 30 KB - 程序能够正常运行 - 理解每个配置选项的作用

任务 2:优化多线程应用,降低 CPU 使用率

目标:优化一个多线程应用,将 CPU 使用率从 > 60% 降低到 < 40%。

初始代码

#include <zephyr/kernel.h>

#define THREAD_STACK_SIZE 1024
#define THREAD_PRIORITY 7

void thread1(void)
{
    while (1) {
        // 模拟工作负载
        k_busy_wait(100);
        k_yield();
    }
}

void thread2(void)
{
    while (1) {
        // 模拟工作负载
        k_busy_wait(100);
        k_yield();
    }
}

void thread3(void)
{
    while (1) {
        // 模拟工作负载
        k_busy_wait(100);
        k_yield();
    }
}

K_THREAD_DEFINE(thread1_id, THREAD_STACK_SIZE, thread1, 
                NULL, NULL, NULL, THREAD_PRIORITY, 0, 0);
K_THREAD_DEFINE(thread2_id, THREAD_STACK_SIZE, thread2, 
                NULL, NULL, NULL, THREAD_PRIORITY, 0, 0);
K_THREAD_DEFINE(thread3_id, THREAD_STACK_SIZE, thread3, 
                NULL, NULL, NULL, THREAD_PRIORITY, 0, 0);

优化步骤

  1. 启用线程运行时统计:

    CONFIG_THREAD_RUNTIME_STATS=y
    CONFIG_SCHED_THREAD_USAGE=y
    

  2. 测量初始 CPU 使用率

  3. 优化策略:

  4. 合并线程
  5. 使用工作队列
  6. 增加睡眠时间
  7. 调整优先级

  8. 重新测量 CPU 使用率

验证标准: - CPU 使用率 < 40% - 功能正常 - 理解优化原理

任务 3:优化低功耗应用,实现 < 100uA 平均功耗

目标:实现一个周期性采集传感器数据的应用,平均功耗 < 100 uA。

需求: - 每 30 秒读取一次温度传感器 - 通过 UART 输出数据 - 使用 nRF52840 DK

步骤

  1. 创建基础应用:

    #include <zephyr/kernel.h>
    #include <zephyr/device.h>
    #include <zephyr/drivers/sensor.h>
    
    void main(void)
    {
        const struct device *sensor = DEVICE_DT_GET(DT_ALIAS(temp_sensor));
    
        while (1) {
            struct sensor_value temp;
            sensor_sample_fetch(sensor);
            sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, &temp);
    
            printk("Temperature: %d.%06d C\n", 
                   temp.val1, temp.val2);
    
            k_sleep(K_SECONDS(30));
        }
    }
    

  2. 配置电源管理:

    CONFIG_PM=y
    CONFIG_PM_DEVICE=y
    CONFIG_TICKLESS_KERNEL=y
    

  3. 优化代码:

  4. 关闭不用的外设
  5. 使用低功耗 UART
  6. 优化传感器读取

  7. 测量功耗:

  8. 使用 Power Profiler Kit
  9. 或使用电流表

验证标准: - 平均功耗 < 100 uA - 功能正常 - 理解功耗优化技术

学习总结

完成本章学习后,你应该掌握:

  1. 系统裁剪
  2. 使用 Kconfig 裁剪不需要的功能
  3. 优化代码体积和内存占用
  4. 理解各配置选项的影响

  5. 性能优化

  6. 分析和优化 CPU 使用率
  7. 减少上下文切换
  8. 优化中断延迟
  9. 使用 DMA 和硬件加速

  10. 实时性优化

  11. 分析实时性需求
  12. 配置优先级和调度策略
  13. 测量和优化响应时间
  14. 减少抖动

  15. 功耗优化

  16. 使用睡眠模式
  17. 管理外设电源
  18. 动态调频
  19. 测量和分析功耗

  20. 性能分析工具

  21. 使用内置工具(Kernel Stats、Thread Monitor)
  22. 使用外部工具(SystemView、Tracealyzer)
  23. 使用性能计数器
  24. 分析内存和栈使用

下一步学习

参考资源

优化建议

  • 优化是一个迭代过程,先测量再优化
  • 不要过早优化,先保证功能正确
  • 使用工具辅助分析,不要盲目优化
  • 记录优化前后的数据,量化优化效果
  • 在优化和可维护性之间找到平衡

💬 讨论与反馈

欢迎在下方评论区分享您的学习心得、提出问题或给出建议。评论系统基于 GitHub Discussions,需要 GitHub 账号登录。

正在加载评论系统...