RT-Thread内核学习笔记

11 篇文章 5 订阅
2 篇文章 1 订阅
RT-Thread是一个实时操作系统,本文详细介绍了其线程的定义、栈、控制块、初始化和调度器。此外,还讨论了对象容器的概念,包括对象和容器的定义,以及如何管理和存储不同类型的内核对象。文章还提到了线程的创建、静态和动态内存分配,并简要提及了RT-Thread在STM32F407上的移植和其他关键组件如消息队列、信号量和互斥量的应用。
摘要由CSDN通过智能技术生成

RT-Thread

一、线程

1. 线程定义

在裸机系统中所有代码都运行在一个main函数中,只能按照顺序运行程序,当遇到中断时才会打断当前程序,用以提高系统的实时性。如果遇到软件延时等情况,CPU只能在那空转,大大浪费了CPU的性能。而多线程是多个独立且无法返回的函数,就好比有多个裸机系统的main函数同时运行。如果把裸机系统比做单车道,那么多线程就是多车道。

2. 线程栈

在学习C语言的时候,里面有一个内存分区模型如下:

  • 代码区:存放函数体的二进制代码

  • 全局区:存放全局变量静态变量,以及常量

  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量

  • 堆区:用new操作符由程序员分配和释放,若程序员不释放,程序结束时操作系统回收

在裸机系统中,全局变量,局部变量等都放在栈区,而我们为了实现多线程,相当于多个main函数,我们必须将栈区划分成一个个独立的、互不干扰的区域

/* 定义线程栈 */
rt_uint8_t rt_flag1_thread_stack[512];

线程栈其实就是一个数组

3. 线程函数 rt_thread_entry()

线程函数是一个线程实现功能的函数,比如点亮一个LED。一般将其称为入口函数

rt_err_t flag1_thread_entry(void *p_arg)
{
    for(;;)
    {
        //一个线程所需执行的功能的代码
    }
    rt_schedule();//线程手动切换
}

4. 线程控制块 struct rt_thread

线程控制块本质是一个C语言的结构体,里面存放了一个线程的属性,用于控制一个线程。

struct rt_thread// (1)
{
    void *sp; /* 线程栈指针 */
    void *entry; /* 线程入口地址 */
    void *parameter; /* 线程形参 */
    void *stack_addr; /* 线程起始地址 */
    rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */

    rt_list_t tlist; /* 线程链表节点 */
};
typedef struct rt_thread *rt_thread_t;// (2)

5. 线程初始化 rt_thread_init()

当我们定义好线程栈、线程函数、线程控制块后需要将其联系起来,通过**rt_thread__init()**函数

rt_err_t rt_thread_init(struct rt_thread *thread,
						void (*entry)(void *parameter),
						void *parameter,
						void *stack_start,
						rt_uint32_t stack_size)
{
    rt_list_init(&(thread->tlist));

    thread->entry = (void *)entry;
    thread->parameter = parameter;

    thread->stack_addr = stack_start;
    thread->stack_size = stack_size;

    /* 初始化线程栈,并返回线程栈指针 */ )
    thread->sp = (void *)rt_hw_stack_init( thread->entry,
    thread->parameter,
    (void *)((char *)thread->stack_addr +␣thread->stack_size - 4) );

    return RT_EOK;
}

6. 就绪列表

线程创建好后,需要将线程添加到就绪列表,表示线程已经就绪,系统可以随时调度

/* 线程就绪列表 */
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];

就绪列表本质是一个数组,大小为RT_THREAD_PRIORITY_MAX,这个宏用于表示最大有多少个优先级。有多少个优先级这个数组就有多少个元素。数值越小优先级越高。每个元素的类型为rt_list_t,定义如下

struct rt_list_node
{
    struct rt_list_node *next; /* 指向后一个节点 */
    struct rt_list_node *prev; /* 指向前一个节点 */
};
typedef struct rt_list_node rt_list_t;

定义为链表的一个节点,当同一优先级下有多个线程时,可以通过插入链表的方式插入该优先级下

image-20230219094601465

