当前位置: 首页 > news >正文

rust嵌入式开发之总结

我们用rust开发的新版产品刚刚交付,已经在海上安装测试完毕并顺利投产。终于松了口气,同时也有时间和精力来做个全面的总结了。

这个产品,目前差不多有三版:

  • 第一个版本是用c+rt-thread写的,投产后出了一个内存泄露的bug,由于我们的产品是安装到海上,bug修复了也很难更新到产品上,最后穷尽洪荒之力才找出对策进行了处置【不是排除】,但现在依然象个定时炸弹一样悬在那。导致我对c失去信心,最终下定决心换用rust
  • 第二个是用rust写的demo版本,是基于RTIC框架写的。后因认为RTIC框架的限制比较大,在写完主体功能后就废弃了
  • 第三个版本就是基于Embassy框架写的这一刚交付的版本

ps:海上的情况太过复杂,很多情况是我们现在的测试环境无法完全模拟的

本文即是基于笔者开发这三个版本的经验,对rust嵌入式的开发所做的一个阶段性的总结。

开发门槛高 vs 现场智能化

就嵌入式来说,rust+Embassy vs c+rt-thread的门槛是两个级别。

我是java和python的系统工程师,开发第一个版本时,就没用过c,也没开发过嵌入式!但花了一个月的时间,第一个版本就写完了。

开发第三个版本时,有了第一个版本的所有设备功能的开发经验,有第二个版本的rust尝试,但总的开发时间是从年后到5月下旬,合到三个月了。

当然,这里面主要的原因有两个:

1、rust在嵌入式方面的生态没有c这么成熟:有rt-thread这样的RTOS。虽然Embassy也比较成熟了,但它不是一个完整的RTOS,需要花大量的精力来开发一些较为底层的功能部件,如之前介绍过的数据锁:rust嵌入式开发之基于await构造应用级临界区。

当然,如果只是如多数嵌入式的应用场景那样的话,用RTIC框架完全可以:rust嵌入式开发之RTICvsEmbassy,那就简单很多了。但如我上面那篇文章所述,RTIC框架能力有限,起码对我司来说的是不够的:

  • 我们需要连接大量各厂家的设备,串口之多以至于只能用10串口的STM32F403VG,每种设备的协作机制完全不同,所以就需要强大的异步协作能力,RTIC就没有提供异步支持
  • 海上工程的特殊性,决定了我们必须提供多种的错误恢复手段,RTIC太弱

2、除了第一版中通常的现场操控能力之外,在新版中我还开发了额外的应用能力,如命令行,这相当于在做RTOS中的shell了,所以本质上还是rust嵌入式的生态还不够成熟的问题。

第一个版本是以在服务器上抄收到现场数据为完工标志,但海上施工非常困难,出海时一切正常,突然瓢泼大雨的再正常不过了,所以有必要提供现场的检查手段,因此需要提供命令行功能。

其它还有debug、对象锁之类。

这也是我坚持认为,随着MCU的资源越来越丰富,嵌入式编程必然会走向复杂化、应用化的原因:为了在竞争中获胜,现场器件越来越智能化是必要的、必须的趋势

如我们现在就在考虑海上碰撞的预警、检测与证据固定,已经在进行功能方面的论证。

也正是基于这个判断,我才会坚定的抛弃c而拥抱rust。

概要之,如果只是瞄准目前通常的嵌入式应用场景:固定功能的现场操控。其实没必要用rust嵌入式编程【rust+RTIC功能有限又自带rust的高门槛,完全属于自找麻烦】,门槛太高、成本也高出很多;开发周期【尤其是产品成型的第一个版本的开发周期】太长,可能会导致产品验证出现问题。

但是,如果希望提高竞争水准、抬升行业准入门槛,并将其中一个发力点落在现场感知、反应的智能化能力上,我认为除非有极深的积累,否则rust嵌入式自然拥有相当强的竞争力,毕竟c太古老了,不太适合快速低成本开发高可靠、高价值的应用。

对冲rust的高门槛

rust门槛高、心智负担重,这是公认的rust的缺点。但就嵌入式而言,这个缺点其实是不存在的。

