RT-Thread 的常用函数可以系统地分为几个核心模块。下面是每个模块的详细功能说明和常用 C-API。


📋 RT-Thread 核心模块与常用 C-API

模块核心 C-API作用与解析关键参数/选项
1. 线程管理rt_thread_create()动态创建线程name: 线程名称
entry: 线程入口函数
parameter: 传入参数
stack_size: 栈大小(字节)
priority: 优先级(数值越小越高)
tick: 时间片大小
rt_thread_init()静态初始化线程静态方式需用户预先分配好线程控制块和栈空间
rt_thread_startup()启动线程使线程进入就绪状态,参与系统调度
rt_thread_mdelay()线程毫秒级延时ms: 延时时间(毫秒),调用该函数会使当前线程让出CPU
rt_thread_self()获取当前线程句柄常用于在线程内部获取自身信息
2. 线程间同步rt_sem_create() / rt_sem_take() / rt_sem_release()信号量机制用于同步与资源计数,take获取,release释放
rt_mutex_create() / rt_mutex_take() / rt_mutex_release()互斥量机制用于互斥,解决优先级反转问题,保护临界区
rt_event_init() / rt_event_send() / rt_event_recv()事件集机制用于等待多个“与/或”关系的事件
3. 线程间通信rt_mb_create() / rt_mb_send() / rt_mb_recv()邮箱机制传递4字节的简短消息,效率高
rt_mq_create() / rt_mq_send() / rt_mq_recv()消息队列机制传递可变长度的数据,是邮箱的扩展
4. 系统与设备管理rt_kprintf()格式化打印类似printf,输出到系统控制台,用于调试
rt_device_find() / rt_device_open() / rt_device_read() / rt_device_write() / rt_device_control()统一设备访问接口通过设备名查找设备句柄,然后进行打开、读写、控制等操作
rt_malloc() / rt_calloc() / rt_realloc() / rt_free()动态内存管理从系统堆中分配/释放内存,使用后要及时释放避免内存泄漏

下面,我们来看看其中一些复杂函数在实际开发中是如何使用的。

💡 核心场景与使用思路

1. 线程管理:创建与启动线程

这是RT-Thread应用开发最基础的步骤。

【方式一】动态创建 (rt_thread_create)

推荐使用,内存从动态堆中分配,更灵活。

/* 定义线程入口函数 */
static void led_thread_entry(void *parameter) {
    while (1) {
        rt_kprintf("LED Thread is Running...\n");
        rt_thread_mdelay(1000);
    }
}

/* 在主函数或其他地方动态创建和启动线程 */
void main(void) {
    rt_thread_t led_thread = RT_NULL;
    /* 动态创建线程 */
    led_thread = rt_thread_create("led",
                                  led_thread_entry,
                                  RT_NULL,
                                  1024,   /* 线程栈大小,单位字节 */
                                  25,     /* 线程优先级 */
                                  10);    /* 时间片大小(tick) */
    /* 如果创建成功,则启动线程 */
    if (led_thread != RT_NULL) {
        rt_thread_startup(led_thread);
    }
}

【方式二】静态初始化 (rt_thread_init)

这种方式适合生命周期固定的线程,内存空间在编译时就分配好了。

/* 1. 静态定义线程栈和控制块 */
static char thread1_stack[1024];
static struct rt_thread thread1;

/* 2. 线程入口函数 */
static void thread1_entry(void *param) {
    while (1) {
        /* 线程执行体 */
        rt_thread_mdelay(500);
    }
}

/* 3. 在线程初始化函数中静态初始化并启动 */
void static_thread_sample(void) {
    rt_thread_init(&thread1,                /* 线程控制块 */
                  "thread1",               /* 线程名称 */
                  thread1_entry,           /* 线程入口函数 */
                  RT_NULL,                 /* 线程入口参数 */
                  &thread1_stack[0],       /* 线程栈起始地址 */
                  sizeof(thread1_stack),   /* 线程栈大小 */
                  10,                      /* 线程优先级 */
                  5);                      /* 时间片 */
    rt_thread_startup(&thread1);
}

2. 线程间同步:信号量

信号量时最常用的同步机制。

【场景】中断与线程同步

这个示例展示了如何通过信号量,在中断发生时唤醒一个线程去处理任务。

/* 定义信号量结构体 */
static struct rt_semaphore rx_sem;

/* 中断服务程序 */
void USARTx_IRQHandler(void) {
    /* 进入中断 */
    rt_interrupt_enter();
    if (/* 判断是否接收到数据 */) {
        /* 释放信号量,唤醒等待的线程 */
        rt_sem_release(&rx_sem);
    }
    /* 退出中断 */
    rt_interrupt_leave();
}

/* 数据处理线程 */
void data_process_thread_entry(void *parameter) {
    while (1) {
        /* 永久等待信号量,直到被中断释放 */
        rt_sem_take(&rx_sem, RT_WAITING_FOREVER);
        /* 收到信号,开始处理数据 */
        /* ... */
    }
}

