返回

Zephyr RTOS笔记

目录

参考

速查

指令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# 更新依赖
west update
# 配置项目的menucoinfig
west build -p always -b esp32s3_devkitc/esp32s3/procpu -t menuconfig
# -p 是否清理构建
# 传递 CMake 参数
west config build.board qemu_x86
west build -b nrf52840dk_nrf52840 -- -DCONF_FILE=prj_custom.conf
west build -p always -b esp32s3_devkitc/esp32s3/procpu
# 下载
west flash
# esp32 串口打开
west espressif monitor
# gdb启动
west debug
# 连接串口
west attach -p /dev/ttyUSB0 -b 115200


# 使用自定义 Manifest 初始化工作区
west init -m https://github.com/my-organization/my-project --mr main ~/my-workspace

代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <zephyr/kernel.h>
int main(void)
{
    uint32_t count = 0;

    printk("hello world\n");

    while (1) {
        // 获取系统运行时间(毫秒)
        uint64_t uptime_ms = k_uptime_get();

        // 转换为秒
        uint32_t uptime_sec = uptime_ms / 1000;

        printk("[%u] Uptime: %u seconds (Count: %u)\n", 
               uptime_sec, uptime_sec, count);

        count++;

        // 休眠 1 秒
        k_sleep(K_MSEC(1000));
    }

    return 0;
}

重定向到USB串口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
/ {
    chosen {
        zephyr,console = &usb_serial;
        zephyr,shell-uart = &usb_serial;
    };
};

&usb_serial {
    status = "okay";
};

创建一个项目

在工作区创建一个文件夹用于存放项目 CmakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 指定最低 CMake 版本
cmake_minimum_required(VERSION 3.20.0)

# 查找 Zephyr 包(必须在 project() 之前)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

# 定义项目名称
project(my_app)

# 添加源文件
target_sources(app PRIVATE src/main.c)

prj.conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 串口配置
CONFIG_SERIAL=y
CONFIG_UART_CONSOLE=y

# 日志系统
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3

# GPIO 驱动
CONFIG_GPIO=y

# 线程栈大小
CONFIG_MAIN_STACK_SIZE=2048

app.overlay(重写部分板级设备树)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/* 覆盖 LED 定义 */
&led0 {
    gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
    label = "My Custom LED";
};

/* 添加自定义 I2C 传感器节点 */
&i2c0 {
    status = "okay";

    my_sensor: sensor@48 {
        compatible = "my-vendor,my-sensor";
        reg = <0x48>;
        label = "MY_SENSOR";
    };
};

/* 添加自定义 GPIO 节点 */
/ {
    custom_gpios {
        compatible = "gpio-keys";
        button0: button_0 {
            gpios = <&gpio0 11 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
            label = "Custom Button";
        };
    };
};

核心

线程

相关知识

状态
  1. running
  2. ready
  3. suspended
  4. terminated
优先级
  • <0 不可抢占 后台任务
  • 0-14 可被高优先级任务抢占 大部分进程
  • -1 特殊的协作线程 系统内部使用
计算线程堆栈
1
2
3
4
5
6
7
8
9
#define BASE_STACK_SIZE    512   // 基础栈大小
#define LOCAL_VAR_SIZE     256   // 局部变量空间
#define CALL_DEPTH_SIZE    256   // 函数调用栈
#define SAFETY_MARGIN      256   // 安全余量

#define TOTAL_STACK_SIZE   (BASE_STACK_SIZE + LOCAL_VAR_SIZE + \
                            CALL_DEPTH_SIZE + SAFETY_MARGIN)

K_THREAD_STACK_DEFINE(my_stack, TOTAL_STACK_SIZE);
栈溢出检测
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void check_stack_usage(void)
{
    struct k_thread *current = k_current_get();
    size_t unused;

    // 获取未使用的栈空间
    unused = k_thread_stack_space_get(current, NULL);

    printk("当前线程栈使用情况:\n");
    printk("  未使用空间: %zu 字节\n", unused);

    if (unused < 128) {
        printk("  警告:栈空间不足!\n");
    }
}
创建
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <zephyr/kernel.h>

#define STACK_SIZE 1024
#define THREAD_PRIORITY 5

K_THREAD_STACK_DEFINE(my_stack_area, STACK_SIZE);
struct k_thread my_thread_data;

void my_thread_entry(void *p1, void *p2, void *p3)
{
    printk("线程启动,参数: %d\n", (int)p1);

    while (1) {
        printk("线程运行中...\n");
        k_sleep(K_SECONDS(1));
    }
}