原因非常简单,嵌入式再如何复杂,现在也远比后台应用简单的多,所以完全可以采取措施来对冲rust的高门槛:

1、用静态变量互斥访问来对冲rust的生命周期、借用难

对rust的生命周期、借用,作为初学者的我也很头疼,而我的解决办法非常粗暴:

嵌入式的应用场景简单,所以我直接把主要功能所需要的数据静态化、然后进行包裹、再用互斥锁进行保护,这样一来,所有的操作接口都是:&'static self。

这就完全不再需要考虑生命周期和借用的问题了,编程难度直线下降,编译器一声都不敢吱:)

这个方法在上线后,轻松抗住了一天40万条数据的采集压力!而且经过计算,还远没到设备极限,大家可以放心大胆的用:)

2、用资源的高耗费来对冲编程的复杂度

既然现在MCU的资源极大的丰富了,堆资源就好,绕开死磕技术难度的坑就是了。

就如我们的串口读取,底层申请了内存,上层应用什么时候用完、需要进行释放是不清楚的,而且串口读取到的数据,还可能流经多种功能模块,比如用蓝牙模块以modbus协议读取设备数据,就需要流经串口模块、蓝牙模块、modbus模块、应用模块。这对内存管理就带了很大的压力。

所以我同样是用简单粗暴的方法来应对:每层自己申请自己释放,要提交给上层的,就做一次内存copy。

这样过一层就做一次内存copy的资源耗费显然太过巨大,但这样简单啊,硬怼资源就很容易实现,反正内存copy那点开销相比现在的MCU和嵌入式场景实在不值一提。

这个以资源开销来换简化编程、消弭bug的方法在我们多个串口、多种设备管控,一天40多万条数据的采集、海上无线传输的压力下工作良好!

一句话:在目前的情况下,rust嵌入式编程不需要考虑优化、不需要担心浪费,堆资源是最应优先考虑的方案。

在我来看,对一个产品来说,有两个指标是决定性的:

  • 关键需求的满足度
  • 产品的稳定性

前者是客户花钱的理由,rust的现代性和良好生态是支撑我们依托智能化产品来提升核心竞争力的利器,自然就是选择rust的最大理由;后者是客户付钱的理由,rust写出的程序天然自带的健壮性和可靠性,就是选择rust的第二大理由。

当然,rust在嵌入式方面的生态还有所不足,但这只是时间问题,总要给新生儿成长的时间。

谁都有缺点,弥补缺点就需要成本。借用工程技术手段和高资源开销来弥补rust的缺点,就是我们目前必要的成本。

现在看下来,这些成本完全可以接受。

rust嵌入式的不足

生态、生态、生态。这必须是rust嵌入式的最大不足。尤其是在芯片国产替代的当下,国产芯片在rust嵌入式方面的投入基本可以忽略。

如我们第一版用的就是国产的GD32,但在看好rust嵌入式的前景之后,由于芯片厂商等在基础设施方面的投入不足,最后只好又选了STM32。

当然,我相信国内的芯片厂家既然提供了这么强大的MCU,最终一定是需要软件来消耗掉这些资源的,我们的新版本已经消耗掉了384K的Flash,还有很大空间值得挖掘:)

