STM32:RTthread_线程

发布时间 2023-04-28 18:13:55作者: caesura_k

1 微处理器系统

  

  随着产品功能的增多,裸机系统不能够满足产品需求,引入RTOS实时操作系统的多线程管理,可以增加程序的稳定性逻辑性,便于管理;

2 线程

  通常默认一个能独立实现功能的函数,称之为线程;多线程管理的意思就是这个程序可以实现多个功能管理;

  2.1 线程栈

    每个线程都有一个独立的线程栈空间,存储该线程的寄存器状态、数据等;通常是一段内存空间或全局数组;

    裸机系统的对象,全部存放在由启动文件初始化的栈空间内;多线程系统的每个线程互不干扰,所以每个线程栈也互不干扰;

rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];

  2.2 线程控制块struct

    每个线程都有一个唯一的线程控制块,是存储该线程的各种参数、地址的结构体;

/*
*************************************************************************
*                               线程结构体
*************************************************************************
*/
struct rt_thread
{
	void        *sp;	          /* 线程栈指针                cae每个线程都有一个自己的线程控制块struct */
	void        *entry;	          /* 线程入口地址              cae通俗来讲就是函数名 */
	void        *parameter;	      /* 线程形参                  cae通俗来讲就是函数参数*/	
	void        *stack_addr;      /* 线程起始地址              cae2.1线程栈起始地址*/
	rt_uint32_t stack_size;       /* 线程栈大小,单位为字节    cae线程栈stack_addr的大小*/
	
	rt_list_t   tlist;            /* 线程链表节点 */
};
typedef struct rt_thread *rt_thread_t;
/************************************************************************/
struct rt_list_node
{
    struct rt_list_node *next;              /* 指向后一个节点 */
    struct rt_list_node *prev;              /* 指向前一个节点 */
};
typedef struct rt_list_node rt_list_t;

  2.3 线程初始化

    

//thread.c
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));//rtservice.h
	
	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;
}
//cpuport.c
/* 线程栈初始化 */
rt_uint8_t *rt_hw_stack_init(void       *tentry,
                             void       *parameter,
                             rt_uint8_t *stack_addr)
{
	struct stack_frame *stack_frame;
	rt_uint8_t         *stk;
	unsigned long       i;
	
	/* 获取栈顶指针
	 rt_hw_stack_init 在调用的时候,传给stack_addr的是(栈顶指针)*/
	stk  = stack_addr + sizeof(rt_uint32_t);
	
	/* 让stk指针向下8字节对齐 */
	stk  = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stk, 8);
	
	/* stk指针继续向下移动sizeof(struct stack_frame)个偏移 */
	stk -= sizeof(struct stack_frame);
	
	/* 将stk指针强制转化为stack_frame类型后存到stack_frame */
	stack_frame = (struct stack_frame *)stk;
	
	/* 以stack_frame为起始地址,将栈空间里面的sizeof(struct stack_frame)
	个内存初始化为0xdeadbeef */
	for (i = 0; i < sizeof(struct stack_frame) / sizeof(rt_uint32_t); i ++)
	{
			((rt_uint32_t *)stack_frame)[i] = 0xdeadbeef;
	}
	
	/* 初始化异常发生时自动保存的寄存器 */
	stack_frame->exception_stack_frame.r0  = (unsigned long)parameter; /* r0 : argument */
	stack_frame->exception_stack_frame.r1  = 0;                        /* r1 */
	stack_frame->exception_stack_frame.r2  = 0;                        /* r2 */
	stack_frame->exception_stack_frame.r3  = 0;                        /* r3 */
	stack_frame->exception_stack_frame.r12 = 0;                        /* r12 */
	stack_frame->exception_stack_frame.lr  = 0;                        /* lr */
	stack_frame->exception_stack_frame.pc  = (unsigned long)tentry;    /* entry point, pc */
	stack_frame->exception_stack_frame.psr = 0x01000000L;              /* PSR */
	
	/* 返回线程栈指针 */
	return stk;
}
struct stack_frame
 struct exception_stack_frame
{
    /* 异常发生时自动保存的寄存器 */
	rt_uint32_t r0;
    rt_uint32_t r1;
    rt_uint32_t r2;
    rt_uint32_t r3;
    rt_uint32_t r12;
    rt_uint32_t lr;
    rt_uint32_t pc;
    rt_uint32_t psr;
};

struct stack_frame
{
    /* r4 ~ r11 register 
	  异常发生时需手动保存的寄存器 */
    rt_uint32_t r4;
    rt_uint32_t r5;
    rt_uint32_t r6;
    rt_uint32_t r7;
    rt_uint32_t r8;
    rt_uint32_t r9;
    rt_uint32_t r10;
    rt_uint32_t r11;

    struct exception_stack_frame exception_stack_frame;
};

  2.4 初始化两个线程的例程

#include <rtthread.h>
#include "ARMCM3.h"
//cae这个数组应该是要用来存储线程节点的;
extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
/* 定义线程控制块 */
struct rt_thread rt_flag1_thread;
struct rt_thread rt_flag2_thread;

ALIGN(RT_ALIGN_SIZE)
/* 定义线程栈 */
rt_uint8_t rt_flag1_thread_stack[512];
rt_uint8_t rt_flag2_thread_stack[512];

