当前位置:   article > 正文

rtthread学习笔记(一)

rtthread


首先贴一下rtthread的官网:

https://www.rt-thread.org/page/about.html
  • 1

一、rtthread介绍

RT-Thread(Real Time-Thread),是一个嵌入式实时操作系统,基本属性之一是支持多任务,在rtt系统中,任务通过线程实现
名词解释——进程和线程:

https://www.ruanyifeng.com/blog/2013/04/processes_and_threads.html                                                                                                                                                                      		   
  • 1
  • rttread特点:是一个高度可伸缩组件完整丰富、简易开发、超低功耗、高安全性的物联网操作系统
  • 名词解释:高度可伸缩——可以根据MCU资源情况裁剪或添加组件,最小仅需3KB flash,1.2KB RAM(NANO版本——极简版内核)。

二、rtthread架构

RT-Thread架构
在这里插入图片描述
NANO版本架构
在这里插入图片描述
Smart版本架构
在这里插入图片描述

三、rtthread移植(STM32)

1、NANO版本移植

RT-Thread Studio移植

https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-nano/nano-port-studio/an0047-nano-port-studio
  • 1

MDK移植

https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-nano/nano-port-keil/an0039-nano-port-keil
  • 1

2、标准版本移植

RT-Thread Studio移植

在RT-Thread Stutio 左上角点击“文件-新建-RT-Thread项目”
在这里插入图片描述
在弹出的窗口中输入工程名、选择rtthread版本4.02,选择芯片信息、调试信息以及控制台串口, 注意:串口号和发送接收引脚需与选择的芯片型号对应,如下图:
在这里插入图片描述

MDK移植

https://blog.csdn.net/lianggoch/article/details/123684927?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168244279016800217263047%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=168244279016800217263047&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-123684927-null-null.142^v86^insert_down1,239^v2^insert_chatgpt&utm_term=mdk%E7%A7%BB%E6%A4%8Drtt%E6%A0%87%E5%87%86%E7%89%88&spm=1018.2226.3001.4187
  • 1

四、系统时钟配置和FinSH组件添加

1、时钟配置

通过修改 board.c 的 SystemClock_Config() 更改系统时钟,只需要修改board.h文件中定义的接口,如下图:
系统时钟配置

2、FinSH组件添加及使用

双击 RT-Thread Settings 进入配置,打开组件,勾选 FinSH Shell,保存配置。
在这里插入图片描述
配置完成后,编译、下载,打开串口终端,打开串口,按enter键可进入命令行交互模式。
注:需要屏蔽main函数中的while(1)死循环,线程才能够执行:

https://blog.csdn.net/qq_33033059/article/details/118491764?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522168244535316800225560493%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=168244535316800225560493&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-118491764-null-null.142^v86^insert_down1,239^v2^insert_chatgpt&utm_term=rtthread%20finsh%E4%BD%BF%E7%94%A8&spm=1018.2226.3001.4187
  • 1

五、RT-Thread内核介绍

1、内核框架和启动流程

1.1内核框架:

实时内核的实现包括:
☐ 对象管理(Object Management)
☐ 线程管理(Thread Management)及实时调度器(Real-time Scheduler)
☐ 线程间通信管理 (inter-Thread Communication)
☐ 时钟管理(Clock Management)
☐ 内存管理(Memory Management)
☐ 设备管理 (Device)
内核最小的资源占用情况是 3KB ROM,1.2KB RAM(NANO)
下图为 RT-Thread 内核架构图,内核处于硬件层之上,内核部分包括内核库、实时内核实现。
在这里插入图片描述

1.1.1、线程调度

线程是 RT-Thread 操作系统中最小的调度单位,线程调度算法是基于优先级的全抢占式多线程调度算法,即在系统中除了中断处理函数、调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身。支持 256 个线程优先级(也可通过配置文件更改为最大支持 32 个或 8 个线程优先级,针对 STM32 默认配置是 32 个线程优先级),0 优先级代表最高优先级,最低优先级留给空闲线程使用;同时它也支持创建多个具有相同优先级的线程,相同优先级的线程间采用时间片的轮转调度算法进行调度,使每个线程运行相应时间;另外调度器在寻找那些处于就绪状态的具有最高优先级的线程时,所经历的时间是恒定的,系统也不限制线程数量的多少,线程数目只和硬件平台的具体内存相关

1.1.2、时钟管理

