RT-Thread(RTOS)之初试线程

Posted 旭日初扬

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RT-Thread(RTOS)之初试线程相关的知识,希望对你有一定的参考价值。

目录

蓝本蓝本参考链接

一、创建工程

二、创建线程

2.1、定义线程栈、线程函数、线程控制

用typedef给RT-thread中涉及到的数据类型取名

线程栈、线程函数、线程控制的声明在main.h中

线程栈、线程函数、线程控制的实现(main.c)

实验中变量的字节对齐量

条件编译与头文件引入

实现线程创建函数

2.2、就绪列表实现

2.3、调度器实现


蓝本蓝本参考链接

一、创建工程

RT-Thread文件下创建如下目录

二、创建线程

实验目标:实现两个变量按照一定的频率轮流的翻转。参照LED灯的闪烁。

2.1、定义线程栈、线程函数、线程控制

  • 线程栈其实就是一个预先定义好的全局数据。
  • 在 RT-Thread 中,涉及到数据类型之处,RT- Thread 都会将标准的 C 数据类型用 typedef 重新取一个类型名,以“rt”前缀开头。
  • rt开头+文件名+函数名    表示外部函数,可以由用户调用。
     
  • _rt开头+文件名+函数名    表示内部函数,只可以在RT_thread内部使用。

用typedef给RT-thread中涉及到的数据类型取名

//#ifndef __RT_DEF_H__
//#define __RT_DEF_H__

/*
*************************************************************************
*                               数据类型
*************************************************************************
*/
/* RT-Thread basic data type definitions */
//  signed 有符号的数据类型   unsigned无符号的数据类型
typedef signed   char                   rt_int8_t;      /**<  8bit integer type */
typedef signed   short                  rt_int16_t;     /**< 16bit integer type */
typedef signed   long                   rt_int32_t;     /**< 32bit integer type */
typedef unsigned char                   rt_uint8_t;     /**<  8bit unsigned integer type */
typedef unsigned short                  rt_uint16_t;    /**< 16bit unsigned integer type */
typedef unsigned long                   rt_uint32_t;    /**< 32bit unsigned integer type */
typedef int                             rt_bool_t;      /**< boolean type */

/* 32bit CPU */
typedef long                            rt_base_t;      /**< Nbit CPU related date type */
typedef unsigned long                   rt_ubase_t;     /**< Nbit unsigned CPU related data type */

typedef rt_base_t                       rt_err_t;       /**< Type for error number */
typedef rt_uint32_t                     rt_time_t;      /**< Type for time stamp */
typedef rt_uint32_t                     rt_tick_t;      /**< Type for tick count */
typedef rt_base_t                       rt_flag_t;      /**< Type for flags */
typedef rt_ubase_t                      rt_size_t;      /**< Type for size number */
typedef rt_ubase_t                      rt_dev_t;       /**< Type for device */
typedef rt_base_t                       rt_off_t;       /**< Type for offset */

/* boolean type definitions */
#define RT_TRUE                         1               /**< boolean true  */
#define RT_FALSE                        0               /**< boolean fails */

#ifdef __CC_ARM
	#define rt_inline                   static __inline
	#define ALIGN(n)                    __attribute__((aligned(n)))

#elif defined (__IAR_SYSTEMS_ICC__)
    #define rt_inline                   static inline
	#define ALIGN(n)                    PRAGMA(data_alignment=n)
		
#elif defined (__GNUC__)
    #define rt_inline                   static __inline
	#define ALIGN(n)                    __attribute__((aligned(n)))
#else
    #error not supported tool chain			
#endif

		
#define RT_ALIGN(size, align)           (((size) + (align) - 1) & ~((align) - 1))
#define RT_ALIGN_DOWN(size, align)      ((size) & ~((align) - 1))

		
#define RT_NULL                         (0)

/*
*************************************************************************
*                               错误码定义
*************************************************************************
*/
/* RT-Thread 错误码重定义 */
#define RT_EOK                          0               /**< There is no error */
#define RT_ERROR                        1               /**< A generic error happens */
#define RT_ETIMEOUT                     2               /**< Timed out */
#define RT_EFULL                        3               /**< The resource is full */
#define RT_EEMPTY                       4               /**< The resource is empty */
#define RT_ENOMEM                       5               /**< No memory */
#define RT_ENOSYS                       6               /**< No system */
#define RT_EBUSY                        7               /**< Busy */
#define RT_EIO                          8               /**< IO error */
#define RT_EINTR                        9               /**< Interrupted system call */
#define RT_EINVAL                       10              /**< Invalid argument */