void create_thread_example(void)
{
    k_tid_t my_tid = k_thread_create(
        &my_thread_data,           // 线程控制块
        my_stack_area,             // 线程栈
        K_THREAD_STACK_SIZEOF(my_stack_area),  // 栈大小
        my_thread_entry,           // 线程入口函数
        (void *)42, NULL, NULL,    // 传递给线程的参数
        THREAD_PRIORITY,           // 优先级
        0,                         // 选项(0 表示默认)
        K_NO_WAIT                  // 启动延迟(立即启动)
    );

    printk("线程创建成功,TID: %p\n", my_tid);
}


// 静态创建
#include <zephyr/kernel.h>

void static_thread_entry(void *p1, void *p2, void *p3)
{
    printk("静态线程启动\n");

    while (1) {
        printk("静态线程运行中...\n");
        k_sleep(K_SECONDS(2));
    }
}

// 在编译时定义线程,系统启动时自动创建
K_THREAD_DEFINE(
    static_thread_id,          // 线程 ID(变量名)
    1024,                      // 栈大小
    static_thread_entry,       // 入口函数
    NULL, NULL, NULL,          // 参数
    7,                         // 优先级
    0,                         // 选项
    0                          // 启动延迟(0 表示立即启动)
);
同步机制
Mutex

Zephyr 的互斥锁支持优先级继承,可以避免优先级反转问题。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 定义互斥锁
K_MUTEX_DEFINE(my_mutex);

// 共享资源
static int shared_counter = 0;

void thread_with_mutex(void *p1, void *p2, void *p3)
{
    int thread_id = (int)p1;

    while (1) {
        // 获取互斥锁
        k_mutex_lock(&my_mutex, K_FOREVER);

        // 临界区:访问共享资源
        int old_value = shared_counter;
        k_sleep(K_MSEC(10));  // 模拟处理时间
        shared_counter = old_value + 1;
        printk("线程 %d: counter = %d\n", thread_id, shared_counter);

        // 释放互斥锁
        k_mutex_unlock(&my_mutex);

        k_sleep(K_MSEC(100));
    }
}
Semaphore
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 定义信号量(初始值 0,最大值 10)
K_SEM_DEFINE(my_sem, 0, 10);

// 生产者线程
void producer_thread(void *p1, void *p2, void *p3)
{
    int count = 0;

    while (1) {
        // 生产数据
        k_sleep(K_MSEC(500));
        count++;

        printk("生产者:生产数据 #%d\n", count);

        // 发送信号量(增加计数)
        k_sem_give(&my_sem);
    }
}
// 消费者线程
void consumer_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        // 等待信号量(减少计数)
        printk("消费者:等待数据...\n");
        k_sem_take(&my_sem, K_FOREVER);

        // 消费数据
        printk("消费者:处理数据\n");
        k_sleep(K_MSEC(200));
    }
}
事件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 定义事件对象
K_EVENT_DEFINE(my_event);

// 事件位定义
#define EVENT_SENSOR_READY   BIT(0)  // 传感器就绪
#define EVENT_DATA_RECEIVED  BIT(1)  // 数据接收完成
#define EVENT_PROCESSING_DONE BIT(2) // 处理完成

// 传感器线程
void sensor_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        k_sleep(K_SECONDS(1));
        printk("传感器:数据就绪\n");

        // 设置事件位
        k_event_post(&my_event, EVENT_SENSOR_READY);
    }
}

// 数据接收线程
void data_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        k_sleep(K_MSEC(1500));
        printk("数据接收:完成\n");

        k_event_post(&my_event, EVENT_DATA_RECEIVED);
    }
}

// 主处理线程
void main_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        printk("主线程:等待传感器和数据...\n");

        // 等待多个事件(AND 模式)
        uint32_t events = k_event_wait(&my_event,
                                       EVENT_SENSOR_READY | EVENT_DATA_RECEIVED,
                                       false,  // 不清除事件
                                       K_FOREVER);

        if (events & EVENT_SENSOR_READY) {
            printk("主线程:传感器就绪\n");
        }
        if (events & EVENT_DATA_RECEIVED) {
            printk("主线程:数据已接收\n");
        }

        // 清除已处理的事件
        k_event_clear(&my_event, EVENT_SENSOR_READY | EVENT_DATA_RECEIVED);

        printk("主线程:开始处理\n");
        k_sleep(K_MSEC(500));

        k_event_post(&my_event, EVENT_PROCESSING_DONE);
    }
}

中断

注册
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <zephyr/kernel.h>
#include <zephyr/irq.h>

