版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
嵌入式实时操作系统基础与RT-Thread入门第1章概述全套可编辑PPT课件第1章_概述第2章_线程第3章_时间管理与中断第4章_消息队列第5章_信号量与互斥量第6章_邮箱与事件1、基本概念2、常见RTOS3、RT-Thread实例4、小结与思考目录CONTENTS常见RTOS实例实践总结思考基本概念嵌入式实时操作系统03基本概念面对外界事件或数据,系统必须在规定时间内完成处理并作出响应,同时协调各项实时任务运行。操作系统通用桌面系统Windows、macOS嵌入式操作系统实时操作系统本书的学习对象非实时操作系统Android、iOS等常见RTOS实例实践总结思考基本概念嵌入式设备的两类约束04基本概念资源预算面向具体场景进行紧凑设计体积和成本受到严格控制STM32片内RAM与Flash往往只有几十至几百KBRTOS必须控制自身存储占用实时性与可靠性控制系统不能容忍长时间停顿输入采样到控制输出的时间间隔必须足够短系统需要长期稳定运行程序无响应和明显卡顿应尽量避免常见RTOS实例实践总结思考基本概念嵌入式软件系统的分层05基本概念01应用程序调用驱动和操作系统API,实现具体业务功能02操作系统统一分配和调度软硬件资源03底层驱动BSP/HAL封装硬件接口与通信。更换硬件平台时,主要调整这一层常见RTOS实例实践总结思考基本概念实时操作系统的主要功能06基本概念01任务调度把应用任务组织为线程,按优先级和任务队列切换运行02线程间通信在线程之间传递信息,例如信号量和消息队列03存储管理管理内存堆与内存池,提高内存使用效率04时钟与中断管理接管系统滴答、PendSV、中断函数和中断栈等资源常见RTOS实例实践总结思考基本概念线程调度与上下文切换07基本概念线程状态挂起就绪运行在抢占式系统中,就绪线程里优先级最高者获得CPU。每个线程拥有独立线程栈,切回原线程时可以从上次停止的位置继续执行。01调度器确定目标线程02保存当前线程的运行环境和位置03从目标线程栈取出并恢复上下文04目标线程继续执行常见RTOS实例实践总结思考基本概念三种常见嵌入式实时操作系统08常见RTOSuC/OS可移植、可裁剪的抢占式实时内核面向8位、16位和32位MCU或DSP原稿所述版本最多管理64个任务任务优先级不同,不支持时间片轮转FreeRTOS轻量级,内核通常占用4KB到9KB支持任务、信号量、队列和软件定时器支持优先级调度与轮转调度具有执行跟踪与堆栈溢出检测RT-Thread国产开源实时操作系统组件丰富,架构模块化且可裁剪Nano最小内核约需1.2KBRAM、3KBFlash标准版、Nano和Smart面向不同芯片常见RTOS实例实践总结思考基本概念本书选择RT-Thread09常见RTOS开源并允许在商业产品中使用资源占用低,并针对超低功耗场景设计服务层组件和软件包生态丰富架构松耦合、模块化,便于裁剪和扩展Nano最小配置1.2KBRAM3KBFlashNano最小内核,适合资源紧张的MCU标准版具备更丰富的服务层组件Smart面向带MMU的中高端芯片,如Cortex-A常见RTOS实例实践总结思考基本概念开发平台与工具10实例实践STM32CubeMX图形化配置时钟树、芯片引脚与组件,并导出含寄存器初始化代码的HAL/LL工程。STM32从SPL转向HAL/LL;CubeMX进一步降低了配置难度。KeilMDK基于uVision的ARM处理器开发环境支持断点仿真、执行分析和性能分析提供项目例程与多种中间件软件包通过厂商支持包开发多种ARMMCU常见RTOS实例实践总结思考基本概念CubeMX移植RT-Thread的总体流程11实例实践01获取RT-Thread软件包并导入CubeMX02创建工程,选择RTOSKernel并完成关键配置03生成工程,在MDK中调整串口并编写第一个应用两个关键配置中断处理函数取消HAL生成的HardFault与PendSV,并取消SysTick处理函数,避免与RT-Thread重复定义。HAL时间基准将TimebaseSource改为SysTick之外的定时器,避免HAL_Delay()受系统滴答抢占影响。常见RTOS实例实践总结思考基本概念进入软件包管理器12实例实践图1.1进入软件包管理图1.2新建用户软件包地址从Help打开Manageembeddedsoftwarepackages,在管理窗口选择FromURL,再单击New。常见RTOS实例实践总结思考基本概念添加并启用RT-Thread软件源13实例实践图1.3输入软件包地址并检查图1.4选择新增的软件源软件包地址/download/cube/RealThread.RT-Thread.pdscCheck通过后单击OK,并在上一级窗口选中新条目。常见RTOS实例实践总结思考基本概念下载并安装所需版本14实例实践图1.5选择版本并开始安装图1.6接受许可并完成安装等待包描述文件加载完成,进入RealThread选项卡选择需要的RT-Thread版本,原稿建议3.1.5单击InstallNow,勾选许可声明后完成安装常见RTOS实例实践总结思考基本概念确认软件包安装完成15实例实践图1.7检查版本状态完成标志对应版本前的方框出现填充颜色,表示软件包安装完成。随后即可进入工程创建和组件配置。常见RTOS实例实践总结思考基本概念选择RT-Thread组件16实例实践图1.8进入组件选择图1.9选择内核组件打开SelectComponents并展开RT-Thread。RTOSKernel为必选项,初次实例暂不启用shell和device。常见RTOS实例实践总结思考基本概念使能内核并释放系统中断入口17实例实践图1.10使能RTOSKernel图1.11取消HAL生成系统处理函数关键配置在NVIC中取消Hardfaultinterrupt、Pendablerequestforservice和TimebaseSystemticktimer的GenerateIRQhandler。常见RTOS实例实践总结思考基本概念调整HAL时间基准并启用串口18实例实践图1.12将HAL时间基准移出SysTick图1.13开启所需串口选择SysTick之外的定时器,避免HAL_Delay()因系统滴答优先级和抢占而卡顿。按开发板引脚开启对应USART,为RTOS串口类型和控制台输出提供基础。常见RTOS实例实践总结思考基本概念在board.c中匹配实际串口19实例实践图1.14修改RTOS使用的串口生成工程后01打开board.c02找到RT-Thread定义的串口实例03改为CubeMX中启用的USART编号常见RTOS实例实践总结思考基本概念第一个应用在main线程中运行20实例实践图1.15LED闪烁程序main函数由系统自动创建为线程应用可以直接写在main线程中,无需手动创建该线程rt_thread_mdelay()挂起当前线程并让出CPU调度器可在延时期间运行其他就绪线程函数声明位于rtthread.h延时不再持续占用CPU。常见RTOS实例实践总结思考基本概念直接在MDK中移植RT-Thread21实例实践适用场景已有第三方工程,或项目并非由CubeMX创建时,可通过MDK软件包机制直接加入RT-Thread。完成内核与工程启动流程的衔接01通过PackInstaller下载RT-Thread02在ManageRun-TimeEnvironment中加入内核03清理冲突的中断处理函数04把HAL、SysTick和BSP初始化放入最早的板级初始化位置常见RTOS实例实践总结思考基本概念通过PackInstaller获取RT-Thread22实例实践图1.16打开PackInstaller图1.17下载所需版本操作要点单击工具栏中的PackInstaller在列表中定位RT-Thread下载工程所需的版本常见RTOS实例实践总结思考基本概念把RT-Thread内核加入工程23实例实践图1.18打开运行环境管理器图1.19选择RTOSKernel第一次配置先加入内核单击ManageRun-TimeEnvironment,在对话框中选中RT-Thread内核组件并确认。后续可按应用需要逐步加入其他组件。常见RTOS实例实践总结思考基本概念检查并处理冲突的中断函数24实例实践图1.20查找处理函数图1.20处理重复定义使用Ctrl+F的FindinFiles查找HardFault_Handler与PendSV_Handler注释非RTOS内核文件中的同名函数,或为其添加__weak修饰符同样检查SysTick_Handler,确保由RTOS内核接管常见RTOS实例实践总结思考基本概念把系统初始化放入板级启动位置25实例实践图1.21修改初始化函数最早初始化区域放入HAL库初始化按RT_TICK_PER_SECOND配置SysTick需要时加入BSP初始化完成后,RT-Thread内核即可衔接原工程的启动过程。常见RTOS实例实践总结思考基本概念RT-Thread程序启动流程26实例实践图1.22程序启动流程关键问题初始化为什么能够不写在main()中?MDK先进入$Sub$$main()扩展入口,完成RT-Thread启动与板级初始化。随后通过$Super$$main()回到原main()。常见RTOS实例实践总结思考基本概念main线程的创建与入口27实例实践图1.23main扩展程序的位置和内容图1.24main线程入口函数rtthread_startup()调用rt_hw_board_init()完成系统初始化。rt_application_init()创建并初始化main线程。main_thread_entry()最终进入用户main()。常见RTOS实例实践总结思考基本概念rt_kprintf()与自定义串口28实例实践RT-Thread使用rt_kprintf()发送控制台信息。未启用设备框架时,可在rt_hw_console_output()中调用开发板串口发送函数。#defineRT_USING_CONSOLEstaticintuart_init(void){return0;}INIT_BOARD_EXPORT(uart_init);voidrt_hw_console_output(constchar*str){printf(str);}01打开RT_USING_CONSOLE02在board.c中找到uart_init()与rt_hw_console_output()03在输出函数中调用本开发板的串口发送代码常见RTOS实例实践总结思考基本概念本章回顾29总结思考本章建立了实时操作系统的基本概念,并讲解了CubeMX与MDK两种RT-Thread移植路径。关键步骤需要通过实际操作熟悉。01实时操作系统与一般操作系统有哪些相同点和不同点?02RT-Thread的启动流程由哪些关键函数衔接?03rt_thread_mdelay()如何在延时期间释放CPU使用权?第1章·概述本章结束下节课见RT-Thread线程创建、调度与生命周期管理第2章线程1、线程作用与创建2、调度原理与启动3、状态迁移与管理4、线程实例与思考目录CONTENTS调度启动状态管理实例思考线程创建线程是RT-Thread的调度单位03线程创建开发者把总体方案拆成相对独立的任务,再由RTOS将这些任务组织为线程。01LCD显示界面刷新与状态呈现02按键扫描周期检测输入并触发处理03总线管理协调外设访问和数据交换调度器按优先级和线程状态安排CPU,提升多任务系统的实时性与稳定性。调度启动状态管理实例思考线程创建一个线程的三个组成部分04线程创建01线程主体函数承载线程需要执行的业务逻辑。周期任务通常在循环中调用延时函数。02线程栈保存线程上下文,包括切换所需的寄存器与运行位置03线程控制块集中记录入口、名称、栈、优先级、时间片、状态、定时器和链表节点。调度启动状态管理实例思考线程创建线程创建与初始化的三个步骤05线程创建01定义线程主体函数。一次性任务可自然退出,周期任务需要循环。02准备线程栈和线程控制块,并确定名称、参数、栈大小、优先级和时间片。03静态线程调用rt_thread_init(),动态线程调用rt_thread_create()。创建与初始化的本质,是把线程信息写入线程控制块。调度启动状态管理实例思考线程创建线程属性决定调度行为06线程创建name线程名称,用于对象管理和调试定位parameter传递给线程主体函数的参数,无参数时使用RT_NULLstack_size线程栈大小,需要覆盖函数调用和局部数据需求priority0至255,数值越小表示优先级越高tick同优先级线程的时间片大小,以系统tick为单位调度启动状态管理实例思考线程创建静态线程与动态线程07线程创建静态线程用户预先定义控制块与线程栈调用rt_thread_init()完成初始化内存生命周期伴随整个程序关闭时使用rt_thread_detach()动态线程创建函数从内存堆申请控制块与线程栈调用rt_thread_create()创建并初始化删除后由空闲线程释放动态内存关闭时使用rt_thread_delete()调度启动状态管理实例思考线程创建rt_thread_init()初始化静态线程08线程创建rt_err_trt_thread_init(structrt_thread*thread,constchar*name,void(*entry)(void*parameter),void*parameter,void*stack_start,rt_uint32_tstack_size,rt_uint8_tpriority,rt_uint32_ttick);静态线程特有参数thread:预先定义的线程控制块stack_start:预先定义的线程栈首地址stack_size:线程栈大小,常用sizeof()取得返回rt_err_t。初始化成功时返回RT_EOK。调度启动状态管理实例思考线程创建rt_thread_create()创建动态线程09线程创建rt_thread_trt_thread_create(constchar*name,void(*entry)(void*parameter),void*parameter,rt_uint32_tstack_size,rt_uint8_tpriority,rt_uint32_ttick);由创建函数管理内存不需要传入线程控制块不需要传入线程栈首地址只需说明需要的栈大小和调度属性返回rt_thread_t。创建失败时返回RT_NULL。调度启动状态管理实例思考线程创建两类线程的创建路径10线程创建动态线程01rt_object_allocate()申请控制块02RT_KERNEL_MALLOC()申请线程栈03_rt_thread_init()写入线程属性静态线程01使用用户预先定义的控制块02使用用户预先定义的线程栈03rt_object_init()后调用_rt_thread_init()两条路径最终都进入内部初始化函数。调度启动状态管理实例思考线程创建动态线程的内存申请11线程创建thread=(structrt_thread*)rt_object_allocate(RT_Object_Class_Thread,name);if(thread==RT_NULL)returnRT_NULL;stack_start=RT_KERNEL_MALLOC(stack_size);if(stack_start==RT_NULL){rt_object_delete((rt_object_t)thread);returnRT_NULL;}_rt_thread_init(thread,name,entry,parameter,stack_start,stack_size,priority,tick);两次申请先申请线程对象再申请线程栈栈申请失败时先删除已申请的线程对象成功后返回控制块地址调度启动状态管理实例思考线程创建静态线程的对象初始化12线程创建RT_ASSERT(thread!=RT_NULL);RT_ASSERT(stack_start!=RT_NULL);rt_object_init((rt_object_t)thread,RT_Object_Class_Thread,name);return_rt_thread_init(thread,name,entry,parameter,stack_start,stack_size,priority,tick);对象类型标记object->type=type|RT_Object_Class_Static;RT_Object_Class_Static的值为0x80。系统通过该位区分静态对象。调度启动状态管理实例思考线程创建生命周期、效率与返回值13线程创建比较项静态线程动态线程内存来源全局变量运行时从堆申请创建开销较低需要完成动态内存申请运行期释放不能释放预定义内存删除后可释放控制块与线程栈函数返回rt_err_t,成功为RT_EOKrt_thread_t,失败为RT_NULL调度启动状态管理实例思考线程创建静态标记影响线程关闭方式14线程创建动态对象object->type=RT_Object_Class_Thread;没有静态标记。关闭后进入失效线程链表,等待空闲线程释放内存。静态对象object->type=RT_Object_Class_Thread|RT_Object_Class_Static;带有0x80静态标记。关闭时只脱离对象链表,不释放预定义内存。调度启动状态管理实例思考线程创建调度器使用的两类数据15调度启动rt_thread_priority_table[]按优先级组织就绪线程。每个数组成员都是一条双向链表,同优先级线程挂在同一链表中。rt_thread_ready_priority_group用位图记录哪些优先级当前存在就绪线程,帮助调度器快速定位最高优先级。线程查找+上下文切换调度启动状态管理实例思考线程创建线程优先级表16调度启动图2.1线程优先级表链表组织优先级码越小,优先级越高同优先级就绪线程构成闭环最低优先级通常保留给IDLE线程链表节点存放在线程控制块中。调度启动状态管理实例思考线程创建双向链表与就绪队列17调度启动structrt_list_node{structrt_list_node*next;structrt_list_node*prev;};typedefstructrt_list_nodert_list_t;RT-Thread的链表节点只保存前后节点地址。实际线程数据仍在线程控制块中。链表头属于线程优先级表线程节点属于各自的线程控制块闭环结构便于插入、移除和轮转调度器从目标优先级链表取出就绪线程调度启动状态管理实例思考线程创建就绪优先级组的位图查找18调度启动每一位对应一个优先级0表示该优先级没有就绪线程,1表示对应链表非空。IDLE所在的最低优先级始终保持就绪。highest_ready_priority=__rt_ffs(rt_thread_ready_priority_group)-1;查找结果01定位最低的置位bit02得到数值最小的优先级码03从对应链表取出首个就绪线程调度启动状态管理实例思考线程创建内核调度中的效率考虑19调度启动图2.2register声明示例一图2.2register声明示例二原稿以register声明说明内核对访问效率的关注。在线程查询阶段,更关键的优化是通过优先级位图避免逐条扫描所有链表。位图定位优先级,链表定位线程调度启动状态管理实例思考线程创建中断中的上下文切换流程20调度启动图2.3线程调度流程调度的核心01保存原线程栈指针02记录目标线程栈指针03触发PendSV异常04在异常处理过程中完成上下文切换调度启动状态管理实例思考线程创建rt_schedule()的两个阶段21调度启动线程查找关闭中断并确认调度器未上锁用__rt_ffs()找到最高优先级从对应链表提取目标线程控制块上下文切换更新当前线程和当前优先级在线程环境发起rt_hw_context_switch()在中断环境记录切换请求,由中断退出路径完成切换目标线程与当前线程相同时不执行切换。调度启动状态管理实例思考线程创建rt_thread_startup()启动线程22调度启动rt_err_trt_thread_startup(rt_thread_tthread);参数thread:需要启动的线程控制块地址01检查线程已经初始化02恢复初始优先级并计算掩码03先挂起,再恢复到就绪列表04调用rt_schedule()触发实时调度初始化只准备控制块,startup才让调度器看到线程。调度启动状态管理实例思考线程创建线程从初始化进入就绪状态23调度启动INIT控制块已经具备完整线程信息,但链表节点尚未进入就绪表startup设置当前优先级与掩码,调用suspend和resume完成队列调整READY线程节点进入优先级链表,就绪优先级组的对应bit被置位schedule如果新线程具有更高优先级,调度器立即切换到该线程调度启动状态管理实例思考线程创建线程的五种状态24状态管理图2.4线程的状态及转化五态INIT初始READY就绪RUNNING运行SUSPEND挂起CLOSE关闭状态由函数调用、调度事件和资源等待共同推动。调度启动状态管理实例思考线程创建INIT、READY与RUNNING的迁移25状态管理INIT→READYrt_thread_startup()把线程节点插入优先级表,并把状态改为就绪READY↔RUNNING优先级最高的就绪线程获得CPU更高优先级线程就绪时,当前线程退回READY同优先级线程的时间片结束后进行轮换READY表示具备运行条件,RUNNING表示正在占用CPU。调度启动状态管理实例思考线程创建挂起、恢复与资源等待26状态管理线程进入SUSPEND,说明它暂时缺少继续运行的条件。常见等待对象延时到期信号量或互斥量可用内存池申请成功邮箱、事件或消息队列到达数据手动管理rt_thread_suspend()把线程移出就绪表rt_thread_resume()把线程恢复为就绪态信号量、邮箱和事件等发送操作也可恢复线程调度启动状态管理实例思考线程创建关闭状态与rt_thread_exit()27状态管理voidrt_thread_exit(void){thread=rt_current_thread;rt_schedule_remove_thread(thread);thread->stat=RT_THREAD_CLOSE;rt_timer_detach(&thread->thread_timer);...rt_schedule();}两种触发方式用户在程序中手动调用线程主体函数运行到末尾时自动调用动态线程先进入rt_thread_defunct链表,空闲线程再释放其线程栈和对象内存,避免线程尚未切走就释放自身栈。调度启动状态管理实例思考线程创建rt_thread_delete()与rt_thread_detach()28状态管理动态线程rt_thread_delete(thread)确认对象没有静态标记移出调度系统并关闭内置定时器进入失效线程链表,等待释放动态内存静态线程rt_thread_detach(thread)确认对象具有RT_Object_Class_Static标记移出调度系统并关闭内置定时器调用rt_object_detach()脱离对象链表调度启动状态管理实例思考线程创建线程关闭后的资源处理29状态管理动态线程对象和线程栈来自内存堆进入defunct链表,由IDLE线程释放静态线程控制块和线程栈是预定义全局变量对象类型置空并脱离链表,内存仍然存在选择线程类型时,需要同时考虑创建开销和运行期资源释放需求。调度启动状态管理实例思考线程创建单线程实例:LED周期闪烁30实例思考静态线程structrt_threadled_thread;rt_uint8_tLED_STACK[512];rt_thread_init(&led_thread,"LED",led_thread_entry,RT_NULL,LED_STACK,sizeof(LED_STACK),2,0);rt_thread_startup(&led_thread);动态线程rt_thread_tled;led=rt_thread_create("LED",led_thread_entry,RT_NULL,512,2,0);if(led!=RT_NULL)rt_thread_startup(led);线程主体中每200ms翻转一次LED,并调用rt_thread_mdelay(200)让出CPU。调度启动状态管理实例思考线程创建多线程实例:KEY、LCD与LED31实例思考KEY优先级7每10ms检测按键,恢复LCD线程并修改页码LCD优先级8完成一次页面刷新后主动挂起,等待按键线程恢复LED优先级9每200ms翻转一次LED,实时性要求最低数值越小,线程优先级越高。调度启动状态管理实例思考线程创建多线程实例中的调度控制32实例思考LCD线程LCD_DispStr(...);rt_thread_suspend(lcd);rt_schedule();挂起函数不会立即调度,因此随后显式调用rt_schedule()。KEY线程if(keyscan==JOY_DOWN_L||keyscan==JOY_DOWN_R){rt_thread_resume(lcd);}按键事件恢复LCD线程,并更新页面编号。main线程rt_enter_critical();/*create+startup*/rt_exit_critical();创建三线程期间锁住调度器,避免高优先级线程提前运行。调度启动状态管理实例思考线程创建本章回顾33实例思考本章围绕线程的创建、调度、生命周期和多线程管理展开,并通过LED、LCD与按键实例完成应用验证。01线程的五种状态分别是什么?状态之间如何转换?02调度器如何借助优先级位图与链表找到目标线程?03静态线程与动态线程在内存、创建开销和关闭方式上有何区别?04RT_ASSERT()在内核中如何检查参数并终止错误流程?第2章·线程本章结束下节课见RT-Thread系统节拍、软件定时器与中断机制第3章时间管理与中断1、系统时钟与时间片2、软件定时器常用函数3、SOFT/HARD运行机制4、中断与临界段目录CONTENTS定时器函数运行机制中断应用系统时钟操作系统时钟03系统时钟时钟由一个或一组定时器产生稳定节拍,操作系统据此管理所有与时间相关的任务。01节拍来源定时器按固定周期进入中断02典型任务按键检测、屏幕刷新和周期控制03RT-Thread使用SysTick作为系统计时与调度时钟定时器函数运行机制中断应用系统时钟系统节拍频率04系统时钟//OStickpersecond//Default:1000(1ms)#defineRT_TICK_PER_SECOND1000RT_TICK_PER_SECOND定义每秒系统节拍数。默认值1000表示每1ms产生一次SysTick中断。频率越高,时间分辨率越细,同时中断开销也会增加。定时器函数运行机制中断应用系统时钟rt_tick_increase()的两项工作05系统时钟时间片检查递增系统时间rt_tick,扣减当前线程的remaining_tick。时间片耗尽时触发同优先级轮换。HARD定时器检查调用rt_timer_check(),检查rt_timer_list中最近到期的定时器。每个SysTick周期都会执行一次定时器函数运行机制中断应用系统时钟同优先级线程的时间片轮换06系统时钟更高优先级线程就绪时,当前线程让出CPU;多个线程优先级相同时,时间片决定轮换节奏。init_tick线程初始化时设定的时间片长度remaining_tick当前轮次剩余的tick数,每次系统节拍减1时间片只在同优先级线程之间承担轮换作用定时器函数运行机制中断应用系统时钟时间片运行示例07系统时钟图3.1时间片原理线程B运行5tick后切换到线程A;线程A运行10tick后再切回线程B。定时器函数运行机制中断应用系统时钟时间片检查过程08系统时钟++rt_tick;thread=rt_thread_self();--thread->remaining_tick;if(thread->remaining_tick==0){thread->remaining_tick=thread->init_tick;rt_thread_yield();}rt_timer_check();01先递增rt_tick,形成新的系统时刻02取得当前线程并扣减remaining_tick03耗尽时恢复init_tick04yield把线程移到同级队尾05最后检查HARD定时器定时器函数运行机制中断应用系统时钟rt_thread_yield()的轮换逻辑09系统时钟函数只在当前线程处于READY且同一优先级链表中存在其他线程时执行轮换。01关闭中断并取得当前线程02把当前线程节点移出优先级链表03将节点重新插入同级链表队尾04恢复中断后调用rt_schedule()若没有更高优先级线程就绪,CPU将交给同优先级的下一个线程。定时器函数运行机制中断应用系统时钟两种主动延时函数10系统时钟rt_thread_delay(tick)把线程挂起指定tick数。计时结束后,线程重新进入就绪态。rt_thread_delay_until(tick)把线程挂起到指定系统时刻。适合需要稳定周期的任务。主动延时让高优先级线程释放CPU,避免低优先级线程长期得不到运行。定时器函数运行机制中断应用系统时钟延时是多线程节奏设计的一部分11系统时钟被动等待消息队列、信号量等资源尚未到达恢复时间取决于其他线程或外设等待时长并不确定主动延时在线程尾部设置固定延时控制线程的执行周期和CPU占用使多个线程按预期交替运行定时器函数运行机制中断应用系统时钟定时器控制块rt_timer12定时器函数structrt_timer{structrt_objectparent;rt_list_trow[RT_TIMER_SKIP_LIST_LEVEL];void(*timeout_func)(void*parameter);void*parameter;rt_tick_tinit_tick;rt_tick_ttimeout_tick;};控制块记录系统对象信息定时器链表节点超时函数及其参数超时时长init_tick超时时刻timeout_tick定时器函数运行机制中断应用系统时钟HARD与SOFT两种定时器13定时器函数HARD定时器超时检查位于SysTick中断中的rt_timer_check(),超时函数也在中断上下文执行。SOFT定时器系统创建timer线程,在线程上下文检查rt_soft_timer_list并执行超时函数。两类定时器的创建、启动、停止与删除接口相同定时器函数运行机制中断应用系统时钟rt_timer_init()14定时器函数voidrt_timer_init(rt_timer_ttimer,constchar*name,void(*timeout)(void*parameter),void*parameter,rt_tick_ttime,rt_uint8_tflag);静态定时器用户预先定义控制块rt_object_init()初始化对象_rt_timer_init()写入计时属性初始化完成后仍需调用rt_timer_start()定时器函数运行机制中断应用系统时钟rt_timer_init()参数15定时器函数timer定时器控制块地址name定时器名称timeout超时函数parameter超时函数参数time超时时间flag计时模式和定时器类型定时器函数运行机制中断应用系统时钟flag参数同时定义两类属性16定时器函数计时模式RT_TIMER_FLAG_ONE_SHOT单次计时,到期后转为非激活态RT_TIMER_FLAG_PERIODIC周期计时,到期后重新启动运行类型RT_TIMER_FLAG_HARD_TIMER在SysTick中断上下文执行RT_TIMER_FLAG_SOFT_TIMER在timer线程上下文执行两个维度通过按位或运算组合定时器函数运行机制中断应用系统时钟rt_timer_create()17定时器函数rt_timer_trt_timer_create(constchar*name,void(*timeout)(void*parameter),void*parameter,rt_tick_ttime,rt_uint8_tflag);动态定时器从内存堆申请控制块内部调用_rt_timer_init()成功返回控制块地址失败返回RT_NULLcreate省略了timer参数,控制块由系统分配定时器函数运行机制中断应用系统时钟rt_timer_create()参数18定时器函数01name定时器名称02timeout超时函数03parameter超时函数参数04time超时时间05flag计时模式和定时器类型定时器函数运行机制中断应用系统时钟rt_timer_start()的启动过程19定时器函数01检查控制块并暂时关闭中断02先从原链表移除并清除激活标志03计算timeout_tick=rt_tick+init_tick04选择rt_timer_list或rt_soft_timer_list05按超时时刻升序插入并置为激活态关闭中断期间只调整链表。SOFT定时器必要时还会恢复timer线程。定时器函数运行机制中断应用系统时钟定时器链表按超时时刻排序20定时器函数链表头保存最近到期的定时器首节点只需检查首个定时器是否到期相同超时按FIFO顺序处理,先启动的先执行默认配置RT_TIMER_SKIP_LIST_LEVEL为1,维护一条链表定时器函数运行机制中断应用系统时钟再次启动会重置定时器21定时器函数rt_timer_start()每次都会先移除旧节点,再重新计算超时时刻并插入链表。01定时器正在计时02再次调用rt_timer_start()03旧倒计时作废04从当前rt_tick重新计时这一特性可用于看门狗式超时与操作去抖定时器函数运行机制中断应用系统时钟停止、控制与重新启动22定时器函数01rt_timer_stop():移出运行链表并清除激活标志02rt_timer_control():读取或修改时间、模式和状态03rt_timer_start():按新参数重新计算并进入运行链表常用控制命令GET_TIMESET_TIMESET_ONESHOTSET_PERIODICGET_STATE定时器函数运行机制中断应用系统时钟静态与动态定时器的删除23定时器函数rt_timer_detach(timer)用于静态定时器移出运行链表清除对象属性不释放用户预定义内存rt_timer_delete(timer)用于动态定时器移出运行链表清除激活状态释放动态控制块内存定时器函数运行机制中断应用系统时钟启用SOFT定时器的两个条件24运行机制编译配置#defineRT_USING_TIMER_SOFT1#ifRT_USING_TIMER_SOFT==0#undefRT_USING_TIMER_SOFT#endif初始化配置flag=RT_TIMER_FLAG_PERIODIC|RT_TIMER_FLAG_SOFT_TIMER;宏开关决定内核是否编译timer线程,flag决定具体定时器进入哪条链表定时器函数运行机制中断应用系统时钟SOFT与HARD定时器的执行位置25运行机制SOFT节点进入rt_soft_timer_listtimer线程检查最近超时时刻超时函数在线程上下文执行可进行会引起调度的操作HARD节点进入rt_timer_listSysTick中断检查最近超时时刻超时函数在中断上下文执行函数应短小且不可阻塞定时器函数运行机制中断应用系统时钟timer线程的运行方式26运行机制next_timeout=rt_timer_list_next_timeout(rt_soft_timer_list);if(next_timeout==RT_TICK_MAX)rt_thread_suspend(rt_thread_self());elsert_thread_delay(next_timeout-rt_tick_get());rt_soft_timer_check();01读取链表首节点的超时时刻02链表为空时挂起自身03时间未到时延时到目标时刻04到期或被唤醒后重新检查,再执行超时函数定时器函数运行机制中断应用系统时钟rt_soft_timer_check()的处理顺序27运行机制01确认rt_soft_timer_list非空02取得首定时器并读取current_tick03判断超时时刻是否到达04移出定时器并执行timeout_func05周期定时器调用rt_timer_start()重新计时先移出节点再调用timeout_func,回调重新启动同一定时器时不会形成重复节点。定时器函数运行机制中断应用系统时钟无符号系统时间会循环回零28运行机制图3.2无符号整数的加减法示意rt_tick与timeout_tick都是32位无符号数。达到2³²−1后再加1,数值回到0。直接比较大小无法正确处理跨越回零点的超时定时器函数运行机制中断应用系统时钟timeout_tick的计算29运行机制图3.3timeout_tick的计算启动时timeout_tick=current_tick+init_tick;判断时使用current_tick−timeout_tick的无符号差值。定时器函数运行机制中断应用系统时钟定时器超时后的差值30运行机制图3.4定时器超时的情况超时时刻到达时,差值变为0。检查延后时,差值会变为有限正数,例如1或10。系统通过阈值判断它仍属于“已经超时”。定时器函数运行机制中断应用系统时钟为什么定时时长小于RT_TICK_MAX/231运行机制初始差值delta=2³²−init_tick超时判定delta<out_delta_lim为避免“开始即结束”,init_tick与超时判定阈值都取无符号范围的一半。init_tick<RT_TICK_MAX/2定时器函数运行机制中断应用系统时钟定时器系统运行流程32运行机制图3.5定时器系统的运作流程定时器函数运行机制中断应用系统时钟定时器实验:LED周期闪烁33运行机制实验目标用周期SOFT定时器代替LED线程;LCD显示周期,按键动态调整、启动或停止定时器。左右按键周期每次增减50ms,范围50~2000ms上下按键启动或停止led_twinkle定时器参数更新依次调用stop、control、start超时函数led_timeout()翻转LED定时器函数运行机制中断应用系统时钟中断与异常34中断应用异常CPU暂停当前程序,并处理内部或外部事件。同步异常:CPU内部错误异步异常:外设事件中断中断属于异步异常。RT-Thread的中断设置与裸机开发基本一致,但线程运行会被中断暂时打断。外设信号经中断控制器NVIC送达CPU定时器函数运行机制中断应用系统时钟中断处理的四个阶段35中断应用01中断请求:检查处理器状态、使能与屏蔽条件02中断进入:保存R0~R3、R12、LR、PSR等寄存器03中断处理:跳转到向量表指定的ISR04中断返回:从栈中恢复现场并继续原程序Cortex-M通过中断向量表直接定位各ISR入口定时器函数运行机制中断应用系统时钟中断延迟的组成36中断应用中断延迟=识别时间+[等待时间]+[关闭时间]图3.6Cortex-M中断延迟典型值等待时间等待更高优先级中断结束识别时间保存现场并取得ISR入口方括号中的两项不一定存在,公式表示最大可能延迟定时器函数运行机制中断应用系统时钟临界资源的三种保护方式37中断应用IPC机制互斥量或信号量协调线程访问中断仍可响应调度锁禁止线程调度仍响应中断中断锁关闭总中断保护最严格但会增加中断延迟临界段必须保证共享变量或链表在一次操作期间保持一致定时器函数运行机制中断应用系统时钟临界段保护的选择38中断应用01线程共享资源优先使用互斥量等IPC机制02仅需阻止线程切换使用调度锁,保留中断响应03链表等内核关键结构使用短时间中断锁,防止中断并发修改04ISR设计只处理必要工作,其余交给线程完成缩短关中断时间,可以直接降低最大中断延迟定时器函数运行机制中断应用系统时钟本章回顾39中断应用本章围绕系统节拍、时间片、定时器系统和中断机制展开,重点比较了SOFT与HARD定时器的执行环境。01定时器系统如何完成创建、排序、检查与重启?02未使能RT_USING_TIMER_SOFT时,SOFT标志会产生什么结果?03rt_thread_mdelay()如何为每个线程计时?04中断处理包含哪些阶段,中断延迟由哪些时间组成?05中断现场保存与线程上下文切换有哪些异同?第3章·时间管理与中断本章结束下节课见RT-ThreadIPC通信、等待机制与队列操作第4章消息队列1、消息队列基础概念2、控制块与消息池3、消息发送机制4、消息接收、控制与实验目录CONTENTS结构初始化发送机制接收应用基础概念消息队列在IPC中的位置03基础概念消息队列是本书介绍的第一个IPC模块,服务于线程与中断之间的信息传递。01线程与线程发送者把消息写入队列,接收者按队列顺序取出02线程与中断中断可发送消息,线程负责后续处理03消息长度每个队列规定最大消息大小,实际发送长度可以不同结构初始化发送机制接收应用基础概念消息队列与线程状态04基础概念01队列有消息接收线程取得消息后继续运行02队列为空接收线程按timeout选择立即返回、限时等待或阻塞03消息到达等待线程从挂起态恢复为就绪态04等待超时线程恢复,并获得超时错误码消息队列把数据传递与线程调度联系在一起结构初始化发送机制接收应用基础概念等待线程的两种排序方式05基础概念FIFO模式按线程开始等待的先后顺序排列。最早挂起的线程最先获得消息。PRIO模式按线程优先级排列。优先级最高的等待线程最先获得消息。两种模式都作用于suspend_thread等待链表结构初始化发送机制接收应用基础概念消息队列提供的操作06基础概念01创建与删除支持静态初始化、动态创建及对应的删除方式02消息发送支持立即发送、限时等待和永久阻塞03消息接收队列为空时可立即返回,也可挂起等待04紧急发送把消息插入有效消息链表的头部05等待排序等待线程可按FIFO或优先级排列结构初始化发送机制接收应用基础概念消息队列模块开关07基础概念消息队列属于可裁剪模块。使用前需在rtconfig.h中启用宏定义。#defineRT_USING_MESSAGEQUEUE关闭该宏后,相关函数不会参与内核编译结构初始化发送机制接收应用基础概念消息队列控制块08结构初始化structrt_messagequeue{structrt_ipc_objectparent;void*msg_pool;rt_uint16_tmsg_size;rt_uint16_tmax_msgs;rt_uint16_tentry;void*msg_queue_head;void*msg_queue_tail;void*msg_queue_free;rt_list_tsuspend_sender_thread;};控制块继承IPC对象保存消息池地址与容量信息维护有效消息和空闲消息链表管理等待发送消息的线程结构初始化发送机制接收应用基础概念控制块字段:消息池与容量09结构初始化01parent继承rt_ipc_object,包含内核对象信息与接收等待链表02msg_pool消息池的首地址03msg_size每条消息允许的最大内容大小04max_msgs消息池能够容纳的消息节点数量05entry当前有效消息数量结构初始化发送机制接收应用基础概念控制块字段:有效消息链表10结构初始化msg_queue_head指向有效消息链表头部。接收操作从这里取走下一条消息。msg_queue_tail指向有效消息链表尾部。普通发送把新消息接到这里。队列为空时,head与tail都指向RT_NULL结构初始化发送机制接收应用基础概念空闲链表与发送等待链表11结构初始化msg_queue_free指向空闲消息节点链表。发送消息前必须先取得一个空闲节点。suspend_sender_thread队列已满时,等待发送的线程挂入该双向链表。接收线程释放节点后可唤醒其中的线程。一个管理空闲存储,一个管理等待存储的线程结构初始化发送机制接收应用基础概念两条线程挂起链表12结构初始化structrt_ipc_object{structrt_objectparent;rt_list_tsuspend_thread;};suspend_thread挂载因队列为空而等待消息的接收线程suspend_sender_thread挂载因队列已满而等待空闲节点的发送线程。结构初始化发送机制接收应用基础概念消息队列的存储结构13结构初始化图4.1消息队列大致结构结构初始化发送机制接收应用基础概念rt_mq_init()接口14结构初始化rt_err_trt_mq_init(rt_mq_tmq,constchar*name,void*msgpool,rt_size_tmsg_size,rt_size_tpool_size,rt_uint8_tflag);参数含义mq消息队列控制块地址name消息队列名称msgpool消息池首地址msg_size每条消息内容的最大大小pool_size消息池总大小flag等待线程按FIFO或优先级排列结构初始化发送机制接收应用基础概念初始化对象与等待策略15结构初始化rt_object_init(&mq->parent.parent,RT_Object_Class_MessageQueue,name);mq->parent.parent.flag=flag;rt_ipc_object_init(&mq->parent);等待线程排列方式#defineRT_IPC_FLAG_FIFO0x00#defineRT_IPC_FLAG_PRIO0x01FIFO选择最早等待的线程,PRIO选择优先级最高的线程。结构初始化发送机制接收应用基础概念消息池容量计算16结构初始化消息内容按RT_ALIGN_SIZE对齐后,再计算消息池能容纳的节点数。mq->msg_pool=msgpool;mq->msg_size=RT_ALIGN(msg_size,RT_ALIGN_SIZE);mq->max_msgs=pool_size/(mq->msg_size+sizeof(structrt_mq_message));每个节点由消息头和定长内容区组成结构初始化发送机制接收应用基础概念空闲消息链表初始化17结构初始化mq->msg_queue_head=RT_NULL;mq->msg_queue_tail=RT_NULL;mq->msg_queue_free=RT_NULL;for(temp=0;temp<mq->max_msgs;temp++){head=(structrt_mq_message*)(...);head->next=mq->msg_queue_free;mq->msg_queue_free=head;}mq->entry=0;rt_list_init(&mq->suspend_sender_thread);01有效链表头、尾均置空02逐个格式化消息池节点03节点依次接入空闲单向链表04有效消息数置零,并初始化发送等待链表结构初始化发送机制接收应用基础概念初始化后的消息队列18结构初始化图4.2初始化后的消息队列结构结构初始化发送机制接收应用基础概念rt_mq_create()与静态初始化19结构初始化rt_mq_init()用于静态消息队列。控制块和消息池由用户预先分配。rt_mq_create()用于动态消息队列。内核动态申请控制块和消息池空间。选择方式与静态线程、动态线程一致结构初始化发送机制接收应用基础概念rt_mq_detach():删除静态消息队列20结构初始化rt_err_trt_mq_detach(rt_mq_tmq){RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent));rt_ipc_list_resume_all(&mq->parent.suspend_thread);rt_ipc_list_resume_all(&mq->suspend_sender_thread);rt_object_detach(&mq->parent.parent);returnRT_EOK;}参数mq静态消息队列控制块地址删除前恢复接收等待与发送等待链表中的全部线程结构初始化发送机制接收应用基础概念rt_mq_delete():删除动态消息队列21结构初始化rt_mq_delete()完成与rt_mq_detach()相同的线程恢复和对象清理,并释放动态申请的内存。01共同操作恢复两条挂起链表中的线程,清除消息队列对象信息02主要区别delete还要释放create时动态申请的内存03使用原则嵌入式内存有限,动态对象不用时及时删除结构初始化发送机制接收应用基础概念rt_mq_send_wait()接口22发送机制rt_err_trt_mq_send_wait(rt_mq_tmq,constvoid*buffer,rt_size_tsize,rt_int32_ttimeout);参数含义mq目标消息队列控制块buffer消息内容所在内存的首地址size本次发送的消息长度timeout等待空闲节点的超时时间发送流程可分为等待空闲节点和插入有效链表两部分结构初始化发送机制接收应用基础概念发送等待:参数与满队列判断23发送机制RT_ASSERT(mq!=RT_NULL);RT_ASSERT(buffer!=RT_NULL);RT_ASSERT(size!=0);if(size>mq->msg_size)return-RT_ERROR;msg=mq->msg_queue_free;if(msg==RT_NULL&&timeout==0)return-RT_EFULL;消息长度不得超过msg_size检查free与取得节点必须处于同一临界区队列已满且不等待时立即返回-RT_EFULL结构初始化发送机制接收应用基础概念发送等待:挂起发送线程24发送机制while((msg=mq->msg_queue_free)==RT_NULL){thread->error=RT_EOK;if(timeout==0)return-RT_EFULL;rt_ipc_list_suspend(&mq->suspend_sender_thread,thread,mq->parent.parent.flag);...}没有空闲节点发送线程挂入suspend_sender_thread,并按FIFO或PRIO排列。被唤醒不代表已经取得节点,代码会回到while重新检查free,再决定发送或继续等待。结构初始化发送机制接收应用基础概念发送等待:定时器与剩余时间25发送机制if(timeout>0){tick_delta=rt_tick_get();rt_timer_control(&thread->thread_timer,RT_TIMER_CTRL_SET_TIME,&timeout);rt_timer_start(&thread->thread_timer);}rt_schedule();tick_delta=rt_tick_get()-tick_delta;timeout-=tick_delta;if(timeout<0)timeout=0;01启动线程内置定时器02重新调度,让出CPU03线程恢复后检查错误码04扣除已等待时间后回到while重新检查free结构初始化发送机制接收应用基础概念发送操作:取得空闲节点26发送机制mq->msg_queue_free=msg->next;msg->next=RT_NULL;rt_memcpy(msg+1,buffer,size);01脱离空闲链表free指针移动到下一个空闲节点02形成游离节点当前节点next置为RT_NULL03复制消息内容buffer数据写入消息头之后的内容区结构初始化发送机制接收应用基础概念发送操作:接入有效消息链表27发送机制if(mq->msg_queue_tail!=RT_NULL)mq->msg_queue_tail->next=msg;mq->msg_queue_tail=msg;if(mq->msg_queue_head==RT_NULL)mq->msg_queue_head=msg;mq->entry++;01旧表尾连接新节点02tail指向新节点03原队列为空时同步设置head04有效消息数量entry加一结构初始化发送机制接收应用基础概念发送操作:唤醒接收线程28发送机制rt_inlinert_err_trt_ipc_list_resume(rt_list_t*list){structrt_thread*thread;thread=rt_list_entry(list->next,structrt_thread,tlist);rt_thread_resume(thread);returnRT_EOK;}参数list需要从挂起链表中恢复的线程节点发送成功后若suspend_thread非空,恢复首个接收线程并立即调度结构初始化发送机制接收应用基础概念rt_mq_send():无等待发送29发送机制rt_err_trt_mq_send(rt_mq_tmq,constvoid*buffer,rt_size_tsize){returnrt_mq_send_wait(mq,buffer,size,0);}参数含义mq目标消息队列控制块buffer消息内容所在内存的首地址size消息长度timeout固定为0,队列已满时立即返回错误结构初始化发送机制接收应用基础概念rt_mq_urgent():紧急消息发送30发送机制msg->next=mq->msg_queue_head;mq->msg_queue_head=msg;if(mq->msg_queue_tail==RT_NULL)mq->msg_queue_tail=msg;mq->entry++;参数含义mq目标消息队列控制块buffer消息内容所在内存的首地址size消息长度队列已满时不等待,直接返回-RT_EFULL新节点插入有效消息链表头部接收线程会优先取到这条紧急消息结构初始化发送机制接收应用基础概念普通发送与紧急发送31发送机制普通发送新消息接到msg_queue_tail。队列满时,send_wait可根据timeout挂起等待。紧急发送新消息插入msg_queue_head。队列满时直接返回,不进入等待循环。二者都会增加entry,并唤醒等待接收的线程结构初始化发送机制接收应用基础概念rt_mq_recv()接口32接收应用rt_err_trt_mq_recv(rt_mq_tmq,void*buffer,rt_size_tsize,rt_int32_ttimeout);参数含义mq目标消息队列控制块buffer接收消息的目标内存首地址size接收缓冲区大小timeout等待有效消息的超时时间结构初始化发送机制接收应用基础概念接收等待:空队列判断33接收应用if(mq->entry==0&&timeout==0)return-RT_ETIMEOUT;while(mq->entry==0){thread->error=RT_EOK;if(timeout==0)return-RT_ETIMEOUT;...}判断依据entry表示有效消息数量。entry为0且不等待时,立即返回
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 活动5 衣服破了我会补教学设计小学劳动北师大版五年级-北师大版
- 三年级下册道德与法治教学设计-5.3 地图就是一幅画 第1课时 合作智慧多 北师大版
- 陕西省石泉县高中化学 第四章 非金属及其化合物 4.3 硫和氮的氧化物教学设计 新人教版必修1
- 一、远离高压线教学设计小学综合实践活动五年级下册沪科黔科版
- IEC 61960-4-2024 扣式二次锂电池性能标准 中文版完整解读
- 三年级英语下册 Unit 6 How many(The fifth period)第五课时教学设计 人教PEP
- 课文11秋天教案
- 新教材高中化学 第一章 物质及其变化 3.2 氧化剂、还原剂教学设计 新人教版必修1
- 武术 教学设计-2025-2026学年高二上学期体育与健康人教版必修第一册
- 风电产业技术演进与海上风电规模化发展路径研究
- 2026年肌肉骨骼康复试题及答案
- 极客邦:2026年中国企业AI人才与组织发展报告-智能体时代的AI人才粮仓模型
- 污染场地地下水修复技术方案
- 新版2026秋新苏教版小学科学六年级上册全册知识点考点合集
- 2026年国际贸易术语(Incoterms2020)模拟试题及答案
- 支部换届两推一选会议记录范文
- 2026 年校园防诈骗安全教育课堂
- 2026年继续教育公需课(科目三)
- 杭州市拱墅区社区工作者考试题库及答案
- 万向传动轴说明书
- 电力安全生产事故总结
评论
0/150
提交评论