下面是几个链表相关函数

  • rt_list_init

    rt_inline void rt_list_init(rt_list_t *l)
    {
    	l->next = l->prev = l;
    }
    
  • rt_list_insert_after

    /* 在双向链表头部插入一个节点 */
    rt_inline void rt_list_insert_after(rt_list_t *l, rt_list_t *n)
    {
        l->next->prev = n; /* 第 步 */
        n->next = l->next; /* 第 步 */
        l->next = n; /* 第 步 */
        n->prev = l; /* 第 步 */
    }
    
  • rt_list_insert_before

    rt_inline void rt_list_insert_before(rt_list_t *l, rt_list_t *n)
    {
        l->prev->next = n; /* 第 步 */
        n->prev = l->prev; /* 第 步 */
        l->prev = n; /* 第 步 */
        n->next = l; /* 第 步 */
    }
    

7. 调度器

主要功能是实现线程的切换,从就绪列表中找到优先级最高的线程,然后执行该线程。

二、对象容器

1. 对象:所有的数据结构都是对象

其中线程,信号量,互斥量、事件、邮箱、消息队列、内存堆、内存池、设备和定时器有明显的枚举定义,即为每个对象打上了一个数字标签
对象容器-对象类型枚举定义

enum rt_object_class_type
{
    RT_Object_Class_Thread = 0, /* 对象是线程 */
    RT_Object_Class_Semaphore, /* 对象是信号量 */
    RT_Object_Class_Mutex, /* 对象是互斥量 */
    RT_Object_Class_Event, /* 对象是事件 */
    RT_Object_Class_MailBox, /* 对象是邮箱 */
    RT_Object_Class_MessageQueue, /* 对象是消息队列 */
    RT_Object_Class_MemHeap, /* 对象是内存堆 */
    RT_Object_Class_MemPool, /* 对象是内存池 */
    RT_Object_Class_Device, /* 对象是设备 */
    RT_Object_Class_Timer, /* 对象是定时器 */
    RT_Object_Class_Module, /* 对象是模块 */
    RT_Object_Class_Unknown, /* 对象未知 */
    RT_Object_Class_Static = 0x80/* 对象是静态对象 */
};

为了管理这些对象,专门定义了一个对象数据类型结构体rt_object

struct rt_object
{
    char name[RT_NAME_MAX]; (1) /* 内核对象的名字 */
    rt_uint8_t type; (2) /* 内核对象的类型 */
    rt_uint8_t flag; (3) /* 内核对象的状态 */
    rt_list_t list; (4) /* 内核对象的列表节点 */
};
typedef struct rt_object *rt_object_t; (5) /* 内核对象数据类型重定义 */

每个对象都会有一个结构体,比如之前的线程,称为线程控制块,这些控制块都会包含内核对象

struct rt_thread
{
    /* rt 对象 */
    char name[RT_NAME_MAX]; /* 对象的名字 */
    rt_uint8_t type; /* 对象类型 */
    rt_uint8_t flags; /* 对象的状态 */
    rt_list_t list; /* 对象的列表节点 */
    /* 内核对象 */
    /* 或者直接通过struct rt_object 创建一个内核变量 */
    rt_object_t rtobject_thread;
    
    
    rt_list_t tlist; /* 线程链表节点 */
    void *sp; /* 线程栈指针 */
    void *entry; /* 线程入口地址 */
    void *parameter; /* 线程形参 */
    void *stack_addr; /* 线程起始地址 */
    rt_uint32_t stack_size; /* 线程栈大小,单位为字节 */
};

2. 容器:每当创建一个对象,就会将这个对象放到容器中。

struct rt_object_information
{
    enum rt_object_class_type type;// (1) /* 对象类型 */
    rt_list_t object_list;// (2) /* 对象列表节点头 */
    rt_size_t object_size;// (3) /* 对象大小 */
};

容器本质是一个数组,类型为rt_object_information,包含了对象的类型,列表头结点,大小。可以通过节点将同类型的对象连接起来放在双向链表中。

容器的定义:

static struct rt_object_information rt_object_container [RT_Object_Info_Unknown] = 
{
/* 初始化对象容器 - 线程 */ 
{
    RT_Object_Class_Thread,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), 
    sizeof(struct rt_thread)
},

#ifdef RT_USING_SEMAPHORE
/* 初始化对象容器 - 信号量 */
{
    RT_Object_Class_Semaphore,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Semaphore),
    sizeof(struct rt_semaphore)
},
#endif

#ifdef RT_USING_MUTEX 
/* 初始化对象容器 - 互斥量 */
{
    RT_Object_Class_Mutex,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Mutex),
    sizeof(struct rt_mutex)
},
#endif

