RT-Thread(RTOS)之初试线程
Posted 旭日初扬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RT-Thread(RTOS)之初试线程相关的知识,希望对你有一定的参考价值。
目录
蓝本蓝本参考链接
一、创建工程
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);
以上是关于RT-Thread(RTOS)之初试线程的主要内容,如果未能解决你的问题,请参考以下文章
RT-Thread RTOS的RT-Thread / uCOS / FreeRTOS 简单比较
The Applications of RT-Thread RTOS