基于STM32L431RC微控制器实时操作系统驻留方法

2023-11-02 12:36王宜怀刘长勇
计算机应用与软件 2023年10期
关键词:扇区文件夹线程

刘 肖 王宜怀 汪 恒 刘长勇,2

1(苏州大学计算机科学与技术学院 江苏 苏州 215006)

2(武夷学院认知计算与智能信息处理福建省高校重点实验室 福建 武夷山 354300)

0 引 言

在面向以微控制器(Microcontroller Unit,MCU)的嵌入式系统的开发中,常常需要使用实时操作系统(Real Time Operation System,RTOS)。为降低嵌入式系统的编程难度,节省程序的编译时间,将实时操作系统驻留在MCU中,即固化在MCU内的非易失存储器中,并提供对外的应用程序接口。

目前针对RTOS移植文献较为丰富,如文献[1]实现了将muTKernel RTOS移植到H8S/2377 MCU上;文献[2]实现了uC/OS-III的移植;文献[3]实现了将MicroC/OS-II RTOS移植到PowerPC 7410处理器上等,而对于RTOS的驻留较少;文献[4]以VxWorks 653分区操作系统为研究实例,采用统一建模语言分析了分区配置和启动机制,为理解分区和驻留提供了参考;文献[5-6]虽实现操作系统的驻留,但均针对的是PC机。

实时操作系统驻留的关键技术在于驻留后实时操作系统的函数如何被调用、驻留后的程序框架如何设计合理和RAM和Flash空间的划分设置。本文在通用嵌入式计算机(General Embedded Computer,GEC)架构的基础上,针对以上问题设计出合理方案,利用直接调用应用程序编程接口(Application Programming Interface,API)实现开发应用程序,使得用户不需要进行寄存器级编程,无须关心RTOS的调用细节,提高编程颗粒度和可移植性,达到降低编程难度、快速编译之目的,同时为RTOS的驻留提供了一种方案。

1 GEC架构简介

MCU性能的不断提高、软件工程概念的普及和为提高编程颗粒度和可移植性,借鉴通用计算机概念与做法,提出通用嵌入式计算机的概念。在软件上,将嵌入式软件分为基本输入输出系统程序(Basic Input and Output System,BIOS)与用户程序(User)两个部分[7]。RTOS随BIOS程序驻留于MCU内的Flash中,在User程序中启动RTOS。

1.1 BIOS程序工程框架

BIOS的功能主要是进行系统的初始化;实现RTOS的驻留;为用户程序提供相关构件接口,包括底层驱动构件接口、应用构件接口以及操作系统构件接口。

以“分门别类、各有归处”为主要原则,遵循硬件层次与软件层次的递进包含关系以及合理命名文件夹名称设计了如表1所示带前缀编号01-09的文件夹,分别存放文档、芯片内核、微控制器、GEC、用户板、软件构件、主程序、RTOS组件和RTOS启动相关文件。这里将文档放入工程结构中,其目的是让文档与源程序密切联系在一起。接着是由内到外的四个文件夹:芯片内核、微控制器、GEC、用户板。然后是软件构件文件夹,存放与硬件无关的构件,如操作系统构件。随后是主程序文件夹,存放相应的头文件、中断例程文件、主程序文件。最后是RTOS文件夹,存放RTOS封装的组件和启动相关文件。

表1 BIOS程序工程框架

1.2 User程序工程框架

User程序是真正的用户二次编程模板,在User程序中以正常函数名及传参的方式调用BIOS程序提供的API接口,实现对底层驱动构件、应用构件、操作系统构件的调用等,而无须再次进行底层驱动的开发。User程序工程框架类似于BIOS程序工程框架,区别在于User程序08文件夹和09文件夹,BIOS程序的08文件夹为RTOS封装的组件,User程序08文件夹为08_OSThread,用于存放主线程和任务线程,User程序无09文件夹。

基于GEC架构,将RTOS随BIOS程序驻留于MCU中有以下好处:

(1) 降低编程难度。由于RTOS随BIOS程序驻留于MCU中,提供RTOS相关接口函数给User程序使用,使得用户只需关心User程序的编程,无须关心RTOS的调用细节,从而降低了编程难度。

(2) 节省编译时间。由于RTOS随BIOS程序驻留在MCU中,编译成功烧入非易失存储器Flash后就一直存在于MCU中,而User程序中无RTOS,只需编译User程序,从而缩短了程序的编译时间。

(3) 提高用户程序的可移植性。在不同的内核、不同MCU已驻留RTOS的前提下,由于BIOS程序提供统一的接口函数给User程序使用,故User程序只需修改少量配置信息,便可在不同的内核之间移植,提高了用户程序的可移植性。