#ifdef RT_USING_EVENT
/* 初始化对象容器 - 事件 */
{
    RT_Object_Class_Event,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Event),
    sizeof(struct rt_event)
},
#endif

#ifdef RT_USING_MAILBOX
/* 初始化对象容器 - 邮箱 */
{
    RT_Object_Class_MailBox,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MailBox),
    sizeof(struct rt_mailbox)
},
#endif

#ifdef RT_USING_MESSAGEQUEUE
/* 初始化对象容器 - 消息队列 */

{
    RT_Object_Class_MessageQueue,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MessageQueue),
    sizeof(struct rt_messagequeue)
},
#endif

#ifdef RT_USING_MEMHEAP
/* 初始化对象容器 - 内存堆 */
{
    RT_Object_Class_MemHeap,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemHeap),
    sizeof(struct rt_memheap)
},
#endif

#ifdef RT_USING_MEMPOOL
/* 初始化对象容器 - 内存池 */
{
    RT_Object_Class_MemPool,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_MemPool),
    sizeof(struct rt_mempool)
},
#endif

#ifdef RT_USING_DEVICE
/* 初始化对象容器 - 设备 */
{
    RT_Object_Class_Device,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Device), 
    sizeof(struct rt_device)
},
#endif

/* 初始化对象容器 - 定时器 */
/*
{
    RT_Object_Class_Timer,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Timer),
    sizeof(struct rt_timer)
},
*/
#ifdef RT_USING_MODULE
/* 初始化对象容器 - 模块 */
{
    RT_Object_Class_Module,
    _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module),
    sizeof(struct rt_module)
},
#endif

1)对象的类型与容器数组的下标是一致的,数组容量大小为RT_Object_Info_Unknown,由下面的枚举类型rt_object_info_type定义,根据系统中对象的多少来决定,如果所有的宏都被定义,则RT_Object_Info_Unknown值为11,共有12个对象。

/*
* 对象容器数组的下标定义,决定容器的大小
*/
enum rt_object_info_type
{
    RT_Object_Info_Thread = 0, /* 对象是线程 */

    #ifdef RT_USING_SEMAPHORE
    RT_Object_Info_Semaphore, /* 对象是信号量 */
    #endif

    #ifdef RT_USING_MUTEX
    RT_Object_Info_Mutex, /* 对象是互斥量 */
    #endif

    #ifdef RT_USING_EVENT
    RT_Object_Info_Event, /* 对象是事件 */
    #endif

    #ifdef RT_USING_MAILBOX
    RT_Object_Info_MailBox, /* 对象是邮箱 */
    #endif

    #ifdef RT_USING_MESSAGEQUEUE
    RT_Object_Info_MessageQueue, /* 对象是消息队列 */
    #endif

    #ifdef RT_USING_MEMHEAP
    RT_Object_Info_MemHeap, /* 对象是内存堆 */
    #endif

    #ifdef RT_USING_MEMPOOL
    RT_Object_Info_MemPool, /* 对象是内存池 */
    #endif

    #ifdef RT_USING_DEVICE
    RT_Object_Info_Device, /* 对象是设备 */
    #endif

    RT_Object_Info_Timer, /* 对象是定时器 */

    #ifdef RT_USING_MODULE
    RT_Object_Info_Module, /* 对象是模块 */
    #endif

    RT_Object_Info_Unknown, /* 对象未知 */
};

2)_OBJ_CONTAINER_LIST_INIT©

#define _OBJ_CONTAINER_LIST_INIT(c) \
{&(rt_object_container[c].object_list),&(rt_object_container[c].object_list)}

容器的接口

  • rt_object_get_information()获取对象信息

    struct rt_object_information *rt_object_get_information(enum rt_object_class_type type)
    {
    	int index;
    
    	for (index = 0; index < RT_Object_Info_Unknown; index ++)
    	if (rt_object_container[index].type == type) return &rt_object_container[index];
    
    	return RT_NULL;
    }
    
  • rt_object_init()对象初始化

    /**
    * 该函数将初始化对象并将对象添加到对象容器中
    *
    * @param object 要初始化的对象
    * @param type 对象的类型
    * @param name 对象的名字,在整个系统中,对象的名字必须是唯一的
    */
    void rt_object_init(struct rt_object *object, (1)
    					enum rt_object_class_type type, (2)
    					const char *name) (3)
    {
        register rt_base_t temp;
        struct rt_object_information *information;
    
        /* 获取对象信息,即从容器里拿到对应对象列表头指针 */
        information = rt_object_get_information(type); (4)
    
        /* 设置对象类型为静态 */
        object->type = type | RT_Object_Class_Static; (5)
    
        /* 拷贝名字 */
        rt_strncpy(object->name, name, RT_NAME_MAX); (6)
    
        /* 关中断 */
        temp = rt_hw_interrupt_disable(); (7)
    
        /* 将对象插入到容器的对应列表中,不同类型的对象所在的列表不一样 */
        rt_list_insert_after(&(information->object_list), &(object->list)); (8)
    
        /* 使能中断 */
        rt_hw_interrupt_enable(temp);
    }
    