而这要靠c肯定不现实,最终还是要在rust上发力的。只是需要提提速啊:(

rust嵌入式在生态方面的最大不足,对我们来说,自然就是缺乏一个如国产的rt-thread这样的国产RTOS。由于缺了不少必要的基础部件,就需要自己来开发,这相当于提升了开发门槛和成本。

rust嵌入式的第二个不足,就是no_std和std的隔离。这就导致rust的良好生态在嵌入式方面被大幅度的削弱了。

比如:我在命令行中原本要实现参数的核验,但rust工业标准的正则表达式crate在no_std下就用不了,只好用了可以支持no_std的regex-automata,但用下来总感觉有些问题,由于时间太紧张了,实在无法全面测试,最后干脆就先取消了命令行的参数核验能力。

就目前来说,no_std的问题还影响不大,但随着现场智能化诉求的提升,这个问题势必会带来越来越大的限制。

第三个不足应该是rust的不足,就是IDE在编辑界面无法自动扩展宏。

我选择rust的一个重要理由,就是相比c中的宏,rust的过程宏异常的强大:完全可以媲美java中的反射和python中的@修饰符,关键还是零开销:rust嵌入式之用类函数宏简写状态机定义。

在java和python中我都自己实现了ORM、状态机等,所以我深知反射技术是各种框架的基础,对现代软件工程和编程技术具有无与伦比的巨大意义,当然,还有所要花费的巨大开销。

在rust中,我同样用过程宏实现了状态机、命令行、debug、控制台、串口/pwm等接口操作的自动扩写、扩展对象锁、命令-期望等等。

对rust的过程宏,我实在是太满意了。

但是,宏用得太多,某些利用宏扩写出来的技术细节我自己也就记不清楚了,当需要探究某个技术细节的时候,IDE竟然不给自动展开所扩写的代码,还需要手动执行命令来展开宏,然后去另外的文件中来查看,非常低效。

这个不足,在排错的时候尤其讨厌!

结语

虽然rust嵌入式有着种种的不足,但我依然相信rust在嵌入式领域前程远大

相关文章:

rust嵌入式开发之总结

我们用rust开发的新版产品刚刚交付,已经在海上安装测试完毕并顺利投产。终于松了口气,同时也有时间和精力来做个全面的总结了。 这个产品,目前差不多有三版: 第一个版本是用crt-thread写的,投产后出了一个内存泄露的…...

【制作100个unity游戏之27】使用unity复刻经典游戏《植物大战僵尸》,制作属于自己的植物大战僵尸随机版和杂交版6(附带项目源码)

最终效果 系列导航 文章目录 最终效果系列导航前言方法一、使用excel配置表excel转txt文本读取txt数据按配置信息生成僵尸 方法二、使用ScriptableObject 配置关卡信息源码结束语 前言 本节主要是推荐两种实现配置关卡信息,并按表生成僵尸和关卡波次 方法一、使用…...

回溯算法指组合总和

题目: 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 思路: 这种问题…...

java-stream转换map key重复报错解决小记

解决key重复问题 在用stream转成map过程中会有key重复的隐患,如果数据没重复还好,如果重复了会提示 java.lang.IllegalStateException: Duplicate key 8753444332651at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)at ja…...

王春城 | 如何解决精益转型过程中的信任问题?

实践证明,精益转型不仅仅是技术和管理方法的更新,更是一场深刻的文化变革。在这个过程中,涉及到多个部门、多个层级的协同合作,需要团队成员之间的深度沟通和高度信任。如果缺乏信任,团队成员之间就会产生隔阂和抵触情…...

Ubuntu Nvidia Docker单机多卡环境配置

ubuntu版本是22.04,现在最新版本是24.xx,截止当前,Nvidia的驱动最高还是22.04版本,不建议更新至最新版本。本部分是从0开始安装Nvidia docker的记录,若已安装Nvdia驱动,请直接跳至3。 1、更新软件软件列表…...

小公司的软件开发IT工具箱

目录 工具链困境 难题的解决 达到的效果 资源要求低 工具箱一览 1、代码管理工具 2、自动化发版(测试)工具 3、依赖库(制品包)管理 4、镜像管理 5、授权管理(可选) 待讨论:为什么不是…...

代码随想录算法训练营第四十四天| 背包问题、背包问题之滚动数组、416. 分割等和子集

背包问题 题目链接:背包问题 文档讲解:代码随想录/背包问题 视频讲解:视频讲解-背包问题 状态:已完成(1遍) 解题过程 这几天属实是有点分身乏术了,先直接看题解AC了,二刷的时候再…...

最新一站式AI创作中文系统网站源码+系统部署+支持GPT对话、Midjourney绘画、Suno音乐、GPT-4o文档分析等大模型

一、系统简介 本文将介绍最新的一站式AI创作中文系统(集成ChatGPTMidjourneySunoStable Diffusion)——星河易创AI系统,该系统基于ChatGPT的核心技术,融合了自然语言问答、绘画、音乐、文档分享、图片识别等创作功能,…...

C# 语言类型(二)—预定义类型之字符串及字符类型简述

