RT-Thread工程建立与内核介绍

工程建立参考如下:

(6条消息) RT-Thread Studio与CubeMX联合编程(超级详细)_zhuzhu、的博客-CSDN博客

一、内核:内核库和实时内核实现

内核库:使内核独立运行的实现子集。

实时内核实现包括:对象管理、线程管理、调度器、线程通信、时钟管理、内存等

内核对象分为静态对象(RAM)和动态对象(内存堆管理器,运行时申请RAM)。(线程控制块和栈空间)

对象容器:线程、信号量、互斥量、事件、邮箱、消息队列、定时器、内存池、设备驱动等。

RT_Thread启动流程(一般执行顺序):系统先从启动文件开始运行,然后进入 RT-Thread 的启动函数 rtthread_startup() ,最后进入用户入口函数 main()。

二、线程(控制块、栈、入口函数等)

控制块(struct rt_thread):线程名称、状态、优先级等

线程状态(5种):初始、就绪、运行、挂起、关闭。

线程优先级(0--255):0最高

时间片:系统节拍

系统线程:1.空闲线程(优先级最低、就绪状态)2.主线程(main)

线程控制块(管理方式):1.创建/初始化 2.启动 3.运行 4.删除/脱离 

示例代码:

#include <rtthread.h>

#define THREAD_PRIORITY 25
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5

/* 使用静态线程时,线程的栈需要设置字节对齐 */
ALIGN(RT_ALIGN_SIZE)
static rt_uint8_t thread_stack[THREAD_STACK_SIZE];
static struct rt_thread tid1;

/* 线程 1 的入口函数 */
static void thread1_entry(void *parameter)
{
rt_uint32_t count = 0;

while (1)
{
rt_kprintf("thread1 count: %d\n", count ++);
rt_thread_mdelay(500);
}
}

/* 线程示例 */
int thread_sample(void)
{
/* 静态创建线程 */

/* 初始化线程 1,名称是 thread1,入口是 thread1_entry*/
rt_thread_init(&tid1,
"thread1",
thread1_entry,
RT_NULL,
thread_stack,
THREAD_STACK_SIZE,
THREAD_PRIORITY,
THREAD_TIMESLICE);
/* 启动线程 */
rt_thread_startup(&tid1);

return 0;
}

三、时钟管理

时钟节拍(特定的周期性中断)长度:1/RT_TICK_PER_SECOND

定时管理:硬件定时(HARD_TIMER)、软件定时(SOFT_TIMER)

定时器控制块:创建/初始化(rt_timer_create/init)、启动(rt_timer_start)、停止/控制(rt_timer_stop/control)、删除/脱离(rt_timer_delete/detach)

控制cmd指令:

#define RT_TIMER_CTRL_SET_TIME 0x0 /* 设置定时器超时时间 */

#define RT_TIMER_CTRL_GET_TIME 0x1 /* 获得定时器超时时间 */

#define RT_TIMER_CTRL_SET_ONESHOT 0x2 /* 设置定时器为单次定时器 */

#define RT_TIMER_CTRL_SET_PERIODIC 0x3 /* 设置定时器为周期型定时器 */

 

创建定时器例子:

#include <rtthread.h>

/* 定时器的控制块 */
static rt_timer_t timer1;
static rt_timer_t timer2;
static int cnt = 0;

/* 定时器 1 超时函数 */
static void timeout1(void *parameter)
{
    rt_kprintf("periodic timer is timeout %d\n", cnt);

    /* 运行第 10 次,停止周期定时器 */
    if (cnt++>= 9)
    {
        rt_timer_stop(timer1);
        rt_kprintf("periodic timer was stopped! \n");
    }
}

/* 定时器 2 超时函数 */
static void timeout2(void *parameter)
{
    rt_kprintf("one shot timer is timeout\n");
}

int timer_sample(void)
{
    /* 创建定时器 1  周期定时器 */
    timer1 = rt_timer_create("timer1", timeout1,
                             RT_NULL, 10,
                             RT_TIMER_FLAG_PERIODIC);

    /* 启动定时器 1 */
    if (timer1 != RT_NULL) rt_timer_start(timer1);

    /* 创建定时器 2 单次定时器 */
    timer2 = rt_timer_create("timer2", timeout2,
                             RT_NULL,  30,
                             RT_TIMER_FLAG_ONE_SHOT);

    /* 启动定时器 2 */
    if (timer2 != RT_NULL) rt_timer_start(timer2);
    return 0;
}

/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(timer_sample, timer sample);
 结果:
 \ | /
- RT -     Thread Operating System
 / | \     3.1.0 build Aug 24 2018
 2006 - 2018 Copyright by rt-thread team