三、空闲线程与阻塞延时

四、创建线程

创建线程-静态内存

在创建一个线程之前,我们需要预先为每一个线程分配一个固定大小的栈空间在SRAM中,也就是一个数组。这就是静态内存。

创建线程-动态内存

创建一个线程之前不需要事先为其分配一个栈空间,在创建线程的时候会一起开辟一个堆空间,堆空间也是在SRAM中的一段内存空间。类比C语言中的动态内存空间分配,这样做的好处是可以更灵活地为线程分配所需空间,节省内存。

在使用方法上静态内存空间与动态内存空间有很大的不同。静态内存空间需要事先分配一个数组,也就是栈空间,而动态内存空间,不需要事先定义线程栈,

静态内存创建一个线程的四个步骤:

  1. 定义线程函数
static void led1_thread_entry(void* parameter)
{
    while (1)
    {
        LED1_ON;
        rt_thread_delay(500); /* 延时 500 个 tick */ (1)

        LED1_OFF;
        rt_thread_delay(500); /* 延时 500 个 tick */
    }
}
  1. 定义线程栈
/* 定义线程控栈时要求 RT_ALIGN_SIZE 个字节对齐 */
ALIGN(RT_ALIGN_SIZE)
/* 定义线程栈 */
static rt_uint8_t rt_led1_thread_stack[1024];
  1. 定义线程控制块
/* 定义线程控制块 */
static struct rt_thread led1_thread;
  1. 初始化线程
rt_thread_init(&led1_thread, /* 线程控制块 */ (1)
            	"led1", /* 线程名字 */ (2)
				led1_thread_entry, /* 线程入口函数 */ (3)
				RT_NULL, /* 线程入口函数参数 */ (4)
				&rt_led1_thread_stack[0], /* 线程栈起始地址 */ (5)
				sizeof(rt_led1_thread_stack), /* 线程栈大小 */ (6)
				3, /* 线程的优先级 */ (7)
				20); /* 线程时间片 */ (8)
  1. 启动线程
rt_thread_startup(&led1_thread);

动态内存创建线程的五个步骤:

  1. 在board.c里可以通过#define(RT_USING_HEAP)来选择是否使用动态内存分配,如果使用动态内存,则下面是board.c文件里有关堆内存初始化的部分代码
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
/* 从内部 SRAM 里面分配一部分静态内存来作为 rtt 的堆空间,这里配置为 4KB */
static uint32_t rt_heap[RT_HEAP_SIZE];
RT_WEAK void *rt_heap_begin_get(void)
{
	return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
	return rt_heap + RT_HEAP_SIZE;
}
#endif