/*
*************************************************************************
*                               双向链表结构体
*************************************************************************
*/
struct rt_list_node

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


/*
*************************************************************************
*                               线程结构体
*************************************************************************
*/

struct rt_thread

	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;


//#endif  /* __RT_DEF_H__ */

线程栈、线程函数、线程控制的声明在main.h中

//#include "public.h"

//  定义线程栈

// 设置变量需要多少个字节对齐,对在它下面的变量起作用
ALIGN(RT_ALIGN_SIZE)
ext rt_uint8_t rt_flag1_thread_stack[512];   // 线程栈实质数组
ext rt_uint8_t rt_flag2_thread_stack[512];


//线程定义的线程控制块
//多线程系统中线程的执行是由系统调度
ext struct rt_thread rt_flag1_thread;  //  实质是一个线程结构体
ext struct rt_thread rt_flag2_thread;
 

//  全局变量
ext rt_uint8_t flag1;  // 类似LED闪烁实验中的于I/O口,高低电平的切换实现LED闪烁
ext rt_uint8_t flag2;

ext rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];


/*方法部分*/
// 延时函数
extern void delay(rt_uint32_t count);

//  线程函数1
extern void  flag1_thread_entry(void *p_arg);

//  线程函数2
extern void  flag2_thread_entry(void *p_arg);

线程栈、线程函数、线程控制的实现(main.c)

#define BOOT
#include "public.h"
#include "ARMCM3.h"

/*************************************************

实现两个变量按照一定的频率轮流的翻转
*************************************************/
//#include "public.h"


 /***********************************************
  * @brief  main函数
  * @param  无
  * @retval 无
  *
  * @attention
*************************************************/
 int main(void)
 
 
   // 硬件初始化  
	 // 将硬件相关的初始化放在这里,如果是软件仿真则没有相关初始化代码 
	 
	 
	 //  调度器初始化
	 rt_system_scheduler_init();
	 
	 //  初始化线程 1、线程控制块  2、线程起始地址 3、线程形参 4、线程栈起始地址 5、线程栈大小,单位为字节
	 rt_thread_init( &rt_flag1_thread,flag1_thread_entry,RT_NULL,&rt_flag1_thread_stack[0],sizeof(rt_flag1_thread_stack)); 

	 //  将线程插入到就绪列表
	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();
 
 
 //  延时函数
void delay(rt_uint32_t count)

   for(;count!=0;count--);



 //线程1 线程是一个独立的、无限循环且 不能返回的函数
void flag1_thread_entry(void *p_arg)

     for(;;)
		 flag1=1;
		 delay(100);
		 flag1=0;
		 delay(100);
			 
		 //  手动切换线程
			 rt_schedule();
		 


//线程2
void flag2_thread_entry(void *p_arg)

     for(;;)
		 flag2=1;
		 delay(100);
		 flag2=0;
		 delay(100);
			 
		 //  手动切换线程
			rt_schedule();
		 

实验中变量的字节对齐量

#ifndef __RTTHREAD_CFG_H__ 
#define __RTTHREAD_CFG_H__ 

#define RT_THREAD_PRIORITY_MAX 32 //  最大优先级 stm32f103ze总线宽度32bit,它的一个字也32bit
#define RT_ALIGN_SIZE    4        //  多少个字节对齐 

#endif

条件编译与头文件引入

//#ifdef _PUBLIC_H
//#define _PUBLIC_H

#ifdef BOOT         //   如果BOOT被定义
#define EXT         //   则定义一个宏EXT
#define ext         //   则定义一个宏ext
#else               //   如果BOOT没有定义
#define EXT extern  //   则定义一个宏
#define ext extern  //   则定义一个宏
#endif




/*******************************
              头文件
********************************/

#include "rtdef.h"
#include "rtconfig.h"
#include "rtthread.h"
#include "rthw.h"
#include "rtservice.h"
#include "main.h"


//#endif

实现线程创建函数

函数功能:关联线程栈,线程函数实体,线程控制块

#include "public.h"
/**************************************************************
创建线程函数
功能:关联线程栈,线程函数实体,线程控制块
所有跟线程相关的函数都在此文件中实现
**************************************************************/
//  1、线程结构体(控制块)指针  2、线程入口   3、线程参数 4、线程栈的起始地址  5、线程栈的大小,单位为字节
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));
	
	//  将线程入口保存到线程控制块的 entry 成员中。
	 thread ->entry = (void*)entry;
	
	//  将线程入口形参保存到线程控制块的 parameter 成员中。
	 thread ->parameter = parameter;
	
	 // 将线程栈起始地址保存到线程控制块的 stack_start 成员中。
	 thread ->stack_addr = stack_start;
	 
	 //  将线程栈起大小保存到线程控制块的 stack_size 成员
	 thread  ->stack_size = stack_size;
	
	 //  初始化线程栈,并返回线程栈顶指针。rt_hw_stack_init()
	thread->sp = (void *)rt_hw_stack_init( thread->entry, 
		                                   thread->parameter,
							               (void *)((char *)thread->stack_addr + thread->stack_size - 4) );
	
	//  初始化线程成功返回错误码 RT_EOK
	return RT_EOK;
 

 