2 驻留方法

在GEC架构下,整个工程被分为BIOS程序和User程序,要想实现mbedOS的驻留,首先需要对MCU的RAM和Flash空间进行合理的划分,使得代码不重叠,变量使用不越界,其次应设计出符合软件工程可复用、可移植、可拓展的接口函数。

2.1 Flash和RAM空间的划分

(1) Flash空间的划分。在嵌入式系统软件开发中,中断向量、程序代码和常数通常存放于Flash中。在GEC架构下,Flash空间的划分采用互不干涉的原则,即BIOS程序占据Flash空间的前a个扇区,User程序占据Flash空间的后b个扇区,Flash总空间为a+b个扇区,如图1所示。因为Flash的擦除是以扇区为单位的,故也应该以扇区为单位进行Flash空间的划分。

图1 Flash空间的划分

(2) RAM空间的划分。RAM通常用来存放可读可写的数据段(.data段)、可读可写且没有初始化的.bss段、heap段、stack段。因为stack段是用来存放临时变量的,故RAM空间的划分可分为栈独享和栈共享两种方式。栈共享方式如图2所示,BIOS程序占用整个RAM空间,User程序则从BIOS程序堆段之后开始使用,且BIOS程序和User程序有共同的栈底(RAM地址的最大值+1)。栈共享方式能够使得RAM空间得到充分使用,但需注意内存冲突,一般当RAM空间小于16 KB时使用。栈独享方式如图3所示,类似于Flash空间的划分,BIOS程序占据RAM空间的前mKB,User程序占据RAM空间的后nKB,RAM总空间为m+nKB,BIOS程序和User程序有各自的栈底。栈独享方式可以使得BIOS程序和User程序内存不冲突,但RAM空间利用率低,一般当RAM空间大于60 KB时使用。

图2 RAM空间栈共享方式

图3 RAM空间栈独享方式

2.2 API表的设计与实现

在Flash和RAM空间分配合理的基础上,User程序若要成功调用BIOS程序提供的接口函数,还需要获取被调用函数的地址。为此,在GEC架构下,BIOS程序将接口函数封装后固化于Flash中,并将接口函数的地址有序地存放于API表中,User程序就可以通过访问这个API表实现对接口函数的调用。API表的设计主要包括接口函数的定义、声明、登记,其过程如图4所示。

图4 API表设计

2.2.1接口函数的定义与声明

接口函数的定义与声明需满足嵌入式软件构件(Embedded Software Component)的基本原则。规范的软件构件由头文件(.h)及源程序文件(.c或.cpp)文件构成[8]。接口函数在源程序文件中定义,在头文件中声明。API表中主要包含底层驱动构件、应用构件和操作系统构件(软件构件)这三类构件的函数。

(1) 底层驱动构件。底层驱动构件是根据MCU内部功能模块的基本知识要素,针对MCU引脚功能或MCU内部功能,利用MCU内部寄存器所制作的直接干预硬件的构件[7]。常用的底层驱动构件主要有GPIO、UART构件等。

(2) 应用构件。应用构件是调用芯片底层驱动构件而制作完成的,符合软件工程封装规范的,面向实际应用硬件模块的驱动构件,例如printf构件。

(3) 操作系统构件。RTOS提供各种类,如线程类、线程信号类等。为了方便用户使用,将RTOS常用函数如操作系统启动函数、创建线程函数、延时函数等封装成构件,表2列出了操作系统部分接口函数。

表2 操作系统部分接口函数

2.2.2接口函数的登记

所谓接口函数登记,就是将接口函数的入口地址按顺序放置在一个统一的区域,该区域即为API表。API表可用一个一维数组(如ComponentFun)来表示,接口函数的编号与数组的下标一一对应。在API表中,预留了一些缺省的接口函数名,方便应用程序接口表的更新与扩充。

2.3 User程序接口函数获取与重定义

User程序获取BIOS程序的API表地址后还需要对接口函数地址进行重定义,最后才能实现对接口函数的调用,其过程如图5所示。

图5 API表的调用

在BIOS程序跳转到User程序之后,执行main函数之前,调用函数BIOS_API_Init,该函数获取BIOS程序提供的API表首地址并将其保存在一个全局数组(如component_fun)中。由于数组component_fun中的元素只是接口函数的入口地址,并没有接口函数实现的具体形式,因此,还需要对接口函数的地址进行重定义,以便用户调用。重定义后的函数名可与之前的不一致,但接口函数重定义顺序必须和登记顺序一致。其一般格式如下:

#define 函数名((接口函数声明指针表达形式)(全局数组[接口函数序号])),例如操作系统启动函数可以定义为#define OS_start((void(*)(void(*func)(void)))(component_fun[63]))。

2.4 驻留的注意事项

(1) 合理分配Flash空间。在实际的Flash空间划分中,应避免BIOS程序的Flash空间划分过大或过小。过大会使得Flash空间的浪费,甚至以牺牲User程序功能来满足程序的需要;过小则会使得BIOS程序无法运行。当Flash空间较小时,只需保留最基本的各类构件和mbedOS的最基本功能函数以满足实际工程需要即可,确保BIOS的Flash空间不浪费。

(2) RAM内存冲突问题。当RAM空间的划分采用栈共享的方式时,由于堆的使用方向是由小地址向大地址方向进行的,因此当BIOS程序中使用new或malloc函数申请空间时,可能会使得堆溢出,从而和User程序的.data和.bss段重叠,导致User程序无法正常运行。因此,在BIOS程序中主动在堆区申请空间,从而避开User程序的全局变量区,如图6所示。

图6 RAM空间分配示意图

(3) 系统服务调用问题。mbedOS的调度依赖于SVC、可挂起系统调用(Pendable Supervisor,PendSV)、系统时间嘀嗒SysTick[9],故在启动mbedOS前,应及时将SVC、PendSV、SysTick的中断向量写入User程序的中断向量表中。

3 驻留测试

选取mbedOS实时操作系统进驻留测试,驻留测试工程在STM32CubeIDE 1.3.0开发环境和STM32L431RC微控制器上完成。其中,RAM空间划分方式采用栈共享方式。

mbedOS是ARM公司在2014年推出的,它是一个专门为物联网(IoT)中的“物体”而设计开源嵌入式实时操作系统[10]。mbedOS提供统一的应用程序编程接口[11],具有嵌入式系统的软、硬件资源的分配、线程(任务)调度、同步机制、中断处理等基本功能,在IP网络组件[12]、物联网[13]等方面得到广泛应用。

STM32L431RC微控制器的片内Flash大小为256 KB,Flash区的地址范围为:0x0800_0000-0x0804_0000。Flash区中扇区大小2 KB,扇区总共有128个。片内RAM为静态随机存储SRAM,大小为64 KB,地址范围为0x2000_0000-0x2001_0000。

3.1 功能设计和测试结果

(1) 功能设计。User程序的主要功能是依次创建红灯、绿灯、蓝灯线程,实现三个线程申请同一个互斥量并在申请到互斥量后,分别延时3 s、1 s、2 s。

(2) 测试结果。User程序中,在调用操作系统启动、线程创建、线程启动、互斥量锁定、互斥量释放等函数前加上相应printf函数输出提示信息,来验证mbedOS是否驻留成功。驻留测试实验结果如图7所示,可以看出mbedOS驻留成功,能准确调用BIOS程序提供的接口函数,线程运行正常,该方法具有可行性。

图7 测试结果

3.2 Flash和RAM空间实际划分情况

在驻留测试工程中,BIOS程序占Flash的前26个扇区(52 KB)、RAM的全部空间;User程序占Flash的后102个扇区(204 KB)、RAM的后48 KB。mbedOS驻留后的Flash和RAM空间划分和实际使用如表3所示,BIOS和User程序实际使用空间大小均小于实际分配空间大小,且User程序剩余空间较大,能满足后续程序开发需要,故Flash和RAM空间分配较为合理。

表3 STM32L431RC中BIOS和User空间划分表

4 结 语

本文在GEC架构的基础上,深入剖析RTOS驻留的关键技术,给出驻留后程序的合理工程框架,提出采用互不干涉的原则划分Flash空间和栈共享、栈独享两种方式划分RAM空间,详细给出API表的设计与实现,最后以mbedOS实时操作系统为例,在STM32L431RC上进行了驻留测试,测试结果表明mbedOS驻留成功,Flash和RAM空间划分合理,并且降低编程难度,缩短程序的编译时间,提高用户程序的可移植性,具有一定的参考价值。

猜你喜欢
扇区文件夹线程
磁力文件夹
分阶段调整增加扇区通行能力策略
U盘故障排除经验谈
摸清超标源头 大文件夹这样处理
调动右键 解决文件夹管理三大难题
浅谈linux多线程协作
基于贝叶斯估计的短时空域扇区交通流量预测
挂在墙上的文件夹
重建分区表与FAT32_DBR研究与实现
基于上下文定界的Fork/Join并行性的并发程序可达性分析*