void rt_hw_board_init()
{
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
  1. 定义线程函数
static void led1_thread_entry(void* parameter)
{
    while (1)
    {
        LED1_ON;
        rt_thread_delay(500); /* 延时 500 个 tick */ (1)

        LED1_OFF;
        rt_thread_delay(500); /* 延时 500 个 tick */

    }
}
  1. 定义线程控制块指针
/* 定义线程控制块指针 */
static rt_thread_t led1_thread = RT_NULL;
  1. 创建线程
led1_thread = /* 线程控制块指针 */ (1)
rt_thread_create( "led1", /* 线程名字 */ (2)
                led1_thread_entry, /* 线程入口函数 */ (3)
                RT_NULL, /* 线程入口函数参数 */ (4)
                512, /* 线程栈大小 */ (5)
                3, /* 线程的优先级 */ (6)
                20); /* 线程时间片 */ (7)
  1. 启动线程
if(led1_thread != RT_NULL)//因为线程的内存是由编译器分配的,所以不能够确保分配成功,因此在启动线程之前需要判断一下是否分配成功
{
    rt_thread_startup(led1_thread);
}
else
{
    return -1;
}

五、RT-Thread移植到STM32F407

http://t.csdn.cn/KbvVJ

六、消息队列

消息队列控制块

struct rt_messagequeue
{
    struct rt_ipc_object_parent;
    void *msg_pool;
    rt_uint16_t msg_size;
    rt_uint16_t max_msgs;
    rt_uint16_t entry;
    void *msg_queue_head;
    void *msg_queue_tail;
    void *msg_queue_free;
}
typedef struct rt_messagequeue *rt_mq_t;

定义静态消息队列
struct rt_messagequeue static_mq;
rt_mq_t dynamic_mq;

初始化与脱离

rt_err_t rt_mq_init(rt_mq_t mq, const char *name, void *msgpool, rt_size_t msg_size, rt_size_tpool_size, rt_uint8_t flag);//RT_IPC_FLAG_FIFO,RT_IPC_FLAG_PRIO
rt_err_t rt_mq_detach(rt_mq_t mq);

创建与删除

rt_mq_T rt_mq_create(const char *name,rt_size_t msg_size, rt_size_t max_msgs,rt_uint8_t flag);
rt_err_t rt_mq_delete(rt_mq_t mq);

发送消息

rt_err_t rt_mq_send(rt_mq_t mq, void *buffer, rt_size_t size);
rt_err_t rt_mq_delete(rt_mq_t mq);

接收消息

rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, rt_size_T size, rt_uint32_t timeout);

七、信号量

线程间通信(Internal Process Communication IPC),RT-Thread中的IPC机制包括信号量、互斥量、时间、邮箱、消息队列。通过IPC机制可以协调多个线程/中断的工作

信号量控制块

struct rt_semaphore
{
    struct rt_ipc_object parent;
    
    rt_uint16_t value;
}

定义静态信号量:struct rt_semaphore static_sem

定义动态信号量:rt_sem_t dynamic_sem

API

信号量的初始化与脱离(静态信号量)

rt_err_t rt_sem_init(rt_sem_t sem, const char* name,rt_uint32_value, rt_uint8_t flag);
rt_err_t rt_sem_detach(rt_sem_t sem);

创建与删除

rt_sem_t rt_sem_create(const char* name,rt_uint32_t value, rt_uint8_t flag);//RT_IPC_FLAG_FIFO RT_IPC_FALG_PRIO

获取信号量

rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time);//RT_WAITING_FOREVER = -1
rt_err_t rt_sem_trytake(rt_sem_t sem);

释放信号量

rt_err_t rt_sem_release(rt_sem_t sem);

八、互斥量

互斥量控制块

struct rt_mutex
{
    struct_rt_ipc_object_parent;
    rt_uint16_t value;
    rt_uint8_t original_priority;
    rt_uint8_t hold;
    struct rt_thread *owner;
};
typedef struct rt_mutex rt_mutex_t;
定义静态互斥量
struct rt_mutex static mutex;
定义动态互斥量
rt_mutex_t dynamic_mutex;

互斥量的操作

初始化与脱离
rt_err_t rt_mutex_init(rT_mutex_t mutex, const char *name, rt_uint8_t flag);//RT_IPC_FLAG_FIFO RT_IPC_FLAG_PRIO
rt_err_t rt_mutex_detach(rT_mutex_t mutex);
创建与删除
rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag);
rt_err_t rt_mutex_delete(rt_mutex_t mutex);
获取互斥量
rt_err_T rt_mutex_take(rt_mutex_t mnutex, rt_int_32_t time);//RT_WAITING_FOREVER=-1
释放互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex);

九、临界区

每个线程中访问(操作)临界资源的那段代码称为临界区(Critical Section),每次只允许一个线程进入临界区。

比如有两个线程,线程1对全局变量从0到10000对value赋值,线程2对value赋值50000,如果要保证线程1对value的正常赋值,就需要临界区保护

rint32_t value;
void thread1_entry(void *parameter)
{
    while(1)
    {
        rt_uint32_t count;
        for(count=0;count<10000;count++)
        {
            value++;
        }
    }
}
void thread2_entry(void *parameter)
{
    while(1)
    {
        value = 50000;
    }
}

临界段保护的两种方式:

  1. 禁止调度

禁止调度就是把调度器锁住,不让其进行线程切换。这样就能保证当前运行的任务不被换出,知道调度器解锁,所以进制调度是常用的临界区保护方法。

