上一节我们已经建立了一个空白的RT-Thread工程,但这个工程只有一个进程main,今天我们在上次的基础上继续学习,通过创建多个进程感受RT-Thread和裸跑程序的不同。这次我们将将上次工程中控制LED和串口的两部分分离,独立成两个不同的线程。
使用静态定义方式时,必须先定义静态的线程控制块,并且定义好堆栈空间,然后调用rt_thread_init来完成线程的初始化工作。采用这种方式,线程控制块和堆栈占用的内存会放在RW段,这段空间在编译时就已经确定,它不是可以动态分配的,所以不能被释放。而只能使用rt_thread_detach函数将该线程控制块从对象管理器中脱离。
使用动态定义方式rt_thread_create时,RT-Thread会动态申请线程控制块和堆栈空间。在编译时,编译器是不会感知到这段空间的,只有在程序运行时,RT-Thread才会从系统堆中申请分配这段内存空间,当不需要使用该线程时,调用rt_thread_delete函数就会将这段申请的内存空间重新释放到内存堆中。
这两种方式各有利弊,静态定义方式会占用RW空间,但是不需要动态分配内存,运行时效率高。动态方式不会占用额外的RW空间,占用空间小,但是运行时需要动态分配内存,效率没有静态方式高。总的来说,这两种方式就是空间和时间效率的平衡,可以根据实际环境需求选择采用具体的分配方式。
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_err_t rt_thread_startup(rt_thread_t thread);
rt_err_t rt_thread_delete(rt_thread_t thread);
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_thread_startup函数。
rt_err_t rt_thread_startup(rt_thread_t thread);
rt_err_t rt_thread_detach (rt_thread_t thread);
rt_err_t rt_thread_suspend (rt_thread_t thread); //挂起线程 注意:应尽量避免使用该函数挂起自身线程
rt_err_t rt_thread_resume (rt_thread_t thread); //恢复一个挂起的线程 注意: 若回复的线程的优先级最高将会进行一次上下文切换。
rt_err_t rt_thread_control(rt_thread_t thread, rt_uint8_t cmd, void* arg);
RT-Thread还提供了一些其他功能函数,这里就不再详细介绍了,大家可以参考官方提供的编程指南进行学习。
rt_thread_t rt_thread_self(void); //获得当前线程
rt_err_t rt_thread_yield(void); //使线程让出处理器资源
rt_err_t rt_thread_sleep(rt_tick_t tick); //使线程睡眠 N 个时间片
rt_err_t rt_thread_delay(rt_tick_t tick); //使线程睡眠 N 个时间片
rt_err_t rt_thread_mdelay(rt_int32_t ms); //使线程睡眠 N 毫秒
这里先说一下我们要实现的功能吧:
1.使用静态方式初始化一个线程:线程1,用于控制板子上的LED灯每隔500ms状态取反一次,并通过串口输出线程1 执行次数;
2.使用动态方式创建一个线程:线程2,用于控制串口1每隔500ms发送一次“Hello RT-Thread!”和线程2执行次数;
3.线程1每执行完将自身挂起,并在线程2中解挂。
4.线程2执行10次之后删除线程1。
程序代码如下:
#include
#include "LED.h"
//LED0_thread
#define LED0_priority 4
#define LED0_timeslices 5
static struct rt_thread led0_thread;//线程控制块
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t rt_led0_thread_stack[1024];//线程栈
//USART_thread
#define USART_priority 5
#define USART_timeslices 5
#define USART_SIZE 1024
static rt_thread_t USART_thread = RT_NULL;
static void USART_enter(void *parameter);
//线程LED0
static void led0_thread_entry(void* parameter);
int main(void)
{
// 创建静态线程
rt_thread_init(&led0_thread, //线程控制块
"led0", //线程名字,在shell里面可以看到
led0_thread_entry, //线程入口函数
RT_NULL, //线程入口函数参数
&rt_led0_thread_stack[0], //线程栈起始地址
sizeof(rt_led0_thread_stack), //线程栈大小
LED0_priority, //线程的优先级
LED0_timeslices); //线程时间片
rt_thread_startup(&led0_thread); //启动线程led0_thread,开启调度
//创建USART线程
USART_thread=rt_thread_create("USART_thread",
USART_enter,
RT_NULL,
USART_SIZE,
USART_priority,
USART_timeslices);
if(USART_thread != RT_NULL) rt_thread_startup(USART_thread);
}
//线程LED0
static void led0_thread_entry(void* parameter)
{
rt_uint32_t count = 0;
while (1)
{
LED0=~LED0;
rt_thread_mdelay(500);
rt_kprintf("thread led0 count: %d\r\n", count ++); /* 打印线程计数值输出 */
rt_kprintf("LED_thread 线程被挂起!\r\n");
rt_thread_suspend(&led0_thread);
rt_schedule();
}
}
void USART_enter(void *parameter)
{
rt_uint32_t count_U = 0;
while (1)
{
rt_thread_mdelay(500);
rt_kprintf("thread usart count: %d\r\n", count_U ++); /* 打印线程计数值输出 */
rt_thread_resume(&led0_thread);
rt_kprintf("LED_thread 线程被解挂!\r\n");
if(count_U==10)
{
rt_thread_detach(&led0_thread);
rt_kprintf("LED_thread 线程被删除!\r\n");
}
}
}
之前学过一些ucos,本打算在线程2中执行对线程1的挂起操作的,但后来发现调用rt_thread_mdelay(); 后这个线程就已经处于了挂起态,不能对他再次进行挂起操作了,所以改成了在线程1中进行自挂起。现在还不知道怎么实现相同的功能?有没有大神指点一下啊。