msh >timer_sample
msh >periodic timer is timeout 0
periodic timer is timeout 1
one shot timer is timeout
periodic timer is timeout 2
periodic timer is timeout 3
periodic timer is timeout 4
periodic timer is timeout 5
periodic timer is timeout 6
periodic timer is timeout 7
periodic timer is timeout 8
periodic timer is timeout 9
periodic timer was stopped!
 
初始化定时器例子:
#include <rtthread.h>

/* 定时器的控制块 */
static struct rt_timer timer1;
static struct rt_timer timer2;
static int cnt = 0;

/* 定时器 1 超时函数 */
static void timeout1(void* parameter)
{
    rt_kprintf("periodic timer is timeout\n");
    /* 运行 10 次 */
    if (cnt++>= 9)
    {
        rt_timer_stop(&timer1);
    }
}

/* 定时器 2 超时函数 */
static void timeout2(void* parameter)
{
    rt_kprintf("one shot timer is timeout\n");
}

int timer_static_sample(void)
{
    /* 初始化定时器 */
    rt_timer_init(&timer1, "timer1",  /* 定时器名字是 timer1 */
                    timeout1, /* 超时时回调的处理函数 */
                    RT_NULL, /* 超时函数的入口参数 */
                    10, /* 定时长度,以 OS Tick 为单位,即 10 个 OS Tick */
                    RT_TIMER_FLAG_PERIODIC); /* 周期性定时器 */
    rt_timer_init(&timer2, "timer2",   /* 定时器名字是 timer2 */
                    timeout2, /* 超时时回调的处理函数 */
                      RT_NULL, /* 超时函数的入口参数 */
                      30, /* 定时长度为 30 个 OS Tick */
                    RT_TIMER_FLAG_ONE_SHOT); /* 单次定时器 */

    /* 启动定时器 */
    rt_timer_start(&timer1);
    rt_timer_start(&timer2);
    return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(timer_static_sample, timer_static sample);
 
结果:
\ | /
- RT -     Thread Operating System
 / | \     3.1.0 build Aug 24 2018
 2006 - 2018 Copyright by rt-thread team
msh >timer_static_sample
msh >periodic timer is timeout
periodic timer is timeout
one shot timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout
periodic timer is timeout

高精度延时例子:

#include <board.h>
void rt_hw_us_delay(rt_uint32_t us)
{
    rt_uint32_t ticks;
    rt_uint32_t told, tnow, tcnt = 0;
    rt_uint32_t reload = SysTick->LOAD;

    /* 获得延时经过的 tick 数 */
    ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
    /* 获得当前时间 */
    told = SysTick->VAL;
    while (1)
    {
        /* 循环获得当前时间,直到达到指定的时间后退出循环 */
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}

四、线程间同步:
核心思想都是:在访问临界区的时候只允许一个 (或一类) 线程运行。

信号量(sem)控制块:创建/初始化、获取、释放、删除/隔离
互斥量(mutex两种):开锁与闭锁。
互斥量控制块:创建/初始化、获取、释放、删除/隔离
事件集(event):创建/初始化、发送、接收、删除/隔离

五、线程间通信
1.邮箱
邮箱的工作机制:

 

 邮箱控制块(创建/初始化、发送、接收、删除/隔离):

struct rt_mailbox
{
    struct rt_ipc_object parent;

    rt_uint32_t* msg_pool;                /* 邮箱缓冲区的开始地址 */
    rt_uint16_t size;                     /* 邮箱缓冲区的大小     */

    rt_uint16_t entry;                    /* 邮箱中邮件的数目     */
    rt_uint16_t in_offset, out_offset;    /* 邮箱缓冲的进出指针   */
    rt_list_t suspend_sender_thread;      /* 发送线程的挂起等待队列 */
};
typedef struct rt_mailbox* rt_mailbox_t;

 

 

 
2.消息队列(异步的通讯方式)
工作机制(FIFO):

 

 控制块(创建/初始化、发送、接收、删除/隔离):

struct rt_messagequeue
{
    struct rt_ipc_object parent;

    void* msg_pool;                     /* 指向存放消息的缓冲区的指针 */

    rt_uint16_t msg_size;               /* 每个消息的长度 */
    rt_uint16_t max_msgs;               /* 最大能够容纳的消息数 */

    rt_uint16_t entry;                  /* 队列中已有的消息数 */

    void* msg_queue_head;               /* 消息链表头 */
    void* msg_queue_tail;               /* 消息链表尾 */
    void* msg_queue_free;               /* 空闲消息链表 */

    rt_list_t suspend_sender_thread;    /* 发送线程的挂起等待队列 */
};
typedef struct rt_messagequeue* rt_mq_t;
 

 

 


3.信号(软中断信号)
工作机制:

线程1的处理方法可以分为三类:

第一种是类似中断的处理程序,对于需要处理的信号,线程可以指定处理函数,由该函数来处理。

第二种方法是,忽略某个信号,对该信号不做任何处理,就像未发生过一样。

第三种方法是,对该信号的处理保留系统的默认值。

管理方式:

 

 

 六、内存管理

1.计算系统中:内部存储空间(RAM-随机存储器)、外部存储空间(ROM-只读存储器)

2.RT_Thread中:动态内存堆管理、静态内存池管理

3.内存堆管理:

内存堆管理根据具体内存设备划分为三种情况:

第一种是针对小内存块的分配管理(小内存管理算法);

第二种是针对大内存块的分配管理(slab 管理算法);

第三种是针对多内存堆的分配情况(memheap 管理算法)。

 

内存堆配置和初始化:

void rt_system_heap_init(void* begin_addr, void* end_addr); (使用内存堆时)
rt_err_t rt_memheap_init(struct rt_memheap  *memheap,
                        const char  *name,
                        void        *start_addr,
                        rt_uint32_t size)       (使用 memheap 堆内存时)

 管理方法:

 

 分配和释放内存块:

从内存堆上分配用户指定大小的内存块

void *rt_malloc(rt_size_t nbytes);
 使用动态分配内存示例:
int *pi;
pi = rt_malloc(100);
if(pi == NULL) 
{
    rt_printf("malloc failed\r\n");
}
 释放内存块的函数接口如下:
void rt_free (void *ptr);
重新分配:
void *rt_realloc(void *rmem, rt_size_t newsize);
 分配多内存块:
  void *rt_calloc(rt_size_t count, rt_size_t size);
 内存钩子函数:
void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size));
 钩子函数接口:
void hook(void *ptr, rt_size_t size);
 在释放内存时,用户可设置一个钩子函数,调用的函数接口如下:
void rt_free_sethook(void (*hook)(void *ptr));

4.内存池管理(用于分配大量大小相同的小内存块):

内存池控制块:

struct rt_mempool
{
    struct rt_object parent;

    void        *start_address;  /* 内存池数据区域开始地址 */
    rt_size_t     size;           /* 内存池数据区域大小 */

    rt_size_t     block_size;    /* 内存块大小  */
    rt_uint8_t    *block_list;   /* 内存块列表  */

    /* 内存池数据区域中能够容纳的最大内存块数  */
    rt_size_t     block_total_count;
    /* 内存池中空闲的内存块数  */
    rt_size_t     block_free_count;
    /* 因为内存块不可用而挂起的线程列表 */
    rt_list_t     suspend_thread;
    /* 因为内存块不可用而挂起的线程数 */
    rt_size_t     suspend_thread_count;
};
typedef struct rt_mempool* rt_mp_t; 

内存池的管理方式:
 

 

 

 

 

 七、中断管理

中断管理示意图:

 

 

 寄存器简介:

 

 

操作模式和特权级别:Cortex-M 引入了操作模式和特权级别的概念,分别为线程模式和处理模式,如果进入异常或中断处理则进入处理模式,其他情况则为线程模式。 

 

嵌套向量中断控制器(NVIC):支持中断嵌套功能,当一个中断触发并且系统进行响应时,处理器硬件会将当前运行位置的上下文寄存器自动压入中断栈中,这部分的寄存器包括 PSR、PC、LR、R12、R3-R0 寄存器。

 

 PendSV系统调用(可悬起的系统调用):是一种最低优先级的异常。

RT_Thread中断工作机制:

中断向量表

 

 

 中断处理过程:中断前导程序、用户中断服务程序(ISR)、中断后续程序三部分。

 

 

中断前导程序:调用 rt_interrupt_enter() 函数,作用是把全局变量 rt_interrupt_nest 加 1,用它来记录中断嵌套的层数

void rt_interrupt_enter(void)
{
    rt_base_t level;

    level = rt_hw_interrupt_disable();
    rt_interrupt_nest ++;
    rt_hw_interrupt_enable(level);
}
 用户中断服务程序(ISR)

 

 中断后续程序:通过调用 rt_interrupt_leave() 函数,将全局变量 rt_interrupt_nest 减 1

void rt_interrupt_leave(void)
{
    rt_base_t level;

    level = rt_hw_interrupt_disable();
    rt_interrupt_nest --;
    rt_hw_interrupt_enable(level);
}
 

 

中断栈:在 Cortex-M 处理器内核里有两个堆栈指针,一个是主堆栈指针(MSP),是默认的堆栈指针,在运行第一个线程之前和在中断和异常服务程序里使用;另一个是线程堆栈指针(PSP),在线程里使用。

