首页 > 其他分享 >STM32:RTthread_线程

STM32:RTthread_线程

时间:2023-04-28 18:25:58浏览次数:46  
标签:rt thread frame list RTthread STM32 线程 stack

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__ */

 

标签:rt,thread,frame,list,RTthread,STM32,线程,stack
From: https://www.cnblogs.com/caesura-k/p/17353515.html

相关文章

  • Java多线程之---用 CountDownLatch 说明 AQS 的实现原理
    本文基于jdk1.8。CountDownLatch的使用前面的文章中说到了volatile以及用volatile来实现自旋锁,例如java.util.concurrent.atomic包下的工具类。但是volatile的使用场景毕竟有限,很多的情况下并不是适用,这个时候就需要synchronized或者各种锁实现了。今天就来说一下几......
  • Java 中的几种线程池,你之前用对了吗
    好久不发文章了,难道是因为忙,其实是因为懒。这是一篇关于线程池使用和基本原理的科普水文,如果你经常用到线程池,不知道你的用法标准不标准,是否有隐藏的OOM风险。不经常用线程池的同学,还有对几种线程的使用不甚了解的同学可以读一下此文。为什么要使用线程池虽然大家应该都已经很清......
  • STM32 + RTThread + UGUI
    一、概述开发板:STM32F103C8T6显示器:ST7735SRT-Thread:5.0.0玩过GUI的小伙伴都知道,界面的显示是一个个像素点组合起来的,那么直接构建出来炫酷的GUI还是相对比较困难的,所以我们一般都会使用一些GUI库来实现,比如LVGL、QT、UGUI等,这样对于驱动开发的人员来说就相对比较简......
  • C# 多线程
    首先要关注电脑配置是否是多核多CPU的。因为一个CPU在同一时刻只能运行一个线程,但是多个CPU在同一时刻就可以运行多个线程。 多线程的优点:1、可以同时完成多个任务;2、可以使程序的响应速度更快;3、可以让占用大量处理时间的任务或当前没有进行处理的任务定期将处理时间让给......
  • 多线程读写文件
    参考:实践1-2:多线程读写文件-l.w.x-博客园(cnblogs.com)得到的结论是,可以多线程读写,但是会有多种情况:多线程同时读同一个文件,在这种情况下并不会造成冲突多线程同时写同一个文件,会造成写数据丢失多线程同时对同一个文件进行写和读,会造成脏读解决办法是加锁,同时......
  • C#使用委托在Socket Udp端口侦听线程内更新主窗口控件显示
    c#开启线程侦听SocketUDP端口,端口接收到网络读卡器的读卡数据后刷新UI界面显示接收数据,解析数据包信息并向读卡器发送显示文字、驱动读卡器播报语音、蜂鸣响声提示、开启继电器开关等操作。  .net提示通过设置:CheckForIllegalCrossThreadCalls=false,可以在子线程内强制更新......
  • 《Effective C#》系列之(六)——提高多线程的性能
    一、综述《EffectiveC#》中提高多线程性能的方法主要有以下几点:避免锁竞争:锁的使用会导致线程阻塞,从而影响程序的性能。为了避免锁竞争,可以采用无锁编程技术,如CAS(Compare-And-Swap),Interlocked等。使用ThreadPool:ThreadPool是.NETFramework提供的一个线程池,它可以......
  • 线程安全问题
    线程安全问题一、造成线程安全问题的原因:GIL全局解释器锁每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说python的多线程并不是真正意义上的同时执行。二、多线程共享全局变量Python多线程是通过threading模......
  • 进程与线程
    进程和线程都是操作系统中的概念,它们是操作系统调度和管理计算机资源的基本单位。进程(Process)是指正在运行中的程序,它是系统资源分配和调度的基本单位。一个进程可以包含多个线程,每个线程都运行在同一个进程的上下文中,共享该进程的内存空间、文件等系统资源。操作系统通过进程间......
  • c#线程安全
    引用:https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/locklock语句获取给定对象的互斥lock,执行语句块,然后释放lock。持有lock时,持有lock的线程可以再次获取并释放lock。阻止任何其他线程获取lock并等待释放lock。lock语句可确保......