嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fpri/fprime点击查看免费下载导读F´Flight Software中真正可运行的软件是一组通过端口相互连接的组件它们运行在活动组件的线程上或由硬件中断、定时源等系统事件驱动。本指南以 F´ 用户手册中构建拓扑Constructing the F´ Topology一章为主体结合 Ref 参考部署 与 autocoder 测试用例完整讲解从组件实例化、初始化、端口互连、命令注册、参数加载到活动组件任务启动的六个关键步骤。读完本文你将掌握 F´ 拓扑的搭建方法、端口连接器的命名规则以及如何组织一个可启动、可运行、可优雅关停的部署程序。拓扑构建的总体流程在 F´ 中拓扑topology指的是把一个部署中所有组件实例化、初始化、连接起来并启动其运行线程的完整过程。现代 F´ 部署通常用 FPP 语言声明实例与连接见 Ref/Top/instances.fpp 与 Ref/Top/topology.fpp由 autocoder 生成大量样板代码但手工拓扑中的关键调用顺序依然由 Ref/Top/RefTopology.cpp 中的setupTopology()明确体现调用 autocoder 生成的initComponents()对每个组件执行init()初始化调用setBaseIds()为每个组件实例设置 ID 基址opcode、通道 ID 等偏移调用connectComponents()把组件间的端口连接起来对需要定制配置的组件执行项目特有配置configureTopology()手工编写调用loadParameters()让各组件加载初始参数值调用startTasks()启动所有活动组件的任务启动不属于活动组件的自定义任务如通信接收任务。从 RefTopology.hpp 的注释可以看出步骤 1、2、3、5、6 的顺序对拓扑能否正常运行是关键的步骤 4配置通常假定组件已连接但未启动因此插在步骤 3 与 5 之间步骤 7 可前可后但自定义任务往往负责启动无线电通信习惯上放在最后。对应的拆除流程teardownTopology()RefTopology.cpp则依次执行stopTasks()、freeThreads()再停止并 join 自定义任务最后释放资源。下面按六个步骤逐一展开。一、实例化组件Instantiating the Components组件基类的构造函数被刻意设计得与内存模型无关开发者可以根据部署需求把组件实例化在静态存储区、堆或栈上三种方式均被支持。每个组件基类构造函数要么接受一个name参数用于对象注册与互连追踪要么不带参数。派生类的构造函数可以额外携带该应用特有的参数。需要特别注意如果组件以静态方式声明要警惕静态对象构造顺序的不确定性——C 标准不保证不同翻译单元中静态对象的初始化顺序因此跨文件依赖彼此构造状态的场景应避免静态实例化。从 PassiveComponentBase.hpp 可以看到组件基类持有m_idBaseID 基址与m_instance实例号两个成员二者正是通过构造后init()与setBaseIds()阶段填充的。在 FPP 风格的现代部署中实例化信息统一写在实例声明文件里例如 Ref/Top/instances.fpp 中活动组件blockDrv的声明instance blockDrv: Drv.BlockDriver base id 0x0100 \ queue size Default.QUEUE_SIZE \ stack size Default.STACK_SIZE \ priority 140其中queue size、stack size、priority会直接影响后续init()与start()的参数base id则用于组件 ID 偏移。二、初始化组件Initializing the Components每个组件都有一个init()调用用于初始化组件基类自身。该调用必须在实例化之后、互连之前执行。对于队列组件queued与活动组件activeinit()需要传入队列深度队列大小的设定应建立在对任务优先级以及组件间消息流量的分析之上过小会丢消息过大会浪费内存。从 QueuedComponentBase.hpp 可以看到队列机制的底层QueuedComponentBase持有Os::Queue m_queue成员并通过createQueue(depth, msgSize)实际创建底层队列doDispatch()是派发单条消息的纯虚方法由派生类实现返回MSG_DISPATCH_OK / MSG_DISPATCH_EMPTY / MSG_DISPATCH_ERROR / MSG_DISPATCH_EXIT等状态其中MSG_DISPATCH_EXIT表示收到退出消息。ActiveComponentBase又继承自QueuedComponentBase因此活动组件天然带有消息队列。在 FPP 部署中队列深度直接在实例声明中指定例如 Ref/Top/instances.fpp 定义默认值module Default { constant QUEUE_SIZE 10 constant STACK_SIZE 64 * 1024 }活动组件cmdDisp使用更深的队列queue size 20因为命令流量大、且其输入来自上行链路fileDownlink等文件组件使用queue size 30而被动组件passive如Svc::Framer、Svc::Time不需要队列参数。autocoder 会根据这些声明生成对应的init()调用。三、互连组件Interconnecting the Components组件之间通过端口互连。连接的本质是把输入端口指针交给调用它的输出端口输出端口保存指向输入端口的指针调用时直接解引用执行对方组件上的处理逻辑。组件基类会生成两类访问方法——获取输入端口指针的方法和设置输出端口指针的方法。底层支撑可参见 Fw/Port/InputPortBase.hpp 与 Fw/Port/OutputPortBase.hpp输入端口基类记录所属组件m_comp与端口号m_portNum输出端口基类则持有串行调用用的m_serPort并在FW_PORT_SERIALIZATION打开时提供registerSerialPort()与invokeSerial()。3.1 接口端口Interface Ports接口端口是组件互连的常规端口。对组件上每个端口类型与端口名代码生成器生成的方法命名如下获取输入端口指针PortType* get_port name_InputPort(NATIVE_INT_TYPE portNum);其中PortType 组件 XML 中端口定义data_type属性所指定的完整端口类型port name 端口定义name属性中的端口名。如果端口只有一个实例portNum传 0 即可。设置输出端口指针void set_port name_OutputPort(NATIVE_INT_TYPE portNum, PortType* port);即把上一步取到的输入指针赋给同类型的输出端口单实例端口portNum同样为 0。当传给输出端口的是串行化端口时存在第二个重载版本void set_port name_OutputPort(NATIVE_INT_TYPE portNum, Fw::InputSerializePort* port);实际用法可以参考 autocoder 测试 Autocoders/Python/test/command1/main.cpp 中的手工接线testImpl.set_CmdStatus_OutputPort(0, cmdSrc.get_cmdStatusPort_InputPort(0)); testImpl.set_CmdReg_OutputPort(0, cmdSrc.get_cmdRegPort_InputPort(0)); cmdSrc.set_cmdSendPort_OutputPort(0, testImpl.get_CmdDisp_InputPort(0));测试夹具 Autocoders/Python/test/command1/test/ut/Tester.cpp 中的connectPorts()也展示了同样的get_..._InputPort/set_..._OutputPort模式。此外Fw/Port/InputPortBase.hpp 提供了setPortNum()用于给输入端口设置端口号配合 FPP 声明中的数组端口如downlink.framedAllocate - staticMemory.bufferAllocate[Ports_StaticMemory.downlink]见 Ref/Top/topology.fpp使用。3.2 命令端口Command Ports为定义了命令的组件代码生成器会自动生成一组命令相关端口其访问函数名是固定的获取命令输入端口Fw::InputFwCmdPort* get_CmdDisp_InputPort();设置命令状态端口void set_CmdStatus_OutputPort(Fw::InputCmdResponse_Port* port);设置命令注册端口void set_CmdReg_OutputPort(Fw::InputCmdRegPort* port);与这些端口相连的组件如命令调度器则使用 3.1 节描述的常规方法访问端口指针。在 FPP 部署中命令连接通过一条模式连接声明批量完成见 Ref/Top/topology.fppcommand connections instance cmdDisp——autocoder 会把所有定义了命令的组件实例的CmdDisp输入端口、CmdStatus/CmdReg输出端口自动接到cmdDisp实例上。3.3 遥测端口Telemetry Ports遥测相关端口的标准访问函数为设置遥测输出端口void set_Tlm_OutputPort(Fw::InputTlmPort* port);设置时间输出端口void set_Time_OutputPort(Fw::InputTimePort* port);遥测组件需要时间戳因此遥测数据发送方需要把时间端口接到时间提供组件。在 Ref/Top/topology.fpp 中对应模式声明为telemetry connections instance tlmSend、time connections instance linuxTime。3.4 事件日志端口Event Logging Ports事件日志相关端口的标准访问函数为设置日志输出端口void set_Log_OutputPort(Fw::InputLogPort* port);设置文本日志输出端口void set_TextLog_OutputPort(Fw::InputFwLogTextPort* port);设置时间输出端口void set_Time_OutputPort(Fw::InputFwTimePort* port);注意set_Time_OutputPort()与遥测端口是共享的同一个时间端口调用一次即可同时服务遥测与事件日志。FPP 中的对应模式声明为event connections instance eventLogger与text event connections instance textLoggerRef/Top/topology.fpp。3.5 参数端口Parameter Ports参数相关端口的标准访问函数为设置参数读取端口void set_ParamGet_OutputPort(Fw::InputFwPrmGetPort* port);设置参数写入端口void set_ParamSet_OutputPort(Fw::InputFwPrmSetPort* port);FPP 模式声明为param connections instance prmDbRef/Top/topology.fpp即把各组件的参数端口统一接到参数数据库实例。说明上述端口类型名为 F´ 长期沿用的命名规范。现代部署中实际生成的类型以 autocoder 输出为准例如测试代码中呈现的get_CmdDisp_InputPort、get_cmdStatusPort_InputPort风格核心访问模式不变。四、注册命令Registering Commands命令分发的标准模式是由用户实现的一个命令调度组件command dispatcher把它的输出命令端口连接到每个服务命令的组件的输入命令端口调度器内部再把某组件的 opcode 集合映射到与该组件相连的端口。为辅助这一过程当组件声明了命令时代码生成器会生成一个命令注册函数regCommands()它接收一个参数——调度器组件上连接到该组件命令端口的端口号。连接方式为把组件的注册端口CmdReg输出连接到调度器的注册端口然后调用该组件的regCommands()方法该方法会为定义的每个 opcode 逐一触发注册端口。每个含有命令的组件都必须调用regCommands()。在 RefTopology.cpp 中这一步由 autocoder 生成的regCommands()完成而手工拓扑的完整范例见 Autocoders/Python/test/command1/main.cpp——接线之后一行testImpl.regCommands();即完成注册。命令处理实现侧组件需实现纯虚的mnemonic_cmdHandler(...)并在命令完成后调用cmdResponse_out(opcode, cmdSeq, response)详见 implementation.md。五、加载参数Loading Parameters先把组件的参数输出端口连接到提供参数存储的组件如Svc::PrmDb连接完成后即可调用基类方法loadParameters()。该方法会通过参数端口向存储组件请求该组件的全部参数值此后参数在实现类中随时可用。虽然常见做法是仅在软件初始化时读取一次但若参数存储在运行期间被更新完全可以再次调用loadParameters()重新读取。参数读取状态可通过paramGet_name(Fw::ParamValid valid)返回值中的valid参数判断implementation.md 给出了四种状态值含义Fw::PARAM_UNINIT从未调用过读取代码多半是初始化时忘了调用组件的loadParameters()公共函数Fw::PARAM_VALID参数成功读取Fw::PARAM_INVALID参数读取失败且没有指定默认值Fw::PARAM_DEFAULT参数读取失败但提供了默认值此外基类还定义了虚方法parameterUpdated(FwPrmIdType id)默认空实现若组件需要感知参数值更新可重写该方法它会在每次参数被更新时被调用。在参考部署中参数库组件本身还需要项目配置configureTopology()内执行prmDb.configure(PrmDb.dat)与prmDb.readParamFile()RefTopology.cpp随后由 autocoder 生成的loadParameters()统一触发各组件加载RefTopology.cpp。六、启动活动组件Starting Active Components拓扑构建的最后一步是启动所有活动组件的任务。start()方法定义于活动组件基类 Fw/Comp/ActiveComponentBase.hpp其现代签名为void start(NATIVE_UINT_TYPE priority Os::Task::TASK_DEFAULT, NATIVE_UINT_TYPE stackSize Os::Task::TASK_DEFAULT, NATIVE_UINT_TYPE cpuAffinity Os::Task::TASK_DEFAULT, NATIVE_UINT_TYPE identifier Os::Task::TASK_DEFAULT);参数含义如下参数含义identifier与线程无关的标识值用于识别该线程的活动在整个系统中应唯一priority任务的执行优先级0 低优先级255 高优先级stackSize分配给任务的栈大小早期版本采用start(identifier, priority, stackSize, cpuAffinity)的有序参数签名现已标记为DEPRECATED并改为上述带默认值的版本。start()由实例化者通常是main()或setupTopology在拓扑装配完毕后调用。与之配套的两个生命周期钩子见 ActiveComponentBase.hpppreamble()进入等待端口调用的循环之前执行一次适合做一次性数据初始化finalizer()退出消息循环之后执行一次适合做清理工作。二者都在活动组件的线程上运行是虚函数、非纯虚可不实现。现代 FPP 部署中priority与stackSize已在实例声明中给出如blockDrv的priority 140、stack size Default.STACK_SIZEautocoder 生成的startTasks(state)RefTopology.cpp会为每个活动组件自动生成start()调用并传入可覆盖的 CPU 亲和性与标识值。活动组件内部使用Os::Task m_task成员ActiveComponentBase.hpp承载线程还提供exit()退出任务与join()回收线程返回值。拓扑的启动入口与关停参考部署的入口 Ref/Main.cpp 给出了拓扑完整生命周期的典型用法Ref::TopologyState inputs; inputs.hostname hostname; inputs.port port_number; Ref::setupTopology(inputs); Ref::startSimulatedCycle(1000); // 以 1Hz 模拟驱动速率组 Ref::teardownTopology(inputs);由于参考拓扑运行在笔记本等通用计算平台上没有真实 1Hz 时钟源startSimulatedCycle()用Os::Task::delay(1000)循环手动调用块驱动的 ISR 调用来驱动速率组RefTopology.cpp信号处理器收到 Ctrl-C 后调用stopSimulatedCycle()停止循环随后执行teardownTopology()依次停止活动组件任务、join 线程并释放缓冲区等资源RefTopology.cpp。真实项目应当用能按精确频率产生输出端口调用的组件替换这套模拟循环见 RefTopology.hpp 的说明。小结构建一个可运行的 F´ 拓扑可以概括为实例化 → 初始化 → 互连 → 注册 → 加载 → 启动六步以开发者选定的内存模型实例化组件通过init()初始化并设定队列深度用get_..._InputPort()/set_..._OutputPort()系方法完成端口互连命令、遥测、事件日志、参数端口均有标准访问函数调用regCommands()注册命令、loadParameters()加载参数最后以start()启动活动组件任务并配合preamble()/finalizer()钩子。在 FPP 时代实例声明instances.fpp、模式连接与直接连接topology.fpp由 autocoder 自动生成大部分样板代码但理解上述手工步骤仍是阅读、调试和定制任何 F´ 部署的基础。如需动手演练可参考 Tutorials 中的实操教程。赞分享嵌入式系统编程【免费下载链接】fprimeF´ - A flight software and embedded systems framework项目地址https://gitcode.com/gh_mirrors/fpri/fprime点击查看免费下载相关推荐F´ 软件框架拓扑构建实战组件实例化、端口互连与任务启动全流程指南F´ 软件框架拓扑构建实战组件实例化、端口互连与任务启动全流程指南 导读 本文是 F´F Prime飞行软件与嵌入式系统框架中构建系统拓扑Cons嵌入式系统编程F´ Reference Deployment 全解析Ref 示例应用的拓扑结构与组件互连实战指南F´ Reference Deployment 全解析Ref 示例应用的拓扑结构与组件互连实战指南 F´F Prime是由 NASA JPL 开源的飞行软嵌入式系统编程Awakened PoE Trade伪属性处理复杂物品修饰符的智能分组算法Awakened PoE Trade伪属性处理复杂物品修饰符的智能分组算法 在《Path of Exile》这款复杂的ARPG游戏中装备和物品往往包含大量复上一篇OpenTelemetry Go 版本化策略全解析semver 2.0、语义化导入版本与模块发布生命周期附 KubeSphere 仓库落地实例下一篇rkt容器数据备份存储成本云存储定价模型分析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
FLUX.1 Kontext 图像编辑实战:从首次改图到 TensorRT 加速的完整路径 FLUX.1 Kontext 图像编辑实战:从首次改图到 TensorRT 加速的完整路径 【免费下载链接】flux Official inference repo for FLUX.1 models 项目地址: https://gitcode.com/GitHub_Trending/flux49/flux
FLUX.1 Kontext [dev] 是 Black Forest Labs 发布的 12B… · 2026/9/25 5:48:38
Absolute Database v7.93在Delphi 4-11下的源码编译与实战指南 简介:面向 Delphi 4 至 11 开发者的 Absolute Database v7.93 完整源代码包,重点适配 Delphi 11 Alexandria,适用于桌面应用、嵌入式数据管理及跨平台业务系统。压缩包共 756 个文件,大小约 18.9MB,主要内容包括 Delph… · 2026/9/25 5:48:38
中兴B860AV3.2-M刷机教程:S905L3线刷EmotnUI固件实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:25:50
EMC核心术语解析:EMI/EMS、RE/CS、EFT/ESD的物理本质与工程落地 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:25:50
USB流量分析实战:从HID协议到Wireshark/tshark还原键盘鼠标操作 1. 从一份USB流量里找到键盘敲出的FlagCTF流量分析里,USB流量算是杂项题中辨识度最高、套路最成熟的一类。题目通常给你一个.pcap或.pcapng文件,描述是"有人用键盘敲了一封信,但捕获文件出了点问题",或者干脆只留下一句… · 2026/9/25 6:25:43
零基础一小时C语言入门:从变量循环到数组指针的极简指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:25:31
CTF流量分析实战:USB键盘与鼠标流量提取与还原 CTF流量分析做了几年,USB这个方向真的是“老面孔”了。从入门赛到省级决赛,USB流量题几乎成了标配,尤其是键盘流量,几乎人手一把梭。但是很多人卡在不知道USB流量到底在说什么、键盘映射怎么处理、鼠标坐标怎么还原,更… · 2026/9/25 6:25:25
辉芒微MCU烧录校验全指南:从Hex到FMD-Link实操 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 6:25:25
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37