// 中断号和优先级定义
#define MY_IRQ_NUM      25
#define MY_IRQ_PRIORITY 2

// 中断服务例程
void my_isr(const void *arg)
{
    // ISR 中的代码应尽可能短
    printk("中断触发!\n");

    // 清除中断标志(硬件相关)
    // clear_interrupt_flag();

    // 通知线程处理数据
    // k_sem_give(&data_ready_sem);
}

void setup_interrupt(void)
{
    // 注册中断
    IRQ_CONNECT(MY_IRQ_NUM,           // 中断号
                MY_IRQ_PRIORITY,      // 优先级
                my_isr,               // ISR 函数
                NULL,                 // 参数
                0);                   // 标志

    // 使能中断
    irq_enable(MY_IRQ_NUM);

    printk("中断已配置和使能\n");
}
中断优先级和嵌套
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// 中断优先级配置示例
#define HIGH_PRIORITY_IRQ    0  // 最高优先级
#define MEDIUM_PRIORITY_IRQ  2  // 中等优先级
#define LOW_PRIORITY_IRQ     4  // 低优先级

// 高优先级中断可以抢占低优先级中断
void high_priority_isr(const void *arg)
{
    // 紧急处理
    printk("高优先级中断\n");
}

void low_priority_isr(const void *arg)
{
    printk("低优先级中断开始\n");
    // 如果此时高优先级中断触发,会被抢占
    k_busy_wait(1000);  // 模拟处理时间
    printk("低优先级中断结束\n");
}

void setup_nested_interrupts(void)
{
    IRQ_CONNECT(10, HIGH_PRIORITY_IRQ, high_priority_isr, NULL, 0);
    IRQ_CONNECT(11, LOW_PRIORITY_IRQ, low_priority_isr, NULL, 0);

    irq_enable(10);
    irq_enable(11);
}
ISR 与线程交互

ISR 应该尽可能短,复杂处理应该延迟到线程上下文。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 信号量用于 ISR 到线程通知
K_SEM_DEFINE(isr_sem, 0, 1);

// 数据缓冲区
static volatile uint32_t isr_data = 0;

// 中断服务例程
void data_ready_isr(const void *arg)
{
    // 读取硬件数据(快速操作)
    isr_data = read_hardware_register();

    // 通知处理线程
    k_sem_give(&isr_sem);

    // ISR 结束,不做复杂处理
}

// 数据处理线程
void data_processing_thread(void *p1, void *p2, void *p3)
{
    while (1) {
        // 等待 ISR 通知
        k_sem_take(&isr_sem, K_FOREVER);

        // 在线程上下文中处理数据(可以使用复杂操作)
        uint32_t data = isr_data;
        printk("处理数据: 0x%08x\n", data);

        // 复杂处理:可以调用阻塞函数、分配内存等
        process_data(data);
    }
}

K_THREAD_DEFINE(data_thread, 2048, data_processing_thread,
                NULL, NULL, NULL, 5, 0, 0);

void setup_isr_thread_interaction(void)
{
    IRQ_CONNECT(MY_IRQ_NUM, 2, data_ready_isr, NULL, 0);
    irq_enable(MY_IRQ_NUM);
}

线程间通信

Queue

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <zephyr/kernel.h>

// 消息结构
struct data_msg {
    uint32_t timestamp;
    uint16_t sensor_id;
    uint16_t value;
};
// 定义消息队列(最多 10 条消息)
K_MSGQ_DEFINE(my_msgq, sizeof(struct data_msg), 10, 4);
// 发送线程
void sender_thread(void *p1, void *p2, void *p3)
{
    struct data_msg msg;
    uint32_t count = 0;

    while (1) {
        // 准备消息
        msg.timestamp = k_uptime_get_32();
        msg.sensor_id = 1;
        msg.value = count++;

        // 发送消息
        int ret = k_msgq_put(&my_msgq, &msg, K_NO_WAIT);
        if (ret == 0) {
            printk("发送消息: value=%d\n", msg.value);
        } else {
            printk("消息队列已满\n");
        }

        k_sleep(K_MSEC(500));
    }
}

// 接收线程
void receiver_thread(void *p1, void *p2, void *p3)
{
    struct data_msg msg;

    while (1) {
        // 接收消息(阻塞等待)
        k_msgq_get(&my_msgq, &msg, K_FOREVER);

        printk("接收消息: sensor=%d, value=%d, time=%u\n",
               msg.sensor_id, msg.value, msg.timestamp);

        k_sleep(K_MSEC(200));
    }
}