RT-Thread 的时钟管理以时钟节拍为基础时钟节拍是 RT-Thread 操作系统中最小的时钟单位。RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止定时器否则将永远持续执行下去。
另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以设置为 HARD_TIMER 模式或者 SOFT_TIMER 模式。
通常使用定时器定时回调函数(即超时函数),完成定时服务。用户根据自己对定时处理的实时性要求选择合适类型的定时器。
名词解释:超时函数——

https://www.bookstack.cn/read/rtthread-manual-doc/4.2.md
  • 1
1.1.3、线程间同步

RT-Thread 采用信号量互斥量事件集实现线程间同步。线程通过对信号量、互斥量的获取与释放进行同步;互斥量采用优先级继承的方式解决了实时系统常见的优先级翻转问题。线程同步机制支持线程按优先级等待或按先进先出方式获取信号量或互斥量。线程通过对事件的发送与接收进行同步;事件集支持多事件的 “或触发” 和“与触发”,适合于线程等待多个事件的情况。
名词解释:优先级继承——

https://blog.csdn.net/qq_49864684/article/details/119323721
  • 1
1.1.4、线程间通信

RT-Thread 支持邮箱消息队列等通信机制。邮箱中一封邮件的长度固定为 4 字节大小;消息队列能够接收不固定长度的消息,并把消息缓存在自己的内存空间中。邮箱效率较消息队列更为高效。邮箱和消息队列的发送动作可安全用于中断服务例程中。通信机制支持线程按优先级等待或按先进先出方式获取。

1.1.5、内存管理

RT-Thread 支持静态内存池管理动态内存堆管理。当静态内存池具有可用内存时,系统对内存块分配的时间将是恒定的;当静态内存池为空时,系统将申请内存块的线程挂起或阻塞掉 (即线程等待一段时间后仍未获得内存块就放弃申请并返回,或者立刻返回。等待的时间取决于申请内存块时设置的等待时间参数),当其他线程释放内存块到内存池时,如果有挂起的待分配内存块的线程存在的话,则系统会将这个线程唤醒。
动态内存堆管理模块在系统资源不同的情况下,分别提供了面向小内存系统内存管理算法面向大内存系统SLAB 内存管理算法
还有一种动态内存堆管理叫做 memheap,适用于系统含有多个地址可不连续的内存堆。使用 memheap 可以将多个内存堆 “粘贴” 在一起,让用户操作起来像是在操作一个内存堆。

1.1.6、I/O设备管理

RT-Thread 将 PIN、I2C、SPI、USB、UART 等作为外设设备,统一通过设备注册完成。实现了按名称访问的设备管理子系统,可按照统一的 API 界面访问硬件设备。在设备驱动接口上,根据嵌入式系统的特点,对不同的设备可以挂接相应的事件。当设备事件触发时,由驱动程序通知给上层的应用程序。

1.2启动流程

rtthread_startup() 函数是 RT-Thread 规定的统一启动入口
一般执行顺序:系统先从启动文件(startup_xx.S)开始运行,然后进入 RT-Thread 的启动 rtthread_startup() ,最后进入用户入口 main()
在这里插入图片描述

1.2.1、汇编阶段

主要过程:
☐ 从Flash中拷贝数据段到SRAM中
☐ 清空BSS段(BSS段清零)
☐ 初始化系统时钟(SystemInit)
☐ 进入entry(C阶段)入口
在这里插入图片描述

Reset_Handler://复位

/* Copy the data segment initializers from flash to SRAM */
  movs r1, #0
  b LoopCopyDataInit

CopyDataInit:
  ldr r3, =_sidata
  ldr r3, [r3, r1]
  str r3, [r0, r1]
  adds r1, r1, #4

LoopCopyDataInit:
  ldr r0, =_sdata
  ldr r3, =_edata
  adds r2, r0, r1
  cmp r2, r3
  bcc CopyDataInit
  ldr r2, =_sbss
  b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
  movs r3, #0
  str r3, [r2], #4

LoopFillZerobss:
  ldr r3, = _ebss
  cmp r2, r3
  bcc FillZerobss

/* Call the clock system intitialization function.*/
    bl  SystemInit
/* Call static constructors */
    /* bl __libc_init_array */
/* Call the application's entry point.*/
  bl  entry//entry 入口
  bx lr
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
1.2.2、C阶段

【1】系统时钟初始化
参考之前章节内容
【2】entry入口

