1.前言随着计算机技术和微电子技术的飞速发展,嵌入式系统的应用领域越来越广泛。注意力。随着工信部提出NB-IoT基站建设的具体目标,三大运营商加速建设,万物互联新时代即将迎来,这是信息的下一个万亿级市场移动互联网之后的行业。这些为实时操作系统应用提供了广阔的前景。嵌入式实时操作系统将部署在越来越多的设备中,这就需要工程师对嵌入式实时操作系统有深刻的理解。本系列文章将与您一起从零搭建一个嵌入式实时操作系统。我将以最简单直接的方式一步步搭建。我会用一篇文章来总结建设中的每个节点阶段,并开源软件工程和源代码。2、嵌入式实时操作系统嵌入式实时操作系统是一种特殊的程序和支持多任务处理的运行环境。嵌入式实时操作系统最大的特点就是“实时”。如果有任务要执行,实时操作系统会立即执行任务,不会有长时间的延迟。典型的实时操作系统包括uCOS、RT-Thread、FreeRTOS、VxWorks、WinCE等。嵌入式实时操作系统是一种创建和控制所有任务的特殊程序(通常称为内核)。嵌入式实时操作系统除了包含内核外,还提供文件系统、协议栈、图形用户界面等其他服务。本文的重点是了解嵌入式实时操作系统内核的工作原理和结构,所以文中所说的实时操作系统通常指的是操作系统内核。实时操作系统内核通常占用CPU运行时间的5%左右。另外,内核是一种软件代码,需要额外的ROM空间和RAM空间。嵌入式实时操作系统主要由以下三个子系统组成:任务调度子系统任务通信子系统内存管理子系统3.实现目标本文讲解构建嵌入式实时操作系统的第一个节点阶段:实现简单任务切换功能。代码区的数据不变,处理器寄存器的值和栈空间的值决定了程序的运行状态。让每个任务“独享”一个堆栈空间。当我们在任务运行时保存处理器寄存器的值,这就保存了任务的运行状态。同理,当我们将任务运行时处理器寄存器保存的值加载到处理寄存器中,恢复任务的运行状态,任务继续运行。切换任务的原则是:每个任务都有一个“独占”的堆栈空间,任务运行时可以通过保存和加载处理器寄存器的值来暂停和恢复任务。暂停一个任务然后恢复另一个任务完成任务切换。任务代码、任务栈空间和处理器状态如下:4、实验环境硬件基于意法半导体的STM32F401(ARM公司Cortex-M4内核),软件开发使用KEILV5.2开发工具。软件工程如下:软件工程包括三个文件:main.c、startup_stm32f401xc.s和readme。startup_stm32f401xc.s文件是STM32F401的启动文件,main.c文件实现任务切换功能,readme文件用于记录版本修改日志。5.代码切换任务的原理是让每个任务都有一个“独占”的堆栈空间。通过在任务运行时保存和加载处理器寄存器的值,可以暂停和恢复任务。暂停一个任务然后恢复另一个任务完成任务切换。因此,需要实现:每个任务独立的栈空间。实现任务挂起和恢复。实施任务调度。(1)实现独立的栈空间栈空间代码如下:为每个任务定义一个静态数组,在任务运行时将处理器的栈指针指向任务“自己”的静态数组,从而实现独立的栈空间。堆栈空间用于存放函数调用时的局部变量、中断调用和处理器寄存器值。切换任务时,需要将处理器寄存器的值保存到任务的独立栈空间中。在保存任务的运行状态时,需要将处理器寄存器的值保存到栈空间中,因此需要深刻理解处理器寄存器的用途和入栈、出栈的顺序.Cortex-M4内核的寄存器和寄存器中断自动入栈的时序图如下:初始化栈空间代码如下:初始化后栈空间的状态如下:栈是第一个-in-last-out数据结构,Cortex-M4内核的栈操作方式设置为向下增长。psp_array用于保存任务栈指针,psp_array[0]任务0栈指针指向task0_stack[112],其中task0_stack[116]保存PC程序指针值,task0_stack[117]保存状态寄存器(符合Cortex-M4核心寄存器出栈顺序:8个寄存器手动出栈,8个寄存器由硬件自动出栈)。(2)任务挂起和恢复代码如下:cortex-M4内核有一个PendSV(suspendablesystemcall)异常,异常号为14,优先级可编程。当软件将PendSV设置为挂起时,程序会进入PendSV异常(中断)。将PendSV异常优先级设置为最低,其他中断函数可以得到正常响应而不受PendSV异常的影响。在PendSV异常中,进行了任务切换。时序图如下:PendSV_Handler为Cortex-M4内核中断服务函数,进入中断函数此时处理器自动保存R0、R1、R2、R3、R12、LR、PC、XPSR,完成R4~R11在PendSV_Handler中断程序中入栈保存,从而实现任务保存。/*读取当前进程栈指针的值*/MRSR0,PSP/*将8个寄存器R4-R11的值保存到当前任务栈中,并将回写地址写入R0*/STMDBR0!,{R4-R11}psp_array[0]为任务0的栈指针,psp_array[1]为任务1的栈指针,下面代码实现任务栈指针切换。/*读取psp_array地址*/LDRR3,=__cpp(&psp_array)/*将当前进程的PSP指针值写入相应的PSP_array位置*/STRR0,[R3,R2,LSL#2]/*获取下一个进程Serialnumber*/LDRR4,=__cpp(&next_task)LDRR4,[R4]/*R1为&curr_task,将下一个程序号写入curr_task*/STRR4,[R1]/*psp_array读取curr_task指针值更新后的PSP*/LDRR0,[R3,R4,LSL#2]在PendSV_Handler中断程序中,R4~R11寄存器出栈,PendSV_Handler时处理器自动弹出R0,R1,R2,R3,R12,LR中断程序返回,PC,XPSR,实现任务恢复。/*弹出R4-R11八个寄存器*/LDMIAR0!,{R4-R11}/*设置PSP指针*/MSRPSP,R0/*中断返回*/BXLR(3)实现任务调度任务调度代码如下如下:SysTick_Handler是一个定时器中断程序,实现了时间片轮流改变目标任务,挂起PendSV_Handle中断,退出SysTick_Handler中断程序进入PendSV_Handle中断程序。六、运行结果代码模拟运行如下:运行代码后,依次加入task_num0和task_num1两个变量,代码实现了轮流切换任务的功能。