RT_Thread中断管理接口

 

中断服务程序挂接:

rt_isr_handler_t rt_hw_interrupt_install(int vector,
                                        rt_isr_handler_t  handler,
                                        void *param,
                                        char *name);

中断源管理;
void rt_hw_interrupt_mask(int vector); //屏蔽中断源
 
void rt_hw_interrupt_umask(int vector);  //打开被屏蔽的中断源
 全局中断开关(中断锁)
rt_base_t rt_hw_interrupt_disable(void);
 恢复中断:
void rt_hw_interrupt_enable(rt_base_t level);
 中断通知:每当进入中断时,可以调用 rt_interrupt_enter() 函数,用于通知内核,当前已经进入了中断状态,并增加中断嵌套深度(执行 rt_interrupt_nest++);

每当退出中断时,可以调用 rt_interrupt_leave() 函数,用于通知内核,当前已经离开了中断状态,并减少中断嵌套深度(执行 rt_interrupt_nest --)。注意不要在应用程序中调用这两个接口函数。

void rt_interrupt_enter(void);
void rt_interrupt_leave(void);

 在上层应用中,在内核需要知道当前已经进入到中断状态或当前嵌套的中断深度时,可调用 rt_interrupt_get_nest() 接口,它会返回 rt_interrupt_nest

rt_uint8_t rt_interrupt_get_nest(void);
 
中断与轮询:
/* 轮询模式向串口写入数据 */
    while (size)
    {
        /* 判断 UART 外设中数据是否发送完毕 */
        while (!(uart->uart_device->SR & USART_FLAG_TXE));
        /* 当所有数据发送完毕后,才发送下一个数据 */
        uart->uart_device->DR = (*ptr & 0x1FF);

        ++ptr; --size;
    }
 

八、内核移植(CPU 架构移植和 BSP(Board support package,板级支持包)移植两部分)

 1.CPU架构移植

RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容。

实现全局中断开关:

/* 关闭全局中断 */
rt_base_t rt_hw_interrupt_disable(void);

;/*
; * rt_base_t rt_hw_interrupt_disable(void);
; */
rt_hw_interrupt_disable    PROC      ;PROC 伪指令定义函数
    EXPORT  rt_hw_interrupt_disable  ;EXPORT 输出定义的函数,类似于 C 语言 extern
    MRS     r0, PRIMASK              ; 读取 PRIMASK 寄存器的值到 r0 寄存器
    CPSID   I                        ; 关闭全局中断
    BX      LR                       ; 函数返回
    ENDP                             ;ENDP 函数结束
 
/* 打开全局中断 */ void rt_hw_interrupt_enable(rt_base_t level);
 
;/*
; * void rt_hw_interrupt_enable(rt_base_t level);
; */
rt_hw_interrupt_enable    PROC      ; PROC 伪指令定义函数
    EXPORT  rt_hw_interrupt_enable  ; EXPORT 输出定义的函数,类似于 C 语言 extern
    MSR     PRIMASK, r0             ; 将 r0 寄存器的值写入到 PRIMASK 寄存器
    BX      LR                      ; 函数返回
    ENDP                            ; ENDP 函数结束
 
CPSID I ;PRIMASK=1, ; 关中断
CPSIE I ;PRIMASK=0, ; 开中断
 实现线程栈初始化:线程初始化函数_rt_thread_init(),_rt_thread_init() 函数会调用栈初始化函数 rt_hw_stack_init()。
实现上下文切换:实现三个线程切换相关的函数

1) rt_hw_context_switch_to():没有来源线程,切换到目标线程,在调度器启动第一个线程的时候被调用。

2) rt_hw_context_switch():在线程环境下,从当前线程切换到目标线程。

3) rt_hw_context_switch_interrupt ():在中断环境下,从当前线程切换到目标线程。

中断到线程上下文切换 :

 

 

 实现PendSV中断:PendSV 中断处理函数是 PendSV_Handler()

 

 实现时钟节拍:在 Cortex M 中,实现 SysTick 的中断处理函数即可实现时钟节拍功能。

void SysTick_Handler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    rt_tick_increase();

    /* leave interrupt */
    rt_interrupt_leave();
}

2.BSP移植

建立让操作系统运行的基本环境

1)初始化 CPU 内部寄存器,设定 RAM 工作时序。

2)实现时钟驱动及中断控制器驱动,完善中断管理。

3)实现串口和 GPIO 驱动。

4)初始化动态内存堆,实现动态堆内存管理。

九、SMP介绍和移植(对称多处理(Symmetrical Multi-Processing))

 

 

 

posted @ 2022-09-09 16:23  shuai2022  阅读(387)  评论(0)    收藏  举报