int entry(void)
{
    rtthread_startup();
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
1.2.3、rtthread_startup函数

主要过程:
☐ 初始化系统相关硬件
☐ 初始化系统内核对象、例如定时器、调度器、信号
☐ 创建主线程、定时器线程、idle线程
☐ 启动调度器

int rtthread_startup(void)
{
    rt_hw_interrupt_disable();//关闭中断

    /* board level initialization
     * NOTE: please initialize heap inside board initialization.
     */
    rt_hw_board_init();//硬件初始化

    /* show RT-Thread version */
    rt_show_version();
	//初始化系统内核对象
    /* timer system initialization */
    rt_system_timer_init();//定时器

    /* scheduler system initialization */
    rt_system_scheduler_init();//调度器

#ifdef RT_USING_SIGNALS
    /* signal system initialization */
    rt_system_signal_init();//信号
#endif

    /* create init_thread */
    rt_application_init();//创建主线程

    /* timer thread initialization */
    rt_system_timer_thread_init();//创建定时器线程

    /* idle thread initialization */
    rt_thread_idle_init();//创建空闲线程(优先级最低)

#ifdef RT_USING_SMP
    rt_hw_spin_lock(&_cpus_lock);
#endif /*RT_USING_SMP*/

    /* start scheduler */
    //选择优先级最高的线程开始调度
    rt_system_scheduler_start();//启动调度器

    /* never reach here */
    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
1.2.4、创建主线程

线程函数入口:main_thread_entry
栈大小:2048
优先级:10
同等优先级时间片轮询时间:20 个OS Tick rfconfig.h 中配置 :
#define RT_TICK_PER_SECOND 1000 Tick每秒1000次,一次的时间为1ms

/* RT-Thread Components */

#define RT_USING_COMPONENTS_INIT
#define RT_USING_USER_MAIN
#define RT_MAIN_THREAD_STACK_SIZE 2048
#define RT_MAIN_THREAD_PRIORITY 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
void rt_application_init(void)//创建主线程
{
	//创建线程,线程函数main_thread_entry
    rt_thread_t tid;

#ifdef RT_USING_HEAP
    tid = rt_thread_create("main", main_thread_entry, RT_NULL,
                           RT_MAIN_THREAD_STACK_SIZE, RT_MAIN_THREAD_PRIORITY, 20);//栈大小和优先级
    RT_ASSERT(tid != RT_NULL);
#else
    rt_err_t result;

    tid = &main_thread;
    result = rt_thread_init(tid, "main", main_thread_entry, RT_NULL,
                            main_stack, sizeof(main_stack), RT_MAIN_THREAD_PRIORITY, 20);
    RT_ASSERT(result == RT_EOK);

    /* if not define RT_USING_HEAP, using to eliminate the warning */
    (void)result;
#endif
	//开启线程 —— 将线程加入到系统的线程队列中,等待系统线程调度器遍历队列调用      
    rt_thread_startup(tid);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
/* the system main thread */
void main_thread_entry(void *parameter)
{
    extern int main(void);
    extern int $Super$$main(void);
    
#ifdef RT_USING_COMPONENTS_INIT
    /* RT-Thread components initialization */
    rt_components_init();
#endif    
#ifdef RT_USING_SMP
    rt_hw_secondary_cpu_up();
#endif
    /* invoke system main function */
#if defined(__CC_ARM) || defined(__CLANG_ARM)
    $Super$$main(); /* for ARMCC. */
#elif defined(__ICCARM__) || defined(__GNUC__)
    main();
#endif
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

注意:

	$Sub$ $foo :定义的新功能函数,在foo()函数之前/后使用$Sub$ $foo 可以添加一些新的程序代码。
	$Super$ $foo :就是原始的未修补的foo函数,使用这个$Super$ $foo函数将直接跳转到foo()函数。
	
	注意区分函数rt_thread_startup(tid)和函数 rtthread_startup():
	 1、 rtthread_startup()是启动入口函数  
	 2、rt_thread_startup(tid)表示此函数将启动一个tid线程并将其放入系统就绪队列
	 函数定义: rt_err_t rt_thread_startup(rt_thread_t thread);
	 参数:param thread——要启动的线程
	 返回操作状态:正常时为RT_EOK,错误时为-RT_ERROR
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

下一章:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/67398
推荐阅读
相关标签
  

闽ICP备14008679号