2.2、就绪列表实现

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

一个被宏定义容量的数组,数组下标值越小,线程优先级越高。

//#ifdef _RTTHREAD_H
//#define _RTTHREAD_H


/*
*************************************************************************
*                                 数据类型
*************************************************************************
*/
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;
;
/* 用于存储上一个线程的栈的sp的指针 */
ext rt_uint32_t rt_interrupt_from_thread;

/* 用于存储下一个将要运行的线程的栈的sp的指针 */
ext rt_uint32_t rt_interrupt_to_thread;

/* PendSV中断服务函数执行标志 */
ext rt_uint32_t rt_thread_switch_interrupt_flag;


/* 线程控制块指针,用于指向当前线程 */
ext struct rt_thread *rt_current_thread;

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

/* 线程休眠列表 */
ext rt_list_t rt_thread_defunct;
/*
*************************************************************************
*                                 线程接口
*************************************************************************
*/

//  线程栈初始化
extern rt_err_t rt_thread_init(struct rt_thread *thread,void(*entry)(void *parameter),void *parameter,void *stack_start,rt_uint32_t stack_size);

extern rt_err_t rt_thread_resume(rt_thread_t thread); 

extern rt_err_t rt_thread_startup(rt_thread_t thread); 


/*
*************************************************************************
*                                 调度接口
*************************************************************************
*/
extern void rt_system_scheduler_init(void);
extern void rt_schedule_insert_thread(struct rt_thread *thread);
extern void rt_system_scheduler_start(void);
extern void rt_schedule(void);



//#endif

2.3、调度器实现

#include "public.h"

//  线程就绪列表
//  数组 的大小由决定最大线程优先级的宏 RT_THREAD_PRIORITY_MAX 决定
//rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];

//struct rt_thread *rt_current_thread;

// 初始化系统调度器
/*线程创建好之后,需要把线程添加到就绪列表里面,表示线程已经就绪, 系统随时可以调度*/
void rt_system_scheduler_init(void)

	//  局部变量,用 C 语言关键词 register 修饰,防止被编译器优化。
   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;
	
	//  手动指定第一个运行的线程  1、线程就绪列表   2、节点所在的结构体的类型  ,3、该节点在该结构体中的成员名称
	to_thread = rt_list_entry(rt_thread_priority_table[0].next,struct rt_thread,tlist); // 0优先级别最高
	
	// 线程控制块指针,用于指向当 前正在运行的线程的线程控制块
	rt_current_thread = to_thread;
	
	/*
	切换到第一个线程,该函数在 context_rvds.S 中实现,在 rthw.h 声明
	用于实现第一次任务切换。当一个汇编函数在 C 文件中调用的时候
	如果有形参,则执行的时候会将形参传人到 CPU 寄存器 r0。
	如果有两个形参,第二个则传入到 r1。
	*/
	rt_hw_context_switch_to((rt_uint32_t)&to_thread->sp);


/* 系统调度 */
/*调度器在启动的时候  会从就绪列表中取出优先级最高的线程的线程控制块,然后切换到该线程*/
void rt_schedule(void)

	struct rt_thread *to_thread;
	struct rt_thread *from_thread;
	
	
	
	/* 两个线程轮流切换  1、就绪列表(节点地址)  2、节点类型   3、节点名称*/
	//  rt_current_thread  指向当前线程flag1   如果当前指针指向flag1,下一个则指向flag2
	if( rt_current_thread == rt_list_entry( rt_thread_priority_table[0].next,
													               struct rt_thread,
													               tlist) )
	
		//   flag2
		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
	
		//  flag1
		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);	
	


本章源码实例

开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于RT-Thread(RTOS)之初试线程的主要内容,如果未能解决你的问题,请参考以下文章

RT-Thread RTOS的RT-Thread / uCOS / FreeRTOS 简单比较

RT-Thread新版入门系列教程(名师带路,不走弯路)

RT-Thread快速入门-事件集

The Applications of RT-Thread RTOS

The Applications of RT-Thread RTOS

RT-Thread RTOS的RT-Thread 开发者自述