管道

管道用于传递字节流数据,适合不定长数据传输。(linux中那个管道)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <zephyr/kernel.h>

// 定义管道(缓冲区大小 256 字节)
K_PIPE_DEFINE(my_pipe, 256, 4);

// 写入线程
void pipe_writer_thread(void *p1, void *p2, void *p3)
{
    const char *messages[] = {
        "Hello",
        "Zephyr",
        "RTOS"
    };
    int msg_index = 0;

    while (1) {
        const char *msg = messages[msg_index];
        size_t len = strlen(msg) + 1;  // 包含 null 终止符
        size_t written;

        // 写入管道
        int ret = k_pipe_put(&my_pipe, (void *)msg, len, 
                            &written, len, K_NO_WAIT);

        if (ret == 0) {
            printk("写入管道: %s (%zu 字节)\n", msg, written);
        } else {
            printk("管道已满\n");
        }

        msg_index = (msg_index + 1) % 3;
        k_sleep(K_SECONDS(1));
    }
}

// 读取线程
void pipe_reader_thread(void *p1, void *p2, void *p3)
{
    char buffer[64];

    while (1) {
        size_t read_bytes;

        // 从管道读取
        int ret = k_pipe_get(&my_pipe, buffer, sizeof(buffer) - 1,
                            &read_bytes, 1, K_FOREVER);

        if (ret == 0 && read_bytes > 0) {
            buffer[read_bytes] = '\0';
            printk("从管道读取: %s (%zu 字节)\n", buffer, read_bytes);
        }

        k_sleep(K_MSEC(500));
    }
}

FIFO

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 定义 FIFO
K_FIFO_DEFINE(my_fifo);

// 数据项结构(必须包含 reserved 字段)
struct data_item {
    void *fifo_reserved;  // 必须是第一个字段
    uint32_t data;
    char name[32];
};

// 生产者线程
void fifo_producer(void *p1, void *p2, void *p3)
{
    uint32_t count = 0;

    while (1) {
        // 分配数据项
        struct data_item *item = k_malloc(sizeof(struct data_item));
        if (item == NULL) {
            printk("内存分配失败\n");
            k_sleep(K_SECONDS(1));
            continue;
        }

        // 填充数据
        item->data = count++;
        snprintf(item->name, sizeof(item->name), "Item-%u", item->data);

        // 放入 FIFO
        k_fifo_put(&my_fifo, item);
        printk("生产: %s (data=%u)\n", item->name, item->data);

        k_sleep(K_MSEC(800));
    }
}

// 消费者线程
void fifo_consumer(void *p1, void *p2, void *p3)
{
    while (1) {
        // 从 FIFO 获取(阻塞等待)
        struct data_item *item = k_fifo_get(&my_fifo, K_FOREVER);

        printk("消费: %s (data=%u)\n", item->name, item->data);

        // 处理完后释放内存
        k_free(item);

        k_sleep(K_MSEC(300));
    }
}

定时器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <zephyr/kernel.h>

// 定时器回调函数
void timer_expiry_function(struct k_timer *timer)
{
    printk("定时器触发: %u ms\n", k_uptime_get_32());
}

// 定时器停止回调
void timer_stop_function(struct k_timer *timer)
{
    printk("定时器停止\n");
}

// 定义定时器
K_TIMER_DEFINE(my_timer, timer_expiry_function, timer_stop_function);

void timer_example(void)
{
    printk("启动周期性定时器(每 1 秒)\n");

    // 启动定时器:延迟 0,周期 1 秒
    k_timer_start(&my_timer, K_NO_WAIT, K_SECONDS(1));

    // 运行 5 秒后停止
    k_sleep(K_SECONDS(5));
    k_timer_stop(&my_timer);

    printk("\n启动一次性定时器(2 秒后)\n");

    // 一次性定时器:延迟 2 秒,周期 0
    k_timer_start(&my_timer, K_SECONDS(2), K_NO_WAIT);

    k_sleep(K_SECONDS(3));
}
void timer_status_example(void)
{
    k_timer_start(&my_timer, K_NO_WAIT, K_MSEC(500));

    k_sleep(K_MSEC(100));

    // 查询定时器状态
    uint32_t status = k_timer_status_get(&my_timer);
    printk("定时器触发次数: %u\n", status);

    // 查询剩余时间
    k_ticks_t remaining = k_timer_remaining_get(&my_timer);
    printk("剩余时间: %lld ticks\n", remaining);

    k_timer_stop(&my_timer);
}
Licensed under CC BY-NC-SA 4.0