/* 线程声明 */
//cae线程函数执行完之后会在函数结尾调用rt_schedule();切换线程;
void flag1_thread_entry(void *p_arg);
void flag2_thread_entry(void *p_arg);
int main(void)
{	
	/* 硬件初始化 */
	/* 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 */	
	
	/* cae将线程管理的链表变量的数组初始化掉32个全部node,然后为第一个node赋休眠地址rt_thread_defunct      */
	rt_system_scheduler_init();
	
	
	/* cae初始化当前线程的rt_thread结构体,初始化线程栈,然后把线程栈顶地址放入rt_thread->sp */
	rt_thread_init( &rt_flag1_thread,                 /* 线程控制块 */
	                flag1_thread_entry,               /* 线程入口地址 */
	                RT_NULL,                          /* 线程形参 */
	                &rt_flag1_thread_stack[0],        /* 线程栈起始地址 */
	                sizeof(rt_flag1_thread_stack) );  /* 线程栈大小,单位为字节 */
	
	/* cae我怎么觉得这里应该是rt_list_insert_after(&table[0], &tlist)?? 
	 * rt_thread_priority_table[32]编译时就从低到高分配了连续空间,你插入table[0]前面那不是都插出数组了?? 
	 * 大约是因为这个代码只使用了该链表的l->pre,不管了,先放着;顺其自然静待开窍把;*/
	rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) );
	
	/* 初始化线程 */
	rt_thread_init( &rt_flag2_thread,                 /* 线程控制块 */
	                flag2_thread_entry,               /* 线程入口地址 */
	                RT_NULL,                          /* 线程形参 */
	                &rt_flag2_thread_stack[0],        /* 线程栈起始地址 */
	                sizeof(rt_flag2_thread_stack) );  /* 线程栈大小,单位为字节 */

	rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) );
	
	/* 启动系统调度器 */
	rt_system_scheduler_start(); 
}

       2.4.1 scheduler 线程切换

/***
    搭配rt_list_t  rt_thread_priority_table[32]使用,进行线程的切换;
    实际切换还是通过结尾那几个汇编函数切换的,先放着;
***/
	/* 产生上下文切换 */
	rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);
	/* 切换到第一个线程,该函数在context_rvds.S中实现,在rthw.h声明,
       用于实现第一次任务切换。当一个汇编函数在C文件中调用的时候,
       如果有形参,则执行的时候会将形参传人到CPU寄存器r0。*/
	rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
scheduler.c
#include <rtthread.h>
#include <rthw.h>


/*
*************************************************************************
*                                 全局变量
*************************************************************************
*/
/* 线程控制块指针,用于指向当前线程 */
struct rt_thread *rt_current_thread;

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

/* 线程休眠列表 */
rt_list_t rt_thread_defunct;


/*
*************************************************************************
*                               函数实现
*************************************************************************
*/

/* 初始化系统调度器 */
void rt_system_scheduler_init(void)
{	
	register rt_base_t offset;	

	
	/* 线程就绪列表初始化 */
	for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++)
	{
			rt_list_init(&rt_thread_priority_table[offset]);
	}
	
	/* 初始化当前线程控制块指针 */
	rt_current_thread = RT_NULL;
	
	/* 初始化线程休眠列表,当线程创建好没有启动之前会被放入到这个列表 */
	rt_list_init(&rt_thread_defunct);
}

/* 启动系统调度器 */
void rt_system_scheduler_start(void)
{
	register struct rt_thread *to_thread;
	

	/* 手动指定第一个运行的线程 */
	to_thread = rt_list_entry(rt_thread_priority_table[0].next,
							  struct rt_thread,
							  tlist);
	rt_current_thread = to_thread;
														
	/* 切换到第一个线程,该函数在context_rvds.S中实现,在rthw.h声明,
       用于实现第一次任务切换。当一个汇编函数在C文件中调用的时候,
       如果有形参,则执行的时候会将形参传人到CPU寄存器r0。*/
	rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);
}

/* 系统调度 */
//cae flag1_thread_entry(),flag2_thread_entry()程序内部执行完之后会调用rt_schedule()来切换到下一个线程;
void rt_schedule(void)
{
	struct rt_thread *to_thread;
	struct rt_thread *from_thread;
	
	
	
	/* 两个线程轮流切换 */
	if( rt_current_thread == rt_list_entry( rt_thread_priority_table[0].next,
									struct rt_thread,
									tlist) )
	{
		from_thread = rt_current_thread;
		to_thread = rt_list_entry( rt_thread_priority_table[1].next,
									struct rt_thread,
									tlist);
	  rt_current_thread = to_thread;
	}
	else
	{
		from_thread = rt_current_thread;
		to_thread = rt_list_entry( rt_thread_priority_table[0].next,
									struct rt_thread,
									tlist);
	  rt_current_thread = to_thread;																		 
	}
	
	/* 产生上下文切换 */
	rt_hw_context_switch((rt_uint32_t)&from_thread->sp,(rt_uint32_t)&to_thread->sp);	
	
}

      2.4.2 node 节点删改

/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
			
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)
/***上面这个结构体没看懂,下面的链表初始化就是一个队列初始化;***/
rtservice.h
 #ifndef __RT_SERVICE_H__
#define __RT_SERVICE_H__

/* 已知一个结构体里面的成员的地址,反推出该结构体的首地址 */
#define rt_container_of(ptr, type, member) \
    ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
			
#define rt_list_entry(node, type, member) \
    rt_container_of(node, type, member)
		
/*
*************************************************************************
*                           双向链表操作相关函数
*  cae链表存储的数据是node地址;node节点里又是前后节点的地址;
*************************************************************************
*/
		
/* 初始化链表节点 */
rt_inline void rt_list_init(rt_list_t *l)
{
    l->next = l->prev = l;
}

/* 在双向链表头部插入一个节点 cae在l节点之后插入n节点 */
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;
}

/* 在双向链表尾部插入一个节点 cae在l节点之前插入n节点 */
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;
}

/* 从双向链表删除一个节点 */
rt_inline void rt_list_remove(rt_list_t *n)
{
    n->next->prev = n->prev;
    n->prev->next = n->next;

    n->next = n->prev = n;
}



#endif /* __RT_SERVICE_H__ */