/* 初始化 */
void sync_sample_with_irq(void) {
    /* 初始化信号量,初始计数值为0 */
    rt_sem_init(&rx_sem, "rx_sem", 0, RT_IPC_FLAG_PRIO);
}

3. 线程间通信:消息队列

当需要在线程间传递大量或变长的数据时,消息队列是首选。

这个示例演示了如何创建消息队列,并在线程间发送和接收结构化的消息数据。

/* 定义消息队列控制块 */
static struct rt_messagequeue mq;
/* 定义消息队列中用到的消息结构体 */
typedef struct {
    rt_uint8_t type;
    rt_uint16_t len;
    rt_uint8_t *data;
} message_t;

/* 用于存储消息的内存池 */
static char msg_pool[2048];

/* 发送消息的线程 */
static void sender_thread_entry(void *parameter) {
    message_t msg;
    while (1) {
        msg.type = 0x01;
        msg.len = 16;
        /* 发送消息 */
        rt_mq_send(&mq, &msg, sizeof(msg));
        rt_thread_mdelay(1000);
    }
}

/* 接收消息的线程 */
static void receiver_thread_entry(void *parameter) {
    message_t msg;
    while (1) {
        /* 接收消息,并永久等待 */
        if (rt_mq_recv(&mq, &msg, sizeof(msg), RT_WAITING_FOREVER) == RT_EOK) {
            /* 成功接收到消息,进行处理 */
        }
    }
}

/* 初始化示例 */
void mq_sample(void) {
    /* 初始化消息队列 */
    rt_mq_init(&mq, 
              "test_mq",       /* 名称 */
              &msg_pool[0],    /* 消息内存池 */
              sizeof(message_t), /* 单个消息大小 */
              sizeof(msg_pool),   /* 消息内存池大小 */
              RT_IPC_FLAG_PRIO);  /* 挂起线程的等待方式 */
    /* 创建发送和接收线程... */
}

4. 系统管理:统一的设备访问模型

RT-Thread提供了一套统一的I/O设备模型,让你可以用同样的API操作不同类型的硬件,让硬件访问标准化。

下面的示例展示了如何查找、打开一个串口设备,并通过轮询方式发送和接收数据。

#include <rtthread.h>
#include <rtdevice.h>

#define SAMPLE_UART_NAME       "uart1" /* 串口设备名称 */

/* 查找系统中的串口设备 */
static rt_device_t serial = RT_NULL;

void uart_sample(void) {
    serial = rt_device_find(SAMPLE_UART_NAME);
    if (serial == RT_NULL) {
        rt_kprintf("find %s failed!\n", SAMPLE_UART_NAME);
        return;
    }

    /* 以读写及中断接收方式打开串口设备 */
    rt_device_open(serial, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX);

    /* 发送数据 */
    char send_str[] = "Hello, RT-Thread!\n";
    rt_device_write(serial, 0, send_str, sizeof(send_str));

    /* 接收数据,轮询方式 */
    char ch;
    while (1) {
        if (rt_device_read(serial, 0, &ch, 1) == 1) {
            rt_kprintf("RX: %c\n", ch);
        }
        rt_thread_mdelay(10);
    }
}

5. 内存管理:动态分配与使用的注意事项

动态内存管理是把双刃剑,用得不好容易造成内存泄漏。使用时要格外小心。

下面的示例展示了一个反模式,即申请了内存却忘记释放,造成了内存泄漏。

/* 错误示例:只分配,不释放,导致内存泄漏 */
void bad_memory_usage(void) {
    while (1) {
        /* 动态分配128字节的内存 */
        void *ptr = rt_malloc(128);
        if (ptr != RT_NULL) {
            rt_kprintf("Memory allocated at %p\n", ptr);
            /* 未调用 rt_free(ptr); 内存泄漏! */
        }
        rt_thread_mdelay(100);
    }
}

/* 正确示例:分配内存,使用后立即释放 */
void good_memory_usage(void) {
    void *ptr = rt_malloc(128);
    if (ptr != RT_NULL) {
        /* 使用内存 */
        memset(ptr, 0, 128);
        /* 释放内存 */
        rt_free(ptr);
    }
}

注意

  • 从信号量/互斥量等回调函数或中断里,不要使用rt_malloc这类可能阻塞的函数
  • 为了避免内存碎片化和提高分配效率,对于频繁分配和释放固定大小内存块的场景,应该优先考虑使用内存池rt_mp_create等)。
  • 上面的例子主要是串口操作,RT-Thread的大部分常用功能,如创建内存池、使用互斥量、在中断中正确同步等,更详细的代码示例可以在官方API参考手册的内核例程模块中找到。

提示:以上介绍的函数是RT-Thread内核中最常用的一部分。要深入学习,可以直接阅读RT-Thread源码中的详细注释或在官网查阅完整的API参考手册