RTT——多线程实现操作思路

freertos

使用模块化,对每一个功能进行单独的封装.c.h,便于后期的移植和调用。

模块的.c.h

.h文件

  • 编写头文件的基本框架

  • 定义需要用的的参数定义,枚举、结构体、共用体等

  • 函数定义,函数包括:线程入口函数、执行操作的函数等……

.c文件

头文件、宏定义

  • rtthread.h:RT-thread内核有关,提供的接口声明、类型定义

  • rtdevice.h:系统用于设备驱动的头文件,定义了个各种接口、宏定义等

  • drv_common.h:与硬件驱动相关的公共头文件,不同硬件开发中相同部分的定义,如高低电平宏定义等

  • 模块的头文件

  • 日志打印标签

    • 宏定义日志打印标签DBG_TAG:Debug Tag

    • 宏定义日志打印的级别DBG_LVL:在rtdbg.h中有详细的说明
      日志级别分为四类:DBG_ERROR、DBG_WARNING、DBG_INFO、DBG_LOG,对应着0~4
      DBG_ERROR:错误级别日志 系统出现崩溃时
      DBG_WARNING:警告级日志 系统出现警告,比如设备参数问题等
      DBG_INFO消息级别日志 消息打印,初始化等操作的打印
      DBG_LOG调试级别日志 开发和调试阶段使用

    • 引用rtdgb.h,一定要在宏定义之后再引用

  • 定义引脚

    • 模块驱动的引脚
  • 全局变量

    • 全局变量参数的定义,如头文件中的枚举、结构体等变量
  • 函数定义

    • 入口函数:入口函数一般以“模块名称”_thread_entry;如果时死循环执行的函数中一定要有延时操作,让出CPU

    • msh命令函数:定义命令函数,命令函数的参数一般为(int argn, char argv[])
      argn代表传入参数个数,argv[]代表传入参数的指针,这里一般都是通过中断输入的命令(字符串格式)

    • 导出msh命令到列表中(终端列表):MSH_CMD_EXPORT(command, desc),command代表函数的名字,desc代表描述命令用法的字符串(类似help)

LED.c&LED.h

这里只放LED一个模块的.c.h,我下面main.c是使用LED与BEEP进行演示的

**LED.**h

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-03-14     LIUBIN       the first version
 */
#ifndef APPLICATIONS_INC_LED_H_
#define APPLICATIONS_INC_LED_H_

/* LED的状态选择,三色灯 */
enum led_mode{
    RED = 0,
    YELLOW,
    BULE
};

/* LED入口函数 */
void LED_thread_entry();

#endif /* APPLICATIONS_INC_LED_H_ */

LED.c

/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-03-14     LIUBIN       the first version
 */

#include "rtthread.h"
#include "rtdevice.h"
#include "drv_common.h"

#include "stdlib.h"
#include "../Inc/LED.h"

#define DBG_TAG "LED"
#define DBG_LVL DBG_LOG
#include "rtdbg.h"

#define LED_R GET_PIN(H, 10)
#define LED_Y GET_PIN(H, 11)
#define LED_B GET_PIN(H, 12)

#define LED_ON(n) rt_pin_write(n, PIN_LOW);
#define LED_OFF(n) rt_pin_write(n, PIN_HIGH);

enum led_mode LED_MOD = RED;        //默认红色灯点亮

void led_set_mode(enum led_mode m) {
    LED_MOD = m;
}

void LED_thread_entry() {
    /* 设置外部引脚 */
    rt_pin_mode(LED_R, PIN_MODE_OUTPUT);
    rt_pin_mode(LED_Y, PIN_MODE_OUTPUT);
    rt_pin_mode(LED_B, PIN_MODE_OUTPUT);

    while(1) {
        switch(LED_MOD) {
            case RED:
                LED_ON(LED_R);
                LED_OFF(LED_B);
                LED_OFF(LED_Y);
                break;
            case YELLOW:
                LED_ON(LED_Y);
                LED_OFF(LED_B);
                LED_OFF(LED_R);
                break;
            case BULE:
                LED_ON(LED_B);
                LED_OFF(LED_R);
                LED_OFF(LED_Y);
                break;
            default:
                LOG_D("mode error\n");
        }
        rt_thread_delay(200);
    }
}

void led_msh(int argn, char *argv[]) {
    if(argn < 2){
        LOG_W("ledmode # mode");
        return ;
    }
    led_set_mode(atoi(argv[1]));
}
MSH_CMD_EXPORT(led_msh, led control);

main.c

头文件、宏定义

  • rtthread.h:RT-thread内核有关,提供的接口声明、类型定义

  • DBG_TAG:宏定义日志标签

  • DBG_LVL:宏定义日志等级

  • rtdgb.h:存放调试接口、宏定义的头文件

  • 引用模块的头文件

线程定义

需要提前宏定义一些线程配置

  • 设置线程栈的大小:THREAD_STACK_SIZE

  • 设置线程的优先级:THREAD_PROORITY

  • 设置线程的时间片:THREAD_TIMESLICEb

线程定义(静态/动态)

  • 线程栈空间设置系统对其:ALIGN(RT_ALIGN_SIZE)

  • 定义静态栈空间:static char beep_stack[THREAD_STACK_SIZE]

  • 静态方式定义线程(定义线程控制块):static struct rt_thread beep_thread;

  • 动态方式定义线程(定义线程句柄):rt_thread_t led_thread = RT_NULL;

main主函数

动态方式创建线程

  • 动态定义的线程句柄 = rt_thread_create(……);
    name:线程名、entry:线程入口函数、stack_size:栈大小、priority:线程优先级、tick:时间片