void thread_entry(void *parameter)
{
    while(1)
    {
        /* 调度器上锁,上锁后将不再切换到其他线程,仅响应中断 */
        rt_enter_critical();
        /* 以下进入临界区 */
        
        /* 调度器解锁 */
        rt_exit_critical();
    }
}
  1. 关闭中断

因为线程的调度都是建立在中断的基础上的,所以关闭中断以后,系统将不能再进行调度,线程本身也自然不会被其他线程抢占了

void thread_entry(void* parameter)
{
    rt_base_level;
    while(1)
    {
        /* 关闭中断 */
        level = rt_hw_interrupt_disable();
        /* 以下是临界区 */
        
        /* 打开中断 */
        rt_hw_interrupt_enable(level);
    }
}

十、事件

信号量主要用于一对一的线程同步,当需要一对多、多对一、多对多的时候就需要时间集来处理

RT-Thread中用一个32位无符号整型变量来表示,变量中的一个位代表一个时间。线程通过逻辑与、逻辑或与一个或多个事件建立关联形成一个事件组合。

事件集控制块

struct rt_event
{
	struct rt_ipc_object parent;
	
	rt_uint32_t set;
};
typedef struct rt_event *rt_event_t;
定义静态事件集
struct rt_event static_evt;
定义动态事件集
rt_event_t dynamic_evt;

事件集的操作

初始化与脱离
rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag);//RT_IPC_FLAG_FIFO RT_IPC_FLAG_PRIO
rt_err_t rt_event_detach(rt_event_t event);
创建与删除
rt_event_t rt_event_create(const char *name, rt_uint8_t flag);
rt_err_t rt_event_delete(rt_event_t event);
发送事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);
接收事件
rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t option, rt_uint32_t timeout, rt_uint32_t *recved);

十一、邮箱

邮箱用于线程间通信,特点是开销比较低,效率较高。邮箱中的每一封邮件只能容纳固定的四字节内容,线程或者终端服务例程把一封4字节长度的邮件发送到邮箱中,而其他需要的线程可以从邮箱中接收这些邮件并进行处理

邮箱控制块

struct rt_mailbox
{
    struct rt_ipc_object parent;
    rt_uint32_t *msg_pool;
    rt_uint16_t size;
    rt_uint16_t entry;
    rt_uint16_t in_offset;
    rt_uint16_t out_offset;
    rt_list_t suspend_sender_thread;
};
typedef struct rt_mailbox *rt_mailbox_t;
定义静态邮箱
struct rt_mailbox static_mb;
定义动态邮箱
rt_mailbox_t dynamic_mb;

邮箱的操作

初始化与脱离
rt_err_t rt_mb_init(rt_mailbox_t mb, const char *name, void *msgpoll, rt_size_t size, rt_uint8_t flag);RT_IPC_FLAG_FIFO RT_IPC_FLAG_PRIO
rt_err_t rt_mb_detach(rt_mailbox_t mb);
创建与删除
rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag);
rt_err_t rt_mb_delete(rt_mailbox_t mb);
发送邮件
rt_err_t rt_mb_send(rt_mailbox_t mb, rt_uint32_t value);
rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_uint32_t value, rt_uint32_t timeout);
接收邮件
rt_err_T rt_mb_recv(rt_mailbox_t mb, rt_uint32_t *value, rt_int32_t timeout);

十二、定时器

定时器的操作

初始化与脱离(静态方式)

coid rt_timer_init(rt_timer_t timer, const char* name, void(*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag);
rt_err_t rt_timer_detach(rt_timer_t timer);

创建与删除(动态方式)

rt_timer_t rt_timer_create(const char *name, void(*timeout)(void *parameter), void*parameter, rt_tick_t time, rt_uint8_t flag);

flag取值

1 RT_TIMER_FLAG_ONE_SHOT,单次定时器

2 RT_TIMER_FLAG_PERIODIC,周期定时器

3 RT_TIMER_FLAG_HARD_TIMER,硬件定时器

4 RT_TIMER_FLAG_SOFT_TIMER,软件定时器

1、2、3、4需要从1 2中选一个 3 4 中选一个。通过按位或的方式输入。定时器默认工作在HARD模式下

启动定时器

rt_err_t rt_timer_start(rt_timer_t timer);

停止定时器

rt_err_t rt_timer_stop(rt_timer_t timer);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

指针到处飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值