总目录 C# 语法总目录 参考链接: C#语法系列:C# 语言类型(一)—预定义类型值之数值类型 C#语法系列:C# 语言类型(二)—预定义类型之字符串及字符类型简述 C#语法系列:C# 语言类型(三)—数组/枚举类型/结构体 C#语法系列:C# 语言类型(四)—传递参数及其修饰符 C#语法…...

微信小程序canvas画图使用百分比适配不同机型屏幕达到任何屏幕比例皆可!完美适配任何机型!指定canvas尺寸适配亦可!保证全网唯一完美

错误代码示例: // 在onLoad中调用 const that this wx.getSystemInfo({success: function (res) {console.log(res)that.setData({model: res.model,screen_width: res.windowWidth/375,screen_height: res.windowHeight})} }) 我看到网上很多使用上面这种代码去…...

Redis-02

redis安装包位置 /opt/redis-7.2.5 redis默认安装路径: 配置文件路径:/usr/local/bin/redisconfig gcc安装位置 /opt/rhredis启动: 在/usr/local/bin目录下输入redis-server redisconfig/redis.confredis-cli -p 6379redis性能测试命令 red…...

如何编辑pdf文件内容?编辑技巧大揭秘,秒变办公达人!

如何编辑pdf文件内容?在数字化办公日益普及的今天,PDF文件因其跨平台、格式稳定的特点,成为我们日常工作和学习中不可或缺的一部分。然而,PDF文件的编辑却常常令人头疼,许多人面对需要修改内容的PDF文件时感到无从下手…...

Linux Shell Script 编写入门

Linux Shell 脚本是一种强大的工具,能够帮助用户自动化任务、简化系统管理以及提高工作效率。本文将带您全面了解如何编写 Linux Shell 脚本,并介绍一些常见的脚本编写技巧和注意事项。 目录 什么是 Linux ShellShell 脚本的基本结构常用 Shell 命令变…...

不是从APP store下载的APP在mac上一直提示有损坏,打不开怎么办?

1.点击设置 2.安全与隐私 3.通用看看允许从以下位置下载的APP是否有任何来源 4.如果没有,mac桌面点击🔍输入终端或Terminal 命令行输入下述代码: sudo spctl --master-disable 5.回车,输入mac开机密码。注意:此时密…...

ubuntu22.04部署docker版zlmediakit和源码运行wvp-GB28181-pro

1 运行zlmediakit 1. 修改zlmediakit配置文件 先用run命令运行zlmediakit,将zlmediakit的配置文件拷贝出来 docker run -d -p 1935:1935 -p 8080:80 -p 8554:554 \ -p 10000:10000 -p 10000:10000/udp -p 8000:8000/udp \ --name zlmediakit \ zlmediakit/zlmedi…...

MySQL表的增删改查初阶(上篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人…...

Spring Boot 集成 zxing 生成条形码与二维码

前面我们知道了怎么通过 使用 zxing 生成二维码以及条形码, 由于我们现在都是 web 端的项目了,那么我们看下怎么使用 Spring Boot 集成然后返回给前端展示: 工程源码 对应的工程源码我放到了这里:github源码路径,点击…...

C# 编程基础:注释、变量、常量、数据类型和自定义类型

C# 是一种功能强大的面向对象编程语言,它提供了丰富的特性来帮助开发者编写清晰、高效的代码。本文将介绍C#中的注释、变量、常量、基本数据类型以及如何创建和使用自定义类型。 注释 注释用于解释代码的目的,它们不会被程序执行。 单行注释使用 //。…...

网络原理-三

一、连接管理 建立连接,断开连接 建立连接,TCP有连接的. 客户端执行 socket new Socket(SeverIP,severPort); -> 这个操作就是在建立连接. 上述只是调用socket api,真正建立连接的过程,实在操作系统内核完成的. 内核是怎样完成上述的 " 建立连接 "过程的…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...

【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案

目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

【技巧】dify前端源代码修改第一弹-增加tab页

回到目录 【技巧】dify前端源代码修改第一弹-增加tab页 尝试修改dify的前端源代码,在知识库增加一个tab页"HELLO WORLD",完成后的效果如下 [gif01] 1. 前端代码进入调试模式 参考 【部署】win10的wsl环境下启动dify的web前端服务 启动调试…...