/**
 * This function will create a thread object and allocate thread object memory
 * and stack.
 *
 * @param name the name of thread, which shall be unique
 * @param entry the entry function of thread
 * @param parameter the parameter of thread enter function
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are same priority thread
 *
 * @return the created thread object
 */
rt_thread_t rt_thread_create(const char *name,
                             void (*entry)(void *parameter),
                             void       *parameter,
                             rt_uint32_t stack_size,
                             rt_uint8_t  priority,
                             rt_uint32_t tick)
  • 验证是否成功,判断句柄是不是为RT_NULL,为空则输出日志信息

  • 如果成功则启动线程(rt_thread_startup(句柄))

静态方式初始化线程

  • 调用rt_thread_init,并定义一个整型,存放函数返回值
    thread:之前定义的线程控制块、name:线程名、entry:入口函数、parameter:参数、stack_start:栈起始地址、stack_size:栈大小、priority优先级、tick:时间片
/**
 * This function will initialize a thread, normally it's used to initialize a
 * static thread object.
 *
 * @param thread the static thread object
 * @param name the name of thread, which shall be unique
 * @param entry the entry function of thread
 * @param parameter the parameter of thread enter function
 * @param stack_start the start address of thread stack
 * @param stack_size the size of thread stack
 * @param priority the priority of thread
 * @param tick the time slice if there are same priority thread
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_init(struct rt_thread *thread,
                        const char       *name,
                        void (*entry)(void *parameter),
                        void             *parameter,
                        void             *stack_start,
                        rt_uint32_t       stack_size,
                        rt_uint8_t        priority,
                        rt_uint32_t       tick)
  • 验证是否成功,如果为RT_EOK则为初始化成功,否则日志输出报错

  • 如果成功则调用rt_thread_startup()启动线程

使用msh命令删除线程

  • 删除线程函数,函数中调用rt_thread_delete或者rt_thread_detach;动态分配的线程用rt_thread_delete、静态分配的用rt_thread_detach

  • 使用MSH_CMD_EXPORT函数将命令加载到msh中

main.c示例

/*
 * Copyright (c) 2006-2025, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2025-03-10     LIUBIN    first version
 */

#include <rtthread.h>

#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

#include "./Inc/BEEP.h"
#include "./Inc/LED.h"

#define THREAD_STACK_SIZE 1024
#define THREAD_PRIORITY 20
#define THREAD_TIMESLICE 10

/* 栈首地址必须系统对其 */
ALIGN(RT_ALIGN_SIZE)
static char beep_stack[THREAD_STACK_SIZE];
static struct rt_thread beepthread;
rt_thread_t ledthread = RT_NULL;

int main(void)
{
    int res;

    /* 动态创建线程 */
    ledthread = rt_thread_create("LED",
                                LED_thread_entry,
                                RT_NULL,
                                THREAD_STACK_SIZE,
                                THREAD_PRIORITY,
                                THREAD_TIMESLICE);
    if(ledthread == RT_NULL) {
        LOG_D("create led thread error\n");
        return -1;
    }else {
        rt_thread_startup(ledthread);
    }

    /* 静态初始化线程 */
    res = rt_thread_init(&beepthread,
                        "BEEP",
                        Beep_thread_entry,
                        RT_NULL,
                        &beep_stack,
                        sizeof(beep_stack),
                        THREAD_PRIORITY,
                        THREAD_TIMESLICE);
    if(res == RT_EOK) {
        rt_thread_startup(&beepthread);
    }else {
        LOG_D("init beep thread error\n");
        return -1;
    }

    return RT_EOK;
}

void Delete_LED(){
    rt_thread_delete(ledthread);
}

void Detach_Beep(){
    rt_thread_delete(&beepthread);
}

MSH_CMD_EXPORT(Delete_LED, delete led);
MSH_CMD_EXPORT(Detach_Beep, detach beep);

将程序烧录进开发板,在终端使用help命令即可看见我新增的命令,我们调用命令观察现象

相关文章

rtt——线程间通信(互斥量)

互斥量的概念 在 RT-Thread 中,互斥量(Mutex,互斥锁)用于确保共享资源在任意时刻只被一个线程访问 - **确保共享资源的互斥访问**:互斥量用于保护对共享资源的访问,确保在任意时刻只有一个线程可以访问该资源;避免多个线程同时修改共享资源而导致的数据不一致或竞态条件 - 实现临界区保护:互斥量通常用于实现临界区保护,将对共享资源的访问限制在临界区内。**只有获取了互斥量的线程...

freertos

rtt——线程间通信(消息队列)

**消息队列是一种线程间的通信方式,是邮箱的扩展** 消息队列的工作机制 消息队列能够接收来自线程或中断服务例程中**不固定长度的消息**,**并把消息缓存在自己的内存空间中**。其他线程也能够从消息队列中读取相应的消息,而当消息队列为空时,可挂起读取线程。当有新的消息到达时,挂起的线程将被唤醒为接收并处理消息 **消息队列是一种异步的通信方式** 通常将先进入消息队列的消息先传给线程...

freertos

rtt——线程间的通信(邮箱)

邮箱 **邮箱是实时操作系统中的一种典型的线程间的通信方式** **邮箱的通信方式除了可以一对一,也可以一对多通信,或者多对多通信** 邮箱的工作机制 在RT-thread操作系统中,邮箱是开销较低、效率较高的一种线程间通信方式。 **邮箱中的每一封邮件的容量固定为4字节(刚好容下一个32位系统的指针)** 线程或中断服务程序把一封长度为4字节的邮件发送到指定邮箱中,而其他一...

freertos